create-substrate 0.1.6 → 0.2.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/LICENSE +21 -0
- package/README.md +11 -4
- package/dist/index.js +25 -7
- package/dist/index.js.map +1 -1
- package/dist/prompts.d.ts +0 -2
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +6 -50
- package/dist/prompts.js.map +1 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +82 -80
- package/dist/scaffold.js.map +1 -1
- package/dist/surfaces/3d-scene.d.ts.map +1 -1
- package/dist/surfaces/3d-scene.js +43 -74
- package/dist/surfaces/3d-scene.js.map +1 -1
- package/dist/surfaces/canvas-2d.d.ts.map +1 -1
- package/dist/surfaces/canvas-2d.js +7 -10
- package/dist/surfaces/canvas-2d.js.map +1 -1
- package/dist/surfaces/data-vis.d.ts.map +1 -1
- package/dist/surfaces/data-vis.js +37 -143
- package/dist/surfaces/data-vis.js.map +1 -1
- package/dist/surfaces/image-gen.d.ts.map +1 -1
- package/dist/surfaces/image-gen.js +15 -35
- package/dist/surfaces/image-gen.js.map +1 -1
- package/dist/surfaces/node-editor.js +1 -1
- package/dist/utils/detect-pm.d.ts +2 -0
- package/dist/utils/detect-pm.d.ts.map +1 -1
- package/dist/utils/detect-pm.js +9 -0
- package/dist/utils/detect-pm.js.map +1 -1
- package/dist/utils/package-info.d.ts +5 -0
- package/dist/utils/package-info.d.ts.map +1 -0
- package/dist/utils/package-info.js +17 -0
- package/dist/utils/package-info.js.map +1 -0
- package/package.json +4 -3
- package/skills/3d-scene/SKILL.md +2 -1
- package/skills/animation/SKILL.md +2 -1
- package/skills/canvas-2d/SKILL.md +67 -5
- package/skills/composing-panels/SKILL.md +1 -4
- package/skills/data-visualisation/SKILL.md +2 -1
- package/skills/image-generation/SKILL.md +2 -1
- package/skills/install-components/SKILL.md +22 -16
- package/skills/scaffold-playground/SKILL.md +39 -19
- package/skills/substrate-canvas/SKILL.md +5 -0
- package/skills/substrate-controls/SKILL.md +57 -2
- package/skills/substrate-interaction/SKILL.md +19 -10
- package/skills/substrate-scaffold/SKILL.md +4 -4
- package/skills/theming/SKILL.md +8 -2
- package/skills/wire-interactions/SKILL.md +6 -3
- package/templates/agents-section.md +41 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-pm.js","sourceRoot":"","sources":["../../src/utils/detect-pm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAIhC,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;QAC5E,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAA;IACf,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB;IAClD,OAAO,GAAG,EAAE,UAAU,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAAkB,EAAE,MAAc;IAC9D,IAAI,EAAE,KAAK,KAAK;QAAE,OAAO,WAAW,MAAM,EAAE,CAAA;IAC5C,OAAO,GAAG,EAAE,IAAI,MAAM,EAAE,CAAA;AAC1B,CAAC"}
|
|
1
|
+
{"version":3,"file":"detect-pm.js","sourceRoot":"","sources":["../../src/utils/detect-pm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAIhC,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,KAAK,CAAA;AAC/D,CAAC;AAED,kFAAkF;AAClF,uEAAuE;AACvE,MAAM,UAAU,2BAA2B,CACzC,YAAgC,OAAO,CAAC,GAAG,CAAC,qBAAqB;IAEjE,MAAM,IAAI,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IACpD,OAAO,IAAI,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;AACrD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;QAC5E,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAA;IACf,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB;IAClD,OAAO,GAAG,EAAE,UAAU,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAAkB,EAAE,MAAc;IAC9D,IAAI,EAAE,KAAK,KAAK;QAAE,OAAO,WAAW,MAAM,EAAE,CAAA;IAC5C,OAAO,GAAG,EAAE,IAAI,MAAM,EAAE,CAAA;AAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-info.d.ts","sourceRoot":"","sources":["../../src/utils/package-info.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY,QAA4D,CAAA;AAErF,eAAO,MAAM,OAAO,EAAE,MAEb,CAAA;AAKT,eAAO,MAAM,SAAS,QAA6C,CAAA;AAInE,wBAAgB,aAAa,IAAI,MAAM,CAGtC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
// Both src/utils and dist/utils sit two levels below the package root
|
|
5
|
+
export const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
6
|
+
export const VERSION = JSON.parse(readFileSync(join(PACKAGE_ROOT, "package.json"), "utf-8")).version;
|
|
7
|
+
// The CLI version the generated project depends on. Keep in step with
|
|
8
|
+
// packages/cli/package.json when it gains a new minor version.
|
|
9
|
+
// SUBSTRATE_CLI_SPEC points it at a local tarball for testing before publish.
|
|
10
|
+
export const CLI_RANGE = process.env.SUBSTRATE_CLI_SPEC ?? "^0.2.0";
|
|
11
|
+
// Agent guidance shared with `substrate-kit init`; copied here by
|
|
12
|
+
// scripts/sync-skills.mjs, with markers so `init` can refresh it later.
|
|
13
|
+
export function agentsSection() {
|
|
14
|
+
const body = readFileSync(join(PACKAGE_ROOT, "templates", "agents-section.md"), "utf-8").trimEnd();
|
|
15
|
+
return `<!-- substrate:start -->\n${body}\n<!-- substrate:end -->\n`;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=package-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-info.js","sourceRoot":"","sources":["../../src/utils/package-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,sEAAsE;AACtE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;AAErF,MAAM,CAAC,MAAM,OAAO,GAClB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CACrE,CAAC,OAAO,CAAA;AAET,sEAAsE;AACtE,+DAA+D;AAC/D,8EAA8E;AAC9E,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,QAAQ,CAAA;AAEnE,kEAAkE;AAClE,wEAAwE;AACxE,MAAM,UAAU,aAAa;IAC3B,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAA;IAClG,OAAO,6BAA6B,IAAI,4BAA4B,CAAA;AACtE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-substrate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Scaffold a Substrate creative tool playground",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"build": "tsc",
|
|
11
11
|
"dev": "tsx src/index.ts",
|
|
12
12
|
"prepack": "node ../../scripts/sync-skills.mjs && npm run build",
|
|
13
|
-
"prepublishOnly": "
|
|
13
|
+
"prepublishOnly": "node ../../scripts/release-stamp.mjs verify"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"chalk": "^5.4.0",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
|
26
|
-
"skills"
|
|
26
|
+
"skills",
|
|
27
|
+
"templates"
|
|
27
28
|
],
|
|
28
29
|
"keywords": [
|
|
29
30
|
"substrate",
|
package/skills/3d-scene/SKILL.md
CHANGED
|
@@ -173,6 +173,7 @@ Some examples of what this surface enables:
|
|
|
173
173
|
|
|
174
174
|
## Related reference skills
|
|
175
175
|
|
|
176
|
+
- **scaffold-playground** — the craft rules and the checklist to run before you say it's done
|
|
176
177
|
- **substrate-scaffold** — Toolbar, Panel, StatusBar, ZoomControls for the app shell
|
|
177
|
-
- **substrate-controls** — NumberInput, Slider, ColourPicker, ActionControls for property panes
|
|
178
|
+
- **substrate-controls** — NumberInput, Slider, ColourPicker, ActionControls for property panes; `useControls` for tuning material, light and camera values from one config object
|
|
178
179
|
- **substrate-interaction** — useUndoable, useClipboard, createKeyboardShortcuts, TreeList for scene hierarchy
|
|
@@ -195,6 +195,7 @@ Some examples of what this surface enables:
|
|
|
195
195
|
|
|
196
196
|
## Related reference skills
|
|
197
197
|
|
|
198
|
+
- **scaffold-playground** — the craft rules and the checklist to run before you say it's done
|
|
198
199
|
- **substrate-scaffold** — Toolbar, Panel, StatusBar for the app shell
|
|
199
|
-
- **substrate-controls** — NumberInput, Slider, EasingCurveEditor, ActionControls for keyframe properties
|
|
200
|
+
- **substrate-controls** — NumberInput, Slider, EasingCurveEditor, ActionControls for keyframe properties. For tuning UI motion by eye (entrances, springs, choreography), use `useControlsTimeline` with `<ControlsTimeline />` instead of building a timeline: define clips in code, bind `clip.current` to style, then copy the tuned values back. The `timeline` component is for editing user-authored keyframes.
|
|
200
201
|
- **substrate-interaction** — useUndoable, createKeyboardShortcuts, LayerList for animation layers
|
|
@@ -19,7 +19,7 @@ This is Substrate's default surface. The `DesignCanvas` component provides a ful
|
|
|
19
19
|
### Install
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
npx
|
|
22
|
+
npx substrate-kit add design-canvas
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
This pulls in the canvas renderer, hit-test system, interaction hooks, transform utilities, and all required stores.
|
|
@@ -85,27 +85,88 @@ The default tool set for a 2D canvas:
|
|
|
85
85
|
|
|
86
86
|
### Panels
|
|
87
87
|
|
|
88
|
-
See the
|
|
88
|
+
See the `composing-panels` skill for full details. In summary:
|
|
89
89
|
|
|
90
90
|
- **Left panel** — layers list (render order, visibility toggles, selection)
|
|
91
91
|
- **Right panel** — properties inspector (transform, fill, stroke, opacity, type-specific properties)
|
|
92
92
|
|
|
93
93
|
### Interactions
|
|
94
94
|
|
|
95
|
-
See the
|
|
95
|
+
See the `wire-interactions` skill for the full state machine. Key patterns:
|
|
96
96
|
|
|
97
97
|
- Click to select, shift-click to multi-select
|
|
98
98
|
- Drag on empty space with select tool → marquee selection
|
|
99
99
|
- Drag on selected element → move
|
|
100
100
|
- Drag on resize handle → resize
|
|
101
|
+
- Drag on the rotate handle → rotate (Shift snaps to 15°)
|
|
101
102
|
- Drag with shape tool → create new element
|
|
102
103
|
- Middle-click or hand tool → pan
|
|
103
104
|
|
|
105
|
+
## State, undo and persistence
|
|
106
|
+
|
|
107
|
+
`useDocumentStore` holds the board: `elements`, `elementOrder` (back to front), `camera`, `showGrid`, the selection and the history.
|
|
108
|
+
|
|
109
|
+
- **Undo and redo cover elements and their order only.** Pan, zoom and the grid are view state, so undoing an edit never moves the viewport.
|
|
110
|
+
- **Call `pushSnapshot()` before every mutation that should be undoable,** including property edits. For a scrub or drag, snapshot once at the start (`onStart`), not on every change.
|
|
111
|
+
- **Composite actions** record their own undo step: `duplicateElements(ids?)` clones the selection (or the given ids), and `setElementOrder(ids)` replaces the stacking order. Call `pushSnapshot()` yourself before `setElementOrder`.
|
|
112
|
+
- **Zoom** with `useCanvasTransform`: `setZoom(zoom)` and `zoomIn`/`zoomOut` zoom around the middle of the canvas, and `zoomToFit()` frames every visible element. Wire `ZoomControls` to `setZoom` and `zoomToFit`; setting `camera.zoom` directly zooms around the world origin and drifts.
|
|
113
|
+
|
|
114
|
+
To remember the board across reloads, restore before the first render and save on change:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { useDocumentStore, type DocumentSnapshot } from "@/stores/substrate/document-store"
|
|
118
|
+
|
|
119
|
+
const KEY = "my-board"
|
|
120
|
+
|
|
121
|
+
// Call before the first render so the saved board is there on first paint
|
|
122
|
+
export function restoreBoard(): boolean {
|
|
123
|
+
const raw = localStorage.getItem(KEY)
|
|
124
|
+
if (!raw) return false
|
|
125
|
+
try {
|
|
126
|
+
useDocumentStore.getState().loadState(JSON.parse(raw) as DocumentSnapshot)
|
|
127
|
+
return true
|
|
128
|
+
} catch {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Returns an unsubscribe function
|
|
134
|
+
export function persistBoard(): () => void {
|
|
135
|
+
let timer: number | undefined
|
|
136
|
+
return useDocumentStore.subscribe((state, prev) => {
|
|
137
|
+
const changed =
|
|
138
|
+
state.elements !== prev.elements ||
|
|
139
|
+
state.elementOrder !== prev.elementOrder ||
|
|
140
|
+
state.camera !== prev.camera ||
|
|
141
|
+
state.showGrid !== prev.showGrid
|
|
142
|
+
if (!changed) return
|
|
143
|
+
window.clearTimeout(timer)
|
|
144
|
+
timer = window.setTimeout(() => {
|
|
145
|
+
const snapshot: DocumentSnapshot = {
|
|
146
|
+
elements: state.elements,
|
|
147
|
+
elementOrder: state.elementOrder,
|
|
148
|
+
camera: state.camera,
|
|
149
|
+
showGrid: state.showGrid,
|
|
150
|
+
}
|
|
151
|
+
localStorage.setItem(KEY, JSON.stringify(snapshot))
|
|
152
|
+
}, 300)
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Selection and history are not saved, which is usually what you want. Seed a starter board with `addElement` only when `restoreBoard()` returns `false`, and give the starter shapes theme-safe colours.
|
|
158
|
+
|
|
159
|
+
## Current limits
|
|
160
|
+
|
|
161
|
+
- **Rotation** works on a single selection: drag the round handle above it (hold Shift to snap to 15°), and the box, handles and cursors turn with the shape. Resizing a rotated shape keeps the opposite corner fixed. Several selected elements can be moved and deleted but not rotated or resized together; there is no group transform.
|
|
162
|
+
- **Text:** a click with the text tool makes a 160×32 note. Text is edited through a properties panel (`textStyle.content`); there is no on-canvas editing.
|
|
163
|
+
- **Hidden and locked layers** (`visible: false`, `locked: true`) are skipped by hit-testing, so they can't be selected on the canvas.
|
|
164
|
+
|
|
104
165
|
## Extending the canvas
|
|
105
166
|
|
|
106
167
|
### Custom elements
|
|
107
168
|
|
|
108
|
-
To add a new element type (e.g. star, arrow, image), follow the
|
|
169
|
+
To add a new element type (e.g. star, arrow, image), follow the `create-custom-tool` skill. The pipeline is: type definition → factory → renderer → hit-test → interaction → shortcut → cursor → toolbar.
|
|
109
170
|
|
|
110
171
|
### Custom rendering
|
|
111
172
|
|
|
@@ -117,7 +178,7 @@ For UI elements that sit on top of the canvas (selection indicators, context men
|
|
|
117
178
|
|
|
118
179
|
## Theming
|
|
119
180
|
|
|
120
|
-
The canvas background is driven by the `--canvas` CSS variable, read at render time via `getComputedStyle`. Grid dots use `--canvas-foreground`. See the
|
|
181
|
+
The canvas background is driven by the `--canvas` CSS variable, read at render time via `getComputedStyle`. Grid dots use `--canvas-foreground`. See the `theming` skill for the full token table.
|
|
121
182
|
|
|
122
183
|
## What to build
|
|
123
184
|
|
|
@@ -132,6 +193,7 @@ Some examples of what this surface enables:
|
|
|
132
193
|
|
|
133
194
|
## Related reference skills
|
|
134
195
|
|
|
196
|
+
- **scaffold-playground** — the craft rules and the checklist to run before you say it's done
|
|
135
197
|
- **substrate-canvas** — full catalogue of spatial hooks (useViewport, useSelection, useDragReorder) and utilities (snapToGrid, boundingBox, math)
|
|
136
198
|
- **substrate-scaffold** — Toolbar, Panel, StatusBar, ZoomControls for the app shell
|
|
137
199
|
- **substrate-controls** — NumberInput, Slider, ColourPicker, ActionControls for property panes
|
|
@@ -27,10 +27,7 @@ Panel (side panel shell — left or right)
|
|
|
27
27
|
## Install
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npx
|
|
31
|
-
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/pane
|
|
32
|
-
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/action
|
|
33
|
-
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/slider
|
|
30
|
+
npx substrate-kit add panel pane action slider
|
|
34
31
|
```
|
|
35
32
|
|
|
36
33
|
## Panel positioning
|
|
@@ -229,6 +229,7 @@ Some examples of what this surface enables:
|
|
|
229
229
|
|
|
230
230
|
## Related reference skills
|
|
231
231
|
|
|
232
|
+
- **scaffold-playground** — the craft rules and the checklist to run before you say it's done
|
|
232
233
|
- **substrate-scaffold** — Toolbar, Panel, StatusBar, DocumentTabs for the app shell
|
|
233
|
-
- **substrate-controls** — Select, NumberInput, Slider, ColourPicker
|
|
234
|
+
- **substrate-controls** — `useControls` for chart settings from one config object (sliders, toggles, colours, selects); Select, NumberInput, Slider, ColourPicker when composing rows by hand
|
|
234
235
|
- **substrate-feedback** — Toast, EmptyState, CommandPalette for data loading feedback
|
|
@@ -167,7 +167,7 @@ Use Pane/Action/Slider to build parameter controls:
|
|
|
167
167
|
- **Parameters pane** — sliders for inference steps, CFG scale, seed, image size selector
|
|
168
168
|
- **Style pane** (optional) — style preset buttons, LoRA weight sliders
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
For this kind of local, tunable state, prefer `useControlsController` with `<Controls />` over hand-built rows: one config object gives the sliders, selects and seed field, plus reset, versions and a copy button, and `setValues` loads a saved generation (see **substrate-controls**). Use the Action/ActionLabel/ActionControls pattern when a row needs a custom layout.
|
|
171
171
|
|
|
172
172
|
## Interaction patterns
|
|
173
173
|
|
|
@@ -211,6 +211,7 @@ Some examples of what this surface enables:
|
|
|
211
211
|
|
|
212
212
|
## Related reference skills
|
|
213
213
|
|
|
214
|
+
- **scaffold-playground** — the craft rules and the checklist to run before you say it's done
|
|
214
215
|
- **substrate-scaffold** — Toolbar, Panel, StatusBar for the app shell
|
|
215
216
|
- **substrate-controls** — Slider, NumberInput, TextArea, Select for generation parameters
|
|
216
217
|
- **substrate-feedback** — Toast, ProgressBar for generation status, EmptyState for no results
|
|
@@ -25,8 +25,8 @@ npx substrate-kit add slider number-input toggle colour-picker
|
|
|
25
25
|
Install every component in a category:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
npx substrate-kit add --category
|
|
29
|
-
npx substrate-kit add --category
|
|
28
|
+
npx substrate-kit add --category form
|
|
29
|
+
npx substrate-kit add --category control
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Open an interactive picker (no arguments):
|
|
@@ -45,16 +45,17 @@ npx substrate-kit add --list-categories
|
|
|
45
45
|
|
|
46
46
|
- Run from a project created with `npx create-substrate` or initialised with `npx @substrate-kit/cli init`. Both add `@substrate-kit/cli` as a dev dependency, which is what makes `npx substrate-kit` resolve. The CLI looks for `components.json` — it will error if it's missing.
|
|
47
47
|
- Components land in `registry/substrate/...` and related aliases defined in `components.json`.
|
|
48
|
-
- Under the hood the CLI delegates to `npx shadcn@latest add` against `https://substrate.georgedrury.co.uk/r/<name
|
|
48
|
+
- Under the hood the CLI delegates to `npx shadcn@latest add` against `https://substrate.georgedrury.co.uk/r/<name>`, in one call for all the names you give it, retrying if the network drops. Do **not** use the shadcn CLI directly — always go through `substrate-kit add` so the CLI can print theme-override hints and keep the workflow uniform.
|
|
49
|
+
- If the Substrate MCP server is connected, `search_components`, `get_component`, `get_docs` and `install_components` do the same jobs without a shell.
|
|
49
50
|
|
|
50
51
|
### Related CLI commands
|
|
51
52
|
|
|
52
53
|
| Command | What it does |
|
|
53
54
|
| --- | --- |
|
|
54
55
|
| `substrate-kit add <name>` | Install one or more components |
|
|
55
|
-
| `substrate-kit docs <name>` | Read component docs in the terminal |
|
|
56
|
-
| `substrate-kit
|
|
57
|
-
| `substrate-kit
|
|
56
|
+
| `substrate-kit docs <name>` | Read component docs in the terminal: props, theme variables, usage |
|
|
57
|
+
| `substrate-kit docs <name> --open` | Open the component's docs page in the browser |
|
|
58
|
+
| `substrate-kit docs --list` | List every component |
|
|
58
59
|
|
|
59
60
|
---
|
|
60
61
|
|
|
@@ -62,10 +63,11 @@ npx substrate-kit add --list-categories
|
|
|
62
63
|
|
|
63
64
|
When you catch yourself about to write a custom version of any of these, stop and run `substrate-kit add <name>` instead.
|
|
64
65
|
|
|
65
|
-
### Form controls — `
|
|
66
|
+
### Form controls — `form`
|
|
66
67
|
|
|
67
68
|
| Component | Command | Use for |
|
|
68
69
|
| --- | --- | --- |
|
|
70
|
+
| Controls | `substrate-kit add controls` | Whole parameter panels from a config object (`useControls` + `<Controls />`) — reach for this before hand-wiring rows |
|
|
69
71
|
| Slider | `substrate-kit add slider` | Any range input — opacity, volume, zoom, numeric sliders |
|
|
70
72
|
| Number Input | `substrate-kit add number-input` | Numeric property fields with drag-to-scrub on the label |
|
|
71
73
|
| Number Slider | `substrate-kit add number-slider` | Combined slider + numeric input on one row |
|
|
@@ -73,11 +75,13 @@ When you catch yourself about to write a custom version of any of these, stop an
|
|
|
73
75
|
| Toggle | `substrate-kit add toggle` | Binary on/off switches (snap, visibility, lock) |
|
|
74
76
|
| Text Area | `substrate-kit add text-area` | Auto-resizing multi-line text input |
|
|
75
77
|
|
|
76
|
-
### Higher-level controls — `
|
|
78
|
+
### Higher-level controls — `control`
|
|
77
79
|
|
|
78
80
|
| Component | Command | Use for |
|
|
79
81
|
| --- | --- | --- |
|
|
80
82
|
| Colour Picker | `substrate-kit add colour-picker` | HSV picker with hex/RGB + opacity |
|
|
83
|
+
| XY Pad | `substrate-kit add xy-pad` | Drag or arrow-key pad that sets two values at once |
|
|
84
|
+
| Image Picker | `substrate-kit add image-picker` | Thumbnail grid for choosing or uploading an image |
|
|
81
85
|
| Swatch Palette | `substrate-kit add swatch-palette` | Grid of preset colours |
|
|
82
86
|
| Alignment Controls | `substrate-kit add alignment-controls` | Horizontal/vertical align + distribute |
|
|
83
87
|
| Collapsible Pane | `substrate-kit add collapsible-pane` | Expandable section inside a panel |
|
|
@@ -85,7 +89,7 @@ When you catch yourself about to write a custom version of any of these, stop an
|
|
|
85
89
|
| Tree List | `substrate-kit add tree-list` | Hierarchical tree with expand/collapse and drag reorder |
|
|
86
90
|
| Node | `substrate-kit add node` | Card-based node with typed input/output ports |
|
|
87
91
|
|
|
88
|
-
### Chrome (app shell) — `
|
|
92
|
+
### Chrome (app shell) — `chrome`
|
|
89
93
|
|
|
90
94
|
| Component | Command | Use for |
|
|
91
95
|
| --- | --- | --- |
|
|
@@ -99,7 +103,7 @@ When you catch yourself about to write a custom version of any of these, stop an
|
|
|
99
103
|
| Status Bar | `substrate-kit add status-bar` | Bottom bar for tool/selection/zoom info |
|
|
100
104
|
| Zoom Controls | `substrate-kit add zoom-controls` | Zoom in/out/reset with percentage display |
|
|
101
105
|
|
|
102
|
-
### Core UI — `
|
|
106
|
+
### Core UI — `ui`
|
|
103
107
|
|
|
104
108
|
| Component | Command | Use for |
|
|
105
109
|
| --- | --- | --- |
|
|
@@ -114,20 +118,22 @@ When you catch yourself about to write a custom version of any of these, stop an
|
|
|
114
118
|
| Progress Bar | `substrate-kit add progress-bar` | Horizontal progress indicator |
|
|
115
119
|
| Toast | `substrate-kit add toast` | Notifications with undo, powered by Sonner |
|
|
116
120
|
|
|
117
|
-
### Animation — `
|
|
121
|
+
### Animation — `animation`
|
|
118
122
|
|
|
119
123
|
| Component | Command | Use for |
|
|
120
124
|
| --- | --- | --- |
|
|
121
125
|
| Timeline | `substrate-kit add timeline` | Ruler + playhead + tracks + keyframe diamonds |
|
|
122
126
|
| Easing Curve Editor | `substrate-kit add easing-curve-editor` | Draggable cubic-bezier editor |
|
|
127
|
+
| Spring Editor | `substrate-kit add spring-editor` | Spring curve preview with time or physics sliders |
|
|
128
|
+
| Controls Timeline | `substrate-kit add controls-timeline` | Clips defined in code, tuned in a scrubbable timeline (`useControlsTimeline` + `<ControlsTimeline />`) |
|
|
123
129
|
|
|
124
|
-
### Visualisation — `
|
|
130
|
+
### Visualisation — `visualization`
|
|
125
131
|
|
|
126
132
|
| Component | Command | Use for |
|
|
127
133
|
| --- | --- | --- |
|
|
128
134
|
| Gallery | `substrate-kit add gallery` | Responsive thumbnail grid with selection and actions |
|
|
129
135
|
|
|
130
|
-
### 2D canvas — `
|
|
136
|
+
### 2D canvas — `canvas`
|
|
131
137
|
|
|
132
138
|
| Component | Command | Use for |
|
|
133
139
|
| --- | --- | --- |
|
|
@@ -136,7 +142,7 @@ When you catch yourself about to write a custom version of any of these, stop an
|
|
|
136
142
|
| Hit Test | `substrate-kit add hit-test` | Point-in-element geometry |
|
|
137
143
|
| Element Factory | `substrate-kit add element-factory` | Factories for rectangle/ellipse/text/etc |
|
|
138
144
|
|
|
139
|
-
### Canvas hooks — `
|
|
145
|
+
### Canvas hooks — `canvas` and `hook`
|
|
140
146
|
|
|
141
147
|
| Hook | Command | Use for |
|
|
142
148
|
| --- | --- | --- |
|
|
@@ -152,7 +158,7 @@ When you catch yourself about to write a custom version of any of these, stop an
|
|
|
152
158
|
| useViewport | `substrate-kit add use-viewport` | Pan/zoom state for canvas surfaces |
|
|
153
159
|
| useUndoable | `substrate-kit add use-undoable` | Lightweight undo/redo stack |
|
|
154
160
|
|
|
155
|
-
### Stores — `
|
|
161
|
+
### Stores — `store`
|
|
156
162
|
|
|
157
163
|
| Store | Command | Use for |
|
|
158
164
|
| --- | --- | --- |
|
|
@@ -161,7 +167,7 @@ When you catch yourself about to write a custom version of any of these, stop an
|
|
|
161
167
|
| panel-store | `substrate-kit add panel-store` | Left/right panel visibility, open panes |
|
|
162
168
|
| animation-store | `substrate-kit add animation-store` | Playback: duration, time, play/pause |
|
|
163
169
|
|
|
164
|
-
### Libraries — `
|
|
170
|
+
### Libraries — `lib`
|
|
165
171
|
|
|
166
172
|
| Lib | Command | Use for |
|
|
167
173
|
| --- | --- | --- |
|
|
@@ -14,13 +14,13 @@ A Substrate playground is a creative app built from composable primitives: a **s
|
|
|
14
14
|
|
|
15
15
|
## Prerequisites
|
|
16
16
|
|
|
17
|
-
A Next.js
|
|
17
|
+
A Vite or Next.js project with React and Tailwind CSS v4. If you don't have one, run:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
npx create-substrate my-playground
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
This scaffolds a
|
|
23
|
+
This scaffolds a Vite + React project with dependencies, `globals.css` with Substrate tokens, and a starter page for the surface you pick. It asks for a name and a surface; pass `--surface <type>` (or `--yes`) when there is no terminal.
|
|
24
24
|
|
|
25
25
|
To add Substrate to an existing project instead:
|
|
26
26
|
|
|
@@ -28,7 +28,7 @@ To add Substrate to an existing project instead:
|
|
|
28
28
|
npx @substrate-kit/cli init
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
This installs skills, CLAUDE.md, components.json
|
|
31
|
+
This detects Vite or Next, installs the skills, adds Substrate guidance to `CLAUDE.md` and `AGENTS.md` without touching the rest of them, adds the `@substrate` registry to `components.json`, and registers the MCP server in `.mcp.json`.
|
|
32
32
|
|
|
33
33
|
## 1. Choose your surface
|
|
34
34
|
|
|
@@ -36,11 +36,12 @@ The surface is the heart of the playground — the central area where the main i
|
|
|
36
36
|
|
|
37
37
|
| Surface | Best for | Skill |
|
|
38
38
|
| ------------------ | --------------------------------------------------------- | --------------------------------------------- |
|
|
39
|
-
| 2D canvas | Shape editors, diagrams, whiteboards, node graphs |
|
|
40
|
-
| 3D scene | Product viewers, scene builders, shader playgrounds |
|
|
41
|
-
| Animation | Timeline editors, motion prototyping, easing explorers |
|
|
42
|
-
| Image generation | Prompt-driven image tools, texture generators, inpainting |
|
|
43
|
-
| Data visualisation | Chart editors, dashboard builders, data explorers |
|
|
39
|
+
| 2D canvas | Shape editors, diagrams, whiteboards, node graphs | `canvas-2d` |
|
|
40
|
+
| 3D scene | Product viewers, scene builders, shader playgrounds | `3d-scene` |
|
|
41
|
+
| Animation | Timeline editors, motion prototyping, easing explorers | `animation` |
|
|
42
|
+
| Image generation | Prompt-driven image tools, texture generators, inpainting | `image-generation` |
|
|
43
|
+
| Data visualisation | Chart editors, dashboard builders, data explorers | `data-visualisation` |
|
|
44
|
+
| Node editor | Visual programming, shader graphs, data pipelines | `substrate-nodes` |
|
|
44
45
|
|
|
45
46
|
Read the relevant surface skill for setup instructions, dependencies, and architecture patterns.
|
|
46
47
|
|
|
@@ -50,17 +51,13 @@ Regardless of surface type, most playgrounds use the same chrome components:
|
|
|
50
51
|
|
|
51
52
|
```bash
|
|
52
53
|
# UI chrome
|
|
53
|
-
npx
|
|
54
|
-
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/panel
|
|
55
|
-
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/pane
|
|
56
|
-
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/action
|
|
57
|
-
npx shadcn@latest add https://substrate.georgedrury.co.uk/r/slider
|
|
54
|
+
npx substrate-kit add toolbar panel pane action slider
|
|
58
55
|
```
|
|
59
56
|
|
|
60
57
|
For the 2D canvas surface specifically, also install:
|
|
61
58
|
|
|
62
59
|
```bash
|
|
63
|
-
npx
|
|
60
|
+
npx substrate-kit add design-canvas
|
|
64
61
|
```
|
|
65
62
|
|
|
66
63
|
## 3. Compose the layout
|
|
@@ -77,7 +74,7 @@ import {
|
|
|
77
74
|
ToolbarToggle,
|
|
78
75
|
ToolbarSeparator,
|
|
79
76
|
} from "@/components/substrate/toolbar"
|
|
80
|
-
import { usePanelStore } from "@/stores/substrate/panel-store"
|
|
77
|
+
import { usePanelStore } from "@/stores/substrate/panel-store" // npx substrate-kit add panel-store
|
|
81
78
|
import { useToolStore } from "@/stores/substrate/tool-store"
|
|
82
79
|
|
|
83
80
|
export default function PlaygroundPage() {
|
|
@@ -128,10 +125,33 @@ These are surface-agnostic — they work the same whether you're building a 3D s
|
|
|
128
125
|
## 5. Next steps
|
|
129
126
|
|
|
130
127
|
- **Build your surface** — read the surface skill for your chosen type
|
|
131
|
-
- **Add property panels** — see
|
|
132
|
-
- **Wire keyboard shortcuts** — see
|
|
133
|
-
- **Create custom tools** — see
|
|
134
|
-
- **Customise appearance** — see
|
|
128
|
+
- **Add property panels** — see `composing-panels`
|
|
129
|
+
- **Wire keyboard shortcuts** — see `wire-interactions`
|
|
130
|
+
- **Create custom tools** — see `create-custom-tool`
|
|
131
|
+
- **Customise appearance** — see `theming`
|
|
132
|
+
|
|
133
|
+
## 6. Craft rules
|
|
134
|
+
|
|
135
|
+
Substrate tools are dense, so the details decide whether one feels finished. Apply these to everything you build, including your own components.
|
|
136
|
+
|
|
137
|
+
- **Density:** chrome controls are 28px (`h-7`) with `text-xs`. Keep that. Don't enlarge inputs or buttons to fill space; a touch layout can use `size-9` or larger instead.
|
|
138
|
+
- **Transitions:** name the properties (`transition-[color,background-color,box-shadow]`), never `transition-all`. Use about 150ms. Pressable buttons get `active:scale-[0.96]`, never below 0.95. Anything that moves must respect `prefers-reduced-motion`; the starter's `globals.css` already does.
|
|
139
|
+
- **Depth:** panels are separated from the canvas by a border. Floating things (toolbar, popovers, menus) use a shadow, not a border. When a rounded child sits inside a rounded parent, its radius is the parent's minus the padding between them.
|
|
140
|
+
- **Both themes:** use tokens (`bg-background`, `text-foreground`, `bg-muted`, `--canvas`), never hex values, in chrome. Content drawn on the board must be visible on both the light and the dark canvas, so check starter shapes and text in both. The element factories' default ink is safe; a hard-coded navy title is invisible on dark.
|
|
141
|
+
- **Changing numbers** (zoom, coordinates, counts, timers) get `tabular-nums`, so digits don't jiggle as they update.
|
|
142
|
+
- **No layout shift:** reserve space for anything that loads or appears. Empty states teach: say what goes there and give the action that creates it, using `EmptyState`.
|
|
143
|
+
- **Keyboard:** every tool and action has a shortcut, listed in `ShortcutOverlay`. `DesignCanvas` already installs tool keys, undo, redo, delete, duplicate, select all and zoom, so don't register those again: they would run twice.
|
|
144
|
+
- **Labels:** every icon-only button has an `aria-label`. Leave focus rings visible and neutral.
|
|
145
|
+
- **First paint:** restore saved state before the first render. Defaulting first and correcting afterwards flashes the wrong state.
|
|
146
|
+
|
|
147
|
+
### Before you say it's done
|
|
148
|
+
|
|
149
|
+
1. `npm run build` passes and the console is clean.
|
|
150
|
+
2. Open it in light and dark and look at it. Starter content, text and borders must be readable in both.
|
|
151
|
+
3. Make each kind of edit, then undo and redo it: move, resize, create, delete, property changes, reorder. Pan or zoom after an edit and undo again; the view must stay put.
|
|
152
|
+
4. Tab through the controls, and use every shortcut you listed.
|
|
153
|
+
5. Reload. The board and the zoom come back.
|
|
154
|
+
6. Click-create each tool without dragging, with a long name and with many layers.
|
|
135
155
|
|
|
136
156
|
## Component reference skills
|
|
137
157
|
|
|
@@ -156,6 +156,11 @@ Core geometry functions.
|
|
|
156
156
|
| `screenToWorld(point, camera)` / `worldToScreen(point, camera)` | Coordinate transforms |
|
|
157
157
|
| `clamp(value, min, max)` | Numeric clamp |
|
|
158
158
|
| `distance(a, b)` | Euclidean distance |
|
|
159
|
+
| `angleBetween(from, to)` / `normalizeAngle(deg)` / `snapAngle(deg, step)` | Angle of a line, wrapped to [-180, 180), snapped to a step. Degrees, positive clockwise on screen |
|
|
160
|
+
| `rotatedCorners(bounds, rotation)` / `rotatedBounds(bounds, rotation)` | The corners of a rotated box, and the axis-aligned box that contains it (for marquee, fit and alignment) |
|
|
161
|
+
| `handlePosition(handle, bounds, rotation)` | Where a resize handle, or `"rotate"`, sits on a rotated box |
|
|
162
|
+
| `resizeBounds(start, handle, delta, rotation)` | Resize by dragging a handle. The opposite handle stays fixed, also when rotated |
|
|
163
|
+
| `HANDLE_SIZE`, `ROTATE_HANDLE_OFFSET`, `HANDLES` | Handle geometry shared by the renderer and hit-test |
|
|
159
164
|
|
|
160
165
|
---
|
|
161
166
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
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
|
|
3
|
+
description: "Substrate input and selection primitives and config-driven panels: Controls and useControls (describe parameters as an object, get a tuning panel), the controls timeline for tuning animation clips, number input, slider, number slider, select, toggle, text area, colour picker, easing curve editor, spring editor, XY pad, image picker, swatch palette, and action controls. Use when building property editors, parameter or settings panels, tuning values by eye, or any interactive control surface. Triggers on: useControls, Controls, controls panel, tweak values, tuning, parameters, input, slider, select, toggle, colour picker, spring, easing, XY pad, swatch, action controls, property editor, form control, useControlsTimeline."
|
|
4
4
|
user-invocable: true
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -61,6 +61,61 @@ The same pattern is used by **Toolbar** (compaction) and **PaneHeader** (auto-po
|
|
|
61
61
|
|
|
62
62
|
## Components in this domain
|
|
63
63
|
|
|
64
|
+
### Controls and useControls
|
|
65
|
+
|
|
66
|
+
`registry/substrate/components/controls.tsx`, `registry/substrate/hooks/use-controls.ts`
|
|
67
|
+
|
|
68
|
+
Config-driven panels. Describe parameters as an object, get typed values back, and render every panel with `<Controls />`. Built on DialKit's headless store; the rows are the Substrate controls below.
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
const chart = useControls("Chart", {
|
|
72
|
+
months: [12, 3, 12, 1], // [default, min, max, step]
|
|
73
|
+
grid: true,
|
|
74
|
+
colour: "#a78bfa",
|
|
75
|
+
metric: { type: "select", options: ["revenue", "users"] },
|
|
76
|
+
style: { strokeWidth: [2, 1, 6] }, // nested object = collapsible folder
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
<Panel>
|
|
80
|
+
<Controls />
|
|
81
|
+
</Panel>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Config types**: number tuple or bare number (NumberSlider), boolean (Toggle), string (TextArea), hex string (ColourPicker), `{ type: "select" }`, `{ type: "easing" }` (EasingCurveEditor), `{ type: "spring" }` with `visualDuration`/`bounce` or `stiffness`/`damping`/`mass` (SpringEditor; easing and spring share a row with a type switch), `{ type: "image", options }` (ImagePicker, uploads become data URLs), `{ type: "pad", x, y }` with slider-style axis tuples (XYPad, returns `{ x, y }`, positive y is up), `{ type: "action" }` with the `onAction` option, nested object (folder).
|
|
85
|
+
|
|
86
|
+
**Options**: `id`, `persist`, `onAction`, `shortcuts` (by control path, e.g. `{ radius: { key: "r" }, visible: { key: "v" } }`: hold a slider's key and scroll, or `interaction: "drag"`/`"move"`; press a toggle's key). **Controls prop**: `panel` to render a single panel by name. Each panel header has a versions menu (DialKit presets), a copy button that copies the values as an agent instruction, and reset.
|
|
87
|
+
|
|
88
|
+
**Setting values from code**: `useControlsController` returns `{ values, setValues, reset }` for loading saved state or presets.
|
|
89
|
+
|
|
90
|
+
**Values that belong to a selection**: mount a small component with `key={selected.id}` that seeds its config from the selected item (hold it in `useState` so it is built once) and writes `useControls` values back in an effect. Only do this when nothing else edits those values while the item is selected.
|
|
91
|
+
|
|
92
|
+
**When to use**: The default for any panel of tunable parameters with local state – chart settings, generation parameters, material properties. Compose the individual controls by hand instead when values live in a store with selection and undo (as in the design editor's property panes), or when a row needs a custom layout.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### ControlsTimeline and useControlsTimeline
|
|
97
|
+
|
|
98
|
+
`registry/substrate/components/controls-timeline.tsx`, `registry/substrate/hooks/use-controls-timeline.ts`
|
|
99
|
+
|
|
100
|
+
Animation clips defined in code, tuned in a timeline editor. Built on DialKit's headless timeline. Bind `timeline.<clip>.current` to style so the UI follows the playhead; `<ControlsTimeline />` scrubs, moves and resizes clips and edits their values in a popover.
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
const timeline = useControlsTimeline("Card", {
|
|
104
|
+
enter: { at: 0, duration: 0.6, from: { y: 24, opacity: 0 }, to: { y: 0, opacity: 1 }, transition: { type: "spring", visualDuration: 0.5, bounce: 0.2 } },
|
|
105
|
+
badge: { at: 0.6, from: { scale: 0 }, steps: [{ duration: 0.2, to: { scale: 1.2 } }, { duration: 0.2, to: { scale: 1 } }] },
|
|
106
|
+
glow: { at: 1, props: { blur: { from: 0, to: 24, duration: 0.6 } } },
|
|
107
|
+
}, { loop: true })
|
|
108
|
+
|
|
109
|
+
<div style={{ opacity: timeline.enter.current.opacity }} />
|
|
110
|
+
<ControlsTimeline />
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Options**: `id`, `persist`, `autoplay` (default true), `loop` (`true` or `{ from }`). **Controls**: `play`, `pause`, `replay`, `seek`.
|
|
114
|
+
|
|
115
|
+
**When to use**: tuning entrance and choreography timing by eye. Once tuned, copy the values and move them into the production animation.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
64
119
|
### NumberInput
|
|
65
120
|
|
|
66
121
|
`registry/substrate/components/number-input.tsx`
|
|
@@ -131,7 +186,7 @@ Auto-resizing textarea with `minRows` / `maxRows`. Shares border/bg styling with
|
|
|
131
186
|
|
|
132
187
|
`registry/substrate/components/colour-picker.tsx`
|
|
133
188
|
|
|
134
|
-
|
|
189
|
+
Colour picker with a hex input. `value` accepts any CSS colour (hex, `oklch()`, `rgb()`, named) and `onChange` returns hex; `onStart` fires when a drag or edit begins, for undo snapshots. The swatch trigger renders `data-slot="colour-swatch"` for slot-aware composition.
|
|
135
190
|
|
|
136
191
|
**When to use**: Fill, stroke, background, text colour – any colour property.
|
|
137
192
|
|