@substrate-kit/cli 0.1.1 → 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/.claude-plugin/plugin.json +1 -1
- package/LICENSE +21 -0
- package/README.md +25 -8
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -60
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts +1 -0
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +51 -106
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/docs.d.ts +4 -2
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +33 -54
- package/dist/commands/docs.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +158 -122
- package/dist/commands/init.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +8 -7
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/get-component.d.ts +1 -1
- package/dist/mcp/tools/get-component.d.ts.map +1 -1
- package/dist/mcp/tools/get-component.js +10 -6
- package/dist/mcp/tools/get-component.js.map +1 -1
- package/dist/mcp/tools/get-docs.d.ts +17 -0
- package/dist/mcp/tools/get-docs.d.ts.map +1 -0
- package/dist/mcp/tools/get-docs.js +23 -0
- package/dist/mcp/tools/get-docs.js.map +1 -0
- package/dist/mcp/tools/install.d.ts +2 -1
- package/dist/mcp/tools/install.d.ts.map +1 -1
- package/dist/mcp/tools/install.js +19 -30
- package/dist/mcp/tools/install.js.map +1 -1
- package/dist/mcp/tools/search.d.ts +1 -1
- package/dist/mcp/tools/search.d.ts.map +1 -1
- package/dist/mcp/tools/search.js +2 -4
- package/dist/mcp/tools/search.js.map +1 -1
- package/dist/{mcp/types.d.ts → registry.d.ts} +6 -1
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +48 -0
- package/dist/registry.js.map +1 -0
- package/dist/utils/agents-file.d.ts +4 -0
- package/dist/utils/agents-file.d.ts.map +1 -0
- package/dist/utils/agents-file.js +36 -0
- package/dist/utils/agents-file.js.map +1 -0
- package/dist/utils/docs.d.ts +7 -0
- package/dist/utils/docs.d.ts.map +1 -0
- package/dist/utils/docs.js +28 -0
- package/dist/utils/docs.js.map +1 -0
- package/dist/utils/shadcn.d.ts +12 -0
- package/dist/utils/shadcn.d.ts.map +1 -0
- package/dist/utils/shadcn.js +29 -0
- package/dist/utils/shadcn.js.map +1 -0
- package/dist/utils/version.d.ts +3 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +8 -0
- package/dist/utils/version.js.map +1 -0
- package/docs/components/action.mdx +30 -0
- package/docs/components/alignment-controls.mdx +30 -0
- package/docs/components/badge.mdx +22 -0
- package/docs/components/breadcrumbs.mdx +25 -0
- package/docs/components/collapsible-pane.mdx +35 -0
- package/docs/components/colour-picker.mdx +66 -0
- package/docs/components/command-palette.mdx +36 -0
- package/docs/components/context-menu.mdx +31 -0
- package/docs/components/controls-timeline.mdx +107 -0
- package/docs/components/controls.mdx +168 -0
- package/docs/components/design-canvas.mdx +51 -0
- package/docs/components/document-tabs.mdx +25 -0
- package/docs/components/easing-curve-editor.mdx +85 -0
- package/docs/components/empty-state.mdx +26 -0
- package/docs/components/gallery.mdx +60 -0
- package/docs/components/history-controls.mdx +47 -0
- package/docs/components/image-picker.mdx +50 -0
- package/docs/components/layer-list.mdx +36 -0
- package/docs/components/node.mdx +85 -0
- package/docs/components/number-input.mdx +44 -0
- package/docs/components/number-slider.mdx +32 -0
- package/docs/components/pane.mdx +39 -0
- package/docs/components/panel.mdx +28 -0
- package/docs/components/popover.mdx +32 -0
- package/docs/components/progress-bar.mdx +24 -0
- package/docs/components/resizer.mdx +24 -0
- package/docs/components/select.mdx +31 -0
- package/docs/components/shortcut-overlay.mdx +43 -0
- package/docs/components/slider.mdx +20 -0
- package/docs/components/spring-editor.mdx +85 -0
- package/docs/components/status-bar.mdx +42 -0
- package/docs/components/surface.mdx +47 -0
- package/docs/components/swatch-palette.mdx +36 -0
- package/docs/components/text-area.mdx +26 -0
- package/docs/components/timeline.mdx +96 -0
- package/docs/components/toast.mdx +42 -0
- package/docs/components/toggle-group.mdx +40 -0
- package/docs/components/toggle.mdx +27 -0
- package/docs/components/toolbar.mdx +24 -0
- package/docs/components/tree-list.mdx +46 -0
- package/docs/components/xy-pad.mdx +49 -0
- package/docs/hooks/use-clipboard.mdx +70 -0
- package/docs/hooks/use-drag-reorder.mdx +60 -0
- package/docs/hooks/use-selection.mdx +68 -0
- package/docs/hooks/use-undoable.mdx +83 -0
- package/docs/hooks/use-viewport.mdx +75 -0
- package/docs/utilities/bounding-box.mdx +76 -0
- package/docs/utilities/keyboard-shortcuts.mdx +64 -0
- package/docs/utilities/snap.mdx +73 -0
- package/package.json +6 -3
- package/registry.json +331 -16
- package/skills/3d-scene/SKILL.md +2 -1
- package/skills/animation/SKILL.md +2 -1
- package/skills/canvas-2d/SKILL.md +68 -6
- package/skills/composing-panels/SKILL.md +9 -12
- 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 +42 -22
- 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
- package/dist/commands/browse.d.ts +0 -2
- package/dist/commands/browse.d.ts.map +0 -1
- package/dist/commands/browse.js +0 -25
- package/dist/commands/browse.js.map +0 -1
- package/dist/commands/create.d.ts +0 -9
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js +0 -415
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/variables.d.ts +0 -4
- package/dist/commands/variables.d.ts.map +0 -1
- package/dist/commands/variables.js +0 -111
- package/dist/commands/variables.js.map +0 -1
- package/dist/mcp/registry.d.ts +0 -3
- package/dist/mcp/registry.d.ts.map +0 -1
- package/dist/mcp/registry.js +0 -43
- package/dist/mcp/registry.js.map +0 -1
- package/dist/mcp/tools/theme.d.ts +0 -10
- package/dist/mcp/tools/theme.d.ts.map +0 -1
- package/dist/mcp/tools/theme.js +0 -37
- package/dist/mcp/tools/theme.js.map +0 -1
- package/dist/mcp/types.d.ts.map +0 -1
- package/dist/mcp/types.js +0 -2
- package/dist/mcp/types.js.map +0 -1
|
@@ -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
|
|
@@ -70,15 +67,15 @@ Every playground follows the same layout pattern — surface in the centre, opti
|
|
|
70
67
|
```tsx
|
|
71
68
|
"use client"
|
|
72
69
|
|
|
73
|
-
import { Panel } from "@/
|
|
70
|
+
import { Panel } from "@/components/substrate/panel"
|
|
74
71
|
import {
|
|
75
72
|
Toolbar,
|
|
76
73
|
ToolbarToggleGroup,
|
|
77
74
|
ToolbarToggle,
|
|
78
75
|
ToolbarSeparator,
|
|
79
|
-
} from "@/
|
|
80
|
-
import { usePanelStore } from "@/
|
|
81
|
-
import { useToolStore } from "@/
|
|
76
|
+
} from "@/components/substrate/toolbar"
|
|
77
|
+
import { usePanelStore } from "@/stores/substrate/panel-store" // npx substrate-kit add panel-store
|
|
78
|
+
import { useToolStore } from "@/stores/substrate/tool-store"
|
|
82
79
|
|
|
83
80
|
export default function PlaygroundPage() {
|
|
84
81
|
const leftPanel = usePanelStore((s) => s.leftPanel)
|
|
@@ -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
|
|
|
@@ -45,7 +45,7 @@ function handleMove(id: string, x: number, y: number) {
|
|
|
45
45
|
}
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
**Connects to**: `
|
|
48
|
+
**Connects to**: `NumberInput.onStart` (push snapshot before scrub), `createKeyboardShortcuts` (⌘Z / ⌘⇧Z). The 2D canvas does not use this hook: `useDocumentStore` has its own history (`pushSnapshot`, `undo`, `redo`) that covers elements and their order, not the camera, so pan and zoom survive an undo. Call `pushSnapshot()` before every mutation you want to be undoable, including property edits.
|
|
49
49
|
|
|
50
50
|
---
|
|
51
51
|
|
|
@@ -139,7 +139,7 @@ formatShortcut({ key: "z", meta: true, shift: true }) // → "⌘+Shift+Z"
|
|
|
139
139
|
|
|
140
140
|
Undo/redo button pair. Renders `data-slot="history-controls"` for auto-positioning in PaneHeader and Toolbar.
|
|
141
141
|
|
|
142
|
-
**Props**: `
|
|
142
|
+
**Props**: `size` (`"sm"` or `"md"`). It takes no undo callbacks: it reads `past`/`future` and calls `undo`/`redo` on `useDocumentStore`, so it works with the canvas store out of the box. For any other undo stack (`useUndoable`), build your own pair of buttons from `canUndo`/`undo`/`canRedo`/`redo`.
|
|
143
143
|
|
|
144
144
|
**Typical placement**:
|
|
145
145
|
- Inside `PaneHeader` – auto-positioned to the right via `**:data-[slot=history-controls]:ml-auto`
|
|
@@ -157,7 +157,9 @@ Flat layer panel with selection, visibility, lock, rename, and reorder.
|
|
|
157
157
|
|
|
158
158
|
**Features**:
|
|
159
159
|
- `LayerItem` supports `selected`, `hovered`, `depth` props
|
|
160
|
-
- `LayerName` enables double-click inline rename
|
|
160
|
+
- `LayerName` takes the name as `children` and enables double-click inline rename via `onRename`
|
|
161
|
+
- `LayerVisibilityToggle` and `LayerLockToggle` are controlled: `visible`/`onVisibleChange` and `locked`/`onLockedChange`
|
|
162
|
+
- Sorting is not built in: spread `useDragReorder`'s `getDragItemProps` on each `LayerItem`
|
|
161
163
|
- `LayerActions` are hidden by default, visible on row hover
|
|
162
164
|
- `LayerVisibilityToggle` and `LayerLockToggle` use eye/lock icons
|
|
163
165
|
|
|
@@ -199,8 +201,8 @@ Hierarchical tree with expand/collapse, depth indentation, and icons.
|
|
|
199
201
|
│ → apply to store │
|
|
200
202
|
├─────────────────────────────────────────────────┤
|
|
201
203
|
│ HistoryControls (in PaneHeader or Toolbar) │
|
|
202
|
-
│ → reads
|
|
203
|
-
│ → calls
|
|
204
|
+
│ → reads past/future from useDocumentStore │
|
|
205
|
+
│ → calls the store's undo/redo │
|
|
204
206
|
├─────────────────────────────────────────────────┤
|
|
205
207
|
│ NumberInput.onStart │
|
|
206
208
|
│ → pushSnapshot before scrub begins │
|
|
@@ -231,7 +233,14 @@ Hierarchical tree with expand/collapse, depth indentation, and icons.
|
|
|
231
233
|
```tsx
|
|
232
234
|
function LayerPanel() {
|
|
233
235
|
const { selectedIds, select, isSelected } = useSelection({ items: elementIds })
|
|
234
|
-
const
|
|
236
|
+
const setElementOrder = useDocumentStore((s) => s.setElementOrder)
|
|
237
|
+
const { getDragItemProps } = useDragReorder({
|
|
238
|
+
items: elements,
|
|
239
|
+
onReorder: (next) => {
|
|
240
|
+
pushSnapshot()
|
|
241
|
+
setElementOrder(next.map((el) => el.id))
|
|
242
|
+
},
|
|
243
|
+
})
|
|
235
244
|
|
|
236
245
|
return (
|
|
237
246
|
<LayerList>
|
|
@@ -244,21 +253,21 @@ function LayerPanel() {
|
|
|
244
253
|
{...getDragItemProps(i)}
|
|
245
254
|
>
|
|
246
255
|
<LayerIcon colour={el.fill?.color} />
|
|
247
|
-
<LayerName
|
|
256
|
+
<LayerName onRename={(name) => updateElement(el.id, { name })}>{el.name}</LayerName>
|
|
248
257
|
<LayerActions>
|
|
249
258
|
<LayerVisibilityToggle
|
|
250
259
|
visible={el.visible}
|
|
251
|
-
|
|
260
|
+
onVisibleChange={(visible) => updateElement(el.id, { visible })}
|
|
252
261
|
/>
|
|
253
262
|
<LayerLockToggle
|
|
254
263
|
locked={el.locked}
|
|
255
|
-
|
|
264
|
+
onLockedChange={(locked) => updateElement(el.id, { locked })}
|
|
256
265
|
/>
|
|
257
266
|
</LayerActions>
|
|
258
267
|
</LayerItem>
|
|
259
268
|
</ContextMenuTrigger>
|
|
260
269
|
<ContextMenuContent>
|
|
261
|
-
<ContextMenuItem onSelect={() =>
|
|
270
|
+
<ContextMenuItem onSelect={() => duplicateElements([el.id])}>
|
|
262
271
|
Duplicate <ContextMenuShortcut>⌘D</ContextMenuShortcut>
|
|
263
272
|
</ContextMenuItem>
|
|
264
273
|
<ContextMenuItem onSelect={() => deleteElement(el.id)}>
|
|
@@ -39,9 +39,9 @@ Floating bottom bar. Renders tool buttons, separators, and groups. Uses `data-sl
|
|
|
39
39
|
| `slider` / `slider-track` | h-6 |
|
|
40
40
|
| `history-controls` | gap-0 |
|
|
41
41
|
|
|
42
|
-
**Exports**: `Toolbar`, `
|
|
42
|
+
**Exports**: `Toolbar`, `ToolbarToggleGroup`, `ToolbarToggle`, `ToolbarSeparator`, `ToolbarDropdown`
|
|
43
43
|
|
|
44
|
-
**Typical use**: One per playground, positioned at the bottom.
|
|
44
|
+
**Typical use**: One per playground, positioned at the bottom. Put the tool buttons in a `ToolbarToggleGroup` of `ToolbarToggle`s and connect it to `useToolStore` for active-tool state.
|
|
45
45
|
|
|
46
46
|
**Related**: substrate-controls (for inputs inside the toolbar), substrate-interaction (for keyboard shortcuts bound to tools)
|
|
47
47
|
|
|
@@ -181,11 +181,11 @@ Breadcrumbs show the path into nested node groups. Right panel shows properties
|
|
|
181
181
|
<div className="flex h-screen flex-col">
|
|
182
182
|
<DocumentTabList>{/* tabs */}</DocumentTabList>
|
|
183
183
|
<div className="flex flex-1 overflow-hidden">
|
|
184
|
-
<Panel
|
|
184
|
+
<Panel position="left">{/* layers/tree */}</Panel>
|
|
185
185
|
<div className="relative flex-1">
|
|
186
186
|
<Surface />{/* central area */}
|
|
187
187
|
</div>
|
|
188
|
-
<Panel
|
|
188
|
+
<Panel position="right">{/* property panes */}</Panel>
|
|
189
189
|
</div>
|
|
190
190
|
<StatusBar>{/* metadata */}</StatusBar>
|
|
191
191
|
<Toolbar>{/* tools */}</Toolbar>
|
package/skills/theming/SKILL.md
CHANGED
|
@@ -113,9 +113,15 @@ const CURSOR_MAP: Record<string, string> = {
|
|
|
113
113
|
}
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
## 5.
|
|
116
|
+
## 5. Light and dark mode
|
|
117
117
|
|
|
118
|
-
Substrate
|
|
118
|
+
Every Substrate component works in both themes. Colours come from tokens, and the theme is chosen by a `.dark` class on `<html>`: light values live in `:root`, dark values in `.dark`.
|
|
119
|
+
|
|
120
|
+
Projects made with `create-substrate` follow the system theme through a small script in `index.html`. To force a theme, remove that script and either set `class="dark"` on `<html>` or leave it off. To add a switch, toggle that class yourself (or use `next-themes` in a Next.js app).
|
|
121
|
+
|
|
122
|
+
Canvas-drawn parts follow the theme too: the design canvas reads `--canvas` and `--canvas-foreground`, the easing curve editor draws in its text colour, and both repaint when the theme changes. In your own canvas code, read colours with `getComputedStyle` at draw time instead of hardcoding them, and use mid-tones for defaults that must read on both backgrounds.
|
|
123
|
+
|
|
124
|
+
When writing a component, use tokens (`bg-panel`, `text-muted-foreground`, `bg-foreground/10`) and check it in both themes. Avoid `bg-background` for a control's track or well: it matches the page in light mode and disappears.
|
|
119
125
|
|
|
120
126
|
## Related reference skills
|
|
121
127
|
|
|
@@ -39,6 +39,7 @@ Keyboard events (DOM)
|
|
|
39
39
|
| `creating` | Drag with shape tool on empty canvas | Preview element dimensions | Commit new element to store |
|
|
40
40
|
| `moving` | Drag on selected element | Update element positions | Commit positions |
|
|
41
41
|
| `resizing` | Drag on resize handle | Update element bounds | Commit bounds |
|
|
42
|
+
| `rotating` | Drag on the rotate handle above a single selection | Turn the element about its centre (hold Shift to snap to 15°) | Commit rotation |
|
|
42
43
|
| `marquee_selecting` | Drag on empty canvas with select tool | Draw selection rectangle | Select contained elements |
|
|
43
44
|
|
|
44
45
|
### Pointer down logic
|
|
@@ -66,7 +67,8 @@ Keyboard events (DOM)
|
|
|
66
67
|
- "pan": update camera
|
|
67
68
|
- "create": preview element (stored on drag ref, rendered by renderer)
|
|
68
69
|
- "move": update element positions (delta from start)
|
|
69
|
-
- "resize": update element bounds
|
|
70
|
+
- "resize": update element bounds with `resizeBounds`, which respects the handle and the element's rotation
|
|
71
|
+
- "rotate": set `rotation` from the pointer's angle around the element centre
|
|
70
72
|
- "marquee": update selection rectangle
|
|
71
73
|
5. Call markDirty() to trigger repaint
|
|
72
74
|
```
|
|
@@ -77,7 +79,7 @@ Keyboard events (DOM)
|
|
|
77
79
|
1. End drag (useCanvasDrag.endDrag)
|
|
78
80
|
2. Based on drag type:
|
|
79
81
|
- "create": push snapshot, add element to store, select it, reset tool to select
|
|
80
|
-
- "move"/"resize": push snapshot
|
|
82
|
+
- "move"/"resize"/"rotate": nothing to push; the snapshot was taken when the drag first moved, so a click without a drag leaves no undo step
|
|
81
83
|
- "marquee": select all elements within the marquee bounds
|
|
82
84
|
- "pan": nothing to commit
|
|
83
85
|
```
|
|
@@ -104,12 +106,13 @@ Always convert pointer event coordinates to world space before doing hit-testing
|
|
|
104
106
|
|
|
105
107
|
```ts
|
|
106
108
|
interface DragInfo {
|
|
107
|
-
type: DragType // "create" | "move" | "resize" | "marquee" | "pan"
|
|
109
|
+
type: DragType // "create" | "move" | "resize" | "rotate" | "marquee" | "pan"
|
|
108
110
|
startWorld: Point // Where the drag began (world space)
|
|
109
111
|
startScreen: Point // Where the drag began (screen space)
|
|
110
112
|
currentWorld: Point // Current position (updated on every move)
|
|
111
113
|
currentScreen: Point
|
|
112
114
|
handle?: HandlePosition // Which resize handle, if resizing
|
|
115
|
+
rotate?: { id, center, startAngle, startRotation } // Set for a rotate drag
|
|
113
116
|
elementStartPositions?: Record<string, { x, y, width, height }> // Snapshot for move/resize
|
|
114
117
|
}
|
|
115
118
|
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Substrate
|
|
2
|
+
|
|
3
|
+
This project uses [Substrate](https://substrate.georgedrury.co.uk), a set of primitives for building creative tools: scaffold (toolbar, panels, panes, shortcuts), canvas and node helpers, and config-driven controls.
|
|
4
|
+
|
|
5
|
+
## Use Substrate components first
|
|
6
|
+
|
|
7
|
+
Components are installed into the project with the Substrate CLI, which wraps shadcn. Never run `npx shadcn@latest add` for them directly.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx substrate-kit add panel slider toolbar # install by name, in one call
|
|
11
|
+
npx substrate-kit add # interactive picker
|
|
12
|
+
npx substrate-kit docs <component> # props, CSS variables, usage
|
|
13
|
+
npx substrate-kit docs <component> --open # open its docs page in the browser
|
|
14
|
+
npx substrate-kit docs --list # every component
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Before writing a custom slider, input, panel, timeline or colour picker, check `docs --list`. If the Substrate MCP server is connected, use `search_components`, `get_component` and `get_docs` instead of the commands above, and `install_components` to install.
|
|
18
|
+
|
|
19
|
+
## Tune values with Controls
|
|
20
|
+
|
|
21
|
+
For parameters you want to adjust live (sizes, colours, springs, toggles), describe them as an object and render them with `Controls` instead of building a form:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
const card = useControls("Card", {
|
|
25
|
+
radius: [16, 0, 48], // [default, min, max, step?] slider
|
|
26
|
+
colour: "#a78bfa", // colour picker
|
|
27
|
+
visible: true, // toggle
|
|
28
|
+
motion: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
|
|
29
|
+
})
|
|
30
|
+
// <Panel><Controls /></Panel> renders every registered panel
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The returned values keep the shape of the config. Panels have versions, reset, keyboard shortcuts (`shortcuts` option) and a copy button that gives an agent the tuned values to paste back in as the new defaults. Install with `npx substrate-kit add controls`, and read `docs controls` for every control type and option. Controls and the timeline are built on [DialKit](https://github.com/joshpuckett/dialkit) (MIT, by Josh Puckett), which installs as the `dialkit` dependency.
|
|
34
|
+
|
|
35
|
+
## Tune animation with the timeline
|
|
36
|
+
|
|
37
|
+
For motion, define clips with `useControlsTimeline` and bind `clip.current` to your UI. `ControlsTimeline` is the editor: scrub, move and resize clips, edit curves, and copy the tuned timing back as defaults. Install with `npx substrate-kit add controls-timeline`, and read `docs controls-timeline` for clip types, looping and zoom.
|
|
38
|
+
|
|
39
|
+
## Skills
|
|
40
|
+
|
|
41
|
+
Substrate skills are in `.claude/skills/` and `.agents/skills/`. Surface guides explain how to build each kind of tool (`scaffold-playground`, `canvas-2d`, `3d-scene`, `animation`, `image-generation`, `data-visualisation`) and workflows (`composing-panels`, `wire-interactions`, `create-custom-tool`, `theming`). Component references (`substrate-scaffold`, `substrate-controls`, `substrate-canvas`, `substrate-nodes`, `substrate-feedback`, `substrate-interaction`) say what to use. `install-components` lists everything available.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"browse.d.ts","sourceRoot":"","sources":["../../src/commands/browse.ts"],"names":[],"mappings":"AAKA,wBAAgB,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAiB/C"}
|
package/dist/commands/browse.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
import { log } from "../utils/logger.js";
|
|
3
|
-
const DOCS_BASE = "https://substrate.georgedrury.co.uk/docs/components";
|
|
4
|
-
export function browse(component) {
|
|
5
|
-
const url = component
|
|
6
|
-
? `${DOCS_BASE}/${component}`
|
|
7
|
-
: "https://substrate.georgedrury.co.uk/docs";
|
|
8
|
-
log.info(`Opening ${url}`);
|
|
9
|
-
try {
|
|
10
|
-
const platform = process.platform;
|
|
11
|
-
if (platform === "darwin") {
|
|
12
|
-
execSync(`open "${url}"`);
|
|
13
|
-
}
|
|
14
|
-
else if (platform === "win32") {
|
|
15
|
-
execSync(`start "${url}"`);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
execSync(`xdg-open "${url}"`);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
log.error(`Could not open browser. Visit: ${url}`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=browse.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"browse.js","sourceRoot":"","sources":["../../src/commands/browse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAExC,MAAM,SAAS,GAAG,qDAAqD,CAAA;AAEvE,MAAM,UAAU,MAAM,CAAC,SAAkB;IACxC,MAAM,GAAG,GAAG,SAAS;QACpB,CAAC,CAAC,GAAG,SAAS,IAAI,SAAS,EAAE;QAC7B,CAAC,CAAC,0CAA0C,CAAA;IAC7C,GAAG,CAAC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC,CAAA;IAC1B,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;QACjC,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC3B,QAAQ,CAAC,SAAS,GAAG,GAAG,CAAC,CAAA;QAC1B,CAAC;aAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAC,CAAA;QAC3B,CAAC;aAAM,CAAC;YACP,QAAQ,CAAC,aAAa,GAAG,GAAG,CAAC,CAAA;QAC9B,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,GAAG,CAAC,KAAK,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAA;IACnD,CAAC;AACF,CAAC"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type PackageManager } from "../utils/detect-pm.js";
|
|
2
|
-
export interface CreateOptions {
|
|
3
|
-
surface?: string;
|
|
4
|
-
skipInstall?: boolean;
|
|
5
|
-
skipGit?: boolean;
|
|
6
|
-
packageManager?: PackageManager;
|
|
7
|
-
}
|
|
8
|
-
export declare function create(projectName: string, options?: CreateOptions): Promise<void>;
|
|
9
|
-
//# sourceMappingURL=create.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAKA,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,uBAAuB,CAAA;AA4B9B,MAAM,WAAW,aAAa;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,cAAc,CAAC,EAAE,cAAc,CAAA;CAC/B;AAED,wBAAsB,MAAM,CAC3B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,aAAkB,GACzB,OAAO,CAAC,IAAI,CAAC,CAoGf"}
|