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,184 @@
|
|
|
1
|
+
export const scene3d = {
|
|
2
|
+
components: [
|
|
3
|
+
"toolbar",
|
|
4
|
+
"panel",
|
|
5
|
+
"pane",
|
|
6
|
+
"slider",
|
|
7
|
+
"number-input",
|
|
8
|
+
"colour-picker",
|
|
9
|
+
"status-bar",
|
|
10
|
+
],
|
|
11
|
+
extraDeps: {
|
|
12
|
+
three: "^0.170.0",
|
|
13
|
+
"@react-three/fiber": "^8.17.0",
|
|
14
|
+
"@react-three/drei": "^9.117.0",
|
|
15
|
+
},
|
|
16
|
+
extraDevDeps: {
|
|
17
|
+
"@types/three": "^0.170.0",
|
|
18
|
+
},
|
|
19
|
+
app: () => `import { useState, useRef } from "react"
|
|
20
|
+
import { Canvas } from "@react-three/fiber"
|
|
21
|
+
import { OrbitControls, Grid, Environment } from "@react-three/drei"
|
|
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 { NumberInput } from "@/components/substrate/number-input"
|
|
36
|
+
import { Slider } from "@/components/substrate/slider"
|
|
37
|
+
import { ColourPicker } from "@/components/substrate/colour-picker"
|
|
38
|
+
import { StatusBar, StatusBarSection, StatusBarItem } from "@/components/substrate/status-bar"
|
|
39
|
+
import {
|
|
40
|
+
RiCursorLine,
|
|
41
|
+
RiDragMoveLine,
|
|
42
|
+
RiRefreshLine,
|
|
43
|
+
RiExpandDiagonal2Line,
|
|
44
|
+
} from "@remixicon/react"
|
|
45
|
+
|
|
46
|
+
interface SceneObject {
|
|
47
|
+
id: string
|
|
48
|
+
type: "box" | "sphere"
|
|
49
|
+
position: [number, number, number]
|
|
50
|
+
colour: string
|
|
51
|
+
roughness: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const INITIAL_OBJECTS: SceneObject[] = [
|
|
55
|
+
{ id: "box-1", type: "box", position: [-1.5, 0.5, 0], colour: "#6366f1", roughness: 0.4 },
|
|
56
|
+
{ id: "sphere-1", type: "sphere", position: [1.5, 0.6, 0], colour: "#f43f5e", roughness: 0.2 },
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
function SceneMesh({
|
|
60
|
+
object,
|
|
61
|
+
selected,
|
|
62
|
+
onSelect,
|
|
63
|
+
}: {
|
|
64
|
+
object: SceneObject
|
|
65
|
+
selected: boolean
|
|
66
|
+
onSelect: () => void
|
|
67
|
+
}) {
|
|
68
|
+
return (
|
|
69
|
+
<mesh position={object.position} onClick={onSelect}>
|
|
70
|
+
{object.type === "box" ? (
|
|
71
|
+
<boxGeometry args={[1, 1, 1]} />
|
|
72
|
+
) : (
|
|
73
|
+
<sphereGeometry args={[0.6, 32, 32]} />
|
|
74
|
+
)}
|
|
75
|
+
<meshStandardMaterial
|
|
76
|
+
color={object.colour}
|
|
77
|
+
roughness={object.roughness}
|
|
78
|
+
emissive={selected ? "#ffffff" : "#000000"}
|
|
79
|
+
emissiveIntensity={selected ? 0.05 : 0}
|
|
80
|
+
/>
|
|
81
|
+
</mesh>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default function App() {
|
|
86
|
+
const [mode, setMode] = useState("select")
|
|
87
|
+
const [objects, setObjects] = useState(INITIAL_OBJECTS)
|
|
88
|
+
const [selectedId, setSelectedId] = useState<string | null>(null)
|
|
89
|
+
|
|
90
|
+
const selected = objects.find((o) => o.id === selectedId) ?? null
|
|
91
|
+
|
|
92
|
+
function updateSelected(patch: Partial<SceneObject>) {
|
|
93
|
+
if (!selectedId) return
|
|
94
|
+
setObjects((prev) =>
|
|
95
|
+
prev.map((o) => (o.id === selectedId ? { ...o, ...patch } : o))
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div className="flex h-screen flex-col bg-background text-foreground">
|
|
101
|
+
<div className="relative flex flex-1 overflow-hidden">
|
|
102
|
+
<div className="flex-1 bg-canvas">
|
|
103
|
+
<Canvas camera={{ position: [4, 3, 4], fov: 50 }}>
|
|
104
|
+
<ambientLight intensity={0.4} />
|
|
105
|
+
<directionalLight position={[5, 8, 5]} intensity={1} />
|
|
106
|
+
<Environment preset="studio" />
|
|
107
|
+
<Grid infiniteGrid fadeDistance={20} cellColor="#333" sectionColor="#555" />
|
|
108
|
+
<OrbitControls makeDefault />
|
|
109
|
+
{objects.map((obj) => (
|
|
110
|
+
<SceneMesh
|
|
111
|
+
key={obj.id}
|
|
112
|
+
object={obj}
|
|
113
|
+
selected={obj.id === selectedId}
|
|
114
|
+
onSelect={() => setSelectedId(obj.id)}
|
|
115
|
+
/>
|
|
116
|
+
))}
|
|
117
|
+
</Canvas>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<Panel position="right">
|
|
121
|
+
{selected ? (
|
|
122
|
+
<>
|
|
123
|
+
<Pane>
|
|
124
|
+
<PaneHeader>
|
|
125
|
+
<PaneLabel>Position</PaneLabel>
|
|
126
|
+
</PaneHeader>
|
|
127
|
+
<PaneContent>
|
|
128
|
+
<div className="grid grid-cols-3 gap-2">
|
|
129
|
+
<NumberInput label="X" value={selected.position[0]} step={0.1} onChange={(v) => updateSelected({ position: [v, selected.position[1], selected.position[2]] })} />
|
|
130
|
+
<NumberInput label="Y" value={selected.position[1]} step={0.1} onChange={(v) => updateSelected({ position: [selected.position[0], v, selected.position[2]] })} />
|
|
131
|
+
<NumberInput label="Z" value={selected.position[2]} step={0.1} onChange={(v) => updateSelected({ position: [selected.position[0], selected.position[1], v] })} />
|
|
132
|
+
</div>
|
|
133
|
+
</PaneContent>
|
|
134
|
+
</Pane>
|
|
135
|
+
<Pane>
|
|
136
|
+
<PaneHeader>
|
|
137
|
+
<PaneLabel>Material</PaneLabel>
|
|
138
|
+
</PaneHeader>
|
|
139
|
+
<PaneContent className="space-y-3">
|
|
140
|
+
<ColourPicker value={selected.colour} onChange={(c) => updateSelected({ colour: c })} />
|
|
141
|
+
<Slider label="Roughness" min={0} max={1} step={0.01} value={[selected.roughness]} onValueChange={([v]) => updateSelected({ roughness: v })} />
|
|
142
|
+
</PaneContent>
|
|
143
|
+
</Pane>
|
|
144
|
+
</>
|
|
145
|
+
) : (
|
|
146
|
+
<Pane>
|
|
147
|
+
<PaneContent>
|
|
148
|
+
<p className="text-sm text-muted-foreground">Click an object to inspect</p>
|
|
149
|
+
</PaneContent>
|
|
150
|
+
</Pane>
|
|
151
|
+
)}
|
|
152
|
+
</Panel>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<Toolbar>
|
|
156
|
+
<ToolbarToggleGroup value={mode} onValueChange={setMode}>
|
|
157
|
+
<ToolbarToggle value="select" tooltip="Select" shortcut="V">
|
|
158
|
+
<RiCursorLine className="size-4" />
|
|
159
|
+
</ToolbarToggle>
|
|
160
|
+
<ToolbarSeparator />
|
|
161
|
+
<ToolbarToggle value="translate" tooltip="Translate" shortcut="G">
|
|
162
|
+
<RiDragMoveLine className="size-4" />
|
|
163
|
+
</ToolbarToggle>
|
|
164
|
+
<ToolbarToggle value="rotate" tooltip="Rotate" shortcut="R">
|
|
165
|
+
<RiRefreshLine className="size-4" />
|
|
166
|
+
</ToolbarToggle>
|
|
167
|
+
<ToolbarToggle value="scale" tooltip="Scale" shortcut="S">
|
|
168
|
+
<RiExpandDiagonal2Line className="size-4" />
|
|
169
|
+
</ToolbarToggle>
|
|
170
|
+
</ToolbarToggleGroup>
|
|
171
|
+
</Toolbar>
|
|
172
|
+
|
|
173
|
+
<StatusBar>
|
|
174
|
+
<StatusBarSection>
|
|
175
|
+
<StatusBarItem label="Mode" value={mode} />
|
|
176
|
+
{selected && <StatusBarItem label="Selected" value={selected.id} />}
|
|
177
|
+
</StatusBarSection>
|
|
178
|
+
</StatusBar>
|
|
179
|
+
</div>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
`,
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=3d-scene.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"3d-scene.js","sourceRoot":"","sources":["../../src/surfaces/3d-scene.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,OAAO,GAAkB;IACpC,UAAU,EAAE;QACV,SAAS;QACT,OAAO;QACP,MAAM;QACN,QAAQ;QACR,cAAc;QACd,eAAe;QACf,YAAY;KACb;IACD,SAAS,EAAE;QACT,KAAK,EAAE,UAAU;QACjB,oBAAoB,EAAE,SAAS;QAC/B,mBAAmB,EAAE,UAAU;KAChC;IACD,YAAY,EAAE;QACZ,cAAc,EAAE,UAAU;KAC3B;IACD,GAAG,EAAE,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmKZ;CACA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../../src/surfaces/animation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,SAAS,EAAE,aAiNvB,CAAA"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
export const animation = {
|
|
2
|
+
components: [
|
|
3
|
+
"toolbar",
|
|
4
|
+
"panel",
|
|
5
|
+
"pane",
|
|
6
|
+
"timeline",
|
|
7
|
+
"easing-curve-editor",
|
|
8
|
+
"slider",
|
|
9
|
+
"number-input",
|
|
10
|
+
"history-controls",
|
|
11
|
+
"status-bar",
|
|
12
|
+
],
|
|
13
|
+
extraDeps: {
|
|
14
|
+
gsap: "^3.12.0",
|
|
15
|
+
},
|
|
16
|
+
app: () => `import { useState, useRef, useEffect } from "react"
|
|
17
|
+
import {
|
|
18
|
+
Toolbar,
|
|
19
|
+
ToolbarToggleGroup,
|
|
20
|
+
ToolbarToggle,
|
|
21
|
+
ToolbarSeparator,
|
|
22
|
+
} from "@/components/substrate/toolbar"
|
|
23
|
+
import { Panel } from "@/components/substrate/panel"
|
|
24
|
+
import {
|
|
25
|
+
Pane,
|
|
26
|
+
PaneHeader,
|
|
27
|
+
PaneLabel,
|
|
28
|
+
PaneContent,
|
|
29
|
+
} from "@/components/substrate/pane"
|
|
30
|
+
import { Timeline } from "@/components/substrate/timeline"
|
|
31
|
+
import { EasingCurveEditor } from "@/components/substrate/easing-curve-editor"
|
|
32
|
+
import { NumberInput } from "@/components/substrate/number-input"
|
|
33
|
+
import { Slider } from "@/components/substrate/slider"
|
|
34
|
+
import { HistoryControls } from "@/components/substrate/history-controls"
|
|
35
|
+
import { StatusBar, StatusBarSection, StatusBarItem } from "@/components/substrate/status-bar"
|
|
36
|
+
import {
|
|
37
|
+
RiPlayLine,
|
|
38
|
+
RiPauseLine,
|
|
39
|
+
RiStopLine,
|
|
40
|
+
RiSkipBackLine,
|
|
41
|
+
} from "@remixicon/react"
|
|
42
|
+
|
|
43
|
+
interface Keyframe {
|
|
44
|
+
id: string
|
|
45
|
+
time: number
|
|
46
|
+
value: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface Track {
|
|
50
|
+
id: string
|
|
51
|
+
label: string
|
|
52
|
+
keyframes: Keyframe[]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const INITIAL_TRACKS: Track[] = [
|
|
56
|
+
{
|
|
57
|
+
id: "position-x",
|
|
58
|
+
label: "Position X",
|
|
59
|
+
keyframes: [
|
|
60
|
+
{ id: "k1", time: 0, value: 50 },
|
|
61
|
+
{ id: "k2", time: 30, value: 300 },
|
|
62
|
+
{ id: "k3", time: 60, value: 150 },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "opacity",
|
|
67
|
+
label: "Opacity",
|
|
68
|
+
keyframes: [
|
|
69
|
+
{ id: "k4", time: 0, value: 100 },
|
|
70
|
+
{ id: "k5", time: 20, value: 30 },
|
|
71
|
+
{ id: "k6", time: 50, value: 100 },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
export default function App() {
|
|
77
|
+
const [playing, setPlaying] = useState(false)
|
|
78
|
+
const [currentFrame, setCurrentFrame] = useState(0)
|
|
79
|
+
const [totalFrames] = useState(60)
|
|
80
|
+
const [tracks] = useState(INITIAL_TRACKS)
|
|
81
|
+
const [easing, setEasing] = useState<[number, number, number, number]>([0.4, 0, 0.2, 1])
|
|
82
|
+
const animRef = useRef<number | null>(null)
|
|
83
|
+
const boxRef = useRef<HTMLDivElement>(null)
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (!playing) {
|
|
87
|
+
if (animRef.current) cancelAnimationFrame(animRef.current)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let frame = currentFrame
|
|
92
|
+
const tick = () => {
|
|
93
|
+
frame = (frame + 1) % totalFrames
|
|
94
|
+
setCurrentFrame(frame)
|
|
95
|
+
animRef.current = requestAnimationFrame(tick)
|
|
96
|
+
}
|
|
97
|
+
animRef.current = requestAnimationFrame(tick)
|
|
98
|
+
return () => {
|
|
99
|
+
if (animRef.current) cancelAnimationFrame(animRef.current)
|
|
100
|
+
}
|
|
101
|
+
}, [playing])
|
|
102
|
+
|
|
103
|
+
// Interpolate position and opacity from keyframes
|
|
104
|
+
const posX = interpolate(tracks[0].keyframes, currentFrame)
|
|
105
|
+
const opacity = interpolate(tracks[1].keyframes, currentFrame) / 100
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div className="flex h-screen flex-col bg-background text-foreground">
|
|
109
|
+
<div className="relative flex flex-1 overflow-hidden">
|
|
110
|
+
{/* Preview stage */}
|
|
111
|
+
<div className="flex flex-1 items-center justify-center bg-canvas">
|
|
112
|
+
<div
|
|
113
|
+
ref={boxRef}
|
|
114
|
+
className="size-20 rounded-lg bg-primary"
|
|
115
|
+
style={{
|
|
116
|
+
transform: \`translateX(\${posX - 150}px)\`,
|
|
117
|
+
opacity,
|
|
118
|
+
}}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<Panel position="right">
|
|
123
|
+
<Pane>
|
|
124
|
+
<PaneHeader>
|
|
125
|
+
<PaneLabel>Easing</PaneLabel>
|
|
126
|
+
</PaneHeader>
|
|
127
|
+
<PaneContent>
|
|
128
|
+
<EasingCurveEditor value={easing} onChange={setEasing} />
|
|
129
|
+
</PaneContent>
|
|
130
|
+
</Pane>
|
|
131
|
+
<Pane>
|
|
132
|
+
<PaneHeader>
|
|
133
|
+
<PaneLabel>Keyframe</PaneLabel>
|
|
134
|
+
</PaneHeader>
|
|
135
|
+
<PaneContent>
|
|
136
|
+
<div className="grid grid-cols-2 gap-2">
|
|
137
|
+
<NumberInput label="Frame" value={currentFrame} onChange={setCurrentFrame} min={0} max={totalFrames} />
|
|
138
|
+
<NumberInput label="Value" defaultValue={0} />
|
|
139
|
+
</div>
|
|
140
|
+
</PaneContent>
|
|
141
|
+
</Pane>
|
|
142
|
+
</Panel>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
{/* Timeline */}
|
|
146
|
+
<div className="h-48 border-t border-border bg-panel">
|
|
147
|
+
<Timeline
|
|
148
|
+
tracks={tracks}
|
|
149
|
+
currentFrame={currentFrame}
|
|
150
|
+
totalFrames={totalFrames}
|
|
151
|
+
onFrameChange={setCurrentFrame}
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<Toolbar>
|
|
156
|
+
<ToolbarToggle
|
|
157
|
+
value="back"
|
|
158
|
+
tooltip="Go to start"
|
|
159
|
+
onPressedChange={() => { setCurrentFrame(0); setPlaying(false) }}
|
|
160
|
+
>
|
|
161
|
+
<RiSkipBackLine className="size-4" />
|
|
162
|
+
</ToolbarToggle>
|
|
163
|
+
<ToolbarToggle
|
|
164
|
+
value="play"
|
|
165
|
+
pressed={playing}
|
|
166
|
+
tooltip={playing ? "Pause" : "Play"}
|
|
167
|
+
shortcut="Space"
|
|
168
|
+
onPressedChange={() => setPlaying(!playing)}
|
|
169
|
+
>
|
|
170
|
+
{playing ? <RiPauseLine className="size-4" /> : <RiPlayLine className="size-4" />}
|
|
171
|
+
</ToolbarToggle>
|
|
172
|
+
<ToolbarToggle
|
|
173
|
+
value="stop"
|
|
174
|
+
tooltip="Stop"
|
|
175
|
+
onPressedChange={() => { setPlaying(false); setCurrentFrame(0) }}
|
|
176
|
+
>
|
|
177
|
+
<RiStopLine className="size-4" />
|
|
178
|
+
</ToolbarToggle>
|
|
179
|
+
</Toolbar>
|
|
180
|
+
|
|
181
|
+
<StatusBar>
|
|
182
|
+
<StatusBarSection>
|
|
183
|
+
<StatusBarItem label="Frame" value={\`\${currentFrame} / \${totalFrames}\`} />
|
|
184
|
+
<StatusBarItem label="State" value={playing ? "Playing" : "Stopped"} />
|
|
185
|
+
</StatusBarSection>
|
|
186
|
+
<StatusBarSection>
|
|
187
|
+
<HistoryControls canUndo={false} canRedo={false} onUndo={() => {}} onRedo={() => {}} />
|
|
188
|
+
</StatusBarSection>
|
|
189
|
+
</StatusBar>
|
|
190
|
+
</div>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function interpolate(keyframes: Keyframe[], frame: number): number {
|
|
195
|
+
if (keyframes.length === 0) return 0
|
|
196
|
+
if (frame <= keyframes[0].time) return keyframes[0].value
|
|
197
|
+
if (frame >= keyframes[keyframes.length - 1].time) return keyframes[keyframes.length - 1].value
|
|
198
|
+
|
|
199
|
+
for (let i = 0; i < keyframes.length - 1; i++) {
|
|
200
|
+
const a = keyframes[i]
|
|
201
|
+
const b = keyframes[i + 1]
|
|
202
|
+
if (frame >= a.time && frame <= b.time) {
|
|
203
|
+
const t = (frame - a.time) / (b.time - a.time)
|
|
204
|
+
return a.value + (b.value - a.value) * t
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return 0
|
|
208
|
+
}
|
|
209
|
+
`,
|
|
210
|
+
};
|
|
211
|
+
//# sourceMappingURL=animation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animation.js","sourceRoot":"","sources":["../../src/surfaces/animation.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,SAAS,GAAkB;IACtC,UAAU,EAAE;QACV,SAAS;QACT,OAAO;QACP,MAAM;QACN,UAAU;QACV,qBAAqB;QACrB,QAAQ;QACR,cAAc;QACd,kBAAkB;QAClB,YAAY;KACb;IACD,SAAS,EAAE;QACT,IAAI,EAAE,SAAS;KAChB;IACD,GAAG,EAAE,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiMZ;CACA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blank.d.ts","sourceRoot":"","sources":["../../src/surfaces/blank.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,KAAK,EAAE,aAsEnB,CAAA"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export const blank = {
|
|
2
|
+
components: [
|
|
3
|
+
"toolbar",
|
|
4
|
+
"panel",
|
|
5
|
+
"pane",
|
|
6
|
+
"surface",
|
|
7
|
+
"empty-state",
|
|
8
|
+
"status-bar",
|
|
9
|
+
],
|
|
10
|
+
app: () => `import { useState } from "react"
|
|
11
|
+
import {
|
|
12
|
+
Toolbar,
|
|
13
|
+
ToolbarToggleGroup,
|
|
14
|
+
ToolbarToggle,
|
|
15
|
+
ToolbarSeparator,
|
|
16
|
+
} from "@/components/substrate/toolbar"
|
|
17
|
+
import { Panel } from "@/components/substrate/panel"
|
|
18
|
+
import { Surface } from "@/components/substrate/surface"
|
|
19
|
+
import { EmptyState } from "@/components/substrate/empty-state"
|
|
20
|
+
import { StatusBar, StatusBarSection, StatusBarItem } from "@/components/substrate/status-bar"
|
|
21
|
+
import { RiCursorLine, RiHand } from "@remixicon/react"
|
|
22
|
+
|
|
23
|
+
export default function App() {
|
|
24
|
+
const [tool, setTool] = useState("select")
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="flex h-screen flex-col bg-background text-foreground">
|
|
28
|
+
<div className="relative flex flex-1 overflow-hidden">
|
|
29
|
+
<Panel position="left">
|
|
30
|
+
<EmptyState
|
|
31
|
+
title="Left panel"
|
|
32
|
+
description="Add layers, assets, or navigation here"
|
|
33
|
+
/>
|
|
34
|
+
</Panel>
|
|
35
|
+
|
|
36
|
+
<Surface className="flex-1">
|
|
37
|
+
<EmptyState
|
|
38
|
+
title="Your surface here"
|
|
39
|
+
description="Replace this with your rendering engine — canvas, Three.js, timeline, or anything else"
|
|
40
|
+
/>
|
|
41
|
+
</Surface>
|
|
42
|
+
|
|
43
|
+
<Panel position="right">
|
|
44
|
+
<EmptyState
|
|
45
|
+
title="Right panel"
|
|
46
|
+
description="Add property controls and inspectors here"
|
|
47
|
+
/>
|
|
48
|
+
</Panel>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<Toolbar>
|
|
52
|
+
<ToolbarToggleGroup value={tool} onValueChange={setTool}>
|
|
53
|
+
<ToolbarToggle value="select" tooltip="Select" shortcut="V">
|
|
54
|
+
<RiCursorLine className="size-4" />
|
|
55
|
+
</ToolbarToggle>
|
|
56
|
+
<ToolbarToggle value="hand" tooltip="Hand" shortcut="H">
|
|
57
|
+
<RiHand className="size-4" />
|
|
58
|
+
</ToolbarToggle>
|
|
59
|
+
</ToolbarToggleGroup>
|
|
60
|
+
</Toolbar>
|
|
61
|
+
|
|
62
|
+
<StatusBar>
|
|
63
|
+
<StatusBarSection>
|
|
64
|
+
<StatusBarItem label="Tool" value={tool} />
|
|
65
|
+
</StatusBarSection>
|
|
66
|
+
</StatusBar>
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
`,
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=blank.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blank.js","sourceRoot":"","sources":["../../src/surfaces/blank.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,KAAK,GAAkB;IAClC,UAAU,EAAE;QACV,SAAS;QACT,OAAO;QACP,MAAM;QACN,SAAS;QACT,aAAa;QACb,YAAY;KACb;IACD,GAAG,EAAE,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4DZ;CACA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-2d.d.ts","sourceRoot":"","sources":["../../src/surfaces/canvas-2d.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,QAAQ,EAAE,aAyItB,CAAA"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
export const canvas2d = {
|
|
2
|
+
components: [
|
|
3
|
+
"design-canvas",
|
|
4
|
+
"toolbar",
|
|
5
|
+
"panel",
|
|
6
|
+
"pane",
|
|
7
|
+
"slider",
|
|
8
|
+
"layer-list",
|
|
9
|
+
"number-input",
|
|
10
|
+
"colour-picker",
|
|
11
|
+
"history-controls",
|
|
12
|
+
"status-bar",
|
|
13
|
+
"zoom-controls",
|
|
14
|
+
],
|
|
15
|
+
app: () => `import { useState } from "react"
|
|
16
|
+
import { DesignCanvas } from "@/components/substrate/design-canvas"
|
|
17
|
+
import {
|
|
18
|
+
Toolbar,
|
|
19
|
+
ToolbarToggleGroup,
|
|
20
|
+
ToolbarToggle,
|
|
21
|
+
ToolbarSeparator,
|
|
22
|
+
} from "@/components/substrate/toolbar"
|
|
23
|
+
import { Panel } from "@/components/substrate/panel"
|
|
24
|
+
import {
|
|
25
|
+
Pane,
|
|
26
|
+
PaneHeader,
|
|
27
|
+
PaneLabel,
|
|
28
|
+
PaneContent,
|
|
29
|
+
} from "@/components/substrate/pane"
|
|
30
|
+
import { LayerList, LayerItem, LayerIcon, LayerName } from "@/components/substrate/layer-list"
|
|
31
|
+
import { NumberInput } from "@/components/substrate/number-input"
|
|
32
|
+
import { ColourPicker } from "@/components/substrate/colour-picker"
|
|
33
|
+
import { HistoryControls } from "@/components/substrate/history-controls"
|
|
34
|
+
import { StatusBar, StatusBarSection, StatusBarItem } from "@/components/substrate/status-bar"
|
|
35
|
+
import { ZoomControls } from "@/components/substrate/zoom-controls"
|
|
36
|
+
import {
|
|
37
|
+
RiCursorLine,
|
|
38
|
+
RiHand,
|
|
39
|
+
RiSquareLine,
|
|
40
|
+
RiCircleLine,
|
|
41
|
+
RiSubtractLine,
|
|
42
|
+
RiText,
|
|
43
|
+
RiArtboard2Line,
|
|
44
|
+
} from "@remixicon/react"
|
|
45
|
+
|
|
46
|
+
const TOOLS = [
|
|
47
|
+
{ value: "select", icon: RiCursorLine, label: "Select", shortcut: "V" },
|
|
48
|
+
{ value: "hand", icon: RiHand, label: "Hand", shortcut: "H" },
|
|
49
|
+
"separator",
|
|
50
|
+
{ value: "rectangle", icon: RiSquareLine, label: "Rectangle", shortcut: "R" },
|
|
51
|
+
{ value: "ellipse", icon: RiCircleLine, label: "Ellipse", shortcut: "O" },
|
|
52
|
+
{ value: "line", icon: RiSubtractLine, label: "Line", shortcut: "L" },
|
|
53
|
+
{ value: "text", icon: RiText, label: "Text", shortcut: "T" },
|
|
54
|
+
{ value: "frame", icon: RiArtboard2Line, label: "Frame", shortcut: "F" },
|
|
55
|
+
] as const
|
|
56
|
+
|
|
57
|
+
export default function App() {
|
|
58
|
+
const [tool, setTool] = useState("select")
|
|
59
|
+
const [zoom, setZoom] = useState(1)
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className="flex h-screen flex-col bg-background text-foreground">
|
|
63
|
+
<div className="relative flex flex-1 overflow-hidden">
|
|
64
|
+
<Panel position="left">
|
|
65
|
+
<Pane>
|
|
66
|
+
<PaneHeader>
|
|
67
|
+
<PaneLabel>Layers</PaneLabel>
|
|
68
|
+
</PaneHeader>
|
|
69
|
+
<PaneContent>
|
|
70
|
+
<LayerList>
|
|
71
|
+
<LayerItem>
|
|
72
|
+
<LayerIcon><RiSquareLine className="size-3.5" /></LayerIcon>
|
|
73
|
+
<LayerName>Rectangle 1</LayerName>
|
|
74
|
+
</LayerItem>
|
|
75
|
+
<LayerItem>
|
|
76
|
+
<LayerIcon><RiCircleLine className="size-3.5" /></LayerIcon>
|
|
77
|
+
<LayerName>Ellipse 1</LayerName>
|
|
78
|
+
</LayerItem>
|
|
79
|
+
</LayerList>
|
|
80
|
+
</PaneContent>
|
|
81
|
+
</Pane>
|
|
82
|
+
</Panel>
|
|
83
|
+
|
|
84
|
+
<DesignCanvas />
|
|
85
|
+
|
|
86
|
+
<Panel position="right">
|
|
87
|
+
<Pane>
|
|
88
|
+
<PaneHeader>
|
|
89
|
+
<PaneLabel>Transform</PaneLabel>
|
|
90
|
+
</PaneHeader>
|
|
91
|
+
<PaneContent>
|
|
92
|
+
<div className="grid grid-cols-2 gap-2">
|
|
93
|
+
<NumberInput label="X" defaultValue={0} />
|
|
94
|
+
<NumberInput label="Y" defaultValue={0} />
|
|
95
|
+
<NumberInput label="W" defaultValue={100} />
|
|
96
|
+
<NumberInput label="H" defaultValue={100} />
|
|
97
|
+
</div>
|
|
98
|
+
</PaneContent>
|
|
99
|
+
</Pane>
|
|
100
|
+
<Pane>
|
|
101
|
+
<PaneHeader>
|
|
102
|
+
<PaneLabel>Fill</PaneLabel>
|
|
103
|
+
</PaneHeader>
|
|
104
|
+
<PaneContent>
|
|
105
|
+
<ColourPicker />
|
|
106
|
+
</PaneContent>
|
|
107
|
+
</Pane>
|
|
108
|
+
</Panel>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<Toolbar>
|
|
112
|
+
<ToolbarToggleGroup value={tool} onValueChange={setTool}>
|
|
113
|
+
{TOOLS.map((t, i) =>
|
|
114
|
+
t === "separator" ? (
|
|
115
|
+
<ToolbarSeparator key={i} />
|
|
116
|
+
) : (
|
|
117
|
+
<ToolbarToggle key={t.value} value={t.value} tooltip={t.label} shortcut={t.shortcut}>
|
|
118
|
+
<t.icon className="size-4" />
|
|
119
|
+
</ToolbarToggle>
|
|
120
|
+
)
|
|
121
|
+
)}
|
|
122
|
+
</ToolbarToggleGroup>
|
|
123
|
+
</Toolbar>
|
|
124
|
+
|
|
125
|
+
<StatusBar>
|
|
126
|
+
<StatusBarSection>
|
|
127
|
+
<StatusBarItem label="Tool" value={tool} />
|
|
128
|
+
</StatusBarSection>
|
|
129
|
+
<StatusBarSection>
|
|
130
|
+
<HistoryControls canUndo={false} canRedo={false} onUndo={() => {}} onRedo={() => {}} />
|
|
131
|
+
<ZoomControls zoom={zoom} onZoomIn={() => setZoom((z) => Math.min(z * 1.25, 10))} onZoomOut={() => setZoom((z) => Math.max(z / 1.25, 0.1))} onReset={() => setZoom(1)} />
|
|
132
|
+
</StatusBarSection>
|
|
133
|
+
</StatusBar>
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
`,
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=canvas-2d.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-2d.js","sourceRoot":"","sources":["../../src/surfaces/canvas-2d.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,QAAQ,GAAkB;IACrC,UAAU,EAAE;QACV,eAAe;QACf,SAAS;QACT,OAAO;QACP,MAAM;QACN,QAAQ;QACR,YAAY;QACZ,cAAc;QACd,eAAe;QACf,kBAAkB;QAClB,YAAY;QACZ,eAAe;KAChB;IACD,GAAG,EAAE,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0HZ;CACA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-vis.d.ts","sourceRoot":"","sources":["../../src/surfaces/data-vis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,OAAO,EAAE,aA6KrB,CAAA"}
|