@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
package/registry.json
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"name": "math",
|
|
33
33
|
"type": "registry:lib",
|
|
34
34
|
"title": "Math",
|
|
35
|
-
"description": "Geometry helpers — rotate point, bounds checking, ellipse hit test, screen/world coordinate conversion, normalize bounds, distance, and
|
|
35
|
+
"description": "Geometry helpers — rotate point, bounds checking, ellipse hit test, screen/world coordinate conversion, normalize bounds, distance, clamping, plus rotation and handle maths: rotated bounds, snap angle, and resizing a rotated box.",
|
|
36
36
|
"author": "George Drury <info@georgedrury.co.uk>",
|
|
37
37
|
"categories": [
|
|
38
38
|
"substrate",
|
|
@@ -120,7 +120,8 @@
|
|
|
120
120
|
"zustand"
|
|
121
121
|
],
|
|
122
122
|
"registryDependencies": [
|
|
123
|
-
"https://substrate.georgedrury.co.uk/r/types"
|
|
123
|
+
"https://substrate.georgedrury.co.uk/r/types",
|
|
124
|
+
"https://substrate.georgedrury.co.uk/r/id"
|
|
124
125
|
],
|
|
125
126
|
"meta": {
|
|
126
127
|
"lineage": [
|
|
@@ -239,7 +240,8 @@
|
|
|
239
240
|
"canvas"
|
|
240
241
|
],
|
|
241
242
|
"registryDependencies": [
|
|
242
|
-
"https://substrate.georgedrury.co.uk/r/types"
|
|
243
|
+
"https://substrate.georgedrury.co.uk/r/types",
|
|
244
|
+
"https://substrate.georgedrury.co.uk/r/math"
|
|
243
245
|
],
|
|
244
246
|
"meta": {
|
|
245
247
|
"lineage": [
|
|
@@ -326,7 +328,8 @@
|
|
|
326
328
|
"registryDependencies": [
|
|
327
329
|
"https://substrate.georgedrury.co.uk/r/types",
|
|
328
330
|
"https://substrate.georgedrury.co.uk/r/math",
|
|
329
|
-
"https://substrate.georgedrury.co.uk/r/document-store"
|
|
331
|
+
"https://substrate.georgedrury.co.uk/r/document-store",
|
|
332
|
+
"https://substrate.georgedrury.co.uk/r/bounding-box"
|
|
330
333
|
],
|
|
331
334
|
"meta": {
|
|
332
335
|
"lineage": [
|
|
@@ -489,12 +492,14 @@
|
|
|
489
492
|
"https://substrate.georgedrury.co.uk/r/tool-store",
|
|
490
493
|
"https://substrate.georgedrury.co.uk/r/use-canvas-renderer",
|
|
491
494
|
"https://substrate.georgedrury.co.uk/r/use-canvas-interaction",
|
|
492
|
-
"https://substrate.georgedrury.co.uk/r/use-keyboard-shortcuts"
|
|
495
|
+
"https://substrate.georgedrury.co.uk/r/use-keyboard-shortcuts",
|
|
496
|
+
"https://substrate.georgedrury.co.uk/r/document-store",
|
|
497
|
+
"https://substrate.georgedrury.co.uk/r/types"
|
|
493
498
|
],
|
|
494
499
|
"cssVars": {
|
|
495
500
|
"theme": {
|
|
496
501
|
"color-canvas": "var(--canvas, var(--color-background))",
|
|
497
|
-
"color-canvas-foreground": "var(--canvas-foreground, var(--color-foreground
|
|
502
|
+
"color-canvas-foreground": "var(--canvas-foreground, var(--color-muted-foreground))"
|
|
498
503
|
},
|
|
499
504
|
"light": {
|
|
500
505
|
"canvas": "oklch(.97 0.0 0)",
|
|
@@ -699,7 +704,7 @@
|
|
|
699
704
|
"cssVars": {
|
|
700
705
|
"theme": {
|
|
701
706
|
"color-canvas": "var(--canvas, var(--color-background))",
|
|
702
|
-
"color-canvas-foreground": "var(--canvas-foreground, var(--color-foreground
|
|
707
|
+
"color-canvas-foreground": "var(--canvas-foreground, var(--color-muted-foreground))"
|
|
703
708
|
},
|
|
704
709
|
"light": {
|
|
705
710
|
"canvas": "oklch(.97 0.0 0)",
|
|
@@ -878,6 +883,9 @@
|
|
|
878
883
|
"type": "registry:component",
|
|
879
884
|
"target": "components/substrate/history-controls.tsx"
|
|
880
885
|
}
|
|
886
|
+
],
|
|
887
|
+
"registryDependencies": [
|
|
888
|
+
"https://substrate.georgedrury.co.uk/r/document-store"
|
|
881
889
|
]
|
|
882
890
|
},
|
|
883
891
|
{
|
|
@@ -1316,7 +1324,35 @@
|
|
|
1316
1324
|
"target": "components/substrate/collapsible-pane.tsx"
|
|
1317
1325
|
}
|
|
1318
1326
|
],
|
|
1319
|
-
"author": "George Drury <info@georgedrury.co.uk>"
|
|
1327
|
+
"author": "George Drury <info@georgedrury.co.uk>",
|
|
1328
|
+
"cssVars": {
|
|
1329
|
+
"theme": {
|
|
1330
|
+
"animate-collapsible-down": "collapsible-down 150ms ease-out",
|
|
1331
|
+
"animate-collapsible-up": "collapsible-up 150ms ease-out"
|
|
1332
|
+
}
|
|
1333
|
+
},
|
|
1334
|
+
"css": {
|
|
1335
|
+
"@keyframes collapsible-down": {
|
|
1336
|
+
"from": {
|
|
1337
|
+
"height": "0",
|
|
1338
|
+
"opacity": "0"
|
|
1339
|
+
},
|
|
1340
|
+
"to": {
|
|
1341
|
+
"height": "var(--radix-collapsible-content-height)",
|
|
1342
|
+
"opacity": "1"
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
"@keyframes collapsible-up": {
|
|
1346
|
+
"from": {
|
|
1347
|
+
"height": "var(--radix-collapsible-content-height)",
|
|
1348
|
+
"opacity": "1"
|
|
1349
|
+
},
|
|
1350
|
+
"to": {
|
|
1351
|
+
"height": "0",
|
|
1352
|
+
"opacity": "0"
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1320
1356
|
},
|
|
1321
1357
|
{
|
|
1322
1358
|
"name": "command-palette",
|
|
@@ -1449,7 +1485,7 @@
|
|
|
1449
1485
|
"name": "layer-list",
|
|
1450
1486
|
"type": "registry:ui",
|
|
1451
1487
|
"title": "Layer List",
|
|
1452
|
-
"description": "
|
|
1488
|
+
"description": "Layer panel with selection, visibility and lock toggles, icons and inline rename. Compound component pattern; pair with use-drag-reorder to sort and context-menu for right-click actions.",
|
|
1453
1489
|
"categories": [
|
|
1454
1490
|
"substrate",
|
|
1455
1491
|
"ui",
|
|
@@ -1493,7 +1529,11 @@
|
|
|
1493
1529
|
"color-node-port-geometry": "var(--node-port-geometry, oklch(0.7 0.15 200))",
|
|
1494
1530
|
"color-node-port-text": "var(--node-port-text, oklch(0.7 0.15 150))",
|
|
1495
1531
|
"color-node-port-boolean": "var(--node-port-boolean, oklch(0.7 0.15 30))",
|
|
1496
|
-
"color-node-port-any": "var(--node-port-any, oklch(0.6 0 0))"
|
|
1532
|
+
"color-node-port-any": "var(--node-port-any, oklch(0.6 0 0))",
|
|
1533
|
+
"color-node-bg": "var(--node-bg, var(--card))",
|
|
1534
|
+
"color-node-border": "var(--node-border, var(--border))",
|
|
1535
|
+
"color-node-header": "var(--node-header, var(--muted))",
|
|
1536
|
+
"color-node-selected": "var(--node-selected, var(--ring))"
|
|
1497
1537
|
}
|
|
1498
1538
|
},
|
|
1499
1539
|
"meta": {
|
|
@@ -1524,8 +1564,8 @@
|
|
|
1524
1564
|
"form"
|
|
1525
1565
|
],
|
|
1526
1566
|
"registryDependencies": [
|
|
1527
|
-
"slider",
|
|
1528
|
-
"number-input"
|
|
1567
|
+
"https://substrate.georgedrury.co.uk/r/slider",
|
|
1568
|
+
"https://substrate.georgedrury.co.uk/r/number-input"
|
|
1529
1569
|
],
|
|
1530
1570
|
"meta": {
|
|
1531
1571
|
"example": "<NumberSlider label=\"Opacity\" min={0} max={100} value={opacity} onChange={setOpacity} />",
|
|
@@ -1600,7 +1640,22 @@
|
|
|
1600
1640
|
"target": "components/substrate/progress-bar.tsx"
|
|
1601
1641
|
}
|
|
1602
1642
|
],
|
|
1603
|
-
"author": "George Drury <info@georgedrury.co.uk>"
|
|
1643
|
+
"author": "George Drury <info@georgedrury.co.uk>",
|
|
1644
|
+
"cssVars": {
|
|
1645
|
+
"theme": {
|
|
1646
|
+
"animate-progress-indeterminate": "progress-indeterminate 1.5s ease-in-out infinite"
|
|
1647
|
+
}
|
|
1648
|
+
},
|
|
1649
|
+
"css": {
|
|
1650
|
+
"@keyframes progress-indeterminate": {
|
|
1651
|
+
"0%": {
|
|
1652
|
+
"transform": "translateX(-100%)"
|
|
1653
|
+
},
|
|
1654
|
+
"100%": {
|
|
1655
|
+
"transform": "translateX(400%)"
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1604
1659
|
},
|
|
1605
1660
|
{
|
|
1606
1661
|
"name": "resizer",
|
|
@@ -1852,9 +1907,6 @@
|
|
|
1852
1907
|
"dependencies": [
|
|
1853
1908
|
"radix-ui"
|
|
1854
1909
|
],
|
|
1855
|
-
"registryDependencies": [
|
|
1856
|
-
"toggle"
|
|
1857
|
-
],
|
|
1858
1910
|
"meta": {
|
|
1859
1911
|
"example": "<ToggleGroup type=\"single\" value={align} onValueChange={setAlign}><ToggleGroupItem value=\"left\">Left</ToggleGroupItem><ToggleGroupItem value=\"center\">Center</ToggleGroupItem><ToggleGroupItem value=\"right\">Right</ToggleGroupItem></ToggleGroup>",
|
|
1860
1912
|
"lineage": [
|
|
@@ -1934,6 +1986,269 @@
|
|
|
1934
1986
|
}
|
|
1935
1987
|
],
|
|
1936
1988
|
"author": "George Drury <info@georgedrury.co.uk>"
|
|
1989
|
+
},
|
|
1990
|
+
{
|
|
1991
|
+
"name": "spring",
|
|
1992
|
+
"type": "registry:lib",
|
|
1993
|
+
"title": "Spring",
|
|
1994
|
+
"description": "Spring physics utilities: convert time-based springs (duration, bounce) to stiffness, damping and mass, and sample their progress over time.",
|
|
1995
|
+
"author": "George Drury <info@georgedrury.co.uk>",
|
|
1996
|
+
"categories": [
|
|
1997
|
+
"substrate",
|
|
1998
|
+
"lib",
|
|
1999
|
+
"animation"
|
|
2000
|
+
],
|
|
2001
|
+
"meta": {
|
|
2002
|
+
"lineage": [
|
|
2003
|
+
{
|
|
2004
|
+
"name": "Substrate"
|
|
2005
|
+
}
|
|
2006
|
+
]
|
|
2007
|
+
},
|
|
2008
|
+
"files": [
|
|
2009
|
+
{
|
|
2010
|
+
"path": "registry/substrate/lib/spring.ts",
|
|
2011
|
+
"type": "registry:lib",
|
|
2012
|
+
"target": "lib/substrate/spring.ts"
|
|
2013
|
+
}
|
|
2014
|
+
]
|
|
2015
|
+
},
|
|
2016
|
+
{
|
|
2017
|
+
"name": "spring-editor",
|
|
2018
|
+
"type": "registry:ui",
|
|
2019
|
+
"title": "Spring Editor",
|
|
2020
|
+
"description": "Spring curve preview with duration and bounce, or stiffness, damping and mass sliders. Used for spring transitions in animation tools.",
|
|
2021
|
+
"author": "George Drury <info@georgedrury.co.uk>",
|
|
2022
|
+
"categories": [
|
|
2023
|
+
"substrate",
|
|
2024
|
+
"ui",
|
|
2025
|
+
"animation"
|
|
2026
|
+
],
|
|
2027
|
+
"cssVars": {
|
|
2028
|
+
"theme": {
|
|
2029
|
+
"color-spring-curve": "var(--spring-curve, var(--color-primary))"
|
|
2030
|
+
},
|
|
2031
|
+
"light": {},
|
|
2032
|
+
"dark": {}
|
|
2033
|
+
},
|
|
2034
|
+
"meta": {
|
|
2035
|
+
"example": "<SpringEditor value={{ type: \"spring\", visualDuration: 0.3, bounce: 0.2 }} onChange={setSpring} />",
|
|
2036
|
+
"lineage": [
|
|
2037
|
+
{
|
|
2038
|
+
"name": "DialKit",
|
|
2039
|
+
"href": "https://github.com/joshpuckett/dialkit"
|
|
2040
|
+
},
|
|
2041
|
+
{
|
|
2042
|
+
"name": "Substrate"
|
|
2043
|
+
}
|
|
2044
|
+
]
|
|
2045
|
+
},
|
|
2046
|
+
"files": [
|
|
2047
|
+
{
|
|
2048
|
+
"path": "registry/substrate/components/spring-editor.tsx",
|
|
2049
|
+
"type": "registry:ui",
|
|
2050
|
+
"target": "components/substrate/spring-editor.tsx"
|
|
2051
|
+
}
|
|
2052
|
+
],
|
|
2053
|
+
"registryDependencies": [
|
|
2054
|
+
"https://substrate.georgedrury.co.uk/r/action",
|
|
2055
|
+
"https://substrate.georgedrury.co.uk/r/number-slider",
|
|
2056
|
+
"https://substrate.georgedrury.co.uk/r/toggle-group",
|
|
2057
|
+
"https://substrate.georgedrury.co.uk/r/spring"
|
|
2058
|
+
]
|
|
2059
|
+
},
|
|
2060
|
+
{
|
|
2061
|
+
"name": "xy-pad",
|
|
2062
|
+
"type": "registry:ui",
|
|
2063
|
+
"title": "XY Pad",
|
|
2064
|
+
"description": "Two-dimensional pad for setting two values at once by dragging or with the arrow keys. Used for offsets, positions and paired parameters.",
|
|
2065
|
+
"author": "George Drury <info@georgedrury.co.uk>",
|
|
2066
|
+
"categories": [
|
|
2067
|
+
"substrate",
|
|
2068
|
+
"ui",
|
|
2069
|
+
"form"
|
|
2070
|
+
],
|
|
2071
|
+
"cssVars": {
|
|
2072
|
+
"theme": {
|
|
2073
|
+
"color-xy-pad-handle": "var(--xy-pad-handle, var(--color-primary))"
|
|
2074
|
+
},
|
|
2075
|
+
"light": {},
|
|
2076
|
+
"dark": {}
|
|
2077
|
+
},
|
|
2078
|
+
"meta": {
|
|
2079
|
+
"example": "<XYPad value={offset} onChange={setOffset} x={{ min: -50, max: 50, step: 1 }} y={{ min: -50, max: 50, step: 1 }} />",
|
|
2080
|
+
"lineage": [
|
|
2081
|
+
{
|
|
2082
|
+
"name": "DialKit",
|
|
2083
|
+
"href": "https://github.com/joshpuckett/dialkit"
|
|
2084
|
+
},
|
|
2085
|
+
{
|
|
2086
|
+
"name": "Substrate"
|
|
2087
|
+
}
|
|
2088
|
+
]
|
|
2089
|
+
},
|
|
2090
|
+
"files": [
|
|
2091
|
+
{
|
|
2092
|
+
"path": "registry/substrate/components/xy-pad.tsx",
|
|
2093
|
+
"type": "registry:ui",
|
|
2094
|
+
"target": "components/substrate/xy-pad.tsx"
|
|
2095
|
+
}
|
|
2096
|
+
]
|
|
2097
|
+
},
|
|
2098
|
+
{
|
|
2099
|
+
"name": "image-picker",
|
|
2100
|
+
"type": "registry:ui",
|
|
2101
|
+
"title": "Image Picker",
|
|
2102
|
+
"description": "Thumbnail grid for choosing an image from a list, with upload and drag-and-drop. Uploads become data URLs.",
|
|
2103
|
+
"author": "George Drury <info@georgedrury.co.uk>",
|
|
2104
|
+
"categories": [
|
|
2105
|
+
"substrate",
|
|
2106
|
+
"ui",
|
|
2107
|
+
"form"
|
|
2108
|
+
],
|
|
2109
|
+
"cssVars": {
|
|
2110
|
+
"theme": {
|
|
2111
|
+
"color-image-picker-selection": "var(--image-picker-selection, var(--color-primary))"
|
|
2112
|
+
},
|
|
2113
|
+
"light": {},
|
|
2114
|
+
"dark": {}
|
|
2115
|
+
},
|
|
2116
|
+
"meta": {
|
|
2117
|
+
"example": "<ImagePicker value={image} onChange={setImage} options={[\"/a.jpg\", \"/b.jpg\"]} />",
|
|
2118
|
+
"lineage": [
|
|
2119
|
+
{
|
|
2120
|
+
"name": "DialKit",
|
|
2121
|
+
"href": "https://github.com/joshpuckett/dialkit"
|
|
2122
|
+
},
|
|
2123
|
+
{
|
|
2124
|
+
"name": "Substrate"
|
|
2125
|
+
}
|
|
2126
|
+
]
|
|
2127
|
+
},
|
|
2128
|
+
"files": [
|
|
2129
|
+
{
|
|
2130
|
+
"path": "registry/substrate/components/image-picker.tsx",
|
|
2131
|
+
"type": "registry:ui",
|
|
2132
|
+
"target": "components/substrate/image-picker.tsx"
|
|
2133
|
+
}
|
|
2134
|
+
],
|
|
2135
|
+
"dependencies": [
|
|
2136
|
+
"@remixicon/react"
|
|
2137
|
+
]
|
|
2138
|
+
},
|
|
2139
|
+
{
|
|
2140
|
+
"name": "controls",
|
|
2141
|
+
"type": "registry:ui",
|
|
2142
|
+
"title": "Controls",
|
|
2143
|
+
"description": "Config-driven control panels — describe parameters as an object with useControls and render them with Controls, built on DialKit's store.",
|
|
2144
|
+
"author": "George Drury <info@georgedrury.co.uk>",
|
|
2145
|
+
"categories": [
|
|
2146
|
+
"substrate",
|
|
2147
|
+
"ui",
|
|
2148
|
+
"form"
|
|
2149
|
+
],
|
|
2150
|
+
"dependencies": [
|
|
2151
|
+
"dialkit",
|
|
2152
|
+
"@remixicon/react"
|
|
2153
|
+
],
|
|
2154
|
+
"meta": {
|
|
2155
|
+
"example": "const values = useControls(\"Card\", { radius: [24, 0, 64], visible: true })\n<Panel><Controls /></Panel>",
|
|
2156
|
+
"lineage": [
|
|
2157
|
+
{
|
|
2158
|
+
"name": "DialKit",
|
|
2159
|
+
"href": "https://github.com/joshpuckett/dialkit"
|
|
2160
|
+
},
|
|
2161
|
+
{
|
|
2162
|
+
"name": "Substrate"
|
|
2163
|
+
}
|
|
2164
|
+
]
|
|
2165
|
+
},
|
|
2166
|
+
"files": [
|
|
2167
|
+
{
|
|
2168
|
+
"path": "registry/substrate/components/controls.tsx",
|
|
2169
|
+
"type": "registry:ui",
|
|
2170
|
+
"target": "components/substrate/controls.tsx"
|
|
2171
|
+
},
|
|
2172
|
+
{
|
|
2173
|
+
"path": "registry/substrate/hooks/use-controls.ts",
|
|
2174
|
+
"type": "registry:hook",
|
|
2175
|
+
"target": "hooks/substrate/use-controls.ts"
|
|
2176
|
+
},
|
|
2177
|
+
{
|
|
2178
|
+
"path": "registry/substrate/lib/controls-shortcuts.ts",
|
|
2179
|
+
"type": "registry:lib",
|
|
2180
|
+
"target": "lib/substrate/controls-shortcuts.ts"
|
|
2181
|
+
}
|
|
2182
|
+
],
|
|
2183
|
+
"registryDependencies": [
|
|
2184
|
+
"https://substrate.georgedrury.co.uk/r/pane",
|
|
2185
|
+
"https://substrate.georgedrury.co.uk/r/collapsible-pane",
|
|
2186
|
+
"https://substrate.georgedrury.co.uk/r/action",
|
|
2187
|
+
"https://substrate.georgedrury.co.uk/r/number-slider",
|
|
2188
|
+
"https://substrate.georgedrury.co.uk/r/number-input",
|
|
2189
|
+
"https://substrate.georgedrury.co.uk/r/toggle",
|
|
2190
|
+
"https://substrate.georgedrury.co.uk/r/select",
|
|
2191
|
+
"https://substrate.georgedrury.co.uk/r/colour-picker",
|
|
2192
|
+
"https://substrate.georgedrury.co.uk/r/text-area",
|
|
2193
|
+
"https://substrate.georgedrury.co.uk/r/easing-curve-editor",
|
|
2194
|
+
"https://substrate.georgedrury.co.uk/r/toggle-group",
|
|
2195
|
+
"https://substrate.georgedrury.co.uk/r/spring-editor",
|
|
2196
|
+
"https://substrate.georgedrury.co.uk/r/xy-pad",
|
|
2197
|
+
"https://substrate.georgedrury.co.uk/r/image-picker",
|
|
2198
|
+
"https://substrate.georgedrury.co.uk/r/spring",
|
|
2199
|
+
"https://substrate.georgedrury.co.uk/r/popover"
|
|
2200
|
+
]
|
|
2201
|
+
},
|
|
2202
|
+
{
|
|
2203
|
+
"name": "controls-timeline",
|
|
2204
|
+
"type": "registry:ui",
|
|
2205
|
+
"title": "Controls Timeline",
|
|
2206
|
+
"description": "Animation clips defined in code with useControlsTimeline, tuned in a timeline editor: scrub, move and resize clips, and edit their values. Built on DialKit's headless timeline.",
|
|
2207
|
+
"author": "George Drury <info@georgedrury.co.uk>",
|
|
2208
|
+
"categories": [
|
|
2209
|
+
"substrate",
|
|
2210
|
+
"ui",
|
|
2211
|
+
"animation"
|
|
2212
|
+
],
|
|
2213
|
+
"dependencies": [
|
|
2214
|
+
"dialkit",
|
|
2215
|
+
"@remixicon/react"
|
|
2216
|
+
],
|
|
2217
|
+
"cssVars": {
|
|
2218
|
+
"theme": {
|
|
2219
|
+
"color-timeline-clip": "var(--timeline-clip, var(--color-primary))"
|
|
2220
|
+
},
|
|
2221
|
+
"light": {},
|
|
2222
|
+
"dark": {}
|
|
2223
|
+
},
|
|
2224
|
+
"meta": {
|
|
2225
|
+
"example": "const timeline = useControlsTimeline(\"Card\", { enter: { at: 0, duration: 0.6, from: { y: 24 }, to: { y: 0 } } })\n<ControlsTimeline />",
|
|
2226
|
+
"lineage": [
|
|
2227
|
+
{
|
|
2228
|
+
"name": "DialKit",
|
|
2229
|
+
"href": "https://github.com/joshpuckett/dialkit"
|
|
2230
|
+
},
|
|
2231
|
+
{
|
|
2232
|
+
"name": "Substrate"
|
|
2233
|
+
}
|
|
2234
|
+
]
|
|
2235
|
+
},
|
|
2236
|
+
"files": [
|
|
2237
|
+
{
|
|
2238
|
+
"path": "registry/substrate/components/controls-timeline.tsx",
|
|
2239
|
+
"type": "registry:ui",
|
|
2240
|
+
"target": "components/substrate/controls-timeline.tsx"
|
|
2241
|
+
},
|
|
2242
|
+
{
|
|
2243
|
+
"path": "registry/substrate/hooks/use-controls-timeline.ts",
|
|
2244
|
+
"type": "registry:hook",
|
|
2245
|
+
"target": "hooks/substrate/use-controls-timeline.ts"
|
|
2246
|
+
}
|
|
2247
|
+
],
|
|
2248
|
+
"registryDependencies": [
|
|
2249
|
+
"https://substrate.georgedrury.co.uk/r/controls",
|
|
2250
|
+
"https://substrate.georgedrury.co.uk/r/popover"
|
|
2251
|
+
]
|
|
1937
2252
|
}
|
|
1938
2253
|
]
|
|
1939
2254
|
}
|
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.
|
|
@@ -27,7 +27,7 @@ This pulls in the canvas renderer, hit-test system, interaction hooks, transform
|
|
|
27
27
|
### Mount
|
|
28
28
|
|
|
29
29
|
```tsx
|
|
30
|
-
import { DesignCanvas } from "@/
|
|
30
|
+
import { DesignCanvas } from "@/components/substrate/design-canvas"
|
|
31
31
|
|
|
32
32
|
export default function PlaygroundPage() {
|
|
33
33
|
return (
|
|
@@ -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,16 +27,13 @@ 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
|
|
37
34
|
|
|
38
35
|
```tsx
|
|
39
|
-
import { Panel } from "@/
|
|
36
|
+
import { Panel } from "@/components/substrate/panel"
|
|
40
37
|
|
|
41
38
|
{
|
|
42
39
|
/* Left panel — layers, tool options */
|
|
@@ -58,19 +55,19 @@ A typical properties panel reads from `useDocumentStore` and writes back via `up
|
|
|
58
55
|
```tsx
|
|
59
56
|
"use client"
|
|
60
57
|
|
|
61
|
-
import { useDocumentStore } from "@/
|
|
58
|
+
import { useDocumentStore } from "@/stores/substrate/document-store"
|
|
62
59
|
import {
|
|
63
60
|
Pane,
|
|
64
61
|
PaneHeader,
|
|
65
62
|
PaneLabel,
|
|
66
63
|
PaneAction,
|
|
67
|
-
} from "@/
|
|
64
|
+
} from "@/components/substrate/pane"
|
|
68
65
|
import {
|
|
69
66
|
Action,
|
|
70
67
|
ActionLabel,
|
|
71
68
|
ActionControls,
|
|
72
|
-
} from "@/
|
|
73
|
-
import { Slider } from "@/
|
|
69
|
+
} from "@/components/substrate/action"
|
|
70
|
+
import { Slider } from "@/components/substrate/slider"
|
|
74
71
|
|
|
75
72
|
export function PropertiesPanel() {
|
|
76
73
|
const selectedIds = useDocumentStore((s) => s.selectedIds)
|
|
@@ -170,12 +167,12 @@ A layers panel lists elements in render order (bottom to top) and lets users sel
|
|
|
170
167
|
```tsx
|
|
171
168
|
"use client"
|
|
172
169
|
|
|
173
|
-
import { useDocumentStore } from "@/
|
|
170
|
+
import { useDocumentStore } from "@/stores/substrate/document-store"
|
|
174
171
|
import {
|
|
175
172
|
Pane,
|
|
176
173
|
PaneHeader,
|
|
177
174
|
PaneLabel,
|
|
178
|
-
} from "@/
|
|
175
|
+
} from "@/components/substrate/pane"
|
|
179
176
|
|
|
180
177
|
export function LayersPanel() {
|
|
181
178
|
const elements = useDocumentStore((s) => s.elements)
|
|
@@ -224,7 +221,7 @@ export function LayersPanel() {
|
|
|
224
221
|
Use `usePanelStore` to control panel visibility:
|
|
225
222
|
|
|
226
223
|
```tsx
|
|
227
|
-
import { usePanelStore } from "@/
|
|
224
|
+
import { usePanelStore } from "@/stores/substrate/panel-store"
|
|
228
225
|
|
|
229
226
|
const leftPanel = usePanelStore((s) => s.leftPanel)
|
|
230
227
|
const rightPanel = usePanelStore((s) => s.rightPanel)
|
|
@@ -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
|