create-substrate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +6 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +127 -0
- package/dist/prompts.js.map +1 -0
- package/dist/scaffold.d.ts +10 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +395 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/surfaces/3d-scene.d.ts +3 -0
- package/dist/surfaces/3d-scene.d.ts.map +1 -0
- package/dist/surfaces/3d-scene.js +184 -0
- package/dist/surfaces/3d-scene.js.map +1 -0
- package/dist/surfaces/animation.d.ts +3 -0
- package/dist/surfaces/animation.d.ts.map +1 -0
- package/dist/surfaces/animation.js +211 -0
- package/dist/surfaces/animation.js.map +1 -0
- package/dist/surfaces/blank.d.ts +3 -0
- package/dist/surfaces/blank.d.ts.map +1 -0
- package/dist/surfaces/blank.js +72 -0
- package/dist/surfaces/blank.js.map +1 -0
- package/dist/surfaces/canvas-2d.d.ts +3 -0
- package/dist/surfaces/canvas-2d.d.ts.map +1 -0
- package/dist/surfaces/canvas-2d.js +139 -0
- package/dist/surfaces/canvas-2d.js.map +1 -0
- package/dist/surfaces/data-vis.d.ts +3 -0
- package/dist/surfaces/data-vis.d.ts.map +1 -0
- package/dist/surfaces/data-vis.js +175 -0
- package/dist/surfaces/data-vis.js.map +1 -0
- package/dist/surfaces/image-gen.d.ts +3 -0
- package/dist/surfaces/image-gen.d.ts.map +1 -0
- package/dist/surfaces/image-gen.js +193 -0
- package/dist/surfaces/image-gen.js.map +1 -0
- package/dist/surfaces/index.d.ts +4 -0
- package/dist/surfaces/index.d.ts.map +1 -0
- package/dist/surfaces/index.js +17 -0
- package/dist/surfaces/index.js.map +1 -0
- package/dist/surfaces/node-editor.d.ts +3 -0
- package/dist/surfaces/node-editor.d.ts.map +1 -0
- package/dist/surfaces/node-editor.js +211 -0
- package/dist/surfaces/node-editor.js.map +1 -0
- package/dist/surfaces/types.d.ts +22 -0
- package/dist/surfaces/types.d.ts.map +1 -0
- package/dist/surfaces/types.js +10 -0
- package/dist/surfaces/types.js.map +1 -0
- package/dist/utils/detect-pm.d.ts +5 -0
- package/dist/utils/detect-pm.d.ts.map +1 -0
- package/dist/utils/detect-pm.js +20 -0
- package/dist/utils/detect-pm.js.map +1 -0
- package/dist/utils/fs.d.ts +7 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +52 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +15 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/shell.d.ts +7 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +28 -0
- package/dist/utils/shell.js.map +1 -0
- package/package.json +35 -0
- package/skills/3d-scene/SKILL.md +172 -0
- package/skills/animation/SKILL.md +194 -0
- package/skills/canvas-2d/SKILL.md +132 -0
- package/skills/composing-panels/SKILL.md +309 -0
- package/skills/create-custom-tool/SKILL.md +157 -0
- package/skills/data-visualisation/SKILL.md +228 -0
- package/skills/image-generation/SKILL.md +211 -0
- package/skills/scaffold-playground/SKILL.md +141 -0
- package/skills/substrate-canvas/SKILL.md +217 -0
- package/skills/substrate-controls/SKILL.md +242 -0
- package/skills/substrate-feedback/SKILL.md +219 -0
- package/skills/substrate-interaction/SKILL.md +286 -0
- package/skills/substrate-nodes/SKILL.md +208 -0
- package/skills/substrate-scaffold/SKILL.md +206 -0
- package/skills/theming/SKILL.md +117 -0
- package/skills/wire-interactions/SKILL.md +155 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: substrate-canvas
|
|
3
|
+
description: "Spatial hooks and utilities for Substrate canvas surfaces: viewport (pan/zoom), selection, drag reorder, snapping, bounding box, coordinate maths, and hit testing. Use when building or extending a 2D/3D spatial surface. Triggers on: viewport, pan, zoom, snap to grid, bounding box, hit test, coordinate transform, canvas interaction, spatial, drag."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Substrate canvas
|
|
8
|
+
|
|
9
|
+
Canvas utilities handle everything spatial – viewport transforms, coordinate systems, snapping, bounding box computation, and hit testing. These are the building blocks for any surface where users interact with positioned objects.
|
|
10
|
+
|
|
11
|
+
## When to use this skill
|
|
12
|
+
|
|
13
|
+
Use the canvas skill when you need to:
|
|
14
|
+
|
|
15
|
+
- Add pan/zoom to a surface
|
|
16
|
+
- Implement snap-to-grid or smart edge snapping
|
|
17
|
+
- Compute bounding boxes for selection or zoom-to-fit
|
|
18
|
+
- Convert between screen and world coordinates
|
|
19
|
+
- Build or extend hit testing
|
|
20
|
+
- Add spatial selection (click, shift-click, marquee)
|
|
21
|
+
|
|
22
|
+
For the app shell around the surface, see **substrate-scaffold**.
|
|
23
|
+
For non-spatial interaction (undo, clipboard, shortcuts), see **substrate-interaction**.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Hooks
|
|
28
|
+
|
|
29
|
+
### useViewport
|
|
30
|
+
|
|
31
|
+
`registry/substrate/hooks/use-viewport.ts`
|
|
32
|
+
|
|
33
|
+
Generic pan/zoom state. Surface-agnostic – works for 2D canvas, node editors, or any pannable area.
|
|
34
|
+
|
|
35
|
+
**Returns**: `viewport` (x, y, zoom), `toWorld`, `toScreen`, `pan`, `zoomToPoint`, `zoomIn`, `zoomOut`, `resetZoom`, `zoomToFit`, `handleWheel`
|
|
36
|
+
|
|
37
|
+
**When to use**: Any surface that needs panning and zooming. Wire `handleWheel` to the container's `onWheel`, use `toWorld`/`toScreen` for coordinate transforms.
|
|
38
|
+
|
|
39
|
+
**Connects to**: `ZoomControls` (substrate-scaffold) reads `viewport.zoom` and calls `zoomIn`/`zoomOut`/`resetZoom`.
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
const { viewport, handleWheel, zoomToFit } = useViewport({
|
|
43
|
+
minZoom: 0.1,
|
|
44
|
+
maxZoom: 10,
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
### useSelection
|
|
51
|
+
|
|
52
|
+
`registry/substrate/hooks/use-selection.ts`
|
|
53
|
+
|
|
54
|
+
Multi-select with modifier key support: plain click, ⌘-click toggle, shift-click range.
|
|
55
|
+
|
|
56
|
+
**Returns**: `selectedIds`, `select`, `setSelection`, `selectAll`, `deselectAll`, `isSelected`
|
|
57
|
+
|
|
58
|
+
**When to use**: Layer lists, node selections, element picking – any context where the user selects from a list or spatial layout.
|
|
59
|
+
|
|
60
|
+
**Connects to**: `LayerList` and `TreeList` (substrate-interaction) for list-based selection. Canvas hit-testing for spatial selection.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### useDragReorder
|
|
65
|
+
|
|
66
|
+
`registry/substrate/hooks/use-drag-reorder.ts`
|
|
67
|
+
|
|
68
|
+
Pointer-based list reordering using pointer capture. Calculates drop index from drag distance.
|
|
69
|
+
|
|
70
|
+
**Returns**: `getDragItemProps`, `dragIndex`, `dropIndex`, `isDragging`
|
|
71
|
+
|
|
72
|
+
**When to use**: Reorderable layer lists, timeline tracks, node port ordering.
|
|
73
|
+
|
|
74
|
+
**Connects to**: `LayerList` (substrate-interaction) for visual layer reordering.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
### Canvas-specific hooks
|
|
79
|
+
|
|
80
|
+
These hooks are tightly coupled to the 2D canvas surface and its stores:
|
|
81
|
+
|
|
82
|
+
| Hook | File | Purpose |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `useCanvasTransform` | `hooks/use-canvas-transform.ts` | Screen ↔ world coordinate conversion using `document-store` camera |
|
|
85
|
+
| `useCanvasRenderer` | `hooks/use-canvas-renderer.ts` | Canvas 2D rendering loop with dirty flag and rAF |
|
|
86
|
+
| `useCanvasDrag` | `hooks/use-canvas-drag.ts` | Pointer-based drag tracking (start/move/end) |
|
|
87
|
+
| `useCanvasInteraction` | `hooks/use-canvas-interaction.ts` | Orchestrates tool-specific behaviour (select, create, pan) |
|
|
88
|
+
| `useHitTest` | `hooks/use-hit-test.ts` | Point-in-element and handle hit detection |
|
|
89
|
+
| `useKeyboardShortcuts` | `hooks/use-keyboard-shortcuts.ts` | Canvas-specific keyboard bindings |
|
|
90
|
+
|
|
91
|
+
These are typically used together inside a `DesignCanvas` or similar surface component. See **substrate-surfaces** for how they compose.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Utilities
|
|
96
|
+
|
|
97
|
+
### snapToGrid / snapToGuides
|
|
98
|
+
|
|
99
|
+
`registry/substrate/lib/snap.ts`
|
|
100
|
+
|
|
101
|
+
Grid snapping and smart edge snapping.
|
|
102
|
+
|
|
103
|
+
| Function | Purpose |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `snapToGrid(point, gridSize, options?)` | Snap a point to nearest grid intersection |
|
|
106
|
+
| `snapValueToGrid(value, gridSize, origin?)` | Snap a single number to grid |
|
|
107
|
+
| `snapToGuides(point, guides, options?)` | Snap to nearby guide lines (edges, centres) within a threshold |
|
|
108
|
+
| `guidesFromBounds(boundsList, exclude?)` | Generate alignment guides from sibling element bounds |
|
|
109
|
+
|
|
110
|
+
**When to use**: Object placement and resizing. Call `snapToGrid` during move/resize interactions. Use `guidesFromBounds` + `snapToGuides` for Figma-style smart alignment lines.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
// During element drag
|
|
114
|
+
const guides = guidesFromBounds(siblingBounds, draggedBounds)
|
|
115
|
+
const { point, snappedGuides } = snapToGuides(newPosition, guides)
|
|
116
|
+
// Render snappedGuides as alignment lines
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### boundingBox
|
|
122
|
+
|
|
123
|
+
`registry/substrate/lib/bounding-box.ts`
|
|
124
|
+
|
|
125
|
+
Bounding box computation and spatial queries.
|
|
126
|
+
|
|
127
|
+
| Function | Purpose |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `boundingBox(items)` | Union bounds of multiple Bounds |
|
|
130
|
+
| `boundingBoxFromPoints(points)` | Bounds from an array of Points |
|
|
131
|
+
| `expandBounds(bounds, padding)` | Expand uniformly |
|
|
132
|
+
| `expandBoundsBy(bounds, { top, right, bottom, left })` | Expand per-side |
|
|
133
|
+
| `containsPoint(bounds, point)` | Point-in-bounds test |
|
|
134
|
+
| `containsBounds(a, b)` | Does A fully contain B? |
|
|
135
|
+
| `boundsCenter(bounds)` | Centre point |
|
|
136
|
+
| `intersectBounds(a, b)` | Intersection or null |
|
|
137
|
+
|
|
138
|
+
**When to use**: Zoom-to-fit (compute union bounds → pass to `useViewport.zoomToFit`), marquee selection (test which elements intersect the selection rectangle), alignment distribution.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
### math
|
|
143
|
+
|
|
144
|
+
`registry/substrate/lib/math.ts`
|
|
145
|
+
|
|
146
|
+
Core geometry functions.
|
|
147
|
+
|
|
148
|
+
| Function | Purpose |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| `rotatePoint(point, center, angleDeg)` | Rotate a point around a centre |
|
|
151
|
+
| `boundsCenter(bounds)` | Centre of a bounding box |
|
|
152
|
+
| `pointInBounds(point, bounds)` | Hit test |
|
|
153
|
+
| `pointInEllipseBounds(point, bounds)` | Ellipse hit test |
|
|
154
|
+
| `boundsOverlap(a, b)` | Overlap test |
|
|
155
|
+
| `normalizeBounds(start, end)` | Normalise a drag rectangle |
|
|
156
|
+
| `screenToWorld(point, camera)` / `worldToScreen(point, camera)` | Coordinate transforms |
|
|
157
|
+
| `clamp(value, min, max)` | Numeric clamp |
|
|
158
|
+
| `distance(a, b)` | Euclidean distance |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Stores
|
|
163
|
+
|
|
164
|
+
### document-store
|
|
165
|
+
|
|
166
|
+
`registry/substrate/stores/document-store.ts`
|
|
167
|
+
|
|
168
|
+
Central store for the 2D canvas surface – elements, selection, camera, history.
|
|
169
|
+
|
|
170
|
+
### tool-store
|
|
171
|
+
|
|
172
|
+
`registry/substrate/stores/tool-store.ts`
|
|
173
|
+
|
|
174
|
+
Active tool state. Read by both the surface (to change interaction mode) and the toolbar (to highlight the active tool).
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Recommended patterns
|
|
179
|
+
|
|
180
|
+
### Adding snap-to-grid to an existing surface
|
|
181
|
+
|
|
182
|
+
1. Import `snapToGrid` from `lib/snap`
|
|
183
|
+
2. In your drag handler, snap the position before applying it:
|
|
184
|
+
```ts
|
|
185
|
+
const snapped = snapToGrid(worldPosition, gridSize)
|
|
186
|
+
updateElement(id, { x: snapped.x, y: snapped.y })
|
|
187
|
+
```
|
|
188
|
+
3. Optionally add a `Toggle` in the toolbar for enabling/disabling snap
|
|
189
|
+
|
|
190
|
+
### Zoom-to-fit selection
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
const selected = elements.filter((el) => selectedIds.has(el.id))
|
|
194
|
+
const bounds = boundingBox(selected)
|
|
195
|
+
if (bounds) zoomToFit(expandBounds(bounds, 40))
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Smart alignment snapping
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
const siblings = elements.filter((el) => !selectedIds.has(el.id))
|
|
202
|
+
const guides = guidesFromBounds(siblings.map(toBounds))
|
|
203
|
+
const { point, snappedGuides } = snapToGuides(dragPos, guides, { threshold: 5 })
|
|
204
|
+
// Render snappedGuides as thin blue lines
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Cross-references
|
|
210
|
+
|
|
211
|
+
| Need | Skill |
|
|
212
|
+
| --- | --- |
|
|
213
|
+
| Zoom controls and status bar | **substrate-scaffold** |
|
|
214
|
+
| Layer list, tree list for structure panels | **substrate-interaction** |
|
|
215
|
+
| Controls inside property panes | **substrate-controls** |
|
|
216
|
+
| Surface architecture and rendering | **substrate-surfaces** |
|
|
217
|
+
| Node editor specifics | **substrate-nodes** |
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: substrate-controls
|
|
3
|
+
description: "Substrate input and selection primitives: number input, slider, number slider, select, toggle, text area, colour picker, easing curve editor, swatch palette, and action controls. Use when building property editors, parameter forms, or any interactive control surface. Triggers on: input, slider, select, toggle, colour picker, swatch, easing, action controls, property editor, form control."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Substrate controls
|
|
8
|
+
|
|
9
|
+
Controls are the interactive inputs and selectors that live inside panes, toolbars, and action groups. They follow the `data-slot` composition pattern – parent containers (ActionControls, Toolbar, PaneHeader) style children automatically via CSS selectors.
|
|
10
|
+
|
|
11
|
+
## When to use this skill
|
|
12
|
+
|
|
13
|
+
Use the controls skill when you need to:
|
|
14
|
+
|
|
15
|
+
- Add a property input to a pane (e.g. width, height, rotation, opacity)
|
|
16
|
+
- Build a colour selection UI
|
|
17
|
+
- Create a parameter group with labels and inline controls
|
|
18
|
+
- Wire an easing curve editor into an animation panel
|
|
19
|
+
- Understand how ActionControls composes child controls
|
|
20
|
+
|
|
21
|
+
For the pane containers that hold controls, see **substrate-scaffold**.
|
|
22
|
+
For selection, undo, and clipboard behaviour, see **substrate-interaction**.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## The data-slot composition pattern
|
|
27
|
+
|
|
28
|
+
This is the core design pattern of Substrate controls. Parent components style children by matching `data-slot` attributes – no prop drilling required.
|
|
29
|
+
|
|
30
|
+
### ActionControls (the key container)
|
|
31
|
+
|
|
32
|
+
`registry/substrate/components/action.tsx`
|
|
33
|
+
|
|
34
|
+
A horizontal row that auto-styles its children:
|
|
35
|
+
|
|
36
|
+
| Child slot | Styling applied |
|
|
37
|
+
| ---------------- | ------------------------------------ |
|
|
38
|
+
| `number-input` | Strips border, sets `bg-transparent` |
|
|
39
|
+
| `select-trigger` | Strips border, sets `bg-transparent` |
|
|
40
|
+
| `colour-swatch` | `size-4`, removes border |
|
|
41
|
+
| `slider` | Removes border |
|
|
42
|
+
|
|
43
|
+
**Exports**: `ActionControls`, `ActionLabel`, `ActionSeparator`
|
|
44
|
+
|
|
45
|
+
**Typical use**: Group a label + one or more controls on a single row inside a pane.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<ActionControls>
|
|
49
|
+
<ActionLabel>Width</ActionLabel>
|
|
50
|
+
<NumberInput value={width} onChange={setWidth} min={0} />
|
|
51
|
+
</ActionControls>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### How it works
|
|
55
|
+
|
|
56
|
+
ActionControls applies Tailwind `**:data-[slot=...]` selectors in its className. When you drop a `NumberInput` (which renders `data-slot="number-input"`) inside, it automatically loses its border and gains transparent background – creating a seamless inline look.
|
|
57
|
+
|
|
58
|
+
The same pattern is used by **Toolbar** (compaction) and **PaneHeader** (auto-positioning HistoryControls).
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Components in this domain
|
|
63
|
+
|
|
64
|
+
### NumberInput
|
|
65
|
+
|
|
66
|
+
`registry/substrate/components/number-input.tsx`
|
|
67
|
+
|
|
68
|
+
Scrub-to-adjust number field. Supports label, suffix, min/max, step/shiftStep, precision, and pointer-lock scrubbing.
|
|
69
|
+
|
|
70
|
+
**Props**: `value`, `onChange`, `onStart`, `label`, `suffix`, `min`, `max`, `step`, `shiftStep`, `precision`, `disabled`
|
|
71
|
+
|
|
72
|
+
**When to use**: Any numeric property – dimensions, position, rotation, opacity, font size. Use `onStart` to push an undo snapshot before the value changes.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### Slider
|
|
77
|
+
|
|
78
|
+
`registry/substrate/components/slider.tsx`
|
|
79
|
+
|
|
80
|
+
Range slider built on Radix Slider. Single value or range (two thumbs).
|
|
81
|
+
|
|
82
|
+
**When to use**: Bounded continuous values where a visual track helps – opacity, volume, zoom level.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### NumberSlider
|
|
87
|
+
|
|
88
|
+
`registry/substrate/components/number-slider.tsx`
|
|
89
|
+
|
|
90
|
+
Compound of Slider + NumberInput on a single row. The slider fills available space, the input is a fixed `w-18`.
|
|
91
|
+
|
|
92
|
+
**Props**: Same as NumberInput plus all Slider range props.
|
|
93
|
+
|
|
94
|
+
**When to use**: When you want both a slider for quick scrubbing and a precise numeric input – e.g. opacity, blur radius.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### Select
|
|
99
|
+
|
|
100
|
+
`registry/substrate/components/select.tsx`
|
|
101
|
+
|
|
102
|
+
Dropdown built on Radix Select. Compact `h-7` trigger.
|
|
103
|
+
|
|
104
|
+
**Exports**: `Select`, `SelectTrigger`, `SelectContent`, `SelectItem`, `SelectGroup`, `SelectLabel`, `SelectSeparator`, `SelectValue`
|
|
105
|
+
|
|
106
|
+
**When to use**: Enum-style choices – blend mode, font family, alignment, export format. Renders `data-slot="select-trigger"` for ActionControls awareness.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### Toggle
|
|
111
|
+
|
|
112
|
+
`registry/substrate/components/toggle.tsx`
|
|
113
|
+
|
|
114
|
+
Binary toggle button built on Radix Toggle. `h-7 min-w-7 px-2`. Accent styling when pressed.
|
|
115
|
+
|
|
116
|
+
**When to use**: On/off features – snap to grid, show guides, lock aspect ratio, bold/italic.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### TextArea
|
|
121
|
+
|
|
122
|
+
`registry/substrate/components/text-area.tsx`
|
|
123
|
+
|
|
124
|
+
Auto-resizing textarea with `minRows` / `maxRows`. Shares border/bg styling with NumberInput.
|
|
125
|
+
|
|
126
|
+
**When to use**: Multi-line text – element content, descriptions, notes, code snippets.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### ColourPicker
|
|
131
|
+
|
|
132
|
+
`registry/substrate/components/colour-picker.tsx`
|
|
133
|
+
|
|
134
|
+
HSV colour picker with hex/RGB inputs and opacity slider. The swatch trigger renders `data-slot="colour-swatch"` for slot-aware composition.
|
|
135
|
+
|
|
136
|
+
**When to use**: Fill, stroke, background, text colour – any colour property.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### SwatchPalette
|
|
141
|
+
|
|
142
|
+
`registry/substrate/components/swatch-palette.tsx`
|
|
143
|
+
|
|
144
|
+
Grid of colour swatches. Exports `MATERIAL_COLOURS` (24) and `TAILWIND_COLOURS` (16) presets.
|
|
145
|
+
|
|
146
|
+
**Props**: `colours`, `columns` (default 8), `selected`, `onSelect`
|
|
147
|
+
|
|
148
|
+
**When to use**: Quick colour selection from a predefined palette. Often paired with ColourPicker.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### EasingCurveEditor
|
|
153
|
+
|
|
154
|
+
`registry/substrate/components/easing-curve-editor.tsx`
|
|
155
|
+
|
|
156
|
+
Cubic bezier editor with draggable control points and real-time preview.
|
|
157
|
+
|
|
158
|
+
**When to use**: Animation easing configuration. Typically inside a CollapsiblePane in an animation surface's right panel.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Recommended combinations
|
|
163
|
+
|
|
164
|
+
### Property pane for a shape
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
<CollapsiblePane>
|
|
168
|
+
<CollapsiblePaneHeader>
|
|
169
|
+
<CollapsiblePaneLabel>Transform</CollapsiblePaneLabel>
|
|
170
|
+
<HistoryControls />
|
|
171
|
+
</CollapsiblePaneHeader>
|
|
172
|
+
<CollapsiblePaneContent className="space-y-1">
|
|
173
|
+
<ActionControls>
|
|
174
|
+
<ActionLabel>X</ActionLabel>
|
|
175
|
+
<NumberInput value={x} onChange={setX} onStart={pushSnapshot} />
|
|
176
|
+
<ActionSeparator />
|
|
177
|
+
<ActionLabel>Y</ActionLabel>
|
|
178
|
+
<NumberInput value={y} onChange={setY} onStart={pushSnapshot} />
|
|
179
|
+
</ActionControls>
|
|
180
|
+
<ActionControls>
|
|
181
|
+
<ActionLabel>W</ActionLabel>
|
|
182
|
+
<NumberInput value={w} onChange={setW} min={0} onStart={pushSnapshot} />
|
|
183
|
+
<ActionSeparator />
|
|
184
|
+
<ActionLabel>H</ActionLabel>
|
|
185
|
+
<NumberInput value={h} onChange={setH} min={0} onStart={pushSnapshot} />
|
|
186
|
+
</ActionControls>
|
|
187
|
+
<ActionControls>
|
|
188
|
+
<ActionLabel>Rotation</ActionLabel>
|
|
189
|
+
<NumberInput
|
|
190
|
+
value={r}
|
|
191
|
+
onChange={setR}
|
|
192
|
+
suffix="°"
|
|
193
|
+
onStart={pushSnapshot}
|
|
194
|
+
/>
|
|
195
|
+
</ActionControls>
|
|
196
|
+
</CollapsiblePaneContent>
|
|
197
|
+
</CollapsiblePane>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Opacity control
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
<ActionControls>
|
|
204
|
+
<ActionLabel>Opacity</ActionLabel>
|
|
205
|
+
<NumberSlider
|
|
206
|
+
value={opacity}
|
|
207
|
+
onChange={setOpacity}
|
|
208
|
+
min={0}
|
|
209
|
+
max={100}
|
|
210
|
+
suffix="%"
|
|
211
|
+
/>
|
|
212
|
+
</ActionControls>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Colour + blend mode row
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
<ActionControls>
|
|
219
|
+
<ColourPicker value={fill} onChange={setFill} />
|
|
220
|
+
<Select value={blendMode} onValueChange={setBlendMode}>
|
|
221
|
+
<SelectTrigger>
|
|
222
|
+
<SelectValue />
|
|
223
|
+
</SelectTrigger>
|
|
224
|
+
<SelectContent>
|
|
225
|
+
<SelectItem value="normal">Normal</SelectItem>
|
|
226
|
+
<SelectItem value="multiply">Multiply</SelectItem>
|
|
227
|
+
<SelectItem value="screen">Screen</SelectItem>
|
|
228
|
+
</SelectContent>
|
|
229
|
+
</Select>
|
|
230
|
+
</ActionControls>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Cross-references
|
|
236
|
+
|
|
237
|
+
| Need | Skill |
|
|
238
|
+
| ------------------------------------- | ------------------------- |
|
|
239
|
+
| Pane and panel containers | **substrate-scaffold** |
|
|
240
|
+
| Undo support (onStart → pushSnapshot) | **substrate-interaction** |
|
|
241
|
+
| Animation easing in timeline surfaces | **substrate-surfaces** |
|
|
242
|
+
| Node property panels | **substrate-nodes** |
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: substrate-feedback
|
|
3
|
+
description: "Substrate status, communication, and discovery components: toast, progress bar, badge, empty state, shortcut overlay, command palette, context menu, and popover. Use when showing feedback, status indicators, contextual menus, or discoverability aids. Triggers on: toast, notification, progress, loading, empty state, badge, command palette, context menu, popover, shortcut help, keyboard shortcuts display."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Substrate feedback
|
|
8
|
+
|
|
9
|
+
Feedback components handle communication with the user – notifications, status indicators, contextual actions, and discoverability features.
|
|
10
|
+
|
|
11
|
+
## When to use this skill
|
|
12
|
+
|
|
13
|
+
Use the feedback skill when you need to:
|
|
14
|
+
|
|
15
|
+
- Show success/error/undo notifications
|
|
16
|
+
- Display loading or progress
|
|
17
|
+
- Show empty states when there's no content
|
|
18
|
+
- Add a command palette (⌘K) for quick actions
|
|
19
|
+
- Build right-click context menus
|
|
20
|
+
- Show a keyboard shortcuts overlay
|
|
21
|
+
- Use popovers for inline detail views
|
|
22
|
+
- Add status badges to elements
|
|
23
|
+
|
|
24
|
+
For structural layout (toolbar, panels), see **substrate-scaffold**.
|
|
25
|
+
For interactive controls (inputs, sliders), see **substrate-controls**.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Components
|
|
30
|
+
|
|
31
|
+
### Toast
|
|
32
|
+
|
|
33
|
+
`registry/substrate/components/toast.tsx`
|
|
34
|
+
|
|
35
|
+
Thin wrapper around Sonner. Exports a `toast` function with convenience methods.
|
|
36
|
+
|
|
37
|
+
| Method | Duration | Use case |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `toast(message)` | 4s | General notification |
|
|
40
|
+
| `toast.success(message)` | 3s | Confirmation (saved, exported) |
|
|
41
|
+
| `toast.error(message)` | 6s | Error with longer reading time |
|
|
42
|
+
| `toast.warning(message)` | 5s | Caution |
|
|
43
|
+
| `toast.info(message)` | 4s | Informational |
|
|
44
|
+
| `toast.undo(message, onUndo)` | 6s | Action with undo option |
|
|
45
|
+
| `toast.dismiss(id?)` | — | Programmatic dismiss |
|
|
46
|
+
|
|
47
|
+
**When to use**: After async operations (save, export, publish), destructive actions (delete with undo), errors. Use `toast.undo` for delete operations to give users a recovery path.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
toast.undo("Layer deleted", () => restoreLayer(id))
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### ProgressBar
|
|
56
|
+
|
|
57
|
+
`registry/substrate/components/progress-bar.tsx`
|
|
58
|
+
|
|
59
|
+
Thin bar (h-1.5) with determinate and indeterminate modes.
|
|
60
|
+
|
|
61
|
+
**Props**: `value`, `max` (default 100), `indeterminate`
|
|
62
|
+
|
|
63
|
+
**When to use**: File uploads, export progress, batch operations. Use `indeterminate` when duration is unknown. Place at the top of a panel or inside a pane.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
### Badge
|
|
68
|
+
|
|
69
|
+
`registry/substrate/components/badge.tsx`
|
|
70
|
+
|
|
71
|
+
Small label with 4 variants: `default`, `secondary`, `outline`, `muted`.
|
|
72
|
+
|
|
73
|
+
**When to use**: Element type indicators (in layer lists), node categories, status labels (draft/published), keyboard shortcut hints.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### EmptyState
|
|
78
|
+
|
|
79
|
+
`registry/substrate/components/empty-state.tsx`
|
|
80
|
+
|
|
81
|
+
Centred placeholder for empty panels or lists.
|
|
82
|
+
|
|
83
|
+
**Exports**: `EmptyState`, `EmptyStateIcon`, `EmptyStateTitle`, `EmptyStateDescription`, `EmptyStateAction`
|
|
84
|
+
|
|
85
|
+
**When to use**: Empty layer lists, empty canvas, no search results, first-run experience.
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<EmptyState>
|
|
89
|
+
<EmptyStateIcon><RiStackLine /></EmptyStateIcon>
|
|
90
|
+
<EmptyStateTitle>No layers yet</EmptyStateTitle>
|
|
91
|
+
<EmptyStateDescription>
|
|
92
|
+
Draw a shape on the canvas to get started
|
|
93
|
+
</EmptyStateDescription>
|
|
94
|
+
</EmptyState>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### CommandPalette
|
|
100
|
+
|
|
101
|
+
`registry/substrate/components/command-palette.tsx`
|
|
102
|
+
|
|
103
|
+
⌘K-triggered command launcher using cmdk + Radix Dialog.
|
|
104
|
+
|
|
105
|
+
**Exports**: `CommandPalette`, `CommandInput`, `CommandList`, `CommandEmpty`, `CommandGroup`, `CommandItem`, `CommandShortcut`, `CommandSeparator`
|
|
106
|
+
|
|
107
|
+
**When to use**: Quick access to any action – tool switching, file operations, element creation, settings. Especially useful in node editors ("Add Node → Blur") and complex apps with many features.
|
|
108
|
+
|
|
109
|
+
**Recommended groups**: Tools, Actions, Create, Recent, Settings.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### ContextMenu
|
|
114
|
+
|
|
115
|
+
`registry/substrate/components/context-menu.tsx`
|
|
116
|
+
|
|
117
|
+
Right-click menu built on Radix ContextMenu. Supports submenus.
|
|
118
|
+
|
|
119
|
+
**Exports**: `ContextMenu`, `ContextMenuTrigger`, `ContextMenuContent`, `ContextMenuItem`, `ContextMenuSeparator`, `ContextMenuLabel`, `ContextMenuShortcut`, `ContextMenuSub`, `ContextMenuSubTrigger`, `ContextMenuSubContent`
|
|
120
|
+
|
|
121
|
+
**When to use**: Right-click on canvas elements, layer items, node cards, tabs. Show contextual actions (copy, paste, delete, group, bring to front) with keyboard shortcut hints.
|
|
122
|
+
|
|
123
|
+
**Connects to**: `createKeyboardShortcuts` (substrate-interaction) – use `formatShortcut` to render shortcut hints in menu items.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### ShortcutOverlay
|
|
128
|
+
|
|
129
|
+
`registry/substrate/components/shortcut-overlay.tsx`
|
|
130
|
+
|
|
131
|
+
Full-screen overlay listing all keyboard shortcuts, triggered by `?` key.
|
|
132
|
+
|
|
133
|
+
**Props**: `groups` – array of `{ label, shortcuts: { label, keys }[] }`
|
|
134
|
+
|
|
135
|
+
**When to use**: Discoverability aid. One per app. Pass the same shortcut groups registered with `createKeyboardShortcuts`.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### Popover
|
|
140
|
+
|
|
141
|
+
`registry/substrate/components/popover.tsx`
|
|
142
|
+
|
|
143
|
+
Floating panel built on Radix Popover. Optional arrow.
|
|
144
|
+
|
|
145
|
+
**Exports**: `Popover`, `PopoverTrigger`, `PopoverContent`, `PopoverClose`
|
|
146
|
+
|
|
147
|
+
**When to use**: Inline details, settings, colour pickers, property editors that don't warrant a full panel.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Recommended patterns
|
|
152
|
+
|
|
153
|
+
### Delete with undo
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
function handleDelete(ids: string[]) {
|
|
157
|
+
const snapshot = getElements(ids)
|
|
158
|
+
pushSnapshot()
|
|
159
|
+
removeElements(ids)
|
|
160
|
+
toast.undo(`${ids.length} element${ids.length > 1 ? "s" : ""} deleted`, () => {
|
|
161
|
+
restoreElements(snapshot)
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Context menu with shortcuts
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<ContextMenu>
|
|
170
|
+
<ContextMenuTrigger>{children}</ContextMenuTrigger>
|
|
171
|
+
<ContextMenuContent>
|
|
172
|
+
<ContextMenuItem onSelect={copy}>
|
|
173
|
+
Copy <ContextMenuShortcut>⌘C</ContextMenuShortcut>
|
|
174
|
+
</ContextMenuItem>
|
|
175
|
+
<ContextMenuItem onSelect={paste}>
|
|
176
|
+
Paste <ContextMenuShortcut>⌘V</ContextMenuShortcut>
|
|
177
|
+
</ContextMenuItem>
|
|
178
|
+
<ContextMenuSeparator />
|
|
179
|
+
<ContextMenuSub>
|
|
180
|
+
<ContextMenuSubTrigger>Arrange</ContextMenuSubTrigger>
|
|
181
|
+
<ContextMenuSubContent>
|
|
182
|
+
<ContextMenuItem onSelect={bringToFront}>Bring to front</ContextMenuItem>
|
|
183
|
+
<ContextMenuItem onSelect={sendToBack}>Send to back</ContextMenuItem>
|
|
184
|
+
</ContextMenuSubContent>
|
|
185
|
+
</ContextMenuSub>
|
|
186
|
+
</ContextMenuContent>
|
|
187
|
+
</ContextMenu>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Command palette with tool switching
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
<CommandPalette>
|
|
194
|
+
<CommandInput placeholder="Search actions…" />
|
|
195
|
+
<CommandList>
|
|
196
|
+
<CommandGroup heading="Tools">
|
|
197
|
+
<CommandItem onSelect={() => setTool("select")}>
|
|
198
|
+
<RiCursorLine /> Select
|
|
199
|
+
<CommandShortcut>V</CommandShortcut>
|
|
200
|
+
</CommandItem>
|
|
201
|
+
<CommandItem onSelect={() => setTool("rectangle")}>
|
|
202
|
+
<RiRectangleLine /> Rectangle
|
|
203
|
+
<CommandShortcut>R</CommandShortcut>
|
|
204
|
+
</CommandItem>
|
|
205
|
+
</CommandGroup>
|
|
206
|
+
</CommandList>
|
|
207
|
+
</CommandPalette>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Cross-references
|
|
213
|
+
|
|
214
|
+
| Need | Skill |
|
|
215
|
+
| --- | --- |
|
|
216
|
+
| Keyboard shortcut registration | **substrate-interaction** |
|
|
217
|
+
| Toolbar and status bar for badges | **substrate-scaffold** |
|
|
218
|
+
| Node editor command palette groups | **substrate-nodes** |
|
|
219
|
+
| Empty state in layer/tree lists | **substrate-interaction** |
|