@almadar/ui 6.12.0 → 6.14.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 (47) hide show
  1. package/dist/{UserContext-g_LcDiGN.d.cts → CurrentPagePathContext-J1lqjvAq.d.cts} +2 -97
  2. package/dist/{UserContext-g_LcDiGN.d.ts → CurrentPagePathContext-J1lqjvAq.d.ts} +2 -97
  3. package/dist/{EntityBindingContext-D5lRu6p1.d.cts → EntityBindingContext-BMz9xiBa.d.cts} +2 -3
  4. package/dist/{EntityBindingContext-D5lRu6p1.d.ts → EntityBindingContext-BMz9xiBa.d.ts} +2 -3
  5. package/dist/{GameAudioProvider-D8GynTNq.d.cts → GameAudioProvider-Cpw_lMPY.d.cts} +1 -1
  6. package/dist/{GameAudioProvider-Dozqx4sL.d.ts → GameAudioProvider-DIjFiy70.d.ts} +1 -1
  7. package/dist/UserContext-D566nfWA.d.cts +99 -0
  8. package/dist/UserContext-D566nfWA.d.ts +99 -0
  9. package/dist/avl/index.cjs +571 -70
  10. package/dist/avl/index.d.cts +1 -1
  11. package/dist/avl/index.d.ts +1 -1
  12. package/dist/avl/index.js +573 -72
  13. package/dist/{avl-schema-parser-PVu8rgsy.d.cts → avl-schema-parser-B25PL-a8.d.cts} +1 -1
  14. package/dist/{avl-schema-parser-DX-aVCCm.d.ts → avl-schema-parser-CM0pQo8E.d.ts} +1 -1
  15. package/dist/components/index.cjs +564 -117
  16. package/dist/components/index.d.cts +8 -24
  17. package/dist/components/index.d.ts +8 -24
  18. package/dist/components/index.js +566 -118
  19. package/dist/context/index.d.cts +2 -1
  20. package/dist/context/index.d.ts +2 -1
  21. package/dist/lib/drawable/three/index.cjs +126 -3
  22. package/dist/lib/drawable/three/index.d.cts +3 -3
  23. package/dist/lib/drawable/three/index.d.ts +3 -3
  24. package/dist/lib/drawable/three/index.js +126 -3
  25. package/dist/lib/index.cjs +1 -4
  26. package/dist/lib/index.d.cts +2 -2
  27. package/dist/lib/index.d.ts +2 -2
  28. package/dist/lib/index.js +1 -4
  29. package/dist/{offline-executor-QUdKOj7f.d.cts → offline-executor-DdV2o0Zu.d.cts} +4 -19
  30. package/dist/{offline-executor-QUdKOj7f.d.ts → offline-executor-DdV2o0Zu.d.ts} +4 -19
  31. package/dist/{paintDispatch-CK5uwYEq.d.cts → paintDispatch-Dh1pgljl.d.cts} +79 -1
  32. package/dist/{paintDispatch-CK5uwYEq.d.ts → paintDispatch-Dh1pgljl.d.ts} +79 -1
  33. package/dist/providers/index.cjs +558 -62
  34. package/dist/providers/index.d.cts +10 -9
  35. package/dist/providers/index.d.ts +10 -9
  36. package/dist/providers/index.js +559 -63
  37. package/dist/renderer/index.cjs +0 -15
  38. package/dist/renderer/index.d.cts +4 -9
  39. package/dist/renderer/index.d.ts +4 -9
  40. package/dist/renderer/index.js +1 -15
  41. package/dist/runtime/index.cjs +573 -81
  42. package/dist/runtime/index.d.cts +14 -11
  43. package/dist/runtime/index.d.ts +14 -11
  44. package/dist/runtime/index.js +575 -83
  45. package/dist/{verificationRegistry-D8bHWD8Q.d.ts → verificationRegistry-Cwa52VyK.d.ts} +1 -1
  46. package/dist/{verificationRegistry-BLsjb1oU.d.cts → verificationRegistry-DTrKDRoa.d.cts} +1 -1
  47. package/package.json +4 -4
@@ -8904,6 +8904,14 @@ function createWebPainter(ctx, onAssetLoad) {
8904
8904
  ctx.drawImage(img, dest.x, dest.y, dw, dh);
8905
8905
  }
8906
8906
  },
8907
+ blitTransformed(tex, src, affine) {
8908
+ const img = imageByHandle.get(tex);
8909
+ if (!img) return;
8910
+ ctx.save();
8911
+ ctx.transform(affine[0], affine[1], affine[2], affine[3], affine[4], affine[5]);
8912
+ ctx.drawImage(img, src.x, src.y, src.w, src.h, src.x, src.y, src.w, src.h);
8913
+ ctx.restore();
8914
+ },
8907
8915
  fillRect(x, y, w, h, style) {
8908
8916
  ctx.fillStyle = toCanvasStyle(style);
8909
8917
  ctx.fillRect(x, y, w, h);
@@ -9493,6 +9501,271 @@ var init_DrawGroup = __esm({
9493
9501
  init_registry();
9494
9502
  }
9495
9503
  });
9504
+
9505
+ // lib/skinning.ts
9506
+ function composeAffine(a, b) {
9507
+ return [
9508
+ a[0] * b[0] + a[2] * b[1],
9509
+ a[1] * b[0] + a[3] * b[1],
9510
+ a[0] * b[2] + a[2] * b[3],
9511
+ a[1] * b[2] + a[3] * b[3],
9512
+ a[0] * b[4] + a[2] * b[5] + a[4],
9513
+ a[1] * b[4] + a[3] * b[5] + a[5]
9514
+ ];
9515
+ }
9516
+ function invertAffine(m) {
9517
+ const det = m[0] * m[3] - m[1] * m[2];
9518
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return IDENTITY;
9519
+ const inv = 1 / det;
9520
+ return [
9521
+ m[3] * inv,
9522
+ -m[1] * inv,
9523
+ -m[2] * inv,
9524
+ m[0] * inv,
9525
+ (m[2] * m[5] - m[3] * m[4]) * inv,
9526
+ (m[1] * m[4] - m[0] * m[5]) * inv
9527
+ ];
9528
+ }
9529
+ function applyAffine(m, x, y) {
9530
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
9531
+ }
9532
+ function rotationAbout(pivot, degrees) {
9533
+ const r = degrees * Math.PI / 180;
9534
+ const cos = Math.cos(r);
9535
+ const sin = Math.sin(r);
9536
+ const [px, py] = pivot;
9537
+ return [cos, sin, -sin, cos, px - cos * px + sin * py, py - sin * px - cos * py];
9538
+ }
9539
+ function computeWorldMatrices(bones, pose) {
9540
+ const indexByName = /* @__PURE__ */ new Map();
9541
+ bones.forEach((b, i) => indexByName.set(b.name, i));
9542
+ const world = new Array(bones.length);
9543
+ const visiting = /* @__PURE__ */ new Set();
9544
+ const resolve = (i) => {
9545
+ const cached = world[i];
9546
+ if (cached) return cached;
9547
+ if (visiting.has(i)) return IDENTITY;
9548
+ visiting.add(i);
9549
+ const b = bones[i];
9550
+ const parentIndex = b.parent === null ? void 0 : indexByName.get(b.parent);
9551
+ const parentWorld = parentIndex === void 0 ? IDENTITY : resolve(parentIndex);
9552
+ const local = rotationAbout(b.pivot, pose[b.name] ?? 0);
9553
+ const m = composeAffine(parentWorld, local);
9554
+ visiting.delete(i);
9555
+ world[i] = m;
9556
+ return m;
9557
+ };
9558
+ return bones.map((_, i) => resolve(i));
9559
+ }
9560
+ function skinVertices(mesh, bindWorld, curWorld) {
9561
+ const skinMats = /* @__PURE__ */ new Map();
9562
+ const skinMat = (bone) => {
9563
+ let m = skinMats.get(bone);
9564
+ if (!m) {
9565
+ m = composeAffine(curWorld[bone] ?? IDENTITY, invertAffine(bindWorld[bone] ?? IDENTITY));
9566
+ skinMats.set(bone, m);
9567
+ }
9568
+ return m;
9569
+ };
9570
+ return mesh.vertices.map((v) => {
9571
+ if (v.weights.length === 0) return [v.x, v.y];
9572
+ let x = 0;
9573
+ let y = 0;
9574
+ for (const { bone, w } of v.weights) {
9575
+ const [bx, by] = applyAffine(skinMat(bone), v.x, v.y);
9576
+ x += w * bx;
9577
+ y += w * by;
9578
+ }
9579
+ return [x, y];
9580
+ });
9581
+ }
9582
+ function triangleAffine(src, dst) {
9583
+ const [s0, s1, s2] = src;
9584
+ const det = s0[0] * (s1[1] - s2[1]) + s1[0] * (s2[1] - s0[1]) + s2[0] * (s0[1] - s1[1]);
9585
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return null;
9586
+ const [d0, d1, d2] = dst;
9587
+ const a = (d0[0] * (s1[1] - s2[1]) + d1[0] * (s2[1] - s0[1]) + d2[0] * (s0[1] - s1[1])) / det;
9588
+ const c = (d0[0] * (s2[0] - s1[0]) + d1[0] * (s0[0] - s2[0]) + d2[0] * (s1[0] - s0[0])) / det;
9589
+ const e = (d0[0] * (s1[0] * s2[1] - s2[0] * s1[1]) + d1[0] * (s2[0] * s0[1] - s0[0] * s2[1]) + d2[0] * (s0[0] * s1[1] - s1[0] * s0[1])) / det;
9590
+ const b = (d0[1] * (s1[1] - s2[1]) + d1[1] * (s2[1] - s0[1]) + d2[1] * (s0[1] - s1[1])) / det;
9591
+ const d = (d0[1] * (s2[0] - s1[0]) + d1[1] * (s0[0] - s2[0]) + d2[1] * (s1[0] - s0[0])) / det;
9592
+ const f3 = (d0[1] * (s1[0] * s2[1] - s2[0] * s1[1]) + d1[1] * (s2[0] * s0[1] - s0[0] * s2[1]) + d2[1] * (s0[0] * s1[1] - s1[0] * s0[1])) / det;
9593
+ return [a, b, c, d, e, f3];
9594
+ }
9595
+ function isRigBundle(json) {
9596
+ return json.mesh !== void 0 || json.bones !== void 0;
9597
+ }
9598
+ function getRigBundle(url, onReady) {
9599
+ if (meshCache.has(url)) return meshCache.get(url) ?? void 0;
9600
+ meshCache.set(url, void 0);
9601
+ fetch(url).then((r) => {
9602
+ if (!r.ok) throw new Error(`rigbundle fetch failed: HTTP ${r.status}`);
9603
+ return r.json();
9604
+ }).then((json) => {
9605
+ const ok = isRigBundle(json) || Array.isArray(json.vertices);
9606
+ if (!ok) throw new Error("rigbundle payload is neither a SkinMesh nor a rigbundle");
9607
+ meshCache.set(url, json);
9608
+ onReady();
9609
+ }).catch(() => {
9610
+ meshCache.set(url, null);
9611
+ onReady();
9612
+ });
9613
+ return void 0;
9614
+ }
9615
+ function skinMeshFailed(url) {
9616
+ return meshCache.get(url) === null;
9617
+ }
9618
+ function vertexBounds(points) {
9619
+ if (points.length === 0) return null;
9620
+ let minX = Infinity;
9621
+ let minY = Infinity;
9622
+ let maxX = -Infinity;
9623
+ let maxY = -Infinity;
9624
+ for (const [x, y] of points) {
9625
+ if (x < minX) minX = x;
9626
+ if (y < minY) minY = y;
9627
+ if (x > maxX) maxX = x;
9628
+ if (y > maxY) maxY = y;
9629
+ }
9630
+ return { minX, minY, maxX, maxY };
9631
+ }
9632
+ var IDENTITY, meshCache;
9633
+ var init_skinning = __esm({
9634
+ "lib/skinning.ts"() {
9635
+ IDENTITY = [1, 0, 0, 1, 0, 0];
9636
+ meshCache = /* @__PURE__ */ new Map();
9637
+ }
9638
+ });
9639
+ function warnMissingOnce2(reason, node) {
9640
+ const key = `${reason}:${node.asset.url}:${String(node.meshUrl)}`;
9641
+ if (loggedMissing2.has(key)) return;
9642
+ loggedMissing2.add(key);
9643
+ skinnedLog.warn("draw-skinned-mesh unresolvable \u2014 painting fallback", { reason, url: node.asset.url, meshUrl: node.meshUrl });
9644
+ }
9645
+ function dominantBone(v) {
9646
+ let best = -1;
9647
+ let bestW = 0;
9648
+ for (const { bone, w } of v.weights) {
9649
+ if (w > bestW) {
9650
+ bestW = w;
9651
+ best = bone;
9652
+ }
9653
+ }
9654
+ return best;
9655
+ }
9656
+ function resolveSkinPose(node, clip) {
9657
+ const effectiveClip = clip ?? node.clip;
9658
+ if (effectiveClip && node.frame !== void 0 && effectiveClip.frames.length > 0) {
9659
+ return effectiveClip.frames[node.frame % effectiveClip.frames.length];
9660
+ }
9661
+ return node.pose ?? {};
9662
+ }
9663
+ function resolveRig(node, dctx) {
9664
+ const fetched = node.meshUrl ? getRigBundle(node.meshUrl, dctx.invalidate) : void 0;
9665
+ if (node.meshUrl && fetched === void 0) return void 0;
9666
+ const rb = fetched && isRigBundle(fetched) ? fetched : void 0;
9667
+ const mesh = node.mesh ?? rb?.mesh ?? (!rb ? fetched : void 0);
9668
+ if (!mesh) return void 0;
9669
+ const bones = node.bones.length > 0 ? node.bones : rb?.bones ?? [];
9670
+ if (bones.length === 0) return void 0;
9671
+ const clip = node.clip ?? (node.clipName ? rb?.clips?.[node.clipName] : void 0);
9672
+ return { mesh, bones, clip };
9673
+ }
9674
+ function meshHasPixelUVs(mesh) {
9675
+ const cached = pixelUVByMesh.get(mesh);
9676
+ if (cached !== void 0) return cached;
9677
+ let pixel = false;
9678
+ for (const v of mesh.vertices) {
9679
+ if (v.u < 0 || v.u > 1 || v.v < 0 || v.v > 1) {
9680
+ pixel = true;
9681
+ break;
9682
+ }
9683
+ }
9684
+ pixelUVByMesh.set(mesh, pixel);
9685
+ return pixel;
9686
+ }
9687
+ function paintMeshPass(painter, node, bones, mesh, tex, dctx, pose, opacity) {
9688
+ const bindWorld = computeWorldMatrices(bones, {});
9689
+ const curWorld = computeWorldMatrices(bones, pose);
9690
+ const skinned = skinVertices(mesh, bindWorld, curWorld);
9691
+ const origin = dctx.projector.project(node.position);
9692
+ const tw = dctx.projector.tileWidth;
9693
+ painter.save();
9694
+ if (opacity !== 1) painter.setAlpha(opacity);
9695
+ for (const [i0, i1, i2] of mesh.triangles) {
9696
+ const v0 = mesh.vertices[i0];
9697
+ const v1 = mesh.vertices[i1];
9698
+ const v2 = mesh.vertices[i2];
9699
+ const p0 = skinned[i0];
9700
+ const p1 = skinned[i1];
9701
+ const p2 = skinned[i2];
9702
+ if (!v0 || !v1 || !v2 || !p0 || !p1 || !p2) continue;
9703
+ const dst = [
9704
+ [origin.x + p0[0] * tw, origin.y + p0[1] * tw],
9705
+ [origin.x + p1[0] * tw, origin.y + p1[1] * tw],
9706
+ [origin.x + p2[0] * tw, origin.y + p2[1] * tw]
9707
+ ];
9708
+ if (node.weightsOverlay || !tex) {
9709
+ if (!node.weightsOverlay) continue;
9710
+ const bone = dominantBone(v0);
9711
+ painter.fillPoly(
9712
+ dst.map(([x, y]) => ({ x, y })),
9713
+ WEIGHT_PALETTE[(bone % WEIGHT_PALETTE.length + WEIGHT_PALETTE.length) % WEIGHT_PALETTE.length]
9714
+ );
9715
+ continue;
9716
+ }
9717
+ const pixelUV = meshHasPixelUVs(mesh);
9718
+ const uv = pixelUV ? [[v0.u, v0.v], [v1.u, v1.v], [v2.u, v2.v]] : [
9719
+ [v0.u * tex.width, v0.v * tex.height],
9720
+ [v1.u * tex.width, v1.v * tex.height],
9721
+ [v2.u * tex.width, v2.v * tex.height]
9722
+ ];
9723
+ const affine = triangleAffine(uv, dst);
9724
+ if (!affine) continue;
9725
+ painter.save();
9726
+ painter.clipPath(`M ${dst[0][0]} ${dst[0][1]} L ${dst[1][0]} ${dst[1][1]} L ${dst[2][0]} ${dst[2][1]} Z`);
9727
+ painter.blitTransformed(tex, { x: 0, y: 0, w: tex.width, h: tex.height }, affine);
9728
+ painter.restore();
9729
+ }
9730
+ painter.restore();
9731
+ }
9732
+ function DrawSkinnedMesh(props) {
9733
+ const register = React79.useContext(DrawableRegistryContext);
9734
+ if (register) register({ ...props, type: "draw-skinned-mesh" });
9735
+ return null;
9736
+ }
9737
+ var skinnedLog, loggedMissing2, WEIGHT_PALETTE, pixelUVByMesh, paintSkinnedMesh;
9738
+ var init_DrawSkinnedMesh = __esm({
9739
+ "components/game/atoms/DrawSkinnedMesh.tsx"() {
9740
+ "use client";
9741
+ init_imageCache();
9742
+ init_contract();
9743
+ init_registry();
9744
+ init_skinning();
9745
+ skinnedLog = logger.createLogger("almadar:ui:draw-skinned-mesh");
9746
+ loggedMissing2 = /* @__PURE__ */ new Set();
9747
+ WEIGHT_PALETTE = Array.from({ length: 10 }, (_, i) => `hsl(${i * 36 % 360}, 70%, 55%)`);
9748
+ pixelUVByMesh = /* @__PURE__ */ new WeakMap();
9749
+ paintSkinnedMesh = (painter, node, dctx) => {
9750
+ if (!isValidScenePos(node.position)) return;
9751
+ const rig = resolveRig(node, dctx);
9752
+ if (!rig) {
9753
+ if (node.meshUrl && skinMeshFailed(node.meshUrl)) warnMissingOnce2("mesh-failed", node);
9754
+ else if (!node.mesh && !node.meshUrl) warnMissingOnce2("mesh-missing", node);
9755
+ return;
9756
+ }
9757
+ const tex = node.weightsOverlay ? null : painter.resolveTexture(node.asset.url);
9758
+ if (!node.weightsOverlay && !tex) {
9759
+ if (getImageStatus(node.asset.url) === "failed") warnMissingOnce2("texture-failed", node);
9760
+ return;
9761
+ }
9762
+ if (node.ghost && rig.clip && rig.clip.frames.length > 0) {
9763
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, rig.clip.frames[node.ghost.frame % rig.clip.frames.length], node.ghost.opacity);
9764
+ }
9765
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, resolveSkinPose(node, rig.clip), node.opacity ?? 1);
9766
+ };
9767
+ }
9768
+ });
9496
9769
  function applyMeshAnimation(node, timeMs) {
9497
9770
  const anim = node.animation;
9498
9771
  if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
@@ -9817,6 +10090,9 @@ function paintDrawable(painter, node, dctx) {
9817
10090
  case "draw-mesh":
9818
10091
  warnUnsupported2d("draw-mesh");
9819
10092
  break;
10093
+ case "draw-skinned-mesh":
10094
+ paintSkinnedMesh(painter, node, dctx);
10095
+ break;
9820
10096
  case "draw-sprite-layer":
9821
10097
  paintSpriteLayer(painter, node, dctx);
9822
10098
  break;
@@ -9841,6 +10117,7 @@ var init_paintDispatch = __esm({
9841
10117
  init_DrawShape();
9842
10118
  init_DrawText();
9843
10119
  init_DrawGroup();
10120
+ init_DrawSkinnedMesh();
9844
10121
  init_DrawMesh();
9845
10122
  init_DrawSpriteLayer();
9846
10123
  init_DrawShapeLayer();
@@ -11501,7 +11778,6 @@ var init_avl_atom_types = __esm({
11501
11778
  "persist": "data",
11502
11779
  "fetch": "data",
11503
11780
  "emit": "communication",
11504
- "notify": "communication",
11505
11781
  "call-service": "communication",
11506
11782
  "spawn": "lifecycle",
11507
11783
  "despawn": "lifecycle",
@@ -11649,12 +11925,6 @@ function effectIcon(type, x, y, s, color) {
11649
11925
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - s, y1: y, x2: x + s * 0.6, y2: y, stroke: color, strokeWidth: 1.5 }),
11650
11926
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: `${x + s * 0.1},${y - s * 0.5} ${x + s},${y} ${x + s * 0.1},${y + s * 0.5}`, fill: "none", stroke: color, strokeWidth: 1.5, strokeLinejoin: "round" })
11651
11927
  ] });
11652
- case "notify":
11653
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
11654
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: `M${x - s * 0.7},${y + s * 0.3} Q${x - s * 0.7},${y - s} ${x},${y - s} Q${x + s * 0.7},${y - s} ${x + s * 0.7},${y + s * 0.3} Z`, fill: "none", stroke: color, strokeWidth: 1.5 }),
11655
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - s * 0.8, y1: y + s * 0.3, x2: x + s * 0.8, y2: y + s * 0.3, stroke: color, strokeWidth: 1.5 }),
11656
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x, cy: y + s * 0.7, r: s * 0.2, fill: color })
11657
- ] });
11658
11928
  case "call-service":
11659
11929
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
11660
11930
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - s, y1: y - s * 0.3, x2: x + s, y2: y - s * 0.3, stroke: color, strokeWidth: 1.5 }),
@@ -16911,9 +17181,6 @@ function getEffectSummary(effects) {
16911
17181
  case "emit":
16912
17182
  summaries.push(`\u2191 ${effect[1] || "event"}`);
16913
17183
  break;
16914
- case "notify":
16915
- summaries.push(`\u{1F4E7} ${effect[1] || ""}`);
16916
- break;
16917
17184
  case "persist":
16918
17185
  summaries.push(`\u{1F4BE} ${effect[1] || "save"}`);
16919
17186
  break;
@@ -16939,7 +17206,7 @@ function extractOutputsFromTransitions(transitions) {
16939
17206
  t.effects.forEach((effect) => {
16940
17207
  if (Array.isArray(effect)) {
16941
17208
  const op = effect[0];
16942
- if (["emit", "notify", "persist", "navigate", "call-service"].includes(op)) {
17209
+ if (["emit", "persist", "navigate", "call-service"].includes(op)) {
16943
17210
  if (isArraySExpr(effect)) {
16944
17211
  const humanText = formatSExprEffectToDomain(effect);
16945
17212
  outputs.add(humanText);
@@ -19903,6 +20170,21 @@ function shapeDrawnItem(n) {
19903
20170
  if (n.shape !== "rect") return { pos: n.position, id: n.id };
19904
20171
  return { pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotate };
19905
20172
  }
20173
+ function skinnedMeshDrawnItem(n) {
20174
+ const mesh = n.mesh;
20175
+ if (!mesh || n.bones.length === 0) return { pos: n.position, id: n.id };
20176
+ const bindWorld = computeWorldMatrices(n.bones, {});
20177
+ const curWorld = computeWorldMatrices(n.bones, resolveSkinPose(n));
20178
+ const bounds = vertexBounds(skinVertices(mesh, bindWorld, curWorld));
20179
+ if (!bounds) return { pos: n.position, id: n.id };
20180
+ return {
20181
+ pos: { ...n.position, x: n.position.x + bounds.minX, y: n.position.y + bounds.minY },
20182
+ id: n.id,
20183
+ anchor: "top-left",
20184
+ width: bounds.maxX - bounds.minX,
20185
+ height: bounds.maxY - bounds.minY
20186
+ };
20187
+ }
19906
20188
  function collectDrawnItems(nodes) {
19907
20189
  const out = [];
19908
20190
  for (const n of nodes) {
@@ -19913,6 +20195,9 @@ function collectDrawnItems(nodes) {
19913
20195
  case "draw-shape":
19914
20196
  if (isValidScenePos(n.position)) out.push(shapeDrawnItem(n));
19915
20197
  break;
20198
+ case "draw-skinned-mesh":
20199
+ if (isValidScenePos(n.position)) out.push(skinnedMeshDrawnItem(n));
20200
+ break;
19916
20201
  case "draw-text":
19917
20202
  case "draw-group":
19918
20203
  case "draw-mesh":
@@ -19980,6 +20265,8 @@ function rotatePoint(p, center, radians) {
19980
20265
  var init_hitTest = __esm({
19981
20266
  "lib/drawable/hitTest.ts"() {
19982
20267
  init_contract();
20268
+ init_DrawSkinnedMesh();
20269
+ init_skinning();
19983
20270
  }
19984
20271
  });
19985
20272
  function normalizeBackdrop(bg) {
@@ -24113,6 +24400,18 @@ var init_useAuthContext = __esm({
24113
24400
  "hooks/useAuthContext.ts"() {
24114
24401
  }
24115
24402
  });
24403
+ function resolveActiveNavHref(items, currentPath) {
24404
+ let best;
24405
+ for (const item of items) {
24406
+ for (const href of item.children?.length ? [item.href, ...item.children.map((c) => c.href)] : [item.href]) {
24407
+ if (currentPath === href) return href;
24408
+ if (currentPath.startsWith(href + "/") && (!best || href.length > best.length)) {
24409
+ best = href;
24410
+ }
24411
+ }
24412
+ }
24413
+ return best;
24414
+ }
24116
24415
  exports.DashboardLayout = void 0; var NavLink, NavLinkTopnav, NavLinkBottom;
24117
24416
  var init_DashboardLayout = __esm({
24118
24417
  "components/core/templates/DashboardLayout.tsx"() {
@@ -24184,6 +24483,7 @@ var init_DashboardLayout = __esm({
24184
24483
  const location = reactRouterDom.useLocation();
24185
24484
  const ctxPagePath = providers.useCurrentPagePath();
24186
24485
  const activePath = (currentPath || void 0) ?? ctxPagePath ?? location.pathname;
24486
+ const activeHref = React79.useMemo(() => resolveActiveNavHref(navItems, activePath), [navItems, activePath]);
24187
24487
  const { user: authUser, signOut: authSignOut } = useAuthContext();
24188
24488
  const user = userProp || (authUser ? {
24189
24489
  name: authUser.displayName || authUser.email?.split("@")[0] || "User",
@@ -24280,7 +24580,7 @@ var init_DashboardLayout = __esm({
24280
24580
  NavLink,
24281
24581
  {
24282
24582
  item,
24283
- currentPath: activePath,
24583
+ activeHref,
24284
24584
  compact: isRail
24285
24585
  },
24286
24586
  item.href
@@ -24347,7 +24647,7 @@ var init_DashboardLayout = __esm({
24347
24647
  NavLinkTopnav,
24348
24648
  {
24349
24649
  item,
24350
- currentPath: activePath
24650
+ activeHref
24351
24651
  },
24352
24652
  item.href
24353
24653
  ))
@@ -24523,7 +24823,7 @@ var init_DashboardLayout = __esm({
24523
24823
  NavLinkBottom,
24524
24824
  {
24525
24825
  item,
24526
- currentPath: activePath
24826
+ activeHref
24527
24827
  },
24528
24828
  item.href
24529
24829
  )) })
@@ -24537,15 +24837,71 @@ var init_DashboardLayout = __esm({
24537
24837
  exports.DashboardLayout.displayName = "DashboardLayout";
24538
24838
  NavLink = ({
24539
24839
  item,
24540
- currentPath,
24840
+ activeHref,
24541
24841
  compact = false
24542
24842
  }) => {
24543
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
24843
+ const hasChildren = !!item.children?.length;
24844
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
24845
+ const isActive = item.href === activeHref || childActive;
24846
+ const [open, setOpen] = React79.useState(childActive);
24544
24847
  const iconClassName = cn(
24545
24848
  "h-5 w-5",
24546
24849
  isActive ? "text-primary-foreground" : "text-muted-foreground"
24547
24850
  );
24548
24851
  if (compact) {
24852
+ if (hasChildren) {
24853
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative", children: [
24854
+ /* @__PURE__ */ jsxRuntime.jsx(
24855
+ exports.Button,
24856
+ {
24857
+ variant: "ghost",
24858
+ title: item.label,
24859
+ "aria-label": item.label,
24860
+ "aria-expanded": open,
24861
+ className: cn(
24862
+ "flex items-center justify-center w-full px-2 py-2 rounded-lg transition-colors",
24863
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
24864
+ ),
24865
+ onClick: () => setOpen(!open),
24866
+ children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { as: "span", className: "relative inline-flex", children: [
24867
+ item.icon ? typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: item.icon, className: iconClassName }) : /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: iconClassName }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "font-semibold", as: "span", children: item.label.charAt(0).toUpperCase() }),
24868
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(
24869
+ exports.Badge,
24870
+ {
24871
+ variant: isActive ? "primary" : "default",
24872
+ size: "sm",
24873
+ className: "absolute -top-2 -right-2 px-1 py-0 text-[10px] leading-4",
24874
+ children: item.badge
24875
+ }
24876
+ )
24877
+ ] })
24878
+ }
24879
+ ),
24880
+ open && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
24881
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
24882
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute left-full top-0 ml-2 w-48 bg-card dark:bg-card rounded-lg shadow-lg border border-border dark:border-border py-1 z-30", children: item.children.map((child) => {
24883
+ const childIsActive = child.href === activeHref;
24884
+ return /* @__PURE__ */ jsxRuntime.jsxs(
24885
+ reactRouterDom.Link,
24886
+ {
24887
+ to: child.href,
24888
+ onClick: () => setOpen(false),
24889
+ className: cn(
24890
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
24891
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
24892
+ ),
24893
+ children: [
24894
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(child.icon, { className: "h-4 w-4" })),
24895
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
24896
+ child.badge && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
24897
+ ]
24898
+ },
24899
+ child.href
24900
+ );
24901
+ }) })
24902
+ ] })
24903
+ ] });
24904
+ }
24549
24905
  return /* @__PURE__ */ jsxRuntime.jsx(
24550
24906
  reactRouterDom.Link,
24551
24907
  {
@@ -24571,6 +24927,78 @@ var init_DashboardLayout = __esm({
24571
24927
  }
24572
24928
  );
24573
24929
  }
24930
+ if (hasChildren) {
24931
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { children: [
24932
+ /* @__PURE__ */ jsxRuntime.jsxs(
24933
+ exports.Button,
24934
+ {
24935
+ variant: "ghost",
24936
+ "aria-expanded": open,
24937
+ className: cn(
24938
+ "flex items-center gap-3 w-full px-3 py-2 rounded-lg text-sm font-medium transition-colors",
24939
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
24940
+ ),
24941
+ onClick: () => setOpen(!open),
24942
+ children: [
24943
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: item.icon, className: iconClassName }) : /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: iconClassName })),
24944
+ /* @__PURE__ */ jsxRuntime.jsx(
24945
+ exports.Typography,
24946
+ {
24947
+ variant: "small",
24948
+ color: isActive ? "inherit" : "primary",
24949
+ className: "flex-1 text-left",
24950
+ as: "span",
24951
+ children: item.label
24952
+ }
24953
+ ),
24954
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
24955
+ /* @__PURE__ */ jsxRuntime.jsx(
24956
+ exports.Icon,
24957
+ {
24958
+ name: "chevron-down",
24959
+ className: cn("h-4 w-4 shrink-0 transition-transform", open && "rotate-180")
24960
+ }
24961
+ )
24962
+ ]
24963
+ }
24964
+ ),
24965
+ open && /* @__PURE__ */ jsxRuntime.jsx(
24966
+ exports.VStack,
24967
+ {
24968
+ gap: "none",
24969
+ className: "mt-1 ml-4 pl-4 border-l border-border dark:border-border space-y-1",
24970
+ children: item.children.map((child) => {
24971
+ const childIsActive = child.href === activeHref;
24972
+ return /* @__PURE__ */ jsxRuntime.jsxs(
24973
+ reactRouterDom.Link,
24974
+ {
24975
+ to: child.href,
24976
+ className: cn(
24977
+ "flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm transition-colors",
24978
+ childIsActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
24979
+ ),
24980
+ children: [
24981
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(child.icon, { className: "h-4 w-4" })),
24982
+ /* @__PURE__ */ jsxRuntime.jsx(
24983
+ exports.Typography,
24984
+ {
24985
+ variant: "small",
24986
+ color: childIsActive ? "inherit" : "primary",
24987
+ className: "flex-1",
24988
+ as: "span",
24989
+ children: child.label
24990
+ }
24991
+ ),
24992
+ child.badge && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
24993
+ ]
24994
+ },
24995
+ child.href
24996
+ );
24997
+ })
24998
+ }
24999
+ )
25000
+ ] });
25001
+ }
24574
25002
  return /* @__PURE__ */ jsxRuntime.jsxs(
24575
25003
  reactRouterDom.Link,
24576
25004
  {
@@ -24599,9 +25027,63 @@ var init_DashboardLayout = __esm({
24599
25027
  NavLink.displayName = "NavLink";
24600
25028
  NavLinkTopnav = ({
24601
25029
  item,
24602
- currentPath
25030
+ activeHref
24603
25031
  }) => {
24604
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
25032
+ const hasChildren = !!item.children?.length;
25033
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
25034
+ const isActive = item.href === activeHref || childActive;
25035
+ const [open, setOpen] = React79.useState(false);
25036
+ if (hasChildren) {
25037
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative", children: [
25038
+ /* @__PURE__ */ jsxRuntime.jsxs(
25039
+ exports.Button,
25040
+ {
25041
+ variant: "ghost",
25042
+ "aria-expanded": open,
25043
+ className: cn(
25044
+ "flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors whitespace-nowrap",
25045
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
25046
+ ),
25047
+ onClick: () => setOpen(!open),
25048
+ children: [
25049
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: item.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: "h-4 w-4" })),
25050
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "inherit", className: "flex-1", as: "span", children: item.label }),
25051
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
25052
+ /* @__PURE__ */ jsxRuntime.jsx(
25053
+ exports.Icon,
25054
+ {
25055
+ name: "chevron-down",
25056
+ className: cn("h-3.5 w-3.5 shrink-0 transition-transform", open && "rotate-180")
25057
+ }
25058
+ )
25059
+ ]
25060
+ }
25061
+ ),
25062
+ open && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
25063
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
25064
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute left-0 top-full mt-1 w-48 bg-card dark:bg-card rounded-lg shadow-lg border border-border dark:border-border py-1 z-30", children: item.children.map((child) => {
25065
+ const childIsActive = child.href === activeHref;
25066
+ return /* @__PURE__ */ jsxRuntime.jsxs(
25067
+ reactRouterDom.Link,
25068
+ {
25069
+ to: child.href,
25070
+ onClick: () => setOpen(false),
25071
+ className: cn(
25072
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
25073
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
25074
+ ),
25075
+ children: [
25076
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(child.icon, { className: "h-4 w-4" })),
25077
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
25078
+ child.badge && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
25079
+ ]
25080
+ },
25081
+ child.href
25082
+ );
25083
+ }) })
25084
+ ] })
25085
+ ] });
25086
+ }
24605
25087
  return /* @__PURE__ */ jsxRuntime.jsxs(
24606
25088
  reactRouterDom.Link,
24607
25089
  {
@@ -24621,9 +25103,9 @@ var init_DashboardLayout = __esm({
24621
25103
  NavLinkTopnav.displayName = "NavLinkTopnav";
24622
25104
  NavLinkBottom = ({
24623
25105
  item,
24624
- currentPath
25106
+ activeHref
24625
25107
  }) => {
24626
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
25108
+ const isActive = item.href === activeHref;
24627
25109
  const iconClassName = cn(
24628
25110
  "h-5 w-5",
24629
25111
  isActive ? "text-primary" : "text-muted-foreground"
@@ -25562,7 +26044,9 @@ function DataGrid({
25562
26044
  const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
25563
26045
  const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
25564
26046
  const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
25565
- const hasRenderProp = typeof children === "function";
26047
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
26048
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
26049
+ const hasRenderProp = typeof itemRenderer === "function";
25566
26050
  React79.useEffect(() => {
25567
26051
  if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
25568
26052
  const schemaArr = Array.isArray(schemaRenderItem) ? schemaRenderItem : null;
@@ -25636,7 +26120,7 @@ function DataGrid({
25636
26120
  onClick: handleCardClick?.(itemData),
25637
26121
  className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
25638
26122
  children: [
25639
- children(itemData, index),
26123
+ itemRenderer(itemData, index),
25640
26124
  actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { onClick: stopCardClick, className: "absolute top-2 right-2 z-10 opacity-0 group-hover/rowactions:opacity-100 focus-within:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity duration-fast", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: [
25641
26125
  inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
25642
26126
  exports.Button,
@@ -25969,7 +26453,9 @@ function DataList({
25969
26453
  );
25970
26454
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
25971
26455
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
25972
- const hasRenderProp = typeof children === "function";
26456
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
26457
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
26458
+ const hasRenderProp = typeof itemRenderer === "function";
25973
26459
  React79__namespace.default.useEffect(() => {
25974
26460
  const renderItemTypeOf = typeof schemaRenderItem;
25975
26461
  const childrenTypeOf = typeof children;
@@ -26187,7 +26673,7 @@ function DataList({
26187
26673
  const actions = renderItemActions(itemData);
26188
26674
  return wrapDnd(
26189
26675
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { "data-entity-row": true, "data-entity-id": id2, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn("relative group/rowactions", rowClickEvent && "cursor-pointer"), children: [
26190
- children(itemData, index),
26676
+ itemRenderer(itemData, index),
26191
26677
  actions && /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "absolute top-2 right-2 z-10 opacity-0 group-hover/rowactions:opacity-100 focus-within:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity duration-fast", children: [
26192
26678
  /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5 [@media(pointer:coarse)]:hidden", children: actions }),
26193
26679
  /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "hidden [@media(pointer:coarse)]:block rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -31540,7 +32026,7 @@ var init_DialogueBubble = __esm({
31540
32026
  init_Typography();
31541
32027
  init_GameIcon();
31542
32028
  DEFAULT_PORTRAIT = {
31543
- url: "https://almadar-kflow-assets.web.app/shared/characters/archetypes/04_hero.png",
32029
+ url: "https://almadar-kflow-assets.web.app/shared/ui-winter-ski-board/default/units/hero/hero.png",
31544
32030
  role: "effect",
31545
32031
  category: "character"
31546
32032
  };
@@ -32532,7 +33018,7 @@ var init_StateIndicator = __esm({
32532
33018
  init_cn();
32533
33019
  init_GameIcon();
32534
33020
  DEFAULT_ASSET_URL = {
32535
- url: "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png",
33021
+ url: "https://almadar-kflow-assets.web.app/shared/ui-topdown-dungeon-board/default/features/chest/chest.png",
32536
33022
  role: "ui",
32537
33023
  category: "state"
32538
33024
  };
@@ -35233,13 +35719,13 @@ var init_MapView = __esm({
35233
35719
  shadowSize: [41, 41]
35234
35720
  });
35235
35721
  L.Marker.prototype.options.icon = defaultIcon;
35236
- const { useEffect: useEffect73, useRef: useRef70, useCallback: useCallback110, useState: useState110 } = React79__namespace.default;
35722
+ const { useEffect: useEffect72, useRef: useRef70, useCallback: useCallback109, useState: useState109 } = React79__namespace.default;
35237
35723
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
35238
35724
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
35239
35725
  function MapUpdater({ centerLat, centerLng, zoom }) {
35240
35726
  const map = useMap();
35241
35727
  const prevRef = useRef70({ centerLat, centerLng, zoom });
35242
- useEffect73(() => {
35728
+ useEffect72(() => {
35243
35729
  const prev = prevRef.current;
35244
35730
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
35245
35731
  map.setView([centerLat, centerLng], zoom);
@@ -35250,7 +35736,7 @@ var init_MapView = __esm({
35250
35736
  }
35251
35737
  function MapClickHandler({ onMapClick }) {
35252
35738
  const map = useMap();
35253
- useEffect73(() => {
35739
+ useEffect72(() => {
35254
35740
  if (!onMapClick) return;
35255
35741
  const handler = (e) => {
35256
35742
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -35278,8 +35764,8 @@ var init_MapView = __esm({
35278
35764
  showAttribution = true
35279
35765
  }) {
35280
35766
  const eventBus = useEventBus2();
35281
- const [clickedPosition, setClickedPosition] = useState110(null);
35282
- const handleMapClick = useCallback110((lat, lng) => {
35767
+ const [clickedPosition, setClickedPosition] = useState109(null);
35768
+ const handleMapClick = useCallback109((lat, lng) => {
35283
35769
  if (showClickedPin) {
35284
35770
  setClickedPosition({ lat, lng });
35285
35771
  }
@@ -35288,7 +35774,7 @@ var init_MapView = __esm({
35288
35774
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
35289
35775
  }
35290
35776
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
35291
- const handleMarkerClick = useCallback110((marker) => {
35777
+ const handleMarkerClick = useCallback109((marker) => {
35292
35778
  onMarkerClick?.(marker);
35293
35779
  if (markerClickEvent) {
35294
35780
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -36149,7 +36635,7 @@ function TableView({
36149
36635
  columns,
36150
36636
  fields,
36151
36637
  itemActions,
36152
- maxInlineActions: _maxInlineActions,
36638
+ maxInlineActions,
36153
36639
  itemClickEvent = "",
36154
36640
  selectable = false,
36155
36641
  selectEvent,
@@ -36182,6 +36668,12 @@ function TableView({
36182
36668
  const [localSelected, setLocalSelected] = React79__namespace.default.useState(/* @__PURE__ */ new Set());
36183
36669
  const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
36184
36670
  const actionDefs = Array.isArray(itemActions) ? itemActions : [];
36671
+ const inlineActions = maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : [];
36672
+ const overflowActions = maxInlineActions != null ? actionDefs.slice(maxInlineActions) : actionDefs;
36673
+ const fireAction = (action, row) => eventBus.emit(`UI:${action.event}`, {
36674
+ id: row.id,
36675
+ row
36676
+ });
36185
36677
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
36186
36678
  const dnd = useDataDnd({
36187
36679
  items: allDataRaw,
@@ -36357,7 +36849,7 @@ function TableView({
36357
36849
  }
36358
36850
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
36359
36851
  }),
36360
- hasActions && /* @__PURE__ */ jsxRuntime.jsx(
36852
+ hasActions && /* @__PURE__ */ jsxRuntime.jsxs(
36361
36853
  exports.HStack,
36362
36854
  {
36363
36855
  gap: "xs",
@@ -36371,23 +36863,38 @@ function TableView({
36371
36863
  "border-l border-[var(--color-border)]",
36372
36864
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
36373
36865
  ),
36374
- children: /* @__PURE__ */ jsxRuntime.jsx(
36375
- exports.Menu,
36376
- {
36377
- position: "bottom-end",
36378
- trigger: /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "more-horizontal", size: "xs" }) }),
36379
- items: actionDefs.map((action) => ({
36380
- label: action.label,
36381
- icon: action.icon,
36382
- event: action.event,
36383
- variant: action.variant === "danger" ? "danger" : "default",
36384
- onClick: () => eventBus.emit(`UI:${action.event}`, {
36385
- id: row.id,
36386
- row
36387
- })
36388
- }))
36389
- }
36390
- )
36866
+ children: [
36867
+ inlineActions.map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
36868
+ exports.Button,
36869
+ {
36870
+ variant: action.variant ?? "ghost",
36871
+ size: "sm",
36872
+ onClick: () => fireAction(action, row),
36873
+ "data-testid": `action-${action.event}`,
36874
+ "data-row-id": String(row.id),
36875
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
36876
+ children: [
36877
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
36878
+ action.label
36879
+ ]
36880
+ },
36881
+ action.event
36882
+ )),
36883
+ overflowActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
36884
+ exports.Menu,
36885
+ {
36886
+ position: "bottom-end",
36887
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "more-horizontal", size: "xs" }) }),
36888
+ items: overflowActions.map((action) => ({
36889
+ label: action.label,
36890
+ icon: action.icon,
36891
+ event: action.event,
36892
+ variant: action.variant === "danger" ? "danger" : "default",
36893
+ onClick: () => fireAction(action, row)
36894
+ }))
36895
+ }
36896
+ )
36897
+ ]
36391
36898
  }
36392
36899
  )
36393
36900
  ]
@@ -52448,6 +52955,7 @@ var init_component_registry_generated = __esm({
52448
52955
  init_DrawMesh();
52449
52956
  init_DrawShape();
52450
52957
  init_DrawShapeLayer();
52958
+ init_DrawSkinnedMesh();
52451
52959
  init_DrawSprite();
52452
52960
  init_DrawSpriteLayer();
52453
52961
  init_DrawText();
@@ -52726,6 +53234,7 @@ var init_component_registry_generated = __esm({
52726
53234
  "DrawMesh": DrawMesh,
52727
53235
  "DrawShape": DrawShape,
52728
53236
  "DrawShapeLayer": DrawShapeLayer,
53237
+ "DrawSkinnedMesh": DrawSkinnedMesh,
52729
53238
  "DrawSprite": DrawSprite,
52730
53239
  "DrawSpriteLayer": DrawSpriteLayer,
52731
53240
  "DrawText": DrawText,
@@ -53054,7 +53563,7 @@ function renderContainedPortal(t, slot, content, onDismiss) {
53054
53563
  const slotId = `slot-${slot}`;
53055
53564
  switch (slot) {
53056
53565
  case "modal":
53057
- if (SELF_OVERLAY_PATTERNS.has(content.pattern)) {
53566
+ if (patterns.SELF_OVERLAY_PATTERN_TYPES.has(content.pattern)) {
53058
53567
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { id: slotId, className: "contents", children: slotContent });
53059
53568
  }
53060
53569
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -53397,7 +53906,7 @@ function CompiledPortal({ slot, className, pattern, sourceTrait, children }) {
53397
53906
  children
53398
53907
  }
53399
53908
  );
53400
- wrapper = pattern !== void 0 && SELF_OVERLAY_PATTERNS.has(pattern) ? innerContent : /* @__PURE__ */ jsxRuntime.jsx(exports.Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
53909
+ wrapper = pattern !== void 0 && patterns.SELF_OVERLAY_PATTERN_TYPES.has(pattern) ? innerContent : /* @__PURE__ */ jsxRuntime.jsx(exports.Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
53401
53910
  break;
53402
53911
  }
53403
53912
  case "drawer":
@@ -53457,7 +53966,7 @@ function SlotPortal({
53457
53966
  let wrapper;
53458
53967
  switch (slot) {
53459
53968
  case "modal":
53460
- wrapper = SELF_OVERLAY_PATTERNS.has(content.pattern) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsxRuntime.jsx(
53969
+ wrapper = patterns.SELF_OVERLAY_PATTERN_TYPES.has(content.pattern) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsxRuntime.jsx(
53461
53970
  exports.Modal,
53462
53971
  {
53463
53972
  isOpen: true,
@@ -53964,7 +54473,7 @@ function UISlotRenderer({
53964
54473
  }
53965
54474
  return wrapped;
53966
54475
  }
53967
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
54476
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
53968
54477
  var init_UISlotRenderer = __esm({
53969
54478
  "components/core/organisms/UISlotRenderer.tsx"() {
53970
54479
  "use client";
@@ -53995,7 +54504,6 @@ var init_UISlotRenderer = __esm({
53995
54504
  modal: "form",
53996
54505
  drawer: "form"
53997
54506
  };
53998
- SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
53999
54507
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
54000
54508
  "logo",
54001
54509
  "master",
@@ -54188,66 +54696,6 @@ init_UISlotRenderer();
54188
54696
  init_ModalSlot();
54189
54697
  init_DrawerSlot();
54190
54698
  init_ToastSlot();
54191
-
54192
- // components/core/organisms/NotifyListener.tsx
54193
- init_useEventBus();
54194
- init_Toast();
54195
- init_Box();
54196
- var nextId = 0;
54197
- function NotifyListener() {
54198
- const eventBus = useEventBus();
54199
- const [items, setItems] = React79.useState([]);
54200
- const [portalRoot, setPortalRoot] = React79.useState(null);
54201
- React79.useEffect(() => {
54202
- let root = document.getElementById("ui-notify-portal-root");
54203
- if (!root) {
54204
- root = document.createElement("div");
54205
- root.id = "ui-notify-portal-root";
54206
- document.body.appendChild(root);
54207
- }
54208
- setPortalRoot(root);
54209
- }, []);
54210
- const handleDismiss = React79.useCallback((id) => {
54211
- setItems((prev) => prev.filter((item) => item.id !== id));
54212
- }, []);
54213
- React79.useEffect(() => {
54214
- const unsubscribe = eventBus.on("UI:NOTIFY", (event) => {
54215
- const payload = event.payload;
54216
- const message = payload && typeof payload.message === "string" ? payload.message : "Notification";
54217
- const severityMap = {
54218
- success: "success",
54219
- error: "error",
54220
- warning: "warning",
54221
- info: "info"
54222
- };
54223
- const variant = payload && String(payload.severity) in severityMap ? severityMap[String(payload.severity)] : "info";
54224
- const id = ++nextId;
54225
- setItems((prev) => {
54226
- const last = prev[prev.length - 1];
54227
- if (last && last.message === message && last.variant === variant) return prev;
54228
- return [...prev, { id, message, variant }].slice(-4);
54229
- });
54230
- });
54231
- return unsubscribe;
54232
- }, [eventBus]);
54233
- if (!portalRoot || items.length === 0) return null;
54234
- return reactDom.createPortal(
54235
- /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "fixed bottom-4 right-4 z-50 pointer-events-auto flex flex-col gap-2", children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
54236
- exports.Toast,
54237
- {
54238
- variant: item.variant,
54239
- message: item.message,
54240
- duration: 5e3,
54241
- onDismiss: () => handleDismiss(item.id)
54242
- },
54243
- item.id
54244
- )) }),
54245
- portalRoot
54246
- );
54247
- }
54248
- NotifyListener.displayName = "NotifyListener";
54249
-
54250
- // components/core/organisms/index.ts
54251
54699
  init_Timeline();
54252
54700
  init_MediaGallery();
54253
54701
 
@@ -56643,7 +57091,6 @@ exports.I18nProvider = I18nProvider;
56643
57091
  exports.LearningScene3D = LearningScene3D;
56644
57092
  exports.MapView = MapView;
56645
57093
  exports.MasterDetail = MasterDetail;
56646
- exports.NotifyListener = NotifyListener;
56647
57094
  exports.OrbitalStateMachineView = exports.StateMachineView;
56648
57095
  exports.RuntimeDebugger = RuntimeDebugger;
56649
57096
  exports.ScoreDisplay = ScoreDisplay;