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,117 @@
|
|
|
1
|
+
# Theming Substrate
|
|
2
|
+
|
|
3
|
+
Guide to customising the visual appearance of Substrate's canvas, UI chrome, and elements.
|
|
4
|
+
|
|
5
|
+
## How theming works
|
|
6
|
+
|
|
7
|
+
Substrate uses a CSS variable fallback chain (Tailwind v4) so you can override specific tokens without redefining everything. The pattern is:
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
/* @theme inline block */
|
|
11
|
+
--color-canvas: var(--canvas, var(--color-background));
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Set `--canvas` to override the canvas background; if unset, it falls back to `--color-background`.
|
|
15
|
+
|
|
16
|
+
## 1. Substrate tokens
|
|
17
|
+
|
|
18
|
+
Substrate defines its own semantic tokens that fall back to standard shadcn tokens:
|
|
19
|
+
|
|
20
|
+
| Tailwind class | CSS variable | Fallback | Used by |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| `bg-canvas` | `--color-canvas` → `--canvas` | `--color-background` | Canvas background |
|
|
23
|
+
| `text-canvas-foreground` | `--color-canvas-foreground` → `--canvas-foreground` | `--color-foreground-muted` | Canvas grid dots |
|
|
24
|
+
| `bg-panel` | `--color-panel` → `--panel` | `--color-card` | Panel background |
|
|
25
|
+
| `text-panel-foreground` | `--color-panel-foreground` → `--panel-foreground` | `--color-card-foreground` | Panel text |
|
|
26
|
+
|
|
27
|
+
Override the short-form variables in your `:root` and `.dark` blocks:
|
|
28
|
+
|
|
29
|
+
```css
|
|
30
|
+
:root {
|
|
31
|
+
/* Canvas */
|
|
32
|
+
--canvas: oklch(.97 0.0 0);
|
|
33
|
+
--canvas-foreground: oklch(0.556 0 0);
|
|
34
|
+
/* Panels */
|
|
35
|
+
--panel: oklch(1 0 0);
|
|
36
|
+
--panel-foreground: oklch(0.145 0 0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.dark {
|
|
40
|
+
--canvas: oklch(17.764% 0.00002 271.152);
|
|
41
|
+
--canvas-foreground: oklch(0.708 0 0);
|
|
42
|
+
--panel: oklch(0.205 0 0);
|
|
43
|
+
--panel-foreground: oklch(0.985 0 0);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 2. Standard shadcn tokens
|
|
48
|
+
|
|
49
|
+
The toolbar, slider, and tooltip still use standard shadcn tokens:
|
|
50
|
+
|
|
51
|
+
### Key tokens per component
|
|
52
|
+
|
|
53
|
+
- **Toolbar** — `bg-background`, `border`, `shadow-lg` for the container; `hover:bg-muted`, `aria-checked:bg-accent` for toggles
|
|
54
|
+
- **Panel** — `bg-panel`, `text-panel-foreground`, `border-border`
|
|
55
|
+
- **Pane** — `bg-panel` for action buttons, inherits `panel-foreground` for text
|
|
56
|
+
- **Slider** — track uses `bg-accent`, thumb uses `bg-foreground`
|
|
57
|
+
- **Tooltip** — inverted: `bg-foreground`, `text-background`
|
|
58
|
+
|
|
59
|
+
## 3. Canvas rendering
|
|
60
|
+
|
|
61
|
+
The canvas background is now driven by the `--canvas` CSS variable, read at render time via `getComputedStyle`. This means it automatically responds to light/dark mode and custom themes.
|
|
62
|
+
|
|
63
|
+
Grid dots, selection UI, and other renderer constants are still hardcoded in `canvas/renderer.ts`:
|
|
64
|
+
|
|
65
|
+
### Grid
|
|
66
|
+
|
|
67
|
+
Look for the grid rendering function. Key values:
|
|
68
|
+
|
|
69
|
+
- Grid line colour (default: light grey)
|
|
70
|
+
- Grid spacing (default: 20px world units)
|
|
71
|
+
- Major grid line interval
|
|
72
|
+
|
|
73
|
+
### Selection UI
|
|
74
|
+
|
|
75
|
+
- Selection box stroke colour (default: blue `#3b82f6`)
|
|
76
|
+
- Resize handle fill/stroke
|
|
77
|
+
- Marquee selection fill (semi-transparent blue)
|
|
78
|
+
|
|
79
|
+
### Element defaults
|
|
80
|
+
|
|
81
|
+
New element colours are set in `lib/element-factory.ts`. Each factory function defines default `fill` and `stroke` values.
|
|
82
|
+
|
|
83
|
+
## 3. Component-level overrides with `cn()`
|
|
84
|
+
|
|
85
|
+
Every Substrate component accepts `className` and spreads it via `cn()`. You can override specific styles per-instance:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<Toolbar className="rounded-lg bg-zinc-900 border-zinc-800">
|
|
89
|
+
{/* Dark toolbar variant */}
|
|
90
|
+
</Toolbar>
|
|
91
|
+
|
|
92
|
+
<Panel position="left" className="w-72 bg-zinc-950">
|
|
93
|
+
{/* Wider, darker panel */}
|
|
94
|
+
</Panel>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 4. Cursor customisation
|
|
98
|
+
|
|
99
|
+
Cursors are mapped in `design-canvas.tsx`'s `CURSOR_MAP`. You can swap these for custom cursor URLs:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const CURSOR_MAP: Record<string, string> = {
|
|
103
|
+
select: "default",
|
|
104
|
+
hand: "grab",
|
|
105
|
+
rectangle: "url(/cursors/crosshair.svg) 12 12, crosshair",
|
|
106
|
+
// ...
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 5. Dark mode
|
|
111
|
+
|
|
112
|
+
Substrate UI components automatically respect dark mode when using `next-themes` or a `.dark` class on the document. The canvas renderer needs manual dark mode handling — check a CSS variable or a store flag and swap your colour constants accordingly.
|
|
113
|
+
|
|
114
|
+
## Related reference skills
|
|
115
|
+
|
|
116
|
+
- **substrate-nodes** — port type CSS variables (7 port colours + 4 node chrome vars) follow the same fallback chain
|
|
117
|
+
- **substrate-controls** — ColourPicker, SwatchPalette for colour selection in property panes
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Wire interactions
|
|
2
|
+
|
|
3
|
+
Guide to connecting canvas pointer events, drag state, keyboard shortcuts, and the interaction state machine in Substrate.
|
|
4
|
+
|
|
5
|
+
## Architecture overview
|
|
6
|
+
|
|
7
|
+
Substrate's interaction system is a layered pipeline:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Pointer events (DOM)
|
|
11
|
+
→ useCanvasInteraction (state machine)
|
|
12
|
+
→ useCanvasDrag (drag tracking)
|
|
13
|
+
→ useCanvasTransform (coordinate conversion)
|
|
14
|
+
→ useHitTest (what's under the cursor)
|
|
15
|
+
→ useDocumentStore (mutations)
|
|
16
|
+
→ markDirty() → useCanvasRenderer (repaint)
|
|
17
|
+
|
|
18
|
+
Keyboard events (DOM)
|
|
19
|
+
→ useKeyboardShortcuts
|
|
20
|
+
→ useToolStore (tool switching)
|
|
21
|
+
→ useDocumentStore (delete, undo/redo, select all, duplicate)
|
|
22
|
+
→ useCanvasTransform (zoom)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## The interaction state machine
|
|
26
|
+
|
|
27
|
+
`useCanvasInteraction` is the central orchestrator. It manages an `InteractionMode`:
|
|
28
|
+
|
|
29
|
+
| Mode | Triggered by | During move | On release |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `idle` | Default state | Update hover, cursor | — |
|
|
32
|
+
| `panning` | Middle-click, or hand tool drag | Update camera via `setCamera` | Return to idle |
|
|
33
|
+
| `creating` | Drag with shape tool on empty canvas | Preview element dimensions | Commit new element to store |
|
|
34
|
+
| `moving` | Drag on selected element | Update element positions | Commit positions |
|
|
35
|
+
| `resizing` | Drag on resize handle | Update element bounds | Commit bounds |
|
|
36
|
+
| `marquee_selecting` | Drag on empty canvas with select tool | Draw selection rectangle | Select contained elements |
|
|
37
|
+
|
|
38
|
+
### Pointer down logic
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
1. Convert screen point → world point (useCanvasTransform)
|
|
42
|
+
2. Check for resize handle hit (useHitTest)
|
|
43
|
+
→ Yes: start "resizing" drag
|
|
44
|
+
3. Check for element hit (useHitTest)
|
|
45
|
+
→ Yes + already selected: start "moving" drag
|
|
46
|
+
→ Yes + not selected: select it, start "moving" drag
|
|
47
|
+
→ Yes + shift held: toggle selection
|
|
48
|
+
4. No hit + shape tool active: start "creating" drag
|
|
49
|
+
5. No hit + select tool: start "marquee_selecting" drag
|
|
50
|
+
6. Middle button or hand tool: start "panning" drag
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Pointer move logic
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
1. If not dragging: update hover state via hit-test, return
|
|
57
|
+
2. Convert to world coordinates
|
|
58
|
+
3. Update drag state (useCanvasDrag.updateDrag)
|
|
59
|
+
4. Based on drag type:
|
|
60
|
+
- "pan": update camera
|
|
61
|
+
- "create": preview element (stored on drag ref, rendered by renderer)
|
|
62
|
+
- "move": update element positions (delta from start)
|
|
63
|
+
- "resize": update element bounds respecting handle position
|
|
64
|
+
- "marquee": update selection rectangle
|
|
65
|
+
5. Call markDirty() to trigger repaint
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Pointer up logic
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
1. End drag (useCanvasDrag.endDrag)
|
|
72
|
+
2. Based on drag type:
|
|
73
|
+
- "create": push snapshot, add element to store, select it, reset tool to select
|
|
74
|
+
- "move"/"resize": push snapshot (positions already updated during drag)
|
|
75
|
+
- "marquee": select all elements within the marquee bounds
|
|
76
|
+
- "pan": nothing to commit
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Coordinate system
|
|
80
|
+
|
|
81
|
+
Substrate uses two coordinate systems:
|
|
82
|
+
|
|
83
|
+
- **Screen space** — pixel coordinates relative to the canvas element
|
|
84
|
+
- **World space** — the infinite canvas coordinate system, affected by camera pan and zoom
|
|
85
|
+
|
|
86
|
+
`useCanvasTransform` provides:
|
|
87
|
+
|
|
88
|
+
- `screenToWorld(point)` — convert screen → world
|
|
89
|
+
- `worldToScreen(point)` — convert world → screen
|
|
90
|
+
- `pan(dx, dy)` — move camera
|
|
91
|
+
- `zoomToPoint(point, delta)` — zoom centred on a point
|
|
92
|
+
|
|
93
|
+
Always convert pointer event coordinates to world space before doing hit-testing or element manipulation.
|
|
94
|
+
|
|
95
|
+
## The drag state machine
|
|
96
|
+
|
|
97
|
+
`useCanvasDrag` tracks the current drag operation via a ref (not React state — avoids re-renders during high-frequency pointer moves):
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
interface DragInfo {
|
|
101
|
+
type: DragType // "create" | "move" | "resize" | "marquee" | "pan"
|
|
102
|
+
startWorld: Point // Where the drag began (world space)
|
|
103
|
+
startScreen: Point // Where the drag began (screen space)
|
|
104
|
+
currentWorld: Point // Current position (updated on every move)
|
|
105
|
+
currentScreen: Point
|
|
106
|
+
handle?: HandlePosition // Which resize handle, if resizing
|
|
107
|
+
elementStartPositions?: Record<string, { x, y, width, height }> // Snapshot for move/resize
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Adding a custom interaction
|
|
112
|
+
|
|
113
|
+
To add a new interaction pattern:
|
|
114
|
+
|
|
115
|
+
1. **Define the mode** — add to `InteractionMode` in `types.ts` if it's a new state
|
|
116
|
+
2. **Entry condition** — add detection in the pointer down handler
|
|
117
|
+
3. **During drag** — add a case in pointer move
|
|
118
|
+
4. **Completion** — add a case in pointer up
|
|
119
|
+
5. **Always call `markDirty()`** after any visual change
|
|
120
|
+
6. **Always call `pushSnapshot()`** before committing mutations (for undo/redo)
|
|
121
|
+
|
|
122
|
+
## Keyboard shortcuts
|
|
123
|
+
|
|
124
|
+
`useKeyboardShortcuts` listens globally on `window`. It:
|
|
125
|
+
|
|
126
|
+
- Skips events when an input/textarea is focused
|
|
127
|
+
- Supports modifier combinations (`meta+key`, `shift+key`)
|
|
128
|
+
- Supports temporary tool switching (hold Space for hand tool, release to restore)
|
|
129
|
+
|
|
130
|
+
To add a shortcut:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// In the handleKeyDown callback
|
|
134
|
+
case "your-key": {
|
|
135
|
+
e.preventDefault()
|
|
136
|
+
// Your action
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For modifier combinations, check `meta` (which is `e.metaKey || e.ctrlKey` for cross-platform):
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
if (meta && e.key.toLowerCase() === "k") {
|
|
145
|
+
e.preventDefault()
|
|
146
|
+
// Cmd+K action
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Related reference skills
|
|
152
|
+
|
|
153
|
+
- **substrate-interaction** — full catalogue of useUndoable, useClipboard, createKeyboardShortcuts (declarative replacement for switch statements), and formatShortcut
|
|
154
|
+
- **substrate-canvas** — useViewport, useSelection, useDragReorder, snap, boundingBox
|
|
155
|
+
- **substrate-feedback** — CommandPalette (⌘K), ShortcutOverlay (?), ContextMenu, Toast (for delete-with-undo)
|