@moldable-ai/ui 0.2.20 → 0.2.22

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 (196) hide show
  1. package/dist/catalog/catalog-coverage.d.ts +15 -0
  2. package/dist/catalog/catalog-coverage.d.ts.map +1 -0
  3. package/dist/catalog/catalog-coverage.js +44 -0
  4. package/dist/catalog/catalog-manifest.d.ts.map +1 -1
  5. package/dist/catalog/catalog-manifest.js +35 -23
  6. package/dist/catalog/catalog-types.d.ts +17 -0
  7. package/dist/catalog/catalog-types.d.ts.map +1 -1
  8. package/dist/catalog/catalog-types.js +7 -0
  9. package/dist/catalog/component-catalog.d.ts.map +1 -1
  10. package/dist/catalog/component-catalog.js +16 -6
  11. package/dist/catalog/core-catalog-exclusions.d.ts +8 -0
  12. package/dist/catalog/core-catalog-exclusions.d.ts.map +1 -0
  13. package/dist/catalog/core-catalog-exclusions.js +155 -0
  14. package/dist/catalog/core-catalog.d.ts +0 -4
  15. package/dist/catalog/core-catalog.d.ts.map +1 -1
  16. package/dist/catalog/core-catalog.js +12 -98
  17. package/dist/catalog/core-collection-stories.d.ts +3 -0
  18. package/dist/catalog/core-collection-stories.d.ts.map +1 -0
  19. package/dist/catalog/core-collection-stories.js +42 -0
  20. package/dist/catalog/core-control-stories.d.ts +3 -0
  21. package/dist/catalog/core-control-stories.d.ts.map +1 -0
  22. package/dist/catalog/core-control-stories.js +97 -0
  23. package/dist/catalog/core-feedback-stories.d.ts +3 -0
  24. package/dist/catalog/core-feedback-stories.d.ts.map +1 -0
  25. package/dist/catalog/core-feedback-stories.js +50 -0
  26. package/dist/catalog/core-foundation-stories.d.ts +3 -0
  27. package/dist/catalog/core-foundation-stories.d.ts.map +1 -0
  28. package/dist/catalog/core-foundation-stories.js +53 -0
  29. package/dist/catalog/core-navigation-stories.d.ts +3 -0
  30. package/dist/catalog/core-navigation-stories.d.ts.map +1 -0
  31. package/dist/catalog/core-navigation-stories.js +25 -0
  32. package/dist/catalog/core-overlay-stories.d.ts +3 -0
  33. package/dist/catalog/core-overlay-stories.d.ts.map +1 -0
  34. package/dist/catalog/core-overlay-stories.js +20 -0
  35. package/dist/catalog/index.d.ts +3 -1
  36. package/dist/catalog/index.d.ts.map +1 -1
  37. package/dist/catalog/index.js +3 -1
  38. package/dist/components/app-error-boundary.d.ts.map +1 -1
  39. package/dist/components/app-error-boundary.js +3 -2
  40. package/dist/components/chat/chat-panel.d.ts +0 -1
  41. package/dist/components/chat/chat-panel.d.ts.map +1 -1
  42. package/dist/components/chat/chat-panel.js +7 -11
  43. package/dist/components/ui/alert-dialog.d.ts +10 -4
  44. package/dist/components/ui/alert-dialog.d.ts.map +1 -1
  45. package/dist/components/ui/alert-dialog.js +14 -6
  46. package/dist/components/ui/alert.d.ts +19 -3
  47. package/dist/components/ui/alert.d.ts.map +1 -1
  48. package/dist/components/ui/alert.js +25 -8
  49. package/dist/components/ui/app-frame.d.ts +9 -3
  50. package/dist/components/ui/app-frame.d.ts.map +1 -1
  51. package/dist/components/ui/app-frame.js +45 -6
  52. package/dist/components/ui/button.d.ts +6 -4
  53. package/dist/components/ui/button.d.ts.map +1 -1
  54. package/dist/components/ui/button.js +40 -8
  55. package/dist/components/ui/collapsible.d.ts +9 -1
  56. package/dist/components/ui/collapsible.d.ts.map +1 -1
  57. package/dist/components/ui/collapsible.js +13 -2
  58. package/dist/components/ui/collection-navigation.d.ts +8 -0
  59. package/dist/components/ui/collection-navigation.d.ts.map +1 -0
  60. package/dist/components/ui/collection-navigation.js +19 -0
  61. package/dist/components/ui/command.d.ts +5 -1
  62. package/dist/components/ui/command.d.ts.map +1 -1
  63. package/dist/components/ui/command.js +13 -1
  64. package/dist/components/ui/confirm-dialog.d.ts +19 -0
  65. package/dist/components/ui/confirm-dialog.d.ts.map +1 -0
  66. package/dist/components/ui/confirm-dialog.js +45 -0
  67. package/dist/components/ui/date-picker.d.ts +24 -0
  68. package/dist/components/ui/date-picker.d.ts.map +1 -0
  69. package/dist/components/ui/date-picker.js +92 -0
  70. package/dist/components/ui/dialog-primary-action.d.ts +4 -0
  71. package/dist/components/ui/dialog-primary-action.d.ts.map +1 -0
  72. package/dist/components/ui/dialog-primary-action.js +37 -0
  73. package/dist/components/ui/dialog.d.ts +8 -2
  74. package/dist/components/ui/dialog.d.ts.map +1 -1
  75. package/dist/components/ui/dialog.js +15 -5
  76. package/dist/components/ui/empty.d.ts +7 -2
  77. package/dist/components/ui/empty.d.ts.map +1 -1
  78. package/dist/components/ui/empty.js +20 -7
  79. package/dist/components/ui/error-boundary-view.d.ts +12 -0
  80. package/dist/components/ui/error-boundary-view.d.ts.map +1 -0
  81. package/dist/components/ui/error-boundary-view.js +8 -0
  82. package/dist/components/ui/grid.d.ts.map +1 -1
  83. package/dist/components/ui/grid.js +70 -46
  84. package/dist/components/ui/icon-button.d.ts.map +1 -1
  85. package/dist/components/ui/icon-button.js +1 -2
  86. package/dist/components/ui/index.d.ts +5 -0
  87. package/dist/components/ui/index.d.ts.map +1 -1
  88. package/dist/components/ui/index.js +5 -0
  89. package/dist/components/ui/input-group.d.ts +1 -1
  90. package/dist/components/ui/list.d.ts +5 -1
  91. package/dist/components/ui/list.d.ts.map +1 -1
  92. package/dist/components/ui/list.js +27 -16
  93. package/dist/components/ui/material.d.ts +34 -0
  94. package/dist/components/ui/material.d.ts.map +1 -0
  95. package/dist/components/ui/material.js +34 -0
  96. package/dist/components/ui/message-scroller-controller.d.ts +162 -0
  97. package/dist/components/ui/message-scroller-controller.d.ts.map +1 -0
  98. package/dist/components/ui/message-scroller-controller.js +888 -0
  99. package/dist/components/ui/message-scroller-policy.d.ts +74 -0
  100. package/dist/components/ui/message-scroller-policy.d.ts.map +1 -0
  101. package/dist/components/ui/message-scroller-policy.js +129 -0
  102. package/dist/components/ui/message-scroller-primitive.d.ts +55 -0
  103. package/dist/components/ui/message-scroller-primitive.d.ts.map +1 -0
  104. package/dist/components/ui/message-scroller-primitive.js +226 -0
  105. package/dist/components/ui/message-scroller.d.ts +2 -2
  106. package/dist/components/ui/message-scroller.d.ts.map +1 -1
  107. package/dist/components/ui/message-scroller.js +3 -15
  108. package/dist/components/ui/navigation-button-group.d.ts +16 -0
  109. package/dist/components/ui/navigation-button-group.d.ts.map +1 -0
  110. package/dist/components/ui/navigation-button-group.js +9 -0
  111. package/dist/components/ui/popover.d.ts +9 -2
  112. package/dist/components/ui/popover.d.ts.map +1 -1
  113. package/dist/components/ui/popover.js +13 -3
  114. package/dist/components/ui/scroll-area.d.ts +8 -2
  115. package/dist/components/ui/scroll-area.d.ts.map +1 -1
  116. package/dist/components/ui/scroll-area.js +6 -3
  117. package/dist/components/ui/search-field.d.ts.map +1 -1
  118. package/dist/components/ui/segmented-control.d.ts.map +1 -1
  119. package/dist/components/ui/segmented-control.js +6 -5
  120. package/dist/components/ui/sheet.d.ts +4 -2
  121. package/dist/components/ui/sheet.d.ts.map +1 -1
  122. package/dist/components/ui/sheet.js +12 -9
  123. package/dist/components/ui/sonner.d.ts +1 -1
  124. package/dist/components/ui/sonner.d.ts.map +1 -1
  125. package/dist/components/ui/spinner.d.ts.map +1 -1
  126. package/dist/components/ui/spinner.js +1 -1
  127. package/dist/components/ui/table.d.ts.map +1 -1
  128. package/dist/components/ui/table.js +7 -2
  129. package/dist/components/ui/toggle.d.ts +12 -4
  130. package/dist/components/ui/toggle.d.ts.map +1 -1
  131. package/dist/components/ui/toggle.js +13 -2
  132. package/dist/components/ui/toolbar.d.ts +9 -2
  133. package/dist/components/ui/toolbar.d.ts.map +1 -1
  134. package/dist/components/ui/toolbar.js +23 -4
  135. package/dist/components/ui/tooltip.d.ts +8 -3
  136. package/dist/components/ui/tooltip.d.ts.map +1 -1
  137. package/dist/components/ui/tooltip.js +11 -6
  138. package/dist/index.d.ts +6 -1
  139. package/dist/index.d.ts.map +1 -1
  140. package/dist/index.js +7 -1
  141. package/dist/lib/frame-lifecycle.d.ts +23 -0
  142. package/dist/lib/frame-lifecycle.d.ts.map +1 -1
  143. package/dist/lib/frame-lifecycle.js +30 -3
  144. package/dist/lib/host-date-time-picker.d.ts +36 -0
  145. package/dist/lib/host-date-time-picker.d.ts.map +1 -0
  146. package/dist/lib/host-date-time-picker.js +162 -0
  147. package/dist/lib/host-file-dialog.d.ts +66 -0
  148. package/dist/lib/host-file-dialog.d.ts.map +1 -0
  149. package/dist/lib/host-file-dialog.js +221 -0
  150. package/dist/lib/host-native-menu.d.ts +75 -0
  151. package/dist/lib/host-native-menu.d.ts.map +1 -0
  152. package/dist/lib/host-native-menu.js +187 -0
  153. package/dist/lib/host-services.d.ts +13 -0
  154. package/dist/lib/host-services.d.ts.map +1 -0
  155. package/dist/lib/host-services.js +161 -0
  156. package/dist/lib/host-share.d.ts +39 -0
  157. package/dist/lib/host-share.d.ts.map +1 -0
  158. package/dist/lib/host-share.js +161 -0
  159. package/dist/lib/theme.d.ts.map +1 -1
  160. package/dist/lib/theme.js +2 -1
  161. package/docs/README.md +4 -1
  162. package/docs/adaptive-materials.md +79 -0
  163. package/docs/app-anatomy.md +3 -1
  164. package/docs/component-catalog.md +26 -3
  165. package/docs/design-tokens.md +17 -4
  166. package/docs/foundations.md +5 -3
  167. package/docs/standalone-app-windows.md +8 -0
  168. package/docs/visual-regression.md +78 -0
  169. package/package.json +12 -4
  170. package/src/components/ui/alert-dialog.md +58 -0
  171. package/src/components/ui/alert.md +28 -6
  172. package/src/components/ui/app-frame.md +5 -0
  173. package/src/components/ui/button.md +10 -3
  174. package/src/components/ui/collapsible.md +52 -0
  175. package/src/components/ui/combobox.md +0 -1
  176. package/src/components/ui/command.md +54 -0
  177. package/src/components/ui/date-field.md +1 -1
  178. package/src/components/ui/date-picker.md +55 -0
  179. package/src/components/ui/dialog.md +59 -0
  180. package/src/components/ui/empty.md +8 -3
  181. package/src/components/ui/error-boundary-view.md +98 -0
  182. package/src/components/ui/grid.md +8 -4
  183. package/src/components/ui/icon-button.md +2 -2
  184. package/src/components/ui/list.md +35 -2
  185. package/src/components/ui/material.md +51 -0
  186. package/src/components/ui/navigation-button-group.md +46 -0
  187. package/src/components/ui/popover.md +31 -0
  188. package/src/components/ui/scroll-area.md +11 -7
  189. package/src/components/ui/search-field.md +1 -1
  190. package/src/components/ui/segmented-control.md +2 -5
  191. package/src/components/ui/sheet.md +30 -0
  192. package/src/components/ui/table.md +4 -2
  193. package/src/components/ui/toggle-button.md +48 -0
  194. package/src/components/ui/toolbar.md +5 -0
  195. package/src/components/ui/tooltip.md +31 -0
  196. package/src/styles/index.css +213 -0
@@ -0,0 +1,55 @@
1
+ # DatePicker
2
+
3
+ `DatePicker` is a portable, date-only calendar popover. Its value is an ISO
4
+ calendar date (`YYYY-MM-DD`), so app storage does not introduce timezone
5
+ conversion.
6
+
7
+ ```tsx
8
+ <DatePicker
9
+ aria-label="Due date"
10
+ value={dueDate}
11
+ onValueChange={setDueDate}
12
+ min="2026-01-01"
13
+ clearable
14
+ />
15
+ ```
16
+
17
+ ## DatePicker or DateField
18
+
19
+ - Use `DatePicker` when a predictable renderer-owned calendar is important.
20
+ - Use `DateField` when compact keyboard entry and the browser's date control
21
+ are preferable.
22
+ - Use the standalone `Calendar` for ranges, multiple dates, or an always
23
+ visible month.
24
+
25
+ ## API
26
+
27
+ - `value`, `defaultValue`: ISO `YYYY-MM-DD` or an empty string.
28
+ - `onValueChange`: receives the next ISO date or an empty string.
29
+ - `min`, `max`: inclusive ISO date limits.
30
+ - `placeholder`: trigger copy when empty.
31
+ - `locale`: the `react-day-picker` locale used by the calendar and display.
32
+ - `formatValue`: custom trigger rendering for the ISO value.
33
+ - `clearable`, `clearLabel`: optional clear action inside the popover.
34
+ - `density`: `compact`, `default`, or `comfortable`.
35
+ - `open`, `defaultOpen`, `onOpenChange`: controlled or uncontrolled popover.
36
+ - Remaining button props apply to the trigger.
37
+
38
+ ## Keyboard and accessibility
39
+
40
+ Label the trigger through visible form text, `aria-labelledby`, or
41
+ `aria-label`. The trigger opens with Enter or Space. The calendar owns arrow,
42
+ Home, End, Page Up, and Page Down behavior. Selection closes the popover and
43
+ focus returns to the trigger.
44
+
45
+ ## Platform behavior
46
+
47
+ This component is portable and uses no host API. A future native date or time
48
+ control must be introduced through a shared host capability with this
49
+ renderer-owned control as its fallback.
50
+
51
+ ## Don't
52
+
53
+ - Do not put timestamps or UTC instants in this date-only value.
54
+ - Do not use this component for time, date-time, or date-range selection.
55
+ - Do not manually convert the selected value through `Date.toISOString()`.
@@ -0,0 +1,59 @@
1
+ # Dialog
2
+
3
+ Use `Dialog` for focused work that temporarily blocks the current surface. Use
4
+ `AlertDialog` or `ConfirmDialog` for consequential confirmation, `Popover` for
5
+ small anchored controls, and `Sheet` for a secondary region that should retain
6
+ spatial context.
7
+
8
+ ## Anatomy
9
+
10
+ ```tsx
11
+ <Dialog>
12
+ <DialogTrigger asChild>
13
+ <Button>Edit details</Button>
14
+ </DialogTrigger>
15
+ <DialogContent size="lg">
16
+ <DialogHeader>
17
+ <DialogTitle>Edit details</DialogTitle>
18
+ <DialogDescription>Update the selected record.</DialogDescription>
19
+ </DialogHeader>
20
+ <DialogBody>{/* the only scrolling region */}</DialogBody>
21
+ <DialogFooter>
22
+ <span className="text-muted-foreground mr-auto inline-flex items-center gap-1 text-xs">
23
+ Save <Kbd>⌘</Kbd>
24
+ <Kbd>Enter</Kbd>
25
+ </span>
26
+ <DialogAction primary loading={isSaving} onClick={saveChanges}>
27
+ Save changes
28
+ </DialogAction>
29
+ </DialogFooter>
30
+ </DialogContent>
31
+ </Dialog>
32
+ ```
33
+
34
+ `DialogContent` supports `sm`, `default`, `lg`, and `full`. It limits itself to
35
+ the host-provided window and chat-safe insets. `DialogBody` owns vertical
36
+ scrolling; do not put overflow on both the content and body.
37
+
38
+ Keep dialogs opaque and visually stable. Translucent material belongs on small
39
+ temporary control overlays where seeing the underlying context is useful.
40
+
41
+ Use `DialogAction` for footer commands that need the standard async and keyboard
42
+ contracts. Its `loading` prop forwards to `Button`, preventing duplicate
43
+ activation while retaining the label width and accessible name. Keep the dialog
44
+ controlled, show inline field errors, and close only after success. Disable the
45
+ whole form only when edits during submission would be unsafe. Use
46
+ `ConfirmDialog` when confirmation is the entire workflow.
47
+
48
+ Set `primary` on at most one `DialogAction` to opt into the default-action
49
+ shortcut. Command+Enter activates it on macOS; Control+Enter does so on other
50
+ platforms. The shortcut works from anywhere in the dialog unless a focused
51
+ control consumes the key event with `preventDefault()`, or the primary action is
52
+ loading or disabled. Plain Enter keeps the focused control's native behavior.
53
+ Use `Kbd` to show the shortcut when it improves discovery; render the
54
+ platform-appropriate modifier in app code.
55
+
56
+ Every dialog needs a `DialogTitle`. Add `DialogDescription` when the outcome or
57
+ scope is not obvious. Escape, outside dismissal, initial focus, and focus return
58
+ come from the Radix primitive; do not replace them with manual document event
59
+ handlers.
@@ -20,7 +20,7 @@ import {
20
20
  EmptyTitle,
21
21
  } from '@moldable-ai/ui'
22
22
 
23
- ;<Empty className="pb-[calc(var(--chat-safe-padding,0px)+2rem)]">
23
+ ;<Empty variant="pane" safeBottom>
24
24
  <EmptyHeader>
25
25
  <EmptyMedia variant="icon">
26
26
  <Database />
@@ -52,6 +52,9 @@ For a successful zero state, omit the action when there is nothing useful to do:
52
52
  ## API summary
53
53
 
54
54
  - `Empty`: centered flex container for the complete state.
55
+ - `Empty variant`: `compact`, `pane`, or `full`; defaults to `pane`.
56
+ - `Empty safeBottom`: adds host-provided chat clearance. Do not enable it when
57
+ an owning `AppFrameContent` or parent scroll region already applies clearance.
55
58
  - `EmptyHeader`: groups media, title, and description.
56
59
  - `EmptyMedia`: `variant="default"` or `variant="icon"`.
57
60
  - `EmptyTitle`: primary label.
@@ -65,7 +68,7 @@ For a successful zero state, omit the action when there is nothing useful to do:
65
68
  - Show one primary setup action when the state is blocked.
66
69
  - Distinguish first-run, filtered-zero, permission, and successful-zero states.
67
70
  - Keep the app’s normal shell or pane structure visible.
68
- - Add chat-safe padding when the empty state occupies a pane that reaches the bottom.
71
+ - Use `safeBottom` when the empty state itself owns a pane that reaches the bottom.
69
72
 
70
73
  ## Don't
71
74
 
@@ -79,4 +82,6 @@ For a successful zero state, omit the action when there is nothing useful to do:
79
82
 
80
83
  An empty state should feel like part of a local instrument, not a landing page. Keep it visually small even when centered. If the desktop chat can resolve the state, provide specific app context or an explicit “Ask Moldable about this” affordance that prefills desktop chat; do not add another chat box.
81
84
 
82
- For scrollable panes, use enough bottom padding to remain visible above chat, commonly `pb-[calc(var(--chat-safe-padding,0px)+1rem)]` or more when a fixed dock is present.
85
+ For scrollable panes, prefer `safeBottom` on the owning `ScrollArea`. Apply it
86
+ to `Empty` only when the empty state is the owner, so nested states do not add
87
+ the same safe area twice.
@@ -0,0 +1,98 @@
1
+ # ErrorBoundaryView
2
+
3
+ `ErrorBoundaryView` presents a compact recovery state without owning error
4
+ catching, reporting, or host policy.
5
+
6
+ ## When to use
7
+
8
+ Use it as the renderer for a React error boundary, route-level failure, or
9
+ host-provided pane error. Supply `actions` when the host owns reporting,
10
+ navigation, or chat recovery.
11
+
12
+ ## When not to use
13
+
14
+ Do not use it for inline field validation, routine empty states, or transient
15
+ request failures that can stay beside the initiating control. It does not catch
16
+ errors itself; use `AppErrorBoundary` when the package should own catching and
17
+ Moldable reporting.
18
+
19
+ ## Anatomy
20
+
21
+ The view contains one compact surface with a title, optional message, optional
22
+ error-details disclosure, and one action row. It does not add illustrations or
23
+ nested cards.
24
+
25
+ ## Usage
26
+
27
+ ```tsx
28
+ import { ErrorBoundaryView } from '@moldable-ai/ui'
29
+
30
+ function PaneError() {
31
+ return (
32
+ <ErrorBoundaryView
33
+ title="Could not load this pane"
34
+ message="Retry the request or report the captured error."
35
+ details={error.stack}
36
+ onRetry={retry}
37
+ actions={
38
+ <Button type="button" variant="secondary" onClick={reportError}>
39
+ Report error
40
+ </Button>
41
+ }
42
+ />
43
+ )
44
+ }
45
+ ```
46
+
47
+ ## Variants and sizes
48
+
49
+ The renderer has one responsive layout. It uses the available height in a pane
50
+ and can fill a window when its parent gives it full height. The content remains
51
+ bounded to a readable width.
52
+
53
+ ## API
54
+
55
+ - `title`: heading content. Defaults to `"Something went wrong"`.
56
+ - `message`: optional supporting copy.
57
+ - `details`: optional string shown in a collapsed disclosure with a bounded,
58
+ scrollable monospace region.
59
+ - `onRetry`: when supplied, renders a `Retry` button and calls the handler once
60
+ per activation.
61
+ - `actions`: optional host-provided action content rendered after Retry.
62
+ - `className`: optional classes merged onto the outer renderer.
63
+
64
+ ## States and interaction
65
+
66
+ Omit `details` when there is no safe diagnostic text to show. Omit both
67
+ `onRetry` and `actions` for a read-only failure. Keep retry state in the caller
68
+ and pass `loading` to a custom action when retrying is asynchronous.
69
+
70
+ ## Keyboard and accessibility
71
+
72
+ The details use native `details` and `summary` semantics. The title is the
73
+ surface heading, the Retry control keeps native button behavior, and diagnostic
74
+ text remains selectable.
75
+
76
+ ## Platform behavior
77
+
78
+ The component has no host calls or platform branches. `AppErrorBoundary`
79
+ supplies Moldable-specific reload and chat-prefill actions.
80
+
81
+ ## Do
82
+
83
+ - Keep the title calm and literal.
84
+ - Give the message a concrete recovery path.
85
+ - Redact secrets before passing diagnostic details.
86
+ - Use semantic theme tokens in custom actions.
87
+
88
+ ## Don't
89
+
90
+ - Do not add decorative error art or nested cards.
91
+ - Do not put binary data or unbounded logs in `details`.
92
+ - Do not make this presentational component report or reload implicitly.
93
+
94
+ ## Testing
95
+
96
+ Contract tests should verify copy, disclosure behavior, Retry activation, and
97
+ host action rendering. Error-catching and reporting tests belong to the owning
98
+ boundary.
@@ -18,7 +18,7 @@ import {
18
18
  GridItemTitle,
19
19
  } from '@moldable-ai/ui'
20
20
 
21
- <Grid
21
+ ;<Grid
22
22
  aria-label="Artifacts"
23
23
  columns={3}
24
24
  selectionMode="single"
@@ -56,14 +56,18 @@ Prefer changing this value at deliberate window breakpoints. A smaller window sh
56
56
 
57
57
  ## Interaction contract
58
58
 
59
- - Arrow Left/Right moves one tile.
60
- - Arrow Up/Down moves by `columns`.
61
- - Home and End move to the first and last available tile.
59
+ - Arrow Left/Right moves within the current row and stops at its edge.
60
+ - Arrow Up/Down moves within the current column and stops at its edge.
61
+ - Home and End move to the first and last available tile in the current row.
62
+ - Control/Command + Home and End move to the first and last available tile in
63
+ the entire grid.
62
64
  - Space changes selection.
63
65
  - Enter changes selection and invokes `onAction`.
64
66
  - A single pointer click changes selection. A double click invokes `onAction`.
65
67
  - Disabled tiles are skipped.
66
68
  - Buttons and links inside a tile preserve their own pointer behavior.
69
+ - When the grid first mounts, the first selected, enabled tile receives the
70
+ roving tab stop. If nothing is selected, the first enabled tile receives it.
67
71
 
68
72
  ## API summary
69
73
 
@@ -7,10 +7,10 @@
7
7
  ## Usage
8
8
 
9
9
  ```tsx
10
- import { IconButton } from '@moldable-ai/ui'
11
10
  import { RefreshCw } from 'lucide-react'
11
+ import { IconButton } from '@moldable-ai/ui'
12
12
 
13
- <IconButton
13
+ ;<IconButton
14
14
  label="Refresh events"
15
15
  tooltip
16
16
  size="sm"
@@ -17,7 +17,7 @@ import {
17
17
  ListItemTitle,
18
18
  } from '@moldable-ai/ui'
19
19
 
20
- <List
20
+ ;<List
21
21
  aria-label="Documents"
22
22
  selectionMode="single"
23
23
  defaultSelectedValues={['brief']}
@@ -38,6 +38,28 @@ import {
38
38
  </List>
39
39
  ```
40
40
 
41
+ ## Sections
42
+
43
+ Group related options with `ListSection` and give every section an accessible
44
+ name. `ListSectionHeader` automatically labels its containing section:
45
+
46
+ ```tsx
47
+ <List aria-label="Documents" selectionMode="single">
48
+ <ListSection>
49
+ <ListSectionHeader>Recent</ListSectionHeader>
50
+ <ListItem value="brief">Project brief</ListItem>
51
+ <ListItem value="notes">Research notes</ListItem>
52
+ </ListSection>
53
+ <ListSection aria-label="Archived documents">
54
+ <ListItem value="archive">Archive</ListItem>
55
+ </ListSection>
56
+ </List>
57
+ ```
58
+
59
+ Use either `ListSectionHeader`, `aria-label`, or an explicit
60
+ `aria-labelledby` value for each section. Keyboard movement follows the
61
+ rendered DOM order across section boundaries.
62
+
41
63
  For controlled selection, pass an iterable to `selectedValues` and replace it when `onSelectionChange` fires:
42
64
 
43
65
  ```tsx
@@ -63,8 +85,9 @@ const [selected, setSelected] = useState<ReadonlySet<string>>(
63
85
  - Enter changes selection and invokes `onAction`.
64
86
  - A single pointer click changes selection. A double click invokes `onAction`.
65
87
  - Disabled items are skipped during keyboard navigation and cannot be selected or activated.
66
- - Interactive descendants such as buttons and links keep their own click behavior. They do not select or activate the containing item.
88
+ - Interactive descendants such as buttons and links keep their own pointer behavior. They do not select or activate the containing item.
67
89
  - Focus uses a roving `tabIndex`, so the collection contributes one stop to the tab order.
90
+ - When the list first mounts, the first selected, enabled item receives the roving tab stop. If nothing is selected, the first enabled item receives it.
68
91
 
69
92
  ## API summary
70
93
 
@@ -86,9 +109,16 @@ const [selected, setSelected] = useState<ReadonlySet<string>>(
86
109
 
87
110
  `ListItemContent`, `ListItemTitle`, `ListItemDescription`, and `ListItemActions` provide compact row layout without changing semantics.
88
111
 
112
+ ### `ListSection`
113
+
114
+ - Groups related items with `role="group"`.
115
+ - Use `ListSectionHeader` for an automatically connected visible label.
116
+ - Alternatively, provide `aria-label` or `aria-labelledby`.
117
+
89
118
  ## Do
90
119
 
91
120
  - Give every collection a visible label or `aria-label`.
121
+ - Give every section an accessible label.
92
122
  - Keep values stable when filtering or reordering.
93
123
  - Put the primary row identity in `ListItemTitle` and secondary metadata in `ListItemDescription`.
94
124
  - Use selection to represent durable collection state and action to open or execute.
@@ -98,6 +128,9 @@ const [selected, setSelected] = useState<ReadonlySet<string>>(
98
128
 
99
129
  - Do not put a `List` inside another list item.
100
130
  - Do not use array indexes as values when rows can reorder.
131
+ - Do not put buttons, links, or other interactive controls inside selectable
132
+ listbox items. Put independent actions outside the listbox or use a pattern
133
+ whose child-interaction model supports them.
101
134
  - Do not rely on double click as the only way to perform an important action.
102
135
  - Do not add a second manual arrow-key handler to the same collection.
103
136
  - Do not visually mark focus and selection as if they were the same state.
@@ -0,0 +1,51 @@
1
+ # Material
2
+
3
+ Adaptive material for navigation and control chrome.
4
+
5
+ ```tsx
6
+ <Material asChild variant="regular" elevation="none">
7
+ <Toolbar>{/* context and actions */}</Toolbar>
8
+ </Material>
9
+
10
+ <MaterialGroup variant="regular" shape="capsule" spacing="compact">
11
+ <IconButton label="Previous" variant="ghost">
12
+ <ChevronLeft />
13
+ </IconButton>
14
+ <IconButton label="Next" variant="ghost">
15
+ <ChevronRight />
16
+ </IconButton>
17
+ </MaterialGroup>
18
+ ```
19
+
20
+ ## Variants
21
+
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.
25
+
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
+
30
+ ## Grouping
31
+
32
+ `MaterialGroup` creates one sampled layer for a related cluster. Put ordinary
33
+ ghost or transparent controls inside it. Do not wrap each child in another
34
+ `Material`; nested sampling creates muddy, inconsistent results.
35
+
36
+ Use separate `Material` instances only when controls are spatially detached and
37
+ do not read as one group.
38
+
39
+ ## Rules
40
+
41
+ - Use material for toolbars, compact floating controls, menus, and other
42
+ temporary control layers.
43
+ - Keep documents, calendars, tables, forms, cards, sheets, and primary content
44
+ canvases opaque.
45
+ - Prefer `regular`. A `clear` surface needs stable contrast across every part
46
+ of the media behind it.
47
+ - Use `interactive` only when the material surface itself is the control.
48
+ - Do not animate blur or saturation. Shared motion tokens provide restrained
49
+ press feedback and remove it when reduced motion is requested.
50
+ - CSS material samples content inside the renderer. Native behind-window
51
+ material remains owned by the host.
@@ -0,0 +1,46 @@
1
+ # NavigationButtonGroup
2
+
3
+ `NavigationButtonGroup` presents paired back and forward actions with one
4
+ consistent compact treatment.
5
+
6
+ ```tsx
7
+ <NavigationButtonGroup
8
+ canGoBack={historyIndex > 0}
9
+ canGoForward={historyIndex < history.length - 1}
10
+ onBack={goBack}
11
+ onForward={goForward}
12
+ />
13
+ ```
14
+
15
+ ## API
16
+
17
+ - `canGoBack` and `canGoForward` disable unavailable directions.
18
+ - `onBack` and `onForward` perform caller-owned navigation.
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.
23
+
24
+ ## Keyboard and accessibility
25
+
26
+ The root has `role="group"` and the default accessible name “Navigation”.
27
+ Each icon button has a stable accessible name and participates in normal Tab
28
+ order. The component does not install global keyboard shortcuts.
29
+
30
+ ## Platform behavior
31
+
32
+ This component is portable. It does not own browser history, Moldable
33
+ navigation state, or native window history. Connect it to the navigation owner
34
+ for the current surface.
35
+
36
+ ## Do
37
+
38
+ - Put it in a leading toolbar group.
39
+ - Disable unavailable directions.
40
+ - Keep back and forward behavior scoped to the visible pane or app.
41
+
42
+ ## Don't
43
+
44
+ - Do not render a fake forward action when no forward history exists.
45
+ - Do not use it for undo and redo.
46
+ - Do not add a second tooltip around its buttons.
@@ -0,0 +1,31 @@
1
+ # Popover
2
+
3
+ Use `Popover` for a small, interactive surface anchored to a control. Use
4
+ `Tooltip` for terse non-interactive help, `HoverCard` for a preview, and
5
+ `Dialog` for a blocking task.
6
+
7
+ ```tsx
8
+ <Popover>
9
+ <PopoverTrigger asChild>
10
+ <Button variant="outline">View options</Button>
11
+ </PopoverTrigger>
12
+ <PopoverContent size="sm" material="regular">
13
+ <PopoverHeader>
14
+ <PopoverTitle>View options</PopoverTitle>
15
+ <PopoverDescription>Choose what appears in the list.</PopoverDescription>
16
+ </PopoverHeader>
17
+ {/* interactive controls */}
18
+ </PopoverContent>
19
+ </Popover>
20
+ ```
21
+
22
+ Sizes are `content`, `sm`, `default`, and `lg`. Content is capped to the
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.
27
+
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.
@@ -11,10 +11,8 @@ For simple content, a native `overflow-y-auto` region is often sufficient. The i
11
11
  ```tsx
12
12
  import { ScrollArea } from '@moldable-ai/ui'
13
13
 
14
- ;<ScrollArea className="h-full min-h-0">
15
- <div className="px-3 pb-[calc(var(--chat-safe-padding,0px)+1rem)] pt-3">
16
- {/* Long list */}
17
- </div>
14
+ ;<ScrollArea className="h-full min-h-0" safeBottom>
15
+ <div className="px-3 pt-3">{/* Long list */}</div>
18
16
  </ScrollArea>
19
17
  ```
20
18
 
@@ -28,12 +26,17 @@ For a grouped list:
28
26
  </ScrollArea>
29
27
  ```
30
28
 
31
- The current `ScrollArea` composition installs a vertical scrollbar automatically.
29
+ The current `ScrollArea` composition installs a vertical scrollbar by default.
30
+ Set `scrollbars` to `horizontal`, `both`, or `none` for another explicit
31
+ configuration.
32
32
 
33
33
  ## API summary
34
34
 
35
35
  - `ScrollArea`: Radix Scroll Area root with a full-size viewport, vertical `ScrollBar`, and corner.
36
36
  - Accepts Radix root props, forwarded `ref`, and `className`.
37
+ - `scrollbars`: `vertical`, `horizontal`, `both`, or `none`.
38
+ - `viewportClassName`: styles the actual scrolling viewport.
39
+ - `safeBottom`: adds the shared chat-safe padding to the viewport exactly once.
37
40
  - `ScrollBar`: exported scrollbar primitive with `orientation="vertical" | "horizontal"`; defaults to vertical.
38
41
  - The root’s direct children are rendered inside the viewport.
39
42
  - The viewport supplies focus-visible outline and ring treatment.
@@ -43,7 +46,7 @@ The current `ScrollArea` composition installs a vertical scrollbar automatically
43
46
  - Give the scroll area a definite height through its own class and full-height ancestors.
44
47
  - Put padding on an inner content wrapper when it should scroll with the content.
45
48
  - Add `min-h-0` to flex or grid ancestors.
46
- - Add bottom chat-safe padding inside every scroll region that reaches the webview bottom.
49
+ - Enable `safeBottom` on every owning scroll region that reaches the webview bottom.
47
50
  - Keep sticky headers inside the same scrolling context as their content.
48
51
  - Test keyboard, wheel, trackpad, and scrollbar-thumb interaction.
49
52
 
@@ -54,7 +57,8 @@ The current `ScrollArea` composition installs a vertical scrollbar automatically
54
57
  - Do not put fixed controls inside the moving content when they must remain reachable.
55
58
  - Do not hide focus outlines.
56
59
  - Do not assume padding on a sibling pane protects this viewport from desktop chat.
57
- - Do not assume adding `ScrollBar` as a child of `ScrollArea` creates a second root scrollbar; children belong to the viewport in the current composition.
60
+ - Do not add `ScrollBar` as a child of `ScrollArea`; children belong to its
61
+ viewport. Use the `scrollbars` prop.
58
62
 
59
63
  ## Moldable considerations
60
64
 
@@ -9,7 +9,7 @@
9
9
  ```tsx
10
10
  import { SearchField } from '@moldable-ai/ui'
11
11
 
12
- <SearchField
12
+ ;<SearchField
13
13
  aria-label="Search projects"
14
14
  value={query}
15
15
  onValueChange={setQuery}
@@ -9,12 +9,9 @@ Use it for choices such as list/grid, week/month, or preview/edit. Use `Tabs` wh
9
9
  ## Usage
10
10
 
11
11
  ```tsx
12
- import {
13
- SegmentedControl,
14
- SegmentedControlItem,
15
- } from '@moldable-ai/ui'
12
+ import { SegmentedControl, SegmentedControlItem } from '@moldable-ai/ui'
16
13
 
17
- <SegmentedControl
14
+ ;<SegmentedControl
18
15
  aria-label="Calendar view"
19
16
  value={view}
20
17
  onValueChange={setView}
@@ -0,0 +1,30 @@
1
+ # Sheet
2
+
3
+ `Sheet` presents a secondary region from an edge while preserving the current
4
+ workspace. It is appropriate for narrow-window navigation, filters, or an
5
+ inspector that cannot remain beside the canvas.
6
+
7
+ ```tsx
8
+ <Sheet>
9
+ <SheetTrigger asChild>
10
+ <Button variant="outline">Filters</Button>
11
+ </SheetTrigger>
12
+ <SheetContent side="right" size="default">
13
+ <SheetHeader>
14
+ <SheetTitle>Filters</SheetTitle>
15
+ <SheetDescription>Limit the visible results.</SheetDescription>
16
+ </SheetHeader>
17
+ <SheetBody>{/* scrolling controls */}</SheetBody>
18
+ <SheetFooter>{/* persistent actions */}</SheetFooter>
19
+ </SheetContent>
20
+ </Sheet>
21
+ ```
22
+
23
+ Side sheets support `sm`, `default`, and `lg`. All sides respect host window
24
+ insets; their bottom edge also clears the embedded chat safe area. `SheetBody`
25
+ is the only flexing scroll owner, leaving its header, close action, and footer
26
+ reachable.
27
+
28
+ Use `Dialog` when the task is centered and blocking, `Drawer` for touch-first
29
+ presentation, and `Inspector` when contextual properties should remain visible
30
+ beside the canvas. Do not use a sheet as permanent desktop navigation.
@@ -29,7 +29,9 @@ Use `compact` for inspector-like datasets, `default` for general work, and
29
29
  `comfortable` where scanning matters more than density.
30
30
 
31
31
  When a row is selectable, selection is controlled by the caller and can be
32
- toggled with pointer, Enter, or Space. Put independent actions inside buttons
33
- and call `event.stopPropagation()` if activating them must not select the row.
32
+ toggled with pointer, Enter, or Space. Independent buttons, links, form
33
+ controls, and editable descendants keep their own pointer and keyboard
34
+ behavior without toggling the containing row. Call `event.preventDefault()` in
35
+ a row event handler only when an app needs to cancel the built-in row response.
34
36
  For rich grid navigation, column sorting, or cell editing, use a dedicated data
35
37
  grid rather than layering those interactions onto a basic table.
@@ -0,0 +1,48 @@
1
+ # ToggleButton
2
+
3
+ `ToggleButton` is a single pressed/unpressed action. It is a semantic facade
4
+ over `Toggle`, so both share one Radix interaction model and one style source.
5
+
6
+ ```tsx
7
+ <ToggleButton pressed={showGrid} onPressedChange={setShowGrid}>
8
+ Show grid
9
+ </ToggleButton>
10
+ ```
11
+
12
+ For an icon-only toggle, opt in explicitly and provide its accessible name:
13
+
14
+ ```tsx
15
+ <ToggleButton
16
+ iconOnly
17
+ aria-label="Pin inspector"
18
+ pressed={pinned}
19
+ onPressedChange={setPinned}
20
+ >
21
+ <PinIcon />
22
+ </ToggleButton>
23
+ ```
24
+
25
+ ## Variants and sizes
26
+
27
+ - `variant`: `default`, `outline`, or `filled`.
28
+ - `size`: `xs`, `sm`, `default`, or `lg`.
29
+ - `iconOnly` makes the control square and requires `aria-label`.
30
+
31
+ ## Choosing the right control
32
+
33
+ - Use `Button` for a momentary action.
34
+ - Use `ToggleButton` for one independent visible mode or tool.
35
+ - Use `SegmentedControl` for exactly one choice from a small set.
36
+ - Use `Switch` for a persistent setting that takes effect immediately.
37
+
38
+ ## Keyboard and accessibility
39
+
40
+ Radix supplies button and `aria-pressed` semantics. Space and Enter toggle the
41
+ state. A visible text label or `aria-label` must identify the action; state
42
+ must not be communicated through color alone.
43
+
44
+ ## Don't
45
+
46
+ - Do not use a toggle for a one-time command.
47
+ - Do not group unrelated toggles into a segmented control.
48
+ - Do not put a checkbox inside a toggle button.