create-substrate 0.1.3 → 0.1.5

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 (39) hide show
  1. package/README.md +53 -0
  2. package/dist/index.js +1 -1
  3. package/dist/scaffold.d.ts.map +1 -1
  4. package/dist/scaffold.js +68 -4
  5. package/dist/scaffold.js.map +1 -1
  6. package/dist/surfaces/3d-scene.d.ts.map +1 -1
  7. package/dist/surfaces/3d-scene.js +414 -67
  8. package/dist/surfaces/3d-scene.js.map +1 -1
  9. package/dist/surfaces/animation.d.ts.map +1 -1
  10. package/dist/surfaces/animation.js +95 -103
  11. package/dist/surfaces/animation.js.map +1 -1
  12. package/dist/surfaces/blank.d.ts.map +1 -1
  13. package/dist/surfaces/blank.js +28 -16
  14. package/dist/surfaces/blank.js.map +1 -1
  15. package/dist/surfaces/canvas-2d.d.ts.map +1 -1
  16. package/dist/surfaces/canvas-2d.js +369 -92
  17. package/dist/surfaces/canvas-2d.js.map +1 -1
  18. package/dist/surfaces/data-vis.d.ts.map +1 -1
  19. package/dist/surfaces/data-vis.js +262 -98
  20. package/dist/surfaces/data-vis.js.map +1 -1
  21. package/dist/surfaces/image-gen.d.ts.map +1 -1
  22. package/dist/surfaces/image-gen.js +354 -92
  23. package/dist/surfaces/image-gen.js.map +1 -1
  24. package/dist/surfaces/node-editor.d.ts.map +1 -1
  25. package/dist/surfaces/node-editor.js +559 -114
  26. package/dist/surfaces/node-editor.js.map +1 -1
  27. package/package.json +4 -2
  28. package/skills/3d-scene/SKILL.md +6 -0
  29. package/skills/animation/SKILL.md +6 -0
  30. package/skills/canvas-2d/SKILL.md +7 -1
  31. package/skills/composing-panels/SKILL.md +14 -8
  32. package/skills/create-custom-tool/SKILL.md +6 -0
  33. package/skills/data-visualisation/SKILL.md +6 -0
  34. package/skills/image-generation/SKILL.md +6 -0
  35. package/skills/install-components/SKILL.md +199 -0
  36. package/skills/scaffold-playground/SKILL.md +12 -6
  37. package/skills/substrate-controls/SKILL.md +1 -3
  38. package/skills/theming/SKILL.md +6 -0
  39. package/skills/wire-interactions/SKILL.md +6 -0
@@ -3,158 +3,317 @@ export const dataVis = {
3
3
  "toolbar",
4
4
  "panel",
5
5
  "pane",
6
+ "action",
6
7
  "select",
7
- "slider",
8
+ "number-slider",
8
9
  "colour-picker",
9
- "number-input",
10
+ "toggle-group",
10
11
  "status-bar",
11
12
  ],
12
13
  extraDeps: {
13
- recharts: "^2.15.0",
14
+ recharts: "^3.2.0",
14
15
  },
15
16
  app: () => `import { useState } from "react"
16
17
  import {
17
- LineChart,
18
- Line,
19
- BarChart,
18
+ Area,
19
+ AreaChart,
20
20
  Bar,
21
- XAxis,
22
- YAxis,
21
+ BarChart,
23
22
  CartesianGrid,
24
- Tooltip,
23
+ Line,
24
+ LineChart,
25
25
  ResponsiveContainer,
26
+ Tooltip,
27
+ XAxis,
28
+ YAxis,
26
29
  } from "recharts"
27
30
  import {
28
31
  Toolbar,
29
- ToolbarToggleGroup,
30
32
  ToolbarToggle,
33
+ ToolbarToggleGroup,
31
34
  } from "@/components/substrate/toolbar"
32
35
  import { Panel } from "@/components/substrate/panel"
36
+ import { Pane, PaneHeader, PaneLabel } from "@/components/substrate/pane"
33
37
  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"
38
+ Action,
39
+ ActionControls,
40
+ ActionLabel,
41
+ } from "@/components/substrate/action"
42
+ import {
43
+ Select,
44
+ SelectContent,
45
+ SelectItem,
46
+ SelectTrigger,
47
+ SelectValue,
48
+ } from "@/components/substrate/select"
49
+ import { NumberSlider } from "@/components/substrate/number-slider"
41
50
  import { ColourPicker } from "@/components/substrate/colour-picker"
42
- import { StatusBar, StatusBarSection, StatusBarItem } from "@/components/substrate/status-bar"
51
+ import { ToggleGroup, ToggleGroupItem } from "@/components/substrate/toggle-group"
52
+ import {
53
+ StatusBar,
54
+ StatusBarItem,
55
+ StatusBarSection,
56
+ StatusBarSeparator,
57
+ } from "@/components/substrate/status-bar"
43
58
  import {
44
- RiLineChartLine,
45
59
  RiBarChartLine,
60
+ RiLineChartLine,
61
+ RiLineChartFill,
46
62
  } from "@remixicon/react"
47
63
 
48
64
  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 },
65
+ { month: "Jan", revenue: 4200, users: 1800, orders: 320 },
66
+ { month: "Feb", revenue: 3800, users: 2200, orders: 290 },
67
+ { month: "Mar", revenue: 5100, users: 2800, orders: 410 },
68
+ { month: "Apr", revenue: 4600, users: 3100, orders: 380 },
69
+ { month: "May", revenue: 6200, users: 3600, orders: 520 },
70
+ { month: "Jun", revenue: 5800, users: 4200, orders: 480 },
71
+ { month: "Jul", revenue: 7100, users: 4800, orders: 610 },
72
+ { month: "Aug", revenue: 6500, users: 5100, orders: 560 },
73
+ { month: "Sep", revenue: 7800, users: 5600, orders: 690 },
74
+ { month: "Oct", revenue: 8200, users: 6200, orders: 740 },
75
+ { month: "Nov", revenue: 7600, users: 6800, orders: 680 },
76
+ { month: "Dec", revenue: 9100, users: 7400, orders: 830 },
61
77
  ]
62
78
 
63
79
  const METRICS = [
64
80
  { value: "revenue", label: "Revenue" },
65
81
  { value: "users", label: "Users" },
66
- ]
82
+ { value: "orders", label: "Orders" },
83
+ ] as const
67
84
 
68
- type ChartType = "line" | "bar"
85
+ const CHART_TYPES = ["line", "area", "bar"] as const
86
+
87
+ type Metric = (typeof METRICS)[number]["value"]
88
+ type ChartType = (typeof CHART_TYPES)[number]
89
+
90
+ function isMetric(value: string): value is Metric {
91
+ return METRICS.some((metric) => metric.value === value)
92
+ }
93
+
94
+ function isChartType(value: string): value is ChartType {
95
+ return CHART_TYPES.some((type) => type === value)
96
+ }
97
+
98
+ const AXIS_TICK = { fill: "var(--muted-foreground)", fontSize: 11 }
99
+ const CHART_MARGIN = { top: 8, right: 16, bottom: 0, left: 0 }
100
+ const TOOLTIP_STYLE = {
101
+ background: "var(--popover)",
102
+ border: "1px solid var(--border)",
103
+ borderRadius: 8,
104
+ fontSize: 12,
105
+ }
69
106
 
70
107
  export default function App() {
71
108
  const [chartType, setChartType] = useState<ChartType>("line")
72
- const [metric, setMetric] = useState("revenue")
109
+ const [metric, setMetric] = useState<Metric>("revenue")
110
+ const [months, setMonths] = useState(12)
73
111
  const [colour, setColour] = useState("#6366f1")
74
112
  const [strokeWidth, setStrokeWidth] = useState(2)
113
+ const [fillOpacity, setFillOpacity] = useState(30)
114
+ const [grid, setGrid] = useState("on")
115
+
116
+ const data = SAMPLE_DATA.slice(0, months)
117
+ const values = data.map((row) => row[metric])
118
+ const total = values.reduce((sum, value) => sum + value, 0)
119
+ const metricLabel = METRICS.find((item) => item.value === metric)?.label ?? metric
120
+
121
+ const axes = (
122
+ <>
123
+ {grid === "on" && (
124
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--border)" vertical={false} />
125
+ )}
126
+ <XAxis dataKey="month" tick={AXIS_TICK} stroke="var(--border)" tickLine={false} />
127
+ <YAxis tick={AXIS_TICK} stroke="var(--border)" tickLine={false} width={48} />
128
+ <Tooltip
129
+ contentStyle={TOOLTIP_STYLE}
130
+ labelStyle={{ color: "var(--muted-foreground)" }}
131
+ itemStyle={{ color: "var(--foreground)" }}
132
+ cursor={{ stroke: "var(--border)", fill: "var(--muted)", fillOpacity: 0.4 }}
133
+ isAnimationActive={false}
134
+ />
135
+ </>
136
+ )
75
137
 
76
138
  return (
77
139
  <div className="flex h-screen flex-col bg-background text-foreground">
78
- <div className="relative flex flex-1 overflow-hidden">
79
- <Panel position="left">
140
+ <div className="flex min-h-0 flex-1">
141
+ <Panel position="left" className="shrink-0">
80
142
  <Pane>
81
143
  <PaneHeader>
82
- <PaneLabel>Dataset</PaneLabel>
144
+ <PaneLabel>Data</PaneLabel>
83
145
  </PaneHeader>
84
- <PaneContent className="space-y-3">
85
- <Select
86
- label="Metric"
87
- value={metric}
88
- onValueChange={setMetric}
89
- options={METRICS}
90
- />
91
- </PaneContent>
146
+ <Action>
147
+ <ActionLabel>Metric</ActionLabel>
148
+ <ActionControls>
149
+ <Select
150
+ value={metric}
151
+ onValueChange={(value) => {
152
+ if (isMetric(value)) setMetric(value)
153
+ }}
154
+ >
155
+ <SelectTrigger aria-label="Metric">
156
+ <SelectValue />
157
+ </SelectTrigger>
158
+ <SelectContent>
159
+ {METRICS.map((item) => (
160
+ <SelectItem key={item.value} value={item.value}>
161
+ {item.label}
162
+ </SelectItem>
163
+ ))}
164
+ </SelectContent>
165
+ </Select>
166
+ </ActionControls>
167
+ </Action>
168
+ <Action>
169
+ <ActionLabel>Months</ActionLabel>
170
+ <ActionControls>
171
+ <NumberSlider
172
+ value={months}
173
+ onChange={(value) => setMonths(Math.round(value))}
174
+ min={3}
175
+ max={SAMPLE_DATA.length}
176
+ step={1}
177
+ precision={0}
178
+ />
179
+ </ActionControls>
180
+ </Action>
92
181
  </Pane>
93
182
  </Panel>
94
183
 
95
184
  {/* 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>
185
+ <main className="flex min-w-0 flex-1 flex-col bg-canvas p-8 pb-28">
186
+ <h1 className="text-sm font-medium">{metricLabel} by month</h1>
187
+ <p className="text-xs text-muted-foreground">
188
+ Sample data, first {months} months
189
+ </p>
190
+ <div className="mt-6 h-[420px] min-h-[240px] w-full min-w-[320px]">
191
+ <ResponsiveContainer
192
+ width="100%"
193
+ height="100%"
194
+ initialDimension={{ width: 640, height: 420 }}
195
+ >
196
+ {chartType === "bar" ? (
197
+ <BarChart data={data} margin={CHART_MARGIN}>
198
+ {axes}
199
+ <Bar
200
+ dataKey={metric}
201
+ name={metricLabel}
202
+ fill={colour}
203
+ radius={[4, 4, 0, 0]}
204
+ maxBarSize={32}
205
+ isAnimationActive={false}
206
+ />
207
+ </BarChart>
208
+ ) : chartType === "area" ? (
209
+ <AreaChart data={data} margin={CHART_MARGIN}>
210
+ {axes}
211
+ <Area
212
+ type="monotone"
213
+ dataKey={metric}
214
+ name={metricLabel}
215
+ stroke={colour}
216
+ strokeWidth={strokeWidth}
217
+ fill={colour}
218
+ fillOpacity={fillOpacity / 100}
219
+ isAnimationActive={false}
220
+ />
221
+ </AreaChart>
222
+ ) : (
223
+ <LineChart data={data} margin={CHART_MARGIN}>
224
+ {axes}
225
+ <Line
226
+ type="monotone"
227
+ dataKey={metric}
228
+ name={metricLabel}
229
+ stroke={colour}
230
+ strokeWidth={strokeWidth}
231
+ dot={{ fill: colour, r: 3 }}
232
+ isAnimationActive={false}
233
+ />
234
+ </LineChart>
235
+ )}
236
+ </ResponsiveContainer>
237
+ </div>
238
+ </main>
129
239
 
130
- <Panel position="right">
240
+ <Panel position="right" className="shrink-0">
131
241
  <Pane>
132
242
  <PaneHeader>
133
243
  <PaneLabel>Style</PaneLabel>
134
244
  </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>
245
+ <Action>
246
+ <ActionLabel>Colour</ActionLabel>
247
+ <ActionControls>
248
+ <ColourPicker value={colour} onChange={setColour} />
249
+ <span className="font-mono text-xs text-muted-foreground">{colour}</span>
250
+ </ActionControls>
251
+ </Action>
252
+ {chartType !== "bar" && (
253
+ <Action>
254
+ <ActionLabel>Stroke</ActionLabel>
255
+ <ActionControls>
256
+ <NumberSlider
257
+ value={strokeWidth}
258
+ onChange={setStrokeWidth}
259
+ min={1}
260
+ max={6}
261
+ step={0.5}
262
+ suffix="px"
263
+ />
264
+ </ActionControls>
265
+ </Action>
266
+ )}
267
+ {chartType === "area" && (
268
+ <Action>
269
+ <ActionLabel>Fill</ActionLabel>
270
+ <ActionControls>
271
+ <NumberSlider
272
+ value={fillOpacity}
273
+ onChange={setFillOpacity}
274
+ min={0}
275
+ max={100}
276
+ step={1}
277
+ precision={0}
278
+ suffix="%"
279
+ />
280
+ </ActionControls>
281
+ </Action>
282
+ )}
283
+ <Action>
284
+ <ActionLabel>Grid</ActionLabel>
285
+ <ActionControls>
286
+ <ToggleGroup
287
+ type="single"
288
+ value={grid}
289
+ onValueChange={(value) => {
290
+ if (value) setGrid(value)
291
+ }}
292
+ aria-label="Grid lines"
293
+ >
294
+ <ToggleGroupItem value="on">On</ToggleGroupItem>
295
+ <ToggleGroupItem value="off">Off</ToggleGroupItem>
296
+ </ToggleGroup>
297
+ </ActionControls>
298
+ </Action>
148
299
  </Pane>
149
300
  </Panel>
150
301
  </div>
151
302
 
152
303
  <Toolbar>
153
- <ToolbarToggleGroup value={chartType} onValueChange={(v) => setChartType(v as ChartType)}>
154
- <ToolbarToggle value="line" tooltip="Line chart">
304
+ <ToolbarToggleGroup
305
+ value={chartType}
306
+ onValueChange={(value) => {
307
+ if (isChartType(value)) setChartType(value)
308
+ }}
309
+ >
310
+ <ToolbarToggle value="line" tooltip="Line chart" aria-label="Line chart">
155
311
  <RiLineChartLine className="size-4" />
156
312
  </ToolbarToggle>
157
- <ToolbarToggle value="bar" tooltip="Bar chart">
313
+ <ToolbarToggle value="area" tooltip="Area chart" aria-label="Area chart">
314
+ <RiLineChartFill className="size-4" />
315
+ </ToolbarToggle>
316
+ <ToolbarToggle value="bar" tooltip="Bar chart" aria-label="Bar chart">
158
317
  <RiBarChartLine className="size-4" />
159
318
  </ToolbarToggle>
160
319
  </ToolbarToggleGroup>
@@ -162,9 +321,14 @@ export default function App() {
162
321
 
163
322
  <StatusBar>
164
323
  <StatusBarSection>
165
- <StatusBarItem>Chart: {chartType}</StatusBarItem>
166
- <StatusBarItem>Metric: {metric}</StatusBarItem>
167
- <StatusBarItem>Points: {SAMPLE_DATA.length}</StatusBarItem>
324
+ <StatusBarItem>{metricLabel}</StatusBarItem>
325
+ <StatusBarSeparator />
326
+ <StatusBarItem>{data.length} points</StatusBarItem>
327
+ </StatusBarSection>
328
+ <StatusBarSection align="right">
329
+ <StatusBarItem>Min {Math.min(...values).toLocaleString()}</StatusBarItem>
330
+ <StatusBarItem>Max {Math.max(...values).toLocaleString()}</StatusBarItem>
331
+ <StatusBarItem>Total {total.toLocaleString()}</StatusBarItem>
168
332
  </StatusBarSection>
169
333
  </StatusBar>
170
334
  </div>
@@ -1 +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"}
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,eAAe;QACf,cAAc;QACd,YAAY;KACb;IACD,SAAS,EAAE;QACT,QAAQ,EAAE,QAAQ;KACnB;IACD,GAAG,EAAE,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiUZ;CACA,CAAA"}
@@ -1 +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"}
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,aAqctB,CAAA"}