@stevejtrettel/shader-sandbox 0.1.1 → 0.1.2

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.
@@ -3,5 +3,5 @@
3
3
  * Called by the generated loader
4
4
  */
5
5
  import { ShadertoyProject, ShadertoyConfig } from './types';
6
- export declare function loadDemo(demoName: string, glslFiles: Record<string, () => Promise<string>>, jsonFiles: Record<string, () => Promise<ShadertoyConfig>>, imageFiles: Record<string, () => Promise<string>>): Promise<ShadertoyProject>;
6
+ export declare function loadDemo(demoPath: string, glslFiles: Record<string, () => Promise<string>>, jsonFiles: Record<string, () => Promise<ShadertoyConfig>>, imageFiles: Record<string, () => Promise<string>>): Promise<ShadertoyProject>;
7
7
  //# sourceMappingURL=loaderHelper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loaderHelper.d.ts","sourceRoot":"","sources":["../../src/project/loaderHelper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,gBAAgB,EAChB,eAAe,EAIhB,MAAM,SAAS,CAAC;AA8CjB,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,EAChD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC,EACzD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAChD,OAAO,CAAC,gBAAgB,CAAC,CAkB3B"}
1
+ {"version":3,"file":"loaderHelper.d.ts","sourceRoot":"","sources":["../../src/project/loaderHelper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,gBAAgB,EAChB,eAAe,EAIhB,MAAM,SAAS,CAAC;AA8CjB,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,EAChD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC,EACzD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAChD,OAAO,CAAC,gBAAgB,CAAC,CAoB3B"}
@@ -40,38 +40,42 @@ function parseChannelValue(value) {
40
40
  }
41
41
  return value;
42
42
  }
43
- export async function loadDemo(demoName, glslFiles, jsonFiles, imageFiles) {
44
- const configPath = `/demos/${demoName}/config.json`;
43
+ export async function loadDemo(demoPath, glslFiles, jsonFiles, imageFiles) {
44
+ // Normalize path - handle both "./shaders/name" and "shaders/name" formats
45
+ const normalizedPath = demoPath.startsWith('./') ? demoPath : `./${demoPath}`;
46
+ const configPath = `${normalizedPath}/config.json`;
45
47
  const hasConfig = configPath in jsonFiles;
46
48
  if (hasConfig) {
47
49
  const config = await jsonFiles[configPath]();
48
50
  const hasPassConfigs = config.Image || config.BufferA || config.BufferB ||
49
51
  config.BufferC || config.BufferD;
50
52
  if (hasPassConfigs) {
51
- return loadWithConfig(demoName, config, glslFiles, imageFiles);
53
+ return loadWithConfig(normalizedPath, config, glslFiles, imageFiles);
52
54
  }
53
55
  else {
54
56
  // Config with only settings (layout, controls, etc.) but no passes
55
- return loadSinglePass(demoName, glslFiles, config);
57
+ return loadSinglePass(normalizedPath, glslFiles, config);
56
58
  }
57
59
  }
58
60
  else {
59
- return loadSinglePass(demoName, glslFiles);
61
+ return loadSinglePass(normalizedPath, glslFiles);
60
62
  }
61
63
  }
62
- async function loadSinglePass(demoName, glslFiles, configOverrides) {
63
- const imagePath = `/demos/${demoName}/image.glsl`;
64
+ async function loadSinglePass(demoPath, glslFiles, configOverrides) {
65
+ const imagePath = `${demoPath}/image.glsl`;
64
66
  const actualImagePath = findFileCaseInsensitive(glslFiles, imagePath);
65
67
  if (!actualImagePath) {
66
- throw new Error(`Demo '${demoName}' not found. Expected ${imagePath}`);
68
+ throw new Error(`Demo '${demoPath}' not found. Expected ${imagePath}`);
67
69
  }
68
70
  const imageSource = await glslFiles[actualImagePath]();
69
71
  const layout = configOverrides?.layout || 'tabbed';
70
72
  const controls = configOverrides?.controls ?? true;
73
+ // Extract name from path for title (e.g., "./shaders/example-gradient" -> "example-gradient")
74
+ const demoName = demoPath.split('/').pop() || demoPath;
71
75
  const title = configOverrides?.title ||
72
76
  demoName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
73
77
  return {
74
- root: `/demos/${demoName}`,
78
+ root: demoPath,
75
79
  meta: {
76
80
  title,
77
81
  author: configOverrides?.author || null,
@@ -95,7 +99,7 @@ async function loadSinglePass(demoName, glslFiles, configOverrides) {
95
99
  textures: [],
96
100
  };
97
101
  }
98
- async function loadWithConfig(demoName, config, glslFiles, imageFiles) {
102
+ async function loadWithConfig(demoPath, config, glslFiles, imageFiles) {
99
103
  // Extract pass configs from top level
100
104
  const passConfigs = {
101
105
  Image: config.Image,
@@ -107,14 +111,14 @@ async function loadWithConfig(demoName, config, glslFiles, imageFiles) {
107
111
  // Load common source
108
112
  let commonSource = null;
109
113
  if (config.common) {
110
- const commonPath = `/demos/${demoName}/${config.common}`;
114
+ const commonPath = `${demoPath}/${config.common}`;
111
115
  const actualCommonPath = findFileCaseInsensitive(glslFiles, commonPath);
112
116
  if (actualCommonPath) {
113
117
  commonSource = await glslFiles[actualCommonPath]();
114
118
  }
115
119
  }
116
120
  else {
117
- const defaultCommonPath = `/demos/${demoName}/common.glsl`;
121
+ const defaultCommonPath = `${demoPath}/common.glsl`;
118
122
  const actualCommonPath = findFileCaseInsensitive(glslFiles, defaultCommonPath);
119
123
  if (actualCommonPath) {
120
124
  commonSource = await glslFiles[actualCommonPath]();
@@ -141,7 +145,7 @@ async function loadWithConfig(demoName, config, glslFiles, imageFiles) {
141
145
  const textures = [];
142
146
  const texturePathToName = new Map();
143
147
  for (const texturePath of texturePathsSet) {
144
- const fullPath = `/demos/${demoName}/${texturePath.replace(/^\.\//, '')}`;
148
+ const fullPath = `${demoPath}/${texturePath.replace(/^\.\//, '')}`;
145
149
  const actualPath = findFileCaseInsensitive(imageFiles, fullPath);
146
150
  if (!actualPath) {
147
151
  throw new Error(`Texture not found: ${texturePath} (expected at ${fullPath})`);
@@ -172,7 +176,7 @@ async function loadWithConfig(demoName, config, glslFiles, imageFiles) {
172
176
  BufferD: 'bufferD.glsl',
173
177
  };
174
178
  const sourceFile = passConfig.source || defaultNames[passName];
175
- const sourcePath = `/demos/${demoName}/${sourceFile}`;
179
+ const sourcePath = `${demoPath}/${sourceFile}`;
176
180
  const actualSourcePath = findFileCaseInsensitive(glslFiles, sourcePath);
177
181
  if (!actualSourcePath) {
178
182
  throw new Error(`Missing shader file: ${sourcePath}`);
@@ -191,8 +195,10 @@ async function loadWithConfig(demoName, config, glslFiles, imageFiles) {
191
195
  };
192
196
  }
193
197
  if (!passes.Image) {
194
- throw new Error(`Demo '${demoName}' must have an Image pass`);
198
+ throw new Error(`Demo '${demoPath}' must have an Image pass`);
195
199
  }
200
+ // Extract name from path for title (e.g., "./shaders/example-gradient" -> "example-gradient")
201
+ const demoName = demoPath.split('/').pop() || demoPath;
196
202
  const title = config.title ||
197
203
  demoName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
198
204
  const author = config.author || null;
@@ -200,7 +206,7 @@ async function loadWithConfig(demoName, config, glslFiles, imageFiles) {
200
206
  const layout = config.layout || 'tabbed';
201
207
  const controls = config.controls ?? true;
202
208
  return {
203
- root: `/demos/${demoName}`,
209
+ root: demoPath,
204
210
  meta: { title, author, description },
205
211
  layout,
206
212
  controls,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stevejtrettel/shader-sandbox",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Local Shadertoy-compatible GLSL shader development environment with live editing",
5
5
  "type": "module",
6
6
  "main": "./dist-lib/index.js",
@@ -56,12 +56,14 @@ function parseChannelValue(value: ChannelValue): ChannelJSONObject | null {
56
56
  }
57
57
 
58
58
  export async function loadDemo(
59
- demoName: string,
59
+ demoPath: string,
60
60
  glslFiles: Record<string, () => Promise<string>>,
61
61
  jsonFiles: Record<string, () => Promise<ShadertoyConfig>>,
62
62
  imageFiles: Record<string, () => Promise<string>>
63
63
  ): Promise<ShadertoyProject> {
64
- const configPath = `/demos/${demoName}/config.json`;
64
+ // Normalize path - handle both "./shaders/name" and "shaders/name" formats
65
+ const normalizedPath = demoPath.startsWith('./') ? demoPath : `./${demoPath}`;
66
+ const configPath = `${normalizedPath}/config.json`;
65
67
  const hasConfig = configPath in jsonFiles;
66
68
 
67
69
  if (hasConfig) {
@@ -70,37 +72,39 @@ export async function loadDemo(
70
72
  config.BufferC || config.BufferD;
71
73
 
72
74
  if (hasPassConfigs) {
73
- return loadWithConfig(demoName, config, glslFiles, imageFiles);
75
+ return loadWithConfig(normalizedPath, config, glslFiles, imageFiles);
74
76
  } else {
75
77
  // Config with only settings (layout, controls, etc.) but no passes
76
- return loadSinglePass(demoName, glslFiles, config);
78
+ return loadSinglePass(normalizedPath, glslFiles, config);
77
79
  }
78
80
  } else {
79
- return loadSinglePass(demoName, glslFiles);
81
+ return loadSinglePass(normalizedPath, glslFiles);
80
82
  }
81
83
  }
82
84
 
83
85
  async function loadSinglePass(
84
- demoName: string,
86
+ demoPath: string,
85
87
  glslFiles: Record<string, () => Promise<string>>,
86
88
  configOverrides?: Partial<ShadertoyConfig>
87
89
  ): Promise<ShadertoyProject> {
88
- const imagePath = `/demos/${demoName}/image.glsl`;
90
+ const imagePath = `${demoPath}/image.glsl`;
89
91
  const actualImagePath = findFileCaseInsensitive(glslFiles, imagePath);
90
92
 
91
93
  if (!actualImagePath) {
92
- throw new Error(`Demo '${demoName}' not found. Expected ${imagePath}`);
94
+ throw new Error(`Demo '${demoPath}' not found. Expected ${imagePath}`);
93
95
  }
94
96
 
95
97
  const imageSource = await glslFiles[actualImagePath]();
96
98
 
97
99
  const layout = configOverrides?.layout || 'tabbed';
98
100
  const controls = configOverrides?.controls ?? true;
101
+ // Extract name from path for title (e.g., "./shaders/example-gradient" -> "example-gradient")
102
+ const demoName = demoPath.split('/').pop() || demoPath;
99
103
  const title = configOverrides?.title ||
100
104
  demoName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
101
105
 
102
106
  return {
103
- root: `/demos/${demoName}`,
107
+ root: demoPath,
104
108
  meta: {
105
109
  title,
106
110
  author: configOverrides?.author || null,
@@ -126,7 +130,7 @@ async function loadSinglePass(
126
130
  }
127
131
 
128
132
  async function loadWithConfig(
129
- demoName: string,
133
+ demoPath: string,
130
134
  config: ShadertoyConfig,
131
135
  glslFiles: Record<string, () => Promise<string>>,
132
136
  imageFiles: Record<string, () => Promise<string>>
@@ -144,13 +148,13 @@ async function loadWithConfig(
144
148
  // Load common source
145
149
  let commonSource: string | null = null;
146
150
  if (config.common) {
147
- const commonPath = `/demos/${demoName}/${config.common}`;
151
+ const commonPath = `${demoPath}/${config.common}`;
148
152
  const actualCommonPath = findFileCaseInsensitive(glslFiles, commonPath);
149
153
  if (actualCommonPath) {
150
154
  commonSource = await glslFiles[actualCommonPath]();
151
155
  }
152
156
  } else {
153
- const defaultCommonPath = `/demos/${demoName}/common.glsl`;
157
+ const defaultCommonPath = `${demoPath}/common.glsl`;
154
158
  const actualCommonPath = findFileCaseInsensitive(glslFiles, defaultCommonPath);
155
159
  if (actualCommonPath) {
156
160
  commonSource = await glslFiles[actualCommonPath]();
@@ -181,7 +185,7 @@ async function loadWithConfig(
181
185
  const texturePathToName = new Map<string, string>();
182
186
 
183
187
  for (const texturePath of texturePathsSet) {
184
- const fullPath = `/demos/${demoName}/${texturePath.replace(/^\.\//, '')}`;
188
+ const fullPath = `${demoPath}/${texturePath.replace(/^\.\//, '')}`;
185
189
  const actualPath = findFileCaseInsensitive(imageFiles, fullPath);
186
190
 
187
191
  if (!actualPath) {
@@ -219,7 +223,7 @@ async function loadWithConfig(
219
223
  };
220
224
 
221
225
  const sourceFile = passConfig.source || defaultNames[passName];
222
- const sourcePath = `/demos/${demoName}/${sourceFile}`;
226
+ const sourcePath = `${demoPath}/${sourceFile}`;
223
227
  const actualSourcePath = findFileCaseInsensitive(glslFiles, sourcePath);
224
228
 
225
229
  if (!actualSourcePath) {
@@ -243,9 +247,11 @@ async function loadWithConfig(
243
247
  }
244
248
 
245
249
  if (!passes.Image) {
246
- throw new Error(`Demo '${demoName}' must have an Image pass`);
250
+ throw new Error(`Demo '${demoPath}' must have an Image pass`);
247
251
  }
248
252
 
253
+ // Extract name from path for title (e.g., "./shaders/example-gradient" -> "example-gradient")
254
+ const demoName = demoPath.split('/').pop() || demoPath;
249
255
  const title = config.title ||
250
256
  demoName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
251
257
  const author = config.author || null;
@@ -254,7 +260,7 @@ async function loadWithConfig(
254
260
  const controls = config.controls ?? true;
255
261
 
256
262
  return {
257
- root: `/demos/${demoName}`,
263
+ root: demoPath,
258
264
  meta: { title, author, description },
259
265
  layout,
260
266
  controls,