@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.
- package/dist/catalog/catalog-coverage.d.ts +15 -0
- package/dist/catalog/catalog-coverage.d.ts.map +1 -0
- package/dist/catalog/catalog-coverage.js +44 -0
- package/dist/catalog/catalog-manifest.d.ts.map +1 -1
- package/dist/catalog/catalog-manifest.js +35 -23
- package/dist/catalog/catalog-types.d.ts +17 -0
- package/dist/catalog/catalog-types.d.ts.map +1 -1
- package/dist/catalog/catalog-types.js +7 -0
- package/dist/catalog/component-catalog.d.ts.map +1 -1
- package/dist/catalog/component-catalog.js +16 -6
- package/dist/catalog/core-catalog-exclusions.d.ts +8 -0
- package/dist/catalog/core-catalog-exclusions.d.ts.map +1 -0
- package/dist/catalog/core-catalog-exclusions.js +155 -0
- package/dist/catalog/core-catalog.d.ts +0 -4
- package/dist/catalog/core-catalog.d.ts.map +1 -1
- package/dist/catalog/core-catalog.js +12 -98
- package/dist/catalog/core-collection-stories.d.ts +3 -0
- package/dist/catalog/core-collection-stories.d.ts.map +1 -0
- package/dist/catalog/core-collection-stories.js +42 -0
- package/dist/catalog/core-control-stories.d.ts +3 -0
- package/dist/catalog/core-control-stories.d.ts.map +1 -0
- package/dist/catalog/core-control-stories.js +97 -0
- package/dist/catalog/core-feedback-stories.d.ts +3 -0
- package/dist/catalog/core-feedback-stories.d.ts.map +1 -0
- package/dist/catalog/core-feedback-stories.js +50 -0
- package/dist/catalog/core-foundation-stories.d.ts +3 -0
- package/dist/catalog/core-foundation-stories.d.ts.map +1 -0
- package/dist/catalog/core-foundation-stories.js +53 -0
- package/dist/catalog/core-navigation-stories.d.ts +3 -0
- package/dist/catalog/core-navigation-stories.d.ts.map +1 -0
- package/dist/catalog/core-navigation-stories.js +25 -0
- package/dist/catalog/core-overlay-stories.d.ts +3 -0
- package/dist/catalog/core-overlay-stories.d.ts.map +1 -0
- package/dist/catalog/core-overlay-stories.js +20 -0
- package/dist/catalog/index.d.ts +3 -1
- package/dist/catalog/index.d.ts.map +1 -1
- package/dist/catalog/index.js +3 -1
- package/dist/components/app-error-boundary.d.ts.map +1 -1
- package/dist/components/app-error-boundary.js +3 -2
- package/dist/components/chat/chat-panel.d.ts +0 -1
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +7 -11
- package/dist/components/ui/alert-dialog.d.ts +10 -4
- package/dist/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog.js +14 -6
- package/dist/components/ui/alert.d.ts +19 -3
- package/dist/components/ui/alert.d.ts.map +1 -1
- package/dist/components/ui/alert.js +25 -8
- package/dist/components/ui/app-frame.d.ts +9 -3
- package/dist/components/ui/app-frame.d.ts.map +1 -1
- package/dist/components/ui/app-frame.js +45 -6
- package/dist/components/ui/button.d.ts +6 -4
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +40 -8
- package/dist/components/ui/collapsible.d.ts +9 -1
- package/dist/components/ui/collapsible.d.ts.map +1 -1
- package/dist/components/ui/collapsible.js +13 -2
- package/dist/components/ui/collection-navigation.d.ts +8 -0
- package/dist/components/ui/collection-navigation.d.ts.map +1 -0
- package/dist/components/ui/collection-navigation.js +19 -0
- package/dist/components/ui/command.d.ts +5 -1
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js +13 -1
- package/dist/components/ui/confirm-dialog.d.ts +19 -0
- package/dist/components/ui/confirm-dialog.d.ts.map +1 -0
- package/dist/components/ui/confirm-dialog.js +45 -0
- package/dist/components/ui/date-picker.d.ts +24 -0
- package/dist/components/ui/date-picker.d.ts.map +1 -0
- package/dist/components/ui/date-picker.js +92 -0
- package/dist/components/ui/dialog-primary-action.d.ts +4 -0
- package/dist/components/ui/dialog-primary-action.d.ts.map +1 -0
- package/dist/components/ui/dialog-primary-action.js +37 -0
- package/dist/components/ui/dialog.d.ts +8 -2
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog.js +15 -5
- package/dist/components/ui/empty.d.ts +7 -2
- package/dist/components/ui/empty.d.ts.map +1 -1
- package/dist/components/ui/empty.js +20 -7
- package/dist/components/ui/error-boundary-view.d.ts +12 -0
- package/dist/components/ui/error-boundary-view.d.ts.map +1 -0
- package/dist/components/ui/error-boundary-view.js +8 -0
- package/dist/components/ui/grid.d.ts.map +1 -1
- package/dist/components/ui/grid.js +70 -46
- package/dist/components/ui/icon-button.d.ts.map +1 -1
- package/dist/components/ui/icon-button.js +1 -2
- package/dist/components/ui/index.d.ts +5 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +5 -0
- package/dist/components/ui/input-group.d.ts +1 -1
- package/dist/components/ui/list.d.ts +5 -1
- package/dist/components/ui/list.d.ts.map +1 -1
- package/dist/components/ui/list.js +27 -16
- package/dist/components/ui/material.d.ts +34 -0
- package/dist/components/ui/material.d.ts.map +1 -0
- package/dist/components/ui/material.js +34 -0
- package/dist/components/ui/message-scroller-controller.d.ts +162 -0
- package/dist/components/ui/message-scroller-controller.d.ts.map +1 -0
- package/dist/components/ui/message-scroller-controller.js +888 -0
- package/dist/components/ui/message-scroller-policy.d.ts +74 -0
- package/dist/components/ui/message-scroller-policy.d.ts.map +1 -0
- package/dist/components/ui/message-scroller-policy.js +129 -0
- package/dist/components/ui/message-scroller-primitive.d.ts +55 -0
- package/dist/components/ui/message-scroller-primitive.d.ts.map +1 -0
- package/dist/components/ui/message-scroller-primitive.js +226 -0
- package/dist/components/ui/message-scroller.d.ts +2 -2
- package/dist/components/ui/message-scroller.d.ts.map +1 -1
- package/dist/components/ui/message-scroller.js +3 -15
- package/dist/components/ui/navigation-button-group.d.ts +16 -0
- package/dist/components/ui/navigation-button-group.d.ts.map +1 -0
- package/dist/components/ui/navigation-button-group.js +9 -0
- package/dist/components/ui/popover.d.ts +9 -2
- package/dist/components/ui/popover.d.ts.map +1 -1
- package/dist/components/ui/popover.js +13 -3
- package/dist/components/ui/scroll-area.d.ts +8 -2
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.js +6 -3
- package/dist/components/ui/search-field.d.ts.map +1 -1
- package/dist/components/ui/segmented-control.d.ts.map +1 -1
- package/dist/components/ui/segmented-control.js +6 -5
- package/dist/components/ui/sheet.d.ts +4 -2
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet.js +12 -9
- package/dist/components/ui/sonner.d.ts +1 -1
- package/dist/components/ui/sonner.d.ts.map +1 -1
- package/dist/components/ui/spinner.d.ts.map +1 -1
- package/dist/components/ui/spinner.js +1 -1
- package/dist/components/ui/table.d.ts.map +1 -1
- package/dist/components/ui/table.js +7 -2
- package/dist/components/ui/toggle.d.ts +12 -4
- package/dist/components/ui/toggle.d.ts.map +1 -1
- package/dist/components/ui/toggle.js +13 -2
- package/dist/components/ui/toolbar.d.ts +9 -2
- package/dist/components/ui/toolbar.d.ts.map +1 -1
- package/dist/components/ui/toolbar.js +23 -4
- package/dist/components/ui/tooltip.d.ts +8 -3
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/components/ui/tooltip.js +11 -6
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/lib/frame-lifecycle.d.ts +23 -0
- package/dist/lib/frame-lifecycle.d.ts.map +1 -1
- package/dist/lib/frame-lifecycle.js +30 -3
- package/dist/lib/host-date-time-picker.d.ts +36 -0
- package/dist/lib/host-date-time-picker.d.ts.map +1 -0
- package/dist/lib/host-date-time-picker.js +162 -0
- package/dist/lib/host-file-dialog.d.ts +66 -0
- package/dist/lib/host-file-dialog.d.ts.map +1 -0
- package/dist/lib/host-file-dialog.js +221 -0
- package/dist/lib/host-native-menu.d.ts +75 -0
- package/dist/lib/host-native-menu.d.ts.map +1 -0
- package/dist/lib/host-native-menu.js +187 -0
- package/dist/lib/host-services.d.ts +13 -0
- package/dist/lib/host-services.d.ts.map +1 -0
- package/dist/lib/host-services.js +161 -0
- package/dist/lib/host-share.d.ts +39 -0
- package/dist/lib/host-share.d.ts.map +1 -0
- package/dist/lib/host-share.js +161 -0
- package/dist/lib/theme.d.ts.map +1 -1
- package/dist/lib/theme.js +2 -1
- package/docs/README.md +4 -1
- package/docs/adaptive-materials.md +79 -0
- package/docs/app-anatomy.md +3 -1
- package/docs/component-catalog.md +26 -3
- package/docs/design-tokens.md +17 -4
- package/docs/foundations.md +5 -3
- package/docs/standalone-app-windows.md +8 -0
- package/docs/visual-regression.md +78 -0
- package/package.json +12 -4
- package/src/components/ui/alert-dialog.md +58 -0
- package/src/components/ui/alert.md +28 -6
- package/src/components/ui/app-frame.md +5 -0
- package/src/components/ui/button.md +10 -3
- package/src/components/ui/collapsible.md +52 -0
- package/src/components/ui/combobox.md +0 -1
- package/src/components/ui/command.md +54 -0
- package/src/components/ui/date-field.md +1 -1
- package/src/components/ui/date-picker.md +55 -0
- package/src/components/ui/dialog.md +59 -0
- package/src/components/ui/empty.md +8 -3
- package/src/components/ui/error-boundary-view.md +98 -0
- package/src/components/ui/grid.md +8 -4
- package/src/components/ui/icon-button.md +2 -2
- package/src/components/ui/list.md +35 -2
- package/src/components/ui/material.md +51 -0
- package/src/components/ui/navigation-button-group.md +46 -0
- package/src/components/ui/popover.md +31 -0
- package/src/components/ui/scroll-area.md +11 -7
- package/src/components/ui/search-field.md +1 -1
- package/src/components/ui/segmented-control.md +2 -5
- package/src/components/ui/sheet.md +30 -0
- package/src/components/ui/table.md +4 -2
- package/src/components/ui/toggle-button.md +48 -0
- package/src/components/ui/toolbar.md +5 -0
- package/src/components/ui/tooltip.md +31 -0
- package/src/styles/index.css +213 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Adaptive materials
|
|
2
|
+
|
|
3
|
+
Moldable uses restrained translucent material to separate controls and
|
|
4
|
+
navigation from the content they operate on. Material frames content; it does
|
|
5
|
+
not replace content surfaces.
|
|
6
|
+
|
|
7
|
+
## Roles
|
|
8
|
+
|
|
9
|
+
Use `regular` material for normal toolbar and window chrome, grouped controls,
|
|
10
|
+
menus, and temporary control overlays. It provides enough frosting for text
|
|
11
|
+
and icons to remain stable as content moves behind it.
|
|
12
|
+
|
|
13
|
+
Use `clear` material only for compact controls floating over photos, maps,
|
|
14
|
+
video, or another richly colored canvas. Check the control against the
|
|
15
|
+
lightest, darkest, and busiest content it can cover. If contrast is not stable,
|
|
16
|
+
use `regular`.
|
|
17
|
+
|
|
18
|
+
Documents, calendars, tables, forms, cards, sheets, inspectors, and primary
|
|
19
|
+
working canvases remain opaque. Do not stack translucent content cards to
|
|
20
|
+
create hierarchy.
|
|
21
|
+
|
|
22
|
+
## Components
|
|
23
|
+
|
|
24
|
+
`Material` applies one adaptive surface:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
<Material asChild variant="clear" shape="circle" interactive>
|
|
28
|
+
<IconButton label="Center map" variant="ghost">
|
|
29
|
+
<Locate />
|
|
30
|
+
</IconButton>
|
|
31
|
+
</Material>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`MaterialGroup` applies one sampled layer to a related cluster:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
<MaterialGroup variant="regular" shape="capsule" spacing="compact">
|
|
38
|
+
<IconButton label="Previous" variant="ghost">
|
|
39
|
+
<ChevronLeft />
|
|
40
|
+
</IconButton>
|
|
41
|
+
<IconButton label="Next" variant="ghost">
|
|
42
|
+
<ChevronRight />
|
|
43
|
+
</IconButton>
|
|
44
|
+
</MaterialGroup>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Children of a `MaterialGroup` should be ordinary transparent or ghost
|
|
48
|
+
controls. Do not apply another `Material` to every child. A single grouped
|
|
49
|
+
sampler produces clearer hierarchy and avoids accumulated blur.
|
|
50
|
+
|
|
51
|
+
`Toolbar`, `AppFrameTitlebar`, and `AppFrameStatusbar` expose a `material`
|
|
52
|
+
property. Their normal chrome uses `regular`; pass `material="none"` for an
|
|
53
|
+
opaque or transparent composition that supplies its own surface.
|
|
54
|
+
|
|
55
|
+
## Fallback behavior
|
|
56
|
+
|
|
57
|
+
Every material starts from an opaque fallback. Translucent sampling is enabled
|
|
58
|
+
only when the renderer supports backdrop filtering.
|
|
59
|
+
|
|
60
|
+
- Reduced transparency uses opaque fills and removes blur and saturation.
|
|
61
|
+
- Increased contrast makes fills more opaque and strengthens borders.
|
|
62
|
+
- Forced colors use system surfaces and borders without blur or shadows.
|
|
63
|
+
- Reduced motion removes material press transforms.
|
|
64
|
+
|
|
65
|
+
The manual `data-transparency="reduced"`, `data-contrast="increased"`, and
|
|
66
|
+
`data-motion="reduced"` attributes mirror system preferences for deterministic
|
|
67
|
+
catalog and app testing.
|
|
68
|
+
|
|
69
|
+
Do not animate blur, saturation, or backdrop filters. They are expensive,
|
|
70
|
+
visually noisy, and difficult to make accessible. Limit the number of visible
|
|
71
|
+
sampled layers and avoid nesting them.
|
|
72
|
+
|
|
73
|
+
## Host boundary
|
|
74
|
+
|
|
75
|
+
CSS material samples content inside the app renderer. It cannot sample the
|
|
76
|
+
desktop behind a native window. Native behind-window material, window
|
|
77
|
+
transparency, active-window appearance, and energy policy remain host-owned
|
|
78
|
+
capabilities. Apps should use the same semantic variants regardless of which
|
|
79
|
+
adapter renders them.
|
package/docs/app-anatomy.md
CHANGED
|
@@ -9,7 +9,9 @@ canvas independent of the desktop shell.
|
|
|
9
9
|
```tsx
|
|
10
10
|
<AppFrame>
|
|
11
11
|
<AppFrameTitlebar>
|
|
12
|
-
<AppFrameToolbar>
|
|
12
|
+
<AppFrameToolbar>
|
|
13
|
+
{/* title, view controls, primary actions */}
|
|
14
|
+
</AppFrameToolbar>
|
|
13
15
|
</AppFrameTitlebar>
|
|
14
16
|
<AppFrameContent scrollable={false}>
|
|
15
17
|
{/* app-owned canvas and panes */}
|
|
@@ -20,6 +20,7 @@ const manifest = createCatalogManifest({
|
|
|
20
20
|
{
|
|
21
21
|
id: 'primary-action',
|
|
22
22
|
title: 'Primary action',
|
|
23
|
+
components: ['button'],
|
|
23
24
|
interactions: ['rest', 'focus-visible', 'disabled', 'loading'],
|
|
24
25
|
render: ({ interaction }) => (
|
|
25
26
|
<Button
|
|
@@ -45,6 +46,8 @@ Every public interactive component should cover:
|
|
|
45
46
|
- light and dark themes;
|
|
46
47
|
- regular and compact density;
|
|
47
48
|
- wide and narrow viewports;
|
|
49
|
+
- standard accessibility behavior plus reduced transparency, increased
|
|
50
|
+
contrast, and reduced motion where material or motion is part of the recipe;
|
|
48
51
|
- rest, focus-visible, disabled, invalid, selected, loading, and empty states
|
|
49
52
|
where those states apply;
|
|
50
53
|
- keyboard behavior in unit tests.
|
|
@@ -54,6 +57,26 @@ lets a visual-regression runner capture one addressable case at a time. Keep
|
|
|
54
57
|
catalog stories deterministic: fixed content, fixed dates, no network calls,
|
|
55
58
|
and no animation unless motion itself is under review.
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
Accessibility environments are opt-in per story through
|
|
61
|
+
`accessibilityModes`. Standard cases preserve the existing case ID; a
|
|
62
|
+
nonstandard mode is appended as the final segment. The catalog maps those
|
|
63
|
+
modes to deterministic `data-transparency`, `data-contrast`, and `data-motion`
|
|
64
|
+
attributes.
|
|
65
|
+
|
|
66
|
+
## Public component accountability
|
|
67
|
+
|
|
68
|
+
Every story declares the public visual component modules it exercises through
|
|
69
|
+
`components`. The core catalog test reads the barrel exports from
|
|
70
|
+
`components/ui/index.ts` and requires every module to be either:
|
|
71
|
+
|
|
72
|
+
- named by at least one deterministic story; or
|
|
73
|
+
- listed in `coreCatalogExclusions` with a concise reason.
|
|
74
|
+
|
|
75
|
+
Exclusions are visible debt, not permanent exemptions. Remove an exclusion in
|
|
76
|
+
the same change that adds representative stories. Unknown component ids, stale
|
|
77
|
+
exclusions, duplicate exclusions, and empty reasons fail the catalog test.
|
|
78
|
+
|
|
79
|
+
The core catalog covers window materials, semantic controls, collections,
|
|
80
|
+
navigation, representative popover material, and feedback recipes. Complex
|
|
81
|
+
portals and geometry-dependent components remain explicitly deferred until an
|
|
82
|
+
isolated browser fixture can make their states deterministic.
|
package/docs/design-tokens.md
CHANGED
|
@@ -119,15 +119,28 @@ related objects visually consistent. `rounded-window` is host-informed and
|
|
|
119
119
|
normally only useful in previews; the native host owns the actual window
|
|
120
120
|
shape.
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
Adaptive material has two semantic variants. Each includes fill, fallback,
|
|
123
|
+
border, highlight, blur, and saturation tokens:
|
|
123
124
|
|
|
124
|
-
- `--material-
|
|
125
|
-
- `--material-
|
|
126
|
-
|
|
125
|
+
- `--material-regular-*` supports normal toolbar and control chrome.
|
|
126
|
+
- `--material-clear-*` supports compact controls over media or a richly
|
|
127
|
+
colored canvas.
|
|
128
|
+
- `shadow-panel`, `shadow-overlay`, and `shadow-window` provide elevation
|
|
129
|
+
independently from translucency.
|
|
130
|
+
|
|
131
|
+
Use `Material` and `MaterialGroup` instead of assembling those tokens in app
|
|
132
|
+
markup. `MaterialGroup` owns one sampled layer for a related control cluster;
|
|
133
|
+
its children must not add nested material.
|
|
127
134
|
|
|
128
135
|
Translucency must have an opaque fallback. The token system removes blur and
|
|
129
136
|
uses opaque toolbar and overlay surfaces when the operating system requests
|
|
130
137
|
reduced transparency or an ancestor has `data-transparency="reduced"`.
|
|
138
|
+
Unsupported renderers stay on the same opaque fallback. Increased contrast
|
|
139
|
+
makes fills more opaque and strengthens their borders.
|
|
140
|
+
|
|
141
|
+
Material is a control and navigation affordance. Primary canvases, documents,
|
|
142
|
+
tables, calendars, forms, cards, sheets, and inspectors remain opaque. See
|
|
143
|
+
[Adaptive materials](./adaptive-materials.md) for composition rules.
|
|
131
144
|
|
|
132
145
|
## Motion
|
|
133
146
|
|
package/docs/foundations.md
CHANGED
|
@@ -37,9 +37,11 @@ Status color communicates status only; it is not decoration.
|
|
|
37
37
|
Use semantic tokens only. Do not introduce raw gray, zinc, white, or black
|
|
38
38
|
classes in app UI.
|
|
39
39
|
|
|
40
|
-
Use material
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
Use adaptive material only for navigation and control layers. Regular material
|
|
41
|
+
supports toolbars and grouped controls; clear material is reserved for compact
|
|
42
|
+
controls over media. Content canvases and content-bearing surfaces stay
|
|
43
|
+
opaque. Group related controls under one sampler and never nest material
|
|
44
|
+
effects.
|
|
43
45
|
|
|
44
46
|
## Motion
|
|
45
47
|
|
|
@@ -41,6 +41,14 @@ the document root. Composite content insets then resolve automatically. The
|
|
|
41
41
|
transport used to deliver those values belongs to the host integration layer,
|
|
42
42
|
not individual apps.
|
|
43
43
|
|
|
44
|
+
Dedicated windows use the host titlebar by default. An app with a unified
|
|
45
|
+
toolbar can declare `{ "window": { "titlebar": "app" } }` in `moldable.json`.
|
|
46
|
+
In that mode the host removes its strip and advertises the titlebar height and
|
|
47
|
+
leading window-control inset. `AppFrameTitlebar` consumes those values, sends
|
|
48
|
+
`startWindowDrag` on a non-interactive pointerdown, and sends
|
|
49
|
+
`titlebarDoubleClick` on a non-interactive double-click. The latter toggles
|
|
50
|
+
maximize as a cross-platform approximation of the macOS titlebar zoom action.
|
|
51
|
+
|
|
44
52
|
Window appearance roles are `--window-chrome-background`,
|
|
45
53
|
`--window-chrome-foreground`, `--window-chrome-muted-foreground`,
|
|
46
54
|
`--window-border`, and `--material-shadow-window`. The host may update the
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Visual and accessibility regression
|
|
2
|
+
|
|
3
|
+
The phase 1 runner exercises every authored story in the core component
|
|
4
|
+
catalog. It does not start the desktop shell or any Moldable-managed app.
|
|
5
|
+
|
|
6
|
+
## What runs
|
|
7
|
+
|
|
8
|
+
`visual-host/` is a small Vite entry that renders one story at a time:
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
?story=controls/button&theme=dark&width=360
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Unique bare story ids are also accepted. Optional
|
|
15
|
+
`transparency=reduced` and `contrast=increased` parameters set the same
|
|
16
|
+
manual accessibility attributes used by the catalog. The host bundles Inter
|
|
17
|
+
and Roboto Mono, waits for `document.fonts.ready`, and disables animations,
|
|
18
|
+
transitions, scrolling, and the caret under `data-motion="reduced"`.
|
|
19
|
+
|
|
20
|
+
The Playwright runner builds and serves that static host, blocks non-host
|
|
21
|
+
network requests, freezes time and randomness, and tests Chromium at:
|
|
22
|
+
|
|
23
|
+
- light and dark themes;
|
|
24
|
+
- 360 × 720 and 960 × 720 viewports;
|
|
25
|
+
- rest plus the browser-driven states declared by an exemplar story.
|
|
26
|
+
|
|
27
|
+
A story opts into browser-driven states with `liveStates` and marks exactly
|
|
28
|
+
one element:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<Button data-state-target="hover focus pressed">Continue</Button>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Playwright performs real hover, focus, and held-pointer interactions on that
|
|
35
|
+
element. Button, segmented control, and toggle stories are the phase 1
|
|
36
|
+
exemplars.
|
|
37
|
+
|
|
38
|
+
Every captured case also receives an axe WCAG 2 A/AA and WCAG 2.1 A/AA scan.
|
|
39
|
+
Serious and critical violations fail. Per-story suppressions belong in
|
|
40
|
+
`visual-tests/visual-matrix.ts` and must include a reason. Global
|
|
41
|
+
suppressions are intentionally unsupported.
|
|
42
|
+
|
|
43
|
+
## Local commands
|
|
44
|
+
|
|
45
|
+
Install the pinned Chromium build once:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pnpm -C packages/ui exec playwright install chromium
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Run against committed baselines:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm -C packages/ui test:visual
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Create or intentionally update baselines after reviewing the UI change:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pnpm -C packages/ui test:visual:update
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Baseline PNGs live in `visual-tests/baselines/`. Failures emit actual,
|
|
64
|
+
expected, diff, and trace artifacts in the gitignored `.visual-results/`
|
|
65
|
+
directory.
|
|
66
|
+
|
|
67
|
+
## Determinism and skips
|
|
68
|
+
|
|
69
|
+
Stories must use fixed inputs and no live data, network, wall-clock timers, or
|
|
70
|
+
unseeded randomness. Add an unavoidable phase 1 skip to
|
|
71
|
+
`deterministicStorySkips` in `visual-tests/visual-matrix.ts` with a specific
|
|
72
|
+
reason. Skips are reported in the runner summary; silent conditional skips
|
|
73
|
+
are not allowed.
|
|
74
|
+
|
|
75
|
+
## CI
|
|
76
|
+
|
|
77
|
+
The `ui-regression` CI job installs Chromium and its Linux dependencies,
|
|
78
|
+
builds only `@moldable-ai/ui`, then runs the combined visual and axe suite.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moldable-ai/ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.22",
|
|
4
4
|
"description": "Shared UI components for Moldable applications",
|
|
5
5
|
"author": "Desiderata LLC",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -42,11 +42,15 @@
|
|
|
42
42
|
],
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "tsc",
|
|
45
|
+
"build:visual-host": "vite build --config vite.visual.config.ts",
|
|
45
46
|
"dev": "tsc --watch",
|
|
46
47
|
"lint": "eslint . --ext .ts,.tsx --max-warnings 0",
|
|
47
48
|
"format": "prettier --write . --ignore-path ../../.prettierignore",
|
|
48
|
-
"check-types": "tsc --noEmit",
|
|
49
|
-
"test": "vitest run"
|
|
49
|
+
"check-types": "tsc --noEmit && tsc --noEmit -p tsconfig.visual.json",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:visual": "pnpm build:visual-host && playwright test --config playwright.visual.config.ts",
|
|
52
|
+
"test:visual:update": "pnpm build:visual-host && playwright test --config playwright.visual.config.ts --update-snapshots",
|
|
53
|
+
"visual:serve": "vite preview --config vite.visual.config.ts --host 127.0.0.1 --port 4179 --strictPort"
|
|
50
54
|
},
|
|
51
55
|
"dependencies": {
|
|
52
56
|
"@base-ui/react": "^1.1.0",
|
|
@@ -80,7 +84,6 @@
|
|
|
80
84
|
"@radix-ui/react-toggle": "^1.1.10",
|
|
81
85
|
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
82
86
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
83
|
-
"@shadcn/react": "^0.2.1",
|
|
84
87
|
"@tailwindcss/typography": "^0.5.16",
|
|
85
88
|
"class-variance-authority": "^0.7.1",
|
|
86
89
|
"clsx": "^2.1.1",
|
|
@@ -106,14 +109,19 @@
|
|
|
106
109
|
"zod": "^4.3.5"
|
|
107
110
|
},
|
|
108
111
|
"devDependencies": {
|
|
112
|
+
"@axe-core/playwright": "^4.12.1",
|
|
113
|
+
"@fontsource/inter": "^5.3.0",
|
|
114
|
+
"@fontsource/roboto-mono": "^5.3.0",
|
|
109
115
|
"@moldable-ai/eslint-config": "workspace:*",
|
|
110
116
|
"@moldable-ai/prettier-config": "workspace:*",
|
|
111
117
|
"@moldable-ai/typescript-config": "workspace:*",
|
|
118
|
+
"@playwright/test": "^1.62.0",
|
|
112
119
|
"@types/node": "^24.0.3",
|
|
113
120
|
"@types/react": "^19.1.6",
|
|
114
121
|
"@types/react-dom": "^19.1.6",
|
|
115
122
|
"jsdom": "^26.1.0",
|
|
116
123
|
"typescript": "^5.8.3",
|
|
124
|
+
"vite": "^6.4.3",
|
|
117
125
|
"vitest": "^4.1.8"
|
|
118
126
|
},
|
|
119
127
|
"peerDependencies": {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Alert Dialog and Confirm Dialog
|
|
2
|
+
|
|
3
|
+
Use `AlertDialog` when the user must acknowledge a consequential choice. Use the
|
|
4
|
+
composed anatomy for custom content and `ConfirmDialog` for the common
|
|
5
|
+
promise-aware confirmation path.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<ConfirmDialog
|
|
9
|
+
trigger={<Button variant="destructive">Delete recording</Button>}
|
|
10
|
+
title="Delete recording?"
|
|
11
|
+
description="This removes the local audio and transcript."
|
|
12
|
+
confirmLabel="Delete"
|
|
13
|
+
pendingLabel="Deleting…"
|
|
14
|
+
variant="destructive"
|
|
15
|
+
onConfirm={() => deleteRecording()}
|
|
16
|
+
/>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`ConfirmDialog` prevents duplicate submission, disables cancellation while the
|
|
20
|
+
operation is pending, closes only after success, and keeps failures in the
|
|
21
|
+
dialog. Pass `getErrorMessage` when a safe, actionable domain error can be
|
|
22
|
+
shown. The default error deliberately avoids exposing internal details.
|
|
23
|
+
|
|
24
|
+
`AlertDialogContent` supports `sm`, `default`, and `lg` and stays within the
|
|
25
|
+
host safe area. Use `AlertDialogBody` when explanation or affected-item content
|
|
26
|
+
can grow. Keep the consequence visible while the action is pending.
|
|
27
|
+
|
|
28
|
+
For composed alert dialogs, put `loading` on `AlertDialogAction` while its
|
|
29
|
+
request is in flight. This forwards the standard `Button` busy state, blocks
|
|
30
|
+
duplicate activation, and preserves the action label width and accessible name.
|
|
31
|
+
Use `pending` with `pendingLabel` when maintaining the confirmation-specific
|
|
32
|
+
status-label pattern; `ConfirmDialog` manages that state automatically. Disable
|
|
33
|
+
the surrounding form only when its other controls must not change during the
|
|
34
|
+
request.
|
|
35
|
+
|
|
36
|
+
Mark one confirming action with `primary` to enable Command+Enter on macOS or
|
|
37
|
+
Control+Enter on other platforms. The shortcut does not run without a marked
|
|
38
|
+
action, while that action is loading or disabled, during key composition or
|
|
39
|
+
repeat, or when a focused control consumes the event with `preventDefault()`.
|
|
40
|
+
Plain Enter remains native and Escape remains the Radix cancel behavior.
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<AlertDialogFooter>
|
|
44
|
+
<span className="text-muted-foreground mr-auto inline-flex items-center gap-1 text-xs">
|
|
45
|
+
Confirm <Kbd>⌘</Kbd>
|
|
46
|
+
<Kbd>Enter</Kbd>
|
|
47
|
+
</span>
|
|
48
|
+
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
49
|
+
<AlertDialogAction primary loading={isDeleting}>
|
|
50
|
+
Delete recording
|
|
51
|
+
</AlertDialogAction>
|
|
52
|
+
</AlertDialogFooter>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use concrete labels such as “Delete recording,” not “OK.” Put the safe,
|
|
56
|
+
reversible action first in keyboard order and the confirming action last. Do
|
|
57
|
+
not use an alert dialog for ordinary navigation, long forms, or success
|
|
58
|
+
messages.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
`Alert` presents
|
|
5
|
+
`Alert` presents a persistent inline callout that belongs to the current
|
|
6
|
+
surface. Use it for recoverable errors, permission or connection problems, and
|
|
7
|
+
information the user should notice before continuing.
|
|
6
8
|
|
|
7
9
|
Use `AlertDialog` when the user must confirm an action. Use `Empty` for the normal absence of data. Use a toast for brief feedback that does not need to remain in the layout.
|
|
8
10
|
|
|
@@ -12,23 +14,42 @@ Use `AlertDialog` when the user must confirm an action. Use `Empty` for the norm
|
|
|
12
14
|
import { CircleAlert } from 'lucide-react'
|
|
13
15
|
import { Alert, AlertDescription, AlertTitle } from '@moldable-ai/ui'
|
|
14
16
|
|
|
15
|
-
;<Alert
|
|
17
|
+
;<Alert tone="danger">
|
|
16
18
|
<CircleAlert />
|
|
17
19
|
<AlertTitle>Could not save</AlertTitle>
|
|
18
20
|
<AlertDescription>
|
|
19
21
|
The file changed on disk. Reload it, then try again.
|
|
20
22
|
</AlertDescription>
|
|
23
|
+
<AlertActions>
|
|
24
|
+
<Button size="sm">Reload</Button>
|
|
25
|
+
</AlertActions>
|
|
21
26
|
</Alert>
|
|
22
27
|
```
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
Dismissible informational callouts may use `AlertDismiss`. Dismissal state is
|
|
30
|
+
owned by the caller:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<Alert tone="info">
|
|
34
|
+
<InfoIcon />
|
|
35
|
+
<AlertTitle>Local-only data</AlertTitle>
|
|
36
|
+
<AlertDescription>This workspace has not been synced.</AlertDescription>
|
|
37
|
+
<AlertDismiss label="Dismiss local-only notice" onClick={dismiss} />
|
|
38
|
+
</Alert>
|
|
39
|
+
```
|
|
25
40
|
|
|
26
41
|
## API summary
|
|
27
42
|
|
|
28
|
-
- `
|
|
29
|
-
- `
|
|
43
|
+
- `tone`: `neutral`, `info`, `success`, `warning`, or `danger`.
|
|
44
|
+
- `density`: `compact` or `default`.
|
|
45
|
+
- Neutral through warning tones default to `role="status"`; danger defaults to
|
|
46
|
+
`role="alert"`. The native `role` prop can override that choice.
|
|
47
|
+
- `variant="default"` and `variant="destructive"` remain compatibility aliases.
|
|
30
48
|
- `AlertTitle`: the short, emphasized heading.
|
|
31
49
|
- `AlertDescription`: muted supporting content; supports nested paragraphs and controls.
|
|
50
|
+
- `AlertActions`: a compact action row.
|
|
51
|
+
- `AlertDismiss`: an icon action with a required accessible `label`; the caller
|
|
52
|
+
owns dismissal.
|
|
32
53
|
- All parts accept the native props for their rendered element and a `className`.
|
|
33
54
|
- A direct icon child on `Alert` receives the component’s grid placement and sizing.
|
|
34
55
|
|
|
@@ -44,9 +65,10 @@ An alert can include a compact retry or setup action in its description, but the
|
|
|
44
65
|
|
|
45
66
|
- Do not use an alert as decorative introductory copy.
|
|
46
67
|
- Do not show stack traces, raw exceptions, or secret values.
|
|
47
|
-
- Do not use `
|
|
68
|
+
- Do not use `danger` for neutral notices.
|
|
48
69
|
- Do not rely on the icon or color alone for meaning.
|
|
49
70
|
- Do not use `Alert` as a confirmation surface.
|
|
71
|
+
- Do not use an alert for brief success feedback that should be a toast.
|
|
50
72
|
|
|
51
73
|
## Moldable considerations
|
|
52
74
|
|
|
@@ -21,6 +21,9 @@ surface.
|
|
|
21
21
|
- Let the host render real window controls. Never draw imitation traffic lights.
|
|
22
22
|
- Make the app's primary canvas the visual focus; titlebars and status bars are
|
|
23
23
|
supporting structure.
|
|
24
|
+
- Titlebars and status bars use `material="regular"` by default. Use
|
|
25
|
+
`material="clear"` only when compact chrome overlays a rich canvas, or
|
|
26
|
+
`material="none"` when the composition supplies an opaque surface.
|
|
24
27
|
- The app frame does not make the outer window draggable. A decorated native
|
|
25
28
|
titlebar or host-owned drag region must provide that behavior.
|
|
26
29
|
- The host owns the `--window-*` inset variables. The component only consumes
|
|
@@ -31,3 +34,5 @@ surface.
|
|
|
31
34
|
tests. Runtime apps should normally use `mode="auto"`; a future host context
|
|
32
35
|
may set `data-moldable-display-mode` before render when a window still uses an
|
|
33
36
|
app iframe.
|
|
37
|
+
- Material samples only renderer content. Native behind-window material remains
|
|
38
|
+
host-owned.
|
|
@@ -25,9 +25,15 @@ import { RefreshCw } from 'lucide-react'
|
|
|
25
25
|
>
|
|
26
26
|
<RefreshCw />
|
|
27
27
|
</Button>
|
|
28
|
+
|
|
29
|
+
<Button type="submit" loading={isSaving}>
|
|
30
|
+
Save changes
|
|
31
|
+
</Button>
|
|
28
32
|
```
|
|
29
33
|
|
|
30
|
-
Keep toolbar actions compact (`xs`, `sm`, `icon-xs`, or `icon-sm`). Reserve `default` or `lg` for the primary action in a form, setup state, or focused control dock.
|
|
34
|
+
Keep toolbar actions compact (`xs`, `sm`, `icon-xs`, or `icon-sm`). Reserve `default` or `lg` for the primary action in a form, setup state, or focused control dock. Set `loading` while that button's async action is in flight. The button blocks pointer and keyboard activation, exposes `aria-busy`, keeps its accessible label mounted, and centers a width-stable spinner.
|
|
35
|
+
|
|
36
|
+
Use `loading` for the action that started the request. Disable the whole form only when editing during the request would be unsafe or would invalidate the submitted payload; do not disable unrelated controls merely to communicate progress.
|
|
31
37
|
|
|
32
38
|
For links that should look like buttons:
|
|
33
39
|
|
|
@@ -42,13 +48,14 @@ For links that should look like buttons:
|
|
|
42
48
|
- `variant`: `default`, `destructive`, `outline`, `secondary`, `ghost`, or `link`. Defaults to `default`.
|
|
43
49
|
- `size`: `default`, `xs`, `sm`, `lg`, `icon`, `icon-xs`, `icon-sm`, or `icon-lg`. Defaults to `default`.
|
|
44
50
|
- `asChild`: renders a Radix `Slot` so the child owns the element and semantics.
|
|
51
|
+
- `loading`: blocks activation, applies disabled behavior and `aria-busy`, and overlays the shared `Spinner` without unmounting or resizing the label. Defaults to `false`.
|
|
45
52
|
- Accepts native button props, including `type`, `disabled`, `aria-*`, and event handlers.
|
|
46
53
|
- `buttonVariants` is exported for cases that need the same visual treatment on another component.
|
|
47
54
|
|
|
48
55
|
## Do
|
|
49
56
|
|
|
50
57
|
- Set `type="button"` unless the control intentionally submits a form.
|
|
51
|
-
- Add `cursor-pointer`; use the real `disabled` attribute for unavailable
|
|
58
|
+
- Add `cursor-pointer`; use the real `disabled` attribute for unavailable actions and `loading` for an in-flight action.
|
|
52
59
|
- Give icon-only buttons an `aria-label` and a tooltip.
|
|
53
60
|
- Use a specific Lucide icon that names the action.
|
|
54
61
|
- Put one visually primary action in a local action group.
|
|
@@ -60,7 +67,7 @@ For links that should look like buttons:
|
|
|
60
67
|
- Do not rely on color alone to explain a destructive or selected action.
|
|
61
68
|
- Do not use raw color classes, decorative gradients, or oversized call-to-action styling.
|
|
62
69
|
- Do not use `Sparkles` as a generic AI marker.
|
|
63
|
-
- Do not
|
|
70
|
+
- Do not use `disabled` alone as the progress signal; use `loading` so progress is announced and the label width stays stable.
|
|
64
71
|
- Do not hide essential actions behind hover-only icon buttons.
|
|
65
72
|
|
|
66
73
|
## Moldable considerations
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Collapsible
|
|
2
|
+
|
|
3
|
+
`Collapsible` exposes the unstyled Radix disclosure primitive.
|
|
4
|
+
`CollapsibleSection` adds a consistent section trigger, state-aware chevron,
|
|
5
|
+
border, and content spacing.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<CollapsibleSection defaultOpen>
|
|
9
|
+
<CollapsibleSectionTrigger>Advanced options</CollapsibleSectionTrigger>
|
|
10
|
+
<CollapsibleSectionContent>
|
|
11
|
+
Controls that are useful less often.
|
|
12
|
+
</CollapsibleSectionContent>
|
|
13
|
+
</CollapsibleSection>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
Use a collapsible section for one independently disclosed region. Use
|
|
19
|
+
`Accordion` when several peer sections need coordinated one-at-a-time or
|
|
20
|
+
multi-section behavior.
|
|
21
|
+
|
|
22
|
+
## API
|
|
23
|
+
|
|
24
|
+
The base `Collapsible`, `CollapsibleTrigger`, and `CollapsibleContent` expose
|
|
25
|
+
the Radix props directly.
|
|
26
|
+
|
|
27
|
+
The section helpers add:
|
|
28
|
+
|
|
29
|
+
- `CollapsibleSection`: bordered section root.
|
|
30
|
+
- `CollapsibleSectionTrigger`: full-width trigger with a chevron driven by the
|
|
31
|
+
root `data-state`.
|
|
32
|
+
- `CollapsibleSectionContent`: standard inset content.
|
|
33
|
+
- `motion`: `default` or `none`.
|
|
34
|
+
- `contentClassName`: styles the inset inner content without changing the
|
|
35
|
+
animated viewport.
|
|
36
|
+
|
|
37
|
+
## Keyboard and accessibility
|
|
38
|
+
|
|
39
|
+
The trigger is a button with `aria-expanded` and `aria-controls` managed by
|
|
40
|
+
Radix. Space and Enter toggle it. The chevron is decorative.
|
|
41
|
+
|
|
42
|
+
## Motion
|
|
43
|
+
|
|
44
|
+
Motion is brief and uses the shared reduced-motion behavior. Set
|
|
45
|
+
`motion="none"` for content where movement would distract or when a parent owns
|
|
46
|
+
the transition.
|
|
47
|
+
|
|
48
|
+
## Don't
|
|
49
|
+
|
|
50
|
+
- Do not put another interactive control inside the trigger.
|
|
51
|
+
- Do not hide required validation errors or primary actions by default.
|
|
52
|
+
- Do not use a disclosure solely to make a sparse layout look busier.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Command
|
|
2
|
+
|
|
3
|
+
`Command` renders searchable, keyboard-driven actions and local choices.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use `CommandDialog` for an app-local command palette or picker with enough
|
|
8
|
+
options to benefit from search. Use the item anatomy helpers when a result has
|
|
9
|
+
supporting text or metadata.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
<CommandItem value="Open project">
|
|
13
|
+
<CommandItemIcon>
|
|
14
|
+
<FolderIcon />
|
|
15
|
+
</CommandItemIcon>
|
|
16
|
+
<CommandItemContent>
|
|
17
|
+
<CommandItemTitle>Open project</CommandItemTitle>
|
|
18
|
+
<CommandItemDescription>Choose a local folder</CommandItemDescription>
|
|
19
|
+
</CommandItemContent>
|
|
20
|
+
<CommandShortcut>⌘O</CommandShortcut>
|
|
21
|
+
</CommandItem>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Anatomy
|
|
25
|
+
|
|
26
|
+
- `Command`, `CommandInput`, and `CommandList` own filtering and navigation.
|
|
27
|
+
- `CommandGroup`, `CommandEmpty`, and `CommandSeparator` organize results.
|
|
28
|
+
- `CommandItem` is the interactive cmdk option.
|
|
29
|
+
- `CommandItemIcon`, `CommandItemContent`, `CommandItemTitle`, and
|
|
30
|
+
`CommandItemDescription` provide consistent row alignment.
|
|
31
|
+
- `CommandShortcut` displays a trailing shortcut hint; it does not install the
|
|
32
|
+
shortcut.
|
|
33
|
+
|
|
34
|
+
Use `NotificationDot`, `Status`, or `Spinner` as a trailing child when a result
|
|
35
|
+
needs an indicator. Keep those states semantic instead of adding decoration to
|
|
36
|
+
the row anatomy.
|
|
37
|
+
|
|
38
|
+
## Keyboard and accessibility
|
|
39
|
+
|
|
40
|
+
cmdk owns arrow-key navigation, selection, and filtering. Give icon-only
|
|
41
|
+
information an accessible text equivalent. `CommandItemIcon` is decorative by
|
|
42
|
+
default because the adjacent title should name the command.
|
|
43
|
+
|
|
44
|
+
## Platform behavior
|
|
45
|
+
|
|
46
|
+
This is a portable renderer component. An app-local palette does not replace
|
|
47
|
+
the host command surface. Register host-visible commands through the Moldable
|
|
48
|
+
command API and use this component only for commands owned by the current app.
|
|
49
|
+
|
|
50
|
+
## Don't
|
|
51
|
+
|
|
52
|
+
- Do not create a second interactive row inside `CommandItem`.
|
|
53
|
+
- Do not install keyboard shortcuts from `CommandShortcut`.
|
|
54
|
+
- Do not use a command palette for a small fixed set that fits in a menu.
|