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.
Files changed (81) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +27 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/prompts.d.ts +6 -0
  6. package/dist/prompts.d.ts.map +1 -0
  7. package/dist/prompts.js +127 -0
  8. package/dist/prompts.js.map +1 -0
  9. package/dist/scaffold.d.ts +10 -0
  10. package/dist/scaffold.d.ts.map +1 -0
  11. package/dist/scaffold.js +395 -0
  12. package/dist/scaffold.js.map +1 -0
  13. package/dist/surfaces/3d-scene.d.ts +3 -0
  14. package/dist/surfaces/3d-scene.d.ts.map +1 -0
  15. package/dist/surfaces/3d-scene.js +184 -0
  16. package/dist/surfaces/3d-scene.js.map +1 -0
  17. package/dist/surfaces/animation.d.ts +3 -0
  18. package/dist/surfaces/animation.d.ts.map +1 -0
  19. package/dist/surfaces/animation.js +211 -0
  20. package/dist/surfaces/animation.js.map +1 -0
  21. package/dist/surfaces/blank.d.ts +3 -0
  22. package/dist/surfaces/blank.d.ts.map +1 -0
  23. package/dist/surfaces/blank.js +72 -0
  24. package/dist/surfaces/blank.js.map +1 -0
  25. package/dist/surfaces/canvas-2d.d.ts +3 -0
  26. package/dist/surfaces/canvas-2d.d.ts.map +1 -0
  27. package/dist/surfaces/canvas-2d.js +139 -0
  28. package/dist/surfaces/canvas-2d.js.map +1 -0
  29. package/dist/surfaces/data-vis.d.ts +3 -0
  30. package/dist/surfaces/data-vis.d.ts.map +1 -0
  31. package/dist/surfaces/data-vis.js +175 -0
  32. package/dist/surfaces/data-vis.js.map +1 -0
  33. package/dist/surfaces/image-gen.d.ts +3 -0
  34. package/dist/surfaces/image-gen.d.ts.map +1 -0
  35. package/dist/surfaces/image-gen.js +193 -0
  36. package/dist/surfaces/image-gen.js.map +1 -0
  37. package/dist/surfaces/index.d.ts +4 -0
  38. package/dist/surfaces/index.d.ts.map +1 -0
  39. package/dist/surfaces/index.js +17 -0
  40. package/dist/surfaces/index.js.map +1 -0
  41. package/dist/surfaces/node-editor.d.ts +3 -0
  42. package/dist/surfaces/node-editor.d.ts.map +1 -0
  43. package/dist/surfaces/node-editor.js +211 -0
  44. package/dist/surfaces/node-editor.js.map +1 -0
  45. package/dist/surfaces/types.d.ts +22 -0
  46. package/dist/surfaces/types.d.ts.map +1 -0
  47. package/dist/surfaces/types.js +10 -0
  48. package/dist/surfaces/types.js.map +1 -0
  49. package/dist/utils/detect-pm.d.ts +5 -0
  50. package/dist/utils/detect-pm.d.ts.map +1 -0
  51. package/dist/utils/detect-pm.js +20 -0
  52. package/dist/utils/detect-pm.js.map +1 -0
  53. package/dist/utils/fs.d.ts +7 -0
  54. package/dist/utils/fs.d.ts.map +1 -0
  55. package/dist/utils/fs.js +52 -0
  56. package/dist/utils/fs.js.map +1 -0
  57. package/dist/utils/logger.d.ts +10 -0
  58. package/dist/utils/logger.d.ts.map +1 -0
  59. package/dist/utils/logger.js +15 -0
  60. package/dist/utils/logger.js.map +1 -0
  61. package/dist/utils/shell.d.ts +7 -0
  62. package/dist/utils/shell.d.ts.map +1 -0
  63. package/dist/utils/shell.js +28 -0
  64. package/dist/utils/shell.js.map +1 -0
  65. package/package.json +35 -0
  66. package/skills/3d-scene/SKILL.md +172 -0
  67. package/skills/animation/SKILL.md +194 -0
  68. package/skills/canvas-2d/SKILL.md +132 -0
  69. package/skills/composing-panels/SKILL.md +309 -0
  70. package/skills/create-custom-tool/SKILL.md +157 -0
  71. package/skills/data-visualisation/SKILL.md +228 -0
  72. package/skills/image-generation/SKILL.md +211 -0
  73. package/skills/scaffold-playground/SKILL.md +141 -0
  74. package/skills/substrate-canvas/SKILL.md +217 -0
  75. package/skills/substrate-controls/SKILL.md +242 -0
  76. package/skills/substrate-feedback/SKILL.md +219 -0
  77. package/skills/substrate-interaction/SKILL.md +286 -0
  78. package/skills/substrate-nodes/SKILL.md +208 -0
  79. package/skills/substrate-scaffold/SKILL.md +206 -0
  80. package/skills/theming/SKILL.md +117 -0
  81. package/skills/wire-interactions/SKILL.md +155 -0
@@ -0,0 +1,175 @@
1
+ export const dataVis = {
2
+ components: [
3
+ "toolbar",
4
+ "panel",
5
+ "pane",
6
+ "select",
7
+ "slider",
8
+ "colour-picker",
9
+ "number-input",
10
+ "status-bar",
11
+ ],
12
+ extraDeps: {
13
+ recharts: "^2.15.0",
14
+ },
15
+ app: () => `import { useState } from "react"
16
+ import {
17
+ LineChart,
18
+ Line,
19
+ BarChart,
20
+ Bar,
21
+ XAxis,
22
+ YAxis,
23
+ CartesianGrid,
24
+ Tooltip,
25
+ ResponsiveContainer,
26
+ } from "recharts"
27
+ import {
28
+ Toolbar,
29
+ ToolbarToggleGroup,
30
+ ToolbarToggle,
31
+ } from "@/components/substrate/toolbar"
32
+ import { Panel } from "@/components/substrate/panel"
33
+ import {
34
+ Pane,
35
+ PaneHeader,
36
+ PaneLabel,
37
+ PaneContent,
38
+ } from "@/components/substrate/pane"
39
+ import { Select } from "@/components/substrate/select"
40
+ import { Slider } from "@/components/substrate/slider"
41
+ import { ColourPicker } from "@/components/substrate/colour-picker"
42
+ import { StatusBar, StatusBarSection, StatusBarItem } from "@/components/substrate/status-bar"
43
+ import {
44
+ RiLineChartLine,
45
+ RiBarChartLine,
46
+ } from "@remixicon/react"
47
+
48
+ const SAMPLE_DATA = [
49
+ { month: "Jan", revenue: 4200, users: 1800 },
50
+ { month: "Feb", revenue: 3800, users: 2200 },
51
+ { month: "Mar", revenue: 5100, users: 2800 },
52
+ { month: "Apr", revenue: 4600, users: 3100 },
53
+ { month: "May", revenue: 6200, users: 3600 },
54
+ { month: "Jun", revenue: 5800, users: 4200 },
55
+ { month: "Jul", revenue: 7100, users: 4800 },
56
+ { month: "Aug", revenue: 6500, users: 5100 },
57
+ { month: "Sep", revenue: 7800, users: 5600 },
58
+ { month: "Oct", revenue: 8200, users: 6200 },
59
+ { month: "Nov", revenue: 7600, users: 6800 },
60
+ { month: "Dec", revenue: 9100, users: 7400 },
61
+ ]
62
+
63
+ const METRICS = [
64
+ { value: "revenue", label: "Revenue" },
65
+ { value: "users", label: "Users" },
66
+ ]
67
+
68
+ type ChartType = "line" | "bar"
69
+
70
+ export default function App() {
71
+ const [chartType, setChartType] = useState<ChartType>("line")
72
+ const [metric, setMetric] = useState("revenue")
73
+ const [colour, setColour] = useState("#6366f1")
74
+ const [strokeWidth, setStrokeWidth] = useState(2)
75
+
76
+ return (
77
+ <div className="flex h-screen flex-col bg-background text-foreground">
78
+ <div className="relative flex flex-1 overflow-hidden">
79
+ <Panel position="left">
80
+ <Pane>
81
+ <PaneHeader>
82
+ <PaneLabel>Dataset</PaneLabel>
83
+ </PaneHeader>
84
+ <PaneContent className="space-y-3">
85
+ <Select
86
+ label="Metric"
87
+ value={metric}
88
+ onValueChange={setMetric}
89
+ options={METRICS}
90
+ />
91
+ </PaneContent>
92
+ </Pane>
93
+ </Panel>
94
+
95
+ {/* Chart surface */}
96
+ <div className="flex flex-1 items-center justify-center bg-canvas p-8">
97
+ <ResponsiveContainer width="100%" height="80%">
98
+ {chartType === "line" ? (
99
+ <LineChart data={SAMPLE_DATA}>
100
+ <CartesianGrid strokeDasharray="3 3" stroke="hsl(0 0% 20%)" />
101
+ <XAxis dataKey="month" stroke="hsl(0 0% 50%)" />
102
+ <YAxis stroke="hsl(0 0% 50%)" />
103
+ <Tooltip
104
+ contentStyle={{ background: "hsl(0 0% 12%)", border: "1px solid hsl(0 0% 20%)", borderRadius: 8 }}
105
+ labelStyle={{ color: "hsl(0 0% 80%)" }}
106
+ />
107
+ <Line
108
+ type="monotone"
109
+ dataKey={metric}
110
+ stroke={colour}
111
+ strokeWidth={strokeWidth}
112
+ dot={{ fill: colour, r: 3 }}
113
+ />
114
+ </LineChart>
115
+ ) : (
116
+ <BarChart data={SAMPLE_DATA}>
117
+ <CartesianGrid strokeDasharray="3 3" stroke="hsl(0 0% 20%)" />
118
+ <XAxis dataKey="month" stroke="hsl(0 0% 50%)" />
119
+ <YAxis stroke="hsl(0 0% 50%)" />
120
+ <Tooltip
121
+ contentStyle={{ background: "hsl(0 0% 12%)", border: "1px solid hsl(0 0% 20%)", borderRadius: 8 }}
122
+ labelStyle={{ color: "hsl(0 0% 80%)" }}
123
+ />
124
+ <Bar dataKey={metric} fill={colour} radius={[4, 4, 0, 0]} />
125
+ </BarChart>
126
+ )}
127
+ </ResponsiveContainer>
128
+ </div>
129
+
130
+ <Panel position="right">
131
+ <Pane>
132
+ <PaneHeader>
133
+ <PaneLabel>Style</PaneLabel>
134
+ </PaneHeader>
135
+ <PaneContent className="space-y-3">
136
+ <ColourPicker value={colour} onChange={setColour} />
137
+ {chartType === "line" && (
138
+ <Slider
139
+ label="Stroke width"
140
+ min={1}
141
+ max={6}
142
+ step={0.5}
143
+ value={[strokeWidth]}
144
+ onValueChange={([v]) => setStrokeWidth(v)}
145
+ />
146
+ )}
147
+ </PaneContent>
148
+ </Pane>
149
+ </Panel>
150
+ </div>
151
+
152
+ <Toolbar>
153
+ <ToolbarToggleGroup value={chartType} onValueChange={(v) => setChartType(v as ChartType)}>
154
+ <ToolbarToggle value="line" tooltip="Line chart">
155
+ <RiLineChartLine className="size-4" />
156
+ </ToolbarToggle>
157
+ <ToolbarToggle value="bar" tooltip="Bar chart">
158
+ <RiBarChartLine className="size-4" />
159
+ </ToolbarToggle>
160
+ </ToolbarToggleGroup>
161
+ </Toolbar>
162
+
163
+ <StatusBar>
164
+ <StatusBarSection>
165
+ <StatusBarItem label="Chart" value={chartType} />
166
+ <StatusBarItem label="Metric" value={metric} />
167
+ <StatusBarItem label="Points" value={String(SAMPLE_DATA.length)} />
168
+ </StatusBarSection>
169
+ </StatusBar>
170
+ </div>
171
+ )
172
+ }
173
+ `,
174
+ };
175
+ //# sourceMappingURL=data-vis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-vis.js","sourceRoot":"","sources":["../../src/surfaces/data-vis.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,OAAO,GAAkB;IACpC,UAAU,EAAE;QACV,SAAS;QACT,OAAO;QACP,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,eAAe;QACf,cAAc;QACd,YAAY;KACb;IACD,SAAS,EAAE;QACT,QAAQ,EAAE,SAAS;KACpB;IACD,GAAG,EAAE,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8JZ;CACA,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { SurfaceConfig } from "./types.js";
2
+ export declare const imageGen: SurfaceConfig;
3
+ //# sourceMappingURL=image-gen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-gen.d.ts","sourceRoot":"","sources":["../../src/surfaces/image-gen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,QAAQ,EAAE,aA+LtB,CAAA"}
@@ -0,0 +1,193 @@
1
+ export const imageGen = {
2
+ components: [
3
+ "toolbar",
4
+ "panel",
5
+ "pane",
6
+ "gallery",
7
+ "text-area",
8
+ "slider",
9
+ "number-input",
10
+ "progress-bar",
11
+ "status-bar",
12
+ ],
13
+ app: () => `import { useState } from "react"
14
+ import {
15
+ Toolbar,
16
+ ToolbarToggleGroup,
17
+ ToolbarToggle,
18
+ } from "@/components/substrate/toolbar"
19
+ import { Panel } from "@/components/substrate/panel"
20
+ import {
21
+ Pane,
22
+ PaneHeader,
23
+ PaneLabel,
24
+ PaneContent,
25
+ } from "@/components/substrate/pane"
26
+ import { Gallery } from "@/components/substrate/gallery"
27
+ import { TextArea } from "@/components/substrate/text-area"
28
+ import { Slider } from "@/components/substrate/slider"
29
+ import { NumberInput } from "@/components/substrate/number-input"
30
+ import { ProgressBar } from "@/components/substrate/progress-bar"
31
+ import { StatusBar, StatusBarSection, StatusBarItem } from "@/components/substrate/status-bar"
32
+ import {
33
+ RiSparklingLine,
34
+ RiImageLine,
35
+ RiSettings3Line,
36
+ } from "@remixicon/react"
37
+
38
+ interface Generation {
39
+ id: string
40
+ prompt: string
41
+ timestamp: number
42
+ placeholder: string
43
+ }
44
+
45
+ const PLACEHOLDER_COLOURS = [
46
+ "from-indigo-500/20 to-purple-500/20",
47
+ "from-emerald-500/20 to-cyan-500/20",
48
+ "from-rose-500/20 to-orange-500/20",
49
+ "from-blue-500/20 to-violet-500/20",
50
+ ]
51
+
52
+ export default function App() {
53
+ const [prompt, setPrompt] = useState("")
54
+ const [steps, setSteps] = useState(30)
55
+ const [cfg, setCfg] = useState(7)
56
+ const [seed, setSeed] = useState(-1)
57
+ const [generating, setGenerating] = useState(false)
58
+ const [progress, setProgress] = useState(0)
59
+ const [generations, setGenerations] = useState<Generation[]>([])
60
+ const [selectedId, setSelectedId] = useState<string | null>(null)
61
+
62
+ async function handleGenerate() {
63
+ if (!prompt.trim()) return
64
+ setGenerating(true)
65
+ setProgress(0)
66
+
67
+ // Simulate generation progress
68
+ for (let i = 0; i <= 100; i += 10) {
69
+ await new Promise((r) => setTimeout(r, 200))
70
+ setProgress(i)
71
+ }
72
+
73
+ const gen: Generation = {
74
+ id: crypto.randomUUID(),
75
+ prompt: prompt.trim(),
76
+ timestamp: Date.now(),
77
+ placeholder: PLACEHOLDER_COLOURS[generations.length % PLACEHOLDER_COLOURS.length],
78
+ }
79
+ setGenerations((prev) => [gen, ...prev])
80
+ setSelectedId(gen.id)
81
+ setGenerating(false)
82
+ setProgress(0)
83
+ }
84
+
85
+ const selected = generations.find((g) => g.id === selectedId)
86
+
87
+ return (
88
+ <div className="flex h-screen flex-col bg-background text-foreground">
89
+ <div className="relative flex flex-1 overflow-hidden">
90
+ <Panel position="left">
91
+ <Pane>
92
+ <PaneHeader>
93
+ <PaneLabel>Prompt</PaneLabel>
94
+ </PaneHeader>
95
+ <PaneContent>
96
+ <TextArea
97
+ value={prompt}
98
+ onChange={(e) => setPrompt(e.target.value)}
99
+ placeholder="Describe the image you want to generate..."
100
+ rows={4}
101
+ />
102
+ </PaneContent>
103
+ </Pane>
104
+ <Pane>
105
+ <PaneHeader>
106
+ <PaneLabel>Parameters</PaneLabel>
107
+ </PaneHeader>
108
+ <PaneContent className="space-y-3">
109
+ <Slider
110
+ label="Steps"
111
+ min={1}
112
+ max={100}
113
+ value={[steps]}
114
+ onValueChange={([v]) => setSteps(v)}
115
+ />
116
+ <Slider
117
+ label="CFG scale"
118
+ min={1}
119
+ max={20}
120
+ step={0.5}
121
+ value={[cfg]}
122
+ onValueChange={([v]) => setCfg(v)}
123
+ />
124
+ <NumberInput label="Seed" value={seed} onChange={setSeed} />
125
+ </PaneContent>
126
+ </Pane>
127
+ </Panel>
128
+
129
+ {/* Preview area */}
130
+ <div className="flex flex-1 flex-col bg-canvas">
131
+ <div className="flex flex-1 items-center justify-center p-8">
132
+ {selected ? (
133
+ <div className={\`flex aspect-square max-h-full w-full max-w-lg items-center justify-center rounded-lg bg-gradient-to-br \${selected.placeholder}\`}>
134
+ <RiImageLine className="size-16 text-muted-foreground/30" />
135
+ </div>
136
+ ) : (
137
+ <div className="text-center text-muted-foreground">
138
+ <RiSparklingLine className="mx-auto mb-3 size-12 opacity-30" />
139
+ <p className="text-sm">Enter a prompt and generate</p>
140
+ </div>
141
+ )}
142
+ </div>
143
+
144
+ {generating && (
145
+ <div className="px-8 pb-4">
146
+ <ProgressBar value={progress} max={100} />
147
+ </div>
148
+ )}
149
+
150
+ {/* Gallery history */}
151
+ {generations.length > 0 && (
152
+ <div className="border-t border-border p-3">
153
+ <Gallery
154
+ items={generations.map((g) => ({
155
+ id: g.id,
156
+ label: g.prompt.slice(0, 40),
157
+ }))}
158
+ selectedId={selectedId}
159
+ onSelect={setSelectedId}
160
+ />
161
+ </div>
162
+ )}
163
+ </div>
164
+ </div>
165
+
166
+ <Toolbar>
167
+ <ToolbarToggle
168
+ value="generate"
169
+ tooltip="Generate"
170
+ shortcut="⌘↵"
171
+ onPressedChange={handleGenerate}
172
+ disabled={generating || !prompt.trim()}
173
+ >
174
+ <RiSparklingLine className="size-4" />
175
+ </ToolbarToggle>
176
+ </Toolbar>
177
+
178
+ <StatusBar>
179
+ <StatusBarSection>
180
+ <StatusBarItem label="Generations" value={String(generations.length)} />
181
+ {generating && <StatusBarItem label="Progress" value={\`\${progress}%\`} />}
182
+ </StatusBarSection>
183
+ <StatusBarSection>
184
+ <StatusBarItem label="Steps" value={String(steps)} />
185
+ <StatusBarItem label="CFG" value={String(cfg)} />
186
+ </StatusBarSection>
187
+ </StatusBar>
188
+ </div>
189
+ )
190
+ }
191
+ `,
192
+ };
193
+ //# sourceMappingURL=image-gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-gen.js","sourceRoot":"","sources":["../../src/surfaces/image-gen.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,QAAQ,GAAkB;IACrC,UAAU,EAAE;QACV,SAAS;QACT,OAAO;QACP,MAAM;QACN,SAAS;QACT,WAAW;QACX,QAAQ;QACR,cAAc;QACd,cAAc;QACd,YAAY;KACb;IACD,GAAG,EAAE,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkLZ;CACA,CAAA"}
@@ -0,0 +1,4 @@
1
+ import type { SurfaceType, SurfaceConfig } from "./types.js";
2
+ export declare const surfaces: Record<SurfaceType, SurfaceConfig>;
3
+ export type { SurfaceType, SurfaceConfig } from "./types.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/surfaces/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAS5D,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,aAAa,CAQvD,CAAA;AAED,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { blank } from "./blank.js";
2
+ import { canvas2d } from "./canvas-2d.js";
3
+ import { scene3d } from "./3d-scene.js";
4
+ import { animation } from "./animation.js";
5
+ import { nodeEditor } from "./node-editor.js";
6
+ import { dataVis } from "./data-vis.js";
7
+ import { imageGen } from "./image-gen.js";
8
+ export const surfaces = {
9
+ "canvas-2d": canvas2d,
10
+ "3d-scene": scene3d,
11
+ animation,
12
+ "node-editor": nodeEditor,
13
+ "data-vis": dataVis,
14
+ "image-gen": imageGen,
15
+ blank,
16
+ };
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/surfaces/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,MAAM,CAAC,MAAM,QAAQ,GAAuC;IAC1D,WAAW,EAAE,QAAQ;IACrB,UAAU,EAAE,OAAO;IACnB,SAAS;IACT,aAAa,EAAE,UAAU;IACzB,UAAU,EAAE,OAAO;IACnB,WAAW,EAAE,QAAQ;IACrB,KAAK;CACN,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { SurfaceConfig } from "./types.js";
2
+ export declare const nodeEditor: SurfaceConfig;
3
+ //# sourceMappingURL=node-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-editor.d.ts","sourceRoot":"","sources":["../../src/surfaces/node-editor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,UAAU,EAAE,aAiNxB,CAAA"}
@@ -0,0 +1,211 @@
1
+ export const nodeEditor = {
2
+ components: [
3
+ "toolbar",
4
+ "panel",
5
+ "pane",
6
+ "node",
7
+ "number-input",
8
+ "colour-picker",
9
+ "status-bar",
10
+ "zoom-controls",
11
+ "command-palette",
12
+ ],
13
+ cssVars: ` /* Node port colours */
14
+ --node-port-number: oklch(0.7 0.15 250);
15
+ --node-port-string: oklch(0.7 0.15 150);
16
+ --node-port-boolean: oklch(0.7 0.15 30);
17
+ --node-port-colour: oklch(0.7 0.15 330);
18
+ --node-port-vector: oklch(0.7 0.15 200);
19
+ --node-port-image: oklch(0.7 0.15 100);
20
+ --node-port-any: oklch(0.6 0 0);`,
21
+ app: () => `import { useState, useCallback } from "react"
22
+ import {
23
+ Toolbar,
24
+ ToolbarToggleGroup,
25
+ ToolbarToggle,
26
+ ToolbarSeparator,
27
+ } from "@/components/substrate/toolbar"
28
+ import { Panel } from "@/components/substrate/panel"
29
+ import {
30
+ Pane,
31
+ PaneHeader,
32
+ PaneLabel,
33
+ PaneContent,
34
+ } from "@/components/substrate/pane"
35
+ import {
36
+ Node,
37
+ NodeHeader,
38
+ NodeTitle,
39
+ NodeContent,
40
+ NodePort,
41
+ NodeDivider,
42
+ } from "@/components/substrate/node"
43
+ import { NumberInput } from "@/components/substrate/number-input"
44
+ import { ColourPicker } from "@/components/substrate/colour-picker"
45
+ import { StatusBar, StatusBarSection, StatusBarItem } from "@/components/substrate/status-bar"
46
+ import { ZoomControls } from "@/components/substrate/zoom-controls"
47
+ import { RiCursorLine, RiHand } from "@remixicon/react"
48
+
49
+ interface NodeData {
50
+ id: string
51
+ type: "math" | "colour"
52
+ label: string
53
+ x: number
54
+ y: number
55
+ }
56
+
57
+ const INITIAL_NODES: NodeData[] = [
58
+ { id: "math-1", type: "math", label: "Add", x: 80, y: 100 },
59
+ { id: "colour-1", type: "colour", label: "Mix Colour", x: 400, y: 80 },
60
+ ]
61
+
62
+ function MathNode({ node, selected, onSelect }: { node: NodeData; selected: boolean; onSelect: () => void }) {
63
+ return (
64
+ <div
65
+ className="absolute"
66
+ style={{ left: node.x, top: node.y }}
67
+ onClick={onSelect}
68
+ >
69
+ <Node data-selected={selected || undefined}>
70
+ <NodeHeader>
71
+ <NodeTitle>{node.label}</NodeTitle>
72
+ </NodeHeader>
73
+ <NodeContent>
74
+ <NodePort type="number" direction="input" label="A" />
75
+ <NodePort type="number" direction="input" label="B" />
76
+ <NodeDivider />
77
+ <NodePort type="number" direction="output" label="Result" />
78
+ </NodeContent>
79
+ </Node>
80
+ </div>
81
+ )
82
+ }
83
+
84
+ function ColourNode({ node, selected, onSelect }: { node: NodeData; selected: boolean; onSelect: () => void }) {
85
+ return (
86
+ <div
87
+ className="absolute"
88
+ style={{ left: node.x, top: node.y }}
89
+ onClick={onSelect}
90
+ >
91
+ <Node data-selected={selected || undefined}>
92
+ <NodeHeader>
93
+ <NodeTitle>{node.label}</NodeTitle>
94
+ </NodeHeader>
95
+ <NodeContent>
96
+ <NodePort type="colour" direction="input" label="Colour A" />
97
+ <NodePort type="colour" direction="input" label="Colour B" />
98
+ <NodePort type="number" direction="input" label="Factor" />
99
+ <NodeDivider />
100
+ <NodePort type="colour" direction="output" label="Result" />
101
+ </NodeContent>
102
+ </Node>
103
+ </div>
104
+ )
105
+ }
106
+
107
+ export default function App() {
108
+ const [tool, setTool] = useState("select")
109
+ const [zoom, setZoom] = useState(1)
110
+ const [nodes] = useState(INITIAL_NODES)
111
+ const [selectedId, setSelectedId] = useState<string | null>(null)
112
+
113
+ const selected = nodes.find((n) => n.id === selectedId) ?? null
114
+
115
+ return (
116
+ <div className="flex h-screen flex-col bg-background text-foreground">
117
+ <div className="relative flex flex-1 overflow-hidden">
118
+ {/* Node canvas */}
119
+ <div
120
+ className="relative flex-1 bg-canvas"
121
+ style={{
122
+ backgroundImage: "radial-gradient(circle, hsl(0 0% 30%) 1px, transparent 1px)",
123
+ backgroundSize: \`\${20 * zoom}px \${20 * zoom}px\`,
124
+ }}
125
+ onClick={() => setSelectedId(null)}
126
+ >
127
+ <div style={{ transform: \`scale(\${zoom})\`, transformOrigin: "0 0" }}>
128
+ {nodes.map((node) =>
129
+ node.type === "math" ? (
130
+ <MathNode
131
+ key={node.id}
132
+ node={node}
133
+ selected={node.id === selectedId}
134
+ onSelect={() => setSelectedId(node.id)}
135
+ />
136
+ ) : (
137
+ <ColourNode
138
+ key={node.id}
139
+ node={node}
140
+ selected={node.id === selectedId}
141
+ onSelect={() => setSelectedId(node.id)}
142
+ />
143
+ )
144
+ )}
145
+
146
+ {/* Sample SVG wire between nodes */}
147
+ <svg className="pointer-events-none absolute inset-0 size-full">
148
+ <path
149
+ d="M 270 155 C 335 155, 335 135, 400 135"
150
+ fill="none"
151
+ stroke="hsl(0 0% 40%)"
152
+ strokeWidth={2}
153
+ />
154
+ </svg>
155
+ </div>
156
+ </div>
157
+
158
+ <Panel position="right">
159
+ {selected ? (
160
+ <Pane>
161
+ <PaneHeader>
162
+ <PaneLabel>{selected.label}</PaneLabel>
163
+ </PaneHeader>
164
+ <PaneContent>
165
+ <div className="grid grid-cols-2 gap-2">
166
+ <NumberInput label="X" value={selected.x} />
167
+ <NumberInput label="Y" value={selected.y} />
168
+ </div>
169
+ </PaneContent>
170
+ </Pane>
171
+ ) : (
172
+ <Pane>
173
+ <PaneContent>
174
+ <p className="text-sm text-muted-foreground">Click a node to inspect</p>
175
+ </PaneContent>
176
+ </Pane>
177
+ )}
178
+ </Panel>
179
+ </div>
180
+
181
+ <Toolbar>
182
+ <ToolbarToggleGroup value={tool} onValueChange={setTool}>
183
+ <ToolbarToggle value="select" tooltip="Select" shortcut="V">
184
+ <RiCursorLine className="size-4" />
185
+ </ToolbarToggle>
186
+ <ToolbarToggle value="hand" tooltip="Pan" shortcut="H">
187
+ <RiHand className="size-4" />
188
+ </ToolbarToggle>
189
+ </ToolbarToggleGroup>
190
+ </Toolbar>
191
+
192
+ <StatusBar>
193
+ <StatusBarSection>
194
+ <StatusBarItem label="Tool" value={tool} />
195
+ {selected && <StatusBarItem label="Node" value={selected.label} />}
196
+ </StatusBarSection>
197
+ <StatusBarSection>
198
+ <ZoomControls
199
+ zoom={zoom}
200
+ onZoomIn={() => setZoom((z) => Math.min(z * 1.25, 5))}
201
+ onZoomOut={() => setZoom((z) => Math.max(z / 1.25, 0.2))}
202
+ onReset={() => setZoom(1)}
203
+ />
204
+ </StatusBarSection>
205
+ </StatusBar>
206
+ </div>
207
+ )
208
+ }
209
+ `,
210
+ };
211
+ //# sourceMappingURL=node-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-editor.js","sourceRoot":"","sources":["../../src/surfaces/node-editor.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,UAAU,GAAkB;IACvC,UAAU,EAAE;QACV,SAAS;QACT,OAAO;QACP,MAAM;QACN,MAAM;QACN,cAAc;QACd,eAAe;QACf,YAAY;QACZ,eAAe;QACf,iBAAiB;KAClB;IACD,OAAO,EAAE;;;;;;;mCAOwB;IACjC,GAAG,EAAE,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4LZ;CACA,CAAA"}
@@ -0,0 +1,22 @@
1
+ export type SurfaceType = "canvas-2d" | "3d-scene" | "animation" | "node-editor" | "data-vis" | "image-gen" | "blank";
2
+ export interface SurfaceOption {
3
+ value: SurfaceType;
4
+ label: string;
5
+ description: string;
6
+ }
7
+ export declare const SURFACE_OPTIONS: SurfaceOption[];
8
+ export interface SurfaceConfig {
9
+ /** Registry components to install via shadcn */
10
+ components: string[];
11
+ /** Additional npm dependencies beyond the base */
12
+ extraDeps?: Record<string, string>;
13
+ /** Additional npm devDependencies */
14
+ extraDevDeps?: Record<string, string>;
15
+ /** Extra CSS variable block appended to globals.css */
16
+ cssVars?: string;
17
+ /** The main App.tsx content */
18
+ app: () => string;
19
+ /** Additional files to write (path relative to project root → content) */
20
+ files?: Record<string, () => string>;
21
+ }
22
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/surfaces/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,UAAU,GACV,WAAW,GACX,aAAa,GACb,UAAU,GACV,WAAW,GACX,OAAO,CAAA;AAEX,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,WAAW,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,eAAO,MAAM,eAAe,EAAE,aAAa,EAQ1C,CAAA;AAED,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+BAA+B;IAC/B,GAAG,EAAE,MAAM,MAAM,CAAA;IACjB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC,CAAA;CACrC"}
@@ -0,0 +1,10 @@
1
+ export const SURFACE_OPTIONS = [
2
+ { value: "canvas-2d", label: "2D canvas", description: "Shape editors, whiteboards, diagram tools" },
3
+ { value: "3d-scene", label: "3D scene", description: "Product viewers, architectural tools, shader playgrounds" },
4
+ { value: "animation", label: "Animation", description: "Keyframe editors, motion graphics, easing explorers" },
5
+ { value: "node-editor", label: "Node editor", description: "Visual programming, shader graphs, data pipelines" },
6
+ { value: "data-vis", label: "Data vis", description: "Charts, dashboards, analytics explorers" },
7
+ { value: "image-gen", label: "Image generation", description: "AI image tools, prompt-driven workflows" },
8
+ { value: "blank", label: "Blank", description: "Scaffold only — bring your own surface" },
9
+ ];
10
+ //# sourceMappingURL=types.js.map