@lotics/ui 7.19.3 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/AGENTS.md +298 -159
  2. package/examples/tpl_allocate.tsx +2 -2
  3. package/examples/tpl_attendance.tsx +2 -2
  4. package/examples/tpl_calendar.tsx +1 -1
  5. package/examples/tpl_dashboard.tsx +1 -1
  6. package/examples/tpl_dieline.tsx +3 -3
  7. package/examples/tpl_documents.tsx +790 -0
  8. package/examples/tpl_item_list.tsx +1015 -124
  9. package/examples/tpl_lookup.tsx +37 -10
  10. package/examples/tpl_pick.tsx +3 -3
  11. package/examples/tpl_pivot.tsx +1 -1
  12. package/examples/tpl_record.tsx +1354 -0
  13. package/examples/tpl_report.tsx +7 -7
  14. package/examples/tpl_rollup.tsx +6 -6
  15. package/examples/tpl_shifts.tsx +2 -2
  16. package/examples/tpl_statements.tsx +221 -0
  17. package/examples/tpl_stock.tsx +7 -7
  18. package/examples/tpl_task_board.tsx +48 -32
  19. package/examples/tpl_tasks.tsx +47 -47
  20. package/examples/tpl_tower.tsx +2 -2
  21. package/package.json +9 -9
  22. package/src/agent_run.tsx +1 -1
  23. package/src/capture_row.tsx +59 -0
  24. package/src/change_review.tsx +732 -236
  25. package/src/checklist.tsx +104 -0
  26. package/src/chip.tsx +12 -3
  27. package/src/composer.tsx +22 -1
  28. package/src/confidence.tsx +1 -1
  29. package/src/control_surface.ts +0 -14
  30. package/src/detail_row.tsx +137 -10
  31. package/src/finding.tsx +112 -80
  32. package/src/floating_action_bar.tsx +1 -1
  33. package/src/inline_date_picker.tsx +8 -3
  34. package/src/inline_edit.tsx +40 -10
  35. package/src/inline_member_select.tsx +3 -0
  36. package/src/inline_number_input.tsx +5 -2
  37. package/src/inline_select.tsx +8 -3
  38. package/src/inline_tag_select.tsx +140 -0
  39. package/src/inline_text_input.tsx +5 -2
  40. package/src/inline_time_picker.tsx +5 -2
  41. package/src/ledger.tsx +220 -0
  42. package/src/locale.tsx +43 -16
  43. package/src/progress_bar.tsx +32 -1
  44. package/src/record_summary.tsx +101 -0
  45. package/src/section_heading.tsx +16 -8
  46. package/src/sources.tsx +8 -5
  47. package/src/suggestion_chip.tsx +47 -0
  48. package/src/use_section_nav.test.ts +69 -0
  49. package/src/use_section_nav.ts +59 -0
  50. package/examples/tpl_assistant.tsx +0 -174
  51. package/examples/tpl_billing.tsx +0 -344
  52. package/examples/tpl_briefing.tsx +0 -121
  53. package/examples/tpl_compare.tsx +0 -133
  54. package/examples/tpl_crosscheck.tsx +0 -120
  55. package/examples/tpl_detail.tsx +0 -232
  56. package/examples/tpl_directory.tsx +0 -260
  57. package/examples/tpl_draft.tsx +0 -163
  58. package/examples/tpl_extract.tsx +0 -193
  59. package/examples/tpl_intake.tsx +0 -206
  60. package/examples/tpl_match.tsx +0 -134
  61. package/examples/tpl_order.tsx +0 -482
  62. package/examples/tpl_quick.tsx +0 -211
  63. package/examples/tpl_ratedesk.tsx +0 -386
  64. package/examples/tpl_record_plain.tsx +0 -259
  65. package/examples/tpl_settings.tsx +0 -178
  66. package/examples/tpl_timeline.tsx +0 -244
  67. package/examples/tpl_triage.tsx +0 -112
  68. package/examples/tpl_wizard.tsx +0 -223
  69. package/src/discrepancy.tsx +0 -114
  70. package/src/match_sides.tsx +0 -79
  71. package/src/record_fields.tsx +0 -68
  72. package/src/review_card.tsx +0 -172
  73. package/src/scored_option.tsx +0 -138
  74. package/src/spec_list.tsx +0 -81
  75. package/src/triage_row.tsx +0 -99
package/AGENTS.md CHANGED
@@ -75,12 +75,12 @@ Pick by capability, not by name. (→ the source file for the API.)
75
75
  `InlineNumberInput` · `InlineSelect` · `InlineMemberSelect` · `InlineDatePicker` ·
76
76
  `InlineTimePicker`; a READ-ONLY field in that same column uses `InlineStatic` (matches the
77
77
  editor box exactly, no input chrome, so it aligns pixel-for-pixel) (see §Data entry).
78
- - **Tasks / to-dos**there is NO Task component (a task row varies too much app-to-app to abstract
79
- well a Reminders row and a Linear column-grid share almost nothing). Compose the row directly:
80
- `CheckCircle` (the completion ring fills with a spring, distinct from the square `Checkbox`) + an
81
- inline-editable title (`InlineTextInput` with `struck` for done) + whatever meta cells you add
82
- (`OptionBadge` dot tags, `InlineDatePicker`, `InlineMemberSelect`). The one reusable primitive is
83
- `CheckCircle`; the rest is composition — see `tpl_tasks` (quick list) and `tpl_task_board` (columns).
78
+ - **Tasks / to-dos — pick by ALTITUDE.** A task-management PAGE (many tasks, grouping, filters,
79
+ expandable rows) is COMPOSITION there is NO Task component (a Reminders row and a Linear
80
+ column-grid share almost nothing): `CheckCircle` + a struck `InlineTextInput` + your meta cells;
81
+ see `tpl_tasks` (quick list) and `tpl_task_board` (columns). A record's own small CHECKLIST
82
+ (the 5–8 tasks living in a drawer/section) is the `Checklist`/`ChecklistRow` compound +
83
+ `SuggestionChip` commons + `CaptureRow` — see `tpl_record`/`tpl_item_list`.
84
84
  - **Tabular data — pick by SCALE + intent.** Two columnar shapes, and the choice is about data size:
85
85
  - **High-volume register** (thousands+ you BROWSE) — `Table` (columns defined once; sortable
86
86
  headers via `SortHeader`; paired with `Pagination`) + read-only rows that open a `Drawer` to edit.
@@ -100,7 +100,9 @@ Pick by capability, not by name. (→ the source file for the API.)
100
100
  - **Numbers & charts** — `KPIStrip` (the dashboard stat band) · `SummaryLine` (the light inline
101
101
  register/list summary — below the toolbar, from the filtered rows) · `KPICard` / `Metric` (headline figures), `TrendChip`
102
102
  (delta), `Sparkline`, `BarChart` / `LineChart` / `PieChart` (the canonical SVG set — no
103
- recharts), `RingGauge`, `ProgressBar` / `StackedProgressBar` / `StepProgress`, `Breakdown`
103
+ recharts), `RingGauge`, `ProgressBar` (its `compact` prop = ONE row, track + a plain sm
104
+ tabular count beside it — the cell/heading/peek-trigger meter; a caption floating above a
105
+ tiny bar reads misaligned) / `StackedProgressBar` / `StepProgress`, `Breakdown`
104
106
  (a stacked bar + ranked share rows, pressable to drill; `maxRows` folds the long tail behind a
105
107
  "Show N more" toggle — `labels` to localize — so several facet cards align to one height in a row),
106
108
  `Funnel` (a CONVERSION funnel — ordered stages as bars that NARROW; the step conversion rate is the
@@ -153,18 +155,22 @@ Pick by capability, not by name. (→ the source file for the API.)
153
155
  - **AI surfaces** — `Composer` (the adaptive command/chat composer — a compact pill when empty that
154
156
  expands for long text + attachments; the surface that triggers agent work),
155
157
  `AgentRun` (the live streaming work feed) + `AgentProgress` (its compact, floating, expandable
156
- form — a composer's "working" state), `ReviewCard` (THE single-proposal review card — compositional
157
- body via `children`: a `RecordFields` record (header-first) or a `MatchSides` pairing (body-first))
158
- + `Confidence`; `ChangeReview` (the BATCH review list whole-set OR 1-by-1; compositional via
159
- `renderItem`, with `ChangeDiff` for the before→after body) + `ChangeReviewActions` (its commit bar,
160
- pinned by the host in a `DialogFooter`/`DrawerFooter` or inline),
158
+ form — a composer's "working" state) + `Confidence`;
159
+ **`ChangeReview` THE one review-before-apply surface, a COMPOUND family** (frame: `ChangeReview`
160
+ · `ChangeReviewHeader` · `ChangeReviewActions`; sections: `Change` · `ChangeLabel` ·
161
+ `ChangeSummary` · `ChangeReasoning`; the grammar: `ChangeFields` + `ChangeField` ·
162
+ `ChangeRecord` · `ChangeBand` + `ChangeValueInput`): adds, updates, removals, conflicts, whole
163
+ records, display-only findings are all compositions — see §AI workflows for the laws;
161
164
  `Clarify` (the agent asks back — selectable `ChoiceList` options),
162
- `Sources` (provenance chips for AI output). For STRUCTURED AI output the review
163
- surfaces above don't cover: `SpecList` (the "exact breakdown"a labeled-value spec sheet, the
164
- panel of an answer),
165
- `Finding` (one ranked briefing/audit insight severity + metric + sources + action), `Discrepancy`
166
- (a field whose value disagrees across sources pick the truth), `TriageRow` (an incoming item the
167
- agent classified + routed), `ScoredOption` (an AI-ranked candidatescore + rationale + specs).
165
+ `Sources` (provenance chips for AI output at review scale, `label={null}` slots the chips at a
166
+ section's bottom), `Finding` (one ranked insight from an AI check localized severity word ·
167
+ title · detail · `Sources` chips · a `children` slot; **`FindingComparison`** is the
168
+ expected-vs-actual body: each disagreeing side a labeled row, the DELTA emphasized under a
169
+ hairline (localized "Difference") quantities, totals, dates; a plain `metric` prop remains for
170
+ one-number findings. The children slot composes ANY visual result a compact `Table` for
171
+ per-line detail (danger color on the offending cells), `ProgressBar` for
172
+ consumption-toward-a-cliff (free time, credit), dot `Badge`s for a present/missing checklist,
173
+ `Confidence` for judgment calls — the AI-page demos show each. Display-only — it informs the verdict the host records; `finding` slice).
168
174
  See §AI workflows.
169
175
 
170
176
  ---
@@ -176,15 +182,15 @@ This is the most common thing to get right. Match the JOB to the pattern:
176
182
  | You're capturing… | Reach for | Why |
177
183
  |---|---|---|
178
184
  | an EXISTING record's fields | **Inline edit** (`Inline*`) | edit in place, no form mode |
179
- | a brand-NEW record (a form) | **fieldset form** (`FormField` + the 2-col grid) | structured entry + a Save |
185
+ | a brand-NEW record | **create-then-refine** (`tpl_record`: "New" is ONE CLICK → a fresh Draft; everything refines in place on the record surface) | nobody fills 5 sections in one sitting; the surface is the editor |
180
186
  | a RELATED record (pick or make) | **find-or-create** (`Combobox allowCustom`) | one control covers both |
181
187
  | REPEATING rows you build & revise | **line items** (create→preview→edit) | add / edit / remove, live totals |
182
- | CHARGES that bill onto documents | **billing** (`tpl_billing`) | the invoice document is the unit |
188
+ | CHARGES that bill onto documents | **billing** (`tpl_record` Billing section) | the invoice document is the unit |
189
+ | a record's FEE/charge SUMMARY | **`Ledger`** (worked example: `tpl_item_list` drawer) | LedgerGroup (label + sum) → LedgerRow (label · meta · ONE right tabular money column; `peek` makes the row a door to its PARTICULARS in an anchored popover — references live inside the peek; `reference` = a trailing link on a non-peek row) → LedgerTotal (divider-set emphasized close, `zeroLabel` for the settled state). Pair with a Record-payment POPOVER that appends a receipt. No bars/charts |
183
190
  | a multi-value TAG field | **`Select multi`** (`renderSelected` → `Chip`) | chips composed, not a separate control |
184
191
  | ONE choice from a small visible set | **`ChipGroup` pills** (or `RadioPicker`) | required single-select, one tap, every option visible |
185
192
  | a STATUS with terminal outcomes | **disposition** (open → resolve → revise) | guides the decision |
186
193
  | FILES | **attachment field** (dropzone + grid + gallery) | add / preview / delete |
187
- | many entries FAST | **quick capture** (one row, Enter to add) | repeat-entry speed |
188
194
  | a state TRANSITION mid-flow | **stage gate** (popover / dialog by weight) | right-sized friction |
189
195
 
190
196
  ### Inline edit — the preferred way to edit an existing record
@@ -196,19 +202,33 @@ reflow, the whole point), and it commits on blur (Enter saves, Escape reverts) o
196
202
  (`format` for currency/units) · `InlineSelect` (plain options OR `renderOptionContent`; floats an
197
203
  `OptionList` in a popover so the row never grows; the RESTING value renders like its option
198
204
  (`renderOptionContent` by default; `renderSelected` overrides) — a colored `OptionBadge`, not just a label) · `InlineMemberSelect` (a `MemberChip` at rest →
199
- member picker; the inline twin of `MemberSelect`) · `InlineDatePicker` (`format="datetime"` for
205
+ member picker; the inline twin of `MemberSelect`; worked example: `tpl_record`'s "Sales owner" fact) · `InlineDatePicker` (`format="datetime"` for
200
206
  always-on time; `optionalTime` to let the user ADD/REMOVE a time — the value's own shape, date vs
201
- datetime, is the source of truth) · `InlineTimePicker` — all
207
+ datetime, is the source of truth) · `InlineTimePicker` · `InlineTagSelect` (the MULTI member
208
+ a tag SET in the inline vocabulary: selected tags render as badges inside the standard chip,
209
+ clicking floats a multi `OptionList` (checkbox rows), CLOSING commits the new set in one
210
+ `onSave` — never a borderless `Select` posing as an inline field) — all
202
211
  on `useInlineEdit` + `InlineEditView` (custom inputs join via those). `onSave` is async: the
203
212
  saving spinner sits INSIDE the control (never a sibling — that reflows); an error shows inline
204
- without losing the edit. Pair with `DetailRow` (label left, inline value right). A READ-ONLY value
205
- in the same column a computed total, a system ID, a synced/locked field — is `InlineStatic`: it
206
- copies the editor box exactly (height, padding, 1px transparent border) but is non-interactive, so
207
- it sits FLUSH with the editors instead of reading as a disabled input. To hang a right-side
208
- action/badge/unit off a row, use `DetailRow`'s `trailing` slot (wrap the editor in a `flex:1` View
209
- so it takes the slack and the trailing pins right) see the "Row anatomy" section of
210
- `tpl_record_plain`, which also reads top→bottom as a full drawer record (header fields
211
- `DangerZone`). Not every field is a same-height swapa tag field, a status, or an attachment grid
213
+ without losing the edit. A STACK of rows lives in a `DetailTable` (label ·
214
+ value · trailing laid out like a TABLE: `labelWidth` / `trailingWidth` /
215
+ `minHeight` set ONCE on the parent, plus the 6px row gap the zinc-50 chips
216
+ need) holding `DetailRow`s set `trailingWidth` when ANY row carries a
217
+ trailing action/badge, so EVERY row reserves the column and one row's
218
+ `Copy` button never makes its editor narrower than its neighbours'. An editor AT REST sits
219
+ on a zinc-50 chip THE editability affordance: users see what's editable without hovering.
220
+ `background="transparent"` opts a field out of the chipfor DENSE, uniformly-editable
221
+ surfaces (a task list/board where EVERY cell edits: the chip repeated everywhere is noise and
222
+ distinguishes nothing; hover/focus still reveal the input). Keep the chip wherever editable and
223
+ static values MIX. A `disabled` editor rests FLAT automatically — the chip is the editability
224
+ promise, and an inert field must not make it. A
225
+ READ-ONLY value in the same column — a computed total, a system ID, a synced/locked field — is
226
+ `InlineStatic`: it copies the editor box metrics exactly (height, padding, 1px transparent border)
227
+ but stays FLAT and non-interactive, so editable (chip) vs read-only (flat) is legible at a glance
228
+ and the static value never reads as a disabled input. To hang a right-side
229
+ action/badge off a row, use `DetailRow`'s `trailing` slot (NOT for units — "kg"/"₫" belong IN the value via `InlineNumberInput format`) — see the "Details"
230
+ `DetailTable` of `tpl_record`, which also reads top→bottom as a full record surface
231
+ (header → fields → `DangerZone`). Not every field is a same-height swap — a tag field, a status, or an attachment grid
212
232
  edit in place too (below).
213
233
 
214
234
  ### Fieldset form — fields lay out on a RESPONSIVE two-column grid
@@ -246,8 +266,11 @@ the chevron shows while the field is empty and a clear ✕ replaces it once ther
246
266
  pressing ✕ clears the text and reopens the full-browse list, so `onClear` lands back on the whole
247
267
  set (on focus an empty field already browses everything — `recentOptions ?? all`). `allowCustom` appends a "Create …" row (`customOptionLabel`) when the query
248
268
  matches no option, emitting the typed text as the value — so a value not in the known set means
249
- CREATE. Wire that branch to a create overlay (a modal `Dialog` for 4+ fields, an anchored popover
250
- for 1–3) that builds the new record and attaches it; existing matches attach directly. The create
269
+ CREATE. Wire that branch to a create overlay (an anchored popover for a 1–2 field gate, a modal
270
+ `Dialog` for 3+) that builds the new record and attaches it; existing matches attach directly.
271
+ A record-CREATION dialog stays a MINIMAL gate and really creates — the row lands and its own
272
+ workspace opens for refinement (an EMPTY checklist, commons as ghosts). NEVER a creation
273
+ wizard: create-then-refine puts complexity on the record surface, not in front of it. The create
251
274
  row sits BELOW matches by default (the keyboard highlight lands on the first MATCH, so Enter on a
252
275
  partial picks it, never a duplicate); pass `customOptionPlacement="top"` to pin it above. Use
253
276
  `reflectSelection={false}` and render the attached record below as a card with a Change action.
@@ -267,12 +290,61 @@ snapshot the item on Edit so Cancel reverts, or DISCARDS a freshly-added one. Du
267
290
  the destructive **Delete is `danger-secondary`**; Cancel + the Save/Edit toggle sit right. Every
268
291
  screen composes its own (~15 lines) so the preview rows + form fit the data.
269
292
 
270
- ### Billing & invoicing the invoice DOCUMENT is the unit (`tpl_billing`)
293
+ ### Handoffa stage transition, never a message
294
+ When work crosses departments (sales → operations → accounting), the HANDOFF is the RECORD
295
+ changing desks — never an inbox, a notification, or a copied task. Two types:
296
+ **(A) Same entity** → a STAGE field on the shared record: sections carry OWNER dot tags, and
297
+ the handoff is MANAGED AS TASKS — each desk's checklist on the record (the task-row vocabulary:
298
+ sibling `CheckCircle` + struck transparent `InlineTextInput` + per-task `InlineMemberSelect`
299
+ assignees + the `CaptureRow` add-affordance — never a decorative progress strip). A NEW record
300
+ starts with an EMPTY checklist — tasks truly vary. The commons split in two: MANDATORY tasks are
301
+ seeded by the app (a workflow on create, per record type) — no human types them; common-but-
302
+ OPTIONAL tasks appear as `SuggestionChip`s under the list — a PILL, never a row,
303
+ so a suggestion can't be mistaken for a task (tap = materialize, ✕ = dismiss for this record;
304
+ already-present labels filter out; suggestions never count in done/total and pause while a
305
+ filter narrows the view) — the same suggestion grammar as the
306
+ billing Standard pill. Real rows carry a ⋯ `menu` (an `ActionMenu`; Delete lives BEHIND it, danger-styled and last — never a bare ✕ a stray tap can hit), completing the checklist's CRUD. Open
307
+ tasks INFORM
308
+ the handoff, they NEVER block it: the CTA stays enabled, the count warns, and open tasks carry
309
+ over. The handoff CTA opens a DIALOG for the receiving desk (assignee `MemberSelect` + an
310
+ optional note; the open-task warning inside); confirming closes the drawer — the record left
311
+ this register. Tasks PEEK from the register: the done/total column is a pressable
312
+ compact-`ProgressBar` trigger whose popover holds the checklist on FIXED columns — every row
313
+ ring · struck `InlineTextInput` title (flex) · quick-reassign `InlineMemberSelect` (140) — no
314
+ expandable rows (tags/files depth is the Task list template's lesson, not the peek's); the
315
+ popover body is `PopoverContent`'s own ScrollView (`disableBodyScroll` is ONLY for children
316
+ that manage their own scroll, like `OptionList`). The DRAWER carries a real Tasks SECTION in
317
+ the Record template's shape (heading + the compact meter + the same checklist) — one shared
318
+ task state per record feeds the column, the peek, the section, and the handoff dialog;
319
+ a section owned by a later desk sits visible but GATED ("Billing opens when the record reaches
320
+ Accounting"). Registers scope by stage — the receiving department's register IS its inbox. The
321
+ sender's sections stay editable after handoff (the gate transfers RESPONSIBILITY, not access —
322
+ locking is app IAM, not template grammar). Worked example: `tpl_record`.
323
+ **(B) Different entity** (a deal → a shipment) → SPAWN-AND-LINK: the upstream record's terminal
324
+ gate CREATES the downstream record on the shared spine, linked both ways (a linked-record row
325
+ each side); usually TWO apps — the upstream closes its own lifecycle, the downstream starts
326
+ fresh in its department's app. Never merge the two lifecycles into one record.
327
+
328
+ ### Sequential phases on one record + the outline rail
329
+ When a record's work happens in ORDERED PHASES (gate in → gate out; receive → dispatch), the
330
+ phases are NOT tabs or a segmented control — hiding the other phase loses the context the
331
+ current one needs and buries why its gate is blocked. Both phase sections sit on ONE page in
332
+ chronological order; each phase owns ITS OWN fee rows + phase total (the invoice-band idea one
333
+ level up — money never reads as one pot); each closes with a SEQUENTIAL confirm gate that names
334
+ its blockers ("Confirm gate in first."). The header status chip carries the phase. A LONG record
335
+ surface pairs with a LEFT OUTLINE RAIL — `MenuButton` items + `useSectionNav` (scroll-spy: jump
336
+ to a section, the highlight follows the scroll); on narrow containers the rail becomes a
337
+ PINNED bar naming the current section that opens a full-page section-picker `Modal`. A per-phase
338
+ dot on a rail item carries its confirmed state. A direction that is a TYPE (one record per gate
339
+ EVENT) is instead a discriminator chosen ONCE at creation — a `SegmentedControl` in the create
340
+ step, never a toggle on the record.
341
+
342
+ ### Billing — the invoice DOCUMENT is the unit (`tpl_record` Billing section)
271
343
  When charges get grouped into issuable documents (an e-invoice, a bill) and then collected, don't
272
344
  split the screen into "enter fees here, issue there" — that smears one job across two places. Make
273
- **each invoice a `Card` that holds its own editable charge lines** (amount input + payment method),
274
- its **live total**, its **status badge** (nothing-to-bill · draft · issued + ref), and its **issue
275
- action** in the footer. A charge never lives apart from the document it bills on. Issuing is gated
345
+ **each invoice a FLAT hairline-set band that holds its own editable charge lines** (amount input +
346
+ payment method), its **live total**, its **status badge** (nothing-to-bill · draft · issued + ref),
347
+ and its **issue action** in its closing row — no card chrome; Data-capture templates are flat. A charge never lives apart from the document it bills on. Issuing is gated
276
348
  **inline, never a dead end**: when a prerequisite is missing (a bill-to tax ID, a method on a
277
349
  charged line) the issue button disables with one muted line saying what's needed; the payment-method
278
350
  picker turns required the instant a line carries an amount. Issuing a real e-invoice is irreversible
@@ -383,12 +455,6 @@ copy stay local):
383
455
  NEW blob for re-upload (`useImageRotation` is view-only; this is how a rotation is persisted). Pair with
384
456
  `FileGalleryModal`'s `onPersistRotation`/`persisting` (the ✓ shown on a rotated image).
385
457
 
386
- ### Quick capture — the SPEED surface
387
- For fast repeat entry (logging activity, expenses), one compact capture row (a `SegmentedControl`
388
- mode, a `Combobox`-as-select, a `Counter`, an Enter-to-submit `TextInputField`); each entry commits
389
- on its own into an undoable log below, the field keeps focus and the prior choices carry over. No
390
- screen-wide Save.
391
-
392
458
  ### Stage gates — tiered by weight
393
459
  A transition that needs NO input is one click. 1–3 quick fields → a POPOVER FORM anchored to its
394
460
  action button (title + one-line stake + `FormField`s; confirm in `PopoverFooter`). A
@@ -406,6 +472,27 @@ accepts / edits / dismisses, the deterministic app applies. The agent owns judgm
406
472
  (recognition, estimation, intent→parameters); the app owns geometry, math, and the write.
407
473
  Compose the surfaces as a loop, and reach for the right one by job:
408
474
 
475
+ **Which AI surface — decide by the OUTCOME's shape, not the task's topic.** Three shapes:
476
+
477
+ 1. **Field writes** (extract, match, rank, classify → the record changes) → **in-app agent**
478
+ (`useAgentRun`) + **`ChangeReview`**. The commits must obey the diff law (Keep/Drop per
479
+ change, app-workflow writes, bounded app authority), and the operator wants one button +
480
+ a review — never a prompt box.
481
+ 2. **Evidence for an in-app decision** (cross-check, audit, tie-out → nothing is written; the
482
+ operator acts on what was found) → **in-app agent** + **`Finding`**. Still bounded and
483
+ prompt-free (an optional instructions brief at most): the brief is fixed, the output is
484
+ structured display-only findings read IN the record's context, and there is no conversation
485
+ to have — the loop closes when the operator acts in the app. Chat would add prompting and
486
+ tear the findings away from the record they judge.
487
+ 3. **A file or an open-ended answer** (edit this document, draft from context, explain) →
488
+ **hand off to the chat agent** (`askAi` in `@lotics/app-sdk`). The loop is multi-turn with
489
+ no output schema, judged by looking — and the chat harness already owns it: preview beside
490
+ the thread, version chains, branching, session memory. An in-app "edit chat" would
491
+ re-implement all of that inside every app.
492
+
493
+ The Document desk's Use-AI fork IS this table as UI: Extract data (1) · Cross-check (2) ·
494
+ Edit with AI (3) — one entry point, three outcome shapes.
495
+
409
496
  - **Command / compose** — `Composer`, the adaptive command surface. Empty + minimal (no `footerRight`
410
497
  model picker, no `pills`, no `files`), it's a COMPACT PILL: an optional `actionsButton` (attach) left,
411
498
  the input, a send arrow right (Enter sends, Shift+Enter for a newline) — the same geometry
@@ -439,28 +526,47 @@ Compose the surfaces as a loop, and reach for the right one by job:
439
526
  free — no hand-assembly). Transparent work, NEVER a bare spinner. On a canvas/composer app reach for
440
527
  `AgentProgress` — `AgentRun` collapsed into a floating pill (avatar + current step) that EXPANDS on
441
528
  press; the composer morphs into it while running, and reveals again when done.
442
- - **Review before apply — ONE card + the batch engine, the BODY is compositional.** Every "the agent
443
- proposes → the human accepts / edits / dismisses → nothing auto-applies" surface wears the same
444
- `reviewCardStyle` + an optional `Confidence`. There are two primitives:
445
- - **`ReviewCard`** THE single-proposal review card. It owns the chrome (eyebrow + `Confidence`,
446
- rationale, a generic `actions` footer Accept/Edit/Dismiss, Confirm/Remove, Accept/Reassign,
447
- whatever the verbs are and a collapse-to-a-row resolved state); the BODY is `children`. **Layout
448
- follows the props**: give it an `eyebrow`/`title` and the chrome LEADS (header-first a value or
449
- record proposal, e.g. a `RecordFields` `tpl_extract`); give it only `children` and the BODY leads,
450
- the confidence + reason beneath it (body-first a `MatchSides` pairing where the link is the hero
451
- `tpl_match`). Stack several (rank by confidence) for a shortlist or a document's records.
452
- - **`ChangeReview`** THE batch engine for a SET you decide each then commit together. `renderItem`
453
- draws each proposal (a `ChangeDiff` before→after body, a record, an editable form), 1-by-1
454
- Keep/Drop collapses each (e.g. ingested rate lines `tpl_ratedesk`); whole-set mode reviews the
455
- lot. It is the LIST only the commit bar (Accept all / Apply / Discard) is the SEPARATE
456
- **`ChangeReviewActions`**, which the host places where commit bars belong (a `DialogFooter` /
457
- `DrawerFooter`, or inline under the list). Don't bundle a commit into a scroll bodylike every
458
- other footer in the kit it's the container's to pin (`tpl_ratedesk` puts it in the `DialogFooter`).
459
- `ChangeReviewActions` reads the same `items`/`statusOf`, so it owns Accept-all + the kept count —
460
- never re-derive them in the host.
461
- The body atoms (`RecordFields`, `MatchSides`, `ChangeDiff`) are pure presentation usable inside
462
- either engine OR standalone (a live-edit field card). The decide / accept-all / apply mechanics are
463
- never re-implemented per surface.
529
+ - **Review before apply — ONE surface, the compound `ChangeReview` family.** Every "the agent
530
+ proposes → the human accepts / edits / dismisses → nothing auto-applies" surface composes from
531
+ twelve pieces in three groups. **Frame**: `ChangeReview` (context provider + stack; hairline
532
+ dividers between `Change` sections and `ChangeFields` rules between fields) ·
533
+ `ChangeReviewHeader` (the SECTION headingmd semibold; keep it lean: never repeat the record
534
+ the dialog is already about) · `ChangeReviewActions` (the commit bar pin it in the
535
+ `DialogFooter`/`DrawerFooter`; **Keep all** bottom-left presses every pending decision pass
536
+ `onAcceptAll` when field state lives in the host; the "N of M kept" counter reads HERE beside it,
537
+ never in the header; Apply disables at 0 kept or via `applyDisabled`). **Sections**: `Change`
538
+ (heading + free body + optional verbs + collapse omit both callbacks for display-only) ·
539
+ `ChangeLabel` · `ChangeSummary` (the collapsed row's content) · `ChangeReasoning` (the agent's
540
+ quiet hairline aside only when not self-explanatory). **The grammar**:
541
+ - **`ChangeFields` + `ChangeField`** THE field unit. `ChangeFields` is the OPEN form (the
542
+ section IS the recordan extract dialog's one order: no card chrome). Each `ChangeField`:
543
+ label (sm, muted — the DetailRow convention) · the `−` band when replacing (`before`) · the value — `ChangeValueInput`
544
+ by default (the green `+` band; pressing it edits IN THE BANDa borderless input with
545
+ identical type metrics, so nothing shifts; `unit="pcs"` fixes the suffix outside the editable
546
+ core type the number, never the unit) or any input · conflict `candidates` as full-width decision rows + the localized
547
+ "Type another value" third option (`custom*` props; the outcome band is read-only via
548
+ `valueReadOnly` the decision comes from picking, never from editing the band; gate with
549
+ `keepDisabled`) · row `reasoning` LAST · per-field **Keep/Drop** bottom-right
550
+ (`status`/`onKeep`/`onDrop`/`onUndo`). A pure REMOVAL is `before` with an empty value — the
551
+ `−` band alone. A decided field collapses to the compact one-row card (mark · label →
552
+ `summary` · Undo).
553
+ - **`ChangeRecord`** — THE item card for SETS of records (order lines): `id` (it registers in
554
+ the review context like a `Change`), `tone` add/edit/remove, the tinted header band with the
555
+ localized op word (Add/Edit/Delete · Thêm/Sửa/Xóa) + title, body = its `ChangeField`s, and
556
+ **the verb level follows the decision level**: add/remove carry ONE card Keep/Drop (fields
557
+ none — editable parts, one write); edit carries NO card verbs (only its changed fields, each
558
+ deciding for itself — dropping a field narrows the update diff). Collapses via `summary`.
559
+ - **`ChangeBand`** — the raw diff band (aligned `+`/`−` marker column, light emerald/red, dark
560
+ text) every piece builds from; reach for it directly for custom strokes.
561
+ **The scaling boundary (known, deliberate)**: `ChangeFields` + `ChangeRecord` cards cover one
562
+ record through ~10; a BULK review (30+ imported rows — a bank statement, an Excel import) needs a
563
+ dense form that does not exist yet — design it against the first real migration, not
564
+ speculatively.
565
+ **The laws**: ONE verb pair everywhere — Keep/Drop, localized; never rename per shape (Apply is
566
+ the only outcome-named button). Candidate rows carry the VALUE only; provenance = `Sources` at
567
+ the VERY bottom of the review (after every change; or omit it — lean beats decorated). The HOST owns every decision in plain `useState`; the family owns the
568
+ mechanics (collapse + Undo, the counter, Keep-all, apply gating). Never apply a field with no
569
+ value — gate unresolved conflicts. Editing IS the review.
464
570
  - **Ask back** — `Clarify`: when the agent is unsure, it asks a question with quick-reply options
465
571
  and PAUSES, instead of guessing wrong. Human-in-the-loop input mid-run.
466
572
  - **Provenance** — `Sources`: openable chips saying where the output came FROM (records, a
@@ -476,54 +582,42 @@ a conversation, a single pass, or generated prose:
476
582
  - **Canvas** (`tpl_dieline`) — the page IS the design on a pannable/zoomable surface: it stays
477
583
  CENTRED at every zoom (a floating zoom pill bottom-left), the FLOATING composer at the bottom (a
478
584
  `Composer`: attach a photo → review/remove it → add a note → send, with `sendDisabled` allowing a
479
- file-and/or-text submit — NOT auto-run on attach) morphs into `AgentProgress` while running, and a pinned PARAMS PANEL (a `RecordFields` field-card
585
+ file-and/or-text submit — NOT auto-run on attach) morphs into `AgentProgress` while running, and a pinned PARAMS PANEL (a label/value field-card
480
586
  in live-edit mode, carrying the single Download; minimizes to a pill) floats centre-right. Panel, zoom pill and composer
481
587
  float ON TOP — they never shift the design. (Floating layers use a `pointerEvents:"none"` wrapper
482
588
  with the interactive child set `"auto"`; RN-Web ignores `"box-none"` in style, so a full-width
483
589
  wrapper would otherwise eat clicks on the canvas behind it.) Change the design by prompt ("5 mm
484
590
  taller") OR by editing a param directly — either re-flows it in place. For a design/document the
485
591
  user shapes over time.
486
- - **Chat** (`tpl_assistant`) — a conversation where the agent's answer carries suggested edits the
487
- user approves/rejects **1-by-1** (`ChangeReview` per-item), applying only the accepted set. An AI
488
- copilot that proposes changes to a record and never commits on its own.
489
- - **One-shot** (`tpl_extract`) — read an image → the agent extracts the records → review each as a
490
- `ReviewCard` wrapping a `RecordFields` body (fields click-to-edit, Confirm collapses it to a
491
- checklist row) → Save all to the table. A data-capture pass; nothing stored until confirmed.
492
- - **Bulk ingest desk** (`tpl_ratedesk`) — the page IS a filterable register (a From → To route picker
493
- + `ColumnFilter` pills → `Card padding:0` + full-bleed `Table`), the register the hero. Updating
494
- prices is ONE header CTA that opens a Dialog wizard: drop SEVERAL source docs at once → the agent
495
- reads them all (`AgentRun`) → a `ChangeReview` 1-by-1 (each proposal a Keep/Drop card, New / Updated
496
- with old→new delta, "Accept all" + Apply) → the kept rows land in the register. Pairs `tpl_extract`'s
497
- ingest with `tpl_directory`'s register. For a maintained reference table — rate card, price list,
498
- tariff, catalogue: re-ingest the source sheets beats hand-typing.
499
- - **Draft** (`tpl_draft`) — the agent writes a first draft from context; the OUTPUT itself streams
500
- (the text fills in live), then becomes an editable surface the human refines before sending.
501
- Tone/length steer it. For generated prose — a reply, a quote, a listing.
502
592
 
503
593
  The next six output STRUCTURE the first four can't — when the answer is a panel of facts, a queue of
504
594
  decisions, or a ranked set, don't cram it into chat prose. Each pairs a template with a primitive:
505
- - **Answer desk** (`tpl_lookup`, `SpecList`) — describe the goods on the LEFT → the agent RANKS the
595
+ - **Answer desk** (`tpl_lookup`) — describe the goods on the LEFT → the agent RANKS the
506
596
  matching codes (NEAREST MATCHES, the top one Recommended) → pick one and its STRUCTURED answer pins on
507
597
  the RIGHT (a verdict header + an exact breakdown + the policies + sources). Input → matches → pick,
508
598
  NOT a single confident verdict: classification is ambiguous, so the alternatives are first-class and
509
599
  picking a different one changes the duty; refining the description re-ranks. For look-up-and-explain:
510
600
  tariff/HS, fee lookup, policy Q&A, a spec/compliance desk. (NOT a chat with the answer in a bubble.)
511
- - **Match** (`tpl_match`, body-first `ReviewCard` + `MatchSides`) — the agent proposes PAIRINGS with
512
- its reasoning; each is a `ReviewCard` (no title, so body-first) whose body is a two-sided `MatchSides`
513
- pairing (source proposed counterpart), with the confidence + reason beneath, accept / reassign /
514
- reject, high-confidence batch-accept. The AI reconciliation / dedup / correlation queue. (Strip the agent's
515
- reasoning and it would be a plain deterministic match register the reasoning is the tell for when
516
- you need this.)
517
- - **Briefing** (`tpl_briefing`, `Finding`) — a generated narrative over the data + a RANKED list of
518
- what needs attention, each finding carrying a severity, a metric, its sources, and one action. The
519
- leadership digest / anomaly scanthe agent's COMMENTARY, not a raw dashboard.
520
- - **Cross-check** (`tpl_crosscheck`, `Discrepancy`)the agent compares the documents of one
521
- transaction and surfaces every field that DISAGREES; resolve each to a source of truth or flag it.
522
- The audit / 3-way-match desk. Symmetric (N sources), unlike a before→after edit.
523
- - **Triage** (`tpl_triage`, `TriageRow`) an inbox the agent classified + routed; accept the call,
524
- override, or dismiss, high-confidence in bulk. Leads, tickets, documents, emails.
525
- - **Compare** (`tpl_compare`, `ScoredOption`) the agent SCORES and ranks options with its rationale
526
- and the key specs; the human picks one. Quotes, carriers, suppliers, plans.
601
+
602
+ - **Document desk** (`tpl_documents` THE go-to for document-driven records) the record's files
603
+ block feeds ONE "Use AI" entry that FORKS into the two document tasks, each a specialized run with
604
+ a task-pure result: **Extract** (files read fields already matching fold into one quiet line →
605
+ every add / update / conflict a `ChangeField` (struck before · bordered input · candidate chips),
606
+ the record's current value a first-class choice — plus proposed new lines as record-body `Change`s
607
+ one outcome-named `ChangeReviewActions` commit) and
608
+ **Cross-check** (documents compared against the record and each other ranked `Finding`s
609
+ severity · title · the prominent metric · sources separated by hairlines; the findings ARE
610
+ the outcome the human acts on no phantom "record verdict" write, a persisted check-status
611
+ goes stale on the next edit). The fork carries an OPTIONAL instructions field the user steers what the agent
612
+ checks or extracts, so `Finding` serves ANY file-based AI request, not just the stock
613
+ cross-check. Every file list in the flow opens the full-page `FileGalleryModal`. Plus **Create documents**: a readiness checklist (unchecked by
614
+ default, missing inputs called out) generate the files land back on the record. Never merge the
615
+ two AI tasks into one mixed output the fork is the design.
616
+ - **Triage** an inbox the agent classified + routed is a `ChangeReview` of `Change`s (body: the
617
+ item + the agent's call; verbs Accept / Dismiss; Accept-all covers the high-confidence sweep).
618
+ - **Compare / ranked pick** — one `ChangeField` whose `candidates` carry the ranked options
619
+ (`description` = the score/reason, `source` where it came from); the human picks one. Quotes,
620
+ carriers, suppliers, plans.
527
621
 
528
622
  The AI vocabulary has **no purple accent and no gimmick glyphs** (no sparkles) — but it is NOT
529
623
  monochrome: **colour is used where it carries meaning, not for decoration.** What the violet sparkle
@@ -533,17 +627,16 @@ a left-ruled margin note (a hairline rule + muted text), quoted apart from the f
533
627
  controls — while **status/severity/diffs use functional colour** the way the rest of the kit does:
534
628
  - **Confidence** — a 3-tick meter + the full phrase ("High confidence" / "Medium" / "Low"), emerald /
535
629
  amber / zinc by level; `labels` to translate (one phrase per level).
536
- - **Finding severity** — a coloured dot `Badge` + metric: red critical, amber warning, blue note,
537
- emerald on-track (+ stacking order, most severe first). The badge words localize through
538
- `LoticsLocaleProvider` (`locale.finding`, override per-instance via `labels`) — never hardcode them.
539
630
  - **Stepper / AgentRun nodes** — progress dots on a spine: `current` a **pulsing accent ring** (white
540
631
  centre), `done` a filled accent dot + **white check**, `upcoming` a faint **grey** ring, the terminal
541
632
  `complete` a **blackish ring + black check**, `warning` **amber**. Default accent is neutral ink
542
633
  (`color` themes it); `AgentRun` keeps the neutral default — an active group's tail pulses (`current`),
543
634
  a settled group is a `done`/`warning` row, and there is no global terminal node (the run ends on text).
544
- - **ChangeReview diffs** — the old value struck in **red**, the new value in **green**.
545
- - **Discrepancy** the agent's pick gets a **blue** highlight; `Mismatch` reads amber, `Resolved`
546
- emerald.
635
+ - **ChangeBand** — the removed value on the light **red** band with the `−` marker, the incoming
636
+ value on the light **emerald** band with `+` (the GitHub-diff idiom, markers in one aligned
637
+ column); everything else in a review stays neutral — a decided row reads a single emerald check,
638
+ an add/remove record card wears the quiet tone wash (50 body · 100 header · 200 border). Colour
639
+ marks the change, never the chrome.
547
640
 
548
641
  Card chrome (borders, microlabels) stays neutral — colour marks the *state*, never the container.
549
642
  The **composer keeps its icons**: `Composer` compact is a single-row pill with an optional circular
@@ -563,11 +656,14 @@ was only ever about the review surfaces' sparkles/severity glyphs, not functiona
563
656
  a period filter — never a summary Badge (those belong to the KPI strip).
564
657
  · **Card header** (a card's own title band, or separate banded cards): `CardHeader` +
565
658
  `CardHeaderTitle` (`sm semibold`; `info` when the title alone doesn't define the numbers).
566
- · **Section title** (a substantial titled block a form, a timeline, a field group — INSIDE one
567
- multi-section card or drawer): a `Text size="sm" weight="semibold"` at the TOP of the section's
568
- content, sections separated by ONE `Divider`. Do NOT wrap each section in its own `CardHeader`
569
- (its auto-divider double-stacks hairlines around every title). A card-less full page uses
570
- `SectionHeading` + `SectionHeadingTitle`. NEVER a bare eyebrow standing in for a section title.
659
+ · **Section title** — ONE construct: `Section` > `SectionHeading` > `SectionHeadingTitle`
660
+ (lg semibold + optional muted `description`/`info`), everything left-aligned at the column
661
+ edge. The `Divider` goes BETWEEN sections (page column `gap: 32`, a bare hairline separating
662
+ one section from the next) NEVER directly under the heading, which orphans the title from
663
+ its own content. Inside a DRAWER, a full multi-section WORKSPACE uses the real
664
+ `SectionHeading` (lg) — its meta + CTA slots carry each section's count and action; a
665
+ `Text size="sm" weight="semibold"` stand-in is only for small sub-groups. Do NOT wrap each section in its own `CardHeader`, do NOT
666
+ hand-roll sm-semibold lead lines on a flat page. NEVER a bare eyebrow as a section title.
571
667
  · **Eyebrow / label** (`<Text size="xs" color="muted" weight="medium">` — **sentence case, NEVER
572
668
  `transform="uppercase"`**): a small quiet label above or beside content — an artifact tag
573
669
  ("Proposed", "Question", "Suggested edit"), a field name, a metric caption, a minor one-line
@@ -639,13 +735,16 @@ was only ever about the review surfaces' sparkles/severity glyphs, not functiona
639
735
  </Card>
640
736
  ```
641
737
  Never nest cards; never hand-compose a title or footer band. `info` is standard, not garnish.
642
- - **Flat work execution vs carded monitoring.** A `Card` is ALWAYS a bordered, lifted container —
643
- there is no "flat card". MONITORING / dashboard screens group with cards (and the `KPIStrip`
644
- band). WORK-EXECUTION screens — registers, lists, the worklists an operator acts on — are FLAT:
645
- the register/list sits DIRECTLY on the white canvas with no Card wrapper; separation comes from
646
- the row wash, gaps, and `Divider`/`CardHeader`/`CardFooter` bands used BARE. Reach for a Card only
738
+ - **Flat work execution & data capture vs carded monitoring.** A `Card` is ALWAYS a bordered,
739
+ lifted container — there is no "flat card". MONITORING / dashboard screens group with cards (and
740
+ the `KPIStrip` band). WORK-EXECUTION screens — registers, lists, worklists AND **data-capture /
741
+ record screens (forms, wizards, billing, settings, inline records) are FLAT**: content sits
742
+ DIRECTLY on the white canvas with no Card wrapper; separation comes from the row wash, gaps,
743
+ `SectionHeading` + `Divider` rhythm. Reach for a Card only
647
744
  where a screen holds a DISTINCT region that must be told apart from another; a single register or
648
- list filling the screen needs none.
745
+ list filling the screen needs none. ONE carve-out inside a flat form: **repeating SUB-OBJECTS
746
+ (line items) are each their own small Card** (`padding: 16`), so discrete items
747
+ read as discrete objects; hairlines between them blur into one run-on list.
649
748
  - **Summaries — two altitudes, never mixed.** A DASHBOARD opens with the boxed stat band
650
749
  `<KPIStrip items={[{label, value, trend?, caption?, tone?, info?}…]}>` (a `Card` of `KPICard`
651
750
  columns). A REGISTER/LIST (work-execution) screen instead carries a LIGHT inline
@@ -726,7 +825,9 @@ was only ever about the review surfaces' sparkles/severity glyphs, not functiona
726
825
  `Drawer` (above). (2) **Act on many** — a flat list with a leading `CheckboxInput` per row + a
727
826
  select-all `CardHeader` band, plus a bottom-pinned `<FloatingActionBar count label onClear>`
728
827
  carrying the bulk action(s) while ≥1 row is ticked (`tpl_item_list`); a row
729
- that can't take the action has NO checkbox. When a region genuinely must sit beside the list (a
828
+ that can't take the action has NO checkbox. Bar weight gradient, left right: the built-in
829
+ Clear is muted (the quietest act), destructive bulk = `danger-secondary` + icon, secondary
830
+ bulk = `secondary` + icon, ONE primary CTA last — never repeat the count in a CTA title; the bar's label already carries it. When a region genuinely must sit beside the list (a
730
831
  source/remainder summary, a capacity picker) make it a TOP summary header or move it into a
731
832
  Popover/sheet at the decision point — not a standing column.
732
833
  - **One control radius** — every interactive control (`Button`, the inputs/selects/pickers,
@@ -808,13 +909,11 @@ Full worked-example screens ship in the package — **read the source at
808
909
  `node_modules/@lotics/ui/examples/<name>.tsx`** (pure `@lotics/ui` + mock data, each a usable
809
910
  recipe for a screen JOB; copy and adapt). Pick by the job:
810
911
 
811
- - **Monitor & decide** — `tpl_dashboard` (KPIStrip + trends + attention list) · `tpl_stock`
812
- (10k-record faceted funnel) · `tpl_report` (scope-first lookup report a header period +
813
- one-of-N dimension search drives KPIs → aligned facets w/ a `maxRows` long-tail toggle →
814
- paginated register → export) · `tpl_tower` (live `StatusGrid` wallboard) · `tpl_pivot` (the
912
+ - **Analytics** — `tpl_dashboard` (KPIStrip + trends + attention list) · `tpl_stock`
913
+ (10k-record faceted funnel) · `tpl_tower` (live `StatusGrid` wallboard) · `tpl_pivot` (the
815
914
  `Matrix` cross-tab desk: one dimension × another, number + heat per cell + row/col/grand totals,
816
915
  press a cell to drill the list behind it) · `tpl_rollup` (hierarchical totals).
817
- - **Work execution** — every screen is FLAT (no Card) and ONE of two shapes: a register/list you
916
+ - **Work** — every screen is FLAT (no Card) and ONE of two shapes: a register/list you
818
917
  WORK EACH of (press a row → workspace `Drawer`) or a list you ACT ON MANY of (tick rows →
819
918
  `FloatingActionBar`) — never a side-by-side panel. `tpl_item_list` (THE canonical register and the
820
919
  consolidated work-list: search + a status `Select` + facet `FilterChip`s LEFT, the New CTA RIGHT in
@@ -822,11 +921,46 @@ recipe for a screen JOB; copy and adapt). Pick by the job:
822
921
  carries a leading checkbox; a select-all band, footer `Pagination`, and a `FloatingActionBar` for
823
922
  the bulk action over the ticked rows; a per-row PRINT button (the trailing action column) is the
824
923
  row's primary action and the ⋯ its overflow; a row that can't take the bulk action gets a DISABLED
825
- checkbox (here an "Awaiting docs" case — the same gating as a blocked run line) — a row press opens
826
- the workspace `Drawer`. This ONE template subsumes the old approvals / dispatch / batch / run
924
+ checkbox (here an "Awaiting docs" record — the same gating as a blocked run line) — a row press opens
925
+ the PRODUCTION workspace `Drawer`: a Details `DetailTable` showing every inline field type
926
+ (text · `InlineMemberSelect` · dates incl. optional-time · dot-select · money number · the
927
+ `InlineTagSelect` tag field · `InlineStatic` + System badge), a LINKED CASES section (the
928
+ customer's other records as `ListItem`s — the aligned row compound (title + description at
929
+ the column edge, its own 8px outdent; badge · fee · chevron right) — pressing one PUSHES an
930
+ EDITABLE workspace for that record inside the drawer via the hosted `ScreenRouter`; the
931
+ drawer HEADER swaps to back + the pushed id, back pops with scroll preserved), a Files section with its
932
+ OWN Add CTA (`FileRows` CRUD via `pickFiles` + an EXPECTED document as a ghost `FileRow
933
+ placeholder` with a Request action), the Payment `Ledger` (peekable fee rows + the Record-payment popover), an ACTIVITY section (the CRM touch-log shape: OUTCOME PILLS — `ChipGroup`, optional, tap-to-deselect — over a MULTILINE note (the note IS the record and the one requirement), an always-visible optional Follow-up `InlineDatePicker`, an "Attach files" secondary (`pickFiles` → removable `FileThumbnailGrid` in the composer), the PRIMARY Log saying WHY when disabled; below, a labelled "History" `Timeline` of outcome-typed items, newest first — an entry's attachments ride its expandable `details` as a thumbnail grid whose press opens the shared `FileGalleryModal`), and a closing `DangerZone`. Detail rows show trailing CTAs where earned (Phone → Call, Due → Today) — the case where `trailingWidth` IS set. This ONE template subsumes the old approvals / dispatch / batch / run
827
934
  screens: register, per-row action, gated selection, and act-on-many in one) · `tpl_pick` (guided `ScanField` run — a single-focus task column with
828
935
  the pick path collapsed into an `Accordion` below) · `tpl_allocate` (`RemainderMeter` split — a
829
936
  source/remainder summary header over the invoice list + Apply) ·
937
+ `tpl_record` (THE record surface, create-then-refine: "New" = a small
938
+ dialog with the identity essentials + find-or-create → lands as a Draft; the surface IS the
939
+ editor — `RecordSummary` header over its key-fact `DetailTable` + a Details `DetailTable`
940
+ (one shared column grid, auto-stacking when narrow), a CUSTOMER section (TWO states, no
941
+ swap mode: attached = the read-only-first card — Edit/Done live in the SECTION HEADING and
942
+ swap the fields to inline chips, where a trailing Fetch fills contact + city from the
943
+ tax-ID registry, un-gating billing; Remove (danger) sits low and detaches the link → the
944
+ find-or-create search, whose custom row opens the create Dialog rendering the SAME
945
+ inline-chip table + Fetch), a FILES section (dropzone add, gallery preview, delete),
946
+ and a BILLING
947
+ section OWNED BY Accounting (gated until the record reaches that stage) in the INLINE vocabulary (each invoice a hairline-set band: charge amounts as chips
948
+ with the list price as ghost placeholder + a one-tap "Standard …" suggestion pill while
949
+ unset, "How paid…" select chips, NO per-band totals — the collect band owns the number; the
950
+ band's action row sits BOTTOM-RIGHT as hint → status dot → Issue, the hint naming exactly
951
+ what blocks (fees → method → the customer gate); an ISSUED invoice keeps its lines editable
952
+ and offers Re-issue — a new lookup code replaces the old, confirmed in the same Dialog;
953
+ validated receipt; separate refundable deposit) — and
954
+ the lifecycle is a HANDOFF CHAIN (Sales → Operations → Accounting → Closed): a TASKS section
955
+ speaking the FULL Task-list grammar — a clearable Group-by `FilterChip` (Desk/Assignee/Status)
956
+ + Assignee/Status filter chips derive the groups (empty groups drop; desk heads keep their
957
+ owner dots, assignee heads are `MemberChip`s), the add-a-task capture at the TOP landing on
958
+ the current desk, rows with per-task `InlineMemberSelect` assignees — EVERY desk's rows edit
959
+ (the stage gates the handoff and Billing, never task editing; planning ahead on a later desk
960
+ is normal work) — OWNER dot tags per section, per-stage handoff CTAs NEVER blocked (open
961
+ tasks warn + carry over); "New" is ONE CLICK → a fresh record at Sales; ALSO the settings shape: Preferences switch rows + `DangerZone`; a LEFT OUTLINE RAIL (`MenuButton` + `useSectionNav`)
962
+ jumps between the sections. Absorbed the
963
+ old order-form, inline-record, intake, settings, billing, and quick-capture templates) ·
830
964
  `tpl_tasks` (the QUICK LIST — Apple-Reminders shape for personal lists: `CheckCircle` rows you tick +
831
965
  expand in place to edit (`Inline*`, stable row — the meta summary never shifts on expand), colour-dot
832
966
  tags, an attachments field (`FileThumbnailGrid` → tap a tile to the gallery for download + confirmed
@@ -837,7 +971,9 @@ recipe for a screen JOB; copy and adapt). Pick by the job:
837
971
  popover grid → gallery), a **dynamic action column** (a per-row underlined action LINK driven by each
838
972
  task's `action` descriptor — attach/approve/open, each wired in a real app to its OWN workflow: one
839
973
  board, a different action per row; the link's pressable fills the cell to match the inline editors),
840
- a trailing **`ActionMenu`** (the ⋯ overflow — a `danger` Remove + confirm
974
+ a trailing **`ActionMenu`** (the ⋯ overflow — a `danger` Remove + confirm; CUSTOM pressable
975
+ cells (files, the action link) hover with the control-surface BORDER reveal (`HOVER_BORDER`),
976
+ the same language as the inline-editor cells beside them — never their own background wash
841
977
  today, more row actions later), per-group add rows that pre-set the group's field, and the same
842
978
  Draft-from-notes CTA). Both `tpl_tasks` and `tpl_task_board`: filters/search left, primary CTA right, one
843
979
  row; group-by holds only real dimensions, ✕ clears to ungrouped (no "Nothing" option). **`tpl_task_board` is the inline-managed
@@ -850,30 +986,25 @@ recipe for a screen JOB; copy and adapt). Pick by the job:
850
986
  (for tap-to-expand) with those controls nested inside: RNW renders the row AND each control as a `<button>`,
851
987
  so you get `<button>`s inside a `<button>` — invalid DOM + the inner controls drop out of the keyboard tab
852
988
  order. Give "expand" its own affordance (a trailing `Pressable` over the meta/chevron), as `tpl_tasks` does.
853
- - **Data capture** — `tpl_intake` (multi-fieldset form + attachments) · `tpl_order` (the
854
- transactional form: find-or-create + line items + attachment CRUD + 2-col grid) · `tpl_billing`
855
- (charges→invoice→collect: the invoice DOCUMENT is the unit editable charge lines + method,
856
- live total, status, inline-gated issue; validated receipt; separate deposit) · `tpl_quick`
857
- (quick-capture log) · `tpl_wizard` (`Stepper` form + review) · `tpl_record_plain` (inline edit + Stage
858
- disposition + `Select` tags + Documents grid; `tpl_record_plain` = card-less).
859
- - **Records & lookup** `tpl_detail` (full record + tabs) · `tpl_directory` (searchable register) ·
860
- `tpl_timeline` (audit feed).
861
- - **Planning & time** `tpl_calendar` · `tpl_attendance` · `tpl_shifts`.
862
- - **Administration** — `tpl_settings`.
863
- - **AI workflows** — *produce*: `tpl_dieline` (the design CANVAS: photo → stream → the dieline
989
+ - **Finance** — `tpl_statements` (the income statement, balance sheet, and cash flow statement
990
+ in ONE statement grammar: right-aligned column captions over fixed money columns, items
991
+ indented under group headers, a hairline rule above every subtotal, the grand total
992
+ DOUBLE-RULED, negatives in accounting parentheses, per-cell currency-free (the meta line says
993
+ VND once), no bars/charts. The three statements TIE net income retained earnings, closing
994
+ cash = the balance sheet's cash, the loan repayment moves the debt line — keep mock books
995
+ reconciled or the template teaches the wrong thing) · `tpl_report` (scope-first lookup report —
996
+ a header period + one-of-N dimension search drives KPIs → aligned facets w/ a `maxRows`
997
+ long-tail toggle paginated register export).
998
+ - **Scheduling** — `tpl_calendar` · `tpl_attendance` · `tpl_shifts`.
999
+ - **Agents** — *produce*: `tpl_dieline` (the design CANVAS: photo → stream → the dieline
864
1000
  reveals CENTRED on a pannable/zoomable surface, the floating composer morphing into `AgentProgress`,
865
1001
  a pinned live-edit params panel centre-right; prompt OR edit a param to iterate) ·
866
- `tpl_assistant` (a chat copilot: the agent's answer carries suggested edits approved/rejected
867
- 1-by-1) · `tpl_extract` (read an image extract records → review/edit each (`ReviewCard` +
868
- `RecordFields`) Save all) · `tpl_ratedesk` (BULK INGEST desk: a register-as-hero with an "Import
869
- prices" CTA a Dialog wizard: drop several sheets → `ChangeReview` 1-by-1 → kept rows land in the
870
- register; for a maintained rate card / price list / tariff) · `tpl_draft` (draft generation: context the reply text streams into an editable surface
871
- refine send; tone/length steer it). *Structure*: `tpl_lookup` (the ANSWER desk: converse left,
872
- a pinned `SpecList` verdict panel right; a follow-up refines it) · `tpl_match` (AI reconciliation:
873
- body-first `ReviewCard` + `MatchSides` pairings with reasoning, accept/reassign/reject) · `tpl_briefing` (a generated narrative
874
- + a ranked `Finding` attention list) · `tpl_crosscheck` (the audit desk: `Discrepancy` cards where
875
- the documents disagree) · `tpl_triage` (an inbox of `TriageRow`s the agent classified + routed) ·
876
- `tpl_compare` (ranked `ScoredOption` candidates the agent scored).
1002
+ *Structure*: `tpl_lookup` (the ANSWER desk: converse left,
1003
+ a pinned verdict panel right; a follow-up refines it) ·
1004
+ `tpl_documents` (the DOCUMENT DESK files on a record the Use-AI fork: extract = the open
1005
+ `ChangeFields` review (adds/updates/removal/conflict) + proposed order lines as `ChangeRecord`
1006
+ cards, Keep-all + one outcome-named Apply; cross-check = display-only `Change` findingsa
1007
+ recorded verdict; plus the Create-documents readiness checklist).
877
1008
 
878
1009
  ---
879
1010
 
@@ -886,7 +1017,7 @@ section_heading (Section · SectionHeading · SectionHeadingTitle · SectionHead
886
1017
  option_badge (OptionBadge — a select value as its configured colored badge) ·
887
1018
  member_chip (MemberChip — avatar + name; the universal person render) ·
888
1019
  member_select (MemberSelect — a Picker of MemberChip options; the member picker) ·
889
- status_badge · button · icon_button · link · text_link (TextLink — underlined text that's optionally an `onPress` action or an `href` link, or plain underlined text to wrap in your own pressable; the neutral counterpart to the fixed-blue `Link`) · chip · tabs (Tabs — switch between content sections; WAI-ARIA tablist + roving tabindex; each TabOption takes an optional `status` ColorName → a small attention dot before its label, for a tab whose area needs work) · segmented_control ·
1020
+ status_badge · button · icon_button · link · text_link (TextLink — underlined text that's optionally an `onPress` action or an `href` link, or plain underlined text to wrap in your own pressable; the neutral counterpart to the fixed-blue `Link`) · chip (Chip — the generic pill: pressable when `onPress` (announces as a button; pass `accessibilityLabel` when children aren't self-describing text) + an absolutely-positioned dismiss ✕ sibling when `onDismiss` (its name = `dismissTooltip` ?? the `chip.remove` locale slice). Suggestion pills → `SuggestionChip`) · tabs (Tabs — switch between content sections; WAI-ARIA tablist + roving tabindex; each TabOption takes an optional `status` ColorName → a small attention dot before its label, for a tab whose area needs work) · segmented_control ·
890
1021
  picker (native `<select>`, plain label-only single) · select (Select — rich/custom-rendered, single/multi, select-all, chips via `renderSelected` + `searchable` + `allowCustom` — the tag field is just a multi Select; opens `OptionList`) · option_list (OptionList — the ONE shared searchable listbox body every selector opens: single/multi, optional internal search, create row, keyboard + native-`<select>` typeahead; host it directly in a `Popover`/`Dialog` for a command palette) · combobox (COMPOUND single-select editable search: `Combobox` root + `ComboboxInput` + `ComboboxContent`, optional `ComboboxEmpty`/`ComboboxFooter`, `useCombobox()`; over the shared `useOptionList` engine; browses on focus; no `multi` — multi-value chips → `Select multi`) ·
891
1022
  text_input_field · number_input · search_input · form_field · checkbox · checkbox_input · switch ·
892
1023
  radio_picker · counter · range_slider · date_picker · date_range_filter_field · time_picker ·
@@ -894,30 +1025,38 @@ inline_text_input · inline_number_input · inline_select · inline_member_selec
894
1025
  inline_time_picker (the Inline* family — per-field editors on `inline_edit`'s `useInlineEdit` +
895
1026
  `InlineEditView`; `InlineSelect`/`InlineMemberSelect` render the resting value like its option — `renderOptionContent` by default, `renderSelected` to override — a chip/badge at rest, not just text) ·
896
1027
  inline_static (InlineStatic — a READ-ONLY value matching the Inline* box metrics EXACTLY (height, padding, 1px transparent border) so a non-editable field — a computed total, a system ID, a synced/locked value — aligns pixel-for-pixel in the same column; non-interactive, NOT a disabled input; `muted`/`tabular`/`align="right"` for a number column, `weight="medium"` to emphasise a total among plain rows) ·
897
- list · list_item · menu_button · menu_list_item · detail_row (DetailRow label+value row for drawer/peek detail; in FORM mode (`labelWidth` set) the value column FILLS the row so a stack of inline editors all span the same width + none jumps wider on edit; optional `trailing` slot renders a right-side action/badge/unit after the value) · danger_zone (DangerZone — the destructive section: a soft danger-tinted frame (`tint`/`solid`, never raw hex) + a danger heading + a description + a destructive action slot (children, e.g. a `danger` Button); sits APART at the bottom of a record/settings surface) · pressable_row ·
898
- check_circle (CheckCircle — the completion ring: an empty ring that springs to a filled check when done, distinct from the square checkbox; the task/to-do/checklist toggle. Compose task rows directly, no Task component) · action_menu ·
1028
+ record_summary (RecordSummary the identity band of a record detail/drawer: ONE row `title` xl semibold tabular · `subtitle` sm muted · `status` Badge slot · optional `metric` {label,value,tone,note} pinned right, the band's ONE accent. The record's FIELDS never live in the header: compose them as `DetailTable`s in the sections below, the header's key facts as their own `DetailTable` right under the band on the same labelWidth/trailingWidth `tpl_record` does this. Replaces hand-rolled record headers (mixed scales, several competing figures, color noise)) ·
1029
+ list · list_item · menu_button · menu_list_item · detail_row (DetailTable + DetailRow — the record field grid. DetailRow: label+value row for drawer/peek detail; in FORM mode (`labelWidth` set) the value column FILLS the row so a stack of inline editors all span the same width + none jumps wider on edit; optional `trailing` slot renders a right-side action/badge after the value (units belong IN the value via `InlineNumberInput format`). DetailTable: the compound parent of a row STACK — `labelWidth`/`trailingWidth`/`minHeight` (default 40, the inline-control grid) declared ONCE + the 6px row gap; with `trailingWidth` every row reserves the trailing column so value cells share one width and trailing items align at one x, like a table. RESPONSIVE with no prop: it measures its own container (onLayout, not the viewport — works inside a Drawer; the unmeasured first frame renders opacity-0 so the first PAINT is already in the right mode — no reshuffle as a drawer opens) and when the columns would crush the value cell it STACKS every row (the label above a full-width value row IN THE FORMFIELD LABEL GRAMMAR — medium, default ink — so narrow record surfaces and forms read as one vocabulary; trailing beside the value, gap 14. The components stay separate: FormField = draft controls validated + committed together; inline editors self-persist); raise `minValueWidth` (default 160) when a cell holds MORE than one editor (an amount+method pair) so the table stacks earlier. Two tables on one page share one grid by repeating the same labelWidth/trailingWidth. Worked example: `tpl_record`) · ledger (Ledger + LedgerGroup + LedgerRow + LedgerTotal — the record-level money list: charge/receipt GROUPS with sums in their headers, every figure on ONE right-aligned tabular column (all lines share the 8px inset), `peek` turns a row into a pressable door floating its particulars in an anchored popover (put links INSIDE the peek — never a button in a button; `reference` is the trailing-link alternative for static rows), LedgerTotal = the divider-set emphasized close with `zeroLabel` for settled. The financial-statement grammar at record density; worked example: `tpl_item_list` drawer) · danger_zone (DangerZone — the destructive section: a soft danger-tinted frame (`tint`/`solid`, never raw hex) + a danger heading + a description + a destructive action slot (children, e.g. a `danger` Button); sits APART at the bottom of a record/settings surface) · pressable_row ·
1030
+ check_circle (CheckCircle — the completion ring: an empty ring that springs to a filled check when done, distinct from the square checkbox; the task/to-do/checklist toggle) · checklist (Checklist + ChecklistRow — the record-scoped checklist COMPOUND: it owns GEOMETRY only (row minHeight 32, gap 12, ring/title alignment, ONE `trailingWidth` so assignee cells column-align) while content stays composed — `control` takes the CheckCircle (omit onChange = read-only ring), children the struck transparent InlineTextInput, `trailing` an InlineMemberSelect, `menu` the row's ⋯ options ({items: ActionMenuItem[], accessibilityLabel}) — Delete lives BEHIND the menu, danger-styled and last, never a bare ✕ (omit on read-only rows). NARROW surfaces (a drawer/peek checklist) put the editors on the `meta` line instead of `trailing` — the second line indents past the ring so the TITLE keeps the full width and stays readable; wide surfaces use `trailing`; never both. SUGGESTIONS are never rows: offer the commons as `SuggestionChip`s under the list (tap = materialize, ✕ = dismiss; a pill can't be mistaken for a task). Close the list with `CaptureRow`. There is deliberately NO monolithic Task component — richer task-management rows (tpl_tasks expand affordances, board cards) compose their own anatomy directly) · suggestion_chip (SuggestionChip — the dismissible SUGGESTION pill: an item the record could have but doesn't yet (a common task, an expected line) as a `Chip` whose press MATERIALIZES it (plus glyph + label, one tuned anatomy) and whose ✕ refuses it; filter out labels already present; suggestions never count in totals. Chrome via the `suggestionChip` locale slice) · capture_row (CaptureRow — the add-an-item row closing an editable list: dashed empty ring + borderless input on the item-title inset + a primary Save that appears on type; Enter commits too. Controlled: `value`/`onChangeText`/`onSubmit`. One shape wherever a list grows in place — task checklists, simple item lists) · action_menu ·
899
1031
  floating_action_bar · filter_chip · column_filter (ColumnFilter — the typed per-column filter pill +
900
1032
  columnFilterToConditions; for a register filtering on several columns) · chip_group · search_input ·
901
1033
  sort_header · table · data_grid (DataGrid — the inline-managed grouped table: a grouped, sortable grid of LIVE inline-editor cells (`columns[].cell` → ANY field) + optional per-row `leading` (a CheckCircle) + `renderGroupFooter` (per-group add, align with the exported `gridRowStyle`) + `labels` (localize the sort-header a11y via `SortHeaderLabels`). Owns header/sections/rows; consumer owns data + sort/group/filter/collapse state + toolbar. Renders ALL rows — MODERATE data; 10k+ → the paginated `Table` register. Example: `tpl_task_board`) · pagination · accordion · stepper (Stepper + Step — done/current/upcoming/warning/complete progress on a track (horizontal) or spine (vertical); compound `<Step status>children` OR data `steps[]`+`current`; **navigable** via `Step.onPress` (both orientations — the whole step is the tap target) + `active` to wash the selected one, so it doubles as a section/phase switcher; the guided-run / agent-feed primitive — subsumes the old StepList) ·
902
- step_progress · timeline (heterogeneous event LOG — per-row icon + expandable details, models the past; NOT progress) · drawer (+ DrawerFooter) · dialog · modal (Modal + ModalHeader + ModalBody + ModalFooter — the full-bleed, edge-to-edge takeover: an OPAQUE surface that COVERS THE WHOLE SCREEN, so unlike Dialog (centered card WITH scrim) and Drawer (docked panel WITH scrim) there is nothing behind it to dim — NO scrim, NO backdrop. Lays children as a flex column: a pinned ModalHeader (eyebrow/title + an actions slot + close), a flex:1 scrolling ModalBody, a pinned ModalFooter (the commit bar, same chrome as DialogFooter/DrawerFooter). Reach for it for a focused capture / multi-step wizard / a console the user steps INTO, where surrounding chrome is a distraction; pick Dialog when the surface is a card the user can see context around) · popover · tooltip ·
1034
+ step_progress · timeline (heterogeneous event LOG — per-row icon + expandable details, models the past; NOT progress) · drawer (+ DrawerFooter) · dialog · modal (Modal + ModalHeader + ModalBody + ModalFooter — the full-bleed, edge-to-edge takeover: an OPAQUE surface that COVERS THE WHOLE SCREEN, so unlike Dialog (centered card WITH scrim) and Drawer (docked panel WITH scrim) there is nothing behind it to dim — NO scrim, NO backdrop. Lays children as a flex column: a pinned ModalHeader (eyebrow/title + an actions slot + close), a flex:1 scrolling ModalBody, a pinned ModalFooter (the commit bar, same chrome as DialogFooter/DrawerFooter). Reach for it for a focused capture / multi-step wizard / a console the user steps INTO, where surrounding chrome is a distraction; pick Dialog when the surface is a card the user can see context around) · screen_router (ScreenRouter + Screen + useScreenRouter — the SCREENS compound: a flat navigation stack (`navigate("/case/:id")` pushes, `goBack` pops, `canGoBack`, route `params`; stacked screens stay mounted `display:none` so scroll survives the round trip). Dialog BAKES a router in (`<Dialog><Screen route="">…`); ANY other container hosts the standalone `<ScreenRouter>` — and it wraps AROUND the container so the CHROME can read the stack: a Drawer drilling into a LINKED record swaps its header to a BACK IconButton + the pushed record's id while `canGoBack` (sequence ◀ ▶ hides — stepping the root from inside a linked record disorients), and the pushed `<Screen route="/case/:id">` is a REAL editable workspace with its own footer CTAs. Key the router by record id so stepping ◀ ▶ resets the stack. Worked example: `tpl_item_list` drawer) · popover · tooltip ·
903
1035
  alert · peek · empty_state · completion_state · callout (Callout · CalloutTitle ·
904
1036
  CalloutText · CalloutActions) · kpi_card · kpi_strip · summary_line (SummaryLine — the light inline summary of a register/list's FILTERED view, sits below the toolbar; NOT the boxed dashboard `kpi_strip` band) · metric · trend_chip · sparkline ·
905
1037
  bar_chart · line_chart · pie_chart · ring_gauge · progress_bar · stacked_progress_bar · breakdown ·
906
1038
  funnel (Funnel — conversion funnel: narrowing bars + the step rate as a bold aligned headline row, count below; `orientation` vertical|horizontal, `onSelect`/`selectedKey` press-to-drill; the subset/drop-off sibling of stacked_progress_bar's whole-split) ·
907
1039
  status_grid (StatusGrid + StatusLegend) · heatmap (density cross-tab — colour-only, no numbers) · matrix (Matrix — the PIVOT cross-tab: band-compound `Matrix` root + `Matrix.Header` (corner + axis labels) + `Matrix.Grid` (`display` number|heat|both — the cells, pressable, the value IN the cell) + `Matrix.Totals` (row + column + grand) + `Matrix.Legend`; the data layer `matrixTotals`/`MatrixAxisItem`/`MatrixCellRef` import from `@lotics/ui/matrix_totals`. Pick over `Heatmap` when the NUMBER and totals matter, not just where it clusters) · legend_item · remainder_meter · allocation_row ·
908
1040
  scan_field · file_dropzone · files_editor (FilesEditor — THE all-in-one attachment field: FileGrid + a toolbar (Upload primary · Select · Download all) that swaps into a batch SELECT mode (Select all · a Menu of Download/Share/Delete · Done; the per-tile ✕ is select-mode-only, never in the default view; `selectTileRemove={false}` drops even that so delete is menu-only) + built-in gallery (Download + inline preview; no "open in new tab") + Alert-confirmed remove; host wires `files` + `onAdd`/`onRemove` (+ optional `uploads`, `onShareSelected`, `readOnly`, `selectTileRemove`, `labels`, `galleryLabels`, `gridMaxHeight` — cap the grid height so it scrolls and the toolbar pins, for a popover/drawer); mirrors the frontend cell_files_editor. Use FileGrid/FileRows bare only when you own the chrome) · file_grid (FileGrid — the upload-aware grid: completed files + a live upload queue in one surface; FileUpload/PendingUpload types; the add-files default) · uploading_thumbnail (UploadingThumbnail — the single in-flight upload tile FileGrid renders; reach for it only when hand-rolling a non-grid upload layout) · file_thumbnail · file_thumbnail_grid · file_row · file_rows (FileRows — batteries-included file list: row press → built-in gallery + a ⋯ Download/Open-external/Remove menu; composes FileRow + ActionMenu + FileGalleryModal) · file_preview (FilePreview — the universal inline preview: image/PDF/video/audio + Word via `@lotics/docx` + Excel/CSV via `@lotics/xlsx`; the heavy engines (pdf.js · `@lotics/docx` · `@lotics/xlsx`) are LAZY (dynamic-imported, ~free until a doc of that type is opened) and SHIP AS `@lotics/ui` deps (7.14.0+) — custom-code apps get PDF/Word/Excel preview with ZERO extra install. Renders to canvas/DOM, never a nested iframe — works in the sandboxed app iframe) ·
909
- file_gallery_modal · image_gallery · use_selection (useSelection — always-on multi-select state for a register/list: the `selected` Set + `toggle`/`setAll`/`allSelected`/`indeterminate`/`count`/`clear`; selectability gating stays with the caller. The checkbox-always-visible counterpart to `use_selection_mode`) · use_selection_mode · share_or_download · rotate_image · avatar · skeleton · activity_indicator · loading · divider ·
1041
+ file_gallery_modal · image_gallery · use_selection (useSelection — always-on multi-select state for a register/list: the `selected` Set + `toggle`/`setAll`/`allSelected`/`indeterminate`/`count`/`clear`; selectability gating stays with the caller. The checkbox-always-visible counterpart to `use_selection_mode`) · use_selection_mode · use_section_nav (useSectionNav — scroll-spy for a LONG record surface with a left outline rail: keys in page order → {scrollRef, onScroll, register(key)→onLayout, jumpTo(key), activeKey}; rail items are `MenuButton`s (selected={activeKey===key}); section wrappers must be DIRECT children of the ScrollView content. On NARROW containers the rail becomes a PINNED bar naming the CURRENT section (the spy keeps it honest) that opens a full-page section-picker `Modal` (`MenuButton` list; Escape / the close control dismiss) — never a horizontal tab strip the thumb has to scroll. Worked example: `tpl_record`) · share_or_download · rotate_image · avatar · skeleton · activity_indicator · loading · divider ·
910
1042
  spacer · stack · section_card · page_header · page_content · calendar (calendar/index.ts) · gantt ·
911
1043
  comments_thread · agent_run (live streaming work feed) · agent_progress (its compact floating
912
1044
  expandable form — a composer's working state) · composer (Composer — the adaptive command/chat
913
- composer: a compact pill that expands for long text + attachments) · review_card (ReviewCard — THE single-proposal review card; compositional body via children + a generic actions footer; header-first with a title/eyebrow, body-first without) · confidence (calibrated high/med/low) ·
914
- record_fields (RecordFields — the editable extracted-record BODY for a ReviewCard, or a standalone live-edit field card) · change_review (ChangeReview — the BATCH review LIST, whole-set OR 1-by-1; compositional `renderItem` body + the `ChangeDiff` before→after helper, `labels` localizes Undo/Applied/Discarded/counter PLUS `ChangeReviewActions`, the separate commit bar the host pins in a `DialogFooter`/`DrawerFooter` or inline so it never scrolls away) ·
1045
+ composer: a compact pill that expands for long text + attachments) · confidence (calibrated high/med/low) ·
1046
+ change_review (the COMPOUND review family frame: ChangeReview provider/stack · ChangeReviewHeader
1047
+ (auto kept-counter over decidable entries) · ChangeReviewActions (the commit bar in the
1048
+ DialogFooter/DrawerFooter: Keep-all bottom-left (onAcceptAll for host-held field state) + Apply
1049
+ gating); sections: Change (host-owned status, labeled verbs, collapses to its ChangeSummary + Undo;
1050
+ no callbacks = display-only) · ChangeLabel · ChangeSummary · ChangeReasoning (the quiet why);
1051
+ grammar: ChangeFields (the open record form) + ChangeField (THE field: − band · value · candidates
1052
+ + type-another-value · reasoning · per-field Keep/Drop · collapse) · ChangeRecord (THE item card:
1053
+ registers like a Change; tone wash + localized op word; verb level follows the decision level) ·
1054
+ ChangeBand (the raw ± band) · ChangeValueInput (the diff-at-rest editor); the `changeReview`
1055
+ locale slice) ·
915
1056
  clarify (the agent asks back, via ChoiceList) · choice_list (ChoiceList — selectable answer options,
916
- the agent's quick-reply surface) · sources (provenance chips, per-kind glyphs) · spec_list (SpecListthe exact
917
- labeled-value breakdown, the panel of an answer) · match_sides (MatchSides — the two-sided pairing BODY for a body-first ReviewCard reconciliation; composable side slots + the MatchSideText default) ·
918
- finding (Finding one ranked briefing/audit insight) · discrepancy (Discrepancy — a field that
919
- disagrees across sources) · triage_row (TriageRow — an item the agent classified + routed) ·
920
- scored_option (ScoredOption — an AI-ranked candidate) ·
1057
+ the agent's quick-reply surface) · sources (provenance chips, per-kind glyphs) · finding (Findingone ranked AI-check insight: severity word · title · detail · Sources ·
1058
+ children slot with FindingComparison, the expected-vs-actual body: labeled sides + emphasized
1059
+ delta; the `finding` locale slice) ·
921
1060
  format_money · format_date · colors (solid · tint · ramp · ColorName ·
922
1061
  isColorName · asColorName — coerce a stored option/status token to a ColorName, neutral fallback) ·
923
1062
  use_focus_ring (useFocusRing — keyboard-aware focus state for painting a control's own ring; see Focus rings) ·