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,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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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
|