wgsl-edit 0.0.25 → 0.0.26

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/wgsl-edit.js CHANGED
@@ -5073,11 +5073,11 @@ var shift = {
5073
5073
  };
5074
5074
  var mac = typeof navigator != "undefined" && /Mac/.test(navigator.platform);
5075
5075
  var ie$1 = typeof navigator != "undefined" && /MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
5076
- for (var i$1 = 0; i$1 < 10; i$1++) base[48 + i$1] = base[96 + i$1] = String(i$1);
5077
- for (var i$1 = 1; i$1 <= 24; i$1++) base[i$1 + 111] = "F" + i$1;
5078
- for (var i$1 = 65; i$1 <= 90; i$1++) {
5079
- base[i$1] = String.fromCharCode(i$1 + 32);
5080
- shift[i$1] = String.fromCharCode(i$1);
5076
+ for (var i = 0; i < 10; i++) base[48 + i] = base[96 + i] = String(i);
5077
+ for (var i = 1; i <= 24; i++) base[i + 111] = "F" + i;
5078
+ for (var i = 65; i <= 90; i++) {
5079
+ base[i] = String.fromCharCode(i + 32);
5080
+ shift[i] = String.fromCharCode(i);
5081
5081
  }
5082
5082
  for (var code in base) if (!shift.hasOwnProperty(code)) shift[code] = base[code];
5083
5083
  function keyName(event) {
@@ -15018,7 +15018,7 @@ tagHighlighter([
15018
15018
  ]);
15019
15019
  //#endregion
15020
15020
  //#region ../../node_modules/.pnpm/@codemirror+language@6.11.2/node_modules/@codemirror/language/dist/index.js
15021
- var _a$1;
15021
+ var _a;
15022
15022
  /**
15023
15023
  Node prop stored in a parser's top syntax node to provide the
15024
15024
  facet that stores language-specific data for that language.
@@ -15479,7 +15479,7 @@ if (typeof requestIdleCallback != "undefined") requestIdle = (callback) => {
15479
15479
  }, 100);
15480
15480
  return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
15481
15481
  };
15482
- const isInputPending = typeof navigator != "undefined" && ((_a$1 = navigator.scheduling) === null || _a$1 === void 0 ? void 0 : _a$1.isInputPending) ? () => navigator.scheduling.isInputPending() : null;
15482
+ const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending) ? () => navigator.scheduling.isInputPending() : null;
15483
15483
  const parseWorker = /* @__PURE__ */ ViewPlugin.fromClass(class ParseWorker {
15484
15484
  constructor(view) {
15485
15485
  this.view = view;
@@ -19035,12 +19035,13 @@ const isBrowser = "document" in globalThis;
19035
19035
  /** Throw an error with an embedded source map so that browser users can
19036
19036
  * click on the error in the browser debug console and see the wesl source code. */
19037
19037
  function throwClickableError(params) {
19038
- const { url, text, lineNumber, lineColumn, length, error } = params;
19038
+ const { url, text, lineNumber, lineColumn, length, offset, error } = params;
19039
19039
  const weslLocation = {
19040
19040
  file: url,
19041
19041
  line: lineNumber,
19042
19042
  column: lineColumn,
19043
- length
19043
+ length,
19044
+ offset
19044
19045
  };
19045
19046
  error.weslLocation = weslLocation;
19046
19047
  if (!isBrowser) throw error;
@@ -19101,6 +19102,7 @@ function failIdentElem(identElem, msg = "") {
19101
19102
  lineNumber,
19102
19103
  lineColumn,
19103
19104
  length: end - start,
19105
+ offset: start,
19104
19106
  error: new Error(detailedMessage)
19105
19107
  });
19106
19108
  }
@@ -19376,7 +19378,7 @@ function lowerAndEmitElem(e, ctx) {
19376
19378
  case "let":
19377
19379
  case "statement":
19378
19380
  case "continuing":
19379
- emitStatement$1(e, ctx);
19381
+ emitStatement(e, ctx);
19380
19382
  return;
19381
19383
  case "override":
19382
19384
  case "const":
@@ -19414,7 +19416,7 @@ function emitModule(e, ctx) {
19414
19416
  lowerAndEmitElem(child, ctx);
19415
19417
  }
19416
19418
  }
19417
- function emitStatement$1(e, ctx) {
19419
+ function emitStatement(e, ctx) {
19418
19420
  if (!(e.contents.length > 0 && e.contents[0].kind === "attribute")) emitAttributes(e.attributes, ctx);
19419
19421
  emitContents(e, ctx);
19420
19422
  }
@@ -22751,20 +22753,13 @@ async function link(params) {
22751
22753
  }
22752
22754
  /** linker api for benchmarking */
22753
22755
  function _linkSync(params) {
22754
- const { weslSrc, libs = [], packageName, debugWeslRoot } = params;
22755
- const { resolver } = params;
22756
- const resolvers = [];
22757
- if (resolver) resolvers.push(resolver);
22758
- else if (weslSrc) resolvers.push(new RecordResolver(weslSrc, {
22756
+ const { weslSrc, libs = [], packageName, debugWeslRoot, resolver } = params;
22757
+ if (!resolver && !weslSrc) throw new Error("Either resolver or weslSrc must be provided");
22758
+ const allResolvers = [resolver ?? new RecordResolver(weslSrc, {
22759
22759
  packageName,
22760
22760
  debugWeslRoot
22761
- }));
22762
- else throw new Error("Either resolver or weslSrc must be provided");
22763
- if (libs.length > 0) {
22764
- const libResolvers = createLibraryResolvers(libs, debugWeslRoot);
22765
- resolvers.push(...libResolvers);
22766
- }
22767
- const finalResolver = resolvers.length === 1 ? resolvers[0] : new CompositeResolver(resolvers);
22761
+ }), ...createLibraryResolvers(libs, debugWeslRoot)];
22762
+ const finalResolver = allResolvers.length === 1 ? allResolvers[0] : new CompositeResolver(allResolvers);
22768
22763
  return linkRegistry({
22769
22764
  ...params,
22770
22765
  resolver: finalResolver
@@ -22848,6 +22843,7 @@ function setupVirtualLibs(virtualLibs, constants) {
22848
22843
  }
22849
22844
  return libs && mapValues(libs, (fn) => ({ fn }));
22850
22845
  }
22846
+ /** Run registered transform plugins over the bound AST. */
22851
22847
  function applyTransformPlugins(rootModule, globalNames, config) {
22852
22848
  const { moduleElem, srcModule } = rootModule;
22853
22849
  const startAst = {
@@ -22860,53 +22856,38 @@ function applyTransformPlugins(rootModule, globalNames, config) {
22860
22856
  }
22861
22857
  /** Assemble WGSL output from prologue statements, root module, and imported declarations. */
22862
22858
  function emitWgsl(rootModuleElem, srcModule, newDecls, newStatements, conditions = {}) {
22863
- const prologueBuilders = newStatements.map((s) => emitStatement(s, conditions));
22864
- const rootBuilder = new SrcMapBuilder({
22865
- text: srcModule.src,
22866
- path: srcModule.debugFilePath
22867
- });
22859
+ const prologueBuilders = newStatements.map((s) => emitElem(s.srcModule, s.elem, conditions, { addNl: true }));
22860
+ const rootBuilder = builderFromModule(srcModule);
22868
22861
  lowerAndEmit({
22869
22862
  srcBuilder: rootBuilder,
22870
22863
  rootElems: [rootModuleElem],
22871
22864
  conditions,
22872
22865
  extracting: false
22873
22866
  });
22874
- const declBuilders = newDecls.map((decl) => emitDecl(decl, conditions));
22867
+ const declBuilders = newDecls.map((decl) => emitElem(decl.srcModule, decl.declElem, conditions, { skipConditionalFiltering: true }));
22875
22868
  return [
22876
22869
  ...prologueBuilders,
22877
22870
  rootBuilder,
22878
22871
  ...declBuilders
22879
22872
  ];
22880
22873
  }
22881
- function emitStatement(s, conditions) {
22882
- const { elem, srcModule } = s;
22883
- const { src: text, debugFilePath: path } = srcModule;
22884
- const builder = new SrcMapBuilder({
22885
- text,
22886
- path
22887
- });
22874
+ /** Emit a single element (prologue statement or imported declaration) into a SrcMapBuilder. */
22875
+ function emitElem(srcModule, elem, conditions, opts = {}) {
22876
+ const builder = builderFromModule(srcModule);
22888
22877
  lowerAndEmit({
22889
22878
  srcBuilder: builder,
22890
22879
  rootElems: [elem],
22891
- conditions
22880
+ conditions,
22881
+ skipConditionalFiltering: opts.skipConditionalFiltering
22892
22882
  });
22893
- builder.addNl();
22883
+ if (opts.addNl) builder.addNl();
22894
22884
  return builder;
22895
22885
  }
22896
- /** Skip conditional filtering because findValidRootDecls already validated these declarations */
22897
- function emitDecl(decl, conditions) {
22898
- const { src: text, debugFilePath: path } = decl.srcModule;
22899
- const builder = new SrcMapBuilder({
22900
- text,
22901
- path
22902
- });
22903
- lowerAndEmit({
22904
- srcBuilder: builder,
22905
- rootElems: [decl.declElem],
22906
- conditions,
22907
- skipConditionalFiltering: true
22886
+ function builderFromModule(srcModule) {
22887
+ return new SrcMapBuilder({
22888
+ text: srcModule.src,
22889
+ path: srcModule.debugFilePath
22908
22890
  });
22909
- return builder;
22910
22891
  }
22911
22892
  matchOneOf(textureStorageTypes);
22912
22893
  matchOneOf(sampledTextureTypes);
@@ -27394,338 +27375,6 @@ async function hydrateBundle(path, registry, hydrated, fetcher) {
27394
27375
  return placeholder;
27395
27376
  }
27396
27377
  //#endregion
27397
- //#region ../../node_modules/.pnpm/fflate@0.8.2/node_modules/fflate/esm/browser.js
27398
- var u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;
27399
- var fleb = new u8([
27400
- 0,
27401
- 0,
27402
- 0,
27403
- 0,
27404
- 0,
27405
- 0,
27406
- 0,
27407
- 0,
27408
- 1,
27409
- 1,
27410
- 1,
27411
- 1,
27412
- 2,
27413
- 2,
27414
- 2,
27415
- 2,
27416
- 3,
27417
- 3,
27418
- 3,
27419
- 3,
27420
- 4,
27421
- 4,
27422
- 4,
27423
- 4,
27424
- 5,
27425
- 5,
27426
- 5,
27427
- 5,
27428
- 0,
27429
- 0,
27430
- 0,
27431
- 0
27432
- ]);
27433
- var fdeb = new u8([
27434
- 0,
27435
- 0,
27436
- 0,
27437
- 0,
27438
- 1,
27439
- 1,
27440
- 2,
27441
- 2,
27442
- 3,
27443
- 3,
27444
- 4,
27445
- 4,
27446
- 5,
27447
- 5,
27448
- 6,
27449
- 6,
27450
- 7,
27451
- 7,
27452
- 8,
27453
- 8,
27454
- 9,
27455
- 9,
27456
- 10,
27457
- 10,
27458
- 11,
27459
- 11,
27460
- 12,
27461
- 12,
27462
- 13,
27463
- 13,
27464
- 0,
27465
- 0
27466
- ]);
27467
- var clim = new u8([
27468
- 16,
27469
- 17,
27470
- 18,
27471
- 0,
27472
- 8,
27473
- 7,
27474
- 9,
27475
- 6,
27476
- 10,
27477
- 5,
27478
- 11,
27479
- 4,
27480
- 12,
27481
- 3,
27482
- 13,
27483
- 2,
27484
- 14,
27485
- 1,
27486
- 15
27487
- ]);
27488
- var freb = function(eb, start) {
27489
- var b = new u16(31);
27490
- for (var i = 0; i < 31; ++i) b[i] = start += 1 << eb[i - 1];
27491
- var r = new i32(b[30]);
27492
- for (var i = 1; i < 30; ++i) for (var j = b[i]; j < b[i + 1]; ++j) r[j] = j - b[i] << 5 | i;
27493
- return {
27494
- b,
27495
- r
27496
- };
27497
- };
27498
- var _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;
27499
- fl[28] = 258, revfl[258] = 28;
27500
- var _b = freb(fdeb, 0), fd = _b.b;
27501
- _b.r;
27502
- var rev = new u16(32768);
27503
- for (var i = 0; i < 32768; ++i) {
27504
- var x = (i & 43690) >> 1 | (i & 21845) << 1;
27505
- x = (x & 52428) >> 2 | (x & 13107) << 2;
27506
- x = (x & 61680) >> 4 | (x & 3855) << 4;
27507
- rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
27508
- }
27509
- var hMap = (function(cd, mb, r) {
27510
- var s = cd.length;
27511
- var i = 0;
27512
- var l = new u16(mb);
27513
- for (; i < s; ++i) if (cd[i]) ++l[cd[i] - 1];
27514
- var le = new u16(mb);
27515
- for (i = 1; i < mb; ++i) le[i] = le[i - 1] + l[i - 1] << 1;
27516
- var co;
27517
- if (r) {
27518
- co = new u16(1 << mb);
27519
- var rvb = 15 - mb;
27520
- for (i = 0; i < s; ++i) if (cd[i]) {
27521
- var sv = i << 4 | cd[i];
27522
- var r_1 = mb - cd[i];
27523
- var v = le[cd[i] - 1]++ << r_1;
27524
- for (var m = v | (1 << r_1) - 1; v <= m; ++v) co[rev[v] >> rvb] = sv;
27525
- }
27526
- } else {
27527
- co = new u16(s);
27528
- for (i = 0; i < s; ++i) if (cd[i]) co[i] = rev[le[cd[i] - 1]++] >> 15 - cd[i];
27529
- }
27530
- return co;
27531
- });
27532
- var flt = new u8(288);
27533
- for (var i = 0; i < 144; ++i) flt[i] = 8;
27534
- for (var i = 144; i < 256; ++i) flt[i] = 9;
27535
- for (var i = 256; i < 280; ++i) flt[i] = 7;
27536
- for (var i = 280; i < 288; ++i) flt[i] = 8;
27537
- var fdt = new u8(32);
27538
- for (var i = 0; i < 32; ++i) fdt[i] = 5;
27539
- var flrm = /* @__PURE__ */ hMap(flt, 9, 1), fdrm = /* @__PURE__ */ hMap(fdt, 5, 1);
27540
- var max = function(a) {
27541
- var m = a[0];
27542
- for (var i = 1; i < a.length; ++i) if (a[i] > m) m = a[i];
27543
- return m;
27544
- };
27545
- var bits = function(d, p, m) {
27546
- var o = p / 8 | 0;
27547
- return (d[o] | d[o + 1] << 8) >> (p & 7) & m;
27548
- };
27549
- var bits16 = function(d, p) {
27550
- var o = p / 8 | 0;
27551
- return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7);
27552
- };
27553
- var shft = function(p) {
27554
- return (p + 7) / 8 | 0;
27555
- };
27556
- var slc = function(v, s, e) {
27557
- if (s == null || s < 0) s = 0;
27558
- if (e == null || e > v.length) e = v.length;
27559
- return new u8(v.subarray(s, e));
27560
- };
27561
- var ec = [
27562
- "unexpected EOF",
27563
- "invalid block type",
27564
- "invalid length/literal",
27565
- "invalid distance",
27566
- "stream finished",
27567
- "no stream handler",
27568
- ,
27569
- "no callback",
27570
- "invalid UTF-8 data",
27571
- "extra field too long",
27572
- "date not in range 1980-2099",
27573
- "filename too long",
27574
- "stream finishing",
27575
- "invalid zip data"
27576
- ];
27577
- var err = function(ind, msg, nt) {
27578
- var e = new Error(msg || ec[ind]);
27579
- e.code = ind;
27580
- if (Error.captureStackTrace) Error.captureStackTrace(e, err);
27581
- if (!nt) throw e;
27582
- return e;
27583
- };
27584
- var inflt = function(dat, st, buf, dict) {
27585
- var sl = dat.length, dl = dict ? dict.length : 0;
27586
- if (!sl || st.f && !st.l) return buf || new u8(0);
27587
- var noBuf = !buf;
27588
- var resize = noBuf || st.i != 2;
27589
- var noSt = st.i;
27590
- if (noBuf) buf = new u8(sl * 3);
27591
- var cbuf = function(l) {
27592
- var bl = buf.length;
27593
- if (l > bl) {
27594
- var nbuf = new u8(Math.max(bl * 2, l));
27595
- nbuf.set(buf);
27596
- buf = nbuf;
27597
- }
27598
- };
27599
- var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;
27600
- var tbts = sl * 8;
27601
- do {
27602
- if (!lm) {
27603
- final = bits(dat, pos, 1);
27604
- var type = bits(dat, pos + 1, 3);
27605
- pos += 3;
27606
- if (!type) {
27607
- var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
27608
- if (t > sl) {
27609
- if (noSt) err(0);
27610
- break;
27611
- }
27612
- if (resize) cbuf(bt + l);
27613
- buf.set(dat.subarray(s, t), bt);
27614
- st.b = bt += l, st.p = pos = t * 8, st.f = final;
27615
- continue;
27616
- } else if (type == 1) lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
27617
- else if (type == 2) {
27618
- var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
27619
- var tl = hLit + bits(dat, pos + 5, 31) + 1;
27620
- pos += 14;
27621
- var ldt = new u8(tl);
27622
- var clt = new u8(19);
27623
- for (var i = 0; i < hcLen; ++i) clt[clim[i]] = bits(dat, pos + i * 3, 7);
27624
- pos += hcLen * 3;
27625
- var clb = max(clt), clbmsk = (1 << clb) - 1;
27626
- var clm = hMap(clt, clb, 1);
27627
- for (var i = 0; i < tl;) {
27628
- var r = clm[bits(dat, pos, clbmsk)];
27629
- pos += r & 15;
27630
- var s = r >> 4;
27631
- if (s < 16) ldt[i++] = s;
27632
- else {
27633
- var c = 0, n = 0;
27634
- if (s == 16) n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];
27635
- else if (s == 17) n = 3 + bits(dat, pos, 7), pos += 3;
27636
- else if (s == 18) n = 11 + bits(dat, pos, 127), pos += 7;
27637
- while (n--) ldt[i++] = c;
27638
- }
27639
- }
27640
- var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
27641
- lbt = max(lt);
27642
- dbt = max(dt);
27643
- lm = hMap(lt, lbt, 1);
27644
- dm = hMap(dt, dbt, 1);
27645
- } else err(1);
27646
- if (pos > tbts) {
27647
- if (noSt) err(0);
27648
- break;
27649
- }
27650
- }
27651
- if (resize) cbuf(bt + 131072);
27652
- var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
27653
- var lpos = pos;
27654
- for (;; lpos = pos) {
27655
- var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
27656
- pos += c & 15;
27657
- if (pos > tbts) {
27658
- if (noSt) err(0);
27659
- break;
27660
- }
27661
- if (!c) err(2);
27662
- if (sym < 256) buf[bt++] = sym;
27663
- else if (sym == 256) {
27664
- lpos = pos, lm = null;
27665
- break;
27666
- } else {
27667
- var add = sym - 254;
27668
- if (sym > 264) {
27669
- var i = sym - 257, b = fleb[i];
27670
- add = bits(dat, pos, (1 << b) - 1) + fl[i];
27671
- pos += b;
27672
- }
27673
- var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
27674
- if (!d) err(3);
27675
- pos += d & 15;
27676
- var dt = fd[dsym];
27677
- if (dsym > 3) {
27678
- var b = fdeb[dsym];
27679
- dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
27680
- }
27681
- if (pos > tbts) {
27682
- if (noSt) err(0);
27683
- break;
27684
- }
27685
- if (resize) cbuf(bt + 131072);
27686
- var end = bt + add;
27687
- if (bt < dt) {
27688
- var shift = dl - dt, dend = Math.min(dt, end);
27689
- if (shift + bt < 0) err(3);
27690
- for (; bt < dend; ++bt) buf[bt] = dict[shift + bt];
27691
- }
27692
- for (; bt < end; ++bt) buf[bt] = buf[bt - dt];
27693
- }
27694
- }
27695
- st.l = lm, st.p = lpos, st.b = bt, st.f = final;
27696
- if (lm) final = 1, st.m = lbt, st.d = dm, st.n = dbt;
27697
- } while (!final);
27698
- return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
27699
- };
27700
- var et = /* @__PURE__ */ new u8(0);
27701
- var gzs = function(d) {
27702
- if (d[0] != 31 || d[1] != 139 || d[2] != 8) err(6, "invalid gzip data");
27703
- var flg = d[3];
27704
- var st = 10;
27705
- if (flg & 4) st += (d[10] | d[11] << 8) + 2;
27706
- for (var zs = (flg >> 3 & 1) + (flg >> 4 & 1); zs > 0; zs -= !d[st++]);
27707
- return st + (flg & 2);
27708
- };
27709
- var gzl = function(d) {
27710
- var l = d.length;
27711
- return (d[l - 4] | d[l - 3] << 8 | d[l - 2] << 16 | d[l - 1] << 24) >>> 0;
27712
- };
27713
- /**
27714
- * Expands GZIP data
27715
- * @param data The data to decompress
27716
- * @param opts The decompression options
27717
- * @returns The decompressed version of the data
27718
- */
27719
- function gunzipSync(data, opts) {
27720
- var st = gzs(data);
27721
- if (st + 8 > data.length) err(6, "invalid gzip data");
27722
- return inflt(data.subarray(st, -8), { i: 2 }, opts && opts.out || new u8(gzl(data)), opts && opts.dictionary);
27723
- }
27724
- var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
27725
- try {
27726
- td.decode(et, { stream: true });
27727
- } catch (e) {}
27728
- //#endregion
27729
27378
  //#region ../../node_modules/.pnpm/nanotar@0.2.0/node_modules/nanotar/dist/index.mjs
27730
27379
  const TAR_TYPE_FILE = 0;
27731
27380
  const TAR_TYPE_DIR = 5;
@@ -27777,6 +27426,13 @@ function parseTar(data, opts) {
27777
27426
  }
27778
27427
  return files;
27779
27428
  }
27429
+ async function parseTarGzip(data, opts = {}) {
27430
+ const stream = new ReadableStream({ start(controller) {
27431
+ controller.enqueue(new Uint8Array(data));
27432
+ controller.close();
27433
+ } }).pipeThrough(new DecompressionStream(opts.compression ?? "gzip"));
27434
+ return parseTar(await new Response(stream).arrayBuffer(), opts);
27435
+ }
27780
27436
  function _readString(buffer, offset, size) {
27781
27437
  const view = new Uint8Array(buffer, offset, size);
27782
27438
  const i = view.indexOf(0);
@@ -27813,7 +27469,7 @@ function memoAsync(fn) {
27813
27469
  async function fetchAndExtractTgzRaw(tgzUrl) {
27814
27470
  const response = await fetch(tgzUrl);
27815
27471
  if (!response.ok) throw new Error(`Failed to fetch package: HTTP ${response.status}`);
27816
- return parseTar(gunzipSync(new Uint8Array(await response.arrayBuffer())));
27472
+ return parseTarGzip(new Uint8Array(await response.arrayBuffer()));
27817
27473
  }
27818
27474
  async function npmPackageToUrlRaw(packageName) {
27819
27475
  const metadataUrl = `https://registry.npmjs.org/${packageName}`;
@@ -27880,6 +27536,10 @@ async function fetchOnePackage(pkgId, loaded) {
27880
27536
  throw new Error(`Package not found: ${pkgId}`);
27881
27537
  }
27882
27538
  //#endregion
27539
+ //#region src/SaveEndpoint.ts
27540
+ /** Shared between client (WgslEdit) and server (SaveMiddleware) so they can't drift. */
27541
+ const saveEndpoint = "/__wgsl-edit/save";
27542
+ //#endregion
27883
27543
  //#region src/WgslEdit.css?inline
27884
27544
  var WgslEdit_default = ":host {\n --tab-bar-bg: transparent;\n --tab-border: #ccc;\n --tab-color: #999;\n --tab-active-bg: #fff;\n --tab-active-color: #222;\n --tab-accent: #5f61d8;\n\n display: flex;\n flex-direction: column;\n position: relative;\n min-height: 100px;\n}\n\n:host(.dark) {\n --tab-bar-bg: transparent;\n --tab-border: #555;\n --tab-color: #999;\n --tab-active-bg: #1e1e1e;\n --tab-active-color: #fff;\n}\n\n.tab-bar {\n --bar-v: 6px;\n --bar-h: 8px;\n display: flex;\n align-items: center;\n gap: 14px;\n padding: var(--bar-v) var(--bar-h);\n background: var(--tab-bar-bg);\n flex-shrink: 0;\n position: relative;\n}\n\n.tab-bar::after {\n content: \"\";\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: var(--tab-border);\n}\n\n.tab {\n --tab-v: 5px;\n --tab-h: 12px;\n display: flex;\n align-items: center;\n gap: 16px;\n padding: var(--tab-v) var(--tab-h);\n background: transparent;\n border: none;\n border-radius: 0;\n color: var(--tab-color);\n cursor: pointer;\n position: relative;\n font-size: 15px;\n font-family: system-ui, sans-serif;\n}\n\n.tab.active {\n color: var(--tab-accent);\n font-weight: 600;\n padding-bottom: calc(var(--tab-v) + var(--bar-v) + 0.5px);\n margin-bottom: calc(-1 * (var(--bar-v) + 0.5px));\n position: relative;\n z-index: 1;\n border-bottom: 2px solid var(--tab-accent);\n}\n\n.tab.active:first-child {\n margin-left: calc(-1 * var(--bar-h));\n padding-left: calc(var(--tab-h) + var(--bar-h));\n}\n\n.tab-close {\n position: absolute;\n right: -8px;\n /* top: 2px; */\n width: 16px;\n height: 16px;\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n cursor: pointer;\n opacity: 0;\n font-size: 18px;\n line-height: 1;\n}\n\n.tab:hover .tab-close {\n opacity: 0.6;\n}\n\n.tab-close:hover {\n opacity: 1;\n}\n\n.tab-rename {\n background: var(--tab-active-bg);\n border: 1px solid var(--tab-border);\n border-radius: 4px;\n color: var(--tab-active-color);\n font-size: 15px;\n font-family: system-ui, sans-serif;\n padding: 0 4px;\n outline: none;\n}\n\n.tab-add {\n padding: 0 10px;\n background: transparent;\n border: none;\n color: var(--tab-color);\n cursor: pointer;\n font-size: 25px;\n line-height: 1;\n}\n\n.tab-add:hover {\n color: var(--tab-active-color);\n}\n\n.editor-container {\n flex: 1;\n min-height: 0;\n width: 100%;\n padding: var(--editor-padding, 16px 0 0);\n}\n\n.cm-editor {\n height: 100%;\n font-size: var(--editor-font-size, 14px);\n}\n\n.cm-scroller {\n overflow: auto;\n}\n\n.snackbar {\n position: absolute;\n bottom: 16px;\n left: 16px;\n background: #e8e8e8;\n color: #333;\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 14px;\n font-family: system-ui, sans-serif;\n line-height: 1.4;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.2s;\n z-index: 10;\n}\n\n:host(.dark) .snackbar {\n background: #3a3a3a;\n color: #e0e0e0;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);\n}\n\n.snackbar.visible {\n opacity: 1;\n}\n";
27885
27545
  //#endregion
@@ -27918,7 +27578,8 @@ var WgslEdit = class extends HTMLElement {
27918
27578
  "lint-from",
27919
27579
  "line-numbers",
27920
27580
  "fetch-libs",
27921
- "gpu-lint"
27581
+ "gpu-lint",
27582
+ "autosave"
27922
27583
  ];
27923
27584
  editorView = null;
27924
27585
  editorContainer;
@@ -27947,6 +27608,10 @@ var WgslEdit = class extends HTMLElement {
27947
27608
  _fetchingPkgs = /* @__PURE__ */ new Set();
27948
27609
  _fetchedPkgs = /* @__PURE__ */ new Set();
27949
27610
  _snackTimer;
27611
+ _devSaveListener = null;
27612
+ _saveTimer;
27613
+ _dirtyFiles = /* @__PURE__ */ new Set();
27614
+ _switchingFile = false;
27950
27615
  _externalDiagnostics = [];
27951
27616
  _lintFromEl = null;
27952
27617
  /** Bound listeners for lint-from element's compile events. */
@@ -27970,10 +27635,13 @@ var WgslEdit = class extends HTMLElement {
27970
27635
  connectedCallback() {
27971
27636
  this.initEditor();
27972
27637
  this.loadInitialContent();
27973
- upgradeProperty(this, "conditions");
27974
- upgradeProperty(this, "source");
27975
- upgradeProperty(this, "sources");
27976
- upgradeProperty(this, "project");
27638
+ for (const p of [
27639
+ "conditions",
27640
+ "source",
27641
+ "sources",
27642
+ "project",
27643
+ "autosave"
27644
+ ]) upgradeProperty(this, p);
27977
27645
  }
27978
27646
  disconnectedCallback() {
27979
27647
  this.connectLintSource(null);
@@ -27981,25 +27649,43 @@ var WgslEdit = class extends HTMLElement {
27981
27649
  this.editorView = null;
27982
27650
  }
27983
27651
  attributeChangedCallback(name, _old, value) {
27984
- if (name === "src" && value && this.editorView) this.loadFromUrl(value);
27985
- else if (name === "readonly") this.updateReadonly();
27986
- else if (name === "theme") this.theme = value || "auto";
27987
- else if (name === "tabs") {
27988
- this._tabs = value !== "false";
27989
- this.renderTabs();
27990
- } else if (name === "lint") {
27991
- this._lint = value || "on";
27992
- this.updateLint();
27993
- } else if (name === "lint-from") this.connectLintSource(value);
27994
- else if (name === "line-numbers") {
27995
- this._lineNumbers = value === "true";
27996
- this.updateLineNumbers();
27997
- } else if (name === "fetch-libs") {
27998
- this._fetchLibs = value !== "false";
27999
- this.updateLint();
28000
- } else if (name === "gpu-lint") {
28001
- this._gpuLint = value !== "off";
28002
- this.updateLint();
27652
+ switch (name) {
27653
+ case "src":
27654
+ if (value && this.editorView) this.loadFromUrl(value);
27655
+ break;
27656
+ case "readonly":
27657
+ this.updateReadonly();
27658
+ break;
27659
+ case "theme":
27660
+ this.theme = value || "auto";
27661
+ break;
27662
+ case "tabs":
27663
+ this._tabs = value !== "false";
27664
+ this.renderTabs();
27665
+ break;
27666
+ case "lint":
27667
+ this._lint = value || "on";
27668
+ this.updateLint();
27669
+ break;
27670
+ case "lint-from":
27671
+ this.connectLintSource(value);
27672
+ break;
27673
+ case "line-numbers":
27674
+ this._lineNumbers = value === "true";
27675
+ this.updateLineNumbers();
27676
+ break;
27677
+ case "fetch-libs":
27678
+ this._fetchLibs = value !== "false";
27679
+ this.updateLint();
27680
+ break;
27681
+ case "gpu-lint":
27682
+ this._gpuLint = value !== "off";
27683
+ this.updateLint();
27684
+ break;
27685
+ case "autosave":
27686
+ if (value !== null && value !== "false") this.enableDevSave();
27687
+ else this.disableDevSave();
27688
+ break;
28003
27689
  }
28004
27690
  }
28005
27691
  /** Conditions for conditional compilation (@if/@elif/@else). */
@@ -28011,6 +27697,10 @@ var WgslEdit = class extends HTMLElement {
28011
27697
  this.updateLint();
28012
27698
  this.dispatchChange();
28013
27699
  }
27700
+ /** Reconfigure a compartment via the active editor view (no-op if unmounted). */
27701
+ reconfigure(comp, ext) {
27702
+ this.editorView?.dispatch({ effects: comp.reconfigure(ext) });
27703
+ }
28014
27704
  /** Active file content (single-file API). */
28015
27705
  get source() {
28016
27706
  return this.editorView?.state.doc.toString() ?? this._pendingSource ?? "";
@@ -28018,27 +27708,30 @@ var WgslEdit = class extends HTMLElement {
28018
27708
  /** Set active file content (single-file API). Auto-creates a default file entry. */
28019
27709
  set source(value) {
28020
27710
  if (!this._activeFile && this._files.size === 0) {
28021
- this._files.set("main.wesl", { doc: Text.of(value.split("\n")) });
27711
+ this._files.set("main.wesl", { doc: toDoc(value) });
28022
27712
  this._activeFile = "main.wesl";
28023
27713
  this.renderTabs();
28024
27714
  }
28025
27715
  if (this.editorView) {
28026
- const to = this.editorView.state.doc.length;
28027
- this.editorView.dispatch({ changes: {
28028
- from: 0,
28029
- to,
28030
- insert: value
28031
- } });
27716
+ const docLength = this.editorView.state.doc.length;
27717
+ this.editorView.dispatch({
27718
+ changes: {
27719
+ from: 0,
27720
+ to: docLength,
27721
+ insert: value
27722
+ },
27723
+ annotations: Transaction.addToHistory.of(false)
27724
+ });
28032
27725
  } else {
28033
27726
  this._pendingSource = value;
28034
27727
  const entry = this._files.get(this._activeFile);
28035
- if (entry) entry.doc = Text.of(value.split("\n"));
27728
+ if (entry) entry.doc = toDoc(value);
28036
27729
  }
28037
27730
  }
28038
27731
  /** All file contents keyed by module path (e.g., "package::main"). */
28039
27732
  get sources() {
28040
27733
  this.saveCurrentFileState();
28041
- const pkg = this._packageName ?? "package";
27734
+ const pkg = this.pkgName();
28042
27735
  const result = {};
28043
27736
  for (const [tabName, state] of this._files) result[fileToModulePath(tabName, pkg, false)] = state.doc.toString();
28044
27737
  return result;
@@ -28048,12 +27741,13 @@ var WgslEdit = class extends HTMLElement {
28048
27741
  this._files.clear();
28049
27742
  for (const [key, content] of Object.entries(value)) {
28050
27743
  const tabName = toTabName(key);
28051
- this._files.set(tabName, { doc: Text.of(content.split("\n")) });
27744
+ this._files.set(tabName, { doc: toDoc(content) });
28052
27745
  }
28053
27746
  const firstKey = Object.keys(value)[0];
28054
27747
  if (firstKey) this.switchToFile(toTabName(firstKey));
28055
27748
  this.renderTabs();
28056
27749
  }
27750
+ /** Snapshot of all editor state needed to link: sources, conditions, libs, root module. */
28057
27751
  get project() {
28058
27752
  return {
28059
27753
  weslSrc: this.sources,
@@ -28061,20 +27755,22 @@ var WgslEdit = class extends HTMLElement {
28061
27755
  conditions: this._conditions,
28062
27756
  constants: this._constants,
28063
27757
  libs: this._libs,
28064
- packageName: this._packageName
27758
+ packageName: this._packageName,
27759
+ shaderRoot: this.shaderRoot ?? void 0
28065
27760
  };
28066
27761
  }
28067
27762
  set project(value) {
28068
27763
  const { weslSrc, rootModuleName, conditions } = value;
28069
- const { constants, packageName, libs } = value;
27764
+ const { constants, packageName, libs, shaderRoot } = value;
28070
27765
  if (conditions !== void 0) this._conditions = conditions;
28071
27766
  if (constants !== void 0) this._constants = constants;
28072
27767
  if (packageName !== void 0) this._packageName = packageName;
28073
27768
  if (libs !== void 0) this._libs = libs;
28074
27769
  if (rootModuleName !== void 0) this._rootModuleName = rootModuleName;
27770
+ if (shaderRoot !== void 0 && !this.hasAttribute("shader-root")) this.shaderRoot = shaderRoot;
28075
27771
  if (weslSrc) {
28076
27772
  this.sources = weslSrc;
28077
- const tab = toTabName(rootModuleName ?? this._rootModuleName ?? "");
27773
+ const tab = toTabName(this._rootModuleName ?? "");
28078
27774
  if (tab) this.activeFile = tab;
28079
27775
  }
28080
27776
  this.updateLint();
@@ -28087,14 +27783,13 @@ var WgslEdit = class extends HTMLElement {
28087
27783
  })).dest;
28088
27784
  }
28089
27785
  linkParams() {
28090
- const pkg = this._packageName ?? "package";
28091
27786
  return {
28092
27787
  weslSrc: this.sources,
28093
- rootModuleName: this._rootModuleName ?? fileToModulePath(this._activeFile, pkg, false),
27788
+ rootModuleName: this._rootModuleName ?? this.activeModulePath(),
28094
27789
  conditions: this._conditions,
28095
27790
  constants: this._constants,
28096
27791
  libs: this._libs,
28097
- packageName: pkg
27792
+ packageName: this.pkgName()
28098
27793
  };
28099
27794
  }
28100
27795
  get libs() {
@@ -28150,6 +27845,20 @@ var WgslEdit = class extends HTMLElement {
28150
27845
  if (!value) this.setAttribute("gpu-lint", "off");
28151
27846
  else this.removeAttribute("gpu-lint");
28152
27847
  }
27848
+ /** Persist edits to disk via the dev-server save endpoint.
27849
+ * Requires `wgslEditAutosave()` to be installed in vite.config.ts. */
27850
+ get autosave() {
27851
+ return this._devSaveListener !== null;
27852
+ }
27853
+ set autosave(value) {
27854
+ if (value) {
27855
+ this.enableDevSave();
27856
+ this.setAttribute("autosave", "");
27857
+ } else {
27858
+ this.disableDevSave();
27859
+ this.removeAttribute("autosave");
27860
+ }
27861
+ }
28153
27862
  /** Whether to auto-fetch missing library packages from npm (default: true). */
28154
27863
  get fetchLibs() {
28155
27864
  return this._fetchLibs;
@@ -28199,13 +27908,15 @@ var WgslEdit = class extends HTMLElement {
28199
27908
  if (value) this.setAttribute("shader-root", value);
28200
27909
  else this.removeAttribute("shader-root");
28201
27910
  }
27911
+ /** Add a new file and switch to it. No-op if `name` already exists. */
28202
27912
  addFile(name, content = "") {
28203
27913
  if (this._files.has(name)) return;
28204
- this._files.set(name, { doc: Text.of(content.split("\n")) });
27914
+ this._files.set(name, { doc: toDoc(content) });
28205
27915
  this.switchToFile(name);
28206
27916
  this.renderTabs();
28207
27917
  this.dispatchFileChange("add", name);
28208
27918
  }
27919
+ /** Remove a file. No-op if it is missing or is the last remaining file. */
28209
27920
  removeFile(name) {
28210
27921
  if (!this._files.has(name) || this._files.size <= 1) return;
28211
27922
  this._files.delete(name);
@@ -28216,6 +27927,7 @@ var WgslEdit = class extends HTMLElement {
28216
27927
  this.renderTabs();
28217
27928
  this.dispatchFileChange("remove", name);
28218
27929
  }
27930
+ /** Rename a file, preserving its document and editor state. No-op on collision. */
28219
27931
  renameFile(oldName, newName) {
28220
27932
  const state = this._files.get(oldName);
28221
27933
  if (!state || this._files.has(newName)) return;
@@ -28231,28 +27943,36 @@ var WgslEdit = class extends HTMLElement {
28231
27943
  this.saveCurrentFileState();
28232
27944
  this._activeFile = name;
28233
27945
  const fileState = this._files.get(name);
28234
- if (this.editorView) {
28235
- const changes = {
28236
- from: 0,
28237
- to: this.editorView.state.doc.length,
28238
- insert: fileState.doc.toString()
28239
- };
28240
- const effects = EditorView.scrollIntoView(fileState.scrollPos ?? 0);
28241
- this.editorView.dispatch({
28242
- changes,
28243
- selection: fileState.selection,
28244
- effects
28245
- });
27946
+ const view = this.editorView;
27947
+ if (view) {
27948
+ const state = fileState.state ?? this.createFileState(fileState.doc);
27949
+ fileState.state = state;
27950
+ view.setState(state);
27951
+ if (fileState.scrollPos != null) view.scrollDOM.scrollTop = fileState.scrollPos;
28246
27952
  }
28247
27953
  this.renderTabs();
28248
27954
  }
27955
+ /** Build a fresh EditorState seeded with `doc` and the current extension set. */
27956
+ createFileState(doc) {
27957
+ return EditorState.create({
27958
+ doc,
27959
+ extensions: this.buildExtensions()
27960
+ });
27961
+ }
28249
27962
  saveCurrentFileState() {
28250
27963
  const view = this.editorView;
28251
- const state = this._activeFile ? this._files.get(this._activeFile) : void 0;
28252
- if (!view || !state) return;
28253
- state.doc = view.state.doc;
28254
- state.selection = view.state.selection;
28255
- state.scrollPos = view.scrollDOM.scrollTop;
27964
+ const fileState = this._activeFile ? this._files.get(this._activeFile) : void 0;
27965
+ if (!view || !fileState) return;
27966
+ fileState.state = view.state;
27967
+ fileState.doc = view.state.doc;
27968
+ fileState.selection = view.state.selection;
27969
+ fileState.scrollPos = view.scrollDOM.scrollTop;
27970
+ }
27971
+ pkgName() {
27972
+ return this._packageName ?? "package";
27973
+ }
27974
+ activeModulePath() {
27975
+ return fileToModulePath(this._activeFile, this.pkgName(), false);
28256
27976
  }
28257
27977
  dispatchChange() {
28258
27978
  this.dispatchEvent(new CustomEvent("change", { detail: this.project }));
@@ -28264,25 +27984,86 @@ var WgslEdit = class extends HTMLElement {
28264
27984
  };
28265
27985
  this.dispatchEvent(new CustomEvent("file-change", { detail }));
28266
27986
  }
27987
+ scheduleAutosave() {
27988
+ this._dirtyFiles.add(this._activeFile);
27989
+ clearTimeout(this._saveTimer);
27990
+ this._saveTimer = setTimeout(() => this.fireAutosave(), 500);
27991
+ }
27992
+ /** Dispatch an `autosave` event with a fresh project snapshot and the dirty-file list. */
27993
+ fireAutosave() {
27994
+ const dirty = [...this._dirtyFiles];
27995
+ this._dirtyFiles.clear();
27996
+ const detail = {
27997
+ project: this.project,
27998
+ dirty
27999
+ };
28000
+ this.dispatchEvent(new CustomEvent("autosave", { detail }));
28001
+ }
28002
+ enableDevSave() {
28003
+ if (this._devSaveListener) return;
28004
+ this._devSaveListener = (e) => this.devSave(e);
28005
+ this.addEventListener("autosave", this._devSaveListener);
28006
+ }
28007
+ disableDevSave() {
28008
+ if (!this._devSaveListener) return;
28009
+ this.removeEventListener("autosave", this._devSaveListener);
28010
+ this._devSaveListener = null;
28011
+ }
28012
+ /** Built-in `autosave` listener: POST each dirty file to the dev-server save endpoint. */
28013
+ async devSave(e) {
28014
+ const root = this.shaderRoot;
28015
+ if (!root) return;
28016
+ const { project, dirty } = e.detail;
28017
+ const weslSrc = project.weslSrc;
28018
+ if (!weslSrc) return;
28019
+ const pkg = this.pkgName();
28020
+ for (const file of dirty) {
28021
+ const content = weslSrc[fileToModulePath(file, pkg, false)];
28022
+ if (content === void 0) continue;
28023
+ try {
28024
+ const res = await fetch(saveEndpoint, {
28025
+ method: "POST",
28026
+ headers: { "Content-Type": "application/json" },
28027
+ body: JSON.stringify({
28028
+ root,
28029
+ file,
28030
+ content
28031
+ })
28032
+ });
28033
+ if (!res.ok) return this.devSaveFailed(`HTTP ${res.status}`);
28034
+ } catch (err) {
28035
+ return this.devSaveFailed(err instanceof Error ? err.message : String(err));
28036
+ }
28037
+ }
28038
+ }
28039
+ /** One-shot disable on first failure: avoids spamming the console on every keystroke. */
28040
+ devSaveFailed(reason) {
28041
+ this.disableDevSave();
28042
+ console.error(`wgsl-edit: autosave disabled (${reason}). Make sure wgslEditAutosave() is installed in vite.config.ts.`);
28043
+ }
28044
+ /** First-time setup: read attributes, parse inline content, mount the EditorView. */
28267
28045
  initEditor() {
28268
28046
  this.readInitialAttributes();
28269
28047
  this.parseInlineContent();
28270
28048
  this._mediaQuery = matchMedia("(prefers-color-scheme: dark)");
28271
28049
  this._mediaQuery.addEventListener("change", () => this.updateTheme());
28272
28050
  const firstFile = this._files.keys().next().value;
28273
- const initialDoc = this._pendingSource ?? (firstFile ? this._files.get(firstFile).doc.toString() : "");
28051
+ const firstDoc = firstFile && this._files.get(firstFile).doc.toString();
28052
+ const initialDoc = this._pendingSource ?? firstDoc ?? "";
28274
28053
  this._pendingSource = null;
28275
28054
  if (firstFile) this._activeFile = firstFile;
28276
28055
  else if (initialDoc) {
28277
- this._files.set("main.wesl", { doc: Text.of(initialDoc.split("\n")) });
28056
+ this._files.set("main.wesl", { doc: toDoc(initialDoc) });
28278
28057
  this._activeFile = "main.wesl";
28279
28058
  }
28280
- const extensions = this.buildExtensions();
28059
+ const state = this.createFileState(initialDoc);
28060
+ const active = this._activeFile ? this._files.get(this._activeFile) : null;
28061
+ if (active) {
28062
+ active.state = state;
28063
+ active.doc = state.doc;
28064
+ }
28281
28065
  this.editorView = new EditorView({
28282
- state: EditorState.create({
28283
- doc: initialDoc,
28284
- extensions
28285
- }),
28066
+ state,
28286
28067
  parent: this.editorContainer
28287
28068
  });
28288
28069
  this.renderTabs();
@@ -28299,6 +28080,7 @@ var WgslEdit = class extends HTMLElement {
28299
28080
  const lintFromAttr = this.getAttribute("lint-from");
28300
28081
  if (lintFromAttr) this.connectLintSource(lintFromAttr);
28301
28082
  }
28083
+ /** Assemble the full CodeMirror extension set for a fresh EditorState. */
28302
28084
  buildExtensions() {
28303
28085
  const baseTheme = EditorView.theme({
28304
28086
  ".cm-content": { padding: "0" },
@@ -28343,6 +28125,7 @@ var WgslEdit = class extends HTMLElement {
28343
28125
  this._externalDiagnostics = [];
28344
28126
  this.saveCurrentFileState();
28345
28127
  this.dispatchChange();
28128
+ if (!this._switchingFile) this.scheduleAutosave();
28346
28129
  }
28347
28130
  })
28348
28131
  ];
@@ -28353,11 +28136,11 @@ var WgslEdit = class extends HTMLElement {
28353
28136
  return [EditorView.theme({}, { dark: isDark }), isDark ? darkColors : lightColors];
28354
28137
  }
28355
28138
  updateTheme() {
28356
- this.editorView?.dispatch({ effects: this.themeCompartment.reconfigure(this.resolveTheme()) });
28139
+ this.reconfigure(this.themeCompartment, this.resolveTheme());
28357
28140
  }
28358
28141
  updateReadonly() {
28359
28142
  const ext = EditorState.readOnly.of(this.readonly);
28360
- this.editorView?.dispatch({ effects: this.readonlyCompartment.reconfigure(ext) });
28143
+ this.reconfigure(this.readonlyCompartment, ext);
28361
28144
  this.renderTabs();
28362
28145
  }
28363
28146
  resolveLint() {
@@ -28365,7 +28148,7 @@ var WgslEdit = class extends HTMLElement {
28365
28148
  const useGpuLint = this._gpuLint && !this._lintFromEl;
28366
28149
  return createWeslLinter({
28367
28150
  getSources: () => this.sources,
28368
- rootModule: () => fileToModulePath(this._activeFile, this._packageName ?? "package", false),
28151
+ rootModule: () => this.activeModulePath(),
28369
28152
  conditions: () => this._conditions,
28370
28153
  packageName: () => this._packageName,
28371
28154
  getExternalDiagnostics: () => this._externalDiagnostics,
@@ -28378,11 +28161,8 @@ var WgslEdit = class extends HTMLElement {
28378
28161
  /** Link WESL->WGSL and validate via WebGPU, returning CodeMirror diagnostics. */
28379
28162
  async gpuValidate() {
28380
28163
  const { validateWgsl } = await import("./GpuValidator-D88-VTq9.js");
28381
- const params = this.linkParams();
28382
- const linked = await link(params);
28383
- const messages = await validateWgsl(linked.dest);
28384
- const pkg = params.packageName ?? "package";
28385
- return mapGpuDiagnostics(messages, linked, this._activeFile, pkg);
28164
+ const linked = await link(this.linkParams());
28165
+ return mapGpuDiagnostics(await validateWgsl(linked.dest), linked, this._activeFile, this.pkgName());
28386
28166
  }
28387
28167
  /** Fetch missing library packages, deduplicating in-flight requests. */
28388
28168
  async fetchLibsOnDemand(names) {
@@ -28409,14 +28189,15 @@ var WgslEdit = class extends HTMLElement {
28409
28189
  }
28410
28190
  }
28411
28191
  updateLint() {
28412
- this.editorView?.dispatch({ effects: this.lintCompartment.reconfigure(this.resolveLint()) });
28192
+ this.reconfigure(this.lintCompartment, this.resolveLint());
28413
28193
  }
28414
28194
  /** Listen for compile-error/compile-success events from a lint source element. */
28415
28195
  connectLintSource(id) {
28416
28196
  const hadExternal = !!this._lintFromEl;
28417
- if (this._lintFromEl) {
28418
- this._lintFromEl.removeEventListener("compile-error", this._boundCompileError);
28419
- this._lintFromEl.removeEventListener("compile-success", this._boundCompileSuccess);
28197
+ const prev = this._lintFromEl;
28198
+ if (prev) {
28199
+ prev.removeEventListener("compile-error", this._boundCompileError);
28200
+ prev.removeEventListener("compile-success", this._boundCompileSuccess);
28420
28201
  this._lintFromEl = null;
28421
28202
  }
28422
28203
  this._externalDiagnostics = [];
@@ -28428,23 +28209,24 @@ var WgslEdit = class extends HTMLElement {
28428
28209
  }
28429
28210
  if (hadExternal !== !!this._lintFromEl) this.updateLint();
28430
28211
  }
28212
+ /** Convert external compile-error locations into CodeMirror diagnostics for the active file. */
28431
28213
  onCompileError(e) {
28432
28214
  const detail = e.detail;
28433
28215
  if (!this.editorView || detail.source === "wesl") return;
28434
28216
  const doc = this.editorView.state.doc;
28435
- const pkg = this._packageName ?? "package";
28436
- const activeModule = fileToModulePath(this._activeFile, pkg, false);
28437
- this._externalDiagnostics = detail.locations.filter((loc) => {
28438
- if (!loc.file) return true;
28439
- return fileToModulePath(loc.file, pkg, false) === activeModule;
28440
- }).map((loc) => {
28217
+ const pkg = this.pkgName();
28218
+ const activeModule = this.activeModulePath();
28219
+ const inActiveFile = (loc) => !loc.file || fileToModulePath(loc.file, pkg, false) === activeModule;
28220
+ this._externalDiagnostics = detail.locations.filter(inActiveFile).map((loc) => {
28441
28221
  const line = doc.line(Math.max(1, Math.min(loc.line, doc.lines)));
28442
28222
  const from = Math.min(line.from + (loc.column ?? 0), doc.length);
28223
+ const to = Math.min(from + (loc.length ?? 1), doc.length);
28224
+ const { severity, message } = loc;
28443
28225
  return {
28444
28226
  from,
28445
- to: Math.min(from + (loc.length ?? 1), doc.length),
28446
- severity: loc.severity,
28447
- message: loc.message,
28227
+ to,
28228
+ severity,
28229
+ message,
28448
28230
  source: "WebGPU"
28449
28231
  };
28450
28232
  });
@@ -28459,21 +28241,20 @@ var WgslEdit = class extends HTMLElement {
28459
28241
  return this._lineNumbers ? [] : EditorView.theme({ ".cm-gutters": { display: "none" } });
28460
28242
  }
28461
28243
  updateLineNumbers() {
28462
- const ext = this.resolveLineNumbers();
28463
- this.editorView?.dispatch({ effects: this.lineNumbersCompartment.reconfigure(ext) });
28244
+ this.reconfigure(this.lineNumbersCompartment, this.resolveLineNumbers());
28464
28245
  }
28465
28246
  /** Parse script tags into _files. Supports single or multi-file via data-name. */
28466
28247
  parseInlineContent() {
28467
28248
  const scripts = Array.from(this.querySelectorAll("script[type=\"text/wgsl\"], script[type=\"text/wesl\"]"));
28468
28249
  if (scripts.length === 0) {
28469
28250
  const content = this.textContent?.trim() ?? "";
28470
- if (content) this._files.set("main.wesl", { doc: Text.of(content.split("\n")) });
28251
+ if (content) this._files.set("main.wesl", { doc: toDoc(content) });
28471
28252
  return;
28472
28253
  }
28473
28254
  for (const script of scripts) {
28474
28255
  const name = script.getAttribute("data-name") || "main.wesl";
28475
28256
  const content = script.textContent?.trim() ?? "";
28476
- this._files.set(name, { doc: Text.of(content.split("\n")) });
28257
+ this._files.set(name, { doc: toDoc(content) });
28477
28258
  }
28478
28259
  }
28479
28260
  renderTabs() {
@@ -28518,28 +28299,27 @@ var WgslEdit = class extends HTMLElement {
28518
28299
  });
28519
28300
  return btn;
28520
28301
  }
28302
+ /** Replace the tab name span with an editable input; commit on Enter/blur, cancel on Escape. */
28521
28303
  startRenameTab(tab, nameSpan, oldName) {
28522
28304
  const input = document.createElement("input");
28523
28305
  input.className = "tab-rename";
28524
28306
  input.value = oldName;
28525
28307
  input.size = Math.max(oldName.length, 8);
28308
+ const cancelRename = () => {
28309
+ nameSpan.style.display = "";
28310
+ input.remove();
28311
+ };
28526
28312
  const finishRename = () => {
28527
28313
  const newName = input.value.trim() || oldName;
28528
28314
  if (newName !== oldName && !this._files.has(newName)) this.renameFile(oldName, newName);
28529
- else {
28530
- nameSpan.style.display = "";
28531
- input.remove();
28532
- }
28315
+ else cancelRename();
28533
28316
  };
28534
28317
  input.addEventListener("keydown", (e) => {
28535
28318
  if (e.key === "Enter") {
28536
28319
  e.preventDefault();
28537
28320
  finishRename();
28538
28321
  }
28539
- if (e.key === "Escape") {
28540
- nameSpan.style.display = "";
28541
- input.remove();
28542
- }
28322
+ if (e.key === "Escape") cancelRename();
28543
28323
  });
28544
28324
  input.addEventListener("blur", finishRename);
28545
28325
  input.addEventListener("input", () => {
@@ -28567,6 +28347,7 @@ var WgslEdit = class extends HTMLElement {
28567
28347
  if (src) this.loadFromUrl(src);
28568
28348
  }
28569
28349
  };
28350
+ /** Build a CodeMirror highlight style from a WESL color palette. */
28570
28351
  function weslColors(c) {
28571
28352
  return syntaxHighlighting(HighlightStyle.define([
28572
28353
  {
@@ -28634,24 +28415,7 @@ function weslColors(c) {
28634
28415
  fontStyle: "normal"
28635
28416
  } }));
28636
28417
  }
28637
- /** Map GPU validation messages back to source positions via the source map. */
28638
- function mapGpuDiagnostics(messages, linked, activeFile, pkg) {
28639
- const { sourceMap } = linked;
28640
- const active = fileToModulePath(activeFile, pkg, false);
28641
- return messages.flatMap((msg) => {
28642
- const srcPos = sourceMap.destToSrc(msg.offset);
28643
- if ((srcPos.src.path ? fileToModulePath(srcPos.src.path, pkg, false) : null) !== active) return [];
28644
- const endPos = sourceMap.destToSrc(msg.offset + msg.length);
28645
- const from = srcPos.position;
28646
- return {
28647
- from,
28648
- to: endPos.position > from ? endPos.position : from + 1,
28649
- severity: msg.severity,
28650
- message: msg.message,
28651
- source: "WebGPU"
28652
- };
28653
- });
28654
- }
28418
+ /** Lazily build and cache the shared component stylesheet. */
28655
28419
  function getStyles() {
28656
28420
  if (!cachedStyleSheet) {
28657
28421
  cachedStyleSheet = new CSSStyleSheet();
@@ -28659,7 +28423,8 @@ function getStyles() {
28659
28423
  }
28660
28424
  return cachedStyleSheet;
28661
28425
  }
28662
- /** Absorb instance properties set before custom element upgrade. */
28426
+ /** Absorb instance properties set before custom element upgrade.
28427
+ * Duplicated in WgslPlay.ts. Later, extract to a shared package. */
28663
28428
  function upgradeProperty(el, prop) {
28664
28429
  if (Object.hasOwn(el, prop)) {
28665
28430
  const value = el[prop];
@@ -28667,11 +28432,36 @@ function upgradeProperty(el, prop) {
28667
28432
  el[prop] = value;
28668
28433
  }
28669
28434
  }
28435
+ /** Build a CodeMirror Text doc from a string. */
28436
+ function toDoc(s) {
28437
+ return Text.of(s.split("\n"));
28438
+ }
28670
28439
  /** Convert a module path or file path to a tab name: "package::main" -> "main", "main.wesl" -> "main.wesl" */
28671
28440
  function toTabName(key) {
28672
28441
  if (key.includes("::")) return key.replace(/^[^:]+::/, "").replaceAll("::", "/");
28673
28442
  return key.replace(/^\.\//, "");
28674
28443
  }
28444
+ /** Map GPU validation messages back to source positions via the source map. */
28445
+ function mapGpuDiagnostics(messages, linked, activeFile, pkg) {
28446
+ const { sourceMap } = linked;
28447
+ const active = fileToModulePath(activeFile, pkg, false);
28448
+ return messages.flatMap((msg) => {
28449
+ const srcPos = sourceMap.destToSrc(msg.offset);
28450
+ const path = srcPos.src.path;
28451
+ if ((path ? fileToModulePath(path, pkg, false) : null) !== active) return [];
28452
+ const endPos = sourceMap.destToSrc(msg.offset + msg.length);
28453
+ const from = srcPos.position;
28454
+ const to = endPos.position > from ? endPos.position : from + 1;
28455
+ const { severity, message } = msg;
28456
+ return {
28457
+ from,
28458
+ to,
28459
+ severity,
28460
+ message,
28461
+ source: "WebGPU"
28462
+ };
28463
+ });
28464
+ }
28675
28465
  //#endregion
28676
28466
  //#region src/index.ts
28677
28467
  if (!customElements.get("wgsl-edit")) customElements.define("wgsl-edit", WgslEdit);