diff --git a/.claude/skills/web-test/SKILL.md b/.claude/skills/web-test/SKILL.md index a7b43fcd..cd864d55 100644 --- a/.claude/skills/web-test/SKILL.md +++ b/.claude/skills/web-test/SKILL.md @@ -284,6 +284,7 @@ Clear filters. Without arguments clears all, with `{ field }` clears specific ba #### `getPage()` → Playwright Page (raw, for advanced scripting) #### `startRecording(path, opts?)` / `stopRecording()` → MP4 video recording #### `showCaption(text, opts?)` / `hideCaption()` → text overlay on page +#### `showTitleSlide(text, opts?)` / `hideTitleSlide()` → full-screen title card (intro/outro) #### `isRecording()` → boolean See [recording.md](recording.md) for setup (ffmpeg), API details, and examples. diff --git a/.claude/skills/web-test/recording.md b/.claude/skills/web-test/recording.md index 5bfb58cb..f52cf1e3 100644 --- a/.claude/skills/web-test/recording.md +++ b/.claude/skills/web-test/recording.md @@ -94,24 +94,48 @@ The overlay uses `pointer-events: none` — does not interfere with clicking. Remove the caption overlay. -## Example: Record a workflow with captions +### `showTitleSlide(text, opts?)` + +Display a full-screen title slide overlay (gradient background, centered text). Useful for intro/outro frames in video recordings. Calling again updates the content. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `text` | string | required | Title text (`\n` → line break) | +| `opts.subtitle` | string | `''` | Smaller text below the title | +| `opts.background` | string | dark gradient | CSS background | +| `opts.color` | string | `'#fff'` | Text color | +| `opts.fontSize` | number | 36 | Title font size in px | + +The overlay covers the entire viewport with `z-index: 999999` and `pointer-events: none`. + +### `hideTitleSlide()` + +Remove the title slide overlay. + +## Example: Record a workflow with title slide and captions ```js await startRecording('recordings/create-order.mp4'); -await showCaption('Step 1: Navigate to Sales'); +// Title slide — 4 seconds +await showTitleSlide('Создание заказа клиента', { subtitle: 'Демонстрация' }); +await wait(4); +await hideTitleSlide(); + +// Steps with captions +await showCaption('Шаг 1. Переходим в раздел «Продажи»'); await navigateSection('Продажи'); await wait(1); -await showCaption('Step 2: Open Customer Orders'); +await showCaption('Шаг 2. Открываем заказы клиентов'); await openCommand('Заказы клиентов'); await wait(1); -await showCaption('Step 3: Create new order'); +await showCaption('Шаг 3. Создаём новый заказ'); await clickElement('Создать'); await wait(2); -await showCaption('Step 4: Fill header fields'); +await showCaption('Шаг 4. Заполняем шапку'); await fillFields({ 'Организация': 'Конфетпром', 'Контрагент': 'Альфа' }); await wait(2); diff --git a/.claude/skills/web-test/scripts/browser.mjs b/.claude/skills/web-test/scripts/browser.mjs index 304d6586..179e374b 100644 --- a/.claude/skills/web-test/scripts/browser.mjs +++ b/.claude/skills/web-test/scripts/browser.mjs @@ -2570,6 +2570,55 @@ export async function hideCaption() { }); } +/** + * Show a full-screen title slide overlay (for video recordings). + * Repeated calls update the content. Use hideTitleSlide() to remove. + * @param {string} text Title text (\n → line break) + * @param {object} [opts] + * @param {string} [opts.subtitle] Smaller text below the title + * @param {string} [opts.background] CSS background (default: dark gradient) + * @param {string} [opts.color] Text color (default: '#fff') + * @param {number} [opts.fontSize] Title font size in px (default: 36) + */ +export async function showTitleSlide(text, opts = {}) { + ensureConnected(); + const { + subtitle = '', + background = 'linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)', + color = '#fff', + fontSize = 36, + } = opts; + await page.evaluate(({ text, subtitle, background, color, fontSize }) => { + let div = document.getElementById('__web_test_title'); + if (!div) { + div = document.createElement('div'); + div.id = '__web_test_title'; + document.body.appendChild(div); + } + div.style.cssText = [ + 'position:fixed', 'top:0', 'left:0', 'width:100%', 'height:100%', + `background:${background}`, + 'display:flex', 'align-items:center', 'justify-content:center', + 'z-index:999999', 'pointer-events:none' + ].join(';'); + const esc = s => s.replace(/&/g, '&').replace(/'); + let html = `
${esc(text)}
`; + if (subtitle) { + html += `
${esc(subtitle)}
`; + } + div.innerHTML = `
${html}
`; + }, { text, subtitle, background, color, fontSize }); +} + +/** Remove the title slide overlay. */ +export async function hideTitleSlide() { + ensureConnected(); + await page.evaluate(() => { + const el = document.getElementById('__web_test_title'); + if (el) el.remove(); + }); +} + // ============================================================ // Private helpers // ============================================================ diff --git a/docs/web-test-guide.md b/docs/web-test-guide.md index d93d1fed..941791aa 100644 --- a/docs/web-test-guide.md +++ b/docs/web-test-guide.md @@ -251,6 +251,13 @@ await closeForm({ save: false }); | `screenshot()` | Скриншот (PNG Buffer) | | `wait(seconds)` | Пауза, возвращает form state | | `getPage()` | Сырой Playwright Page для горячих клавиш и нестандартных операций | +| `startRecording(path, opts?)` | Начать запись видео (CDP screencast → ffmpeg → MP4) | +| `stopRecording()` | Остановить запись, вернуть `{ file, duration, size }` | +| `showCaption(text, opts?)` | Текстовая подпись поверх страницы (для видеозаписей) | +| `hideCaption()` | Убрать подпись | +| `showTitleSlide(text, opts?)` | Полноэкранный титульный слайд (`\n` → перенос, `subtitle`, `background`) | +| `hideTitleSlide()` | Убрать титульный слайд | +| `isRecording()` | Идёт ли запись (boolean) | ## Клавиатурные сочетания