@moldable-ai/ui 0.2.26 → 0.2.31

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 (186) hide show
  1. package/dist/catalog/component-catalog.d.ts +9 -2
  2. package/dist/catalog/component-catalog.d.ts.map +1 -1
  3. package/dist/catalog/component-catalog.js +11 -7
  4. package/dist/catalog/core-control-stories.d.ts.map +1 -1
  5. package/dist/catalog/core-control-stories.js +1 -1
  6. package/dist/catalog/core-foundation-stories.d.ts.map +1 -1
  7. package/dist/catalog/core-foundation-stories.js +15 -3
  8. package/dist/components/ui/app-frame.d.ts +20 -1
  9. package/dist/components/ui/app-frame.d.ts.map +1 -1
  10. package/dist/components/ui/app-frame.js +37 -7
  11. package/dist/components/ui/badge.d.ts +1 -1
  12. package/dist/components/ui/badge.js +1 -1
  13. package/dist/components/ui/button.d.ts +5 -3
  14. package/dist/components/ui/button.d.ts.map +1 -1
  15. package/dist/components/ui/button.js +39 -10
  16. package/dist/components/ui/card.d.ts +12 -2
  17. package/dist/components/ui/card.d.ts.map +1 -1
  18. package/dist/components/ui/card.js +3 -2
  19. package/dist/components/ui/checkbox.d.ts +4 -1
  20. package/dist/components/ui/checkbox.d.ts.map +1 -1
  21. package/dist/components/ui/checkbox.js +4 -4
  22. package/dist/components/ui/context-menu.js +6 -6
  23. package/dist/components/ui/date-field.d.ts +1 -1
  24. package/dist/components/ui/dropdown-menu.js +6 -6
  25. package/dist/components/ui/icon-button.d.ts +1 -1
  26. package/dist/components/ui/icon-button.d.ts.map +1 -1
  27. package/dist/components/ui/icon-button.js +1 -0
  28. package/dist/components/ui/index.d.ts +1 -0
  29. package/dist/components/ui/index.d.ts.map +1 -1
  30. package/dist/components/ui/index.js +1 -0
  31. package/dist/components/ui/input-group.d.ts +2 -2
  32. package/dist/components/ui/input.d.ts +6 -2
  33. package/dist/components/ui/input.d.ts.map +1 -1
  34. package/dist/components/ui/input.js +2 -2
  35. package/dist/components/ui/material.d.ts +8 -3
  36. package/dist/components/ui/material.d.ts.map +1 -1
  37. package/dist/components/ui/material.js +2 -2
  38. package/dist/components/ui/search-field.d.ts +3 -1
  39. package/dist/components/ui/search-field.d.ts.map +1 -1
  40. package/dist/components/ui/search-field.js +16 -6
  41. package/dist/components/ui/segmented-control.d.ts +3 -1
  42. package/dist/components/ui/segmented-control.d.ts.map +1 -1
  43. package/dist/components/ui/segmented-control.js +23 -17
  44. package/dist/components/ui/select.js +2 -2
  45. package/dist/components/ui/sidebar.d.ts +5 -3
  46. package/dist/components/ui/sidebar.d.ts.map +1 -1
  47. package/dist/components/ui/sidebar.js +53 -15
  48. package/dist/components/ui/status.d.ts +1 -1
  49. package/dist/components/ui/status.js +1 -1
  50. package/dist/components/ui/switch.d.ts +3 -1
  51. package/dist/components/ui/switch.d.ts.map +1 -1
  52. package/dist/components/ui/switch.js +3 -2
  53. package/dist/components/ui/toolbar-controls.d.ts +49 -0
  54. package/dist/components/ui/toolbar-controls.d.ts.map +1 -0
  55. package/dist/components/ui/toolbar-controls.js +47 -0
  56. package/dist/components/ui/toolbar.d.ts +5 -4
  57. package/dist/components/ui/toolbar.d.ts.map +1 -1
  58. package/dist/components/ui/toolbar.js +3 -3
  59. package/dist/index.d.ts +2 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +3 -1
  62. package/dist/lib/frame-lifecycle.d.ts +1 -0
  63. package/dist/lib/frame-lifecycle.d.ts.map +1 -1
  64. package/dist/lib/frame-lifecycle.js +7 -0
  65. package/dist/lib/theme.d.ts +39 -5
  66. package/dist/lib/theme.d.ts.map +1 -1
  67. package/dist/lib/theme.js +105 -17
  68. package/dist/tokens/contracts.d.ts +79 -0
  69. package/dist/tokens/contracts.d.ts.map +1 -0
  70. package/dist/tokens/contracts.js +57 -0
  71. package/dist/tokens/index.d.ts +2 -0
  72. package/dist/tokens/index.d.ts.map +1 -0
  73. package/dist/tokens/index.js +1 -0
  74. package/docs/README.md +2 -0
  75. package/docs/adaptive-materials.md +14 -9
  76. package/docs/design-tokens.md +10 -6
  77. package/docs/foundations.md +3 -3
  78. package/docs/macos-design-audit.md +144 -0
  79. package/docs/native-integration.md +4 -4
  80. package/docs/platform-architecture.md +189 -0
  81. package/docs/standalone-app-windows.md +37 -0
  82. package/docs/visual-regression.md +6 -8
  83. package/package.json +13 -1
  84. package/src/components/ui/accordion.md +46 -0
  85. package/src/components/ui/alert.md +12 -4
  86. package/src/components/ui/app-frame.md +8 -5
  87. package/src/components/ui/aspect-ratio.md +37 -0
  88. package/src/components/ui/avatar.md +48 -0
  89. package/src/components/ui/breadcrumb.md +52 -0
  90. package/src/components/ui/button-group.md +43 -0
  91. package/src/components/ui/button.md +20 -4
  92. package/src/components/ui/calendar.md +42 -0
  93. package/src/components/ui/card.md +43 -0
  94. package/src/components/ui/carousel.md +47 -0
  95. package/src/components/ui/chart.md +52 -0
  96. package/src/components/ui/checkbox.md +36 -0
  97. package/src/components/ui/collapsible.md +2 -3
  98. package/src/components/ui/color-well.md +14 -4
  99. package/src/components/ui/combobox.md +3 -3
  100. package/src/components/ui/confirm-dialog.md +5 -4
  101. package/src/components/ui/context-menu.md +3 -2
  102. package/src/components/ui/date-field.md +6 -1
  103. package/src/components/ui/date-picker.md +6 -3
  104. package/src/components/ui/dialog.md +14 -12
  105. package/src/components/ui/drawer.md +58 -0
  106. package/src/components/ui/error-boundary-view.md +1 -1
  107. package/src/components/ui/field.md +2 -1
  108. package/src/components/ui/form.md +71 -0
  109. package/src/components/ui/grid.md +4 -3
  110. package/src/components/ui/icon-button.md +4 -1
  111. package/src/components/ui/input-group.md +49 -0
  112. package/src/components/ui/input-otp.md +56 -0
  113. package/src/components/ui/input.md +40 -0
  114. package/src/components/ui/inspector.md +47 -7
  115. package/src/components/ui/item.md +5 -3
  116. package/src/components/ui/kbd.md +30 -0
  117. package/src/components/ui/label.md +29 -0
  118. package/src/components/ui/list.md +2 -2
  119. package/src/components/ui/material.md +25 -12
  120. package/src/components/ui/menu-items.md +60 -0
  121. package/src/components/ui/menubar.md +58 -0
  122. package/src/components/ui/message-scroller-primitive.md +70 -0
  123. package/src/components/ui/message-scroller.md +86 -0
  124. package/src/components/ui/navigation-button-group.md +4 -3
  125. package/src/components/ui/navigation-menu.md +52 -0
  126. package/src/components/ui/number-input.md +8 -0
  127. package/src/components/ui/pagination.md +51 -0
  128. package/src/components/ui/panel.md +6 -0
  129. package/src/components/ui/popover.md +10 -7
  130. package/src/components/ui/progress.md +31 -0
  131. package/src/components/ui/radio-group.md +36 -0
  132. package/src/components/ui/resizable.md +2 -1
  133. package/src/components/ui/scroll-area.md +3 -1
  134. package/src/components/ui/search-field.md +6 -1
  135. package/src/components/ui/segmented-control.md +6 -1
  136. package/src/components/ui/separator.md +35 -0
  137. package/src/components/ui/sidebar.md +20 -3
  138. package/src/components/ui/skeleton.md +36 -0
  139. package/src/components/ui/slider.md +58 -0
  140. package/src/components/ui/spinner.md +41 -0
  141. package/src/components/ui/split-view.md +8 -4
  142. package/src/components/ui/switch.md +44 -0
  143. package/src/components/ui/table.md +7 -7
  144. package/src/components/ui/tabs.md +3 -1
  145. package/src/components/ui/textarea.md +41 -0
  146. package/src/components/ui/toggle-group.md +2 -2
  147. package/src/components/ui/toggle.md +9 -9
  148. package/src/components/ui/toolbar-controls.md +83 -0
  149. package/src/components/ui/toolbar.md +12 -6
  150. package/src/styles/index.css +199 -107
  151. package/src/tokens/contracts.ts +131 -0
  152. package/src/tokens/index.css +6 -0
  153. package/src/tokens/index.ts +23 -0
  154. package/src/tokens/macos-26/README.md +37 -0
  155. package/src/tokens/macos-26/aliases.css +53 -0
  156. package/src/tokens/macos-26/compatibility.css +7 -0
  157. package/src/tokens/macos-26/index.css +3 -0
  158. package/src/tokens/macos-26/source/provenance.generated.json +28 -0
  159. package/src/tokens/macos-26/source/sidebar.generated.css +29 -0
  160. package/src/tokens/macos-26/source.css +7 -0
  161. package/src/tokens/macos-26/web-adaptations.json +25 -0
  162. package/src/tokens/macos-26/web-adaptations.md +15 -0
  163. package/src/tokens/macos-27/accessibility.css +11 -0
  164. package/src/tokens/macos-27/aliases.css +304 -0
  165. package/src/tokens/macos-27/compatibility.css +212 -0
  166. package/src/tokens/macos-27/index.css +15 -0
  167. package/src/tokens/macos-27/recipes.css +23 -0
  168. package/src/tokens/macos-27/source/chrome.generated.css +338 -0
  169. package/src/tokens/macos-27/source/colors.generated.css +140 -0
  170. package/src/tokens/macos-27/source/context.generated.css +18 -0
  171. package/src/tokens/macos-27/source/controls.generated.css +764 -0
  172. package/src/tokens/macos-27/source/effects.generated.css +68 -0
  173. package/src/tokens/macos-27/source/kit.generated.css +50 -0
  174. package/src/tokens/macos-27/source/overlays-status.generated.css +931 -0
  175. package/src/tokens/macos-27/source/paint-styles.generated.css +81 -0
  176. package/src/tokens/macos-27/source/provenance.generated.json +62 -0
  177. package/src/tokens/macos-27/source/sizes.generated.css +385 -0
  178. package/src/tokens/macos-27/source/source.generated.json +554 -0
  179. package/src/tokens/macos-27/source/text-styles.generated.css +116 -0
  180. package/src/tokens/macos-27/source.css +17 -0
  181. package/src/tokens/macos-27/web-adaptations.css +98 -0
  182. package/src/tokens/macos-27/web-adaptations.json +163 -0
  183. package/src/tokens/macos-27/web-adaptations.md +117 -0
  184. package/src/tokens/moldable/brand.css +48 -0
  185. package/src/tokens/moldable/index.css +1 -0
  186. package/src/tokens/platform-contract.test.ts +290 -0
@@ -1,11 +1,51 @@
1
1
  # Inspector
2
2
 
3
- Secondary property controls for the current selection.
3
+ ## Purpose
4
4
 
5
- Compose `Inspector`, `InspectorHeader`, `InspectorContent`,
6
- `InspectorSection`, and aligned `InspectorRow` pairs. Controls should usually
7
- use compact sizes.
5
+ The inspector primitives compose a secondary property pane for the current
6
+ selection. The root is an `aside`; its header stays fixed while
7
+ `InspectorContent` scrolls and includes bottom chat clearance.
8
8
 
9
- The inspector must remain subordinate to the primary pane. Collapse it when
10
- width is constrained, group related properties, and avoid placing primary
11
- workflow actions inside it.
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import {
13
+ Input,
14
+ Inspector,
15
+ InspectorContent,
16
+ InspectorHeader,
17
+ InspectorLabel,
18
+ InspectorRow,
19
+ InspectorSection,
20
+ InspectorValue,
21
+ } from '@moldable-ai/ui'
22
+
23
+ ;<Inspector>
24
+ <InspectorHeader>Inspector</InspectorHeader>
25
+ <InspectorContent>
26
+ <InspectorSection title="Appearance">
27
+ <InspectorRow>
28
+ <InspectorLabel htmlFor="document-title">Title</InspectorLabel>
29
+ <InspectorValue>
30
+ <Input id="document-title" controlSize="small" />
31
+ </InspectorValue>
32
+ </InspectorRow>
33
+ </InspectorSection>
34
+ </InspectorContent>
35
+ </Inspector>
36
+ ```
37
+
38
+ `InspectorSection` accepts optional `title` and `actions`; it omits its header
39
+ when neither is present. `InspectorRow` aligns a label and value in two
40
+ columns. `InspectorLabel` renders a compact native label, and
41
+ `InspectorValue` contains the control or read-only value.
42
+
43
+ ## Quality bar
44
+
45
+ - Keep the inspector subordinate to the primary pane and collapse it when
46
+ width is constrained.
47
+ - Group related properties into short sections and use section actions only
48
+ for local operations.
49
+ - Connect each `InspectorLabel` to its control with `htmlFor` and `id`.
50
+ - Use compact control sizes and keep unpredictable values truncatable.
51
+ - Keep primary workflow actions out of the inspector.
@@ -23,7 +23,7 @@ import {
23
23
  } from '@moldable-ai/ui'
24
24
 
25
25
  ;<ItemGroup>
26
- <Item size="sm">
26
+ <Item role="listitem" size="sm">
27
27
  <ItemMedia variant="icon">
28
28
  <FileText />
29
29
  </ItemMedia>
@@ -53,8 +53,10 @@ Use `asChild` when the whole item is a link or another interactive primitive. Av
53
53
 
54
54
  ## API summary
55
55
 
56
- - `ItemGroup`: `role="list"` container.
57
- - `Item`: `variant="default"`, `"outline"`, or `"muted"`; `size="default"` or `"sm"`; optional `asChild`.
56
+ - `ItemGroup`: `role="list"` container. Give each direct `Item`
57
+ `role="listitem"`.
58
+ - `Item`: `variant="default"`, `"outline"`, or `"muted"`;
59
+ `size="default"` or `"sm"`; optional `asChild`.
58
60
  - `ItemMedia`: `variant="default"`, `"icon"`, or `"image"`.
59
61
  - `ItemContent`: flexible title/description column.
60
62
  - `ItemTitle`, `ItemDescription`: primary and secondary text.
@@ -0,0 +1,30 @@
1
+ # Kbd
2
+
3
+ ## Purpose
4
+
5
+ `Kbd` displays a keyboard key or shortcut hint. `KbdGroup` lays out several
6
+ keys as one shortcut. These components are visual labels only; they do not
7
+ register keyboard handlers.
8
+
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import { Kbd, KbdGroup } from '@moldable-ai/ui'
13
+
14
+ ;<KbdGroup aria-label="Command K">
15
+ <Kbd>⌘</Kbd>
16
+ <Kbd>K</Kbd>
17
+ </KbdGroup>
18
+ ```
19
+
20
+ Both components accept native `<kbd>` props and `className`. `Kbd` applies the
21
+ keycap treatment and sizes unclassified SVG children; `KbdGroup` supplies only
22
+ an inline flex layout.
23
+
24
+ ## Guidance
25
+
26
+ - Show shortcuts only when the command really supports them.
27
+ - Keep key names brief and use the same notation throughout the app.
28
+ - Add an accessible label when modifier glyphs may not be announced clearly.
29
+ - Keep `Kbd` non-interactive; place click behavior on a real button or link.
30
+ - Do not use shortcut hints as the only way to discover an action.
@@ -0,0 +1,29 @@
1
+ # Label
2
+
3
+ ## Purpose
4
+
5
+ `Label` gives a form control a visible, clickable name. It wraps the Radix
6
+ label primitive and accepts its props.
7
+
8
+ ## Usage
9
+
10
+ ```tsx
11
+ import { Input, Label } from '@moldable-ai/ui'
12
+
13
+ ;<div className="space-y-2">
14
+ <Label htmlFor="project-name">Project name</Label>
15
+ <Input id="project-name" name="projectName" />
16
+ </div>
17
+ ```
18
+
19
+ ## Guidance
20
+
21
+ - Pair `htmlFor` with a unique control `id`, or nest the control when that is
22
+ the clearer native relationship.
23
+ - Use a short noun phrase and keep instructions or validation outside the
24
+ label.
25
+ - Keep a visible label for text fields; a placeholder is not a replacement.
26
+ - Mark optional or required state consistently and expose it to assistive
27
+ technology on the control.
28
+ - The built-in styles respond to disabled peer or group state; set the actual
29
+ control's `disabled` attribute as well.
@@ -81,8 +81,8 @@ const [selected, setSelected] = useState<ReadonlySet<string>>(
81
81
 
82
82
  - Arrow Up/Down moves through a vertical list; Arrow Left/Right moves through a horizontal list.
83
83
  - Home and End move to the first and last available item.
84
- - Space changes selection.
85
- - Enter changes selection and invokes `onAction`.
84
+ - Space changes selection when selection is enabled.
85
+ - Enter changes selection when enabled and invokes `onAction`.
86
86
  - A single pointer click changes selection. A double click invokes `onAction`.
87
87
  - Disabled items are skipped during keyboard navigation and cannot be selected or activated.
88
88
  - Interactive descendants such as buttons and links keep their own pointer behavior. They do not select or activate the containing item.
@@ -1,13 +1,14 @@
1
1
  # Material
2
2
 
3
- Adaptive material for navigation and control chrome.
3
+ `Material` provides an adaptive sampled layer for navigation and control
4
+ chrome. `MaterialGroup` gives a related control cluster one shared layer.
4
5
 
5
6
  ```tsx
6
- <Material asChild variant="regular" elevation="none">
7
+ <Material asChild variant="medium" elevation="none">
7
8
  <Toolbar>{/* context and actions */}</Toolbar>
8
9
  </Material>
9
10
 
10
- <MaterialGroup variant="regular" shape="capsule" spacing="compact">
11
+ <MaterialGroup variant="thin" shape="capsule" spacing="compact">
11
12
  <IconButton label="Previous" variant="ghost">
12
13
  <ChevronLeft />
13
14
  </IconButton>
@@ -19,15 +20,25 @@ Adaptive material for navigation and control chrome.
19
20
 
20
21
  ## Variants
21
22
 
22
- - `regular` provides a frosted, legible control or navigation layer.
23
- - `clear` is less frosted and is only for compact controls floating over
24
- photos, maps, video, or another richly colored canvas.
23
+ - Standard thicknesses are `ultra-thick`, `thick`, `medium`, `thin`, and
24
+ `ultra-thin`, ordered from most filled to most transparent. They all use the
25
+ standard material blur; thickness controls translucency.
26
+ - `regular` is a compatibility alias for `medium` and remains the default.
27
+ - `clear` is a compatibility alias for `ultra-thin`.
25
28
 
26
- Both variants start with an opaque fallback. Backdrop sampling is enabled only
27
- when the renderer supports it. Reduced transparency and forced colors keep the
28
- fallback opaque.
29
+ Every variant starts with an opaque fallback. Backdrop sampling is enabled only
30
+ when the renderer supports it.
29
31
 
30
- ## Grouping
32
+ ## Shape, elevation, and grouping
33
+
34
+ - `shape` is `rounded`, `capsule`, or `circle`; it defaults to `rounded`.
35
+ - `elevation` is `none`, `panel`, or `overlay`; it defaults to `none` and
36
+ controls shadow independently of thickness.
37
+ - `interactive` adds hover and press feedback when the material surface itself
38
+ is the control.
39
+ - `asChild` applies `Material` to its single child through a Radix slot.
40
+ - `MaterialGroup` supports `spacing="compact" | "default" | "comfortable"`
41
+ and defaults to `default`.
31
42
 
32
43
  `MaterialGroup` creates one sampled layer for a related cluster. Put ordinary
33
44
  ghost or transparent controls inside it. Do not wrap each child in another
@@ -42,8 +53,10 @@ do not read as one group.
42
53
  temporary control layers.
43
54
  - Keep documents, calendars, tables, forms, cards, sheets, and primary content
44
55
  canvases opaque.
45
- - Prefer `regular`. A `clear` surface needs stable contrast across every part
46
- of the media behind it.
56
+ - Prefer a named standard thickness when the hierarchy is known. Use the
57
+ compatibility aliases only where their existing semantics are intentional.
58
+ - Thin and ultra-thin surfaces need stable contrast across every part of the
59
+ content behind them.
47
60
  - Use `interactive` only when the material surface itself is the control.
48
61
  - Do not animate blur or saturation. Shared motion tokens provide restrained
49
62
  press feedback and remove it when reduced motion is requested.
@@ -0,0 +1,60 @@
1
+ # Menu Items
2
+
3
+ ## Purpose
4
+
5
+ `menu-items.tsx` is the internal adapter shared by `DropdownMenu` and
6
+ `ContextMenu` when their declarative `items` API is used. It coordinates the
7
+ host-native request and portable fallback, formats shortcut labels, renders
8
+ portable row contents, and removes conflicting authored content.
9
+
10
+ App code should use `DropdownMenu` or `ContextMenu`, not import these helpers
11
+ directly.
12
+
13
+ ## Declarative usage
14
+
15
+ ```tsx
16
+ import { Button, DropdownMenu, DropdownMenuTrigger } from '@moldable-ai/ui'
17
+
18
+ ;<DropdownMenu
19
+ items={[
20
+ {
21
+ id: 'rename',
22
+ label: 'Rename',
23
+ shortcut: { key: 'r', modifiers: ['cmd'] },
24
+ },
25
+ { id: 'divider', kind: 'separator' },
26
+ { id: 'delete', label: 'Move to trash', destructive: true },
27
+ ]}
28
+ onSelectItem={(id) => runCommand(id)}
29
+ >
30
+ <DropdownMenuTrigger asChild>
31
+ <Button
32
+ type="button"
33
+ variant="outline"
34
+ size="sm"
35
+ className="cursor-pointer"
36
+ >
37
+ Actions
38
+ </Button>
39
+ </DropdownMenuTrigger>
40
+ </DropdownMenu>
41
+ ```
42
+
43
+ ## Internal contract
44
+
45
+ - `useDeclarativeMenuState` supports controlled or uncontrolled portable-open
46
+ state. While a native request is active, `nativeOpen` is true and
47
+ `portableOpen` is forced false.
48
+ - A completed native request passes `selectedId` to `onSelectItem`.
49
+ Cancellation does nothing. A fallback result or request failure opens the
50
+ portable menu unless the caller supplies its own `onFallback`.
51
+ - `formatMenuShortcut` renders modifiers in array order as `⌘`, `⇧`, `⌥`, and
52
+ `⌃`, then uppercases a one-character key. It is display metadata only.
53
+ - `MenuItemContents` renders an optional Lucide icon, the label, and a `Kbd`
54
+ shortcut. Separators are handled by the owning menu.
55
+ - Content detection and removal recurse through fragments. When declarative
56
+ items and authored content are both provided, the owning menu reports the
57
+ conflict in development and declarative items win.
58
+
59
+ Keep item IDs stable and labels concise. Do not execute a command from shortcut
60
+ metadata, and do not mix declarative items with authored menu content.
@@ -0,0 +1,58 @@
1
+ # Menubar
2
+
3
+ ## Purpose
4
+
5
+ `Menubar` presents a persistent row of related command menus, such as File,
6
+ Edit, and View. Use it for command families that benefit from repeated
7
+ keyboard access; use `DropdownMenu` for a single contextual action menu.
8
+
9
+ ## Usage
10
+
11
+ ```tsx
12
+ import {
13
+ Menubar,
14
+ MenubarContent,
15
+ MenubarItem,
16
+ MenubarMenu,
17
+ MenubarShortcut,
18
+ MenubarTrigger,
19
+ } from '@moldable-ai/ui'
20
+
21
+ ;<Menubar>
22
+ <MenubarMenu>
23
+ <MenubarTrigger className="cursor-pointer">File</MenubarTrigger>
24
+ <MenubarContent>
25
+ <MenubarItem onSelect={createDocument}>
26
+ New document
27
+ <MenubarShortcut>⌘N</MenubarShortcut>
28
+ </MenubarItem>
29
+ </MenubarContent>
30
+ </MenubarMenu>
31
+ </Menubar>
32
+ ```
33
+
34
+ ## API summary
35
+
36
+ - `Menubar`, `MenubarMenu`, and `MenubarTrigger` form the required shell.
37
+ - `MenubarContent` is portalled and defaults to `align="start"`,
38
+ `alignOffset={-4}`, and `sideOffset={8}`.
39
+ - `MenubarItem` supports `inset` and
40
+ `variant="default" | "destructive"`.
41
+ - Checkbox items, radio groups/items, groups, labels, separators, shortcuts,
42
+ and submenus are exported for structured command sets.
43
+ - `MenubarLabel` and `MenubarSubTrigger` support `inset`.
44
+ - Components otherwise accept the corresponding Radix menubar props.
45
+
46
+ ## Guidance
47
+
48
+ - Keep top-level trigger labels short and conventional.
49
+ - Use checkbox and radio items for menu-owned state, and `onSelect` for
50
+ commands.
51
+ - Put destructive commands last or in a separated group; do not rely on color
52
+ alone to communicate risk.
53
+ - `MenubarShortcut` is display text only and does not register a handler.
54
+ - Radix supplies menu roles, focus movement, typeahead, Escape dismissal, and
55
+ submenu navigation. Preserve those semantics and give icon-only content an
56
+ accessible name.
57
+ - Keep submenus shallow and use `Popover` or `Dialog` for arbitrary form
58
+ controls.
@@ -0,0 +1,70 @@
1
+ # Message Scroller Primitive
2
+
3
+ ## Purpose
4
+
5
+ `message-scroller-primitive.tsx` is the unstyled React wiring for the
6
+ in-repository transcript controller and scroll-ownership policy. The public
7
+ `MessageScroller*` components wrap this module with layout, button, and
8
+ animation styles; app code should use those wrappers.
9
+
10
+ ## Anatomy
11
+
12
+ ```tsx
13
+ <MessageScroller.Provider autoScroll>
14
+ <MessageScroller.Root>
15
+ <MessageScroller.Viewport>
16
+ <MessageScroller.Content>
17
+ <MessageScroller.Item messageId="turn-1" scrollAnchor>
18
+ {/* message */}
19
+ </MessageScroller.Item>
20
+ </MessageScroller.Content>
21
+ </MessageScroller.Viewport>
22
+ <MessageScroller.Button direction="end" />
23
+ </MessageScroller.Root>
24
+ </MessageScroller.Provider>
25
+ ```
26
+
27
+ This local-only example uses the `MessageScroller` object exported by the
28
+ primitive module. `Provider` must wrap every root, item, button, and hook.
29
+
30
+ ## Primitive contract
31
+
32
+ - Provider defaults: `autoScroll={false}`, `defaultScrollPosition="end"`,
33
+ `scrollEdgeThreshold={8}`, `scrollPreviousItemPeek={64}`, and
34
+ `scrollMargin={0}`. Changing `defaultScrollPosition` resets the one-time
35
+ initial-position decision for the next content mutation.
36
+ - `Viewport` forwards scroll, wheel, touch, and scroll-key events after
37
+ notifying the controller. It defaults to a focusable messages region and
38
+ observes viewport size changes.
39
+ - `Content` defaults to an additions-only live log, observes direct child
40
+ mutations and size changes, and appends a hidden internal spacer.
41
+ - `Item` registers `messageId` elements and writes anchor data used for
42
+ initial placement, new-turn anchoring, targeted scrolling, and visibility.
43
+ - `Button` supports `direction="start" | "end"`, `behavior`, and a `render`
44
+ element or render function. Defaults are end, smooth, and `type="button"`.
45
+ Inactive buttons are inert with `tabIndex={-1}`; preventing the click event's
46
+ default also prevents the scroll command.
47
+
48
+ ## Controller and policy behavior
49
+
50
+ The controller is the sole owner of programmatic transcript movement. The
51
+ policy distinguishes following the end, free reading, an anchored turn, and a
52
+ settling commanded jump. Wheel, touch, scroll-key, scrollbar, or other motion
53
+ away from a declared target gives control to the reader immediately.
54
+
55
+ It also:
56
+
57
+ - keeps a followed end pinned during growth;
58
+ - reanchors a streaming turn after resize;
59
+ - preserves the first visible row when older items are prepended;
60
+ - restores a stable visible leaf when content above collapses;
61
+ - grows the internal spacer so late items can reach the requested alignment;
62
+ - exposes edge state as `data-scrollable` and active programmatic movement as
63
+ `data-autoscrolling` on root and viewport;
64
+ - observes visibility only while `useMessageScrollerVisibility` has
65
+ subscribers.
66
+
67
+ For finer reading-position stability inside a complex item, mark stable
68
+ descendants with `data-message-scroll-anchor`. Do not manipulate the
69
+ controller, spacer, data attributes, or scroll position from a competing
70
+ effect; extend the wrapper API when new behavior is required.
@@ -0,0 +1,86 @@
1
+ # Message Scroller
2
+
3
+ ## Purpose
4
+
5
+ `MessageScroller` is the styled, public transcript scroller. It keeps streamed
6
+ content stable, preserves the reader's position when history is prepended or
7
+ content above changes size, supports turn anchors, and exposes jump and
8
+ visibility state.
9
+
10
+ ## Usage
11
+
12
+ ```tsx
13
+ import {
14
+ MessageScroller,
15
+ MessageScrollerButton,
16
+ MessageScrollerContent,
17
+ MessageScrollerItem,
18
+ MessageScrollerProvider,
19
+ MessageScrollerViewport,
20
+ } from '@moldable-ai/ui'
21
+
22
+ ;<MessageScrollerProvider autoScroll defaultScrollPosition="end">
23
+ <MessageScroller>
24
+ <MessageScrollerViewport>
25
+ <MessageScrollerContent className="gap-3 p-3">
26
+ {messages.map((message) => (
27
+ <MessageScrollerItem
28
+ key={message.id}
29
+ messageId={message.id}
30
+ scrollAnchor={message.role === 'user'}
31
+ >
32
+ {renderMessage(message)}
33
+ </MessageScrollerItem>
34
+ ))}
35
+ </MessageScrollerContent>
36
+ </MessageScrollerViewport>
37
+ <MessageScrollerButton />
38
+ </MessageScroller>
39
+ </MessageScrollerProvider>
40
+ ```
41
+
42
+ ## API summary
43
+
44
+ - `MessageScrollerProvider` is required. Defaults are `autoScroll={false}`,
45
+ `defaultScrollPosition="end"`, `scrollEdgeThreshold={8}`,
46
+ `scrollPreviousItemPeek={64}`, and `scrollMargin={0}`.
47
+ - Initial position can be `start`, `end`, or `last-anchor`. The last option
48
+ anchors the newest marked turn only when enough transcript follows it;
49
+ otherwise it lands at the end.
50
+ - `MessageScrollerViewport` is a focusable `role="region"` labelled
51
+ “Messages” by default. `preserveScrollOnPrepend` defaults to true.
52
+ - `MessageScrollerContent` defaults to `role="log"` and
53
+ `aria-relevant="additions"`. It accepts `spacerClassName` for the internal
54
+ positioning spacer.
55
+ - `MessageScrollerItem` accepts `messageId` and `scrollAnchor` (false by
56
+ default). IDs enable targeted scrolling and visibility reporting; mark the
57
+ start of each new turn as an anchor.
58
+ - `MessageScrollerButton` defaults to an end jump with smooth behavior,
59
+ `variant="secondary"`, and `size="icon-sm"`. It becomes inert and leaves
60
+ the tab order when that edge is not scrollable. `direction="start"` creates
61
+ the corresponding top jump.
62
+
63
+ ## Hooks and behavior
64
+
65
+ - `useMessageScroller()` returns `scrollToStart`, `scrollToEnd`, and
66
+ `scrollToMessage`. Targeted scrolls accept `align`, `behavior`, and
67
+ `scrollMargin`; alignment defaults to `start` and behavior to `auto`.
68
+ - `useMessageScrollerScrollable()` returns `{ start, end }`.
69
+ - `useMessageScrollerVisibility()` returns `currentAnchorId` and ordered
70
+ `visibleMessageIds`.
71
+ - Hooks must be descendants of the provider.
72
+ - Reader input takes ownership immediately. With `autoScroll`, content follows
73
+ the end only while follow mode is active; a newly appended turn anchor is
74
+ held near the top with the configured previous-item peek.
75
+
76
+ ## Guidance
77
+
78
+ - Give every navigable item a stable `messageId`; do not use an array index.
79
+ - Mark only meaningful turn starts as `scrollAnchor`.
80
+ - Keep one viewport as the owning vertical scroll region and give its flex
81
+ ancestors `min-h-0`.
82
+ - Preserve the default region label or supply a more specific `aria-label`.
83
+ - Keep the content's log semantics and busy state accurate while streaming or
84
+ loading history.
85
+ - Do not add a second effect that writes `scrollTop`; all movement must go
86
+ through the scroller API so it cannot fight the reader.
@@ -17,9 +17,10 @@ consistent compact treatment.
17
17
  - `canGoBack` and `canGoForward` disable unavailable directions.
18
18
  - `onBack` and `onForward` perform caller-owned navigation.
19
19
  - `backLabel` and `forwardLabel` customize accessible names.
20
- - `size` accepts the `IconButton` size scale.
21
- - `variant` is `ghost` or `outline`.
22
- - Native `ButtonGroup` props apply to the group.
20
+ - `size` accepts the `IconButton` size scale and defaults to `sm`.
21
+ - `variant` is `ghost` or `outline` and defaults to `ghost`.
22
+ - Remaining `ButtonGroup` props apply to the root except `children` and
23
+ `orientation`; the component owns two buttons in a horizontal group.
23
24
 
24
25
  ## Keyboard and accessibility
25
26
 
@@ -0,0 +1,52 @@
1
+ # Navigation Menu
2
+
3
+ `NavigationMenu` presents a compact row of destinations, with optional
4
+ disclosure panels for grouped links. Use it for navigation, not for commands
5
+ or form actions.
6
+
7
+ ```tsx
8
+ import {
9
+ NavigationMenu,
10
+ NavigationMenuContent,
11
+ NavigationMenuItem,
12
+ NavigationMenuLink,
13
+ NavigationMenuList,
14
+ NavigationMenuTrigger,
15
+ } from '@moldable-ai/ui'
16
+
17
+ ;<NavigationMenu viewport={false}>
18
+ <NavigationMenuList>
19
+ <NavigationMenuItem>
20
+ <NavigationMenuTrigger>Projects</NavigationMenuTrigger>
21
+ <NavigationMenuContent>
22
+ <NavigationMenuLink href="/projects/recent" active>
23
+ Recent
24
+ </NavigationMenuLink>
25
+ <NavigationMenuLink href="/projects/shared">Shared</NavigationMenuLink>
26
+ </NavigationMenuContent>
27
+ </NavigationMenuItem>
28
+ </NavigationMenuList>
29
+ </NavigationMenu>
30
+ ```
31
+
32
+ ## API
33
+
34
+ - `NavigationMenu` exposes the Radix root props plus `viewport`. The shared
35
+ viewport is rendered by default; set `viewport={false}` for a panel attached
36
+ to each item.
37
+ - `NavigationMenuList`, `NavigationMenuItem`, `NavigationMenuTrigger`,
38
+ `NavigationMenuContent`, `NavigationMenuLink`, and
39
+ `NavigationMenuIndicator` expose their corresponding Radix props.
40
+ - `NavigationMenuLink` accepts `active` for the current destination.
41
+ - `NavigationMenuViewport` and `navigationMenuTriggerStyle` are exported for
42
+ custom composition.
43
+
44
+ ## Quality and accessibility
45
+
46
+ - Use real destinations and preserve link semantics with `href`.
47
+ - Give every trigger and icon-only link an accessible name.
48
+ - Mark only the current destination active.
49
+ - Keep labels short and disclosure panels easy to scan.
50
+ - Preserve the keyboard and focus behavior supplied by Radix; do not make
51
+ essential destinations available only on pointer hover.
52
+ - Use `Menubar`, `DropdownMenu`, or ordinary buttons for actions.
@@ -17,3 +17,11 @@ Use in inspectors and settings where the numeric value matters. Use `Slider`
17
17
  when relative adjustment matters more than exact entry. Always supply a visible
18
18
  label or `aria-label`. Treat `null` as empty input and validate domain-specific
19
19
  constraints outside the component.
20
+
21
+ `value`/`onValueChange` provide controlled state; `defaultValue` provides
22
+ uncontrolled initial state. `variant` is `default` or `filled`, and `size` is
23
+ `sm`, `default`, or `lg`. Both default to `default`. Steppers are shown by
24
+ default; set `steppers={false}` to hide them. They use a finite numeric `step`
25
+ (falling back to `1`), start an empty value from zero, and clamp their result
26
+ to finite `min`/`max` values. Typed input still relies on native numeric
27
+ validity and caller validation.
@@ -0,0 +1,51 @@
1
+ # Pagination
2
+
3
+ `Pagination` links between pages in a bounded result set.
4
+
5
+ ```tsx
6
+ import {
7
+ Pagination,
8
+ PaginationContent,
9
+ PaginationItem,
10
+ PaginationLink,
11
+ PaginationNext,
12
+ PaginationPrevious,
13
+ } from '@moldable-ai/ui'
14
+
15
+ ;<Pagination>
16
+ <PaginationContent>
17
+ <PaginationItem>
18
+ <PaginationPrevious href="?page=1" />
19
+ </PaginationItem>
20
+ <PaginationItem>
21
+ <PaginationLink href="?page=2" isActive>
22
+ 2
23
+ </PaginationLink>
24
+ </PaginationItem>
25
+ <PaginationItem>
26
+ <PaginationNext href="?page=3" />
27
+ </PaginationItem>
28
+ </PaginationContent>
29
+ </Pagination>
30
+ ```
31
+
32
+ ## API
33
+
34
+ - `Pagination` renders a centered `nav` named “pagination”.
35
+ - `PaginationContent` and `PaginationItem` render the list structure.
36
+ - `PaginationLink` renders an anchor. `isActive` applies the outlined state
37
+ and `aria-current="page"`; `size` uses the shared button size scale.
38
+ - `PaginationPrevious` and `PaginationNext` provide responsive text and
39
+ accessible labels.
40
+ - `PaginationEllipsis` is a decorative gap marker, not a link.
41
+
42
+ ## Quality and accessibility
43
+
44
+ - Supply a real `href` for every available page and keep the current location
45
+ marked with `isActive`.
46
+ - Keep page order stable and show enough neighboring pages to retain context.
47
+ - At a range boundary, omit the unavailable link or implement
48
+ `aria-disabled`, remove it from Tab order, and prevent navigation; anchors
49
+ do not support the native `disabled` attribute.
50
+ - Do not use pagination for infinite loading or a small list that fits in one
51
+ view.
@@ -9,3 +9,9 @@ bounded content groups inside a pane.
9
9
 
10
10
  The group and its containing shell need a definite height. Long content belongs
11
11
  in `PanelContent`, and chat-safe padding belongs on that scrolling region.
12
+
13
+ `PanelGroup` defaults to horizontal and accepts `orientation="vertical"`.
14
+ `Panel` supports `surface="default" | "muted" | "sidebar" | "transparent"`
15
+ and `border="none" | "all" | "inline" | "block"`. Surface defaults to
16
+ `default`; border defaults to `none`. The structural components otherwise
17
+ accept native props for their rendered `section`, `header`, `div`, or `footer`.
@@ -21,11 +21,14 @@ Use `Popover` for a small, interactive surface anchored to a control. Use
21
21
 
22
22
  Sizes are `content`, `sm`, `default`, and `lg`. Content is capped to the
23
23
  collision-aware available width and height and scrolls when necessary.
24
- `material="regular"` is the standard temporary overlay. Use `clear` only above
25
- media or a richly colored canvas where the stronger sampled effect has a clear
26
- layering purpose.
24
+ Content defaults to `size="default"`, `align="center"`, `sideOffset={4}`, and
25
+ `material="regular"`. The `material` prop accepts every `MaterialVariant`:
26
+ the five named thicknesses plus the `regular` and `clear` compatibility
27
+ aliases. Use a thin surface only where the content behind it keeps text and
28
+ controls legible.
27
29
 
28
- `PopoverClose`, `PopoverPortal`, and `PopoverArrow` are available when the
29
- composition needs them. Provide an accessible name on an icon-only close
30
- action. Keep required instructions and critical actions discoverable without
31
- hover.
30
+ `PopoverContent` is already portalled. `PopoverAnchor`, `PopoverClose`, and
31
+ `PopoverArrow` are available when the composition needs them;
32
+ `PopoverPortal` is exported for custom primitive composition. Provide an
33
+ accessible name on an icon-only trigger or close action. Keep required
34
+ instructions and critical actions discoverable without hover.