Skip to content

feat: playwright recorder for example-app walkthroughs#108

Open
thephez wants to merge 11 commits into
mainfrom
feat/walkthrough
Open

feat: playwright recorder for example-app walkthroughs#108
thephez wants to merge 11 commits into
mainfrom
feat/walkthrough

Conversation

@thephez

@thephez thephez commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a Playwright-based walkthrough recorder that produces narrated .webm screencasts and a preview .png for example apps. The recorder drives the live app with an on-screen cursor and caption overlay, optionally signing in with a testnet mnemonic to show full read/write flows.

What's included

  • scripts/walkthroughs/record.mjs — the recorder. One run<App> flow per app, a shared cursor/caption driver, graceful SIGINT/SIGTERM handling that still finalizes a partial video, and progress logging.
  • scripts/walkthroughs/captions/*.json — overlay copy per app, kept as plain JSON so wording can change without touching recorder code (caption files are required — no inline fallback).
  • package.jsonwalkthrough:<app> npm scripts for each app.
  • scripts/walkthroughs/README.md — usage, env vars, and output layout.
  • Committed dashnote recording + preview under walkthrough/dashnote/.

Credentials & safety

The recorder loads .env.walkthrough when present and accepts a mnemonic via WALKTHROUGH_MNEMONIC / PLATFORM_MNEMONIC (and fallbacks). Sign-in uses .fill() into masked fields (native password inputs or color: transparent), so the mnemonic never renders on screen. Without credentials, apps record in read-only mode via --no-login. All identifiers shown are public testnet data.

Summary by CodeRabbit

  • New Features

    • Added walkthrough recording for DashMint Lab, Dashnote, Dashnote Starter, DashRate, and TokenOps.
    • Supports authenticated and read-only walkthroughs, custom URLs, headed browser mode, and configurable environments.
    • Generates captioned walkthrough videos and preview images for each application.
  • Documentation

    • Added setup and usage instructions, including authentication, captions, output locations, and troubleshooting guidance.

thephez and others added 11 commits July 15, 2026 12:27
Adds scripts/walkthroughs/record.mjs to record narrated walkthrough
videos and preview images for dashnote, dashnote-starter, and
dashmint-lab, with caption copy in scripts/walkthroughs/captions/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The recorder previously printed nothing between browser launch and final output, making long runs (especially dashmint-lab) look hung. Add a timestamped logStep helper and emit it at each major phase: browser launch, navigation, key waits, login, and tab switches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… wait

Press Ctrl+C (SIGINT) or SIGTERM during recording to finalize the partial video. The shutdown path now tolerates already-closed browsers and prefers video.saveAs over manual copy.

Caption restyled as a dark glass card pinned upper-right so it reads on dark UIs without covering the heading and tabs on the left.

Wait for any card article rather than a specific card name so the recorder doesn't depend on contract contents.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reorder the dashmint-lab walkthrough so tab navigation (All / Marketplace / Sort) and the Buy hover demo run before login. After signing in, walk through the Yours tab and animate the owned-card overflow menu (Transfer, Copy ID, View on Explorer, Burn).

Center the caption within the content area to the right of the 208px sidebar so the heading and account balance stay visible. Theme the caption with an amber accent that matches the app.

Extract dashmint-lab caption copy into scripts/walkthroughs/captions/dashmint-lab.json so it can be edited without touching the recorder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Caption files are now the sole source of caption strings. The recorder errors
out when the captions JSON is missing or a referenced key is absent, and the
dashnote-starter captions file ships alongside the existing ones.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a runDashrate driver covering public aggregate browsing, identity-owned review create/edit, and review history, with caption copy in captions/dashrate.json and a walkthrough:dashrate npm script.

Add scrollToLocator, scrollToFirstLocator, and scrollToTop driver helpers, a configurable per-app viewport (dashrate records at 1440x1000), and pointer-events: none on the caption overlay so it does not intercept clicks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dashmint-lab walkthrough now performs one real card mint so the token-paid create flow is visible, then visits the Tokens tab to show the signed-in identity's DashMint balance and transfer form. Captions are updated to narrate the mint and Tokens steps; the browse-only (--no-login) recording is unchanged. The recorder still avoids token transfers, purchases, burns, and contract registration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every app now outputs to a per-app directory under the repo-root walkthrough/ tree instead of beside each app, and TokenOps drops its flat-output special case.
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Playwright CLI that records captioned walkthrough videos for five applications, supporting authentication options, browser configuration, conditional UI flows, previews, and persisted video artifacts.

Changes

Walkthrough recording

Layer / File(s) Summary
CLI configuration and caption contracts
package.json, scripts/walkthroughs/README.md, scripts/walkthroughs/captions/*, scripts/walkthroughs/record.mjs
Adds npm commands, CLI and environment options, credential extraction, caption validation, setup documentation, and caption manifests for five applications.
Browser loading and walkthrough interaction
scripts/walkthroughs/record.mjs
Loads Playwright, launches Chromium, injects the caption/cursor overlay, and provides locator-based interaction helpers.
Application walkthrough runners
scripts/walkthroughs/record.mjs
Adds conditional authenticated and read-only flows for Dashnote Starter, Dashnote, and DashMint Lab.
Rating and governance walkthrough runners
scripts/walkthroughs/record.mjs
Adds DashRate review/settings flows and TokenOps action, governance, authority, and settings flows.
Recording lifecycle and artifact persistence
scripts/walkthroughs/record.mjs
Coordinates recording, interruption handling, preview capture, WebM persistence, cleanup, progress logging, and error handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NpmScripts
  participant RecordCLI
  participant Playwright
  participant TargetApp
  participant WalkthroughOutput
  NpmScripts->>RecordCLI: invoke app walkthrough command
  RecordCLI->>TargetApp: open configured URL
  RecordCLI->>Playwright: launch Chromium and enable video recording
  Playwright->>TargetApp: execute application walkthrough
  RecordCLI->>Playwright: capture preview and finalize video
  Playwright->>WalkthroughOutput: save WebM and PNG artifacts
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a Playwright recorder for example-app walkthroughs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/walkthrough

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thephez

thephez commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/walkthroughs/README.md`:
- Around line 8-13: Add the missing TokenOps walkthrough documentation to the
command list by including the walkthrough:token-ops command and its
token-ops.json caption manifest alongside the existing walkthrough entries in
scripts/walkthroughs/README.md.

In `@scripts/walkthroughs/record.mjs`:
- Around line 1342-1350: Update the cleanup logic in the walkthrough recording
flow to remove tmpVideoDir after the temporary video has been processed,
including when processing fails if cleanup is handled in a finally path. Reuse
the existing tmpVideoDir symbol and ensure its contents are deleted without
affecting targetWebm or targetPreview.
- Around line 1393-1404: Update the video persistence handling around
video.saveAs and its copy fallback so a failure of both operations is retained
rather than only logged. Preserve the original persistence error or combined
failure, allow the surrounding recording cleanup to complete, then rethrow it so
the command does not report success without the video artifact.
- Around line 608-677: Default authenticated walkthroughs must preserve the
documented no-write contract: in scripts/walkthroughs/record.mjs lines 608-677,
guard the Dashnote create/update/delete flow behind explicit write consent or
stop before it; apply the same consent guard or early stop to the DashRate
review save flow at lines 1115-1148; update scripts/walkthroughs/README.md lines
35-39 to accurately describe the no-write behavior or document the opt-in write
mode.
- Around line 95-97: Update the --env-file handling in the argument parser to
validate that argv[++i] exists before passing it to path.resolve. Preserve the
intended “--env-file requires a value” error for missing values, then resolve
and assign the validated value to args.envFile.
- Around line 924-931: Update the Login handling around loginButtons to use the
checked locator helper, and guard both the absent-button and null-boundingBox
cases before accessing coordinates. Preserve the no-login recording flow by
skipping the cursor click when no visible Login button is available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 85d207d3-8289-4ade-a956-24c4e1e84ab8

📥 Commits

Reviewing files that changed from the base of the PR and between 6575b63 and 6a7d0ac.

⛔ Files ignored due to path filters (2)
  • walkthrough/dashnote/dashnote-preview.png is excluded by !**/*.png
  • walkthrough/dashnote/dashnote-walkthrough.webm is excluded by !**/*.webm
📒 Files selected for processing (8)
  • package.json
  • scripts/walkthroughs/README.md
  • scripts/walkthroughs/captions/dashmint-lab.json
  • scripts/walkthroughs/captions/dashnote-starter.json
  • scripts/walkthroughs/captions/dashnote.json
  • scripts/walkthroughs/captions/dashrate.json
  • scripts/walkthroughs/captions/token-ops.json
  • scripts/walkthroughs/record.mjs

Comment on lines +8 to +13
```sh
npm run walkthrough:dashmint-lab
npm run walkthrough:dashnote
npm run walkthrough:dashnote-starter
npm run walkthrough:dashrate
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the TokenOps walkthrough.

The available walkthrough:token-ops command and token-ops.json caption manifest are both omitted.

Proposed fix
 npm run walkthrough:dashnote-starter
 npm run walkthrough:dashrate
+npm run walkthrough:token-ops
 scripts/walkthroughs/captions/dashrate.json
+scripts/walkthroughs/captions/token-ops.json

Also applies to: 57-62

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/walkthroughs/README.md` around lines 8 - 13, Add the missing TokenOps
walkthrough documentation to the command list by including the
walkthrough:token-ops command and its token-ops.json caption manifest alongside
the existing walkthrough entries in scripts/walkthroughs/README.md.

Comment on lines +95 to +97
} else if (arg === '--env-file') {
args.envFile = path.resolve(argv[++i]);
if (!args.envFile) throw new Error('--env-file requires a value');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate the --env-file value before resolving it.

Without a following value, path.resolve(undefined) throws its own TypeError, bypassing the intended CLI error.

Proposed fix
     } else if (arg === '--env-file') {
-      args.envFile = path.resolve(argv[++i]);
-      if (!args.envFile) throw new Error('--env-file requires a value');
+      const envFile = argv[++i];
+      if (!envFile) throw new Error('--env-file requires a value');
+      args.envFile = path.resolve(envFile);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (arg === '--env-file') {
args.envFile = path.resolve(argv[++i]);
if (!args.envFile) throw new Error('--env-file requires a value');
} else if (arg === '--env-file') {
const envFile = argv[++i];
if (!envFile) throw new Error('--env-file requires a value');
args.envFile = path.resolve(envFile);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/walkthroughs/record.mjs` around lines 95 - 97, Update the --env-file
handling in the argument parser to validate that argv[++i] exists before passing
it to path.resolve. Preserve the intended “--env-file requires a value” error
for missing values, then resolve and assign the validated value to args.envFile.

Comment on lines +608 to +677
if (page.walkthroughCredentials) {
const newButton = page.getByRole('button', { name: 'New note' });
if ((await newButton.count()) === 1) {
const uniqueTitle = `Walkthrough note ${Date.now().toString().slice(-6)}`;
await driver.captionKey('createNote');
await driver.clickLocator(newButton);
await page.getByLabel('Title').waitFor({
state: 'visible',
timeout: 10000,
});
await driver.clickLocator(page.getByLabel('Title'));
await page.keyboard.type(uniqueTitle, { delay: 35 });
await driver.clickLocator(page.getByLabel('Body'));
await page.keyboard.type(
'Created by the walkthrough recorder so we can show create, update, revision metadata, and delete.',
{ delay: 20 },
);
await driver.moveCursor(1040, 740);
await driver.delay(1200);
await driver.captionKey('saveCreate');
await driver.clickLocator(
page.getByRole('button', { name: 'Create note' }),
);
await page.getByText('Note created.', { exact: true }).first().waitFor({
state: 'visible',
timeout: 90000,
});
await page.getByText(uniqueTitle, { exact: true }).first().waitFor({
state: 'visible',
timeout: 30000,
});
await driver.captionKey('createdMetadata');
await driver.moveCursor(508, 188);
await driver.delay(900);
await driver.moveCursor(688, 739);
await driver.delay(900);

await driver.captionKey('updateNote');
await driver.clickLocator(page.getByLabel('Body'));
await page.keyboard.press('End');
await page.keyboard.type(
'\n\nUpdated during the walkthrough to show revision changes.',
{ delay: 20 },
);
await driver.clickLocator(page.getByRole('button', { name: 'Save' }));
await page.getByText('Note saved.', { exact: true }).first().waitFor({
state: 'visible',
timeout: 90000,
});
await driver.captionKey('updatedMetadata');
await driver.moveCursor(506, 188);
await driver.delay(1200);

await driver.captionKey('deleteNote');
await driver.clickLocator(page.getByRole('button', { name: 'Delete' }));
await page.getByRole('dialog', { name: 'Delete note' }).waitFor({
state: 'visible',
timeout: 10000,
});
await driver.clickLocator(
page
.getByRole('dialog', { name: 'Delete note' })
.getByRole('button', { name: 'Delete' }),
);
await page.getByText('Note deleted.', { exact: true }).first().waitFor({
state: 'visible',
timeout: 90000,
});
await driver.captionKey('deletedNote');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Default authenticated walkthroughs violate the documented no-write contract.

  • scripts/walkthroughs/record.mjs#L608-L677: stop before creating, updating, or deleting the Dashnote document, or require explicit write consent.
  • scripts/walkthroughs/record.mjs#L1115-L1148: stop before saving DashRate reviews, or require the same explicit consent.
  • scripts/walkthroughs/README.md#L35-L39: keep the no-write guarantee accurate or document the opt-in write mode.
📍 Affects 2 files
  • scripts/walkthroughs/record.mjs#L608-L677 (this comment)
  • scripts/walkthroughs/record.mjs#L1115-L1148
  • scripts/walkthroughs/README.md#L35-L39
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/walkthroughs/record.mjs` around lines 608 - 677, Default
authenticated walkthroughs must preserve the documented no-write contract: in
scripts/walkthroughs/record.mjs lines 608-677, guard the Dashnote
create/update/delete flow behind explicit write consent or stop before it; apply
the same consent guard or early stop to the DashRate review save flow at lines
1115-1148; update scripts/walkthroughs/README.md lines 35-39 to accurately
describe the no-write behavior or document the opt-in write mode.

Comment on lines +924 to +931
const loginButtons = await page
.getByRole('button', { name: 'Login' })
.all();
const loginBox = await loginButtons[loginButtons.length - 1].boundingBox();
await driver.clickCursor(
loginBox.x + loginBox.width / 2,
loginBox.y + loginBox.height / 2,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use the checked locator helper for the read-only Login path.

An absent Login button makes the last array entry undefined; a hidden button makes loginBox null. Either condition crashes the no-login recording.

Proposed fix
-    const loginButtons = await page
-      .getByRole('button', { name: 'Login' })
-      .all();
-    const loginBox = await loginButtons[loginButtons.length - 1].boundingBox();
-    await driver.clickCursor(
-      loginBox.x + loginBox.width / 2,
-      loginBox.y + loginBox.height / 2,
+    await driver.clickLastLocator(
+      page.getByRole('button', { name: 'Login' }),
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const loginButtons = await page
.getByRole('button', { name: 'Login' })
.all();
const loginBox = await loginButtons[loginButtons.length - 1].boundingBox();
await driver.clickCursor(
loginBox.x + loginBox.width / 2,
loginBox.y + loginBox.height / 2,
);
await driver.clickLastLocator(
page.getByRole('button', { name: 'Login' }),
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/walkthroughs/record.mjs` around lines 924 - 931, Update the Login
handling around loginButtons to use the checked locator helper, and guard both
the absent-button and null-boundingBox cases before accessing coordinates.
Preserve the no-login recording flow by skipping the cursor click when no
visible Login button is available.

Comment on lines +1342 to +1350
const tmpVideoDir = path.join(
os.tmpdir(),
`${config.fileStem}-video-${Date.now()}`,
);
const targetWebm = path.join(outDir, `${config.fileStem}-walkthrough.webm`);
const targetPreview = path.join(outDir, `${config.fileStem}-preview.png`);

await fs.mkdir(outDir, { recursive: true });
await fs.mkdir(tmpVideoDir, { recursive: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove the temporary video directory during cleanup.

Every run leaves the original recording under os.tmpdir(), so repeated walkthroughs accumulate large WebM files.

Proposed cleanup
     try {
       await browser.close();
     } catch (err) {
       logStep(`browser.close failed: ${err.message}`);
     }
+    try {
+      await fs.rm(tmpVideoDir, { recursive: true, force: true });
+    } catch (err) {
+      logStep(`temporary video cleanup failed: ${err.message}`);
+    }

Also applies to: 1406-1415

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/walkthroughs/record.mjs` around lines 1342 - 1350, Update the cleanup
logic in the walkthrough recording flow to remove tmpVideoDir after the
temporary video has been processed, including when processing fails if cleanup
is handled in a finally path. Reuse the existing tmpVideoDir symbol and ensure
its contents are deleted without affecting targetWebm or targetPreview.

Comment on lines +1393 to +1404
if (video) {
try {
await video.saveAs(targetWebm);
} catch (err) {
logStep(`video.saveAs failed: ${err.message}`);
try {
const source = await video.path();
await fs.copyFile(source, targetWebm);
} catch (copyErr) {
logStep(`video copy fallback failed: ${copyErr.message}`);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail the recording when the video cannot be persisted.

If both saveAs and the copy fallback fail, the error is only logged. The command then reports Recorded and exits successfully even though the promised artifact is absent. Retain the failure, finish cleanup, and then throw it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/walkthroughs/record.mjs` around lines 1393 - 1404, Update the video
persistence handling around video.saveAs and its copy fallback so a failure of
both operations is retained rather than only logged. Preserve the original
persistence error or combined failure, allow the surrounding recording cleanup
to complete, then rethrow it so the command does not report success without the
video artifact.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant