#11

Three issues recording Next.js flows (with local patches)

Opendanielsgriffinopened 8mo ago

I've been using webreel to record Next.js App Router flows and ran into three issues. I created local patches for the first two with help from Claude Code (Opus 4.6) — sharing them here in case they're useful upstream.

1. typeText hangs with charDelay: 0

When using charDelay: 0 for bulk paste behavior, the recording hangs indefinitely. waitForNextTick() never resolves because Input.insertText (bulk mode) doesn't produce the per-character rendering events the tick cursor waits for.

Patch (@webreel/core/dist/actions.js, around line 538): skip waitForNextTick() when delayMs === 0.

if (delayMs > 0) {
  await waitForNextTick();
}

2. Stale video frames during client-side navigation

During client-side navigation (Next.js Link clicks), Page.captureScreenshot in the capture loop returns stale compositor output. The video shows the pre-navigation page even though step-level "action": "screenshot" captures render correctly. The --run-all-compositor-stages-before-draw flag is already in the Chrome launch args but isn't sufficient for client-side transitions.

Patch (@webreel/core/dist/recorder.js, in captureLoop): add a requestAnimationFrame sync before each screenshot.

await client.Runtime.evaluate({
  expression: "new Promise(r => requestAnimationFrame(r))",
  awaitPromise: true,
});
const screenshotResult = await client.Page.captureScreenshot({...});

3. Orphaned Chrome processes after hang/kill

When webreel hangs or is killed, chrome-headless-shell processes survive and block the debug port on future runs. The kill() method in chrome.js sends SIGTERM but doesn't wait for exit, and there are no global signal handlers in the standard record command (only in --watch mode). Working around this with a wrapper script that runs pkill -f "chrome-headless" before/after each run.


Claude Code (Opus 4.6) created these patches to get it working. Happy to open PRs if any of these are worth upstreaming.

Environment

  • webreel 0.1.4
  • macOS (Apple Silicon)
  • Next.js 16.1.6 with App Router, React 19

Activity

1
hackonhari6mo ago

I hit something related while recording multi-page flows with webreel 0.1.4 + Chrome v142 — the raceStop() method in recorder.js has no timeout on CDP calls, so Page.captureScreenshot hangs indefinitely after any navigation (full reload or SPA). The capture loop freezes and frames are lost.

What happens: After navigation, the next CDP call in the loop (either Runtime.evaluate for cursor tick or Page.captureScreenshot) hangs without a timeout. The raceStop only races against stoppedPromise, which never resolves until the recorder is explicitly stopped. The loop just stalls.

Workaround I added: A 2s timeout on raceStop:

async raceStop(promise) {
    const stopped = this.stoppedPromise.then(() => null);
    const timeout = new Promise((_, rej) => setTimeout(
        () => rej(new Error("CDP call timeout (2s) — likely post-navigation hang")), 2000));
    const result = await Promise.race([promise, stopped, timeout]);
    return result;
}

This combined with your rAF sync fix solved the multi-page issue for me. Both are needed — the rAF sync prevents stale compositor frames, the timeout prevents the loop from stalling on hung CDP calls.

Also filed #27 for a separate frameSlots cap issue I found during the same debugging session.