Skip to content

Record and stitch

Planning (no I/O):

ts
const DEMO_WALK_ROOT = 'test-results/e2e-demo'
type DemoWalkKind = 'stills' | 'video'

function isDemoVideo(env: Record<string, string | undefined>): boolean
function isDemoScreenshotCapture(
  env?: Record<string, string | undefined>,
): boolean
function demoScreenshotAnimations(
  env: Record<string, string | undefined>,
): 'disabled' | 'allow'
function isDemoClean(env?: Record<string, string | undefined>): boolean
function demoWalkKind(env: Record<string, string | undefined>): DemoWalkKind
function demoWalkKindRoot(env: Record<string, string | undefined>): string
function demoVideoStamp(nowMs: number): string
function demoVideoDir(env: Record<string, string | undefined>, stamp: string): string
function demoChatArtifactPath(
  env: Record<string, string | undefined>,
  artifactsDirAvailable: boolean,
  stamp: string,
): string | null
function evenVideoSize(size: DemoVideoSize): DemoVideoSize
function demoRecordVideoOption(viewport: DemoVideoSize, dir: string): { dir: string; size: DemoVideoSize }
function personaVideoName(persona: string): string
function chapterSegments(chapters: readonly DemoChapter[], endMs: number): DemoChapterSegment[]
function chapterFileRange(
  segment: { startMs: number; endMs: number },
  startedAt: number,
  videoOriginMs: number,
): { startMs: number; endMs: number }

I/O (needs ffmpeg for stitch):

ts
function prepareDemoWalkRoot(env?: NodeJS.ProcessEnv): Promise<void>
function ensureDemoVideoDir(dir: string): Promise<void>
function savePersonaVideo(video: Video | null, persona: string, dir: string): Promise<string | null>
function stitchDemoWalk(meta: DemoRecordMeta): Promise<string | null>
function writeDemoRecordManifest(meta: DemoRecordMeta, files: Record<string, string>): Promise<string>
function writeDemoLatestPointer(
  dir: string,
  env?: Record<string, string | undefined>,
): Promise<void>
function copyWalkForChat(walk: string, env?: NodeJS.ProcessEnv, stamp?: string): Promise<string | null>
function writeWalkTranscript(meta: DemoRecordMeta): Promise<string | null>
function copyTranscriptForChat(transcript: string, env?: NodeJS.ProcessEnv, stamp?: string): Promise<string | null>

CURSOR_ARTIFACTS_DIR is the default artifacts path. Runtime copies use getDemoWalkConfig().artifactsDir.

isDemoScreenshotCapture is false while recordVideo is on — video does not hold the ring, write a still, or call page.screenshot. The the click pulse plays, then the click (pulseDemoCursor, wait the full pulse, then act). demoScreenshotAnimations is "disabled" for the stills PNG.

prepareDemoWalkRoot keeps prior stamps unless E2E_DEMO_CLEAN=1. Stamps land under e2e-demo/stills/<stamp>/ or e2e-demo/video/<stamp>/ from isDemoVideo. Each kind has its own latest.txt. Playwright outputDir must be test-results/playwright (not test-results) so a test run does not delete those stamps.

Released under the MIT License.