Skip to content

feat: add a Timeline panel of what each action re-ran - #29

Open
lxsmnsyc wants to merge 3 commits into
feat/warnings-panelfrom
feat/timeline-panel
Open

lxsmnsyc wants to merge 3 commits into
feat/warnings-panelfrom
feat/timeline-panel

Conversation

@lxsmnsyc

@lxsmnsyc lxsmnsyc commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Adds a Timeline tab that lists what each user action made the app do: the signals it wrote, every memo and effect that re-ran, why each one ran, and how long the update took. This PR is based on #28.

timeline-detail

Changes

  • The toolbar turns on the attribution engine from solid-js/attribution when it mounts, with log: false and values: 'full', so the first action is recorded even while the dock is closed.
  • The panel reads the rerun, effect, flush, interaction, hold and navigation records from OBSERVE.records. Records of one interaction share its origin object, which is the key they are grouped by.
  • Re-runs that no interaction caused, such as a timer or a promise, are grouped until the flush that ends them.
  • Each row shows the writes, the number of re-runs and the total time. The detail shows the input delay, handler time and settle time, the writes with their old and new values, and each re-run.
  • Opening a re-run shows its cause chain, such as report-doubled ← doubled changed ← write count 1 → 2. A run that produced the same value is marked as no change.
  • The eye on a re-run opens the node in the reactivity graph.
  • The copy button puts a Markdown report of the action on the clipboard. Values of writes with sensitive names are redacted.
  • Interactions that wrote and ran nothing, such as clicks in the toolbar, are left out.
  • Updates no action caused whose writes all went to unnamed signals, or that wrote nothing, are hidden by default. Most come from Solid's own state, such as a Show condition or a Loading boundary settling. A button above the list shows how many there are and brings them back, since an app's own unnamed signal looks the same.
  • Durations under 0.1ms read as <0.1ms, since browsers round the clock.
  • The panel keeps 100 actions and 300 re-runs per action.
  • The panel also reads the call record. A call joins the action whose origin it carries, and lists with its method, status and time. Clicking it opens the call in the server-function panel.
  • A click that only calls the server looks idle when its interaction record arrives. Idle interactions are kept aside, so a later call can attach to its own.

Example report

# Solid update: `click on button#increment-count "count 1 doubled 2"`

- Outcome: committed
- Input delay: 0.30ms
- Settled: 0.50ms after the event

## Writes

- write `count` 1 → 2

## Re-runs, in order

- memo `doubled` (<0.1ms)
  - ← write `count` 1 → 2
- effect `report-doubled` (<0.1ms, callback <0.1ms)
  - ← `doubled` changed
    - ← write `count` 1 → 2

Testing

  • Unit tests in src/dev-toolbar/timeline/tracker.test.ts feed records by hand and cover grouping, outside updates, idle interactions, the entry limit, the snapshot copies and the report.
  • A second e2e test clicks a button that only calls the server, checks the call under its action, and opens it in the server-function panel.
  • A new e2e test clicks the counter, checks the row, the write, the three re-runs, the cause chain, and the jump to the graph.
  • pnpm test, pnpm typecheck, pnpm format:check, pnpm build, pnpm check and the full e2e suite pass locally.

🤖 Generated with Claude Code

- A Timeline tab lists each user action, such as a click or a key press, with the writes it made, the memos and effects that re-ran, and how long the update took. Updates from timers or promises are rows of their own.
- A re-run shows the changes that caused it, down to the write that started them. A re-run that produced the same value is marked as no change.
- The detail shows the input delay, handler time and settle time from the interaction record, and links each re-run to the reactivity graph.
- The copy button puts a report of the action on the clipboard for an agent.
- The toolbar turns on the attribution engine of solid-js when it mounts, with its console log off.
- Clicks in the toolbar change nothing in the app, so they are left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lxsmnsyc and others added 2 commits October 24, 2026 22:02
- The timeline reads the `call` record, whose origin is the interaction's own object, or a frame that names it. A call joins the action it ran for.
- A click that only calls the server writes nothing, so its interaction record looks idle. Idle interactions are kept aside, and a later call attaches to its own.
- The detail lists the calls with method, status and time, and the report has a Server calls section.
- A call opens in the server-function panel. The timeline keeps that panel's key for the call, since the runtime's live object for it is not held by anything once it settled.
- The test fixture has a "call server" button that sends a call record from its handler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Updates no action caused, whose writes all went to unnamed signals or that wrote nothing, come mostly from Solid's own state, such as a Show condition or a Loading boundary settling. They pushed real actions down the list.
- They are hidden by default. A button above the list shows how many there are and brings them back, since an app's own unnamed signal looks the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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