Skip to content

AI chat: a real 10,000-message session ​

A real Claude Code session: 10,350 messages. Only the few rows you can see are in the page, and a message's text loads when you scroll to it. The strip above the chat counts both as you go.

The chat is built with the Infinite Malleability Kit: every part is an ivue class you can replace from outside, without editing a file. It runs on the same scroller as the million-row example.

What to try ​

  • Scroll anywhere. Unloaded rows render as placeholders sized to the scroller's running estimate, so a page arriving above the viewport does not move the row you are reading. The request log in the strip names each page, its bytes and its time.
  • Expand a tool call. A run of tool calls contracts to one batch row; the batch opens to its calls, and each call opens to its full input and output — a shell command with stdout and stderr, an edit as a diff, a file read with line numbers, a subagent with its whole thread. Nothing is truncated beyond the sample's own cut marker, and "show everything" lifts the card's cap.
  • Type a message. The reply is a replay of a real assistant turn from the session, streamed at the picked model's pace: thinking with a live counter, tokens, tool calls that start and resolve in place while the bottom stays pinned — until you scroll up, when it stops pinning.
  • Open the index. Every message on one line, filtered by role and by whether it holds tool calls, searchable. Click selects, shift-click selects a range, ctrl-click toggles; the selection survives a filter change, and exports as Markdown, plain text or JSONL in thread order.
  • Open your own session. Any ~/.claude/projects/*/*.jsonl parses as a stream in this tab, through the same scrub the sample went through. Nothing is uploaded.
  • Select and copy across the fold. Drag a selection past the bottom edge and copy: the selection is a range over the data, so it reaches rows the DOM never held at the same time.

The shape ​

One index, many pages. The index (index.json) carries one small row per message: id, role, a preview, the tool count, the time. It is what the side panel filters and what the chat renders as stubs. Content pages of 200 messages are fetched when the window reaches them, one page of margin on each side, never twice.

A message is a list of parts: text, thinking, a tool call, a batch of tool calls, an attachment, a system record. Rendering is a registry, not a switch: a part kind names its component, a tool name names its card, and a name nobody mapped falls to a generic card, so a record the parser has not seen never breaks the page.

One clock. Every loader's elapsed label is a getter over one ticking now, and the interval runs only while something is pending. A thousand pending-looking rows cost one timer, and a counter never ticks after its part is done.

Loading…

The parts and the tool calls ​

Loading…

The sample ​

The sample is built from a real session file by docs_v2/scripts/chat-sample.ts: the conversation records are kept, the session's bookkeeping (about a third of the records and most of the bytes) is dropped, emails, key-shaped strings, secret assignments, PEM blocks and home paths are replaced, tool outputs are cut in the middle with a marker that says how much was removed, and images are kept within a budget. The build refuses to write a page in which a forbidden pattern survives. The same scrub runs in the browser over a session you open yourself, before a character renders.

Released under the MIT License.