mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-19 09:09:41 +03:00
feat(web-test): add showTitleSlide/hideTitleSlide for video intros
Full-screen overlay with gradient background, centered title text, optional subtitle. Useful for intro/outro frames in video recordings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -284,6 +284,7 @@ Clear filters. Without arguments clears all, with `{ field }` clears specific ba
|
|||||||
#### `getPage()` → Playwright Page (raw, for advanced scripting)
|
#### `getPage()` → Playwright Page (raw, for advanced scripting)
|
||||||
#### `startRecording(path, opts?)` / `stopRecording()` → MP4 video recording
|
#### `startRecording(path, opts?)` / `stopRecording()` → MP4 video recording
|
||||||
#### `showCaption(text, opts?)` / `hideCaption()` → text overlay on page
|
#### `showCaption(text, opts?)` / `hideCaption()` → text overlay on page
|
||||||
|
#### `showTitleSlide(text, opts?)` / `hideTitleSlide()` → full-screen title card (intro/outro)
|
||||||
#### `isRecording()` → boolean
|
#### `isRecording()` → boolean
|
||||||
|
|
||||||
See [recording.md](recording.md) for setup (ffmpeg), API details, and examples.
|
See [recording.md](recording.md) for setup (ffmpeg), API details, and examples.
|
||||||
|
|||||||
@@ -94,24 +94,48 @@ The overlay uses `pointer-events: none` — does not interfere with clicking.
|
|||||||
|
|
||||||
Remove the caption overlay.
|
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
|
```js
|
||||||
await startRecording('recordings/create-order.mp4');
|
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 navigateSection('Продажи');
|
||||||
await wait(1);
|
await wait(1);
|
||||||
|
|
||||||
await showCaption('Step 2: Open Customer Orders');
|
await showCaption('Шаг 2. Открываем заказы клиентов');
|
||||||
await openCommand('Заказы клиентов');
|
await openCommand('Заказы клиентов');
|
||||||
await wait(1);
|
await wait(1);
|
||||||
|
|
||||||
await showCaption('Step 3: Create new order');
|
await showCaption('Шаг 3. Создаём новый заказ');
|
||||||
await clickElement('Создать');
|
await clickElement('Создать');
|
||||||
await wait(2);
|
await wait(2);
|
||||||
|
|
||||||
await showCaption('Step 4: Fill header fields');
|
await showCaption('Шаг 4. Заполняем шапку');
|
||||||
await fillFields({ 'Организация': 'Конфетпром', 'Контрагент': 'Альфа' });
|
await fillFields({ 'Организация': 'Конфетпром', 'Контрагент': 'Альфа' });
|
||||||
await wait(2);
|
await wait(2);
|
||||||
|
|
||||||
|
|||||||
@@ -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(/</g, '<').replace(/\n/g, '<br>');
|
||||||
|
let html = `<div style="font-size:${fontSize}px;font-weight:600;line-height:1.4;">${esc(text)}</div>`;
|
||||||
|
if (subtitle) {
|
||||||
|
html += `<div style="font-size:${Math.round(fontSize * 0.5)}px;margin-top:16px;opacity:0.7;">${esc(subtitle)}</div>`;
|
||||||
|
}
|
||||||
|
div.innerHTML = `<div style="text-align:center;max-width:70%;color:${color};font-family:'Segoe UI',Arial,sans-serif;">${html}</div>`;
|
||||||
|
}, { 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
|
// Private helpers
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -251,6 +251,13 @@ await closeForm({ save: false });
|
|||||||
| `screenshot()` | Скриншот (PNG Buffer) |
|
| `screenshot()` | Скриншот (PNG Buffer) |
|
||||||
| `wait(seconds)` | Пауза, возвращает form state |
|
| `wait(seconds)` | Пауза, возвращает form state |
|
||||||
| `getPage()` | Сырой Playwright Page для горячих клавиш и нестандартных операций |
|
| `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) |
|
||||||
|
|
||||||
## Клавиатурные сочетания
|
## Клавиатурные сочетания
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user