@energy8platform/golem 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +18 -0
  2. package/dist/editor.css +3 -4
  3. package/dist/editor.js +177 -75
  4. package/dist/lib/cli.js +302 -126
  5. package/dist/lib/cli.js.map +4 -4
  6. package/dist/lib/e8/agent.d.ts +35 -0
  7. package/dist/lib/e8/host.d.ts +22 -0
  8. package/dist/lib/e8/node.d.ts +66 -0
  9. package/dist/lib/e8/runtime.d.ts +10 -0
  10. package/dist/lib/e8/schema.d.ts +3 -0
  11. package/dist/lib/e8/types.d.ts +80 -0
  12. package/dist/lib/e8-agent.js +6781 -0
  13. package/dist/lib/e8-agent.js.map +7 -0
  14. package/dist/lib/e8-client.js +116 -0
  15. package/dist/lib/e8-host.js +6929 -0
  16. package/dist/lib/e8-host.js.map +7 -0
  17. package/dist/lib/e8-runtime.js +2024 -0
  18. package/dist/lib/e8-runtime.js.map +7 -0
  19. package/dist/lib/e8-schema.js +7 -0
  20. package/dist/lib/e8-schema.js.map +7 -0
  21. package/dist/lib/editor/api.d.ts +1 -0
  22. package/dist/lib/editor/embed.d.ts +9 -0
  23. package/dist/lib/editor/server.d.ts +23 -1
  24. package/dist/lib/editor/store.d.ts +107 -0
  25. package/dist/lib/editor/timeline.d.ts +15 -0
  26. package/dist/lib/editor-entry.d.ts +1 -1
  27. package/dist/lib/editor-entry.js +303 -126
  28. package/dist/lib/editor-entry.js.map +4 -4
  29. package/dist/lib/harness.js +69 -14
  30. package/dist/lib/interactive-editor.js.map +1 -1
  31. package/dist/lib/rig-anim.d.ts +2 -0
  32. package/dist/lib/rig-control-qa.d.ts +27 -0
  33. package/dist/lib/rig-format.d.ts +989 -0
  34. package/dist/lib/rig-import-layers.d.ts +2 -0
  35. package/dist/lib/rig-version.d.ts +12 -0
  36. package/dist/lib/runtime.js +69 -14
  37. package/dist/lib/runtime.js.map +3 -3
  38. package/dist/lib/spine-import.d.ts +4 -0
  39. package/dist/lib/tool-schema.d.ts +3 -0
  40. package/dist/lib/tools.d.ts +2 -0
  41. package/dist/lib/tools.js +257 -73
  42. package/dist/lib/tools.js.map +4 -4
  43. package/editor.html +2 -2
  44. package/package.json +38 -3
  45. package/skills/e8-golem/SKILL.md +71 -0
  46. package/skills/golem-symbol-animation/SKILL.md +87 -0
  47. package/skills/golem-symbol-animation/agents/openai.yaml +4 -0
  48. package/skills/golem-symbol-animation/references/facial-controls.md +48 -0
  49. package/skills/golem-symbol-animation/references/game-engine-integration.md +133 -0
  50. package/skills/golem-symbol-animation/references/golem-authoring.md +138 -0
  51. package/skills/golem-symbol-animation/references/interactive-editor.md +17 -0
  52. package/skills/golem-symbol-animation/references/motion-craft.md +113 -0
  53. package/skills/golem-symbol-animation/references/packed-delivery.md +88 -0
  54. package/skills/golem-symbol-animation/references/quantitative-qa.md +106 -0
  55. package/skills/golem-symbol-animation/references/spine-rive-study.md +87 -0
  56. package/skills/golem-symbol-animation/scripts/atlas_parts.py +87 -0
  57. package/skills/golem-symbol-animation/scripts/check_package.py +71 -0
  58. package/skills/golem-symbol-animation/scripts/image_gates.py +143 -0
  59. package/skills/golem-symbol-cutting/SKILL.md +70 -0
  60. package/skills/golem-symbol-cutting/agents/openai.yaml +4 -0
  61. package/skills/golem-symbol-cutting/assets/h1/h1.atlas.png +0 -0
  62. package/skills/golem-symbol-cutting/assets/h1/h1.png +0 -0
  63. package/skills/golem-symbol-cutting/references/cutting-workflow.md +92 -0
  64. package/skills/golem-symbol-cutting/references/facial-layers.md +44 -0
  65. package/skills/golem-symbol-cutting/references/generation-workflow.md +109 -0
  66. package/skills/golem-symbol-cutting/references/golem-handoff.md +62 -0
  67. package/skills/golem-symbol-cutting/references/h1-example.md +46 -0
  68. package/skills/golem-symbol-cutting/references/hybrid-workflow.md +119 -0
  69. package/skills/golem-symbol-cutting/references/registration-and-motion.md +89 -0
  70. package/skills/golem-symbol-cutting/references/spine-rive-construction.md +73 -0
  71. package/skills/golem-symbol-cutting/scripts/atlas_parts.py +87 -0
  72. package/skills/golem-symbol-cutting/scripts/hybrid_parts.py +279 -0
package/README.md CHANGED
@@ -17,6 +17,10 @@
17
17
  поверх параметрической мимики и сохраняются при смене анимации.
18
18
  [Результаты объединения и проверок](docs/verification-2026-09-28.md).
19
19
 
20
+ В 0.6 по официальным примерам Spine и Rive обновлены скиллы нарезки и анимации, исправлены
21
+ импорт skins, linked meshes, кривые, преобразования и активные clipping-маски.
22
+ [Источники, результаты и ограничения проверки](docs/verification-2026-10-05.md).
23
+
20
24
  ```
21
25
  src/rig-format.ts схема документа (zod) + семантическая валидация; v1: deform-треки, asset.frame (атлас)
22
26
  src/rig-anim.ts чистая математика: easing, семплирование, Pose, аффинные матрицы костей, контуры слотов
@@ -196,6 +200,20 @@ for p in symbol_idle symbol_win dim; do npm run rig -- rig_apply_preset '{"path"
196
200
  npm run rig -- rig_render_preview '{"path":"'$S'","animation":"win","fps":10,"onionSkin":true,"scale":0.5,"saveTo":"examples/symbol/out/win.png"}'
197
201
  ```
198
202
 
203
+ ## Модуль e8-engine
204
+
205
+ golem — e8-row `golem1` (`@energy8platform/golem`). Что даёт: тулы `rig_*` в MCP e8, node `golem` в сцене, вкладка
206
+ редактора для `rig.json` в IDE, undo через историю golem. Риги лежат в `assets/rigs/<name>/`.
207
+
208
+ Node `golem` в игре, тулы `rig_*` и skills приходят со строкой `golem1` бандла `e8-base` (проект без него добавляет ту же
209
+ строку в `e8.patch.yml`). Строка в `e8.yml` добавляет вкладку редактора `rig.json` в IDE и мост undo:
210
+
211
+ ```yaml
212
+ - id: golem1
213
+ name: '@energy8platform/golem'
214
+ config: {}
215
+ ```
216
+
199
217
  ## Шаг 3 — слои (риск №1 закрыт)
200
218
 
201
219
  Спайк сегментации на `cashier.png` (артефакты удалены при чистке репозитория): вырезание из плоской картинки + bleed 6 px даёт чистый
package/dist/editor.css CHANGED
@@ -13,6 +13,8 @@
13
13
  --ctl-line:#3C3C3C;
14
14
  --err:#F14C4C;
15
15
  --warn:#CCA700;
16
+ --font:-apple-system,"Segoe UI",sans-serif;
17
+ --size:13px;
16
18
  --r-xs:2px;
17
19
  --r-s:4px;
18
20
  --r-m:6px;
@@ -26,10 +28,7 @@ body {
26
28
  height: 100%;
27
29
  background: var(--chrome);
28
30
  color: var(--fg);
29
- font:
30
- 13px -apple-system,
31
- "Segoe UI",
32
- sans-serif;
31
+ font: var(--size) var(--font);
33
32
  user-select: none;
34
33
  }
35
34
  #app {
package/dist/editor.js CHANGED
@@ -43308,7 +43308,7 @@ var require_gifenc = __commonJS({
43308
43308
  bins[nb.fw].bk = nb.bk;
43309
43309
  nb.mtm = bincountMinusOne;
43310
43310
  }
43311
- let palette = [];
43311
+ let palette2 = [];
43312
43312
  var k4 = 0;
43313
43313
  for (i6 = 0; ; ++k4) {
43314
43314
  let r5 = clamp3(Math.round(bins[i6].rc), 0, 255);
@@ -43327,17 +43327,17 @@ var require_gifenc = __commonJS({
43327
43327
  }
43328
43328
  }
43329
43329
  const color = hasAlpha ? [r5, g4, b4, a5] : [r5, g4, b4];
43330
- const exists = existsInPalette(palette, color);
43330
+ const exists = existsInPalette(palette2, color);
43331
43331
  if (!exists)
43332
- palette.push(color);
43332
+ palette2.push(color);
43333
43333
  if ((i6 = bins[i6].fw) == 0)
43334
43334
  break;
43335
43335
  }
43336
- return palette;
43336
+ return palette2;
43337
43337
  }
43338
- function existsInPalette(palette, color) {
43339
- for (let i6 = 0; i6 < palette.length; i6++) {
43340
- const p6 = palette[i6];
43338
+ function existsInPalette(palette2, color) {
43339
+ for (let i6 = 0; i6 < palette2.length; i6++) {
43340
+ const p6 = palette2[i6];
43341
43341
  let matchesRGB = p6[0] === color[0] && p6[1] === color[1] && p6[2] === color[2];
43342
43342
  let matchesAlpha = p6.length >= 4 && color.length >= 4 ? p6[3] === color[3] : true;
43343
43343
  if (matchesRGB && matchesAlpha)
@@ -43376,14 +43376,14 @@ var require_gifenc = __commonJS({
43376
43376
  data[i6] = a5 << 24 | b4 << 16 | g4 << 8 | r5 << 0;
43377
43377
  }
43378
43378
  }
43379
- function applyPalette2(rgba, palette, format = "rgb565") {
43379
+ function applyPalette2(rgba, palette2, format = "rgb565") {
43380
43380
  if (!rgba || !rgba.buffer) {
43381
43381
  throw new Error("quantize() expected RGBA Uint8Array data");
43382
43382
  }
43383
43383
  if (!(rgba instanceof Uint8Array) && !(rgba instanceof Uint8ClampedArray)) {
43384
43384
  throw new Error("quantize() expected RGBA Uint8Array data");
43385
43385
  }
43386
- if (palette.length > 256) {
43386
+ if (palette2.length > 256) {
43387
43387
  throw new Error("applyPalette() only works with 256 colors or less");
43388
43388
  }
43389
43389
  const data = new Uint32Array(rgba.buffer);
@@ -43400,7 +43400,7 @@ var require_gifenc = __commonJS({
43400
43400
  const g4 = color >> 8 & 255;
43401
43401
  const r5 = color & 255;
43402
43402
  const key = rgba8888_to_rgba4444(r5, g4, b4, a5);
43403
- const idx = key in cache ? cache[key] : cache[key] = nearestColorIndexRGBA(r5, g4, b4, a5, palette);
43403
+ const idx = key in cache ? cache[key] : cache[key] = nearestColorIndexRGBA(r5, g4, b4, a5, palette2);
43404
43404
  index[i6] = idx;
43405
43405
  }
43406
43406
  } else {
@@ -43411,17 +43411,17 @@ var require_gifenc = __commonJS({
43411
43411
  const g4 = color >> 8 & 255;
43412
43412
  const r5 = color & 255;
43413
43413
  const key = rgb888_to_key(r5, g4, b4);
43414
- const idx = key in cache ? cache[key] : cache[key] = nearestColorIndexRGB(r5, g4, b4, palette);
43414
+ const idx = key in cache ? cache[key] : cache[key] = nearestColorIndexRGB(r5, g4, b4, palette2);
43415
43415
  index[i6] = idx;
43416
43416
  }
43417
43417
  }
43418
43418
  return index;
43419
43419
  }
43420
- function nearestColorIndexRGBA(r5, g4, b4, a5, palette) {
43420
+ function nearestColorIndexRGBA(r5, g4, b4, a5, palette2) {
43421
43421
  let k4 = 0;
43422
43422
  let mindist = 1e100;
43423
- for (let i6 = 0; i6 < palette.length; i6++) {
43424
- const px2 = palette[i6];
43423
+ for (let i6 = 0; i6 < palette2.length; i6++) {
43424
+ const px2 = palette2[i6];
43425
43425
  const a22 = px2[3];
43426
43426
  let curdist = sqr2(a22 - a5);
43427
43427
  if (curdist > mindist)
@@ -43443,11 +43443,11 @@ var require_gifenc = __commonJS({
43443
43443
  }
43444
43444
  return k4;
43445
43445
  }
43446
- function nearestColorIndexRGB(r5, g4, b4, palette) {
43446
+ function nearestColorIndexRGB(r5, g4, b4, palette2) {
43447
43447
  let k4 = 0;
43448
43448
  let mindist = 1e100;
43449
- for (let i6 = 0; i6 < palette.length; i6++) {
43450
- const px2 = palette[i6];
43449
+ for (let i6 = 0; i6 < palette2.length; i6++) {
43450
+ const px2 = palette2[i6];
43451
43451
  const r22 = px2[0];
43452
43452
  let curdist = sqr2(r22 - r5);
43453
43453
  if (curdist > mindist)
@@ -43465,12 +43465,12 @@ var require_gifenc = __commonJS({
43465
43465
  }
43466
43466
  return k4;
43467
43467
  }
43468
- function snapColorsToPalette(palette, knownColors, threshold = 5) {
43469
- if (!palette.length || !knownColors.length)
43468
+ function snapColorsToPalette(palette2, knownColors, threshold = 5) {
43469
+ if (!palette2.length || !knownColors.length)
43470
43470
  return;
43471
- const paletteRGB = palette.map((p6) => p6.slice(0, 3));
43471
+ const paletteRGB = palette2.map((p6) => p6.slice(0, 3));
43472
43472
  const thresholdSq = threshold * threshold;
43473
- const dim2 = palette[0].length;
43473
+ const dim2 = palette2[0].length;
43474
43474
  for (let i6 = 0; i6 < knownColors.length; i6++) {
43475
43475
  let color = knownColors[i6];
43476
43476
  if (color.length < dim2) {
@@ -43484,7 +43484,7 @@ var require_gifenc = __commonJS({
43484
43484
  const idx = r5[0];
43485
43485
  const distanceSq = r5[1];
43486
43486
  if (distanceSq > 0 && distanceSq <= thresholdSq) {
43487
- palette[idx] = color;
43487
+ palette2[idx] = color;
43488
43488
  }
43489
43489
  }
43490
43490
  }
@@ -43554,7 +43554,7 @@ var require_gifenc = __commonJS({
43554
43554
  transparent = false,
43555
43555
  transparentIndex = 0,
43556
43556
  delay = 0,
43557
- palette = null,
43557
+ palette: palette2 = null,
43558
43558
  repeat = 0,
43559
43559
  colorDepth = 8,
43560
43560
  dispose = -1
@@ -43572,21 +43572,21 @@ var require_gifenc = __commonJS({
43572
43572
  width = Math.max(0, Math.floor(width));
43573
43573
  height = Math.max(0, Math.floor(height));
43574
43574
  if (first) {
43575
- if (!palette) {
43575
+ if (!palette2) {
43576
43576
  throw new Error("First frame must include a { palette } option");
43577
43577
  }
43578
- encodeLogicalScreenDescriptor(stream, width, height, palette, colorDepth);
43579
- encodeColorTable(stream, palette);
43578
+ encodeLogicalScreenDescriptor(stream, width, height, palette2, colorDepth);
43579
+ encodeColorTable(stream, palette2);
43580
43580
  if (repeat >= 0) {
43581
43581
  encodeNetscapeExt(stream, repeat);
43582
43582
  }
43583
43583
  }
43584
43584
  const delayTime = Math.round(delay / 10);
43585
43585
  encodeGraphicControlExt(stream, dispose, delayTime, transparent, transparentIndex);
43586
- const useLocalColorTable = Boolean(palette) && !first;
43587
- encodeImageDescriptor(stream, width, height, useLocalColorTable ? palette : null);
43586
+ const useLocalColorTable = Boolean(palette2) && !first;
43587
+ encodeImageDescriptor(stream, width, height, useLocalColorTable ? palette2 : null);
43588
43588
  if (useLocalColorTable)
43589
- encodeColorTable(stream, palette);
43589
+ encodeColorTable(stream, palette2);
43590
43590
  encodePixels(stream, index, width, height, colorDepth, accum, htab, codetab);
43591
43591
  }
43592
43592
  };
@@ -43620,10 +43620,10 @@ var require_gifenc = __commonJS({
43620
43620
  stream.writeByte(transparentIndex || 0);
43621
43621
  stream.writeByte(0);
43622
43622
  }
43623
- function encodeLogicalScreenDescriptor(stream, width, height, palette, colorDepth = 8) {
43623
+ function encodeLogicalScreenDescriptor(stream, width, height, palette2, colorDepth = 8) {
43624
43624
  const globalColorTableFlag = 1;
43625
43625
  const sortFlag = 0;
43626
- const globalColorTableSize = colorTableSize(palette.length) - 1;
43626
+ const globalColorTableSize = colorTableSize(palette2.length) - 1;
43627
43627
  const fields = globalColorTableFlag << 7 | colorDepth - 1 << 4 | sortFlag << 3 | globalColorTableSize;
43628
43628
  const backgroundColorIndex = 0;
43629
43629
  const pixelAspectRatio = 0;
@@ -43641,12 +43641,12 @@ var require_gifenc = __commonJS({
43641
43641
  writeUInt16(stream, repeat);
43642
43642
  stream.writeByte(0);
43643
43643
  }
43644
- function encodeColorTable(stream, palette) {
43645
- const colorTableLength = 1 << colorTableSize(palette.length);
43644
+ function encodeColorTable(stream, palette2) {
43645
+ const colorTableLength = 1 << colorTableSize(palette2.length);
43646
43646
  for (let i6 = 0; i6 < colorTableLength; i6++) {
43647
43647
  let color = [0, 0, 0];
43648
- if (i6 < palette.length) {
43649
- color = palette[i6];
43648
+ if (i6 < palette2.length) {
43649
+ color = palette2[i6];
43650
43650
  }
43651
43651
  stream.writeByte(color[0]);
43652
43652
  stream.writeByte(color[1]);
@@ -48869,7 +48869,9 @@ var PathAttachment = external_exports.object({
48869
48869
  /** curve lengths for constant-speed positioning */
48870
48870
  lengths: external_exports.array(external_exports.number()).default([]),
48871
48871
  closed: external_exports.boolean().default(false),
48872
- constantSpeed: external_exports.boolean().default(true)
48872
+ constantSpeed: external_exports.boolean().default(true),
48873
+ /** Spine-style active clipping span in the posed draw order; end slot is inclusive. */
48874
+ clipping: external_exports.object({ end: Id.optional() }).optional()
48873
48875
  });
48874
48876
  var Attachment = external_exports.discriminatedUnion("type", [RegionAttachment, MeshAttachment, PathAttachment]);
48875
48877
  var IkConstraint = external_exports.object({
@@ -48938,7 +48940,9 @@ var Key = external_exports.object({
48938
48940
  t: external_exports.number().nonnegative(),
48939
48941
  v: KeyValue,
48940
48942
  /** easing INTO the next key */
48941
- ease: Ease.default("ease_in_out")
48943
+ ease: Ease.default("ease_in_out"),
48944
+ /** Optional independent easing for numeric-array components (for example Spine RGBA curves). */
48945
+ componentEase: external_exports.array(Ease).min(1).optional()
48942
48946
  });
48943
48947
  var Interpolation = { interpolation: external_exports.enum(["segment", "pchip"]).optional() };
48944
48948
  var Track = external_exports.discriminatedUnion("target", [
@@ -49125,6 +49129,10 @@ function validate(doc2) {
49125
49129
  else if (a5.slot !== s6.id) errs.push(`slot "${s6.id}": attachment "${s6.attachment}" belongs to slot "${a5.slot}"`);
49126
49130
  }
49127
49131
  }
49132
+ for (const a5 of doc2.attachments) if (a5.type === "path" && a5.clipping) {
49133
+ if (!a5.closed) errs.push(`attachment "${a5.id}": clipping path must be closed`);
49134
+ if (a5.clipping.end && !slots.has(a5.clipping.end)) errs.push(`attachment "${a5.id}": unknown clipping end slot "${a5.clipping.end}"`);
49135
+ }
49128
49136
  const vertexCount2 = (a5) => {
49129
49137
  if (a5.type === "region") return 4;
49130
49138
  if (a5.type === "mesh") return a5.uvs.length / 2;
@@ -49202,12 +49210,13 @@ function validate(doc2) {
49202
49210
  if (tracks.has(tid)) errs.push(`animation "${a5.id}": duplicate track ${tid}`);
49203
49211
  tracks.add(tid);
49204
49212
  if (driven.has(tid)) errs.push(`animation "${a5.id}": ${tid} is controlled by a binding; animate its control instead`);
49205
- const known = t5.target === "bone" ? bones.has(t5.id) : t5.target === "slot" ? slots.has(t5.id) : t5.target === "deform" ? attachments.has(t5.id) : t5.target === "ik" ? iks.has(t5.id) : t5.target === "transform" ? transforms.has(t5.id) : t5.target === "path" ? paths.has(t5.id) : t5.target === "control" ? controls.has(t5.id) : true;
49206
- if (!known) errs.push(`animation "${a5.id}": unknown ${t5.target} "${t5.id}"`);
49213
+ const known2 = t5.target === "bone" ? bones.has(t5.id) : t5.target === "slot" ? slots.has(t5.id) : t5.target === "deform" ? attachments.has(t5.id) : t5.target === "ik" ? iks.has(t5.id) : t5.target === "transform" ? transforms.has(t5.id) : t5.target === "path" ? paths.has(t5.id) : t5.target === "control" ? controls.has(t5.id) : true;
49214
+ if (!known2) errs.push(`animation "${a5.id}": unknown ${t5.target} "${t5.id}"`);
49207
49215
  const bound = t5.target === "deform" ? DEFORM_BOUNDS[t5.prop] : void 0;
49208
49216
  const att = t5.target === "deform" ? attachments.get(t5.id) : void 0;
49209
49217
  let prev = -1;
49210
49218
  for (const k4 of t5.keys) {
49219
+ if (k4.componentEase && (!Array.isArray(k4.v) || !k4.v.every((v6) => typeof v6 === "number") || k4.componentEase.length !== k4.v.length)) errs.push(`animation "${a5.id}" ${tid}: componentEase must match the numeric-array value length`);
49211
49220
  if (k4.t <= prev && prev >= 0) errs.push(`animation "${a5.id}" ${tid}: keys not strictly increasing`);
49212
49221
  if (k4.t > a5.duration) errs.push(`animation "${a5.id}" ${tid}: key at ${k4.t}s exceeds duration`);
49213
49222
  if (t5.target === "control") {
@@ -49372,8 +49381,8 @@ function sample(track, t5, loop2 = false) {
49372
49381
  }
49373
49382
  if (Array.isArray(a5.v) && Array.isArray(b4.v) && typeof a5.v[0] === "number") {
49374
49383
  if (track.interpolation === "pchip") return a5.v.map((_4, j5) => smoothValue(track, i6, t5, j5, loop2));
49375
- const u6 = ease(a5.ease)((t5 - a5.t) / (b4.t - a5.t)), av = a5.v, bv = b4.v;
49376
- return av.map((v6, k4) => v6 + ((bv[k4] ?? v6) - v6) * u6);
49384
+ const phase = (t5 - a5.t) / (b4.t - a5.t), av = a5.v, bv = b4.v;
49385
+ return av.map((v6, k4) => v6 + ((bv[k4] ?? v6) - v6) * ease(a5.componentEase?.[k4] ?? a5.ease)(phase));
49377
49386
  }
49378
49387
  return a5.v;
49379
49388
  }
@@ -49461,6 +49470,13 @@ function boneWorldOf(bone, s6, parent) {
49461
49470
  if (!parent) return local;
49462
49471
  if (bone.inherit === "normal") return mul(parent, local);
49463
49472
  const [tx, ty] = apply(parent, s6.x, s6.y);
49473
+ if (bone.inherit === "noScale" || bone.inherit === "noScaleOrReflection") {
49474
+ const r5 = s6.rotation * DEG2, x4 = parent.a * Math.cos(r5) + parent.c * Math.sin(r5), y5 = parent.b * Math.cos(r5) + parent.d * Math.sin(r5);
49475
+ const length2 = Math.hypot(x4, y5), a5 = length2 > 1e-5 ? x4 / length2 : x4, b4 = length2 > 1e-5 ? y5 / length2 : y5;
49476
+ const sign = bone.inherit === "noScale" && parent.a * parent.d - parent.b * parent.c < 0 ? -1 : 1;
49477
+ const basis2 = { a: a5, b: b4, c: -b4 * sign, d: a5 * sign, tx: 0, ty: 0 };
49478
+ return { ...mul(basis2, affineOf(0, 0, 0, s6.scaleX, s6.scaleY, s6.shearX, s6.shearY)), tx, ty };
49479
+ }
49464
49480
  return { ...mul(inheritedParentLinear(bone, parent), { ...local, tx: 0, ty: 0 }), tx, ty };
49465
49481
  }
49466
49482
  var orderCache = /* @__PURE__ */ new WeakMap();
@@ -49493,11 +49509,11 @@ var localOf = (doc2, pose2, b4) => {
49493
49509
  shearY: num(pose2, pk("bone", id, "shearY"), r5.shearY)
49494
49510
  };
49495
49511
  };
49496
- function updateSubtree(skel, root2) {
49512
+ function updateSubtree(skel, root2, includeRoot = true) {
49497
49513
  const doc2 = skel.doc;
49498
49514
  const kids = /* @__PURE__ */ new Map();
49499
49515
  for (const b4 of doc2.bones) if (b4.parent) (kids.get(b4.parent) ?? kids.set(b4.parent, []).get(b4.parent)).push(b4);
49500
- const stack = [root2];
49516
+ const stack = includeRoot ? [root2] : (kids.get(root2) ?? []).map((b4) => b4.id);
49501
49517
  while (stack.length) {
49502
49518
  const id = stack.pop(), b4 = boneOf(doc2, id), st = skel.bones.get(id);
49503
49519
  st.world = boneWorldOf(b4, st, b4.parent ? skel.bones.get(b4.parent).world : void 0);
@@ -49531,6 +49547,23 @@ function evaluate(doc2, pose2, modifiers) {
49531
49547
  }
49532
49548
  return skel;
49533
49549
  }
49550
+ function slotClips(skel) {
49551
+ const keyed = skel.pose.get(pk("drawOrder", "order", "order"));
49552
+ const order = Array.isArray(keyed) ? keyed : [...skel.doc.slots].sort((a5, b4) => a5.z - b4.z).map((s6) => s6.id);
49553
+ const masks = /* @__PURE__ */ new Map();
49554
+ let active;
49555
+ for (const id of order) {
49556
+ const att = skel.attachment.get(id);
49557
+ if (!active && att?.type === "path" && att.clipping) {
49558
+ active = att;
49559
+ continue;
49560
+ }
49561
+ if (active) masks.set(id, active.id);
49562
+ if (active?.clipping?.end === id) active = void 0;
49563
+ }
49564
+ for (const s6 of skel.doc.slots) if (s6.clip) masks.set(s6.id, s6.clip);
49565
+ return masks;
49566
+ }
49534
49567
  var cnum = (skel, kind, id, prop, rest) => num(skel.pose, pk(kind, id, prop), typeof rest === "boolean" ? rest ? 1 : 0 : rest);
49535
49568
  var IDENTITY = { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 };
49536
49569
  var wrapRad = (r5) => r5 > Math.PI ? r5 - 2 * Math.PI : r5 < -Math.PI ? r5 + 2 * Math.PI : r5;
@@ -49740,7 +49773,8 @@ function applyTransformConstraint(skel, c5) {
49740
49773
  if (!mixRotate && !mixX && !mixY && !mixScaleX && !mixScaleY && !mixShearY) return;
49741
49774
  c5 = { ...c5, ...Object.fromEntries(["x", "y", "rotation", "scaleX", "scaleY", "shearY"].map((p6) => [p6, cnum(skel, "transform", c5.id, p6, c5[p6])])) };
49742
49775
  const target = skel.bones.get(c5.target), tw = target.world;
49743
- const offsetRotation = c5.rotation * DEG2, offsetShearY = c5.shearY * DEG2;
49776
+ const handedness = tw.a * tw.d - tw.b * tw.c < 0 ? -1 : 1;
49777
+ const offsetRotation = c5.rotation * DEG2 * (c5.local ? 1 : handedness), offsetShearY = c5.shearY * DEG2 * (c5.local ? 1 : handedness);
49744
49778
  for (const id of c5.bones) {
49745
49779
  const b4 = boneOf(skel.doc, id), st = skel.bones.get(id);
49746
49780
  if (c5.local) {
@@ -49902,6 +49936,7 @@ function applyPathConstraint(skel, c5) {
49902
49936
  const position = cnum(skel, "path", c5.id, "position", c5.position), spacing = cnum(skel, "path", c5.id, "spacing", c5.spacing);
49903
49937
  const tangents = c5.rotateMode === "tangent", scale = c5.rotateMode === "chainScale";
49904
49938
  const bones = c5.bones.map((id) => ({ b: boneOf(skel.doc, id), st: skel.bones.get(id) }));
49939
+ const inputWorlds = bones.map(({ st }) => ({ ...st.world }));
49905
49940
  const spacesCount = tangents ? bones.length : bones.length + 1;
49906
49941
  const spaces = new Array(spacesCount).fill(0), worldLengths = [];
49907
49942
  let sum = 0;
@@ -49936,9 +49971,10 @@ function applyPathConstraint(skel, c5) {
49936
49971
  if (tw.a * tw.d - tw.b * tw.c < 0) offsetRotation = -offsetRotation;
49937
49972
  }
49938
49973
  let boneX = samples[0].x, boneY = samples[0].y;
49974
+ const solvedWorlds = /* @__PURE__ */ new Map();
49939
49975
  for (let i6 = 0; i6 < bones.length; i6++) {
49940
49976
  const { b: b4, st } = bones[i6];
49941
- let w5 = { ...st.world };
49977
+ let w5 = { ...inputWorlds[i6] };
49942
49978
  w5.tx += (boneX - w5.tx) * mixX;
49943
49979
  w5.ty += (boneY - w5.ty) * mixY;
49944
49980
  const next = samples[i6 + 1] ?? { x: 0, y: 0, angle: 0 };
@@ -49968,7 +50004,11 @@ function applyPathConstraint(skel, c5) {
49968
50004
  const cos = Math.cos(r5), sin = Math.sin(r5);
49969
50005
  w5 = { a: cos * a5 - sin * bb, b: sin * a5 + cos * bb, c: cos * cc - sin * d6, d: sin * cc + cos * d6, tx: w5.tx, ty: w5.ty };
49970
50006
  }
49971
- setEffectiveWorld(skel, b4, st, w5);
50007
+ solvedWorlds.set(b4.id, w5);
50008
+ }
50009
+ for (const b4 of boneOrder(skel.doc)) {
50010
+ const world = solvedWorlds.get(b4.id);
50011
+ if (world) setEffectiveWorld(skel, b4, skel.bones.get(b4.id), world);
49972
50012
  }
49973
50013
  }
49974
50014
  function measuredPathLength(points, closed, lengths, constantSpeed) {
@@ -49997,9 +50037,14 @@ function inheritedParentLinear(bone, parent) {
49997
50037
  case "onlyTranslation":
49998
50038
  return { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 };
49999
50039
  case "noRotationOrReflection": {
50000
- const lenX = Math.hypot(parent.a, parent.b) || 1;
50040
+ const lenX = Math.hypot(parent.a, parent.b);
50001
50041
  const det = parent.a * parent.d - parent.b * parent.c;
50002
- return { a: lenX, b: 0, c: 0, d: Math.abs(det) / lenX, tx: 0, ty: 0 };
50042
+ if (lenX * lenX <= 1e-4) {
50043
+ const lenY = Math.hypot(parent.c, parent.d), ux3 = lenY ? parent.c / lenY : 0, uy3 = lenY ? parent.d / lenY : 0;
50044
+ return { a: lenY * ux3 * ux3, b: lenY * ux3 * uy3, c: lenY * ux3 * uy3, d: lenY * uy3 * uy3, tx: 0, ty: 0 };
50045
+ }
50046
+ const ux2 = parent.a / lenX, uy2 = parent.b / lenX, sy = Math.abs(det) / lenX;
50047
+ return { a: lenX * ux2 * ux2 + sy * uy2 * uy2, b: (lenX - sy) * ux2 * uy2, c: (lenX - sy) * ux2 * uy2, d: lenX * uy2 * uy2 + sy * ux2 * ux2, tx: 0, ty: 0 };
50003
50048
  }
50004
50049
  case "noScale":
50005
50050
  case "noScaleOrReflection": {
@@ -50025,7 +50070,16 @@ function setEffectiveWorld(skel, bone, st, world) {
50025
50070
  st.scaleY = Math.hypot(lin.c, lin.d);
50026
50071
  st.shearX = 0;
50027
50072
  st.shearY = shortest(Math.atan2(lin.d, lin.c) / DEG2 - st.rotation - 90);
50028
- updateSubtree(skel, bone.id);
50073
+ if (parent && (bone.inherit === "noScale" || bone.inherit === "noScaleOrReflection")) {
50074
+ const direction = mul(invert({ ...parent, tx: 0, ty: 0 }), { ...world, tx: 0, ty: 0 });
50075
+ st.rotation = Math.atan2(direction.b, direction.a) / DEG2;
50076
+ st.scaleX = Math.hypot(world.a, world.b);
50077
+ st.scaleY = Math.hypot(world.c, world.d);
50078
+ const sign = bone.inherit === "noScale" && parent.a * parent.d - parent.b * parent.c < 0 ? -1 : 1;
50079
+ st.shearY = shortest((Math.atan2(world.d, world.c) - Math.atan2(world.b, world.a)) / DEG2 * sign - 90);
50080
+ }
50081
+ st.world = world;
50082
+ updateSubtree(skel, bone.id, false);
50029
50083
  }
50030
50084
  function attachmentWorldVertices(skel, slotId, att) {
50031
50085
  const deform = skel.pose.get(pk("deform", att.id, "vertices"));
@@ -50904,12 +50958,12 @@ var json = async (res) => {
50904
50958
  return body;
50905
50959
  };
50906
50960
  async function getDoc(rig) {
50907
- const r5 = await json(await fetch(`/api/doc?rig=${encodeURIComponent(rig)}`));
50961
+ const r5 = await json(await fetch(`api/doc?rig=${encodeURIComponent(rig)}`));
50908
50962
  if (r5.isError) throw new Error(String(r5.text).replace(/^ERROR: /, ""));
50909
50963
  return r5;
50910
50964
  }
50911
50965
  async function callTool(rig, name, args, baseVersion) {
50912
- const res = await fetch(`/api/tool/${encodeURIComponent(name)}`, {
50966
+ const res = await fetch(`api/tool/${encodeURIComponent(name)}`, {
50913
50967
  method: "POST",
50914
50968
  headers: { "content-type": "application/json" },
50915
50969
  body: JSON.stringify({ rig, args, baseVersion })
@@ -50917,25 +50971,25 @@ async function callTool(rig, name, args, baseVersion) {
50917
50971
  return { ...await json(res), status: res.status };
50918
50972
  }
50919
50973
  function subscribe(rig, onChanged, onState) {
50920
- const es = new EventSource(`/api/events?rig=${encodeURIComponent(rig)}`);
50974
+ const es = new EventSource(`api/events?rig=${encodeURIComponent(rig)}`);
50921
50975
  es.addEventListener("changed", (e5) => onChanged(JSON.parse(e5.data).version));
50922
50976
  es.onopen = () => onState(true);
50923
50977
  es.onerror = () => onState(false);
50924
50978
  return () => es.close();
50925
50979
  }
50926
- var assetUrl = (rigDir2, src) => `/rig/${rigDir2 ? rigDir2 + "/" : ""}${src}`;
50980
+ var assetUrl = (rigDir2, src) => `rig/${rigDir2 ? rigDir2 + "/" : ""}${src}`;
50927
50981
  async function listRigs() {
50928
- return json(await fetch("/api/rigs"));
50982
+ return json(await fetch("api/rigs"));
50929
50983
  }
50930
50984
  async function createRig(rig, name, width, height) {
50931
- const res = await fetch("/api/rigs", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ rig, name, width, height }) });
50985
+ const res = await fetch("api/rigs", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ rig, name, width, height }) });
50932
50986
  return { ...await json(res), status: res.status };
50933
50987
  }
50934
50988
  async function listFiles(rig) {
50935
- return json(await fetch(`/api/files?rig=${encodeURIComponent(rig)}`));
50989
+ return json(await fetch(`api/files?rig=${encodeURIComponent(rig)}`));
50936
50990
  }
50937
50991
  async function upload(rig, file) {
50938
- const res = await fetch(`/api/upload?rig=${encodeURIComponent(rig)}&name=${encodeURIComponent(file.name)}`, { method: "POST", headers: { "content-type": "image/png" }, body: file });
50992
+ const res = await fetch(`api/upload?rig=${encodeURIComponent(rig)}&name=${encodeURIComponent(file.name)}`, { method: "POST", headers: { "content-type": "image/png" }, body: file });
50939
50993
  const r5 = await json(res);
50940
50994
  if (r5.isError) throw new Error(String(r5.text).replace(/^ERROR: /, ""));
50941
50995
  return r5;
@@ -51355,6 +51409,7 @@ __export(store_exports, {
51355
51409
  drawOrderFront: () => drawOrderFront,
51356
51410
  drivenBy: () => drivenBy,
51357
51411
  editable: () => editable,
51412
+ embedded: () => embedded,
51358
51413
  ensureTemplate: () => ensureTemplate,
51359
51414
  externalChange: () => externalChange,
51360
51415
  files: () => files,
@@ -51541,6 +51596,7 @@ function hitMesh(v6, tri, px, py) {
51541
51596
  var params = new URLSearchParams(typeof location === "undefined" ? "" : location.search);
51542
51597
  var rigPath = params.get("rig") || "examples/hero/rig.json";
51543
51598
  var rigDir = rigPath.slice(0, Math.max(0, rigPath.lastIndexOf("/")));
51599
+ var embedded = params.get("embed") === "1";
51544
51600
  var doc = y3(void 0);
51545
51601
  var draft = y3(void 0);
51546
51602
  var shown = g2(() => draft.value ?? doc.value);
@@ -58172,7 +58228,7 @@ var RigPlayer = class {
58172
58228
  this.clipPoints.clear();
58173
58229
  const skel = this.skel = evaluate(this.doc, pose2, this.beforePose);
58174
58230
  pose2 = skel.pose;
58175
- const clips = new Set(this.doc.slots.flatMap((s6) => s6.clip ? [s6.clip] : []));
58231
+ const masks = slotClips(skel), clips = new Set(masks.values());
58176
58232
  for (const [id, g4] of this.clipGraphics) if (!clips.has(id)) {
58177
58233
  g4.destroy();
58178
58234
  this.clipGraphics.delete(id);
@@ -58199,7 +58255,8 @@ var RigPlayer = class {
58199
58255
  if (Array.isArray(orderV)) orderV.forEach((id, i6) => order.set(id, i6));
58200
58256
  for (const it of this.items) {
58201
58257
  const s6 = it.slot, shown2 = skel.attachment.get(s6.id);
58202
- it.node.mask = s6.clip ? this.clipGraphics.get(s6.clip) ?? null : null;
58258
+ const mask = masks.get(s6.id);
58259
+ it.node.mask = mask ? this.clipGraphics.get(mask) ?? null : null;
58203
58260
  if (shown2 !== it.att || this.hidden.has(s6.id)) {
58204
58261
  it.node.visible = false;
58205
58262
  continue;
@@ -58825,8 +58882,8 @@ var Stage = class {
58825
58882
  void p6.play(anim.id, { loop: false, mix: 0 });
58826
58883
  p6.tick(i6 / fps);
58827
58884
  const { pixels } = this.app.renderer.extract.pixels({ target: this.camera, frame });
58828
- const palette = (0, import_gifenc.quantize)(pixels, 256);
58829
- gif.writeFrame((0, import_gifenc.applyPalette)(pixels, palette), w5, h6, { palette, delay: 1e3 / fps });
58885
+ const palette2 = (0, import_gifenc.quantize)(pixels, 256);
58886
+ gif.writeFrame((0, import_gifenc.applyPalette)(pixels, palette2), w5, h6, { palette: palette2, delay: 1e3 / fps });
58830
58887
  setStatus(`gif: frame ${i6 + 1}/${n4}`);
58831
58888
  await new Promise((r5) => setTimeout(r5));
58832
58889
  }
@@ -59640,9 +59697,9 @@ var KEY_HIT = 5;
59640
59697
  var times2 = (ts) => [...new Set(ts.flatMap((t5) => t5.keys.map((k4) => k4.t)))].sort((a5, b4) => a5 - b4);
59641
59698
  function rows(doc2, anim) {
59642
59699
  const out2 = [{ kind: "all", label: "all keys", keys: times2(anim.tracks), depth: 0 }];
59643
- const push = (target, known) => {
59700
+ const push = (target, known2) => {
59644
59701
  const mine = anim.tracks.filter((t5) => t5.target === target);
59645
- const ids = [.../* @__PURE__ */ new Set([...known.filter((id) => mine.some((t5) => t5.id === id)), ...mine.map((t5) => t5.id)])];
59702
+ const ids = [.../* @__PURE__ */ new Set([...known2.filter((id) => mine.some((t5) => t5.id === id)), ...mine.map((t5) => t5.id)])];
59646
59703
  for (const id of ids) {
59647
59704
  const own = mine.filter((t5) => t5.id === id);
59648
59705
  out2.push({ kind: "group", label: id, keys: times2(own), depth: 0 });
@@ -59708,14 +59765,19 @@ function clampDuration(requested, lastKeyOrEventTime, fps) {
59708
59765
  }
59709
59766
 
59710
59767
  // src/editor/timeline.ts
59711
- var PANEL = "#181818";
59712
- var ALT = "#1E1E1E";
59713
- var LINE = "#2B2B2B";
59714
- var CTL_LINE = "#3C3C3C";
59715
- var ACCENT4 = "#0078D4";
59716
- var FG = "#CCCCCC";
59717
- var FG2 = "#9D9D9D";
59718
- var ERR = "#F14C4C";
59768
+ var TOKENS = {
59769
+ PANEL: ["--chrome", "#181818"],
59770
+ ALT: ["--bg", "#1E1E1E"],
59771
+ LINE: ["--line", "#2B2B2B"],
59772
+ CTL_LINE: ["--ctl-line", "#3C3C3C"],
59773
+ ACCENT: ["--accent", "#0078D4"],
59774
+ FG: ["--fg", "#CCCCCC"],
59775
+ FG2: ["--fg2", "#9D9D9D"],
59776
+ ERR: ["--err", "#F14C4C"]
59777
+ };
59778
+ function palette(style) {
59779
+ return Object.fromEntries(Object.entries(TOKENS).map(([k4, [token, fallback]]) => [k4, style.getPropertyValue(token).trim() || fallback]));
59780
+ }
59719
59781
  var LEFT = 220;
59720
59782
  var ROW_H = 20;
59721
59783
  var HEADER_H = 24;
@@ -59745,6 +59807,9 @@ var Timeline = class {
59745
59807
  selectedKeys.value;
59746
59808
  f3(() => this.draw());
59747
59809
  }));
59810
+ const retheme = () => f3(() => this.draw());
59811
+ window.addEventListener("golem:theme", retheme);
59812
+ this.dispose.push(() => window.removeEventListener("golem:theme", retheme));
59748
59813
  this.bindPointer();
59749
59814
  this.resize();
59750
59815
  }
@@ -59815,6 +59880,7 @@ var Timeline = class {
59815
59880
  g4.fill();
59816
59881
  }
59817
59882
  draw() {
59883
+ const { PANEL, ALT, LINE, CTL_LINE, ACCENT: ACCENT4, FG, FG2, ERR } = palette(getComputedStyle(document.documentElement));
59818
59884
  const g4 = this.g, w5 = this.w, h6 = this.h, l6 = this.layout();
59819
59885
  const anim = currentAnim.value, rs = this.rowsNow(), d6 = this.drag;
59820
59886
  const dur = d6?.kind === "duration" ? d6.duration : anim?.duration ?? 0;
@@ -60245,6 +60311,41 @@ function ControlForms() {
60245
60311
  ] });
60246
60312
  }
60247
60313
 
60314
+ // src/editor/embed.ts
60315
+ var THEME_TOKENS = [
60316
+ "--bg",
60317
+ "--panel",
60318
+ "--chrome",
60319
+ "--line",
60320
+ "--accent",
60321
+ "--accent-hover",
60322
+ "--fg",
60323
+ "--fg2",
60324
+ "--ctl",
60325
+ "--ctl-hover",
60326
+ "--ctl-line",
60327
+ "--err",
60328
+ "--warn",
60329
+ "--font",
60330
+ "--size"
60331
+ ];
60332
+ var known = new Set(THEME_TOKENS);
60333
+ function themeVars(data) {
60334
+ if (!data || typeof data !== "object" || data.type !== "golem:theme") return [];
60335
+ const vars = data.vars;
60336
+ if (!vars || typeof vars !== "object") return [];
60337
+ return Object.entries(vars).filter((e5) => known.has(e5[0]) && typeof e5[1] === "string");
60338
+ }
60339
+ function listenForTheme(win = window) {
60340
+ const on = (e5) => {
60341
+ if (e5.origin !== win.location.origin) return;
60342
+ for (const [name, value] of themeVars(e5.data)) win.document.documentElement.style.setProperty(name, value);
60343
+ win.dispatchEvent(new Event("golem:theme"));
60344
+ };
60345
+ win.addEventListener("message", on);
60346
+ return () => win.removeEventListener("message", on);
60347
+ }
60348
+
60248
60349
  // src/editor/app.tsx
60249
60350
  var stage;
60250
60351
  var timeline;
@@ -60406,7 +60507,7 @@ function OpenList() {
60406
60507
  const body = rigsLoading.value ? /* @__PURE__ */ u4("div", { class: "rig", children: "loading\u2026" }) : rigsError.value ? /* @__PURE__ */ u4("div", { class: "rig error", children: [
60407
60508
  "open: ",
60408
60509
  rigsError.value
60409
- ] }) : rigs.value.length ? rigs.value.map((r5) => /* @__PURE__ */ u4("a", { class: "rig", "data-rig": r5.rig, href: `/editor.html?rig=${encodeURIComponent(r5.rig)}`, children: [
60510
+ ] }) : rigs.value.length ? rigs.value.map((r5) => /* @__PURE__ */ u4("a", { class: "rig", "data-rig": r5.rig, href: `editor.html?rig=${encodeURIComponent(r5.rig)}`, children: [
60410
60511
  /* @__PURE__ */ u4("b", { children: r5.name }),
60411
60512
  " ",
60412
60513
  /* @__PURE__ */ u4("span", { class: "path", children: r5.rig }),
@@ -60438,7 +60539,7 @@ function NewForm() {
60438
60539
  setStatus(String(r5.text).replace(/^ERROR: /, ""), "error");
60439
60540
  return;
60440
60541
  }
60441
- location.href = `/editor.html?rig=${encodeURIComponent(`${dir}/rig.json`)}`;
60542
+ location.href = `editor.html?rig=${encodeURIComponent(`${dir}/rig.json`)}`;
60442
60543
  } catch (e5) {
60443
60544
  setStatus(`new: ${e5.message}`, "error");
60444
60545
  }
@@ -60578,8 +60679,8 @@ function TopBar() {
60578
60679
  /* @__PURE__ */ u4("input", { class: "lock", type: "checkbox", checked: lockImages.value, onChange: (e5) => lockImages.value = e5.target.checked }),
60579
60680
  "lock images"
60580
60681
  ] }),
60581
- /* @__PURE__ */ u4("button", { class: "new", onClick: click(() => popover.value = popover.peek() === "new" ? void 0 : "new"), children: "new" }),
60582
- /* @__PURE__ */ u4("button", { class: "open", onClick: click(() => {
60682
+ !embedded && /* @__PURE__ */ u4("button", { class: "new", onClick: click(() => popover.value = popover.peek() === "new" ? void 0 : "new"), children: "new" }),
60683
+ !embedded && /* @__PURE__ */ u4("button", { class: "open", onClick: click(() => {
60583
60684
  if (popover.peek() === "open") {
60584
60685
  closePopover();
60585
60686
  return;
@@ -60839,6 +60940,7 @@ function App() {
60839
60940
  /* @__PURE__ */ u4(Splitter, { edge: "tl" })
60840
60941
  ] });
60841
60942
  }
60943
+ if (embedded) listenForTheme();
60842
60944
  restoreLayout();
60843
60945
  R(/* @__PURE__ */ u4(App, {}), document.getElementById("app"));
60844
60946
  await reload();