@stevejtrettel/shader-sandbox 0.1.3 → 0.1.4
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/README.md +220 -23
- package/bin/cli.js +106 -14
- package/dist-lib/app/App.d.ts +143 -15
- package/dist-lib/app/App.d.ts.map +1 -1
- package/dist-lib/app/App.js +1343 -108
- package/dist-lib/app/app.css +349 -24
- package/dist-lib/app/types.d.ts +48 -5
- package/dist-lib/app/types.d.ts.map +1 -1
- package/dist-lib/editor/EditorPanel.d.ts +2 -2
- package/dist-lib/editor/EditorPanel.d.ts.map +1 -1
- package/dist-lib/editor/EditorPanel.js +1 -1
- package/dist-lib/editor/editor-panel.css +55 -32
- package/dist-lib/editor/prism-editor.css +16 -16
- package/dist-lib/embed.js +1 -1
- package/dist-lib/engine/{ShadertoyEngine.d.ts → ShaderEngine.d.ts} +134 -10
- package/dist-lib/engine/ShaderEngine.d.ts.map +1 -0
- package/dist-lib/engine/ShaderEngine.js +1523 -0
- package/dist-lib/engine/glHelpers.d.ts +24 -0
- package/dist-lib/engine/glHelpers.d.ts.map +1 -1
- package/dist-lib/engine/glHelpers.js +88 -0
- package/dist-lib/engine/std140.d.ts +47 -0
- package/dist-lib/engine/std140.d.ts.map +1 -0
- package/dist-lib/engine/std140.js +119 -0
- package/dist-lib/engine/types.d.ts +55 -5
- package/dist-lib/engine/types.d.ts.map +1 -1
- package/dist-lib/engine/types.js +1 -1
- package/dist-lib/index.d.ts +4 -3
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +2 -1
- package/dist-lib/layouts/SplitLayout.d.ts +2 -1
- package/dist-lib/layouts/SplitLayout.d.ts.map +1 -1
- package/dist-lib/layouts/SplitLayout.js +3 -0
- package/dist-lib/layouts/TabbedLayout.d.ts.map +1 -1
- package/dist-lib/layouts/UILayout.d.ts +55 -0
- package/dist-lib/layouts/UILayout.d.ts.map +1 -0
- package/dist-lib/layouts/UILayout.js +147 -0
- package/dist-lib/layouts/default.css +2 -2
- package/dist-lib/layouts/index.d.ts +11 -1
- package/dist-lib/layouts/index.d.ts.map +1 -1
- package/dist-lib/layouts/index.js +17 -1
- package/dist-lib/layouts/split.css +33 -31
- package/dist-lib/layouts/tabbed.css +127 -74
- package/dist-lib/layouts/types.d.ts +14 -3
- package/dist-lib/layouts/types.d.ts.map +1 -1
- package/dist-lib/main.js +33 -0
- package/dist-lib/project/configHelpers.d.ts +45 -0
- package/dist-lib/project/configHelpers.d.ts.map +1 -0
- package/dist-lib/project/configHelpers.js +196 -0
- package/dist-lib/project/generatedLoader.d.ts +2 -2
- package/dist-lib/project/generatedLoader.d.ts.map +1 -1
- package/dist-lib/project/generatedLoader.js +23 -5
- package/dist-lib/project/loadProject.d.ts +6 -6
- package/dist-lib/project/loadProject.d.ts.map +1 -1
- package/dist-lib/project/loadProject.js +396 -144
- package/dist-lib/project/loaderHelper.d.ts +4 -4
- package/dist-lib/project/loaderHelper.d.ts.map +1 -1
- package/dist-lib/project/loaderHelper.js +278 -116
- package/dist-lib/project/types.d.ts +292 -13
- package/dist-lib/project/types.d.ts.map +1 -1
- package/dist-lib/project/types.js +13 -1
- package/dist-lib/styles/base.css +5 -1
- package/dist-lib/uniforms/UniformControls.d.ts +60 -0
- package/dist-lib/uniforms/UniformControls.d.ts.map +1 -0
- package/dist-lib/uniforms/UniformControls.js +518 -0
- package/dist-lib/uniforms/UniformStore.d.ts +74 -0
- package/dist-lib/uniforms/UniformStore.d.ts.map +1 -0
- package/dist-lib/uniforms/UniformStore.js +145 -0
- package/dist-lib/uniforms/UniformsPanel.d.ts +53 -0
- package/dist-lib/uniforms/UniformsPanel.d.ts.map +1 -0
- package/dist-lib/uniforms/UniformsPanel.js +124 -0
- package/dist-lib/uniforms/index.d.ts +11 -0
- package/dist-lib/uniforms/index.d.ts.map +1 -0
- package/dist-lib/uniforms/index.js +8 -0
- package/package.json +1 -1
- package/src/app/App.ts +1469 -126
- package/src/app/app.css +349 -24
- package/src/app/types.ts +53 -5
- package/src/editor/EditorPanel.ts +5 -5
- package/src/editor/editor-panel.css +55 -32
- package/src/editor/prism-editor.css +16 -16
- package/src/embed.ts +1 -1
- package/src/engine/ShaderEngine.ts +1934 -0
- package/src/engine/glHelpers.ts +117 -0
- package/src/engine/std140.ts +136 -0
- package/src/engine/types.ts +69 -5
- package/src/index.ts +4 -3
- package/src/layouts/SplitLayout.ts +8 -3
- package/src/layouts/TabbedLayout.ts +3 -3
- package/src/layouts/UILayout.ts +185 -0
- package/src/layouts/default.css +2 -2
- package/src/layouts/index.ts +20 -1
- package/src/layouts/split.css +33 -31
- package/src/layouts/tabbed.css +127 -74
- package/src/layouts/types.ts +19 -3
- package/src/layouts/ui.css +289 -0
- package/src/main.ts +39 -1
- package/src/project/configHelpers.ts +225 -0
- package/src/project/generatedLoader.ts +27 -6
- package/src/project/loadProject.ts +459 -173
- package/src/project/loaderHelper.ts +377 -130
- package/src/project/types.ts +360 -14
- package/src/styles/base.css +5 -1
- package/src/styles/theme.css +292 -0
- package/src/uniforms/UniformControls.ts +660 -0
- package/src/uniforms/UniformStore.ts +166 -0
- package/src/uniforms/UniformsPanel.ts +163 -0
- package/src/uniforms/index.ts +13 -0
- package/src/uniforms/uniform-controls.css +342 -0
- package/src/uniforms/uniforms-panel.css +277 -0
- package/templates/shaders/example-buffer/config.json +1 -0
- package/dist-lib/engine/ShadertoyEngine.d.ts.map +0 -1
- package/dist-lib/engine/ShadertoyEngine.js +0 -704
- package/src/engine/ShadertoyEngine.ts +0 -929
package/README.md
CHANGED
|
@@ -5,13 +5,20 @@ A lightweight, Shadertoy-compatible GLSL shader development environment. Copy sh
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Shadertoy Compatibility** - Copy/paste shaders directly from Shadertoy
|
|
8
|
-
- **Full Shadertoy Uniforms** - `iTime`, `iResolution`, `iFrame`, `iMouse`, `iTimeDelta`, `iChannel0-3`
|
|
8
|
+
- **Full Shadertoy Uniforms** - `iTime`, `iResolution`, `iFrame`, `iMouse`, `iTimeDelta`, `iDate`, `iFrameRate`, `iChannel0-3`
|
|
9
9
|
- **Multi-Buffer Rendering** - BufferA-D passes with correct ping-pong semantics
|
|
10
|
-
- **Texture Support** - Load images with configurable filtering and wrapping
|
|
11
|
-
- **Keyboard Input** - Full keyboard state via
|
|
10
|
+
- **Texture Support** - Load images (including cubemaps) with configurable filtering and wrapping
|
|
11
|
+
- **Keyboard Input** - Full keyboard state via texture. In standard mode, key constants (`KEY_A`–`KEY_Z`, `KEY_SPACE`, etc.) and helpers (`isKeyDown`, `keyToggle`) are auto-injected
|
|
12
|
+
- **Audio Input** - Microphone FFT spectrum and waveform as a texture, as in shadertoy
|
|
13
|
+
- **Webcam & Video** - Live webcam or video files as channel inputs, as in shadertoy
|
|
14
|
+
- **Custom Uniforms** - Float, int, bool, vec2, vec3, vec4 sliders and color pickers via config. *An extension beyond shadertoy*
|
|
15
|
+
- **UBO Array Uniforms** - Large data arrays (positions, colors, matrices) via Uniform Buffer Objects. *An extension beyond shadertoy*
|
|
16
|
+
- **Scripting API** - JavaScript hooks for per-frame computation, texture upload, and GPU readback. *An extension beyond shadertoy*
|
|
17
|
+
- **Touch Support** - Multi-touch, pinch, and gesture uniforms for mobile
|
|
12
18
|
- **Live Code Editing** - Edit shaders in the browser with instant recompilation
|
|
13
19
|
- **Multiple Layouts** - Fullscreen, split-view, or tabbed code display
|
|
14
20
|
- **Playback Controls** - Play/pause, reset, and screenshot capture
|
|
21
|
+
- **Themes** - Light, dark, or system-following theme
|
|
15
22
|
|
|
16
23
|
## Quick Start
|
|
17
24
|
|
|
@@ -131,7 +138,6 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
|
131
138
|
|
|
132
139
|
Place an image in your shader folder and reference it in config:
|
|
133
140
|
|
|
134
|
-
**shaders/my-shader/config.json:**
|
|
135
141
|
```json
|
|
136
142
|
{
|
|
137
143
|
"Image": {
|
|
@@ -140,30 +146,135 @@ Place an image in your shader folder and reference it in config:
|
|
|
140
146
|
}
|
|
141
147
|
```
|
|
142
148
|
|
|
143
|
-
|
|
149
|
+
With options:
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"Image": {
|
|
153
|
+
"iChannel0": {
|
|
154
|
+
"texture": "photo.jpg",
|
|
155
|
+
"filter": "nearest",
|
|
156
|
+
"wrap": "clamp"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Custom Uniforms (Slider Controls)
|
|
163
|
+
|
|
164
|
+
Add interactive controls to your shader by defining uniforms in config:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"controls": true,
|
|
169
|
+
"uniforms": {
|
|
170
|
+
"uSpeed": { "type": "float", "value": 1.0, "min": 0.0, "max": 5.0, "label": "Speed" },
|
|
171
|
+
"uColor": { "type": "vec3", "value": [1.0, 0.5, 0.2], "color": true, "label": "Color" },
|
|
172
|
+
"uAnimate": { "type": "bool", "value": true, "label": "Animate" }
|
|
173
|
+
},
|
|
174
|
+
"Image": {}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Uniforms declared in config are **auto-injected** into your shader code — you don't need to write `uniform` declarations. Just use them directly:
|
|
179
|
+
|
|
144
180
|
```glsl
|
|
145
181
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
146
182
|
vec2 uv = fragCoord / iResolution.xy;
|
|
147
|
-
|
|
148
|
-
|
|
183
|
+
float t = uAnimate ? iTime * uSpeed : 0.0;
|
|
184
|
+
vec3 col = uColor * (0.5 + 0.5 * sin(uv.x * 10.0 - t));
|
|
185
|
+
fragColor = vec4(col, 1.0);
|
|
149
186
|
}
|
|
150
187
|
```
|
|
151
188
|
|
|
152
|
-
|
|
189
|
+
#### Supported Uniform Types
|
|
190
|
+
|
|
191
|
+
| Type | UI Control | Config Fields |
|
|
192
|
+
|------|-----------|---------------|
|
|
193
|
+
| `float` | Slider | `value`, `min` (0), `max` (1), `step` (0.01) |
|
|
194
|
+
| `int` | Discrete slider | `value`, `min` (0), `max` (10), `step` (1) |
|
|
195
|
+
| `bool` | Toggle | `value` |
|
|
196
|
+
| `vec2` | XY pad | `value`, `min` ([0,0]), `max` ([1,1]) |
|
|
197
|
+
| `vec3` | 3 sliders or color picker | `value`, `color` (false), `min`, `max`, `step` |
|
|
198
|
+
| `vec4` | 4 sliders or color+alpha picker | `value`, `color` (false), `min`, `max`, `step` |
|
|
199
|
+
|
|
200
|
+
Set `"hidden": true` on any scalar uniform to exclude it from the UI panel (useful for script-controlled values).
|
|
201
|
+
|
|
202
|
+
#### Array Uniforms (UBOs)
|
|
203
|
+
|
|
204
|
+
For large data arrays (positions, matrices, etc.), use array uniforms backed by Uniform Buffer Objects:
|
|
205
|
+
|
|
153
206
|
```json
|
|
154
207
|
{
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
208
|
+
"uniforms": {
|
|
209
|
+
"matrices": { "type": "mat3", "count": 128 },
|
|
210
|
+
"matrixCount": { "type": "int", "value": 1, "hidden": true }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Array uniforms support types: `float`, `vec2`, `vec3`, `vec4`, `mat3`, `mat4`. They have no UI — data is provided from JavaScript via `engine.setUniformValue()`.
|
|
216
|
+
|
|
217
|
+
In the compiled shader, array uniforms are wrapped in a `layout(std140)` uniform block with a `_ub_` prefix (e.g., `_ub_matrices`). The array variable itself uses the original name, so you reference it directly in GLSL:
|
|
218
|
+
|
|
219
|
+
```glsl
|
|
220
|
+
// Auto-injected by the engine (you don't write this):
|
|
221
|
+
// layout(std140) uniform _ub_matrices {
|
|
222
|
+
// mat3 matrices[128];
|
|
223
|
+
// };
|
|
224
|
+
|
|
225
|
+
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
226
|
+
for (int i = 0; i < matrixCount; i++) {
|
|
227
|
+
vec3 p = matrices[i] * vec3(fragCoord, 1.0);
|
|
228
|
+
// ...
|
|
160
229
|
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
See the [Configuration Reference](docs/learn/configuration.md) for all uniform types.
|
|
234
|
+
|
|
235
|
+
### Scripting (JavaScript Hooks)
|
|
236
|
+
|
|
237
|
+
For computed data that changes every frame, add a `script.js` to your shader folder:
|
|
238
|
+
|
|
239
|
+
**shaders/my-shader/script.js:**
|
|
240
|
+
```js
|
|
241
|
+
const COUNT = 32;
|
|
242
|
+
|
|
243
|
+
export function onFrame(engine, time) {
|
|
244
|
+
const data = new Float32Array(COUNT * 4);
|
|
245
|
+
for (let i = 0; i < COUNT; i++) {
|
|
246
|
+
const phase = (i / COUNT) * Math.PI * 2.0;
|
|
247
|
+
data[i * 4 + 0] = 0.5 + Math.cos(time + phase) * 0.3; // x
|
|
248
|
+
data[i * 4 + 1] = 0.5 + Math.sin(time + phase) * 0.3; // y
|
|
249
|
+
data[i * 4 + 2] = 0.02; // radius
|
|
250
|
+
data[i * 4 + 3] = i / COUNT; // hue
|
|
161
251
|
}
|
|
252
|
+
engine.setUniformValue('positions', data);
|
|
162
253
|
}
|
|
163
254
|
```
|
|
164
255
|
|
|
165
|
-
|
|
166
|
-
|
|
256
|
+
Scripts can export `setup(engine)` (called once) and/or `onFrame(engine, time, deltaTime, frame)` (called every frame).
|
|
257
|
+
|
|
258
|
+
The script API provides:
|
|
259
|
+
- `engine.setUniformValue(name, value)` — set any uniform
|
|
260
|
+
- `engine.getUniformValue(name)` — read current value
|
|
261
|
+
- `engine.updateTexture(name, width, height, data)` — upload a texture from JS
|
|
262
|
+
- `engine.readPixels(passName, x, y, w, h)` — read pixels from a buffer (GPU readback)
|
|
263
|
+
- `engine.width` / `engine.height` — canvas dimensions
|
|
264
|
+
|
|
265
|
+
## Channel Types
|
|
266
|
+
|
|
267
|
+
Channels can be bound using string shortcuts or full objects:
|
|
268
|
+
|
|
269
|
+
| Shorthand | Object Form | Description |
|
|
270
|
+
|-----------|-------------|-------------|
|
|
271
|
+
| `"BufferA"` | `{ "buffer": "BufferA" }` | Buffer pass output |
|
|
272
|
+
| `"photo.jpg"` | `{ "texture": "photo.jpg" }` | Image texture |
|
|
273
|
+
| `"keyboard"` | `{ "keyboard": true }` | Keyboard state |
|
|
274
|
+
| `"audio"` | `{ "audio": true }` | Microphone FFT + waveform |
|
|
275
|
+
| `"webcam"` | `{ "webcam": true }` | Live webcam feed |
|
|
276
|
+
| — | `{ "video": "clip.mp4" }` | Video file |
|
|
277
|
+
| — | `{ "script": "myData" }` | Script-uploaded texture |
|
|
167
278
|
|
|
168
279
|
## Layouts
|
|
169
280
|
|
|
@@ -182,14 +293,6 @@ Control how the shader is displayed in `config.json`:
|
|
|
182
293
|
| `tabbed` | Tabs to switch between shader and code |
|
|
183
294
|
| `split` | Side-by-side shader and code editor |
|
|
184
295
|
|
|
185
|
-
## Keyboard Shortcuts
|
|
186
|
-
|
|
187
|
-
| Key | Action |
|
|
188
|
-
|-----|--------|
|
|
189
|
-
| **S** | Save screenshot (PNG) |
|
|
190
|
-
| **Space** | Play/Pause |
|
|
191
|
-
| **R** | Reset to frame 0 |
|
|
192
|
-
|
|
193
296
|
## Shadertoy Uniforms
|
|
194
297
|
|
|
195
298
|
All standard Shadertoy uniforms are supported:
|
|
@@ -200,11 +303,105 @@ All standard Shadertoy uniforms are supported:
|
|
|
200
303
|
| `iTime` | `float` | Elapsed time in seconds |
|
|
201
304
|
| `iTimeDelta` | `float` | Time since last frame |
|
|
202
305
|
| `iFrame` | `int` | Frame counter |
|
|
306
|
+
| `iFrameRate` | `float` | Frames per second |
|
|
203
307
|
| `iMouse` | `vec4` | Mouse position and click state |
|
|
204
308
|
| `iChannel0-3` | `sampler2D` | Input textures/buffers |
|
|
205
309
|
| `iChannelResolution[4]` | `vec3[]` | Resolution of each channel |
|
|
206
310
|
| `iDate` | `vec4` | Year, month, day, time in seconds |
|
|
207
311
|
|
|
312
|
+
### Touch Uniforms (Mobile / Multi-touch)
|
|
313
|
+
|
|
314
|
+
| Uniform | Type | Description |
|
|
315
|
+
|---------|------|-------------|
|
|
316
|
+
| `iTouchCount` | `int` | Number of active touches |
|
|
317
|
+
| `iTouch0-2` | `vec4` | Per-touch position and state |
|
|
318
|
+
| `iPinch` | `float` | Current pinch distance |
|
|
319
|
+
| `iPinchDelta` | `float` | Change in pinch distance |
|
|
320
|
+
| `iPinchCenter` | `vec2` | Center point between pinch fingers |
|
|
321
|
+
|
|
322
|
+
## Keyboard Shortcuts
|
|
323
|
+
|
|
324
|
+
| Key | Action |
|
|
325
|
+
|-----|--------|
|
|
326
|
+
| **S** | Save screenshot (PNG) |
|
|
327
|
+
| **Space** | Play/Pause |
|
|
328
|
+
| **R** | Reset to frame 0 |
|
|
329
|
+
|
|
330
|
+
## Recording and Export
|
|
331
|
+
|
|
332
|
+
### Video Recording
|
|
333
|
+
|
|
334
|
+
When `controls: true`, click the record button (or use the controls menu) to capture your shader as a WebM video. Recording uses the browser's `MediaRecorder` API at 60fps with VP9 encoding. Click the stop button to end recording — the video downloads automatically.
|
|
335
|
+
|
|
336
|
+
### HTML Export
|
|
337
|
+
|
|
338
|
+
Click the export button in the controls menu to generate a standalone HTML file with your shader embedded. The exported file includes:
|
|
339
|
+
- All shader passes and common code
|
|
340
|
+
- Current custom uniform values baked in
|
|
341
|
+
- Full WebGL2 rendering pipeline (no dependencies)
|
|
342
|
+
- Mouse interaction support
|
|
343
|
+
- Resize handling
|
|
344
|
+
|
|
345
|
+
**Limitations:** Array uniforms (UBOs), audio, webcam, video, and script hooks are not included in the export. Textures are replaced with a procedural checkerboard pattern.
|
|
346
|
+
|
|
347
|
+
## Embedding as a Library
|
|
348
|
+
|
|
349
|
+
The package exports its core classes for use in custom applications:
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
import { App, createLayout, loadDemo } from '@stevejtrettel/shader-sandbox';
|
|
353
|
+
import type { ShaderProject, LayoutMode } from '@stevejtrettel/shader-sandbox';
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Exports
|
|
357
|
+
|
|
358
|
+
| Export | Description |
|
|
359
|
+
|--------|-------------|
|
|
360
|
+
| `App` | Main application class — creates canvas, engine, and animation loop |
|
|
361
|
+
| `createLayout(mode, options)` | Factory to create a layout (fullscreen, default, split, tabbed) |
|
|
362
|
+
| `applyTheme(mode)` | Apply a theme (light, dark, system) |
|
|
363
|
+
| `loadDemo(files)` | Load a shader project from bundled file data |
|
|
364
|
+
|
|
365
|
+
### Example: Embedding a shader
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
import { App, createLayout } from '@stevejtrettel/shader-sandbox';
|
|
369
|
+
|
|
370
|
+
const project = /* your ShaderProject object */;
|
|
371
|
+
|
|
372
|
+
const layout = createLayout(project.layout, {
|
|
373
|
+
container: document.getElementById('shader-container'),
|
|
374
|
+
project,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const app = new App({
|
|
378
|
+
container: layout.getCanvasContainer(),
|
|
379
|
+
project,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
if (!app.hasErrors()) {
|
|
383
|
+
app.start();
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Clean up when done
|
|
387
|
+
app.dispose();
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Embed entry point
|
|
391
|
+
|
|
392
|
+
For build-time embedding of a specific shader, the package also provides an `embed()` function:
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import { embed } from '@stevejtrettel/shader-sandbox/embed';
|
|
396
|
+
|
|
397
|
+
const { app, destroy } = await embed({
|
|
398
|
+
container: '#my-container', // CSS selector or HTMLElement
|
|
399
|
+
pixelRatio: window.devicePixelRatio,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Later: destroy() to clean up
|
|
403
|
+
```
|
|
404
|
+
|
|
208
405
|
## Building for Production
|
|
209
406
|
|
|
210
407
|
```bash
|
package/bin/cli.js
CHANGED
|
@@ -63,24 +63,41 @@ function copyDir(src, dest, skipFiles = []) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
function
|
|
66
|
+
function getShaderList(cwd) {
|
|
67
67
|
const shadersDir = path.join(cwd, 'shaders');
|
|
68
68
|
if (!fs.existsSync(shadersDir)) {
|
|
69
|
-
|
|
70
|
-
console.error('Run "shader init" first');
|
|
71
|
-
process.exit(1);
|
|
69
|
+
return null;
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
const entries = fs.readdirSync(shadersDir, { withFileTypes: true });
|
|
75
|
-
|
|
73
|
+
return entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function listShaders(cwd) {
|
|
77
|
+
const shaders = getShaderList(cwd);
|
|
78
|
+
|
|
79
|
+
if (shaders === null) {
|
|
80
|
+
console.error('Error: shaders/ directory not found');
|
|
81
|
+
console.error('');
|
|
82
|
+
console.error('To get started:');
|
|
83
|
+
console.error(' shader init Initialize shaders in current directory');
|
|
84
|
+
console.error(' shader create Create a new shader project');
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
76
87
|
|
|
77
88
|
if (shaders.length === 0) {
|
|
78
|
-
console.log('No shaders found.
|
|
89
|
+
console.log('No shaders found.');
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log('Create your first shader:');
|
|
92
|
+
console.log(' shader new my-shader');
|
|
79
93
|
return;
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
console.log('Available shaders:');
|
|
83
97
|
shaders.forEach(s => console.log(` ${s}`));
|
|
98
|
+
console.log('');
|
|
99
|
+
console.log('Run a shader:');
|
|
100
|
+
console.log(` shader dev ${shaders[0]}`);
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
async function create(projectName) {
|
|
@@ -328,18 +345,61 @@ switch (command) {
|
|
|
328
345
|
|
|
329
346
|
case 'dev': {
|
|
330
347
|
const shaderName = args[1];
|
|
348
|
+
const cwd = process.cwd();
|
|
349
|
+
|
|
331
350
|
if (!shaderName) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
351
|
+
const shaders = getShaderList(cwd);
|
|
352
|
+
|
|
353
|
+
if (shaders === null) {
|
|
354
|
+
console.error('Error: shaders/ directory not found');
|
|
355
|
+
console.error('');
|
|
356
|
+
console.error('To get started:');
|
|
357
|
+
console.error(' shader init Initialize shaders in current directory');
|
|
358
|
+
console.error(' shader create Create a new shader project');
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (shaders.length === 0) {
|
|
363
|
+
console.error('Error: No shaders found');
|
|
364
|
+
console.error('');
|
|
365
|
+
console.error('Create your first shader:');
|
|
366
|
+
console.error(' shader new my-shader');
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
console.error('Error: Specify which shader to run');
|
|
371
|
+
console.error('');
|
|
372
|
+
console.error('Available shaders:');
|
|
373
|
+
shaders.forEach(s => console.error(` ${s}`));
|
|
374
|
+
console.error('');
|
|
375
|
+
console.error('Usage:');
|
|
376
|
+
console.error(` shader dev ${shaders[0]}`);
|
|
335
377
|
process.exit(1);
|
|
336
378
|
}
|
|
337
379
|
|
|
338
|
-
const cwd = process.cwd();
|
|
339
380
|
const shaderPath = path.join(cwd, 'shaders', shaderName);
|
|
340
381
|
if (!fs.existsSync(shaderPath)) {
|
|
382
|
+
const shaders = getShaderList(cwd);
|
|
383
|
+
|
|
341
384
|
console.error(`Error: Shader "${shaderName}" not found`);
|
|
342
|
-
|
|
385
|
+
|
|
386
|
+
if (shaders && shaders.length > 0) {
|
|
387
|
+
// Check for similar names (typo detection)
|
|
388
|
+
const similar = shaders.filter(s =>
|
|
389
|
+
s.toLowerCase().includes(shaderName.toLowerCase()) ||
|
|
390
|
+
shaderName.toLowerCase().includes(s.toLowerCase())
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
if (similar.length > 0) {
|
|
394
|
+
console.error('');
|
|
395
|
+
console.error('Did you mean:');
|
|
396
|
+
similar.forEach(s => console.error(` ${s}`));
|
|
397
|
+
} else {
|
|
398
|
+
console.error('');
|
|
399
|
+
console.error('Available shaders:');
|
|
400
|
+
shaders.forEach(s => console.error(` ${s}`));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
343
403
|
process.exit(1);
|
|
344
404
|
}
|
|
345
405
|
|
|
@@ -350,16 +410,48 @@ switch (command) {
|
|
|
350
410
|
|
|
351
411
|
case 'build': {
|
|
352
412
|
const shaderName = args[1];
|
|
413
|
+
const cwd = process.cwd();
|
|
414
|
+
|
|
353
415
|
if (!shaderName) {
|
|
354
|
-
|
|
355
|
-
|
|
416
|
+
const shaders = getShaderList(cwd);
|
|
417
|
+
|
|
418
|
+
if (shaders && shaders.length > 0) {
|
|
419
|
+
console.error('Error: Specify which shader to build');
|
|
420
|
+
console.error('');
|
|
421
|
+
console.error('Available shaders:');
|
|
422
|
+
shaders.forEach(s => console.error(` ${s}`));
|
|
423
|
+
console.error('');
|
|
424
|
+
console.error('Usage:');
|
|
425
|
+
console.error(` shader build ${shaders[0]}`);
|
|
426
|
+
} else {
|
|
427
|
+
console.error('Error: Specify a shader name');
|
|
428
|
+
console.error(' shader build <shader-name>');
|
|
429
|
+
}
|
|
356
430
|
process.exit(1);
|
|
357
431
|
}
|
|
358
432
|
|
|
359
|
-
const cwd = process.cwd();
|
|
360
433
|
const shaderPath = path.join(cwd, 'shaders', shaderName);
|
|
361
434
|
if (!fs.existsSync(shaderPath)) {
|
|
435
|
+
const shaders = getShaderList(cwd);
|
|
436
|
+
|
|
362
437
|
console.error(`Error: Shader "${shaderName}" not found`);
|
|
438
|
+
|
|
439
|
+
if (shaders && shaders.length > 0) {
|
|
440
|
+
const similar = shaders.filter(s =>
|
|
441
|
+
s.toLowerCase().includes(shaderName.toLowerCase()) ||
|
|
442
|
+
shaderName.toLowerCase().includes(s.toLowerCase())
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
if (similar.length > 0) {
|
|
446
|
+
console.error('');
|
|
447
|
+
console.error('Did you mean:');
|
|
448
|
+
similar.forEach(s => console.error(` ${s}`));
|
|
449
|
+
} else {
|
|
450
|
+
console.error('');
|
|
451
|
+
console.error('Available shaders:');
|
|
452
|
+
shaders.forEach(s => console.error(` ${s}`));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
363
455
|
process.exit(1);
|
|
364
456
|
}
|
|
365
457
|
|