Communitygithub.com

UI-Demo-Videorecorder

Erstelle professionelle Demo-Videos für Webanwendungen mit Playwright. Verwende diesen Skill, wenn ein Nutzer ein Video-Tutorial, einen Walkthrough, eine Bildschirmaufzeichnung oder eine Demovorführung einer Web-App anfordert.

Was ist UI-Demo-Videorecorder?

Dieser Skill ermöglicht die automatisierte Erstellung von hochwertigen Demo-Videos für Webanwendungen, basierend auf Playwright. Er folgt einem strukturierten Drei-Phasen-Prozess: **Entdecken (Discover)**, **Proben (Rehearse)** und **Aufnehmen (Record)**. In der Entdeckungsphase werden die interaktiven Elemente der Zielseiten analysiert, um korrekte Selektoren zu gewährleisten. In der Probenphase wird jeder Selektor auf Sichtbarkeit geprüft, um stille Fehler zu vermeiden. Die Aufnahmephase erzeugt ein WebM-Video mit einer sichtbaren Mauszeiger-Überlagerung, natürlicher Eingabeverzögerung, Untertiteln und einer erzählerischen Ablaufstruktur. Der Skill unterstützt die gängigen Playwright-Plattformen (Chromium) und eignet sich besonders für Dokumentationen, Onboarding-Materialien und Präsentationen vor Stakeholdern.

Funktioniert mit~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/ui-demo

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht UI-Demo-Videorecorder?

Erstelle professionelle Demo-Videos von Webanwendungen mit Playwrights Videoaufzeichnung, einer eingeblendeten Mauszeiger-Überlagerung, natürlichem Tempo und einer erzählerischen Ablaufstruktur.

Verwendung

  • Der Nutzer fordert ein „Demo-Video”, eine „Bildschirmaufzeichnung”, einen „Walkthrough” oder ein „Tutorial” an.
  • Der Nutzer möchte eine Funktion oder einen Arbeitsablauf visuell präsentieren.
  • Der Nutzer benötigt ein Video für Dokumentation, Onboarding oder eine Präsentation für Stakeholder.

Drei-Phasen-Prozess

Jede Demo durchläuft drei Phasen: Entdecken -> Probieren -> Aufnehmen. Gehe niemals direkt zur Aufnahme über.


Phase 1: Entdecken

Bevor du ein Skript schreibst, erkunde die Zielseiten, um zu verstehen, was dort tatsächlich vorhanden ist.

Warum

Du kannst kein Skript für Inhalte schreiben, die du nicht gesehen hast. Felder könnten <input> und nicht <textarea> sein, Dropdowns könnten benutzerdefinierte Komponenten und nicht <select> sein, und Kommentarfelder könnten @mentions oder #tags unterstützen. Annahmen führen zu stillen Fehlern in der Aufnahme.

Vorgehen

Navigiere zu jeder Seite im Ablauf und gib ihre interaktiven Elemente aus:

// Führe dies für jede Seite im Ablauf AUS, bevor du das Demo-Skript schreibst
const fields = await page.evaluate(() => {
  const els = [];
  document.querySelectorAll('input, select, textarea, button, [contenteditable]').forEach(el => {
    if (el.offsetParent !== null) {
      els.push({
        tag: el.tagName,
        type: el.type || '',
        name: el.name || '',
        placeholder: el.placeholder || '',
        text: el.textContent?.trim().substring(0, 40) || '',
        contentEditable: el.contentEditable === 'true',
        role: el.getAttribute('role') || '',
      });
    }
  });
  return els;
});
console.log(JSON.stringify(fields, null, 2));

Worauf zu achten ist

  • Formularfelder: Sind es <select>, <input>, benutzerdefinierte Dropdowns oder Comboboxen?
  • Select-Optionen: Gib Optionswerte UND Texte aus. Platzhalter haben oft value="0" oder value="", was nicht leer aussieht. Verwende Array.from(el.options).map(o => ({ value: o.value, text: o.text })). Überspringe Optionen, deren Text „Select” enthält oder deren Wert "0" ist.
  • Rich Text: Unterstützt das Kommentarfeld @mentions, #tags, Markdown oder Emoji? Überprüfe den Platzhaltertext.
  • Pflichtfelder: Welche Felder blockieren das Absenden des Formulars? Überprüfe required, * in Labels und versuche, das Formular leer abzusenden, um Validierungsfehler zu sehen.
  • Dynamische Inhalte: Erscheinen Felder erst, nachdem andere Felder ausgefüllt wurden?
  • Button-Beschriftungen: Genaue Texte wie "Submit", "Submit Request" oder "Send".
  • Tabellenspaltenüberschriften: Ordne bei tabellenbasierten Modi jedem input[type="number"] seine Spaltenüberschrift zu, anstatt anzunehmen, dass alle numerischen Eingaben dasselbe bedeuten.

Ausgabe

Eine Feldzuordnung für jede Seite, die verwendet wird, um korrekte Selektoren im Skript zu schreiben. Beispiel:

/purchase-requests/new:
  - Budget Code: <select> (erstes Select auf der Seite, 4 Optionen)
  - Desired Delivery: <input type="date">
  - Context: <textarea> (kein input)
  - BOM table: inline-editierbare Zellen mit span.cursor-pointer -> Eingabemuster
  - Submit: <button> text="Submit"

/purchase-requests/N (Detail):
  - Comment: <input placeholder="Type a message..."> unterstützt @user und #PR-Tags
  - Send: <button> text="Send" (deaktiviert, bis das Eingabefeld Inhalt hat)

Phase 2: Probieren (Rehearse)

Gehe alle Schritte ohne Aufnahme durch. Verifiziere, dass jeder Selektor aufgelöst werden kann.

Warum

Stille Selektorfehler sind der Hauptgrund, warum Demo-Aufnahmen fehlschlagen. Das Probieren fängt sie ab, bevor eine Aufnahme verschwendet wird.

Vorgehen

Verwende ensureVisible, einen Wrapper, der Fehler protokolliert und laut ausgibt:

async function ensureVisible(page, locator, label) {
  const el = typeof locator === 'string' ? page.locator(locator).first() : locator;
  const visible = await el.isVisible().catch(() => false);
  if (!visible) {
    const msg = `PROBEN-FEHLER: "${label}" nicht gefunden - Selektor: ${typeof locator === 'string' ? locator : '(locator object)'}`;
    console.error(msg);
    const found = await page.evaluate(() => {
      return Array.from(document.querySelectorAll('button, input, select, textarea, a'))
        .filter(el => el.offsetParent !== null)
        .map(el => `${el.tagName}[${el.type || ''}] "${el.textContent?.trim().substring(0, 30)}"`)
        .join('\n  ');
    });
    console.error('  Sichtbare Elemente:\n  ' + found);
    return false;
  }
  console.log(`PROBEN-ERFOLG: "${label}"`);
  return true;
}

Struktur des Probenskripts

const steps = [
  { label: 'Login E-Mail-Feld', selector: '#email' },
  { label: 'Login Absenden', selector: 'button[type="submit"]' },
  { label: 'Neuer Antrag Button', selector: 'button:has-text("New Request")' },
  { label: 'Budget Code Select', selector: 'select' },
  { label: 'Lieferdatum', selector: 'input[type="date"]:visible' },
  { label: 'Beschreibungsfeld', selector: 'textarea:visible' },
  { label: 'Artikel hinzufügen Button', selector: 'button:has-text("Add Item")' },
  { label: 'Absenden Button', selector: 'button:has-text("Submit")' },
];

let allOk = true;
for (const step of steps) {
  if (!await ensureVisible(page, step.selector, step.label)) {
    allOk = false;
  }
}
if (!allOk) {
  console.error('PROBEN-FEHLGESCHLAGEN - Korrigiere Selektoren vor der Aufnahme');
  process.exit(1);
}
console.log('PROBEN-BESTANDEN - alle Selektoren verifiziert');

Wenn das Probieren fehlschlägt

  1. Lies die Auflistung der sichtbaren Elemente.
  2. Finde den korrekten Selektor.
  3. Aktualisiere das Skript.
  4. Führe das Probieren erneut aus.
  5. Fahre erst fort, wenn jeder Selektor bestanden ist.

Phase 3: Aufnehmen

Erstelle die Aufnahme erst, nachdem Entdecken und Probieren erfolgreich waren.

Aufnahmeprinzipien

1. Erzählerischer Ablauf (Storytelling Flow)

Plane das Video als Geschichte. Folge der vom Nutzer angegebenen Reihenfolge oder verwende diese Standardreihenfolge:

  • Einstieg: Login oder Navigation zum Startpunkt
  • Kontext: Zeige das Umfeld, damit sich die Zuschauer orientieren können
  • Aktion: Führe die Hauptschritte des Arbeitsablaufs durch
  • Variation: Zeige eine sekundäre Funktion wie Einstellungen, Theme oder Lokalisierung
  • Ergebnis: Zeige das Resultat, die Bestätigung oder den neuen Zustand

2. Tempo (Pacing)

  • Nach dem Login: 4s
  • Nach der Navigation: 3s
  • Nach einem Button-Klick: 2s
  • Zwischen Hauptschritten: 1.5-2s
  • Nach der letzten Aktion: 3s
  • Eingabeverzögerung: 25-40ms pro Zeichen

3. Mauszeiger-Überlagerung (Cursor Overlay)

Füge einen SVG-Pfeilzeiger ein, der den Mausbewegungen folgt:

async function injectCursor(page) {
  await page.evaluate(() => {
    if (document.getElementById('demo-cursor')) return;
    const cursor = document.createElement('div');
    cursor.id = 'demo-cursor';
    cursor.innerHTML = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M5 3L19 12L12 13L9 20L5 3Z" fill="white" stroke="black" stroke-width="1.5" stroke-linejoin="round"/>
    </svg>`;
    cursor.style.cssText = `
      position: fixed; z-index: 999999; pointer-events: none;
      width: 24px; height: 24px;
      transition: left 0.1s, top 0.1s;
      filter: drop-shadow(1px 1px 2px rgba(0,0,0,0.3));
    `;
    cursor.style.left = '0px';
    cursor.style.top = '0px';
    document.body.appendChild(cursor);
    document.addEventListener('mousemove', (e) => {
      cursor.style.left = e.clientX + 'px';
      cursor.style.top = e.clientY + 'px';
    });
  });
}

Rufe injectCursor(page) nach jeder Seitennavigation auf, da die Überlagerung bei Navigation zerstört wird.

4. Mausbewegung (Mouse Movement)

Teleportiere den Zeiger niemals. Bewege ihn vor dem Klicken zum Ziel:

async function moveAndClick(page, locator, label, opts = {}) {
  const { postClickDelay = 800, ...clickOpts } = opts;
  const el = typeof locator === 'string' ? page.locator(locator).first() : locator;
  const visible = await el.isVisible().catch(() => false);
  if (!visible) {
    console.error(`WARNUNG: moveAndClick übersprungen - "${label}" nicht sichtbar`);
    return false;
  }
  try {
    await el.scrollIntoViewIfNeeded();
    await page.waitForTimeout(300);
    const box = await el.boundingBox();
    if (box) {
      await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps: 10 });
      await page.waitForTimeout(400);
    }
    await el.click(clickOpts);
  } catch (e) {
    console.error(`WARNUNG: moveAndClick fehlgeschlagen bei "${label}": ${e.message}`);
    return false;
  }
  await page.waitForTimeout(postClickDelay);
  return true;
}

Jeder Aufruf sollte ein beschreibendes label für Debugging-Zwecke enthalten.

5. Tippen (Typing)

Tippe sichtbar, nicht sofort ausfüllen:

async function typeSlowly(page, locator, text, label, charDelay = 35) {
  const el = typeof locator === 'string' ? page.locator(locator).first() : locator;
  const visible = await el.isVisible().catch(() => false);
  if (!visible) {
    console.error(`WARNUNG: typeSlowly übersprungen - "${label}" nicht sichtbar`);
    return false;
  }
  await moveAndClick(page, el, label);
  await el.fill('');
  await el.pressSequentially(text, { delay: charDelay });
  await page.waitForTimeout(500);
  return true;
}

6. Scrollen (Scrolling)

Verwende sanftes Scrollen anstelle von Sprüngen:

await page.evaluate(() => window.scrollTo({ top: 400, behavior: 'smooth' }));
await page.waitForTimeout(1500);

7. Dashboard-Panning

Wenn du eine Dashboard- oder Übersichtsseite zeigst, bewege den Zeiger über die wichtigsten Elemente:

async function panElements(page, selector, maxCount = 6) {
  const elements = await page.locator(selector).all();
  for (let i = 0; i < Math.min(elements.length, maxCount); i++) {
    try {
      const box = await elements[i].boundingBox();
      if (box && box.y < 700) {
        await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps: 8 });
        await page.waitForTimeout(600);
      }
    } catch (e) {
      console.warn(`WARNUNG: panElements übersprungen Element ${i} (Selektor: "${selector}"): ${e.message}`);
    }
  }
}

8. Untertitel (Subtitles)

Füge eine Untertitel-Leiste am unteren Rand des Viewports ein:

async function injectSubtitleBar(page) {
  await page.evaluate(() => {
    if (document.getElementById('demo-subtitle')) return;
    const bar = document.createElement('div');
    bar.id = 'demo-subtitle';
    bar.style.cssText = `
      position: fixed; bottom: 0; left: 0; right: 0; z-index: 999998;
      text-align: center; padding: 12px 24px;
      background: rgba(0, 0, 0, 0.75);
      color: white; font-family: -apple-system, "Segoe UI", sans-serif;
      font-size: 16px; font-weight: 500; letter-spacing: 0.3px;
      transition: opacity 0.3s;
      pointer-events: none;
    `;
    bar.textContent = '';
    bar.style.opacity = '0';
    document.body.appendChild(bar);
  });
}

async function showSubtitle(page, text) {
  await page.evaluate((t) => {
    const bar = document.getElementById('demo-subtitle');
    if (!bar) return;
    if (t) {
      bar.textContent = t;
      bar.style.opacity = '1';
    } else {
      bar.style.opacity = '0';
    }
  }, text);
  if (text) await page.waitForTimeout(800);
}

Rufe injectSubtitleBar(page) zusammen mit injectCursor(page) nach jeder Navigation auf.

Verwendungsmuster:

await showSubtitle(page, 'Schritt 1 - Anmelden');
await showSubtitle(page, 'Schritt 2 - Dashboard-Übersicht');
await showSubtitle(page, '');

Richtlinien:

  • Halte den Untertitel kurz, idealerweise unter 60 Zeichen.
  • Verwende das Format Schritt N - Aktion für Konsistenz.
  • Lösche den Untertitel während längerer Pausen, in denen die UI für sich selbst sprechen kann.

Skriptvorlage

'use strict';
const { chromium } = require('playwright');
const path = require('path');
const fs = require('fs');

const BASE_URL = process.env.QA_BASE_URL || 'http://localhost:3000';
const VIDEO_DIR = path.join(__dirname, 'screenshots');
const OUTPUT_NAME = 'demo-FEATURE.webm';
const REHEARSAL = process.argv.includes('--rehearse');

// Füge hier injectCursor, injectSubtitleBar, showSubtitle, moveAndClick,
// typeSlowly, ensureVisible und panElements ein.

(async () => {
  const browser = await chromium.launch({ headless: true });

  if (REHEARSAL) {
    const context = await browser.newContext({ viewport: { width: 1280, height: 720 } });
    const page = await context.newPage();
    // Navigiere durch den Ablauf und führe ensureVisible für jeden Selektor aus.
    await browser.close();
    return;
  }

  const context = await browser.newContext({
    recordVideo: { dir: VIDEO_DIR, size: { width: 1280, height: 720 } },
    viewport: { width: 1280, height: 720 }
  });
  const page = await context.newPage();

  try {
    await injectCursor(page);
    await injectSubtitleBar(page);

    await showSubtitle(page, 'Schritt 1 - Anmelden');
    // Anmeldeaktionen

    await page.goto(`${BASE_URL}/dashboard`);
    await injectCursor(page);
    await injectSubtitleBar(page);
    await showSubtitle(page, 'Schritt 2 - Dashboard-Übersicht');
    // Dashboard panning

    await showSubtitle(page, 'Schritt 3 - Hauptarbeitsablauf');
    // Aktionssequenz

    await showSubtitle(page, 'Schritt 4 - Ergebnis');
    // Abschließende Einblendung
    await showSubtitle(page, '');
  } catch (err) {
    console.error('DEMO-FEHLER:', err.message);
  } finally {
    await context.close();
    const video = page.video();
    if (video) {
      const src = await video.path();
      const dest = path.join(VIDEO_DIR, OUTPUT_NAME);
      try {
        fs.copyFileSync(src, dest);
        console.log('Video gespeichert:', dest);
      } catch (e) {
        console.error('FEHLER: Video konnte nicht kopiert werden:', e.message);
        console.error('  Quelle:', src);
        console.error('  Ziel:', dest);
      }
    }
    await browser.close();
  }
})();

Verwendung:

# Phase 2: Probieren (Rehearse)
node demo-script.cjs --rehearse

# Phase 3: Aufnehmen (Record)
node demo-script.cjs

Checkliste vor der Aufnahme

  • Entdeckungsphase abgeschlossen
  • Probieren erfolgreich, alle Selektoren OK
  • Headless-Modus aktiviert
  • Auflösung auf 1280x720 eingestellt
  • Mauszeiger- und Untertitel-Overlays nach jeder Navigation erneut eingefügt
  • showSubtitle(page, 'Schritt N - ...') bei wichtigen Übergängen verwendet
  • moveAndClick für alle Klicks mit beschreibenden Labels verwendet
  • typeSlowly für sichtbare Eingaben verwendet
  • Keine stillen Catches; …

… (gekürzt für Übersetzung)

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Verwandte Skills