38 file(s), +2448 / -12 lines
| Path | Role | + | - |
|---|---|---|---|
| design/v1/colors_and_type.css | Styles | +1 | -0 |
| messages/en.json | Config | +66 | -1 |
| messages/hi.json | Config | +66 | -1 |
| package.json | Config | +2 | -0 |
| pnpm-lock.yaml | Config | +191 | -0 |
| src/app/[locale]/(workspace)/matters/[matterId]/record/doc/[documentId]/page.tsx | App route | +56 | -0 |
| src/app/globals.css | App route | +3 | -0 |
| src/components/documents/register/register-document-row.tsx | Component | +16 | -2 |
| src/components/documents/register/register-table.tsx | Component | +4 | -0 |
| src/components/documents/register/register-view.tsx | Component | +5 | -1 |
| src/components/documents/register/saved-views-register.tsx | Component | +14 | -1 |
| src/components/documents/viewer/agent-note-card.tsx | Component | +46 | -0 |
| src/components/documents/viewer/citation-landing-pill.tsx | Component | +44 | -0 |
| src/components/documents/viewer/document-badges.tsx | Component | +104 | -0 |
| src/components/documents/viewer/document-control-bar.stories.tsx | Component | +48 | -0 |
| src/components/documents/viewer/document-fact-rail.stories.tsx | Component | +63 | -0 |
| src/components/documents/viewer/document-fact-rail.tsx | Component | +162 | -0 |
| src/components/documents/viewer/document-fact-row.tsx | Component | +88 | -0 |
| src/components/documents/viewer/document-margin-note.tsx | Component | +33 | -0 |
| src/components/documents/viewer/document-note-composer.tsx | Component | +56 | -0 |
| src/components/documents/viewer/document-note-item.tsx | Component | +46 | -0 |
| src/components/documents/viewer/document-notes.stories.tsx | Component | +46 | -0 |
| src/components/documents/viewer/document-page-canvas.tsx | Component | +60 | -0 |
| src/components/documents/viewer/document-pdf-viewer.tsx | Component | +78 | -0 |
| src/components/documents/viewer/document-processing-stages.tsx | Component | +63 | -0 |
| src/components/documents/viewer/document-text-pane.tsx | Component | +49 | -0 |
| src/components/documents/viewer/document-viewer-header.tsx | Component | +98 | -0 |
| src/components/documents/viewer/document-viewer-toolbar.tsx | Component | +126 | -0 |
| src/components/documents/viewer/document-viewer.stories.tsx | Component | +122 | -0 |
| src/components/documents/viewer/document-viewer.tsx | Component | +179 | -0 |
| src/components/documents/viewer/live-document-viewer-page.tsx | Component | +113 | -0 |
| src/components/documents/viewer/live-presentation.ts | Component | +84 | -0 |
| src/components/documents/viewer/types.ts | Component | +107 | -0 |
| src/components/documents/viewer/viewer-fixtures.tsx | Component | +183 | -0 |
| src/components/matter-detail/ccb/mira-control-bar.tsx | Component | +10 | -0 |
| src/components/shared/primary-button.tsx | Component | +2 | -2 |
| src/components/shared/secondary-button.tsx | Component | +2 | -2 |
| src/lib/surface-registry/route-registry.ts | Library | +12 | -2 |
The document permalink —
/matters/[matterId]/record/doc/[documentId] — is the
document's three lives in one page: the Original PDF
(dominant ~60% viewer), the extracted Text, and the
"What matters here" rail of card facts pinned to their
pages. The founder-approved Claude Design
(pramaan-desk discussions/document-viewer/document-view.dc.html
+ PNGs) was lifted, not redesigned. Built Storybook-first, then wired to the
one live data path that exists today (the presigned PDF), with honest typed
seams for the contracts that don't (key facts, per-block bboxes, notes
presentation).
A presentational component set under
src/components/documents/viewer/, each token-clean, i18n'd
(DocumentViewer namespace, en + hi) and motion-correct.
Documents/Viewer/DocumentViewer:
ProcessedFactSelected (1a — "Alleged cause" selected, its
paragraph highlighted on p.4), ArrivedViaCitation (1b — split
Original + Text, the "Cited in Brief › Core dispute" landing pill),
StillProcessing (1c — staged progress, facts gracefully empty),
NeedsReview (1d — one extracted field flagged Confirm/Edit),
plus WithNotes (2a — rail Notes + a margin note in the gutter).
DocumentPageCanvas) positions boxes by
percentage of the page frame, so the exact same overlay math drives the
fixture sheet in Storybook and the real react-pdf page live.
Documents/Viewer/ParagraphControlBar. It is the existing
Counsel Control Bar (MiraControlBar) extended, not
forked: a new optional onNote action adds a
Note button to the bar (Mira · Command · Why · Cite · Note). The
Brief's CCB is unaffected (it omits onNote).
Documents/Viewer/Notes: the
paragraph-anchored note composer (2b), a note Mira left while reading with
Keep / Dismiss (2c), and a margin note (2a). The rail's Notes list and
badges are covered by Documents/Viewer/FactRail.
Avatar,
PrimaryButton/SecondaryButton,
AnimatedList, MICRO_TRANSITION, and the CCB are
all shared primitives. A dense xs height was added to the
button primitives (serves this rail and other tight CCB surfaces). The
one new token is --surface-viewer-canvas (the viewer canvas
is a step darker than the page so the white sheet lifts) — added to both
globals.css and the canonical
design/v1/colors_and_type.css.
record/doc/[documentId]/page.tsx is an RSC that validates the
params, reuses the record bootstrap (one RLS-scoped round-trip) to resolve
the matter (breadcrumb) and find the document row, and hands off to
LiveDocumentViewerPage. The matter chrome (Ribbon + Worknav +
Action Rail) comes free from the fat matter layout. It exports
agentSurface (key: "matter.record.doc",
presence keys in both locales); pnpm gen:routes /
check:routes are green (38 surfaces).
useAppGetDocumentDownloadUrl) feeds react-pdf
(the industry-default pdf.js renderer — no bespoke viewer). It is isolated
in document-pdf-viewer.tsx, loaded via
next/dynamic({ ssr: false }); the pdf.js worker is bundled
locally (a CDN worker would be blocked by the app's strict CSP, LI-5).
Page navigation and zoom are live; Download original + Copy permalink work;
header metadata + rail chips come from the register row.
buildSurfacePath("matter.record.doc")).
The document detail drawer (privilege set/clear, Story-1854) is preserved:
a small per-row detail affordance (onShowDetail, revealed on
hover) still opens it, so the matters:write privilege control
is never dropped.
?cite=Brief%20%E2%80%BA%20Core%20dispute.
The card key-facts (CE-7) and per-block page/bbox + extracted-text
(CE-17 / pages.json) have no mirrored contract yet, and the
notes substrate's presentation fields (author display, paragraph anchor) are
not merged. So live, the rail carries no facts, the viewer no Text tab or
bbox highlight, and no notes — it degrades to the honest processing/empty
state (live-presentation.ts, seams documented inline). The full
designed experience — fact-click jump + highlight, hover-bar, composer,
Keep/Dismiss — is proven in Storybook against fixtures and lights up when
those contracts land. No pnpm codegen was run.
pnpm typecheck — clean.pnpm lint — clean (viewer, register, buttons, CCB).pnpm test — 163 files / 1014 tests green.pnpm build — green; the route compiles as dynamic (ƒ).pnpm check:routes — surface registry fresh (38 surfaces).storybook build — all stories compile.DocumentViewer keys present in both en and hi.
Live-data boundary: the PDF path is real end-to-end
(presign → react-pdf). The rail's facts/text/highlight/notes are the
documented seams above — the empty first-run state is the honest truth until
CE-7 / CE-17 / the notes substrate land. The staging run to confirm on
stg.app.pramaan.io is the component-boundary → system-boundary
check: navigate from a real Record row to a real document's permalink and
confirm the presigned PDF renders.