A Storybook-only prototype of the RegisterView primitive family — the Evidentiary Record register. One dataset, saveable table views à la GitHub Projects, worded for lawyers. Built bottom-up (badges → row → grouped table → view chrome → filter bar → side peek → arriving rows → empty state) and composed into the full surface. No live route is touched; founder approval gates the wiring. Fixtures are the design contract's own Rathode arbitration set. Every screenshot below is a real render of a Storybook story (static build, no mocks beyond fixtures).
Grouped by side (Client's / Opponent's / Other) with count·size subtotals; booktabs discipline (single hairlines, no vertical rules); the Document column is sticky and the table pans horizontally. HOLD chip on D-03, live Indexing status on D-05, the privileged D-07 muted with a lock, and the D-08 duplicate-of marker.
RegisterViewRegisterTable RegisterGroupHeaderRegisterRow SideBadgeRecordStatusIndicator TagListHoldChipDuplicateMarker AnimatedList (reuse)
Plain words, one control per line — Group into sections not "Group by", Order rows not "Sort", Browse by tag not "Slice". Each row shows its current value, so the menu doubles as a summary of the view. Opens on click or shift-v.
ViewMenu
One box, no query language. Structured chips (Side / Type) AND together with a remove affordance; a "Narrow by" row offers one-click chips from what's on screen; the Browse-by-tag left rail carries counts; a muted footer states how many documents are hidden.
FilterBarNarrowByRow HiddenByFiltersTagBrowseRail
Space or the eye icon opens the peek over the (dimmed) table without leaving the register; ↑↓ walk rows while it stays open. Summary, tags, a provenance block (brought in by / source / original name / sha256 fingerprint), threaded notes, and Download / Copy permalink / Reclassify in the footer.
DocumentPeekRegisterRow (selected)
Documents arriving from the import drawer pin on top of the register with the four-stage journey — Received → Scanned → Read → On the record — then settle into their group. Rows animate through AnimatedList.
ArrivingStripArrivingRowAnimatedList (reuse)
Changing the view marks the tab with a dot and shows the quiet save bar — Discard / Save as new view / Save changes. The bar enters and exits with the shared 0.24s micro-transition (symmetric).
ViewTabsModifiedViewBar
No upload button on the page — the invitation points to the cloud icon in the matter rail (which the shell pulses) or a drop-anywhere gesture.
RecordEmptyState
Wired in useRegisterKeyboard, demonstrated live in the
RegisterView / KeyboardDemo story (input-aware — ignored while typing,
except Esc).
| Primitive | File | Role |
|---|---|---|
| Badges | badges.tsx | SideBadge, RecordStatusIndicator, DocKindGlyph, TagList, HoldChip, DuplicateMarker |
| RegisterRow | register-row.tsx | Sticky Document cell + scalar columns; hover/selected action cluster; privileged + duplicate variants |
| RegisterGroupHeader | register-group-header.tsx | Party section header with subtotal |
| RegisterTable | register-table.tsx | Panning grid, sticky column, grouped body via AnimatedList |
| ViewTabs | view-tabs.tsx | Saved-view tabs + unsaved dot + New view |
| ModifiedViewBar | modified-view-bar.tsx | Quiet save bar (Discard / Save as new / Save changes) |
| ViewMenu | view-menu.tsx | Columns / Group / Order / Browse-by-tag with current-value summaries |
| FilterBar + NarrowByRow + HiddenByFilters | filter-bar.tsx | One-box typed chips, suggestions, hidden-count |
| TagBrowseRail | tag-browse-rail.tsx | Left rail, tag counts |
| DocumentPeek | document-peek.tsx | Side peek: summary, tags, provenance, notes, footer |
| ArrivingRow + ArrivingStrip | arriving-row.tsx | Four-stage optimistic journey |
| RecordEmptyState | record-empty-state.tsx | Empty first-run invitation |
| RegisterView | register-view.tsx | Composed surface + keyboard wiring |
Reused shared primitives: AnimatedList / AnimatedListRow
MICRO_TRANSITION (motion.ts). Copy is single-sourced through the
i18n-ready RegisterCopy object; presentation colour/label logic lives in
presentation.ts.
design/v1 palette steps, so the prototype ships tokens-only (no new
tokens, no inline colour literals — the token & i18n scans are clean):
shadow-xl utility instead of an rgba literal.
useAppList hook (forum data pattern), next-intl
keys swapped in for the RegisterCopy default (both en and
hi), optimistic reclassify/side-change writes, and the real empty/first-run
run against an empty matter. The register is a distinct view under the matter shell, so it
lands as its own route (route-per-mode), not a client tab.
40 file(s), +2725 / -0 lines
| Path | Role | + | - |
|---|---|---|---|
| docs/walkthroughs/assets/story-2166/1a-default-register.png | Walkthrough | bin | bin |
| docs/walkthroughs/assets/story-2166/1b-view-menu.png | Walkthrough | bin | bin |
| docs/walkthroughs/assets/story-2166/1c-filters-active.png | Walkthrough | bin | bin |
| docs/walkthroughs/assets/story-2166/1d-quick-preview.png | Walkthrough | bin | bin |
| docs/walkthroughs/assets/story-2166/1e-arriving.png | Walkthrough | bin | bin |
| docs/walkthroughs/assets/story-2166/1f-unsaved-view.png | Walkthrough | bin | bin |
| docs/walkthroughs/assets/story-2166/1g-empty-first-run.png | Walkthrough | bin | bin |
| src/components/shared/register/arriving-row.stories.tsx | Component | +53 | -0 |
| src/components/shared/register/arriving-row.tsx | Component | +100 | -0 |
| src/components/shared/register/badges.stories.tsx | Component | +67 | -0 |
| src/components/shared/register/badges.tsx | Component | +137 | -0 |
| src/components/shared/register/copy.ts | Component | +175 | -0 |
| src/components/shared/register/document-peek.stories.tsx | Component | +52 | -0 |
| src/components/shared/register/document-peek.tsx | Component | +163 | -0 |
| src/components/shared/register/filter-bar.stories.tsx | Component | +58 | -0 |
| src/components/shared/register/filter-bar.tsx | Component | +162 | -0 |
| src/components/shared/register/fixtures.ts | Component | +216 | -0 |
| src/components/shared/register/grid.ts | Component | +11 | -0 |
| src/components/shared/register/grouping.ts | Component | +25 | -0 |
| src/components/shared/register/modified-view-bar.stories.tsx | Component | +42 | -0 |
| src/components/shared/register/modified-view-bar.tsx | Component | +72 | -0 |
| src/components/shared/register/presentation.ts | Component | +83 | -0 |
| src/components/shared/register/record-empty-state.stories.tsx | Component | +18 | -0 |
| src/components/shared/register/record-empty-state.tsx | Component | +26 | -0 |
| src/components/shared/register/register-group-header.tsx | Component | +50 | -0 |
| src/components/shared/register/register-header.tsx | Component | +32 | -0 |
| src/components/shared/register/register-row.stories.tsx | Component | +50 | -0 |
| src/components/shared/register/register-row.tsx | Component | +166 | -0 |
| src/components/shared/register/register-table.stories.tsx | Component | +23 | -0 |
| src/components/shared/register/register-table.tsx | Component | +123 | -0 |
| src/components/shared/register/register-view.stories.tsx | Component | +109 | -0 |
| src/components/shared/register/register-view.tsx | Component | +182 | -0 |
| src/components/shared/register/tag-browse-rail.stories.tsx | Component | +20 | -0 |
| src/components/shared/register/tag-browse-rail.tsx | Component | +114 | -0 |
| src/components/shared/register/types.ts | Component | +90 | -0 |
| src/components/shared/register/use-register-keyboard.ts | Component | +77 | -0 |
| src/components/shared/register/view-menu.stories.tsx | Component | +23 | -0 |
| src/components/shared/register/view-menu.tsx | Component | +117 | -0 |
| src/components/shared/register/view-tabs.stories.tsx | Component | +23 | -0 |
| src/components/shared/register/view-tabs.tsx | Component | +66 | -0 |