Skip to content

refactor(components): decompose DocumentViewer into document-viewer/ modules (maturity X3)#1025

Merged
BigSimmo merged 8 commits into
mainfrom
claude/maturity-x3-documentviewer
Jul 21, 2026
Merged

refactor(components): decompose DocumentViewer into document-viewer/ modules (maturity X3)#1025
BigSimmo merged 8 commits into
mainfrom
claude/maturity-x3-documentviewer

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

Maturity X3 — decompose the DocumentViewer.tsx monolith. It was 3,164 lines, capped
but never reduced by the maintainability ratchet. Its self-contained presentational leaf
clusters move into a src/components/document-viewer/ sibling directory (the same home as the
existing pdf-canvas-viewer / non-pdf-source-preview modules), leaving the container as a
composition-only shell.

  • document-viewer/types.ts — shared row shapes (pages, images, table facts, chunks,
    full-document search hits, index health)
  • document-viewer/source-panels.tsx — clinical summary profile, high-yield summary, source
    images/tables, the table-review queue, section anchors and summaries, pinned source
    evidence, and the indexed source-text search panel
  • document-viewer/manual-tag-editor.tsx — the curated manual-tag editor
  • document-viewer/document-overview-landing.tsx — the overview landing card, quick actions,
    and page-jump chips

All pure moves — the component bodies are verbatim and the container imports and composes
them exactly as before. DocumentViewer.tsx 3,164 → 1,733 lines (−45%); the
maintainability budget is ratcheted 3,166 → 1,733 to lock in the reduction. knip
confirms no orphaned exports.

The rendered-text-formatting guard now scans the extracted modules alongside the container —
following that file's own established "raw-render guards travel with the code" pattern — so the
image-caption formatter assertion follows DocumentImage into source-panels.

This is off the RAG-ranking lane: no src/lib/rag/**, retrieval, ranking, selection, eval, or
fixture files are touched — purely a UI component split.

Verification

  • npm run typecheck — 0 errors
  • npm run lint — 0 warnings
  • npm run test — 3,027 passing. The only failure is the pre-existing container-only
    pdf-extraction-budget flake (Python OCR stack absent in this VM), confirmed failing
    identically on the base checkout and touching no DocumentViewer code.
  • npm run check:maintainability-budgets — ratcheted to 1,733, passes
  • npm run check:knip — no dead code / orphaned exports
  • Isolated production build — compiles cleanly (1,676 pages generated)
  • Targeted verify:ui (Chromium) — 7/8 document-viewer specs pass, including both
    @critical specs and the dashboard/document-viewer screenshot capture.

UI verification note: the container's Playwright browser is build 1194 but the project
pins build 1228 (chrome-headless-shell-1228 is absent), so the full verify:ui run
errors at browser launch for every spec — a documented container limitation, not a code
issue. Pointing Playwright at the installed Chromium (chromium-1194) and running the
document-viewer specs gives 7/8 green. The 1 failure is pdf-canvas-scroll canvas visibility —
pdf.js canvas paint under the mismatched browser, in the pdf-canvas-viewer path that this
diff never touches (the dynamic import/render wiring is 0-diff), and the spec passed every
assertion on the extracted components (pinned evidence, highlighted passage, viewer nav)
before hitting that unrelated canvas check. CI's Production UI job runs the gate with the
correct browser binaries.

Risk and rollout

  • Risk: low — a pure structural extraction (verbatim component moves), verified by typecheck,
    the full unit suite, the production build, and targeted Chromium specs. No behaviour,
    props, or render structure changed.
  • Rollback: revert the single commit.
  • Provider or production effects: None.

Clinical Governance Preflight

Not applicable — per scripts/pr-policy.mjs, presentation components are clinical-risk only
when they touch auth/upload/download/privacy/patient surfaces, which this does not. No
ingestion, answer generation, retrieval/ranking, source-governance, document-access, privacy,
schema, or production behaviour changed; the rendered clinical output is byte-identical.

Notes

  • Part of the repository-maturity backlog (docs/maturity-backlog-workorders.md, X3), updated
    on-branch: X3 → IN PROGRESS. ClinicalDashboard.tsx (4,271) and rag.ts remain the open
    decomposition targets; rag.ts stays deferred until the RAG lane is confirmed quiet.

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added a document viewer overview landing with metadata, preview/open and download, “add to scope,” and “request an answer from this,” plus clickable useful-page navigation.
    • Added manual document label management with add, rename/update, and remove capabilities.
    • Enhanced document source browsing with section summaries, pinned evidence, indexed text search with next/previous hit navigation, and improved image/table presentation.
  • Documentation

    • Updated the work-order tracker with revised workflow-state wording and current X3 execution status.
  • Refactor / Tests / Chores

    • Reorganized document viewer internals, updated related regression tests, and adjusted maintainability line limits for the viewer.

…modules (maturity X3)

DocumentViewer.tsx was a 3,164-line monolith the maintainability ratchet caps but
never reduces. Extract its self-contained presentational leaf clusters into a
document-viewer/ sibling directory (matching the pdf-canvas-viewer /
non-pdf-source-preview modules already there), leaving the container composition-only:

- document-viewer/types.ts — shared row shapes (pages, images, table facts, chunks,
  full-document search hits, index health)
- document-viewer/source-panels.tsx — clinical summary profile, high-yield summary,
  source images/tables, the table-review queue, section anchors and summaries,
  pinned source evidence, and the indexed source-text search panel
- document-viewer/manual-tag-editor.tsx — the curated manual-tag editor
- document-viewer/document-overview-landing.tsx — the overview landing card, quick
  actions, and page-jump chips

All pure moves — component bodies are verbatim and the container imports and composes
them exactly as before. DocumentViewer.tsx 3,164 -> 1,733 lines; the maintainability
budget is ratcheted 3,166 -> 1,733 to lock in the reduction.

The rendered-text-formatting guard now scans the extracted modules alongside the
container (its established "raw-render guards travel with the code" pattern) so the
image-caption formatter assertion follows DocumentImage into source-panels.

Verified: typecheck, lint, the full unit suite (3,027 passing; only the pre-existing
container-only pdf-extraction-budget flake fails, identical on the base), knip,
maintainability budgets, the isolated production build, and 7/8 targeted
document-viewer Chromium specs. The one UI failure is pdf.js canvas paint under the
container's mismatched Playwright browser build (1194 vs the pinned 1228), in the
untouched pdf-canvas-viewer path — CI runs the UI gate with the correct browser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Jc1ZYHFjXjn6mE6U6riVU
@supabase

supabase Bot commented Jul 21, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b4476b8f-0af9-42bb-9166-800bd6d788b2

📥 Commits

Reviewing files that changed from the base of the PR and between 10779ae and 9df17d3.

📒 Files selected for processing (1)
  • docs/maturity-backlog-workorders.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/maturity-backlog-workorders.md

📝 Walkthrough

Walkthrough

The document viewer is split into typed source-panel and overview modules, while DocumentViewer.tsx becomes composition-focused. Manual document-label editing is added, regression coverage follows extracted modules, and the maintainability tracker is updated.

Changes

Document viewer decomposition

Layer / File(s) Summary
Extract viewer contracts and source panels
src/components/document-viewer/types.ts, src/components/document-viewer/source-panels.tsx
Viewer payload types and rendering components for summaries, images, evidence, tables, anchors, and indexed text are moved into dedicated exports.
Add document overview landing
src/components/document-viewer/document-overview-landing.tsx
Adds document metadata, overview, key-section, useful-page, PDF/preview, and action-button rendering.
Add manual tag editing
src/components/document-viewer/manual-tag-editor.tsx
Adds authenticated add, rename, and delete operations for manual document labels with inline editing and request-state handling.
Wire the composition entry point
src/components/DocumentViewer.tsx
Imports extracted contracts and panels, removes duplicated local declarations, and retains DocumentViewer as the primary entry point.
Update validation and progress tracking
tests/rendered-text-formatting.test.ts, scripts/check-maintainability-budgets.mjs, docs/maturity-backlog-workorders.md
Extends formatter regression checks to extracted modules, lowers the DocumentViewer line budget to 1733, and records the decomposition as in progress.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: decomposing DocumentViewer into sibling document-viewer modules.
Description check ✅ Passed The description follows the template and includes summary, verification, risk, governance, and notes with relevant details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/maturity-x3-documentviewer

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo marked this pull request as ready for review July 21, 2026 00:59
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@BigSimmo
BigSimmo enabled auto-merge (squash) July 21, 2026 00:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/components/document-viewer/manual-tag-editor.tsx (2)

103-105: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

No confirmation before deleting a manual tag.

deleteManualTag fires the DELETE request immediately on click (Line 244), with no confirmation step for what is an irreversible action. A single misclick removes a curated label.

Also applies to: 242-254

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/document-viewer/manual-tag-editor.tsx` around lines 103 - 105,
Update deleteManualTag to request explicit user confirmation before calling
submitManualTag with the DELETE operation. If confirmation is declined, return
without submitting; preserve the existing deletion payload and request key when
confirmed.

57-80: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

401 handling falls through to the generic error path.

Line 70 calls onUnauthorized() on a 401 but doesn't return, so execution still hits Line 71's throw (since a 401 response is !response.ok), which then sets error to the server message via setError in the catch block. This means both the unauthorized callback and the inline error banner fire together, which can leave a stale/confusing error message rendered underneath whatever unauthorized-state UI the parent shows.

🩹 Proposed fix
       const payload = await response.json().catch(() => ({}));
-      if (response.status === 401) onUnauthorized();
+      if (response.status === 401) {
+        onUnauthorized();
+        return false;
+      }
       if (!response.ok) throw new Error(typeof payload?.error === "string" ? payload.error : "Tag update failed.");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/document-viewer/manual-tag-editor.tsx` around lines 57 - 80,
Update submitManualTag so the 401 branch invokes onUnauthorized() and exits
before the generic !response.ok error path. Preserve existing error handling for
all other unsuccessful responses and ensure busy-state cleanup still occurs
through finally.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/maturity-backlog-workorders.md`:
- Around line 84-89: Update the DocumentViewer progress entry to remove the
“Pure moves” and “composition-only” claims, describing the work instead as
composition-focused/presentational extraction. Acknowledge that
manual-tag-editor.tsx contains behavior-bearing tag-editing logic and that
DocumentViewer.tsx retains orchestration such as dynamic PDF loading, without
changing the listed decomposition targets.

In `@src/components/document-viewer/manual-tag-editor.tsx`:
- Around line 119-157: Align manual tag client validation with the API’s 2–64
character label constraint in the add and edit flows. Update the add input and
submit button plus the edit-mode input and save button near the existing
draftLabel/edit label handling to enforce a maximum length of 64 and disable
submission when the trimmed value is shorter than 2 characters; preserve the
existing permission and busy-state checks.

---

Nitpick comments:
In `@src/components/document-viewer/manual-tag-editor.tsx`:
- Around line 103-105: Update deleteManualTag to request explicit user
confirmation before calling submitManualTag with the DELETE operation. If
confirmation is declined, return without submitting; preserve the existing
deletion payload and request key when confirmed.
- Around line 57-80: Update submitManualTag so the 401 branch invokes
onUnauthorized() and exits before the generic !response.ok error path. Preserve
existing error handling for all other unsuccessful responses and ensure
busy-state cleanup still occurs through finally.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d2c573a6-8641-4614-a2d9-fc4efa00f17b

📥 Commits

Reviewing files that changed from the base of the PR and between 5bd7e8d and 10779ae.

📒 Files selected for processing (8)
  • docs/maturity-backlog-workorders.md
  • scripts/check-maintainability-budgets.mjs
  • src/components/DocumentViewer.tsx
  • src/components/document-viewer/document-overview-landing.tsx
  • src/components/document-viewer/manual-tag-editor.tsx
  • src/components/document-viewer/source-panels.tsx
  • src/components/document-viewer/types.ts
  • tests/rendered-text-formatting.test.ts

Comment thread docs/maturity-backlog-workorders.md Outdated
Comment thread src/components/document-viewer/manual-tag-editor.tsx
claude and others added 2 commits July 21, 2026 01:15
"Presentational leaf clusters"/"composition-only" overstated it: manual-tag-editor.tsx
is behaviour-bearing (add/rename/delete label API calls) and the container retains its
detail fetch, dynamic PDF loading, and state orchestration. Reword to "cohesive leaf
modules" and "composition-focused" while keeping the accurate verbatim-move claim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Jc1ZYHFjXjn6mE6U6riVU
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Autofix skipped. No unresolved CodeRabbit review comments with fix instructions found.

@BigSimmo
BigSimmo merged commit fd997d9 into main Jul 21, 2026
17 checks passed
@BigSimmo
BigSimmo deleted the claude/maturity-x3-documentviewer branch July 21, 2026 01:58
BigSimmo added a commit that referenced this pull request Jul 21, 2026
… confirm, 401) (#1035)

* fix(document-viewer): harden manual-tag editor UX (validation, delete confirm, 401)

Address the three pre-existing manual-tag-editor findings deferred from #1025 — they
predate the decomposition, so folding them into that pure-move PR would have broken its
no-behaviour-change contract. Client-side only; the server already enforces the label
contract and auth.

- Match the server's 2-64 char label rule (manualLabelSchema in
  src/app/api/documents/[id]/labels/route.ts) in the field: maxLength={64} on the add and
  edit inputs, and disable submit/save until the trimmed value reaches the 2-char minimum,
  so a too-short/too-long tag no longer round-trips to a generic 400.
- Require an explicit second click to confirm a manual-tag delete (inline two-click confirm
  with a Cancel, no native window.confirm) so a single misclick can't remove a curated label.
- Return after onUnauthorized() on a 401 instead of falling through to the generic throw, so
  the unauthorized handoff no longer also raises a stale inline error banner.

Adds tests/manual-tag-editor.dom.test.tsx covering all three behaviours.

Verified: typecheck, lint, and the full unit suite (3,065 passing incl. the 3 new tests;
only the pre-existing container-only pdf-extraction-budget flake fails).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Jc1ZYHFjXjn6mE6U6riVU

* fix(document-viewer): make manual-tag delete confirm double-click-safe (CodeRabbit)

The first version rendered the "Confirm remove" button at the same fragment index as
the "Remove" button, so React reused the Remove button's DOM node for the destructive
confirm — a rapid double-click on Remove could land its second click on the in-place
delete control. Fix it two ways:

- Give the Rename/Remove and confirm/cancel controls distinct `key`s so React never
  reuses the Remove node for the destructive confirm — the control the user pressed is
  never turned into "delete" in place.
- Render a non-action "Remove this tag?" prompt in the position the Remove button was
  clicked from, shifting the confirm control off that hit target, and label the confirm
  button "Confirm remove".

Adds a regression test: after one Remove click the original Remove control is gone
(replaced by the prompt) and re-clicking it issues no DELETE.

Verified: typecheck, lint, the manual-tag-editor dom suite (4 passing), and the full
unit suite (3,066 passing; only the pre-existing pdf-extraction-budget flake fails).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Jc1ZYHFjXjn6mE6U6riVU

---------

Co-authored-by: Claude <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.

2 participants