Feed RENDER-layer rewrite: artifact cards, sealed-line streaming highlight, ANSI output#524
Open
Juliusolsson05 wants to merge 23 commits into
Open
Feed RENDER-layer rewrite: artifact cards, sealed-line streaming highlight, ANSI output#524Juliusolsson05 wants to merge 23 commits into
Juliusolsson05 wants to merge 23 commits into
Conversation
Approved design for the full feed painter rewrite: artifact view-model layer normalizing committed + live planes, one shared component kit, sealed-line streaming code highlighting, ANSI-aware command output, per-tool cards for every Claude/Codex artifact family. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Grounded in a four-way audit + first-hand reads of the painter, streaming path, and kit surfaces. Spec amended: hljs v11 has no continuation API, so StreamingCodeBlock is stateless-per-line with finalize repair. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Command output has never been ANSI-aware anywhere in the feed — colored test/build output rendered as literal escape garbage. parseAnsi handles SGR color/weight (16/256/24-bit), collapses \r progress rewrites, and strips cursor/OSC noise; AnsiText paints spans with the same luminance-branched palette the terminal panes use (readXtermTheme), so feed output and raw terminal output share one color dialect. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sealed lines highlight once and cache by index (append cost = new bytes, not total bytes); only the live tail line re-tokenizes per delta. blockKey deliberately excludes the language so a late fence info-string re-highlights once instead of remounting. Uses toHighlightLanguage to bridge the Monaco-vocabulary names (typescriptreact/shell) that hljs.getLanguage doesn't know. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r-delta Monaco The open-fence branch rebuilt a full Monaco editor+model+LSP document on every streaming token (CodeBlock's Monaco effect lists `code` in its deps) and remounted again when the fence language arrived a delta late (language was baked into the key). Live code now paints highlighted line-by-line with sealed-line caching and zero remounts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nly the tail re-parses StreamingProse memoizes on the full growing string, so during a stream the memo never hit and the whole unified pipeline re-ran per delta: O(len²) per message, and a second fence re-parsed the first through markdown each delta. SegmentedMarkdown seals everything through the last CLOSED fence (identity changes only when a fence closes), streams only the tail, and routes open fences through StreamingCodeBlock. Applied to both live block text (BlockRow) and blockless turn text (Feed semantic-text). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
OutputWell is the single collapsible output region (successor to both TruncatedOutputRow copies — behavior preserved, ANSI + loud byte-cap added). StatusBadge is the one status vocabulary every artifact card flips through. ExpandSection generalizes ToolResultRow's private LazyDetails first-open Monaco gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pies The shared row and Codex's byte-similar private twin both dumped raw bytes into <pre> (escape codes rendered as literal garbage) with no byte cap once expanded. All committed tool output, live Codex function output, and live tool results now flow through the one kit OutputWell: same 3-line/expand behavior, ANSI colors, loud truncation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…m port) Feed.tsx drops from ~1060 to ~640 lines. The scroll family (mount restore, sticky-bottom listener + auto-scroll, older-history load, bootstrap pin-once, scroll-to-latest) moves to useScrollFeedBehaviors; both picker tweens move to usePickerAutoScroll (which returns a stable cancelTween — scroll-to-latest's one legitimate touch point on the tween ref); feed-debug emission moves to useFeedDebugEmission. Logic and WHY comments ported verbatim; zero behavior change intended. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…act) Types only — the normalization contract both planes resolve into and every card renders from. Live ≈ committed becomes structural: same VM, same card, status flip on completion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… one VM commandFromCommitted/commandFromLive and genericFromCommitted/ genericFromLive produce identical CommandArtifact/GenericToolArtifact shapes from ToolUseBlock+result pairs and SemanticLiveBlock+snapshot respectively. Provider wire knowledge moves to pure extractor modules (claude: order-insensitive slash-command envelope parser, verified against real transcripts where tag order VARIES between CC versions; codex: execCommandInput canonical home + result meta reader). Headline logic reuses the existing corpus-hardened smartHeadline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… + exit codes Bash / exec_command / local_shell_call / write_stdin route through routeFamily → CommandCard on BOTH planes: committed interception in Block.tsx (paired result consumed into the card, suppressed below — same predicate both branches per the #442 lesson) and live routing in BlockRow (output streams in via the tool_output_delta fold). Exit codes finally display (red 'exit N'); git-intent cards still win for recognized git commands; empty write_stdin keeps its render-nothing carve-out (renderUnits.isInvisibleWriteStdinBlock mirrors it). CodexExecCommandRow/CodexWriteStdinRow and the duplicated execCommandInput are deleted — extractors.ts is the canonical home. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…he raw-JSON live dump MCP and unknown tools render through ONE card on both planes: JsonToolRow's corpus-hardened per-param presentation preserved, plus StatusBadge, a result slot (JSON slab / ANSI OutputWell) consumed into the card, and — the live upgrade — partial input streaming as growing highlighted JSON via StreamingCodeBlock instead of a raw <pre> dump. The interim Write preview also moves onto StreamingCodeBlock, so live file writes are now line-by-line highlighted ahead of the dedicated FileWriteCard. Suppression is gated by RESULT_CONSUMING_FAMILIES ⊂ landed cards, so un-carded families (apply_patch errors etc.) keep their legacy result rendering — no data loss mid-migration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Slash invocations render as a /name pill + message/args instead of raw <command-name> tag soup, and the separate local-command-stdout user entry renders as a muted ANSI OutputWell (real transcripts carry escape codes in it — verified). The envelope parser is order-insensitive because tag order varies between CC versions. Codex rollout stops dropping exit-0/no-output exec results so the CommandCard can paint the header-only '$ cmd ✓' record; the full corpus runs clean with no divergence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iEdit/apply_patch DiffView succeeds DiffSlab (line core + overflow sizer ported verbatim) adding per-file headers with action badges / ±counts / RTL-truncated paths and first-40/last-40 windowing for huge diffs. DiffCard renders Claude Edit+MultiEdit (diffLines) and Codex apply_patch (grammar parser, moved to extractors with the partial-JSON wrapper decoder) identically on both planes; live paths show the file the moment its JSON literal closes and the diff fills in as it streams. Patch successes finally get a visible ✓ (they rendered NOTHING); patch failures render tinted per-file unified_diffs inside the card. Deleted: EditRow/MultiEditRow, CodexApplyPatchRow, PatchFileHeader, CodexToolRow, the dead patch_apply_end result branch, and the BlockRow live-tool conversion layer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One Write surface for both planes: path + language pill + growing line counter + StatusBadge over a sealed-line StreamingCodeBlock body. The live preview is finally syntax-highlighted (the old one was forced plain because whole-string hljs per delta was quadratic). Finalized large files offer a lazy Monaco editor view. WriteRow deleted; Write results consumed by the card (errors render inside it in red). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ource One card owns both halves of a lookup (the use row was a bare generic card; the result was special-cased by tool name inside the shared ToolResultRow — audit gap #9). Codex parsed read/search execs keep their 'Read N lines from path' summary, now inside CommandCard. Also fixes a cutover regression: opencode's legacy-claimed read/ todowrite results bypass family suppression (isLegacyProviderClaimed), so OpencodeReadResult keeps parsing its tag soup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unifies TodoRow (committed) and SemanticTodoList (live) — near-identical JSX maintained twice. Glyphs preserved (☑ ◐ ☐); the label prop keeps OpenCode's lowercase todowrite header honest and its dispatch special- case dies. Also excises a wholesale duplicated dispatch block that a splice had left in BlockRow (the shadowed copy had a wrong provider tag and no ReadCard route) — BlockRow is now 336 lines, down from 635. ClaudeRows.tsx and TodoList.tsx deleted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…al tools Codex web_search / tool_search / image_generation had bespoke live chips and unrecognizable generic committed rows (the drift the rollout synthesis comment itself flagged). One card each now renders both planes; Claude WebSearch/WebFetch join the web family. The committed synthesized names (web_search/tool_search/image_generation/local_shell) enter the routing table alongside their live *_call kinds — committed local_shell rows get CommandCard too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…yloads + Task spawn synonym One thinking renderer for both planes (the two copies had drifted); redacted_thinking gets an explicit pill instead of silently vanishing; Codex reasoning shows summary + full tracks when distinct. Image blocks accept the spec's url source (http(s)-gated) and get a dependency-free click-to-zoom lightbox. Hook/system entries expose their payload behind a collapsed ExpandSection. Claude spawn matching accepts 'Task' alongside 'Agent' (verified: this CLI generation emits Agent — 184 transcript hits — but newer upstream vocabulary is Task). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…wns the paint renderToolUse leaves the capability contract and all three providers (every family now has a landed card; routeFamily is total, with a loud generic-card fallback instead of a silent non-render). ToolUseRow and the JsonToolRow fallback path in Block.tsx are deleted. renderToolResult stays: it still carries real behavior for unpaired results (Codex meta- keyed rows) and OpenCode's legacy-claimed read tag-soup parser — whose tool_use side now renders the generic card without consuming the result. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stage RENDER now describes the resolve/artifacts/kit architecture, the hooks extraction, and the streaming primitives; stale references to the deleted dual-ladder dispatch (renderToolUse, JsonToolRow fallback, EditRow reuse) are gone per the doc's own fix-stale-docs rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The duplicated-block cleanup kept the copy WITHOUT the Write branch, so a streaming Write fell to the generic card and painted the raw partial JSON buffer instead of the decoded file preview. The whole point of the live path is decoding the action WHILE the JSON streams — extractStreamingWriteInput pulls file_path + unescaped content out of the incomplete buffer; it just wasn't being called. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
What this is
The full RENDER-layer rewrite per
docs/superpowers/specs/2026-07-11-feed-render-layer-rewrite-design.mdand its 21-task implementation plan (both in this branch). The DECIDE layer (ownership ledger, view bridge,FeedRenderItemcontract) is untouched — every change is in the painter and provider renderer modules.Headline fixes
StreamingCodeBlockseals each completed line into a per-line hljs cache; only the tail line re-tokenizes per delta.SegmentedMarkdown: the prefix through the last closed fence parses once; O(len²) → O(len)).AnsiTexton the terminal palette;\rprogress rewrites collapse) and exit codes finally display (redexit Nbadges; Codex silent successes render a$ cmd ✓record instead of vanishing).ui/resolve/+ui/artifacts/): CommandCard, DiffCard (Edit/MultiEdit/apply_patch incl. tinted patch-failure diffs — successes used to render nothing), FileWriteCard (live-highlighted streaming writes), ReadCard, TodoCard, WebCard/ImageGenCard (Codex special tools get committed parity), SlashCommandRow (no more raw<command-name>tag soup), ThinkingBlock (redacted gets an explicit pill), GenericToolCard (the ONE fallback — live MCP tools no longer dump raw partial JSON).ui/hooks/. Deleted: both dispatch ladders' tool routing,JsonToolRow/ToolUseRow/DiffSlab-consumers,ClaudeRows, Codex exec/patch/stdin rows, bothTruncatedOutputRowcopies,renderToolUsefrom the capability contract.TaskalongsideAgent(future-proofing; current transcripts emitAgent).Verification
tscclean on both projects; 481/481 tests pass including the bundle + recording corpora (zero divergence — the one rollout change, silent-success results, was absorbed cleanly). The 1 failed test file is the documented pre-existing emptyhotkeyBindingsuite.electron-vite build+ remote-client build pass (kit stays browser-pure; Monaco remains desktop-lazy).🤖 Generated with Claude Code