wgsl-play 0.0.37 → 0.0.38

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 CHANGED
@@ -56,7 +56,7 @@ You can include shader code inline if you'd prefer. Use a `<script type="text/wg
56
56
 
57
57
  ```typescript
58
58
  const player = document.querySelector("wgsl-play");
59
- player.source = shaderCode;
59
+ player.shader = shaderCode;
60
60
  player.pause();
61
61
  player.rewind();
62
62
  player.play();
@@ -68,7 +68,7 @@ player.play();
68
68
  import shader from './examples/noise.wesl?raw';
69
69
 
70
70
  const player = document.querySelector("wgsl-play");
71
- player.source = shader;
71
+ player.shader = shader;
72
72
  ```
73
73
 
74
74
  The `?raw` suffix imports the file as a string. This keeps shaders alongside your source files with HMR support.
@@ -80,12 +80,14 @@ The `?raw` suffix imports the file as a string. This keeps shaders alongside you
80
80
  - `shader-root` - Root path for internal imports (default: `/shaders`)
81
81
  - `autoplay` - Start animating on load (default: `true`). Set `autoplay="false"` to start paused
82
82
  - `transparent` - Use premultiplied alpha for transparent backgrounds (default: opaque)
83
+ - `from` - Element ID of a source provider (e.g., wgsl-edit) to connect to
83
84
  - `fetch-libs` - Auto-fetch missing libraries from npm (default: `true`). Set `fetch-libs="false"` to disable
85
+ - `fetch-sources` - Auto-fetch local .wesl source files via HTTP (default: `true`). Set `fetch-sources="false"` to disable
84
86
 
85
87
  ### Properties
86
- - `source: string` - Get/set shader source
88
+ - `shader: string` - Get/set shader source (single-file convenience)
87
89
  - `conditions: Record<string, boolean>` - Get/set conditions for conditional compilation (`@if`/`@elif`/`@else`)
88
- - `project: WeslProject` - Set full project config (weslSrc, libs, conditions, constants)
90
+ - `project: WeslProject` - Get/set full project config (weslSrc, libs, conditions, constants)
89
91
  - `isPlaying: boolean` - Playback state (readonly)
90
92
  - `time: number` - Animation time in seconds (readonly)
91
93
  - `hasError: boolean` - Compilation error state (readonly)
@@ -147,7 +149,7 @@ import super::common::tint;
147
149
 
148
150
  ## Using with wesl-plugin
149
151
 
150
- For more control, use the [wesl-plugin](https://github.com/wgsl-tooling-wg/wesl-js/tree/main/tools/packages/wesl-plugin) to
152
+ For more control, use the [wesl-plugin](https://github.com/wgsl-tooling-wg/wesl-js/tree/main/packages/wesl-plugin) to
151
153
  assemble shaders and libraries at build time and provide
152
154
  them wgsl-play in JavaScript or TypeScript.
153
155
  - provides full support for Hot Module Reloading during development
@@ -204,12 +204,13 @@ var WgslPlay = class extends HTMLElement {
204
204
  static observedAttributes = [
205
205
  "src",
206
206
  "shader-root",
207
- "source",
207
+ "from",
208
208
  "no-controls",
209
209
  "theme",
210
210
  "autoplay",
211
211
  "transparent",
212
- "fetch-libs"
212
+ "fetch-libs",
213
+ "fetch-sources"
213
214
  ];
214
215
  canvas;
215
216
  errorOverlay;
@@ -226,7 +227,7 @@ var WgslPlay = class extends HTMLElement {
226
227
  _rootModuleName = "package::main";
227
228
  _libs;
228
229
  _linkOptions = {};
229
- _fromFullProject = false;
230
+ _fetchSources = true;
230
231
  _initPromise;
231
232
  _sourceEl = null;
232
233
  _sourceListener = null;
@@ -274,7 +275,7 @@ var WgslPlay = class extends HTMLElement {
274
275
  }
275
276
  this.initialize();
276
277
  upgradeProperty(this, "conditions");
277
- upgradeProperty(this, "source");
278
+ upgradeProperty(this, "shader");
278
279
  upgradeProperty(this, "project");
279
280
  }
280
281
  disconnectedCallback() {
@@ -302,17 +303,20 @@ var WgslPlay = class extends HTMLElement {
302
303
  this._fetchLibs = newValue !== "false";
303
304
  return;
304
305
  }
306
+ if (name === "fetch-sources") {
307
+ this._fetchSources = newValue !== "false";
308
+ return;
309
+ }
305
310
  if (name === "src" && newValue && this._initPromise) this.loadFromUrl(newValue);
306
311
  }
307
312
  /** Current shader source code (main module). */
308
- get source() {
313
+ get shader() {
309
314
  return this._weslSrc[this._rootModuleName] ?? "";
310
315
  }
311
- /** Set shader source directly. */
312
- set source(value) {
316
+ /** Set shader source directly (single-file convenience). */
317
+ set shader(value) {
313
318
  this._weslSrc = { [this._rootModuleName]: value };
314
319
  this._libs = void 0;
315
- this._fromFullProject = false;
316
320
  this.requestBuild();
317
321
  }
318
322
  /** Conditions for conditional compilation (@if/@elif/@else). */
@@ -330,28 +334,21 @@ var WgslPlay = class extends HTMLElement {
330
334
  /** Set project configuration (mirrors wesl link() API). */
331
335
  set project(value) {
332
336
  const { weslSrc, rootModuleName, libs, packageName, conditions, constants } = value;
333
- if (packageName || conditions || constants) this._linkOptions = {
334
- packageName,
335
- conditions,
336
- constants
337
- };
337
+ if (packageName !== void 0) this._linkOptions.packageName = packageName;
338
+ if (conditions !== void 0) this._linkOptions.conditions = conditions;
339
+ if (constants !== void 0) this._linkOptions.constants = constants;
338
340
  if (libs) this._libs = libs;
339
341
  if (weslSrc) {
340
- this.setProjectSources(weslSrc, rootModuleName);
342
+ const pkg = this._linkOptions.packageName || "package";
343
+ const root = rootModuleName ?? "main";
344
+ this._weslSrc = toModulePaths(weslSrc, pkg);
345
+ this._rootModuleName = fileToModulePath(root, pkg, false);
346
+ this.requestBuild();
341
347
  return;
342
348
  }
343
349
  if (Object.keys(this._weslSrc).length === 0) return;
344
350
  this.requestBuild();
345
351
  }
346
- /** Set sources from a full project with weslSrc. */
347
- setProjectSources(weslSrc, rootModuleName) {
348
- const pkg = this._linkOptions.packageName || "package";
349
- const root = rootModuleName ?? "main";
350
- this._weslSrc = toModulePaths(weslSrc, pkg);
351
- this._rootModuleName = fileToModulePath(root, pkg, false);
352
- this._fromFullProject = true;
353
- this.requestBuild();
354
- }
355
352
  /** Whether to auto-fetch missing library packages from npm (default: true). */
356
353
  get fetchLibs() {
357
354
  return this._fetchLibs;
@@ -361,6 +358,15 @@ var WgslPlay = class extends HTMLElement {
361
358
  if (value) this.removeAttribute("fetch-libs");
362
359
  else this.setAttribute("fetch-libs", "false");
363
360
  }
361
+ /** Whether to fetch local .wesl source files via HTTP (default: true). */
362
+ get fetchSources() {
363
+ return this._fetchSources;
364
+ }
365
+ set fetchSources(value) {
366
+ this._fetchSources = value;
367
+ if (value) this.removeAttribute("fetch-sources");
368
+ else this.setAttribute("fetch-sources", "false");
369
+ }
364
370
  /** Whether autoplay is enabled (default: true). Set autoplay="false" to start paused. */
365
371
  get autoplay() {
366
372
  return this.getAttribute("autoplay") !== "false";
@@ -456,9 +462,9 @@ var WgslPlay = class extends HTMLElement {
456
462
  }
457
463
  /** Load from source element, src URL, script child, or inline textContent. */
458
464
  loadInitialContent() {
459
- const sourceId = this.getAttribute("source");
460
- if (sourceId) {
461
- this.connectToSource(sourceId);
465
+ const fromId = this.getAttribute("from");
466
+ if (fromId) {
467
+ this.connectFrom(fromId);
462
468
  return;
463
469
  }
464
470
  const src = this.getAttribute("src");
@@ -469,43 +475,21 @@ var WgslPlay = class extends HTMLElement {
469
475
  const inlineSource = this.querySelector("script[type=\"text/wgsl\"], script[type=\"text/wesl\"]")?.textContent?.trim() ?? this.textContent?.trim();
470
476
  if (!inlineSource) return;
471
477
  this._weslSrc = { [this._rootModuleName]: inlineSource };
472
- this._fromFullProject = false;
473
478
  this.requestBuild();
474
479
  }
475
480
  /** Connect to a source provider element (e.g., wgsl-edit). */
476
- connectToSource(id) {
481
+ connectFrom(id) {
477
482
  const el = document.getElementById(id);
478
483
  if (!el) {
479
484
  console.error(`wgsl-play: source element "${id}" not found`);
480
485
  return;
481
486
  }
482
487
  this._sourceEl = el;
483
- const getSources = () => {
484
- const sources = el.sources;
485
- if (sources && Object.keys(sources).length > 0) return sources;
486
- const source = el.source ?? el.textContent ?? "";
487
- return { [this._rootModuleName]: source };
488
- };
489
- const { conditions, rootModuleName, libs } = el;
490
- const root = rootModuleName ?? "main";
491
- this._weslSrc = getSources();
492
- this._rootModuleName = fileToModulePath(root, "package", false);
493
- if (conditions) this._linkOptions = {
494
- ...this._linkOptions,
495
- conditions
496
- };
497
- if (libs) this._libs = libs;
498
- this._fromFullProject = false;
499
- this.requestBuild();
488
+ const p = el.project;
489
+ if (p) this.project = p;
500
490
  this._sourceListener = (e) => {
501
491
  const detail = e.detail;
502
- const fallback = { [this._rootModuleName]: detail?.source ?? "" };
503
- this.project = {
504
- weslSrc: detail?.sources ?? fallback,
505
- rootModuleName: detail?.rootModuleName,
506
- conditions: detail?.conditions,
507
- libs: detail?.libs
508
- };
492
+ if (detail) this.project = detail;
509
493
  };
510
494
  el.addEventListener("change", this._sourceListener);
511
495
  }
@@ -515,7 +499,6 @@ var WgslPlay = class extends HTMLElement {
515
499
  const { weslSrc, libs, rootModuleName } = await loadShaderFromUrl(url, this.getConfigOverrides()?.shaderRoot);
516
500
  this._weslSrc = weslSrc;
517
501
  this._libs = libs;
518
- this._fromFullProject = false;
519
502
  if (rootModuleName) this._rootModuleName = rootModuleName;
520
503
  this.requestBuild();
521
504
  } catch (error) {
@@ -540,11 +523,12 @@ var WgslPlay = class extends HTMLElement {
540
523
  }
541
524
  try {
542
525
  this.errorOverlay.hide();
543
- if (!this._fromFullProject) {
526
+ if (this._fetchSources || this._fetchLibs) {
544
527
  const { weslSrc, libs } = await fetchDependencies(mainSource, {
545
528
  shaderRoot: this.getConfigOverrides()?.shaderRoot,
546
529
  existingSources: this._weslSrc,
547
- skipExternal: !this._fetchLibs
530
+ fetchLibs: this._fetchLibs,
531
+ fetchSources: this._fetchSources
548
532
  });
549
533
  this._weslSrc = {
550
534
  ...this._weslSrc,
@@ -1,4 +1,4 @@
1
- import { Conditions, LinkParams } from "wesl";
1
+ import { Conditions, WeslProject } from "wesl";
2
2
 
3
3
  //#region src/Config.d.ts
4
4
  /** Configuration for wgsl-play. */
@@ -14,8 +14,6 @@ declare function getConfig(overrides?: Partial<WgslPlayConfig>): WgslPlayConfig;
14
14
  declare function resetConfig(): void;
15
15
  //#endregion
16
16
  //#region src/WgslPlay.d.ts
17
- /** Project configuration for multi-file shaders (subset of wesl link() API). */
18
- type WeslProject = Pick<LinkParams, "weslSrc" | "rootModuleName" | "conditions" | "constants" | "libs" | "packageName">;
19
17
  /** One source location within a compile error. */
20
18
  interface CompileErrorLocation {
21
19
  file?: string;
@@ -45,7 +43,7 @@ declare class WgslPlay extends HTMLElement {
45
43
  private _rootModuleName;
46
44
  private _libs?;
47
45
  private _linkOptions;
48
- private _fromFullProject;
46
+ private _fetchSources;
49
47
  private _initPromise?;
50
48
  private _sourceEl;
51
49
  private _sourceListener;
@@ -62,19 +60,20 @@ declare class WgslPlay extends HTMLElement {
62
60
  disconnectedCallback(): void;
63
61
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
64
62
  /** Current shader source code (main module). */
65
- get source(): string;
66
- /** Set shader source directly. */
67
- set source(value: string);
63
+ get shader(): string;
64
+ /** Set shader source directly (single-file convenience). */
65
+ set shader(value: string);
68
66
  /** Conditions for conditional compilation (@if/@elif/@else). */
69
67
  get conditions(): Conditions;
70
68
  set conditions(value: Conditions);
71
69
  /** Set project configuration (mirrors wesl link() API). */
72
70
  set project(value: WeslProject);
73
- /** Set sources from a full project with weslSrc. */
74
- private setProjectSources;
75
71
  /** Whether to auto-fetch missing library packages from npm (default: true). */
76
72
  get fetchLibs(): boolean;
77
73
  set fetchLibs(value: boolean);
74
+ /** Whether to fetch local .wesl source files via HTTP (default: true). */
75
+ get fetchSources(): boolean;
76
+ set fetchSources(value: boolean);
78
77
  /** Whether autoplay is enabled (default: true). Set autoplay="false" to start paused. */
79
78
  get autoplay(): boolean;
80
79
  set autoplay(value: boolean | string);
@@ -106,7 +105,7 @@ declare class WgslPlay extends HTMLElement {
106
105
  /** Load from source element, src URL, script child, or inline textContent. */
107
106
  private loadInitialContent;
108
107
  /** Connect to a source provider element (e.g., wgsl-edit). */
109
- private connectToSource;
108
+ private connectFrom;
110
109
  /** Fetch shader from URL, then trigger a build. */
111
110
  private loadFromUrl;
112
111
  /** Mark build as needed. Coalesces rapid requests into a single build. */
@@ -118,4 +117,4 @@ declare class WgslPlay extends HTMLElement {
118
117
  private extractLocations;
119
118
  }
120
119
  //#endregion
121
- export { WgslPlayConfig as a, resetConfig as c, WgslPlay as i, CompileErrorLocation as n, defaults as o, WeslProject as r, getConfig as s, CompileErrorDetail as t };
120
+ export { defaults as a, WgslPlayConfig as i, CompileErrorLocation as n, getConfig as o, WgslPlay as r, resetConfig as s, CompileErrorDetail as t };
@@ -1,2 +1,2 @@
1
- import { c as resetConfig, i as WgslPlay, n as CompileErrorLocation, o as defaults, r as WeslProject, s as getConfig, t as CompileErrorDetail } from "./WgslPlay-B86UkrM7.js";
2
- export { CompileErrorDetail, CompileErrorLocation, WeslProject, WgslPlay, defaults, getConfig, resetConfig };
1
+ import { a as defaults, n as CompileErrorLocation, o as getConfig, r as WgslPlay, s as resetConfig, t as CompileErrorDetail } from "./WgslPlay-uTIOrTQq.js";
2
+ export { CompileErrorDetail, CompileErrorLocation, WgslPlay, defaults, getConfig, resetConfig };
package/dist/WgslPlay.js CHANGED
@@ -1,2 +1,2 @@
1
- import { i as resetConfig, n as defaults, r as getConfig, t as WgslPlay } from "./WgslPlay-DZgeJ1h6.js";
1
+ import { i as resetConfig, n as defaults, r as getConfig, t as WgslPlay } from "./WgslPlay-BuIuE3cB.js";
2
2
  export { WgslPlay, defaults, getConfig, resetConfig };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { a as WgslPlayConfig, c as resetConfig, i as WgslPlay, n as CompileErrorLocation, o as defaults, r as WeslProject, s as getConfig, t as CompileErrorDetail } from "./WgslPlay-B86UkrM7.js";
1
+ import { a as defaults, i as WgslPlayConfig, n as CompileErrorLocation, o as getConfig, r as WgslPlay, s as resetConfig, t as CompileErrorDetail } from "./WgslPlay-uTIOrTQq.js";
2
2
  export * from "wesl-fetch";
3
- export { CompileErrorDetail, CompileErrorLocation, WeslProject, WgslPlay, WgslPlayConfig, defaults, getConfig, resetConfig };
3
+ export { CompileErrorDetail, CompileErrorLocation, WgslPlay, WgslPlayConfig, defaults, getConfig, resetConfig };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { i as resetConfig, n as defaults, r as getConfig, t as WgslPlay } from "./WgslPlay-DZgeJ1h6.js";
1
+ import { i as resetConfig, n as defaults, r as getConfig, t as WgslPlay } from "./WgslPlay-BuIuE3cB.js";
2
2
  export * from "wesl-fetch";
3
3
  //#region src/index.ts
4
4
  if (typeof customElements !== "undefined" && !customElements.get("wgsl-play")) customElements.define("wgsl-play", WgslPlay);
package/dist/wgsl-play.js CHANGED
@@ -5035,7 +5035,8 @@ async function fetchDependencies(rootModuleSource, options) {
5035
5035
  const shaderRoot = options?.shaderRoot ?? "/shaders";
5036
5036
  const currentPath = options?.currentPath;
5037
5037
  const existingSources = options?.existingSources;
5038
- const skipExternal = options?.skipExternal ?? false;
5038
+ const fetchLibs = options?.fetchLibs ?? true;
5039
+ const fetchSources = options?.fetchSources ?? true;
5039
5040
  const rootModuleName = currentPath ? urlToModulePath(currentPath, shaderRoot) : "package::main";
5040
5041
  const resolver = new FetchingResolver({
5041
5042
  ...existingSources,
@@ -5051,12 +5052,12 @@ async function fetchDependencies(rootModuleSource, options) {
5051
5052
  const unresolved = getNonVirtualUnresolved(resolver, fetched);
5052
5053
  if (unresolved.length === 0) break;
5053
5054
  const [internal, external] = partition(unresolved, isInternal);
5054
- await Promise.all(internal.map((p) => resolver.resolveModuleAsync(p)));
5055
- if (skipExternal) for (const p of external) fetched.add(p.split("::")[0]);
5056
- else {
5055
+ if (fetchSources) await Promise.all(internal.map((p) => resolver.resolveModuleAsync(p)));
5056
+ else for (const p of internal) fetched.add(p);
5057
+ if (fetchLibs) {
5057
5058
  const newLibs = await fetchExternalBundles(external, fetched);
5058
5059
  libs.push(...newLibs);
5059
- }
5060
+ } else for (const p of external) fetched.add(p.split("::")[0]);
5060
5061
  }
5061
5062
  return {
5062
5063
  libs,
@@ -5606,12 +5607,13 @@ var WgslPlay = class extends HTMLElement {
5606
5607
  static observedAttributes = [
5607
5608
  "src",
5608
5609
  "shader-root",
5609
- "source",
5610
+ "from",
5610
5611
  "no-controls",
5611
5612
  "theme",
5612
5613
  "autoplay",
5613
5614
  "transparent",
5614
- "fetch-libs"
5615
+ "fetch-libs",
5616
+ "fetch-sources"
5615
5617
  ];
5616
5618
  canvas;
5617
5619
  errorOverlay;
@@ -5628,7 +5630,7 @@ var WgslPlay = class extends HTMLElement {
5628
5630
  _rootModuleName = "package::main";
5629
5631
  _libs;
5630
5632
  _linkOptions = {};
5631
- _fromFullProject = false;
5633
+ _fetchSources = true;
5632
5634
  _initPromise;
5633
5635
  _sourceEl = null;
5634
5636
  _sourceListener = null;
@@ -5676,7 +5678,7 @@ var WgslPlay = class extends HTMLElement {
5676
5678
  }
5677
5679
  this.initialize();
5678
5680
  upgradeProperty(this, "conditions");
5679
- upgradeProperty(this, "source");
5681
+ upgradeProperty(this, "shader");
5680
5682
  upgradeProperty(this, "project");
5681
5683
  }
5682
5684
  disconnectedCallback() {
@@ -5704,17 +5706,20 @@ var WgslPlay = class extends HTMLElement {
5704
5706
  this._fetchLibs = newValue !== "false";
5705
5707
  return;
5706
5708
  }
5709
+ if (name === "fetch-sources") {
5710
+ this._fetchSources = newValue !== "false";
5711
+ return;
5712
+ }
5707
5713
  if (name === "src" && newValue && this._initPromise) this.loadFromUrl(newValue);
5708
5714
  }
5709
5715
  /** Current shader source code (main module). */
5710
- get source() {
5716
+ get shader() {
5711
5717
  return this._weslSrc[this._rootModuleName] ?? "";
5712
5718
  }
5713
- /** Set shader source directly. */
5714
- set source(value) {
5719
+ /** Set shader source directly (single-file convenience). */
5720
+ set shader(value) {
5715
5721
  this._weslSrc = { [this._rootModuleName]: value };
5716
5722
  this._libs = void 0;
5717
- this._fromFullProject = false;
5718
5723
  this.requestBuild();
5719
5724
  }
5720
5725
  /** Conditions for conditional compilation (@if/@elif/@else). */
@@ -5732,28 +5737,21 @@ var WgslPlay = class extends HTMLElement {
5732
5737
  /** Set project configuration (mirrors wesl link() API). */
5733
5738
  set project(value) {
5734
5739
  const { weslSrc, rootModuleName, libs, packageName, conditions, constants } = value;
5735
- if (packageName || conditions || constants) this._linkOptions = {
5736
- packageName,
5737
- conditions,
5738
- constants
5739
- };
5740
+ if (packageName !== void 0) this._linkOptions.packageName = packageName;
5741
+ if (conditions !== void 0) this._linkOptions.conditions = conditions;
5742
+ if (constants !== void 0) this._linkOptions.constants = constants;
5740
5743
  if (libs) this._libs = libs;
5741
5744
  if (weslSrc) {
5742
- this.setProjectSources(weslSrc, rootModuleName);
5745
+ const pkg = this._linkOptions.packageName || "package";
5746
+ const root = rootModuleName ?? "main";
5747
+ this._weslSrc = toModulePaths(weslSrc, pkg);
5748
+ this._rootModuleName = fileToModulePath(root, pkg, false);
5749
+ this.requestBuild();
5743
5750
  return;
5744
5751
  }
5745
5752
  if (Object.keys(this._weslSrc).length === 0) return;
5746
5753
  this.requestBuild();
5747
5754
  }
5748
- /** Set sources from a full project with weslSrc. */
5749
- setProjectSources(weslSrc, rootModuleName) {
5750
- const pkg = this._linkOptions.packageName || "package";
5751
- const root = rootModuleName ?? "main";
5752
- this._weslSrc = toModulePaths(weslSrc, pkg);
5753
- this._rootModuleName = fileToModulePath(root, pkg, false);
5754
- this._fromFullProject = true;
5755
- this.requestBuild();
5756
- }
5757
5755
  /** Whether to auto-fetch missing library packages from npm (default: true). */
5758
5756
  get fetchLibs() {
5759
5757
  return this._fetchLibs;
@@ -5763,6 +5761,15 @@ var WgslPlay = class extends HTMLElement {
5763
5761
  if (value) this.removeAttribute("fetch-libs");
5764
5762
  else this.setAttribute("fetch-libs", "false");
5765
5763
  }
5764
+ /** Whether to fetch local .wesl source files via HTTP (default: true). */
5765
+ get fetchSources() {
5766
+ return this._fetchSources;
5767
+ }
5768
+ set fetchSources(value) {
5769
+ this._fetchSources = value;
5770
+ if (value) this.removeAttribute("fetch-sources");
5771
+ else this.setAttribute("fetch-sources", "false");
5772
+ }
5766
5773
  /** Whether autoplay is enabled (default: true). Set autoplay="false" to start paused. */
5767
5774
  get autoplay() {
5768
5775
  return this.getAttribute("autoplay") !== "false";
@@ -5858,9 +5865,9 @@ var WgslPlay = class extends HTMLElement {
5858
5865
  }
5859
5866
  /** Load from source element, src URL, script child, or inline textContent. */
5860
5867
  loadInitialContent() {
5861
- const sourceId = this.getAttribute("source");
5862
- if (sourceId) {
5863
- this.connectToSource(sourceId);
5868
+ const fromId = this.getAttribute("from");
5869
+ if (fromId) {
5870
+ this.connectFrom(fromId);
5864
5871
  return;
5865
5872
  }
5866
5873
  const src = this.getAttribute("src");
@@ -5871,43 +5878,21 @@ var WgslPlay = class extends HTMLElement {
5871
5878
  const inlineSource = this.querySelector("script[type=\"text/wgsl\"], script[type=\"text/wesl\"]")?.textContent?.trim() ?? this.textContent?.trim();
5872
5879
  if (!inlineSource) return;
5873
5880
  this._weslSrc = { [this._rootModuleName]: inlineSource };
5874
- this._fromFullProject = false;
5875
5881
  this.requestBuild();
5876
5882
  }
5877
5883
  /** Connect to a source provider element (e.g., wgsl-edit). */
5878
- connectToSource(id) {
5884
+ connectFrom(id) {
5879
5885
  const el = document.getElementById(id);
5880
5886
  if (!el) {
5881
5887
  console.error(`wgsl-play: source element "${id}" not found`);
5882
5888
  return;
5883
5889
  }
5884
5890
  this._sourceEl = el;
5885
- const getSources = () => {
5886
- const sources = el.sources;
5887
- if (sources && Object.keys(sources).length > 0) return sources;
5888
- const source = el.source ?? el.textContent ?? "";
5889
- return { [this._rootModuleName]: source };
5890
- };
5891
- const { conditions, rootModuleName, libs } = el;
5892
- const root = rootModuleName ?? "main";
5893
- this._weslSrc = getSources();
5894
- this._rootModuleName = fileToModulePath(root, "package", false);
5895
- if (conditions) this._linkOptions = {
5896
- ...this._linkOptions,
5897
- conditions
5898
- };
5899
- if (libs) this._libs = libs;
5900
- this._fromFullProject = false;
5901
- this.requestBuild();
5891
+ const p = el.project;
5892
+ if (p) this.project = p;
5902
5893
  this._sourceListener = (e) => {
5903
5894
  const detail = e.detail;
5904
- const fallback = { [this._rootModuleName]: detail?.source ?? "" };
5905
- this.project = {
5906
- weslSrc: detail?.sources ?? fallback,
5907
- rootModuleName: detail?.rootModuleName,
5908
- conditions: detail?.conditions,
5909
- libs: detail?.libs
5910
- };
5895
+ if (detail) this.project = detail;
5911
5896
  };
5912
5897
  el.addEventListener("change", this._sourceListener);
5913
5898
  }
@@ -5917,7 +5902,6 @@ var WgslPlay = class extends HTMLElement {
5917
5902
  const { weslSrc, libs, rootModuleName } = await loadShaderFromUrl(url, this.getConfigOverrides()?.shaderRoot);
5918
5903
  this._weslSrc = weslSrc;
5919
5904
  this._libs = libs;
5920
- this._fromFullProject = false;
5921
5905
  if (rootModuleName) this._rootModuleName = rootModuleName;
5922
5906
  this.requestBuild();
5923
5907
  } catch (error) {
@@ -5942,11 +5926,12 @@ var WgslPlay = class extends HTMLElement {
5942
5926
  }
5943
5927
  try {
5944
5928
  this.errorOverlay.hide();
5945
- if (!this._fromFullProject) {
5929
+ if (this._fetchSources || this._fetchLibs) {
5946
5930
  const { weslSrc, libs } = await fetchDependencies(mainSource, {
5947
5931
  shaderRoot: this.getConfigOverrides()?.shaderRoot,
5948
5932
  existingSources: this._weslSrc,
5949
- skipExternal: !this._fetchLibs
5933
+ fetchLibs: this._fetchLibs,
5934
+ fetchSources: this._fetchSources
5950
5935
  });
5951
5936
  this._weslSrc = {
5952
5937
  ...this._weslSrc,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wgsl-play",
3
- "version": "0.0.37",
3
+ "version": "0.0.38",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -22,9 +22,9 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "wesl": "0.7.24",
26
- "wesl-gpu": "0.1.26",
27
- "wesl-fetch": "0.0.12"
25
+ "wesl-fetch": "0.0.13",
26
+ "wesl-gpu": "0.1.27",
27
+ "wesl": "0.7.25"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@playwright/test": "^1.53.2",
package/src/WgslPlay.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Conditions, LinkParams, WeslBundle } from "wesl";
1
+ import type { Conditions, WeslBundle, WeslProject } from "wesl";
2
2
  import { fileToModulePath, WeslParseError } from "wesl";
3
3
  import { fetchDependencies, loadShaderFromUrl } from "wesl-fetch";
4
4
  import type { WgslPlayConfig } from "./Config.ts";
@@ -16,17 +16,6 @@ import cssText from "./WgslPlay.css?inline";
16
16
 
17
17
  export { defaults, getConfig, resetConfig } from "./Config.ts";
18
18
 
19
- /** Project configuration for multi-file shaders (subset of wesl link() API). */
20
- export type WeslProject = Pick<
21
- LinkParams,
22
- | "weslSrc"
23
- | "rootModuleName"
24
- | "conditions"
25
- | "constants"
26
- | "libs"
27
- | "packageName"
28
- >;
29
-
30
19
  /** One source location within a compile error. */
31
20
  export interface CompileErrorLocation {
32
21
  file?: string;
@@ -53,12 +42,13 @@ export class WgslPlay extends HTMLElement {
53
42
  static observedAttributes = [
54
43
  "src",
55
44
  "shader-root",
56
- "source",
45
+ "from",
57
46
  "no-controls",
58
47
  "theme",
59
48
  "autoplay",
60
49
  "transparent",
61
50
  "fetch-libs",
51
+ "fetch-sources",
62
52
  ];
63
53
 
64
54
  private canvas: HTMLCanvasElement;
@@ -78,7 +68,7 @@ export class WgslPlay extends HTMLElement {
78
68
  private _rootModuleName = "package::main";
79
69
  private _libs?: WeslBundle[];
80
70
  private _linkOptions: LinkOptions = {};
81
- private _fromFullProject = false;
71
+ private _fetchSources = true;
82
72
  private _initPromise?: Promise<boolean>;
83
73
  private _sourceEl: HTMLElement | null = null;
84
74
  private _sourceListener: ((e: Event) => void) | null = null;
@@ -138,7 +128,7 @@ export class WgslPlay extends HTMLElement {
138
128
  }
139
129
  this.initialize();
140
130
  upgradeProperty(this, "conditions");
141
- upgradeProperty(this, "source");
131
+ upgradeProperty(this, "shader");
142
132
  upgradeProperty(this, "project");
143
133
  }
144
134
 
@@ -179,6 +169,11 @@ export class WgslPlay extends HTMLElement {
179
169
  return;
180
170
  }
181
171
 
172
+ if (name === "fetch-sources") {
173
+ this._fetchSources = newValue !== "false";
174
+ return;
175
+ }
176
+
182
177
  // Initial src is handled by initialize(); this handles later changes
183
178
  if (name === "src" && newValue && this._initPromise) {
184
179
  this.loadFromUrl(newValue);
@@ -186,15 +181,14 @@ export class WgslPlay extends HTMLElement {
186
181
  }
187
182
 
188
183
  /** Current shader source code (main module). */
189
- get source(): string {
184
+ get shader(): string {
190
185
  return this._weslSrc[this._rootModuleName] ?? "";
191
186
  }
192
187
 
193
- /** Set shader source directly. */
194
- set source(value: string) {
188
+ /** Set shader source directly (single-file convenience). */
189
+ set shader(value: string) {
195
190
  this._weslSrc = { [this._rootModuleName]: value };
196
191
  this._libs = undefined;
197
- this._fromFullProject = false;
198
192
  this.requestBuild();
199
193
  }
200
194
 
@@ -219,15 +213,17 @@ export class WgslPlay extends HTMLElement {
219
213
  conditions,
220
214
  constants,
221
215
  } = value;
222
-
223
- // Update link options if provided
224
- if (packageName || conditions || constants) {
225
- this._linkOptions = { packageName, conditions, constants };
226
- }
216
+ if (packageName !== undefined) this._linkOptions.packageName = packageName;
217
+ if (conditions !== undefined) this._linkOptions.conditions = conditions;
218
+ if (constants !== undefined) this._linkOptions.constants = constants;
227
219
  if (libs) this._libs = libs;
228
220
 
229
221
  if (weslSrc) {
230
- this.setProjectSources(weslSrc, rootModuleName);
222
+ const pkg = this._linkOptions.packageName || "package";
223
+ const root = rootModuleName ?? "main";
224
+ this._weslSrc = toModulePaths(weslSrc, pkg);
225
+ this._rootModuleName = fileToModulePath(root, pkg, false);
226
+ this.requestBuild();
231
227
  return;
232
228
  }
233
229
 
@@ -235,19 +231,6 @@ export class WgslPlay extends HTMLElement {
235
231
  this.requestBuild();
236
232
  }
237
233
 
238
- /** Set sources from a full project with weslSrc. */
239
- private setProjectSources(
240
- weslSrc: Record<string, string>,
241
- rootModuleName?: string,
242
- ): void {
243
- const pkg = this._linkOptions.packageName || "package";
244
- const root = rootModuleName ?? "main";
245
- this._weslSrc = toModulePaths(weslSrc, pkg);
246
- this._rootModuleName = fileToModulePath(root, pkg, false);
247
- this._fromFullProject = true;
248
- this.requestBuild();
249
- }
250
-
251
234
  /** Whether to auto-fetch missing library packages from npm (default: true). */
252
235
  get fetchLibs(): boolean {
253
236
  return this._fetchLibs;
@@ -259,6 +242,17 @@ export class WgslPlay extends HTMLElement {
259
242
  else this.setAttribute("fetch-libs", "false");
260
243
  }
261
244
 
245
+ /** Whether to fetch local .wesl source files via HTTP (default: true). */
246
+ get fetchSources(): boolean {
247
+ return this._fetchSources;
248
+ }
249
+
250
+ set fetchSources(value: boolean) {
251
+ this._fetchSources = value;
252
+ if (value) this.removeAttribute("fetch-sources");
253
+ else this.setAttribute("fetch-sources", "false");
254
+ }
255
+
262
256
  /** Whether autoplay is enabled (default: true). Set autoplay="false" to start paused. */
263
257
  get autoplay(): boolean {
264
258
  return this.getAttribute("autoplay") !== "false";
@@ -383,9 +377,9 @@ export class WgslPlay extends HTMLElement {
383
377
 
384
378
  /** Load from source element, src URL, script child, or inline textContent. */
385
379
  private loadInitialContent(): void {
386
- const sourceId = this.getAttribute("source");
387
- if (sourceId) {
388
- this.connectToSource(sourceId);
380
+ const fromId = this.getAttribute("from");
381
+ if (fromId) {
382
+ this.connectFrom(fromId);
389
383
  return;
390
384
  }
391
385
 
@@ -404,12 +398,11 @@ export class WgslPlay extends HTMLElement {
404
398
  if (!inlineSource) return;
405
399
 
406
400
  this._weslSrc = { [this._rootModuleName]: inlineSource };
407
- this._fromFullProject = false;
408
401
  this.requestBuild();
409
402
  }
410
403
 
411
404
  /** Connect to a source provider element (e.g., wgsl-edit). */
412
- private connectToSource(id: string): void {
405
+ private connectFrom(id: string): void {
413
406
  const el = document.getElementById(id);
414
407
  if (!el) {
415
408
  console.error(`wgsl-play: source element "${id}" not found`);
@@ -417,34 +410,12 @@ export class WgslPlay extends HTMLElement {
417
410
  }
418
411
  this._sourceEl = el;
419
412
 
420
- // Get sources from element - support both single-file and multi-file APIs
421
- const getSources = (): Record<string, string> => {
422
- const sources = (el as any).sources;
423
- if (sources && Object.keys(sources).length > 0) return sources;
424
- const source = (el as any).source ?? el.textContent ?? "";
425
- return { [this._rootModuleName]: source };
426
- };
427
-
428
- // Load initial sources, conditions, and libs from source element.
429
- const { conditions, rootModuleName, libs } = el as any;
430
- const root = rootModuleName ?? "main";
431
- this._weslSrc = getSources();
432
- this._rootModuleName = fileToModulePath(root, "package", false);
433
- if (conditions) this._linkOptions = { ...this._linkOptions, conditions };
434
- if (libs) this._libs = libs;
435
- this._fromFullProject = false;
436
- this.requestBuild();
413
+ const p = (el as any).project as WeslProject | undefined;
414
+ if (p) this.project = p;
437
415
 
438
- // Listen for changes - rebuild with libs from source element
439
416
  this._sourceListener = (e: Event) => {
440
- const detail = (e as CustomEvent).detail;
441
- const fallback = { [this._rootModuleName]: detail?.source ?? "" };
442
- this.project = {
443
- weslSrc: detail?.sources ?? fallback,
444
- rootModuleName: detail?.rootModuleName,
445
- conditions: detail?.conditions,
446
- libs: detail?.libs,
447
- };
417
+ const detail = (e as CustomEvent).detail as WeslProject | undefined;
418
+ if (detail) this.project = detail;
448
419
  };
449
420
  el.addEventListener("change", this._sourceListener);
450
421
  }
@@ -458,7 +429,6 @@ export class WgslPlay extends HTMLElement {
458
429
  );
459
430
  this._weslSrc = weslSrc;
460
431
  this._libs = libs;
461
- this._fromFullProject = false;
462
432
  if (rootModuleName) this._rootModuleName = rootModuleName;
463
433
  this.requestBuild();
464
434
  } catch (error) {
@@ -490,11 +460,12 @@ export class WgslPlay extends HTMLElement {
490
460
 
491
461
  try {
492
462
  this.errorOverlay.hide();
493
- if (!this._fromFullProject) {
463
+ if (this._fetchSources || this._fetchLibs) {
494
464
  const { weslSrc, libs } = await fetchDependencies(mainSource, {
495
465
  shaderRoot: this.getConfigOverrides()?.shaderRoot,
496
466
  existingSources: this._weslSrc,
497
- skipExternal: !this._fetchLibs,
467
+ fetchLibs: this._fetchLibs,
468
+ fetchSources: this._fetchSources,
498
469
  });
499
470
  this._weslSrc = { ...this._weslSrc, ...weslSrc };
500
471
  this._libs = dedupLibs(this._libs, libs);