@lovett/ui 0.0.1

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 (102) hide show
  1. package/README.md +28 -0
  2. package/package.json +53 -0
  3. package/src/accordion.tsx +153 -0
  4. package/src/atoms.tsx +197 -0
  5. package/src/badge.tsx +91 -0
  6. package/src/brand-logo-tile.tsx +200 -0
  7. package/src/bulk-action-bar.tsx +104 -0
  8. package/src/button-group.tsx +46 -0
  9. package/src/button.tsx +93 -0
  10. package/src/calculator-shell-v2.tsx +380 -0
  11. package/src/calculator-shell.tsx +310 -0
  12. package/src/card.tsx +369 -0
  13. package/src/checkbox.tsx +114 -0
  14. package/src/chip-nav.tsx +347 -0
  15. package/src/choropleth.tsx +62 -0
  16. package/src/code-block.tsx +235 -0
  17. package/src/completion-ring.tsx +119 -0
  18. package/src/copy-field.tsx +169 -0
  19. package/src/data-grid/column-options-menu.tsx +127 -0
  20. package/src/data-grid/data-grid.tsx +391 -0
  21. package/src/data-grid/drawer-panel.tsx +146 -0
  22. package/src/data-grid/index.ts +29 -0
  23. package/src/data-grid/sortable-parts.tsx +215 -0
  24. package/src/data-grid/table-body.tsx +238 -0
  25. package/src/data-grid/table-elements.tsx +135 -0
  26. package/src/data-grid/table-header.tsx +95 -0
  27. package/src/data-grid/table-overlays.tsx +78 -0
  28. package/src/data-grid/table-parts.tsx +15 -0
  29. package/src/data-grid/table-summary-footer.tsx +101 -0
  30. package/src/data-grid/table-view.tsx +176 -0
  31. package/src/data-grid/types.ts +141 -0
  32. package/src/data-grid/use-grid-columns.ts +311 -0
  33. package/src/data-grid/use-grid-editing.ts +154 -0
  34. package/src/device-frame.tsx +274 -0
  35. package/src/dropdown-button.tsx +221 -0
  36. package/src/dropdown-menu.tsx +450 -0
  37. package/src/empty-state.tsx +85 -0
  38. package/src/file-upload/file-drop-zone.tsx +156 -0
  39. package/src/file-upload/file-preview-grid.tsx +39 -0
  40. package/src/file-upload/file-preview-item.tsx +175 -0
  41. package/src/file-upload/file-thumbnail.tsx +98 -0
  42. package/src/file-upload/file-upload-button.tsx +95 -0
  43. package/src/file-upload/index.ts +13 -0
  44. package/src/file-upload/use-file-upload.ts +371 -0
  45. package/src/filter-bar.tsx +125 -0
  46. package/src/filter-dropdown.tsx +289 -0
  47. package/src/floating-drawer.tsx +420 -0
  48. package/src/folder-card.tsx +188 -0
  49. package/src/folder-tree-picker.tsx +511 -0
  50. package/src/format.ts +117 -0
  51. package/src/frame-stack.tsx +73 -0
  52. package/src/hero-form-card.tsx +471 -0
  53. package/src/index.ts +372 -0
  54. package/src/input.tsx +130 -0
  55. package/src/kbd.tsx +41 -0
  56. package/src/lib/layout.ts +22 -0
  57. package/src/lib/utils.ts +6 -0
  58. package/src/markdown.tsx +214 -0
  59. package/src/menu-button.tsx +142 -0
  60. package/src/meta-previews/CreativeMedia.tsx +119 -0
  61. package/src/meta-previews/InstagramExplorePreview.tsx +340 -0
  62. package/src/meta-previews/InstagramFeedCarousel.tsx +245 -0
  63. package/src/meta-previews/InstagramFeedPreview.tsx +259 -0
  64. package/src/meta-previews/MetaFeedCarousel.tsx +228 -0
  65. package/src/meta-previews/MetaFeedPreview.tsx +294 -0
  66. package/src/meta-previews/MetaInstreamPreview.tsx +135 -0
  67. package/src/meta-previews/MetaMessengerPreview.tsx +795 -0
  68. package/src/meta-previews/MetaReelPreview.tsx +750 -0
  69. package/src/meta-previews/MetaRightColumnPreview.tsx +245 -0
  70. package/src/meta-previews/MetaSearchPreview.tsx +113 -0
  71. package/src/meta-previews/MetaStoryPreview.tsx +1132 -0
  72. package/src/meta-previews/ProfileAvatar.tsx +79 -0
  73. package/src/meta-previews/defaults.ts +27 -0
  74. package/src/meta-previews/index.ts +37 -0
  75. package/src/meta-previews/types.ts +219 -0
  76. package/src/metric-card.tsx +117 -0
  77. package/src/modal.tsx +131 -0
  78. package/src/option-tile.tsx +252 -0
  79. package/src/page-hero.tsx +78 -0
  80. package/src/page-shell.tsx +440 -0
  81. package/src/pagination.tsx +86 -0
  82. package/src/pill-button.tsx +103 -0
  83. package/src/profile-section.tsx +389 -0
  84. package/src/progress-bar.tsx +84 -0
  85. package/src/radio.tsx +172 -0
  86. package/src/range-slider.tsx +161 -0
  87. package/src/resizable.tsx +282 -0
  88. package/src/search-bar.tsx +81 -0
  89. package/src/segmented-pill.tsx +143 -0
  90. package/src/shell.tsx +464 -0
  91. package/src/slider.tsx +107 -0
  92. package/src/sortable-table.tsx +198 -0
  93. package/src/sortable.tsx +137 -0
  94. package/src/stat-row.tsx +132 -0
  95. package/src/stats-grid.tsx +38 -0
  96. package/src/step-loader.tsx +141 -0
  97. package/src/styles.css +562 -0
  98. package/src/tabs.tsx +143 -0
  99. package/src/tag-chip-input.tsx +394 -0
  100. package/src/textarea.tsx +39 -0
  101. package/src/toast.tsx +59 -0
  102. package/src/tokens.css +579 -0
package/src/index.ts ADDED
@@ -0,0 +1,372 @@
1
+ // @lovett/ui — public exports.
2
+ //
3
+ // The set below is the contract from ADR-008 Decision 3 plus the toast
4
+ // surface (Decision 5). Nothing else lands here without an ADR
5
+ // promotion. Apps consume via `import { Button, toast } from '@lovett/ui'`.
6
+ //
7
+ // CSS surface (apps import these alongside the JS):
8
+ // `@lovett/ui/tokens.css` — the token vocabulary
9
+ // `@lovett/ui/styles.css` — component CSS the primitives render through
10
+
11
+ export { default as Card, IconTile, TagRow } from './card'
12
+ export { Button, type ButtonProps, type ButtonVariant, type ButtonSize, type ButtonShape } from './button'
13
+ export { ButtonGroup, type ButtonGroupProps } from './button-group'
14
+ export { Input, type InputProps, type InputSize } from './input'
15
+ export { Textarea, type TextareaProps } from './textarea'
16
+ export { default as Modal } from './modal'
17
+ export {
18
+ FloatingDrawer,
19
+ DetailSection,
20
+ type FloatingDrawerProps,
21
+ } from './floating-drawer'
22
+ export { default as BrandLogoTile, type BrandLogoTileProps, type BrandLogoTileSize } from './brand-logo-tile'
23
+ export { default as ProfileSection, type ProfileSectionProps } from './profile-section'
24
+ export { default as ChipNav, type ChipNavItem } from './chip-nav'
25
+ export { default as CompletionRing, type CompletionRingProps } from './completion-ring'
26
+ export { default as Choropleth, type ChoroplethProps } from './choropleth'
27
+ export {
28
+ default as PageShell,
29
+ AppScroll,
30
+ PageHeaderHost,
31
+ PageHeaderSlotProvider,
32
+ type Crumb,
33
+ } from './page-shell'
34
+ export { Toaster, toast } from './toast'
35
+
36
+ // Brand-profile atoms — promoted in Phase 2b (ADR-010 Decision 3). Section
37
+ // files across the brand-profile lens (and later sibling lenses) import
38
+ // these from `@lovett/ui` rather than a local `./atoms`.
39
+ export {
40
+ SectionLabel,
41
+ IdentityLabel,
42
+ IdentityValue,
43
+ TextInput,
44
+ TextareaInput,
45
+ EmptyPlaceholder,
46
+ ListItem,
47
+ } from './atoms'
48
+
49
+ // Sortable primitives — promoted in Phase 2b (ADR-010 Decision 5). Wraps
50
+ // @dnd-kit/core + sortable + utilities (CLAUDE.md §1 amendment in same phase).
51
+ export { SortableList, SortableItem, DragHandle } from './sortable'
52
+
53
+ // EmptyState — promoted post-phase-15 (ADR-019). 4-consumer trigger:
54
+ // Products / Personas / Competitors / Assets lenses all forked their own
55
+ // full-area empty-state pattern. Canonical shape derived from the Phase
56
+ // 15 Assets implementation. Distinct from EmptyPlaceholder (atoms.tsx),
57
+ // which is the in-card dashed-border variant for brand-profile sections.
58
+ export { EmptyState, type EmptyStateProps } from './empty-state'
59
+
60
+ // Form primitives promoted post-utility-tools-batch-1 (ADR-029). The
61
+ // 4 lens-local tinted-card-with-input forks (Convert Case, Text
62
+ // Formatter, Replace/Dedupe modals) consolidated into OptionTile.
63
+ // Radio + RadioGroup for stand-alone radios outside the tile context.
64
+ export { Radio, RadioGroup, type RadioProps, type RadioGroupProps } from './radio'
65
+ export {
66
+ OptionTile,
67
+ OptionTileGroup,
68
+ type OptionTileProps,
69
+ type OptionTileGroupProps,
70
+ } from './option-tile'
71
+
72
+ // Phase 3a/3b/3c/8/12 batch promotions (ADRs 020-024) — shipped via
73
+ // pre-batch coordination commit per ADR-023 D20. Each primitive has
74
+ // 2+ committed consumers across the batch.
75
+ export { MetricCard, type MetricCardProps, type MetricCardTone } from './metric-card'
76
+ export { ProgressBar, type ProgressBarProps } from './progress-bar'
77
+ export { StepLoader, type StepLoaderProps, type StepLoaderStep } from './step-loader'
78
+ export { Badge, type BadgeProps, type BadgeTone } from './badge'
79
+ export { StatsGrid, type StatsGridProps } from './stats-grid'
80
+ export {
81
+ SortableTable,
82
+ type SortableTableProps,
83
+ type Column as SortableTableColumn,
84
+ } from './sortable-table'
85
+ export { Pagination, type PaginationProps } from './pagination'
86
+ export { Tabs, type TabsProps, type Tab } from './tabs'
87
+ export { PillButton, type PillButtonProps } from './pill-button'
88
+
89
+ // DataGrid + supporting primitives — intaked from gray-ui-csm under
90
+ // MIT per ADR-025 (first external-component intake under
91
+ // SPEC-DEV-FOR-WORKSPACE §8). DropdownMenu trigger inherits the
92
+ // secondary Button visual per ADR-026 D4 (DQ-4 LIFT).
93
+ export { Checkbox, type CheckboxProps } from './checkbox'
94
+ export {
95
+ DropdownMenu,
96
+ DropdownMenuTrigger,
97
+ DropdownMenuContent,
98
+ DropdownMenuItem,
99
+ DropdownMenuCheckboxItem,
100
+ DropdownMenuSeparator,
101
+ DropdownMenuLabel,
102
+ DropdownMenuGroup,
103
+ type DropdownMenuProps,
104
+ type DropdownMenuTriggerProps,
105
+ type DropdownMenuContentProps,
106
+ type DropdownMenuItemProps,
107
+ type DropdownMenuCheckboxItemProps,
108
+ type DropdownMenuLabelProps,
109
+ } from './dropdown-menu'
110
+ export {
111
+ DataGrid,
112
+ DataGridColumnOptionsMenu,
113
+ DataGridDragOverlay,
114
+ DataGridDropIndicator,
115
+ type DataGridColumn,
116
+ type DataGridDrawerPanelProps,
117
+ type DataGridProps,
118
+ type DataGridRowBase,
119
+ type DataGridSortState,
120
+ type DataGridToolbarRenderProps,
121
+ type DataGridIcon,
122
+ type EditingCell,
123
+ } from './data-grid'
124
+
125
+ // DropdownButton — Button-styled trigger + rich popover menu. Composed
126
+ // over <DropdownMenu> primitives. First consumer: Keywords dashboard's
127
+ // "New Keyword Plan" CTA (Set/Group picker). Reusable across other
128
+ // lenses with a multi-create CTA pattern.
129
+ export {
130
+ DropdownButton,
131
+ type DropdownButtonProps,
132
+ type DropdownButtonItem,
133
+ } from './dropdown-button'
134
+
135
+ // Accordion — controlled collapsible-section group. Compound:
136
+ // Accordion + Accordion.Section. First consumer: SEM Spec "New
137
+ // campaign spec" form (ADR-033).
138
+ export {
139
+ Accordion,
140
+ type AccordionProps,
141
+ type AccordionSectionProps,
142
+ } from './accordion'
143
+
144
+ // MenuButton — toolbar-style dropdown for menu-bar affordances (File /
145
+ // Edit). Transparent trigger + compact action rows. Composed over
146
+ // <DropdownMenu>. First consumer: utility-tool workbench header
147
+ // (ADR-031).
148
+ export {
149
+ MenuButton,
150
+ type MenuButtonProps,
151
+ type MenuButtonItem,
152
+ type MenuButtonEntry,
153
+ type MenuButtonSeparator,
154
+ } from './menu-button'
155
+
156
+ // CopyField — input/output field with a copy button pinned top-right.
157
+ // Owns the copy interaction (clipboard + ✓ feedback + toast). First
158
+ // consumer: utility-tool workbench body (ADR-031). Reusable anywhere a
159
+ // field's contents need a one-click copy.
160
+ export { CopyField, type CopyFieldProps } from './copy-field'
161
+
162
+ // TagChipInput — chips + autocomplete + type-to-create. First consumers:
163
+ // Keywords NewSetPage + NewGroupPage. Reusable anywhere a multi-tag
164
+ // input is needed.
165
+ export { TagChipInput, type TagChipInputProps } from './tag-chip-input'
166
+
167
+ // FolderTreePicker — dropdown picker over a nestable folder tree with
168
+ // optional inline-create. Generic shape (`{ id, name, parentId }`) so
169
+ // any folder-like data structure can use it. First consumers: Keywords
170
+ // NewSetPage + NewGroupPage.
171
+ export {
172
+ FolderTreePicker,
173
+ type FolderTreeNode,
174
+ type FolderTreePickerProps,
175
+ } from './folder-tree-picker'
176
+
177
+ // HeroFormCard — centered hero-layout form scaffold (eyebrow + title +
178
+ // subtitle + body card + footer). Compound sub-components: Section,
179
+ // Field, FieldRow, Banner, Advanced, Footer. Icon-agnostic — consumers
180
+ // pass any Lucide / custom node as `icon`. First consumers: Keywords
181
+ // NewSetPage + NewGroupPage. Reusable for SEM Spec input + other
182
+ // centered create / config flows.
183
+ export {
184
+ HeroFormCard,
185
+ type HeroFormCardProps,
186
+ type HeroFormCardSectionProps,
187
+ type HeroFormCardFieldProps,
188
+ type HeroFormCardBannerProps,
189
+ type HeroFormCardAdvancedProps,
190
+ } from './hero-form-card'
191
+
192
+ // RangeSlider — dual-thumb numeric range selector built on two
193
+ // stacked native range inputs. First consumer: Keywords filter bar
194
+ // (Volume + Avg CPC range filters).
195
+ export { RangeSlider, type RangeSliderProps } from './range-slider'
196
+
197
+ // SegmentedPill — segmented control with outlined-accent active state.
198
+ // Generic items shape so any small mutually-exclusive choice can use
199
+ // it. First consumer: Keywords lens research-mode picker.
200
+ export {
201
+ SegmentedPill,
202
+ type SegmentedPillProps,
203
+ type SegmentedPillItem,
204
+ } from './segmented-pill'
205
+
206
+ // Shell — multi-tray framed-card pattern. Outer slate frame holds one
207
+ // or more white inner trays. Each tray has a rich header (title +
208
+ // meta + optional accent numeral), body (form/grid/flush spacing
209
+ // variants), and optional recessed footer strip (secondary controls
210
+ // left, primary action right). First consumers: 5 utility tools
211
+ // (Dedupe / Combine / Replace / ConvertCase / TextFormatter) and the
212
+ // New Set / New Group pages.
213
+ export {
214
+ Shell,
215
+ ShellStat,
216
+ type ShellProps,
217
+ type ShellTrayProps,
218
+ type ShellTrayHeaderProps,
219
+ type ShellTrayBodyProps,
220
+ type ShellTrayFooterProps,
221
+ type ShellSubCardProps,
222
+ type ShellStatProps,
223
+ type ShellStatTone,
224
+ } from './shell'
225
+
226
+ // FolderCard — drive-style folder tile (colour-keyed swatch + name +
227
+ // count + hover checkbox / menu slot). Promoted in ADR-035 (Assets
228
+ // lens redesign); a card surface, so it owns the internal surface
229
+ // tokens as a primitive. Reusable by the v1.1 Swipes / Collections
230
+ // lenses (PRD §5.6).
231
+ export {
232
+ FolderCard,
233
+ FOLDER_COLOR_KEYS,
234
+ folderColorVar,
235
+ type FolderCardProps,
236
+ type FolderColorKey,
237
+ } from './folder-card'
238
+
239
+ // BulkActionBar — floating bottom-centred selection action bar.
240
+ // Promoted in ADR-035 — two consumers (Assets page + Manage Folders
241
+ // page). Owns the shell; callers pass their own action buttons.
242
+ export { BulkActionBar, type BulkActionBarProps } from './bulk-action-bar'
243
+
244
+ // File-upload subtree — promoted from brand-ai-extension (ADR-036 D20).
245
+ // D14 fix: uploadUrl + authHeaders are caller-supplied; the primitive never
246
+ // reads import.meta.env or hardcodes a URL.
247
+ export {
248
+ FileUploadButton,
249
+ FileDropZone,
250
+ FilePreviewGrid,
251
+ FilePreviewItem,
252
+ FileThumbnail,
253
+ useFileUpload,
254
+ type PendingFile,
255
+ type UploadResult,
256
+ type UseFileUploadOptions,
257
+ } from './file-upload/index'
258
+
259
+ // CodeBlock + MarkdownCode — syntax-highlighted code block and react-markdown
260
+ // code element adapter. Promoted from brand-ai-extension (ADR-036 D20).
261
+ export { CodeBlock, MarkdownCode, type CodeBlockProps } from './code-block'
262
+
263
+ // MarkdownRenderer — full markdown surface with GFM support (headings, lists,
264
+ // code via CodeBlock, tables, task lists, links). Promoted from
265
+ // brand-ai-extension chat-message.tsx (ADR-036 D20, BUILD_FRESH extraction).
266
+ export { MarkdownRenderer, type MarkdownRendererProps } from './markdown'
267
+
268
+ // ResizableHandle — pointer-driven splitter with WAI-ARIA splitter a11y.
269
+ // Promoted from the Generate lens's local mouse-event resize per
270
+ // ADR-049 D8/D9 (ADR-008 D3 2+ consumer gate: Generate lens + sidebar
271
+ // pinned-panel resize). ResizablePane is an optional thin <aside> wrapper.
272
+ export {
273
+ ResizableHandle,
274
+ ResizablePane,
275
+ type ResizableHandleProps,
276
+ type ResizablePaneProps,
277
+ } from './resizable'
278
+
279
+ // Kbd — semantic keyboard-input chip. Promoted post-ADR-050 D11 — 3+
280
+ // committed consumers (Chat title chip, palette body + footer, sidebar
281
+ // switcher-popout Esc hint). Renders <kbd> with sm/md size variants.
282
+ export { Kbd, type KbdProps } from './kbd'
283
+
284
+ // FrameStack — slate outer-frame wrapper for the shell/tray pattern.
285
+ // Wraps 2+ Card / SectionCard children as ONE visual group with 10 px
286
+ // padding + 10 px gap. Dedicated single-purpose primitive (avoids the
287
+ // "Shell" name collision with the lens-shell + the data-framed div).
288
+ // See `frame-stack.tsx` docstring for the visual diagram. Lens-level
289
+ // lock: Meta Creative `CLAUDE.md` §"The shell/tray pattern".
290
+ export { FrameStack, type FrameStackProps } from './frame-stack'
291
+
292
+ // CalculatorShell — centered, fixed-width layout primitive for marketing
293
+ // / ad-performance / customer-economics calculators. Promoted under
294
+ // ADR-058 D8 (sharpens ADR-030 D2/D3/D5/D6/D8). First-day consumers
295
+ // (ADR-008 D3 gate cleared 3×): ROAS / ROI / CTR / CPC / CPM /
296
+ // Conversion Rate. The shell is presentational; each calculator route
297
+ // owns its `useState` and computes `result` reactively.
298
+ export { CalculatorShell, type CalculatorShellProps } from './calculator-shell'
299
+
300
+ // Calculator Shell v2 kit (ADR-076). Additive — v1 above is retained for
301
+ // the existing six math calculators. v2 = a superset shell with two
302
+ // layouts + a flat brand-ink header, the StatRow signature primitive, and
303
+ // a single-value Slider. Frame = ProfileSection (reused, D3); KPI chips =
304
+ // MetricCard labelPosition="top" (D8). First consumer: /tools/ctv-frequency.
305
+ export {
306
+ CalculatorShellV2,
307
+ type CalculatorShellV2Props,
308
+ type CalcLayout,
309
+ type CalcPrefill,
310
+ } from './calculator-shell-v2'
311
+ export { StatRow, type StatRowProps, type StatRowStep } from './stat-row'
312
+ export { Slider, type SliderProps } from './slider'
313
+
314
+ // Number-formatting helpers — `formatCurrency` / `formatPercent` /
315
+ // `formatRatio` / `formatNumber`. Pure functions, zero deps (built on
316
+ // `Intl.NumberFormat`). Spec'd in ADR-030 D6 and re-exported here per
317
+ // ADR-058 D8 so calculator routes consume the shell + helpers from a
318
+ // single `@lovett/ui` import. All four return `'—'` on NaN / ±Infinity
319
+ // so partial-input UI never crashes.
320
+ export { formatCurrency, formatPercent, formatRatio, formatNumber } from './format'
321
+
322
+ // Shared utilities — used by primitives, also re-exported for app/lens code
323
+ // that needs the same class-composition or layout numerics (e.g. Phase 2's
324
+ // brand-profile atoms when they promote into packages/ui/).
325
+ export { cn } from './lib/utils'
326
+ export { HEADER_HEIGHT, SECTION_SCROLL_OFFSET } from './lib/layout'
327
+
328
+ // v1.1 lens-port-batch promotions (Phase A pre-pass). 3+ consumers each
329
+ // in the v1.1 batch (Swipes / Collections / Discover / Prompts). Source:
330
+ // `brand-ai-extension/webapp/src/components/ui/` — same shape, tokens
331
+ // scrubbed to use --foreground / --text-tertiary / public surfaces only
332
+ // where the original used --text-muted (still allowed inside packages/ui).
333
+ export { PageHero, type PageHeroProps } from './page-hero'
334
+ export { SearchBar, type SearchBarProps } from './search-bar'
335
+ export {
336
+ FilterBar,
337
+ type FilterBarProps,
338
+ type FilterBarSearchProps,
339
+ type FilterBarCountProps,
340
+ } from './filter-bar'
341
+ export {
342
+ FilterDropdown,
343
+ type FilterDropdownProps,
344
+ type FilterOption,
345
+ } from './filter-dropdown'
346
+
347
+ // Meta / Instagram platform previews — promoted from
348
+ // docs/prototype/meta/preview-package/ per ADR-060 D9a (amended
349
+ // 2026-05-28). Pixel-accurate FB+IG+Messenger renderers; zero coupling
350
+ // to stores/hooks/routers. Consumed by the Meta Creative lens's
351
+ // PreviewRail; available for future Meta-related lenses (the D9a
352
+ // promotion rationale). Namespace import keeps the 10 platform-named
353
+ // components out of the root `@lovett/ui` export.
354
+ // DeviceFrame — generic, content-agnostic device bezel (macOS Safari
355
+ // window or iPhone). Genericized/promoted from the sem-spec source app's
356
+ // SerpCard MacOSFrame + IPhoneFrame (all Google/SERP specifics stripped).
357
+ // Device-replica chrome colors are owned by the primitive (like the
358
+ // platform-chrome allowlist), not the theme palette. First reuse target:
359
+ // AdStage. Ratified by ADR-087 (accepted 2026-06-07); sem-spec retrofit is a
360
+ // tracked follow-up in that ADR.
361
+ export { DeviceFrame, type DeviceFrameProps } from './device-frame'
362
+
363
+ export * as MetaPreviews from './meta-previews'
364
+ export type {
365
+ PreviewAdData,
366
+ PreviewPlatform,
367
+ PreviewPlacement,
368
+ PreviewDevice,
369
+ PlacementPreview,
370
+ PreviewConfiguration,
371
+ } from './meta-previews/types'
372
+ export { PLACEMENT_CONFIGS } from './meta-previews/types'
package/src/input.tsx ADDED
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Input — typed text input wrapped in the .input-shell from styles.css.
3
+ * Supports leading/trailing affixes (icon, badge, action button), three
4
+ * sizes (sm 32 / md 40 / lg 48), and the standard error/disabled states.
5
+ *
6
+ * <Input value={v} onChange={e => setV(e.target.value)} placeholder="Search…" />
7
+ *
8
+ * <Input
9
+ * leadingAffix={<Search className="h-4 w-4" />}
10
+ * trailingAffix={<KeyboardShortcut keys="⌘K" />}
11
+ * placeholder="Type to search"
12
+ * />
13
+ *
14
+ * <Input error placeholder="Invalid" />
15
+ *
16
+ * <Input
17
+ * type="password"
18
+ * showPasswordToggle
19
+ * autoComplete="current-password"
20
+ * />
21
+ */
22
+ import {
23
+ forwardRef,
24
+ useState,
25
+ type InputHTMLAttributes,
26
+ type ReactNode,
27
+ } from 'react'
28
+ import { Eye, EyeOff } from 'lucide-react'
29
+ import { cn } from './lib/utils'
30
+
31
+ export type InputSize = 'sm' | 'md' | 'lg'
32
+
33
+ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'> {
34
+ inputSize?: InputSize
35
+ error?: boolean
36
+ /** Element rendered before the input — typically an icon. Static, no interaction. */
37
+ leadingAffix?: ReactNode
38
+ /** Element rendered after the input. Pass an `<button class="affix action">` for clickable. */
39
+ trailingAffix?: ReactNode
40
+ /** Override classes on the .input-shell wrapper. */
41
+ shellClassName?: string
42
+ /**
43
+ * Show/hide affordance for `type="password"`. Manages visibility state
44
+ * internally and renders an Eye/EyeOff toggle in the trailing affix slot.
45
+ * No-op when `type !== "password"`. Caller-supplied `trailingAffix`
46
+ * wins — the toggle is only injected when the slot is otherwise empty.
47
+ */
48
+ showPasswordToggle?: boolean
49
+ /**
50
+ * Numeric variant (ADR-076 D8) — renders the field in the monospace
51
+ * face with `tabular-nums` so digits align as the value changes.
52
+ * Pair with the existing `leadingAffix` (e.g. a `$`) / `trailingAffix`
53
+ * (e.g. a unit suffix) for calculator inputs. No new prefix/suffix
54
+ * props — affixes are the canonical adornment slots.
55
+ */
56
+ numeric?: boolean
57
+ }
58
+
59
+ const SIZE_CLASS: Record<InputSize, string> = {
60
+ sm: 'size-sm',
61
+ md: '',
62
+ lg: 'size-lg',
63
+ }
64
+
65
+ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
66
+ {
67
+ inputSize = 'md',
68
+ error,
69
+ leadingAffix,
70
+ trailingAffix,
71
+ shellClassName,
72
+ className,
73
+ disabled,
74
+ type,
75
+ showPasswordToggle,
76
+ numeric,
77
+ ...rest
78
+ },
79
+ ref,
80
+ ) {
81
+ const [revealed, setRevealed] = useState(false)
82
+ const isPasswordToggle =
83
+ showPasswordToggle === true && type === 'password' && !trailingAffix
84
+ const resolvedType = isPasswordToggle && revealed ? 'text' : type
85
+ const resolvedTrailingAffix = isPasswordToggle ? (
86
+ <button
87
+ type="button"
88
+ onClick={() => setRevealed((v) => !v)}
89
+ aria-label={revealed ? 'Hide password' : 'Show password'}
90
+ aria-pressed={revealed}
91
+ className="affix action"
92
+ tabIndex={-1}
93
+ >
94
+ {revealed ? (
95
+ <EyeOff className="h-4 w-4" aria-hidden />
96
+ ) : (
97
+ <Eye className="h-4 w-4" aria-hidden />
98
+ )}
99
+ </button>
100
+ ) : (
101
+ trailingAffix
102
+ )
103
+
104
+ return (
105
+ <div
106
+ className={cn(
107
+ 'input-shell',
108
+ SIZE_CLASS[inputSize],
109
+ numeric && 'is-numeric',
110
+ leadingAffix && 'has-leading',
111
+ resolvedTrailingAffix && 'has-trailing',
112
+ error && 'is-error',
113
+ disabled && 'is-disabled',
114
+ shellClassName,
115
+ )}
116
+ >
117
+ {leadingAffix && <span className="affix">{leadingAffix}</span>}
118
+ <input
119
+ ref={ref}
120
+ type={resolvedType}
121
+ disabled={disabled}
122
+ className={className}
123
+ {...rest}
124
+ />
125
+ {resolvedTrailingAffix && (
126
+ <span className="affix">{resolvedTrailingAffix}</span>
127
+ )}
128
+ </div>
129
+ )
130
+ })
package/src/kbd.tsx ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Kbd — semantic keyboard-input chip.
3
+ *
4
+ * Promoted from the Chat lens's inline title-chip markup per ADR-050 D11.
5
+ * Committed consumers (ADR-008 D3 promotion gate):
6
+ * • Chat lens title-bar chip (apps/workspace/src/lenses/chat/index.tsx) —
7
+ * first migrated consumer, lands in this phase.
8
+ * • Command palette body / footer hints (ADR-050 Phases B–E).
9
+ * • Sidebar switcher-popout Esc hint (ADR-049 Phase C).
10
+ *
11
+ * Renders a `<kbd>` semantic element (screen readers announce as keyboard
12
+ * input). Token-driven styling — no hex literals, all colors flow through
13
+ * public tokens.
14
+ */
15
+
16
+ import type { ReactNode, CSSProperties } from 'react'
17
+
18
+ export interface KbdProps {
19
+ /** Child text — typically a single key glyph or short string. */
20
+ children: ReactNode
21
+ /** Optional size — defaults to 'md'. */
22
+ size?: 'sm' | 'md'
23
+ }
24
+
25
+ export function Kbd({ children, size = 'md' }: KbdProps) {
26
+ const style: CSSProperties = {
27
+ display: 'inline-flex',
28
+ alignItems: 'center',
29
+ justifyContent: 'center',
30
+ gap: '0.125rem',
31
+ background: 'rgb(var(--surface-overlay-soft))',
32
+ border: '1px solid rgb(var(--border))',
33
+ color: 'rgb(var(--text-tertiary))',
34
+ fontFamily: 'var(--font-mono)',
35
+ fontSize: size === 'sm' ? '10.5px' : '11.5px',
36
+ padding: size === 'sm' ? '1px 6px' : '2px 8px',
37
+ borderRadius: 'var(--radius-xs)',
38
+ lineHeight: 1,
39
+ }
40
+ return <kbd style={style}>{children}</kbd>
41
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Layout constants shared between CSS and JS.
3
+ *
4
+ * Some primitives (e.g. ChipNav) need numeric pixel values for
5
+ * `IntersectionObserver` rootMargin or `position: sticky` `top` style
6
+ * — those can't read from CSS custom properties. To keep the layout
7
+ * coherent, this module mirrors the canonical values from `tokens.css`.
8
+ * If you change one, change the other.
9
+ *
10
+ * The corresponding CSS tokens are:
11
+ * --header-height → HEADER_HEIGHT
12
+ * --section-scroll-offset → SECTION_SCROLL_OFFSET
13
+ */
14
+
15
+ /** Height of the sticky AppHeader, in px. Mirrors `--header-height`. */
16
+ export const HEADER_HEIGHT = 56
17
+
18
+ /**
19
+ * Vertical offset for in-page section anchors (header + chip nav +
20
+ * breathing room). Mirrors `--section-scroll-offset`.
21
+ */
22
+ export const SECTION_SCROLL_OFFSET = 120
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }