feat/load your own recording - #14
Merged
Merged
Conversation
ribdsp
commented
Aug 29, 2026
Owner
- feat: read a recording from a file the person chose
- feat: offer the file loader from the picker and the empty stage
- docs: say how to load a file, and how to record one worth loading
Loading a recording that is not one of the three bundled samples currently means copying JSON into traces/public/recordings/, adding a line to the static manifest, and redeploying. There is no ingestion path at all — no file input, no drop target, no query parameter. The three lines that put a recording into the store already exist and are already shared by both callers, so this adds a second source for them rather than a second way to load: useFileLoader — sibling of useSampleLoader, same three fields. A size guard before file.text() so a mis-dropped multi-gigabyte file is refused in a sentence instead of freezing the tab; JSON.parse's own SyntaxError passed through unwrapped, because it names the byte offset it gave up at and "invalid JSON" does not; loadingName always cleared in a finally. deriveRecordingName — pure, tested, and separate because both halves of its result travel: label into the activity feed, id into a localStorage key and into recordingId in a tool response. A file name is the one string here supplied verbatim by a person and re-validated by nothing downstream, so it is reduced to a closed character set once. SampleLoadError becomes RecordingLoadError and gains source. Both components ended a failure with "Samples live in traces/public/recordings/" — the right next step for a missing sample, and advice that sends someone to look in this repo for their own file. A failure has to be able to say which of the two it was. Also installs a window-level dragover/drop preventDefault. Without it, dropping a JSON file anywhere on the window navigates the tab to file:///…, discarding every marker and hypothesis on the page. That guard is not part of the drop feature and stays whether or not the affordance does. No confirmation dialog before replacing an open recording: the store's loadRecording resets to initialState by design, loading a sample already does exactly this with no prompt, and prompting on one path of two would teach that the two differ.
Two entry points onto `useFileLoader`, plus the drop hazard that exists whether or not a drop target does. The picker grows a fourth listbox row. It has to live inside the `<ul>`: the panel's key handler closes on Tab, so a control after the list would be unreachable by keyboard. That also forced one source of index truth — `activeIndex`, `aria-activedescendant`, the scroll effect and the End bound each indexed `SAMPLE_RECORDINGS` separately, and index 3 was `undefined`, which would have pointed `aria-activedescendant` at no element. They now derive from one `ROWS` array. The empty stage grows a matching row and is itself the drop target, since someone arriving with a file they just recorded has no reason to guess the header holds a menu. Dropping a file on the window navigates the tab to it by default, discarding every marker and hypothesis in the session. `useFileLoader` cancels that at the window level, so a missed drop does nothing. Each component tracks which of the two loaders it last used. Without it a failed file left its alert on screen next to a sample that had since loaded fine, because each hook only clears its own error. The remedial hint branches on the same fact: pointing someone at this repository to find their own file is the wrong next step.
"Drop the downloaded JSON into Traces" described nothing that existed. It now names where the control is. Adds what `npm i rrweb` plus `record()` leaves out, verified against bugbait/src/lib/record.ts rather than assumed: the checkout interval, the fetch patch, the console-event normalisation, the user-agent stamp, and `maskAllInputs`, which is not a preference — without it the recording holds the password and the card number in plain text. Also the gap: a fetch patch sees fetch only, so XMLHttpRequest traffic is invisible to read_network and shows as an empty timeline rather than an error. Every fixture here uses fetch, so none of them expose it. Cost is quoted from the three sample files rather than estimated, and the sandbox claim about replaying someone else's recording is the one already recorded in replay-engine.ts from a spike.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…yer chrome Two changes arrived together in one working tree. They are kept in one commit because page.tsx carries both, and splitting them by hand would leave an intermediate commit that does not build. error-toast.tsx is new: reportError/reportSuccess plus one <ErrorToasts /> mount, so a recording that fails to load says so where the eye already is rather than only inside the panel that asked for it. The sample loader, the file loader and the replay stage all route through it. The rest is chrome. Activity rows are tinted by author and carry an icon chip, so who did what survives video compression; their timestamps now tick in live seconds, because a counter that jumps to "1m ago" and then sits still reads as frozen while an agent is working. The scrubber track goes from 4px to 6px and the handle from 11px to 13px, centred on the track rather than aligned to its top, so it can be aimed with a mouse and seen at 1080p. The empty state names ChatGPT and Chrome beside their icons. tsc clean, 303 tests green across 25 files.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.