Repository navigation
UN-4400 [FEAT] Add the v1 code and structured data components: CodeBlock, JsonView, DiffView, Markdown, CopyButton and CodeEditor - #2447
Conversation
CodeBlock, JsonView (tree and raw, virtualised), DiffView (split and unified), Markdown, CopyButton, CodeEditor (lazy Monaco) and InlineCode under components/ui/code, with pure helpers in lib/code. Adds the code-* and diff-* colour tokens in every mode, refractor, diff and @tanstack/react-virtual as direct dependencies, tests including the 5,000-line JSON case, and usage docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… name Markdown code blocks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ritwik-g
left a comment
There was a problem hiding this comment.
Self-review round 1: review-deep (INITIAL), tip 24f70faad
Review: /unstract:review-deep, because review-shallow's gate refuses this PR (it changes dependencies: package.json, bun.lock). Base main, merge base ed53cfded, base fetched. 39 files, 4,626 lines; the count matches GitHub. Signals: lenses 4, 13 and 14 raised (Markdown parses untrusted text, tests changed, dependencies) plus component lifecycle. The code is self-authored.
Verdict: REQUEST CHANGES
Summary: Critical: 0 · High: 2 · Medium: 13 · Low: 6 · Lenses run: 19/19 (N/A where stated)
Security deep-dive: done (vendored prompt, find sub-task returned; no candidate reached confidence 8, so no filters ran). Boundary sweep: 3 sinks traced (toast titles, the invalid-JSON alert, the clipboard), clean.
Unanchored findings
1. [High] [Lens 2/6]: JsonView re-renders forever ("Too many re-renders") once an invalid document re-renders
- Location:
frontend/src/v1/components/ui/code/JsonView.tsx:L184andL196-L204 - Failure mode: when the model is invalid,
lines = model.ok ? model.lines : []builds a new[]on every render. The render-phase resetif (fold.lines !== lines) setFold(...)therefore loops until React throws. It triggers on valid→invalid text, and on any parent re-render while the text is invalid. - Evidence: two agents reproduced the pattern independently on React 19.2.8.
- Suggested fix: a stable module-level
NO_LINES; test re-renders of invalid text. - Confidence: High. Verification: VERIFIED (standalone repro of the exact pattern).
2. [High] [Lens 5/3]: JsonView text= re-serialises the source, so the tree, Raw and Copy all change numbers
- Location:
frontend/src/v1/components/ui/code/JsonView.tsx:L79-L96,frontend/src/v1/lib/code/json-lines.ts(normalise) - Failure mode:
JSON.parsethenJSON.stringifyrounds integers above 2^53 (4111111111111111111becomes4111111111111111000), turns1.10into1.1, drops duplicate keys and reorders integer-like keys. A 19-digit ID in model output is shown and copied as a different number. - Suggested fix: when
textis given, flatten from the source tokens (lossless), and useJSON.parseonly to validate. - Confidence: High. Verification: VERIFIED (node round trip).
3. [Medium] [Lens 4]: Markdown loads remote images from model output
- Location:
frontend/src/v1/components/ui/code/Markdown.tsx:L157-L159,L219 - Failure mode: a prompt-injected
is fetched on render (exfiltration). The CSPimg-srcallows analytics hosts, and relative URLs send same-origin GETs. - Suggested fix: render images as links unless the caller opts in.
- Confidence: Medium. Verification: VERIFIED that
imgpasses the URL through and for the CSP content.
4. [Medium] [Lens 3]: An unserialisable value shows and copies [object Object] under "Invalid JSON"
- Location:
frontend/src/v1/components/ui/code/JsonView.tsx:L91-L95 - Suggested fix: for
value, show only the error, with its own title, and no raw block or copy. - Confidence: High. Verification: VERIFIED by reading.
5. [Medium] [Lens 1]: The tree's active row can be a closing-bracket line, so aria-activedescendant points to a missing id
- Location:
frontend/src/v1/components/ui/code/JsonView.tsx:L226-L228,L359-L372 - Failure mode: it happens on a click on a
}row, or after a document change. The arrow keys then also start from the wrong row. - Suggested fix: map a closing line to its opening line in one place, and reset
activeon a document change. - Confidence: High. Verification: UNVERIFIED (traced by reading). Does clicking
}leavearia-activedescendantnaming a missing id?
6. [Medium] [Lens 8/3]: CodeEditor stays on "Loading editor" forever when the Monaco CDN fails, and a failed lazy chunk throws past v1
- Location:
frontend/src/v1/components/ui/code/CodeEditor.tsx:L22,L101-L137 - Suggested fix: a timeout that shows an InlineAlert, plus an error boundary around the lazy editor.
- Confidence: High. Verification: VERIFIED by reading
@monaco-editor/react4.7 dist (the catch only logs).
7. [Medium] [Lens 4/16]: Light and Unstract syntax colours fall below 4.5:1 on muted and on the diff tints
- Location:
frontend/src/v1/styles/tokens.json(code-* tokens) - Failure mode: keyword and comment measure about 4.3 on muted; most kinds measure 3.9–4.3 on tinted diff rows. axe's
color-contrastis off in the harness. - Suggested fix: darken the light and Unstract values; add a contrast assertion to
tokens.test.ts. - Confidence: High. Verification: VERIFIED (computed ratios).
8. [Medium] [Lens 9]: DiffView's jsdiff call has no bound
- Location:
frontend/src/v1/lib/code/diff.ts(diffLineswith no options) - Suggested fix: pass jsdiff's
timeout; show a "too large to compare" state when it returnsundefined. - Confidence: Medium. Verification: UNVERIFIED. Does a 5,000-line diff with many edits freeze the tab?
9. [Medium] [Lens 13]: The 5,000-line test asserts a wall-clock budget
- Location:
frontend/src/v1/components/ui/code/JsonView.test.tsx:L228,L245 - Suggested fix: drop the timing assertion and keep the row-count bounds.
- Confidence: High. Verification: VERIFIED by reading.
10. [Medium] [Lens 16]: The README's height rule is wrong for Markdown and CodeEditor
- Location:
frontend/src/v1/components/ui/code/README.md:L46-L50 - Confidence: High. Verification: VERIFIED by reading.
11. [Medium] [Lens 16]: Docs say inline failure text appears; on the default icon-only CopyButton it never does
- Location:
frontend/src/v1/components/ui/code/README.md:L165-L166,CopyButton.tsx:L46-L47 - Confidence: High. Verification: VERIFIED by reading.
12. [Medium] [Lens 7]: JsonView's value-xor-text rule is lost under Omit/Pick; the parts are not exported
- Location:
frontend/src/v1/components/ui/code/JsonView.tsx:L42-L73 - Confidence: High. Verification: VERIFIED (tsc probe).
13. [Medium] [Lens 7]: CodeEditor's lineNumbers and the siblings' showLineNumbers name the same prop differently
- Location:
frontend/src/v1/components/ui/code/CodeEditor.tsx:L55 - Confidence: High. Verification: VERIFIED by reading.
14. [Medium] [Lens 7]: Exporting computeLineDiff freezes the internal row model; tuning constants are exported too
- Location:
frontend/src/v1/components/ui/code/index.ts:L2-L6 - Suggested fix: export a
diffStats(old, new)instead; removeVIRTUALIZE_ABOVE,EXPAND_ALL_UP_TOandCOPY_FEEDBACK_MSfrom the index. - Confidence: High. Verification: VERIFIED (tsc probe).
15. [Medium] [Lens 17]: The tokens.ts header still says colour is "the Midnight Bloom collection minus its palette entries"
- Location:
frontend/src/v1/styles/tokens.ts:L11 - Made stale by:
frontend/src/v1/styles/tokens.json(10 code/diff colours added) - Confidence: High. Verification: VERIFIED (targeted merge sweep).
16. [Low] [Lens 16]: The README says "javascript: never survives"; the href is javascript:void(0)
- Location:
frontend/src/v1/components/ui/code/README.md:L143-L144 - Verification: VERIFIED by reading react-markdown's
uri-transformer.js.
17. [Low] [Lens 16]: The JsonLine.label doc claims a screen-reader use that does not exist; the README says "(or text)" is compared by identity
- Location:
frontend/src/v1/lib/code/json-lines.ts:L24,README.md:L99 - Verification: VERIFIED by grep.
18. [Low] [Lens 13]: Removing skipHtml, or the "hide fold controls" guard, is caught only by the snapshot
- Location:
frontend/src/v1/components/ui/code/Markdown.test.tsx:L80,JsonView.test.tsx:L174-L179 - Verification: VERIFIED by mutation (both survive the behavioural tests).
19. [Low] [Lens 13]: The fixture docstring says "as in frame V14", but autopay, previous_balance and transactions are added to cover branches
- Location:
frontend/src/v1/test/fixtures/json.ts:L6 - Verification: VERIFIED against the V14 inspect output.
20. [Low] [Lens 3]: A throwing text() getter in CopyButton gives no feedback; copyText drops the error name
- Location:
frontend/src/v1/components/ui/code/CopyButton.tsx:L68 - Verification: VERIFIED by reading.
21. [Low] [Lens 9/7]: Notes
- Location:
frontend/src/v1/components/ui/code/CodeEditor.tsx:L122 - Monaco's theme is global, so a dark v1 editor recolours a concurrent v0 editor.
useLineHeightis read once, andestimateSizeis not re-measured (no effect while the token stays 22px).- Virtualised tree items have no
aria-setsize. highlight.tsregisters grammars at import, so the barrel keeps refractor in any chunk that importsInlineCode.- Verification: VERIFIED by reading; the bundle point is UNVERIFIED.
Lens checklist
- Correctness — See findings #5
- Design — See findings #1
- Error handling — See findings #4, #6, #20
- Security — See findings #3, #7
- Data integrity — See findings #2
- Concurrency — See findings #1
- API compatibility — See findings #12, #13, #14
- Reliability — See findings #6
- Performance — See findings #8, #21
- Observability — N/A — client-only UI components, no logs, metrics or network calls
- Operational safety — N/A — no rollout, flag or infrastructure change; v1 is not routed
- LLM/agent — N/A — no prompt, model or agent configuration
- Testing — See findings #9, #18, #19
- Dependencies — Clean — frozen install reproduces the lockfile byte for byte; licences MIT/BSD-3; versions pinned (agent, lenses 6/8/14)
- Style/tooling — Clean —
lint:v1andtypecheck:v1pass at the tip (assessed by the orchestrator) - Doc accuracy — See findings #10, #11, #16, #17
- Cross-document consistency — See findings #15
- On-prem experience — N/A — no chart, values, schema or on-prem build path; v1 is not routed in any deployment
- Scaling & workload lifecycle — N/A — no workload, scaling or lifecycle configuration
Open questions for the author
- None that block judgement.
Assumptions made
- That model-output Markdown will be rendered through
Markdown(its README says so), which makes #3 reachable.
cost: agents=9 wall=08:42 lenses-examined=14 files=39 lines=4626
- JsonView: stable empty lines (an invalid document no longer re-renders forever); JSON text is printed as written, so large numbers, key order and duplicate keys survive; an unserialisable value shows an error with nothing to copy; a closing bracket is never the active tree item. - Markdown: images are links unless loadImages is set. - CodeEditor: showLineNumbers (was lineNumbers); an error after 15 s without Monaco, and an error boundary for a failed editor chunk. - DiffView: jsdiff time limit with a 'Too large to compare' state; diffStats replaces computeLineDiff in the public index. - Code colour tokens darkened to 4.5:1 on every surface, with a test. - Docs corrected; the 5,000-line test no longer asserts wall-clock time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ritwik-g
left a comment
There was a problem hiding this comment.
Self-review round 2: review-deep (FOLLOWUP), tip 2f6994256, delta 24f70faad..2f6994256
Review: /unstract:review-deep (review-shallow refuses this PR: it changes dependencies). Base main, merge base ed53cfded. The delta is 23 files, +935/−302. Self-authored. Security deep-dive: done (full, no candidate at confidence 8). Boundary sweep: 3 sinks traced (toasts, editor/diff/JSON alerts, clipboard), clean.
Verdict: REQUEST CHANGES
Prior context fetched: followup-fetch.sh exited 0 (6 of 6). The "since" boundary is 24f70faad, the round 1 review (5782550598).
Scope change: NO
Prior findings status
- #1 [High] JsonView re-render loop → RESOLVED:
NO_LINESatJsonView.tsx:88; test "survives re-renders…"; mutant killed. - #2 [High]
textrounds numbers → RESOLVED:flattenJsonTextatjson-lines.ts:283; tests injson-lines.test.tsandJsonView.test.tsx. - #3 [Medium] Markdown loads remote images → PARTIALLY RESOLVED: images are links by default (
Markdown.tsx:192), but a linked image now nests<a>in<a>; see new #2. - #4 [Medium]
[object Object]for an unserialisable value → RESOLVED:JsonView.tsx:556. - #5 [Medium] Closing bracket as the active item → RESOLVED:
itemIndexatJsonView.tsx:107; test; mutant killed. - #6 [Medium] Editor stuck on load → PARTIALLY RESOLVED: timeout and boundary added (
CodeEditor.tsx:39,:111), but a slow load past 15 s is discarded for good; see new #3. - #7 [Medium] Contrast below AA → RESOLVED: tokens darkened, test at
tokens.test.ts:150. - #8 [Medium] Unbounded diff → PARTIALLY RESOLVED: timeout added (
diff.ts:76), but the result is machine-dependent and the timeout is untested; see new #4. - #9 [Medium] Wall-clock test → RESOLVED: assertion removed.
- #10 [Medium] README height rule → RESOLVED:
README.md:46. - #11 [Medium] CopyButton failure text → RESOLVED:
CopyButton.tsx:46,README.md:165. - #12 [Medium] Value-xor-text lost under
Omit→ RESOLVED:JsonSourceandJsonViewOptionsexported. - #13 [Medium]
lineNumbersnaming → RESOLVED: renamed toshowLineNumbers. - #14 [Medium]
computeLineDiffand constants in the index → RESOLVED:index.tsnow exportsdiffStats. - #15 [Medium] Stale
tokens.tsheader → RESOLVED:tokens.ts:11. - #16 [Low] "
javascript:never survives" → RESOLVED. - #17 [Low]
labeldoc and "(or text)" → PARTIALLY RESOLVED: the README is fixed; theJsonLine.labeldoc is fixed. - #18 [Low]
skipHtmland fold-controls pins → RESOLVED: tests added;skipHtmlmutant killed. - #19 [Low] Fixture provenance → RESOLVED:
fixtures/json.ts:6. - #20 [Low] CopyButton getter / error name → WAIVED: below the floor; a caller-side getter error, kept as a note.
- #21 [Low] Notes → WAIVED: below the floor (Monaco global theme, line height, setsize, bundle).
New findings
1. [High] [Lens 3/8]: Deeply nested JSON text crashes the host page (a regression in this delta)
- Location:
frontend/src/v1/components/ui/code/JsonView.tsx:L90-L97,frontend/src/v1/lib/code/json-lines.ts:L104-L170 - Failure mode:
JSON.parseaccepts about 1,800+ levels of nesting, but the recursivefromText/printthrow a RangeError, and the text branch has no try. The page unmounts. - Suggested fix: wrap the text branch like the value branch.
- Confidence: High. Verification: VERIFIED (node probe by three agents).
2. [Medium] [Lens 3]: A linked image renders as an <a> inside an <a>
- Location:
frontend/src/v1/components/ui/code/Markdown.tsx:L192-L200 - Suggested fix: render plain text when the image is already inside a link.
- Confidence: High. Verification: UNVERIFIED. Does react-markdown pass the
imginside thea?
3. [Medium] [Lens 8]: A slow editor load past 15 s is discarded, and a later load cannot recover
- Location:
frontend/src/v1/components/ui/code/CodeEditor.tsx:L111-L118,L159-L161 - Suggested fix: keep Monaco mounted behind a "taking a while" alert; only a failed chunk shows the final error. Use neutral wording.
- Confidence: Medium. Verification: VERIFIED by reading.
4. [Medium] [Lens 13/8]: The diff limit is wall-clock only and its test does not pin it
- Location:
frontend/src/v1/lib/code/diff.ts:L76,frontend/src/v1/lib/code/diff.test.ts:L61-L67 - Suggested fix: a deterministic default
maxEditLengthwith the timeout as backstop; test the timeout with a stubbed clock. - Confidence: High. Verification: VERIFIED (mutant M1 survived).
5. [Medium] [Lens 16/17]: The documented JSON-diff recipe (formatJson) rounds large numbers, while JsonView keeps them
- Location:
frontend/src/v1/components/ui/code/DiffView.tsx:L28,README.md:L119 - Suggested fix: export a lossless
formatJsonTextand point the recipe at it for JSON text. - Confidence: High. Verification: VERIFIED by reading.
6. [Medium] [Lens 16]: The README claims the gallery covers "every variant and state"
- Location:
frontend/src/v1/components/ui/code/README.md:L211 - Confidence: High. Verification: VERIFIED by grep.
7. [Low] [Lens 16/13]: Notes
- Location:
frontend/src/v1/styles/tokens.json:L303 - Stale token notes; fixture "token kind" wording; contrast test omits
accent; no test that folds one of two duplicate keys; diff labels show above "Too large"; the README calls the 5,000-line test a "performance case". - Verification: VERIFIED by reading.
Lens checklist
- Correctness — Clean
- Design — See findings #2
- Error handling — See findings #1, #2, #3
- Security — Clean
- Data integrity — See findings #5
- Concurrency — Clean
- API compatibility — Clean
- Reliability — See findings #3, #4
- Performance — Clean
- Observability — N/A — client-only UI, no logs or metrics
- Operational safety — N/A — no rollout or infrastructure change
- LLM/agent — N/A — no prompt or model configuration
- Testing — See findings #4, #7
- Dependencies — N/A — no dependency change in this delta
- Style/tooling — Clean
- Doc accuracy — See findings #5, #6, #7
- Cross-document consistency — See findings #5
- On-prem experience — N/A — no on-prem surface
- Scaling & workload lifecycle — N/A — no workload configuration
Open questions / assumptions
- None.
- JsonView: JSON text nested deeper than the printer can go shows an error instead of crashing the page. - Markdown: an image inside a link is text, never a link in a link. - CodeEditor: after 15 s a 'taking a while' note, with the editor kept mounted so a slow load still appears; an error in the editor shows one error in its place. - Diff: a deterministic limit of 2,000 changed lines, the timeout as a backstop, both tested; labels hidden when too large. - formatJsonText: lossless re-indent for diffing JSON text. - Contrast test covers the active-row surface; doc and note fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ritwik-g
left a comment
There was a problem hiding this comment.
Self-review round 3: review-deep (FOLLOWUP), tip adf196729, delta 2f6994256..adf196729
Review: /unstract:review-deep (review-shallow refuses: the PR changes dependencies). The delta is 17 files, +207/−65. Self-authored. Security deep-dive: done (full, no candidate at confidence 8). Boundary sweep: clean (no new sinks).
Verdict: REQUEST CHANGES
Summary: Critical: 0 · High: 0 · Medium: 3 · Low: 1 · Lenses run: 19/19 (N/A where stated)
Prior context fetched: followup-fetch.sh exited 0 (6 of 6). The "since" boundary is 2f6994256 (round 2 review 5782848493).
Scope change: NO
Prior findings status (round 2)
- #1 [High] Deep JSON text crashes the page → RESOLVED: catch at
JsonView.tsx:97; test; mutant killed. - #2 [Medium] Nested anchors for linked images → RESOLVED:
InsideLinkatMarkdown.tsx:64; test; mutant killed. - #3 [Medium] A slow editor load is discarded → PARTIALLY RESOLVED: the editor stays mounted, but a blocked CDN now never reaches an error and the copy promises it will appear; see new #1.
- #4 [Medium] Diff limit nondeterministic and untested → RESOLVED:
DIFF_MAX_EDIT_LENGTHplus a stubbed-clock test; mutants killed. - #5 [Medium] JSON-diff recipe rounds numbers → RESOLVED:
formatJsonTextexported. - #6 [Medium] Gallery claim → RESOLVED.
- #7 [Low] Notes → RESOLVED (token notes, fixture wording, accent surface, duplicate-key fold test, labels).
New findings
1. [Medium] [Lens 3/8]: A blocked Monaco CDN shows "it appears here once it loads" forever
- Location:
frontend/src/v1/components/ui/code/CodeEditor.tsx:L120-L126,L167-L175 - Failure mode: the loader's promise rejects and never retries, and @monaco-editor/react only logs it, so "failed" is unreachable for the case the timeout exists for.
- Suggested fix: join
loader.init()(neverconfig) and set "failed" on rejection; make the slow copy say to reload if it does not appear. - Confidence: High. Verification: VERIFIED by reading
@monaco-editor/loader(onerror = state.reject, memoised promise).
2. [Medium] [Lens 3]: Valid JSON nested too deep is titled "Invalid JSON", and the catch hides any printer bug
- Location:
frontend/src/v1/components/ui/code/JsonView.tsx:L97-L105,L566-L573;frontend/src/v1/lib/code/json-lines.ts:L306 - Suggested fix: catch only RangeError, with its own title; rethrow anything else.
- Confidence: High. Verification: VERIFIED by reading.
3. [Medium] [Lens 16]: The README still says the diff limit is "more than a second"; "2000 changed lines" counts an edited line twice
- Location:
frontend/src/v1/components/ui/code/README.md:L139-L141;frontend/src/v1/lib/code/diff.ts:L58-L69 - Made stale by:
frontend/src/v1/lib/code/diff.ts:L80 - Confidence: High. Verification: VERIFIED (jsdiff run: 1,000 edited lines diff, 1,001 give up).
4. [Low] [Lens 16/13]: Notes
- Location:
frontend/src/v1/styles/tokens.json:L303 - "Darkened" holds only for light values; the
Markdown.tsxcomment sits above the wrong declaration; the chunk test's comment mentions the timeout; the edit-length boundary has no odd-count case; "Too large" vs "differ too much" wording. - Verification: VERIFIED by reading.
Lens checklist
- Correctness — See findings #1
- Design — Clean
- Error handling — See findings #1, #2
- Security — Clean
- Data integrity — Clean
- Concurrency — Clean
- API compatibility — Clean
- Reliability — See findings #1
- Performance — Clean
- Observability — See findings #2
- Operational safety — N/A — no rollout or infrastructure change
- LLM/agent — N/A — no prompt or model configuration
- Testing — See findings #4
- Dependencies — N/A — no dependency change in this delta
- Style/tooling — Clean
- Doc accuracy — See findings #3, #4
- Cross-document consistency — See findings #3
- On-prem experience — N/A — no on-prem surface
- Scaling & workload lifecycle — N/A — no workload configuration
Open questions / assumptions
- None.
- CodeEditor: watch the Monaco loader (init, never config) so a blocked CDN ends in 'could not load'; the slow note says to reload if it does not appear. - JsonView: valid JSON nested too deep has its own title; only a RangeError is caught, so printer bugs are not hidden (also in formatJsonText). - Diff limit documented as added plus removed lines; odd-count boundary test; README corrected; comment and token-note fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ritwik-g
left a comment
There was a problem hiding this comment.
Self-review round 4: review-deep (FOLLOWUP), tip 730e77109, delta adf196729..730e77109
Review: /unstract:review-deep (review-shallow refuses: the PR changes dependencies). The delta is 11 files, +102/−43. Self-authored. Security deep-dive: done (full, no candidate at confidence 8). Boundary sweep: clean.
Verdict: REQUEST CHANGES
Summary: Critical: 0 · High: 3 · Medium: 1 · Low: 1 · Lenses run: 19/19 (N/A where stated)
Prior context fetched: followup-fetch.sh exited 0 (6 of 6). The "since" boundary is adf196729 (round 3 review 5782990202).
Scope change: NO
Prior findings status (round 3)
- #1 [Medium] Blocked CDN never fails → RESOLVED: loader watch at
CodeEditor.tsx:120; test; mutant killed. - #2 [Medium] "Invalid JSON" for too-deep text → PARTIALLY RESOLVED: own title, but the RangeError-only catch misses Firefox; see new #1.
- #3 [Medium] Diff limit docs → RESOLVED.
- #4 [Low] Notes → RESOLVED.
New findings
1. [High] [Lens 3]: Firefox reports stack overflow as InternalError, so the RangeError-only catch rethrows and crashes the page
- Location:
frontend/src/v1/components/ui/code/JsonView.tsx:L106-L108,frontend/src/v1/lib/code/json-lines.ts:L307-L312 - Suggested fix: one shared
isTooDeeppredicate covering both. - Confidence: High. Verification: UNVERIFIED in Firefox (MDN documents InternalError). Does a 20,000-deep document crash there?
2. [High] [Lens 13]: The editor tests leave an unhandled rejection from the real Monaco loader, so the suite exits 1 with every test passing
- Location:
frontend/src/v1/components/ui/code/CodeEditor.tsx:L128-L138 - Failure mode: two concurrent first imports of the mocked module; one resolves to the real package, which injects the CDN script.
- Confidence: High. Verification: VERIFIED (scoped run exit 1,
Errors 1 error).
3. [High] [Lens 13]: The edit-limit test runs under the real 1 s timeout and fails under coverage load
- Location:
frontend/src/v1/lib/code/diff.test.ts:L69-L79 - Confidence: High. Verification: VERIFIED (3 of 3 full-suite failures reported by the test agent).
4. [Medium] [Lens 16]: The README test-mock recipe lacks the loader export the editor now reads
- Location:
frontend/src/v1/components/ui/code/README.md:L204-L206 - Confidence: High. Verification: VERIFIED by reading.
5. [Low] [Lens 2/13]: Notes
- Location:
frontend/src/v1/components/ui/code/CodeEditor.tsx:L131-L134 - The cancellation guard is unreachable (and its test exercises a case the loader cannot produce); a comment sits on the wrong line in
diff.test.ts; the too-deep title says "show" though the text is shown; the PR body is stale on the blocked CDN. - Verification: VERIFIED by reading the loader's
makeCancelable.
Lens checklist
- Correctness — See findings #5
- Design — See findings #5
- Error handling — See findings #1
- Security — Clean
- Data integrity — Clean
- Concurrency — Clean
- API compatibility — N/A — no public API change in this delta
- Reliability — Clean
- Performance — Clean
- Observability — Clean
- Operational safety — N/A — no rollout or infrastructure change
- LLM/agent — N/A — no prompt or model configuration
- Testing — See findings #2, #3
- Dependencies — N/A — no dependency change in this delta
- Style/tooling — Clean
- Doc accuracy — See findings #4
- Cross-document consistency — Clean
- On-prem experience — N/A — no on-prem surface
- Scaling & workload lifecycle — N/A — no workload configuration
Open questions / assumptions
- None.
- isTooDeep covers Firefox's InternalError as well as RangeError, for JsonView and formatJsonText. - CodeEditor: one shared Monaco import for the lazy editor and the loader watch (two concurrent first imports let a test reach the real loader); the unreachable cancellation branch removed. - Tests: the edit-limit test runs on a still clock; the loader is mocked in both editor test files; the suite now exits 0 with no unhandled errors. - README mock recipe includes the loader; title wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ritwik-g
left a comment
There was a problem hiding this comment.
Self-review round 5: review-deep (FOLLOWUP), tip 0ccfb21a4, delta 730e77109..0ccfb21a4
Review: /unstract:review-deep (review-shallow refuses: the PR changes dependencies). The delta is 9 files, +58/−26. Self-authored. Security deep-dive: done (full, no candidate at confidence 8). Boundary sweep: clean.
Verdict: APPROVE WITH NITS
Summary: Critical: 0 · High: 0 · Medium: 1 · Low: 2 · Lenses run: 19/19 (N/A where stated)
Prior context fetched: followup-fetch.sh exited 0 (6 of 6). The "since" boundary is 730e77109 (round 4 review 5783181867).
Scope change: NO
Prior findings status (round 4)
- #1 [High] Firefox InternalError → RESOLVED:
isTooDeepatjson-lines.ts:298, used at both sites; predicate test kills both mutants. - #2 [High] Unhandled rejection from the real loader → RESOLVED: one shared
importMonaco()(CodeEditor.tsx:30). The whole suite exits 0 with no Errors line on repeated runs; the loader mocks are now only a safeguard (removing them still passes). - #3 [High] Edit-limit test on the wall clock → RESOLVED: still clock (
diff.test.ts:72); boundary mutant killed. - #4 [Medium] README mock recipe → RESOLVED.
- #5 [Low] Notes → RESOLVED (unreachable cancellation branch and its test removed; comment moved; title wording).
New findings
1. [Medium] [Lens 16]: The isTooDeep docstring says "running out of stack"; it matches any RangeError or InternalError
- Location:
frontend/src/v1/lib/code/json-lines.ts:L294-L303 - Failure mode: a shallow but enormous document (hundreds of MB, "Invalid string length") would be labelled "nested too deeply". No crash, and not a realistic input here.
- Accepted: below the floor (a code comment; behaviour unchanged). It will be tightened in a later change rather than reopening review for a comment.
- Verification: VERIFIED by reading.
2. [Low] [Lens 13]: No test feeds an InternalError through the call sites (only through the predicate)
- Location:
frontend/src/v1/components/ui/code/JsonView.tsx:L107 - Accepted: the predicate is pinned both ways, and each call site is one line.
- Verification: VERIFIED by mutation (M4 survives).
3. [Low] [Lens 2]: The memoised import's comment says what it does, not why (the test race and one shared rejection)
- Location:
frontend/src/v1/components/ui/code/CodeEditor.tsx:L29 - Accepted: no failure mode.
- Verification: VERIFIED by reading.
Lens checklist
- Correctness — Clean
- Design — See findings #3
- Error handling — Clean
- Security — Clean
- Data integrity — Clean
- Concurrency — Clean
- API compatibility — N/A — no public API change in this delta
- Reliability — Clean
- Performance — Clean
- Observability — Clean
- Operational safety — N/A — no rollout or infrastructure change
- LLM/agent — N/A — no prompt or model configuration
- Testing — See findings #2
- Dependencies — N/A — no dependency change in this delta
- Style/tooling — Clean
- Doc accuracy — See findings #1
- Cross-document consistency — Clean
- On-prem experience — N/A — no on-prem surface
- Scaling & workload lifecycle — N/A — no workload configuration
Ready test (self-review-loop)
- Severity floor: nothing above it in this round.
- Delta reviewed: this round covered the fixes from round 4, and none of its findings is above the floor.
- Hard gates:
- Each new guard is mutation-verified.
- No invented fixture: the unreachable cancellation case was removed.
- Each claim was reproduced, including suite exit codes.
- No test was weakened.
- All Highs are resolved; the Mediums and Lows above are accepted here.
- Result: ready to mark ready for review.
Open questions / assumptions
- None.
- JsonView: row clicks are handled once on the tree (no click handler on non-interactive rows). - Markdown headings render their children explicitly. - visibleLines no longer reassigns its loop counter. - The three scrollable regions keep tabIndex (WCAG 2.1.1), marked NOSONAR with the reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ritwik-g
left a comment
There was a problem hiding this comment.
Self-review round 6: review-deep (FOLLOWUP), tip 28b90e024, delta 0ccfb21a4..28b90e024
Review: /unstract:review-deep (review-shallow refuses: the PR changes dependencies). The delta is 5 files, +53/−44, and clears SonarCloud's reliability issues (the quality gate now passes). Agents dispatched by the table's triggers: code-reviewer, which also ran the focused tests (87 passed), and component lifecycle. silent-failure, test-analyzer, type-design and comment-analyzer were not dispatched: the delta changes no error handling, tests, types or substantive docs. Security deep-dive: done (full, no candidate). Boundary sweep: clean.
Verdict: APPROVE
Summary: Critical: 0 · High: 0 · Medium: 0 · Low: 0 · Lenses run: 19/19 (N/A where stated)
Prior context fetched: followup-fetch.sh exited 0 (6 of 6). The "since" boundary is 0ccfb21a4 (round 5 review 5783273126).
Scope change: NO
Prior findings status (round 5)
- #1 [Medium]
isTooDeepdocstring → WAIVED: accepted in round 5 (below the floor). - #2 [Low] Call-site InternalError test → WAIVED: accepted in round 5.
- #3 [Low] Memo comment → WAIVED: accepted in round 5.
New findings
None. Notes, below the floor:
onTreeClick'sclosest("[data-pos]")is not bounded to the tree. It would matter only if a host wrapped JsonView in an element withdata-pos, and nothing does.- The rest are equivalence checks: delegation (the same commit's
rowsanddata-pos), thevisibleLinesloop, and heading output all confirmed identical.
Lens checklist
- Correctness — Clean
- Design — Clean
- Error handling — Clean
- Security — Clean
- Data integrity — N/A — no data handling change in this delta
- Concurrency — Clean
- API compatibility — N/A — no public API change in this delta
- Reliability — Clean
- Performance — Clean
- Observability — N/A — no logging or metrics
- Operational safety — N/A — no rollout or infrastructure change
- LLM/agent — N/A — no prompt or model configuration
- Testing — Clean
- Dependencies — N/A — no dependency change in this delta
- Style/tooling — Clean
- Doc accuracy — Clean
- Cross-document consistency — Clean
- On-prem experience — N/A — no on-prem surface
- Scaling & workload lifecycle — N/A — no workload configuration
Ready test
- Both conditions hold: the round 5 fix delta was reviewed in round 5 with nothing above the floor, and this round's delta returned nothing above the floor.
- Hard gates: each guard is mutation-verified (earlier rounds); this delta's behaviour is pinned by the existing click and
visibleLinestests. No fixture is invented, no test is weakened, and every High is resolved. typecheck:v1,lint:v1andtest:v1all exit 0, and the SonarCloud quality gate passes.- Ready.
[Medium impact] The PR appears safe to merge; no actionable defects remain from this review.
|
- Markdown: fence names that are Object's own keys (constructor, __proto__) fall back to plain text. - JsonView: a new document starts scrolled to the top, where its active row is. - DiffView: gives up past 10,000 lines between the two texts, so long, nearly identical texts cannot render every row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- CodeEditor: options and the change handler are stable across renders; v1's own named themes; Monaco's one-theme-per-page limit documented. - Markdown: react-markdown's depth prop no longer reaches ul/ol. - Diff: each side split once; diffStats counts without building rows; DIFF_MAX_LINES lowered to 2,000 (rows render unwindowed); a change only in line endings reads 'Only line endings differ'. - JsonView: the line-height token is re-read when the scroll element changes, and the virtualiser re-measures. - CopyButton: no state or timer after it unmounts mid-copy. - react-markdown's types come from a module stub mapped in the v1 tsconfig; the global JSX re-declaration is gone. - One plural() and one toolbar-button base shared across the module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- CodeEditor: the latest onChange ref is updated after commit, as DocumentViewer does. - DiffStats.endingsOnly is required; 'Too large to compare' no longer says the texts differ; diff docs no longer overstate diffStats. - CopyButton sets its inline result before calling onCopy. - react-markdown stub: exports named as the package's (Options); header notes the root typecheck once @v1 reaches Markdown. - Tests pin the themes' inherit flag and react-markdown's non-boolean props. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
praveen-formido
left a comment
There was a problem hiding this comment.
All ten findings verified fixed at e14310a (threads resolved). The two places you went a different way, keeping v0's Monaco theme untouched with v1-named themes and lowering the diff cap to 2,000 lines instead of windowing, both hold for the consumers this PR serves. Approving.
Frontend Lint Report (Biome)✅ All checks passed! No linting or formatting issues found. |
|
Unstract test resultsPer-group results
Critical paths
|



What
frontend/src/v1/components/ui/code/, imported from@v1/components/ui/code:CodeBlock: read-only highlighted code (json, bash, python, javascript, typescript, yaml, markdown, text), with optional title, actions, copy, line numbers and wrap.JsonView: JSON as an ARIA tree (keyboard: arrows, Home/End, Enter/Space) or raw text, over one line model. Avalueprints exactly asJSON.stringify(value, null, 2);textprints as written, re-indented only, so a 19-digit ID is never rounded. Expand all / Collapse all, folded{…} 3 keys, error states for invalid text and unserialisable values, and virtualisation above 300 showing lines.DiffView: line diff,split(frame V12) orunified, with old/new labels (BEFORE/AFTER for H20), a "1 change +2 −1" summary, and +/− markers plus "Added:/Removed:" for screen readers. It gives up with "Too large to compare" past 2,000 lines between the two texts or 2,000 added plus removed lines (1 s timeout as backstop), because rows render unwindowed. A change only in line endings reads "Only line endings differ".diffStats()andformatJson()/formatJsonText()support consumers.Markdown: react-markdown 8 + remark-gfm 3 (versions unchanged), no raw HTML, safe-URL filter on links and images, external linksnoopener noreferrer, fenced code rendered asCodeBlock. Images are links by default (loadImagesopts in), so prompt-injected Markdown cannot exfiltrate data through an image URL.CopyButton: inline feedback (check icon, or "Copy failed" / "Copy unavailable") plus a toast throughtoastfrom@v1/lib/notify.CodeEditor: Monaco via@monaco-editor/react, lazy-loaded behind a skeleton, with engine-agnostic props (value,defaultValue,onChange,language,readOnly,showLineNumbers,height,aria-label). Follows the v1 theme (its ownunstract-v1-light/unstract-v1-dark) and the mono font token. Monaco has one theme per page; v0 and v1 never render together, and v0's editor sets its own on mount, so v1 does not restyle v0 (documented). If the Monaco runtime fails to load (a blocked CDN), it says so; after 15 s of a slow load it says it is taking a while and still appears if it loads; an error in the editor (for example a failed chunk) shows an error in its place instead of unmounting the page.InlineCode: mono text as a chip (code in prose) orplain(IDs, token counts).lib/code/:highlight.ts(refractor 5 to our own token model),json-lines.ts,diff.ts(jsdiff),clipboard.ts.styles/tokens.json, defined in light, dark and unstract:code-key,code-string,code-number,code-literal,code-keyword,code-comment,diff-added(-foreground)anddiff-removed(-foreground). They are mapped inindex.css(ui:text-code-*,ui:bg-diff-*).components/ui/code/README.md(every prop, examples, the Monaco/CDN rule, the mono-font rule, test tips) and a "Code and data" section insrc/v1/README.md.code.gallery.tsxlists every variant and state, ready for the gallery (P1-T01).Why
How
token/language-*classesPrism, so registering grammars never touches v0'sprismjs. Only the eight grammars above are registered.@tanstack/react-virtualabove 300 showing lines; documents of 300 lines or fewer open fully expanded, longer ones to depth 2aria-activedescendanton thetree, not roving tabindexCopyButton: the toast always fires; the live region only whentoastOnCopy={false}loader.config()cdn.jsdelivr.net.src/v1/tsconfig.jsonmapsreact-markdownto a types-only stub (lib/react-markdown.d.ts).tstype sources written against React 18's globalJSX, soskipLibCheckcannot skip them andtypecheck:v1failed. The stub declares only what v1 uses, so v1 code still has no globalJSX. Vite and Vitest load the real package. Remove it when react-markdown is upgraded.buttonborder, fill and font reset in the component classesmono-scopeinvariant test (the tracker plan's step 5)font-monotoken". The merged document viewer (TextView) andbase.cssalready use the token, so the guard would fail on main.Token values (lane C to confirm)
The values start from the frames: V14 binds keys to
destructive, numbers tosuccessand strings toaccent-foreground; V10 binds markdown headings toprimary; V12 uses#15803d1fand#b41b301afor the diff rows; H20 uses red-700 and green-700 for its markers. No frame shows dark mode, so the dark values are lighter tints chosen to read onmuted(#fca5a5,#4ade80,#c1c5fb,#b4aaf7,#fbbf24). Unstract mode reuses the light values, exceptcode-keyword, which uses Unstract's navyprimary.code-literal(true, false, null) useswarning, because no frame shows a literal. Light and Unstract values ofcode-string,code-number,code-literal,code-keyword,code-commentanddiff-added-foregroundwere then darkened slightly so every syntax colour reads at 4.5:1 or more onmuted,card,accentand both diff tints;tokens.test.tsenforces it (axe's contrast rule is off in the harness). Lane C: please confirm or replace these.Gap analysis: spec vs this PR
Origin tags: Original spec · Existing v0 behaviour · Gap (spec silent or contradicted by reality) · Proposed by Claude.
InlineCode(point 9).font-monotoken. No exclusivity guard, becauseTextViewandbase.cssalready use the token.toast.success/toast.errorfrom@v1/lib/notify(P1-T13, merged), plus inline feedback.defaultValue,onChange,lineNumbers,heightand a requiredaria-label.code.gallery.tsxholds every variant;markup.test.tsxsnapshots each one and runs axe on it. Register these entries when P1-T01 lands.__snapshots__/markup.md(v1 holds no.snapfiles).InlineCode variant="plain"is the documented way.loader.config(), which would change v0's editors too. Revisit when v0 goes (P9).transformImageUriis set, and images are links unlessloadImagesis set, because model output steered by a document could otherwise send data to any host in an image URL.textkeeps key order and duplicate keys as writtentextis printed from its own tokens, not re-serialised. AvaluefollowsJSON.stringify(integer-like keys first).textare never roundedvalue, precision was lost when it was parsed, before JsonView sees it.wrap={false}gives the clipped look.init(), neverconfig()): a failed load says "could not load"; a slow one says "taking a while" after 15 s and still appears if it loads.valueis compared by identityCan this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)
frontend/src/v1/, which no v0 route renders. The dependency changes are additive:diff9.0.0 andrefractor5.0.0 were already inbun.lock(through react-diff-viewer-continued) and are now direct, at the same resolved versions;@tanstack/react-virtual3.14.21 is new.bun.lockgains only those entries (it was edited by hand to avoid unrelated re-hoisting) andbun install --frozen-lockfilepasses. The production build passes.Database Migrations
Env Config
Relevant Docs
frontend/src/v1/components/ui/code/README.md, and the "Code and data" section infrontend/src/v1/README.md.Related Issues or PRs
Dependencies Versions
@tanstack/react-virtual3.14.21 (new; virtualises large JSON).diff9.0.0 andrefractor5.0.0 (made direct; already locked at these versions).Notes on Testing
bun run typecheck:v1,bun run lint:v1andbun run test:v1all pass: 381 v1 tests, coverage 97.5% lines and 94.7% branches.javascript:links and images), diff pairing and stats, the exactJSON.stringifyprint, and the 5,000-line performance case. axe runs in all three modes.vi.mock("@monaco-editor/react")), because its loader fetches from the CDN.Screenshots
Checklist
I have read and understood the Contribution Guidelines.
🤖 Generated with Claude Code