@lovett/ui 0.0.10 → 0.1.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 (158) hide show
  1. package/dist/chunk-RBYWGBQ2.js +2752 -0
  2. package/dist/chunk-RBYWGBQ2.js.map +1 -0
  3. package/dist/index.d.ts +6064 -80
  4. package/dist/index.js +21561 -9696
  5. package/dist/index.js.map +1 -1
  6. package/dist/rich-composer-impl-5NO443A6.js +1859 -0
  7. package/dist/rich-composer-impl-5NO443A6.js.map +1 -0
  8. package/dist/styles.css +1586 -2
  9. package/dist/theme-v2.css +228 -0
  10. package/dist/tokens.css +121 -0
  11. package/package.json +8 -1
  12. package/src/__tests__/anchor.test.tsx +422 -0
  13. package/src/__tests__/avatar.test.tsx +272 -0
  14. package/src/__tests__/bar-chart.test.tsx +809 -0
  15. package/src/__tests__/board.test.tsx +420 -0
  16. package/src/__tests__/chart-math.test.ts +922 -0
  17. package/src/__tests__/chart-series.test.ts +339 -0
  18. package/src/__tests__/code-block.test.tsx +134 -0
  19. package/src/__tests__/combobox.test.tsx +677 -0
  20. package/src/__tests__/display-popover.test.tsx +195 -0
  21. package/src/__tests__/display-store.test.tsx +208 -0
  22. package/src/__tests__/donut-chart.test.tsx +397 -0
  23. package/src/__tests__/dropdown-menu.test.tsx +572 -0
  24. package/src/__tests__/filter-menu.test.tsx +175 -0
  25. package/src/__tests__/gauge-ring.test.tsx +233 -0
  26. package/src/__tests__/helpers/geometry.ts +58 -0
  27. package/src/__tests__/layer-stack.test.tsx +228 -0
  28. package/src/__tests__/line-chart.test.tsx +612 -0
  29. package/src/__tests__/popover.test.tsx +460 -0
  30. package/src/__tests__/ranked-bars.test.tsx +343 -0
  31. package/src/__tests__/remark-underline.test.ts +194 -0
  32. package/src/__tests__/select.test.tsx +543 -0
  33. package/src/__tests__/sparkline.test.tsx +368 -0
  34. package/src/__tests__/stat-layer.test.tsx +271 -0
  35. package/src/__tests__/stat-strip.test.tsx +175 -0
  36. package/src/__tests__/status.test.tsx +293 -0
  37. package/src/__tests__/tabs.test.tsx +303 -0
  38. package/src/__tests__/token-shape.test.ts +132 -2
  39. package/src/__tests__/tooltip.test.tsx +355 -0
  40. package/src/avatar.tsx +352 -0
  41. package/src/bar-chart.tsx +1214 -0
  42. package/src/board.tsx +658 -0
  43. package/src/calculator-shell-v2.tsx +19 -39
  44. package/src/chart-frame.tsx +960 -0
  45. package/src/chart-legend.tsx +304 -0
  46. package/src/chart-tooltip.tsx +267 -0
  47. package/src/code-block.tsx +62 -8
  48. package/src/combobox.tsx +796 -0
  49. package/src/delta-chip.tsx +263 -0
  50. package/src/detail/__tests__/activity-pane.test.tsx +186 -0
  51. package/src/detail/__tests__/detail-chrome.test.tsx +134 -0
  52. package/src/detail/__tests__/detail-surface.test.tsx +529 -0
  53. package/src/detail/__tests__/field-row.test.tsx +357 -0
  54. package/src/detail/activity-pane.tsx +507 -0
  55. package/src/detail/calendar.tsx +355 -0
  56. package/src/detail/detail-divider.tsx +261 -0
  57. package/src/detail/detail-header.tsx +287 -0
  58. package/src/detail/detail-menu.tsx +254 -0
  59. package/src/detail/detail-surface.tsx +1110 -0
  60. package/src/detail/field-list.tsx +196 -0
  61. package/src/detail/field-row.tsx +1131 -0
  62. package/src/detail/index.ts +58 -0
  63. package/src/detail/segmented-choice.tsx +94 -0
  64. package/src/detail/types.ts +129 -0
  65. package/src/display-popover.tsx +487 -0
  66. package/src/display-store.tsx +242 -0
  67. package/src/donut-chart.tsx +988 -0
  68. package/src/dropdown-menu.tsx +412 -151
  69. package/src/filter-core/EXPORTS.md +85 -0
  70. package/src/filter-core/__tests__/columns.test.ts +159 -0
  71. package/src/filter-core/__tests__/faceting.test.ts +193 -0
  72. package/src/filter-core/__tests__/filter-fns.test.ts +519 -0
  73. package/src/filter-core/__tests__/operators.test.ts +235 -0
  74. package/src/filter-core/__tests__/state.test.ts +268 -0
  75. package/src/filter-core/__tests__/url.test.ts +350 -0
  76. package/src/filter-core/columns.ts +134 -0
  77. package/src/filter-core/date-utils.ts +38 -0
  78. package/src/filter-core/examples/task-filter-columns.ts +121 -0
  79. package/src/filter-core/faceting.ts +120 -0
  80. package/src/filter-core/filter-fns.ts +335 -0
  81. package/src/filter-core/index.ts +105 -0
  82. package/src/filter-core/operators.ts +433 -0
  83. package/src/filter-core/state.ts +280 -0
  84. package/src/filter-core/types.ts +247 -0
  85. package/src/filter-core/url.ts +261 -0
  86. package/src/filter-dropdown.tsx +12 -0
  87. package/src/filter-menu.tsx +649 -0
  88. package/src/floating-drawer.tsx +19 -1
  89. package/src/gauge-ring.tsx +435 -0
  90. package/src/hue.ts +52 -0
  91. package/src/index.ts +402 -0
  92. package/src/kbd.tsx +27 -4
  93. package/src/lib/anchor.ts +427 -0
  94. package/src/lib/chart.ts +866 -0
  95. package/src/lib/focus.ts +74 -0
  96. package/src/lib/layer-stack.ts +215 -0
  97. package/src/lib/refs.ts +31 -0
  98. package/src/lib/remark-underline.ts +443 -0
  99. package/src/lib/series.ts +169 -0
  100. package/src/line-chart.tsx +1176 -0
  101. package/src/markdown.tsx +26 -7
  102. package/src/metric-card.tsx +57 -22
  103. package/src/modal.tsx +73 -66
  104. package/src/popover.tsx +407 -0
  105. package/src/progress-ledger.tsx +304 -0
  106. package/src/ranked-bars.tsx +386 -0
  107. package/src/segmented-pill.tsx +32 -9
  108. package/src/select.tsx +646 -0
  109. package/src/sortable.tsx +315 -1
  110. package/src/sparkline.tsx +416 -0
  111. package/src/stat-card.tsx +376 -0
  112. package/src/stat-row.tsx +108 -70
  113. package/src/stat-strip.tsx +327 -0
  114. package/src/status.tsx +215 -0
  115. package/src/styles.css +1586 -2
  116. package/src/tabs.tsx +206 -25
  117. package/src/task-card.tsx +610 -0
  118. package/src/thread/__tests__/comment-body-hostile.test.tsx +331 -0
  119. package/src/thread/__tests__/comment-tree.test.ts +151 -0
  120. package/src/thread/__tests__/emoji.test.ts +187 -0
  121. package/src/thread/__tests__/fixtures/thread-fixture.ts +235 -0
  122. package/src/thread/__tests__/link-preview-source.test.ts +120 -0
  123. package/src/thread/__tests__/link-preview.test.tsx +600 -0
  124. package/src/thread/__tests__/markdown-format.test.ts +82 -0
  125. package/src/thread/__tests__/markdown-spec.test.ts +469 -0
  126. package/src/thread/__tests__/relative-time.test.ts +71 -0
  127. package/src/thread/__tests__/rich-composer.test.tsx +799 -0
  128. package/src/thread/__tests__/scroll-caret.test.ts +58 -0
  129. package/src/thread/__tests__/suggestion-list.test.tsx +648 -0
  130. package/src/thread/__tests__/thread-scroll-ownership.test.tsx +88 -0
  131. package/src/thread/__tests__/thread.test.tsx +662 -0
  132. package/src/thread/__tests__/use-attachments.test.tsx +679 -0
  133. package/src/thread/actions.tsx +196 -0
  134. package/src/thread/attachments.tsx +1071 -0
  135. package/src/thread/comment-body.tsx +148 -0
  136. package/src/thread/comment-tree.ts +182 -0
  137. package/src/thread/comment.tsx +915 -0
  138. package/src/thread/composer-footer.tsx +125 -0
  139. package/src/thread/composer.tsx +319 -0
  140. package/src/thread/emoji.ts +283 -0
  141. package/src/thread/index.ts +153 -0
  142. package/src/thread/link-preview.tsx +341 -0
  143. package/src/thread/markdown-format.ts +155 -0
  144. package/src/thread/markdown-spec.ts +754 -0
  145. package/src/thread/rail.tsx +372 -0
  146. package/src/thread/reactions.tsx +415 -0
  147. package/src/thread/relative-time.tsx +94 -0
  148. package/src/thread/rich-composer-impl.tsx +1601 -0
  149. package/src/thread/rich-composer.tsx +195 -0
  150. package/src/thread/scroll-caret.ts +37 -0
  151. package/src/thread/suggestion-list.tsx +182 -0
  152. package/src/thread/thread.tsx +718 -0
  153. package/src/thread/types.ts +218 -0
  154. package/src/thread/use-attachments.ts +598 -0
  155. package/src/thread/use-now.ts +73 -0
  156. package/src/thread/use-thread.ts +316 -0
  157. package/src/tokens.css +121 -0
  158. package/src/tooltip.tsx +297 -0
package/src/index.ts CHANGED
@@ -71,6 +71,79 @@ export {
71
71
  // @dnd-kit/core + sortable + utilities (CLAUDE.md §1 amendment in same phase).
72
72
  export { SortableList, SortableItem, DragHandle } from './sortable'
73
73
 
74
+ // Multi-container drag — ADR-146 slice A. <SortableList> is single-container;
75
+ // a kanban needs a card to LEAVE its column, which is a droppable per column
76
+ // plus a DragOverlay. Keeps the KeyboardSensor, so cross-column movement is
77
+ // reachable without a pointer.
78
+ export {
79
+ MultiSortableList,
80
+ SortableDropZone,
81
+ type MultiSortableListProps,
82
+ type MultiSortableMove,
83
+ type SortableContainers,
84
+ } from './sortable'
85
+
86
+ // The board kit — ADR-146 D2/D7/D13, spec'd in
87
+ // `docs/design-briefs/tasks-board-design-language.md` and its
88
+ // interface-standards addendum.
89
+ //
90
+ // ONE board primitive, TWO consumers: the Tasks lens and the Community
91
+ // roadmap. It is generic over its card BODY (`renderCard`), never over its
92
+ // data — a Community card carries votes where a Tasks card carries a ledger.
93
+ // Community's fixture-backed `components/ui/community/board.tsx` is deleted in
94
+ // the same change.
95
+ //
96
+ // `hueClass()` arms the derived tint ladder (`.hue` + `.hue-<key>` in
97
+ // styles.css), which mixes a --folder-* hue INTO --surface-card with
98
+ // color-mix so every rung is opaque in both themes by construction. `red` is
99
+ // excluded: --folder-red is byte-identical to --accent in light mode.
100
+ export {
101
+ BOARD_HUE_KEYS,
102
+ hueClass,
103
+ hueForKey,
104
+ type BoardHueKey,
105
+ } from './hue'
106
+ export {
107
+ Board,
108
+ BoardColumn,
109
+ type BoardProps,
110
+ type BoardColumnProps,
111
+ type BoardColumnSpec,
112
+ type BoardCardContext,
113
+ type BoardItemLeadContext,
114
+ } from './board'
115
+ export {
116
+ TaskCard,
117
+ type TaskCardProps,
118
+ type TaskCardAvatar,
119
+ type TaskCardCount,
120
+ type TaskCardDate,
121
+ type TaskCardDragProps,
122
+ type TaskCardMedia,
123
+ } from './task-card'
124
+ export {
125
+ ProgressLedger,
126
+ ledgerProgress,
127
+ type ProgressLedgerProps,
128
+ type LedgerCell,
129
+ type LedgerProgress,
130
+ type LedgerSegment,
131
+ } from './progress-ledger'
132
+ // The status vocabulary (header-primitives §2). `StatusRing` renders a REAL
133
+ // percent — the one `ledgerProgress()` computes, so a ring and the ledger it
134
+ // sits beside cannot disagree; `StatusGlyph` covers the discrete states, in
135
+ // lucide rather than the source's six hand-written inline SVGs.
136
+ export {
137
+ BOARD_STATUS_KEYS,
138
+ STATUS_GLYPHS,
139
+ STATUS_LABELS,
140
+ StatusGlyph,
141
+ StatusRing,
142
+ type BoardStatusKey,
143
+ type StatusGlyphProps,
144
+ type StatusRingProps,
145
+ } from './status'
146
+
74
147
  // EmptyState — promoted post-phase-15 (ADR-019). 4-consumer trigger:
75
148
  // Products / Personas / Competitors / Assets lenses all forked their own
76
149
  // full-area empty-state pattern. Canonical shape derived from the Phase
@@ -172,6 +245,112 @@ export {
172
245
  type EditingCell,
173
246
  } from './data-grid'
174
247
 
248
+ // Anchored-positioning kit + dismiss-layer stack — promoted per ADR-0030
249
+ // Decision G (meta-ads-audit-dashboard task manager: its pickers, sheets
250
+ // and palette need one positioning routine and one Escape order). The
251
+ // workspace app is the other consumer: DropdownMenu (137 call sites),
252
+ // Modal (51 files) and Popover are rebuilt on these in the same change.
253
+ // `useAnchoredPosition` measures / flips / clamps against an element ref
254
+ // OR a virtual rect (ContextMenu's right-click point); `useOutsideClick`
255
+ // is layer-aware; `useLayer` / `useEscapeKey` are the topmost-wins stack
256
+ // extracted from Modal's `openPanels`. Select / Combobox / Tooltip /
257
+ // ContextMenu build on them next; FilterDropdown / FolderTreePicker /
258
+ // TagChipInput still carry local copies of the positioning block
259
+ // (follow-up).
260
+ export {
261
+ useAnchoredPosition,
262
+ computeAnchoredPosition,
263
+ useOutsideClick,
264
+ type AnchorSide,
265
+ type AnchorAlign,
266
+ type AnchorRect,
267
+ type AnchorSize,
268
+ type AnchorPlacement,
269
+ type AnchoredPosition,
270
+ type AnchoredPositionOptions,
271
+ type UseAnchoredPositionOptions,
272
+ type UseAnchoredPositionResult,
273
+ type OutsideClickTarget,
274
+ type UseOutsideClickOptions,
275
+ } from './lib/anchor'
276
+ export {
277
+ useLayer,
278
+ useEscapeKey,
279
+ type LayerKind,
280
+ type LayerHandle,
281
+ type UseLayerOptions,
282
+ type UseEscapeKeyOptions,
283
+ } from './lib/layer-stack'
284
+
285
+ // Popover — anchored, non-modal surface: portal to body, `.ds-enter-pop`
286
+ // once positioned, focus into the content on open and back to the trigger
287
+ // on close, Escape closes the topmost layer only, outside-click closes.
288
+ // Compound: Popover + Popover.Trigger (`asChild` to wrap a <Button> or a
289
+ // cell) + Popover.Content (`role="dialog"` or `"none"`). Promoted per
290
+ // ADR-0030 Decision G; the workspace app's three hand-rolled popovers
291
+ // (sidebar user menu, group-settings colour / icon pickers, calculator
292
+ // AssumptionsPopover) are the migration targets. DropdownMenu shares its
293
+ // surface chrome (`POPOVER_SURFACE_STYLE`) and stack, but stays a sibling
294
+ // because a menu does not move focus into itself on open.
295
+ export {
296
+ Popover,
297
+ POPOVER_SURFACE_STYLE,
298
+ type PopoverProps,
299
+ type PopoverTriggerProps,
300
+ type PopoverContentProps,
301
+ } from './popover'
302
+
303
+ // Select — single-value picker: an `.input-shell` trigger (sm / md, sits
304
+ // flush beside <Input>) that opens a real listbox (APG select-only
305
+ // combobox: role=combobox trigger, aria-activedescendant, role=listbox /
306
+ // option, arrow / Home / End / Enter / Space / type-ahead, disabled
307
+ // options, optional icon + description). Promoted per ADR-0030 Decision G
308
+ // — the meta-ads-audit-dashboard task manager's status / priority / role
309
+ // pickers; its Users page hand-rolled a native <select> with a comment
310
+ // saying the package ships none. The workspace app is the second consumer
311
+ // (26 native <select>s, products/product-form.tsx says the same). Built on
312
+ // useAnchoredPosition + the layer stack; shares the popover surface chrome.
313
+ // `ListboxOption` / `LISTBOX_CLASS` are the row + popup chrome Combobox
314
+ // reuses, exported for the same reason POPOVER_SURFACE_STYLE is.
315
+ export {
316
+ Select,
317
+ ListboxOption,
318
+ LISTBOX_CLASS,
319
+ type SelectProps,
320
+ type SelectOption,
321
+ type SelectSize,
322
+ type ListboxOptionProps,
323
+ } from './select'
324
+
325
+ // Combobox — text input + filtered listbox (APG editable combobox with list
326
+ // autocomplete), single or multi-select with chips, optional grouped options
327
+ // with headings, a per-option live `count` slot and a `renderOption` slot.
328
+ // Async-friendly: `shouldFilter={false}` + `loading` + `onInputValueChange`;
329
+ // it never fetches. Promoted per ADR-0030 Decision G — the task manager's
330
+ // row / card / context-menu / palette pickers are "popover + search + live
331
+ // counts". Second consumer is the workspace app
332
+ // (sem-spec/location-autocomplete.tsx hand-rolls the role; TagChipInput
333
+ // carries the chip model this generalises).
334
+ export {
335
+ Combobox,
336
+ type ComboboxProps,
337
+ type ComboboxSingleProps,
338
+ type ComboboxMultipleProps,
339
+ type ComboboxOption,
340
+ type ComboboxGroup,
341
+ type ComboboxOptionState,
342
+ type ComboboxSize,
343
+ } from './combobox'
344
+
345
+ // Tooltip — hover (300 ms) / focus (immediate) triggered role=tooltip with
346
+ // aria-describedby wiring, Escape-dismissable through the layer stack, never
347
+ // focusable, pointer-events none, token-only inline surface like Kbd.
348
+ // Promoted per ADR-0030 Decision G (task manager: truncated cells, icon-only
349
+ // row actions, timestamps). Second consumer is the workspace app, which
350
+ // hand-rolls two today (tools/_shared/calc/info-tip.tsx,
351
+ // components/ui/sidebar/rail-tooltip.tsx).
352
+ export { Tooltip, type TooltipProps, type TooltipTriggerProps } from './tooltip'
353
+
175
354
  // DropdownButton — Button-styled trigger + rich popover menu. Composed
176
355
  // over <DropdownMenu> primitives. First consumer: Keywords dashboard's
177
356
  // "New Keyword Plan" CTA (Set/Group picker). Reusable across other
@@ -273,6 +452,25 @@ export {
273
452
  type ShellStatTone,
274
453
  } from './shell'
275
454
 
455
+ // Avatar / AvatarGroup — a person. Promoted in ADR-147 D14, collapsing four
456
+ // forks (task-card's AvatarStack, community/primitives, its byte-identical
457
+ // admin clone, and the inline one in folders/folder-row) that disagreed on
458
+ // hash, initials cap, geometry and colour. `initials()` and
459
+ // `avatarTintForKey()` are the one derivation each, exported so a caller that
460
+ // needs the monogram or the hue outside an avatar does not re-derive it.
461
+ export {
462
+ Avatar,
463
+ AvatarGroup,
464
+ AVATAR_TINTS,
465
+ asAvatarTint,
466
+ avatarTintForKey,
467
+ initials,
468
+ type AvatarProps,
469
+ type AvatarGroupProps,
470
+ type AvatarSize,
471
+ type AvatarTint,
472
+ } from './avatar'
473
+
276
474
  // FolderCard — drive-style folder tile (colour-keyed swatch + name +
277
475
  // count + hover checkbox / menu slot). Promoted in ADR-035 (Assets
278
476
  // lens redesign); a card surface, so it owns the internal surface
@@ -420,3 +618,207 @@ export type {
420
618
  PreviewConfiguration,
421
619
  } from './meta-previews/types'
422
620
  export { PLACEMENT_CONFIGS } from './meta-previews/types'
621
+
622
+ // Board filter model — pure TypeScript, no React. See src/filter-core/index.ts.
623
+ export * from './filter-core'
624
+
625
+ // FilterMenu + FilterChips — the UI layer over filter-core (header-primitives
626
+ // §5). Two-step field-then-values picker with faceted counts, and the active
627
+ // filters as removable, editable chips. The host owns FiltersState and is
628
+ // expected to keep it in the URL, which is what makes a filtered view a
629
+ // shareable link.
630
+ export {
631
+ FilterMenu,
632
+ FilterChipBar,
633
+ type FilterMenuProps,
634
+ type FilterChipBarProps,
635
+ type FilterOptionIconRenderer,
636
+ type FilterFieldIconRenderer,
637
+ } from './filter-menu'
638
+
639
+ // The Display popover and its row primitives (header-primitives §4) — the
640
+ // boundary the source never drew. Every control here is real or absent;
641
+ // PopoverFooterActions renders no primary unless one is supplied.
642
+ export {
643
+ DisplayPopover,
644
+ OptionRow,
645
+ SelectRow,
646
+ ToggleRow,
647
+ ChipToggleGroup,
648
+ SectionHeading,
649
+ PopoverFooterActions,
650
+ type DisplayPopoverProps,
651
+ type DisplayPopoverSectionProps,
652
+ type OptionRowProps,
653
+ type SelectRowProps,
654
+ type ToggleRowProps,
655
+ type ChipToggleGroupProps,
656
+ type ChipToggleItem,
657
+ type SectionHeadingProps,
658
+ type PopoverFooterActionsProps,
659
+ type PopoverFooterAction,
660
+ } from './display-popover'
661
+
662
+ // createDisplayStore — one factory for per-scope display settings, passed
663
+ // down by CONTEXT rather than read from a module singleton (the trap
664
+ // header-primitives §4 names: their cards subscribe to a global store, so two
665
+ // boards with different settings cannot coexist).
666
+ export {
667
+ createDisplayStore,
668
+ type DisplayStore,
669
+ type DisplayController,
670
+ type DisplaySettings,
671
+ type DisplayValue,
672
+ type DisplayAllowed,
673
+ type DisplayMigrations,
674
+ type DisplayProviderProps,
675
+ } from './display-store'
676
+ // ── ADR-146 — the analytics primitive set ─────────────────────────────
677
+ //
678
+ // Promoted as ONE block (ADR-146 D5). The trigger was not a missing chart:
679
+ // it was ~26 forked stat tiles, 3 independent delta chips, and 7 hand-written
680
+ // SVG charts sitting in `design-system/spec/shell-charts.tsx` that no product
681
+ // lens could reach — while `--series-1..8` sat in `theme-v2.css` with zero
682
+ // consumers. Each primitive below carries a MEASURED consumer count in its
683
+ // own docstring — measured 2026-09-06, gallery demos excluded. Only
684
+ // `StatCard` clears the ADR-008 D3 two-consumer gate today; the rest are
685
+ // shipped ahead of their consumers, which is a deliberate, recorded debt, not
686
+ // a met gate. The per-primitive ledger and the named surface that should
687
+ // become each one's second consumer live in
688
+ // `docs/follow-ups/_pending/adr-146-consumer-gate.md`. The ~26 forked tiles
689
+ // are NOT rewritten here (ADR-146 D13).
690
+ //
691
+ // Three rules that govern every export in this block:
692
+ // D7 a chart is MONOCHROME until it earns a palette — the accent's scarcity
693
+ // is the point, and bar length already encodes what bar colour would.
694
+ // D8 series colour is a frozen literal lookup, never a constructed
695
+ // `--series-${i}`. A name that misses is invalid CSS the browser
696
+ // silently discards, with every gate green.
697
+ // D10 identity is never colour alone, and text wears text tokens, never a
698
+ // series colour. The empty / loading / error states and the table view
699
+ // come from `ChartFrame`, so — measured 2026-09-06, not asserted — only
700
+ // `LineChart`, `AreaChart` and `BarChart` carry them. `DonutChart`,
701
+ // `RankedBars`, `Sparkline` and `GaugeRing` do NOT compose the frame (a
702
+ // donut has no axes; ranked bars are their own rows), so they have no
703
+ // table view. Their data does reach assistive tech another way — the
704
+ // donut's legend is a label+value+percentage list and RankedBars is a
705
+ // labelled `<ul>` — but D10.5 says "every chart", and four of seven is
706
+ // not every. Tracked as a deviation in the ADR-146 completion report.
707
+ //
708
+ // Stat layer — DeltaChip replaces the 3 forks (admin/stat-tile.tsx:10,
709
+ // shell-preview.tsx:254, community/primitives.tsx:117); StatCard is the KPI
710
+ // tile WITH a delta slot, the slot whose absence caused the forks; StatStrip
711
+ // is one card divided by hairlines, which is a different object from a grid
712
+ // of StatCards and is why budget-flighting's GlobalHeader forked.
713
+ export { DeltaChip, type DeltaChipProps, type DeltaDirection, type DeltaFormat } from './delta-chip'
714
+ export { StatCard, type StatCardProps, type StatCardSize, type StatCardFooterLink } from './stat-card'
715
+ export { StatStrip, type StatStripProps, type StatStripCell } from './stat-strip'
716
+
717
+ // Chart chrome — the frame every chart draws inside, the legend that is the
718
+ // only place a series hue may sit beside text, and the readout that positions
719
+ // by transform so it can never reflow the plot.
720
+ export {
721
+ ChartFrame,
722
+ type ChartFrameProps,
723
+ type ChartFrameState,
724
+ type ChartFrameView,
725
+ type ChartTableData,
726
+ type ChartTableRow,
727
+ } from './chart-frame'
728
+ export { ChartLegend, type ChartLegendProps, type ChartLegendItem } from './chart-legend'
729
+ export { ChartTooltip, tooltipPosition, type ChartTooltipProps, type ChartTooltipRow } from './chart-tooltip'
730
+
731
+ // The series palette (ADR-146 D2/D8). Re-exported through `chart-legend.tsx`
732
+ // so this block stays one import surface and no consumer reaches into `lib/`.
733
+ // `lib/series.ts` remains the canonical definition. Callers pass a SLOT
734
+ // INDEX — never a colour, and never a token name.
735
+ export {
736
+ SERIES,
737
+ SERIES_SCATTER,
738
+ SERIES_SLOT_COUNT,
739
+ SCATTER_SLOT_COUNT,
740
+ OTHER_SLOT_COLOR,
741
+ seriesColor,
742
+ scatterColor,
743
+ paletteColor,
744
+ slotColor,
745
+ isSeriesSlot,
746
+ isScatterSlot,
747
+ type SeriesSlot,
748
+ type ScatterSlot,
749
+ type SeriesPalette,
750
+ type ChartSlot,
751
+ } from './chart-legend'
752
+
753
+ // Chart layer. Every one takes its data as `{ label, value }[]` and its series
754
+ // identity as a slot index; none of them accepts a colour.
755
+ export {
756
+ LineChart,
757
+ AreaChart,
758
+ type LineChartProps,
759
+ type LineChartSeries,
760
+ type AreaChartProps,
761
+ } from './line-chart'
762
+ export {
763
+ BarChart,
764
+ type BarChartProps,
765
+ type BarChartDatum,
766
+ type BarChartSeries,
767
+ type BarChartOrientation,
768
+ type BarChartMode,
769
+ type BarChartEmphasis,
770
+ } from './bar-chart'
771
+ export {
772
+ DonutChart,
773
+ foldDonutData,
774
+ withMinimumArc,
775
+ DONUT_OTHER_KEY,
776
+ type DonutChartProps,
777
+ type DonutChartDatum,
778
+ type DonutSlice,
779
+ type DonutFold,
780
+ type DonutGapSurface,
781
+ } from './donut-chart'
782
+ export { RankedBars, type RankedBarsProps, type RankedBarsItem, type RankedBarsSort } from './ranked-bars'
783
+ export { Sparkline, type SparklineProps, type SparklineVariant } from './sparkline'
784
+ export { GaugeRing, type GaugeRingProps, type GaugeBand, type GaugeTone } from './gauge-ring'
785
+
786
+ // Chart math (ADR-146 D6) — the scale / tick / path / arc arithmetic that was
787
+ // inlined in seven places, now one tested module. `niceTicks` is the piece
788
+ // with no ancestor in the repo: gridlines were the literal list
789
+ // [0.25, 0.5, 0.75] and axis labels were hardcoded strings. Pure functions;
790
+ // they produce numbers and path data and never name a colour or a token.
791
+ export {
792
+ linearScale,
793
+ bandScale,
794
+ niceTicks,
795
+ linePath,
796
+ areaPath,
797
+ arcGeometry,
798
+ arcSegments,
799
+ ringDash,
800
+ clampPct,
801
+ normalizeToMax,
802
+ stackTotals,
803
+ stackFractions,
804
+ monthDomain,
805
+ type NumericInput,
806
+ type Point,
807
+ type Extent,
808
+ type ScaleFn,
809
+ type BandScale,
810
+ type ArcGeometry,
811
+ type ArcSegment,
812
+ type RingDash,
813
+ } from './lib/chart'
814
+
815
+ // ── Item detail (tasks-item-detail-full.md) ─────────────────────────────────
816
+ // The three-mode detail frame, its slotted header and ellipsis menu, the
817
+ // Notion-style field rows, and the activity pane. Composed by a lens; owns no
818
+ // fetch, no entity and no board.
819
+ export * from './detail'
820
+
821
+ // ── Thread (ADR-147) ────────────────────────────────────────────────────────
822
+ // A headless comment/social core plus one styled skin, bound to an arbitrary
823
+ // entity rather than to a chat room. Owns no fetch client and no base URL.
824
+ export * from './thread'
package/src/kbd.tsx CHANGED
@@ -20,17 +20,40 @@ export interface KbdProps {
20
20
  children: ReactNode
21
21
  /** Optional size — defaults to 'md'. */
22
22
  size?: 'sm' | 'md'
23
+ /**
24
+ * Which surface this sits on. `neutral` is a normal page/card surface;
25
+ * `on-accent` is inside a filled accent control (a primary button's shortcut
26
+ * hint).
27
+ *
28
+ * This exists because the neutral palette is illegible on an accent fill and
29
+ * fails quietly rather than loudly. `--text-tertiary` ink and a `--border`
30
+ * hairline are tuned for a light-neutral ground; dropped onto `--accent` they
31
+ * read as grey-on-red mush, which is exactly what shipped in the thread
32
+ * composer before this prop existed — and it forked this primitive inline to
33
+ * do it, so the bug was invisible here.
34
+ */
35
+ tone?: 'neutral' | 'on-accent'
23
36
  }
24
37
 
25
- export function Kbd({ children, size = 'md' }: KbdProps) {
38
+ export function Kbd({ children, size = 'md', tone = 'neutral' }: KbdProps) {
39
+ const onAccent = tone === 'on-accent'
26
40
  const style: CSSProperties = {
27
41
  display: 'inline-flex',
28
42
  alignItems: 'center',
29
43
  justifyContent: 'center',
30
44
  gap: '0.125rem',
31
- background: 'rgb(var(--surface-overlay-soft))',
32
- border: '1px solid rgb(var(--border))',
33
- color: 'rgb(var(--text-tertiary))',
45
+ // On an accent fill the INK stays at full strength and the container does
46
+ // the quieting. Fading the text instead is the tempting move and it is
47
+ // wrong: white at 85% over --accent measures 4.33:1, under AA, while full
48
+ // white is 5.64:1. The translucent fill also lightens the local ground, so
49
+ // it raises the text's contrast rather than lowering it.
50
+ background: onAccent
51
+ ? 'rgb(var(--accent-foreground) / 0.16)'
52
+ : 'rgb(var(--surface-overlay-soft))',
53
+ border: `1px solid ${
54
+ onAccent ? 'rgb(var(--accent-foreground) / 0.4)' : 'rgb(var(--border))'
55
+ }`,
56
+ color: onAccent ? 'rgb(var(--accent-foreground))' : 'rgb(var(--text-tertiary))',
34
57
  fontFamily: 'var(--font-mono)',
35
58
  fontSize: size === 'sm' ? '10.5px' : '11.5px',
36
59
  padding: size === 'sm' ? '1px 6px' : '2px 8px',