@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
@@ -228,7 +228,6 @@ var init_avl_atom_types = __esm({
228
228
  "persist": "data",
229
229
  "fetch": "data",
230
230
  "emit": "communication",
231
- "notify": "communication",
232
231
  "call-service": "communication",
233
232
  "spawn": "lifecycle",
234
233
  "despawn": "lifecycle",
@@ -516,12 +515,6 @@ function effectIcon(type, x, y, s, color) {
516
515
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - s, y1: y, x2: x + s * 0.6, y2: y, stroke: color, strokeWidth: 1.5 }),
517
516
  /* @__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" })
518
517
  ] });
519
- case "notify":
520
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
521
- /* @__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 }),
522
- /* @__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 }),
523
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x, cy: y + s * 0.7, r: s * 0.2, fill: color })
524
- ] });
525
518
  case "call-service":
526
519
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
527
520
  /* @__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 }),
@@ -11599,7 +11592,7 @@ var init_DialogueBubble = __esm({
11599
11592
  init_Typography();
11600
11593
  init_GameIcon();
11601
11594
  DEFAULT_PORTRAIT = {
11602
- url: "https://almadar-kflow-assets.web.app/shared/characters/archetypes/04_hero.png",
11595
+ url: "https://almadar-kflow-assets.web.app/shared/ui-winter-ski-board/default/units/hero/hero.png",
11603
11596
  role: "effect",
11604
11597
  category: "character"
11605
11598
  };
@@ -12923,6 +12916,14 @@ function createWebPainter(ctx, onAssetLoad) {
12923
12916
  ctx.drawImage(img, dest.x, dest.y, dw, dh);
12924
12917
  }
12925
12918
  },
12919
+ blitTransformed(tex, src, affine) {
12920
+ const img = imageByHandle.get(tex);
12921
+ if (!img) return;
12922
+ ctx.save();
12923
+ ctx.transform(affine[0], affine[1], affine[2], affine[3], affine[4], affine[5]);
12924
+ ctx.drawImage(img, src.x, src.y, src.w, src.h, src.x, src.y, src.w, src.h);
12925
+ ctx.restore();
12926
+ },
12926
12927
  fillRect(x, y, w, h, style) {
12927
12928
  ctx.fillStyle = toCanvasStyle(style);
12928
12929
  ctx.fillRect(x, y, w, h);
@@ -13429,6 +13430,271 @@ var init_DrawGroup = __esm({
13429
13430
  init_registry();
13430
13431
  }
13431
13432
  });
13433
+
13434
+ // lib/skinning.ts
13435
+ function composeAffine(a, b) {
13436
+ return [
13437
+ a[0] * b[0] + a[2] * b[1],
13438
+ a[1] * b[0] + a[3] * b[1],
13439
+ a[0] * b[2] + a[2] * b[3],
13440
+ a[1] * b[2] + a[3] * b[3],
13441
+ a[0] * b[4] + a[2] * b[5] + a[4],
13442
+ a[1] * b[4] + a[3] * b[5] + a[5]
13443
+ ];
13444
+ }
13445
+ function invertAffine(m) {
13446
+ const det = m[0] * m[3] - m[1] * m[2];
13447
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return IDENTITY;
13448
+ const inv = 1 / det;
13449
+ return [
13450
+ m[3] * inv,
13451
+ -m[1] * inv,
13452
+ -m[2] * inv,
13453
+ m[0] * inv,
13454
+ (m[2] * m[5] - m[3] * m[4]) * inv,
13455
+ (m[1] * m[4] - m[0] * m[5]) * inv
13456
+ ];
13457
+ }
13458
+ function applyAffine(m, x, y) {
13459
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
13460
+ }
13461
+ function rotationAbout(pivot, degrees) {
13462
+ const r2 = degrees * Math.PI / 180;
13463
+ const cos = Math.cos(r2);
13464
+ const sin = Math.sin(r2);
13465
+ const [px, py] = pivot;
13466
+ return [cos, sin, -sin, cos, px - cos * px + sin * py, py - sin * px - cos * py];
13467
+ }
13468
+ function computeWorldMatrices(bones, pose) {
13469
+ const indexByName = /* @__PURE__ */ new Map();
13470
+ bones.forEach((b, i) => indexByName.set(b.name, i));
13471
+ const world = new Array(bones.length);
13472
+ const visiting = /* @__PURE__ */ new Set();
13473
+ const resolve = (i) => {
13474
+ const cached = world[i];
13475
+ if (cached) return cached;
13476
+ if (visiting.has(i)) return IDENTITY;
13477
+ visiting.add(i);
13478
+ const b = bones[i];
13479
+ const parentIndex = b.parent === null ? void 0 : indexByName.get(b.parent);
13480
+ const parentWorld = parentIndex === void 0 ? IDENTITY : resolve(parentIndex);
13481
+ const local = rotationAbout(b.pivot, pose[b.name] ?? 0);
13482
+ const m = composeAffine(parentWorld, local);
13483
+ visiting.delete(i);
13484
+ world[i] = m;
13485
+ return m;
13486
+ };
13487
+ return bones.map((_, i) => resolve(i));
13488
+ }
13489
+ function skinVertices(mesh, bindWorld, curWorld) {
13490
+ const skinMats = /* @__PURE__ */ new Map();
13491
+ const skinMat = (bone) => {
13492
+ let m = skinMats.get(bone);
13493
+ if (!m) {
13494
+ m = composeAffine(curWorld[bone] ?? IDENTITY, invertAffine(bindWorld[bone] ?? IDENTITY));
13495
+ skinMats.set(bone, m);
13496
+ }
13497
+ return m;
13498
+ };
13499
+ return mesh.vertices.map((v) => {
13500
+ if (v.weights.length === 0) return [v.x, v.y];
13501
+ let x = 0;
13502
+ let y = 0;
13503
+ for (const { bone, w } of v.weights) {
13504
+ const [bx, by] = applyAffine(skinMat(bone), v.x, v.y);
13505
+ x += w * bx;
13506
+ y += w * by;
13507
+ }
13508
+ return [x, y];
13509
+ });
13510
+ }
13511
+ function triangleAffine(src, dst) {
13512
+ const [s0, s1, s2] = src;
13513
+ const det = s0[0] * (s1[1] - s2[1]) + s1[0] * (s2[1] - s0[1]) + s2[0] * (s0[1] - s1[1]);
13514
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return null;
13515
+ const [d0, d1, d2] = dst;
13516
+ const a = (d0[0] * (s1[1] - s2[1]) + d1[0] * (s2[1] - s0[1]) + d2[0] * (s0[1] - s1[1])) / det;
13517
+ const c = (d0[0] * (s2[0] - s1[0]) + d1[0] * (s0[0] - s2[0]) + d2[0] * (s1[0] - s0[0])) / det;
13518
+ 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;
13519
+ const b = (d0[1] * (s1[1] - s2[1]) + d1[1] * (s2[1] - s0[1]) + d2[1] * (s0[1] - s1[1])) / det;
13520
+ const d = (d0[1] * (s2[0] - s1[0]) + d1[1] * (s0[0] - s2[0]) + d2[1] * (s1[0] - s0[0])) / det;
13521
+ 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;
13522
+ return [a, b, c, d, e, f3];
13523
+ }
13524
+ function isRigBundle(json) {
13525
+ return json.mesh !== void 0 || json.bones !== void 0;
13526
+ }
13527
+ function getRigBundle(url, onReady) {
13528
+ if (meshCache.has(url)) return meshCache.get(url) ?? void 0;
13529
+ meshCache.set(url, void 0);
13530
+ fetch(url).then((r2) => {
13531
+ if (!r2.ok) throw new Error(`rigbundle fetch failed: HTTP ${r2.status}`);
13532
+ return r2.json();
13533
+ }).then((json) => {
13534
+ const ok = isRigBundle(json) || Array.isArray(json.vertices);
13535
+ if (!ok) throw new Error("rigbundle payload is neither a SkinMesh nor a rigbundle");
13536
+ meshCache.set(url, json);
13537
+ onReady();
13538
+ }).catch(() => {
13539
+ meshCache.set(url, null);
13540
+ onReady();
13541
+ });
13542
+ return void 0;
13543
+ }
13544
+ function skinMeshFailed(url) {
13545
+ return meshCache.get(url) === null;
13546
+ }
13547
+ function vertexBounds(points) {
13548
+ if (points.length === 0) return null;
13549
+ let minX = Infinity;
13550
+ let minY = Infinity;
13551
+ let maxX = -Infinity;
13552
+ let maxY = -Infinity;
13553
+ for (const [x, y] of points) {
13554
+ if (x < minX) minX = x;
13555
+ if (y < minY) minY = y;
13556
+ if (x > maxX) maxX = x;
13557
+ if (y > maxY) maxY = y;
13558
+ }
13559
+ return { minX, minY, maxX, maxY };
13560
+ }
13561
+ var IDENTITY, meshCache;
13562
+ var init_skinning = __esm({
13563
+ "lib/skinning.ts"() {
13564
+ IDENTITY = [1, 0, 0, 1, 0, 0];
13565
+ meshCache = /* @__PURE__ */ new Map();
13566
+ }
13567
+ });
13568
+ function warnMissingOnce2(reason, node) {
13569
+ const key = `${reason}:${node.asset.url}:${String(node.meshUrl)}`;
13570
+ if (loggedMissing2.has(key)) return;
13571
+ loggedMissing2.add(key);
13572
+ skinnedLog.warn("draw-skinned-mesh unresolvable \u2014 painting fallback", { reason, url: node.asset.url, meshUrl: node.meshUrl });
13573
+ }
13574
+ function dominantBone(v) {
13575
+ let best = -1;
13576
+ let bestW = 0;
13577
+ for (const { bone, w } of v.weights) {
13578
+ if (w > bestW) {
13579
+ bestW = w;
13580
+ best = bone;
13581
+ }
13582
+ }
13583
+ return best;
13584
+ }
13585
+ function resolveSkinPose(node, clip) {
13586
+ const effectiveClip = clip ?? node.clip;
13587
+ if (effectiveClip && node.frame !== void 0 && effectiveClip.frames.length > 0) {
13588
+ return effectiveClip.frames[node.frame % effectiveClip.frames.length];
13589
+ }
13590
+ return node.pose ?? {};
13591
+ }
13592
+ function resolveRig(node, dctx) {
13593
+ const fetched = node.meshUrl ? getRigBundle(node.meshUrl, dctx.invalidate) : void 0;
13594
+ if (node.meshUrl && fetched === void 0) return void 0;
13595
+ const rb = fetched && isRigBundle(fetched) ? fetched : void 0;
13596
+ const mesh = node.mesh ?? rb?.mesh ?? (!rb ? fetched : void 0);
13597
+ if (!mesh) return void 0;
13598
+ const bones = node.bones.length > 0 ? node.bones : rb?.bones ?? [];
13599
+ if (bones.length === 0) return void 0;
13600
+ const clip = node.clip ?? (node.clipName ? rb?.clips?.[node.clipName] : void 0);
13601
+ return { mesh, bones, clip };
13602
+ }
13603
+ function meshHasPixelUVs(mesh) {
13604
+ const cached = pixelUVByMesh.get(mesh);
13605
+ if (cached !== void 0) return cached;
13606
+ let pixel = false;
13607
+ for (const v of mesh.vertices) {
13608
+ if (v.u < 0 || v.u > 1 || v.v < 0 || v.v > 1) {
13609
+ pixel = true;
13610
+ break;
13611
+ }
13612
+ }
13613
+ pixelUVByMesh.set(mesh, pixel);
13614
+ return pixel;
13615
+ }
13616
+ function paintMeshPass(painter, node, bones, mesh, tex, dctx, pose, opacity) {
13617
+ const bindWorld = computeWorldMatrices(bones, {});
13618
+ const curWorld = computeWorldMatrices(bones, pose);
13619
+ const skinned = skinVertices(mesh, bindWorld, curWorld);
13620
+ const origin = dctx.projector.project(node.position);
13621
+ const tw = dctx.projector.tileWidth;
13622
+ painter.save();
13623
+ if (opacity !== 1) painter.setAlpha(opacity);
13624
+ for (const [i0, i1, i2] of mesh.triangles) {
13625
+ const v0 = mesh.vertices[i0];
13626
+ const v1 = mesh.vertices[i1];
13627
+ const v2 = mesh.vertices[i2];
13628
+ const p0 = skinned[i0];
13629
+ const p1 = skinned[i1];
13630
+ const p2 = skinned[i2];
13631
+ if (!v0 || !v1 || !v2 || !p0 || !p1 || !p2) continue;
13632
+ const dst = [
13633
+ [origin.x + p0[0] * tw, origin.y + p0[1] * tw],
13634
+ [origin.x + p1[0] * tw, origin.y + p1[1] * tw],
13635
+ [origin.x + p2[0] * tw, origin.y + p2[1] * tw]
13636
+ ];
13637
+ if (node.weightsOverlay || !tex) {
13638
+ if (!node.weightsOverlay) continue;
13639
+ const bone = dominantBone(v0);
13640
+ painter.fillPoly(
13641
+ dst.map(([x, y]) => ({ x, y })),
13642
+ WEIGHT_PALETTE[(bone % WEIGHT_PALETTE.length + WEIGHT_PALETTE.length) % WEIGHT_PALETTE.length]
13643
+ );
13644
+ continue;
13645
+ }
13646
+ const pixelUV = meshHasPixelUVs(mesh);
13647
+ const uv = pixelUV ? [[v0.u, v0.v], [v1.u, v1.v], [v2.u, v2.v]] : [
13648
+ [v0.u * tex.width, v0.v * tex.height],
13649
+ [v1.u * tex.width, v1.v * tex.height],
13650
+ [v2.u * tex.width, v2.v * tex.height]
13651
+ ];
13652
+ const affine = triangleAffine(uv, dst);
13653
+ if (!affine) continue;
13654
+ painter.save();
13655
+ painter.clipPath(`M ${dst[0][0]} ${dst[0][1]} L ${dst[1][0]} ${dst[1][1]} L ${dst[2][0]} ${dst[2][1]} Z`);
13656
+ painter.blitTransformed(tex, { x: 0, y: 0, w: tex.width, h: tex.height }, affine);
13657
+ painter.restore();
13658
+ }
13659
+ painter.restore();
13660
+ }
13661
+ function DrawSkinnedMesh(props) {
13662
+ const register = React96.useContext(DrawableRegistryContext);
13663
+ if (register) register({ ...props, type: "draw-skinned-mesh" });
13664
+ return null;
13665
+ }
13666
+ var skinnedLog, loggedMissing2, WEIGHT_PALETTE, pixelUVByMesh, paintSkinnedMesh;
13667
+ var init_DrawSkinnedMesh = __esm({
13668
+ "components/game/atoms/DrawSkinnedMesh.tsx"() {
13669
+ "use client";
13670
+ init_imageCache();
13671
+ init_contract();
13672
+ init_registry();
13673
+ init_skinning();
13674
+ skinnedLog = logger.createLogger("almadar:ui:draw-skinned-mesh");
13675
+ loggedMissing2 = /* @__PURE__ */ new Set();
13676
+ WEIGHT_PALETTE = Array.from({ length: 10 }, (_, i) => `hsl(${i * 36 % 360}, 70%, 55%)`);
13677
+ pixelUVByMesh = /* @__PURE__ */ new WeakMap();
13678
+ paintSkinnedMesh = (painter, node, dctx) => {
13679
+ if (!isValidScenePos(node.position)) return;
13680
+ const rig = resolveRig(node, dctx);
13681
+ if (!rig) {
13682
+ if (node.meshUrl && skinMeshFailed(node.meshUrl)) warnMissingOnce2("mesh-failed", node);
13683
+ else if (!node.mesh && !node.meshUrl) warnMissingOnce2("mesh-missing", node);
13684
+ return;
13685
+ }
13686
+ const tex = node.weightsOverlay ? null : painter.resolveTexture(node.asset.url);
13687
+ if (!node.weightsOverlay && !tex) {
13688
+ if (getImageStatus(node.asset.url) === "failed") warnMissingOnce2("texture-failed", node);
13689
+ return;
13690
+ }
13691
+ if (node.ghost && rig.clip && rig.clip.frames.length > 0) {
13692
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, rig.clip.frames[node.ghost.frame % rig.clip.frames.length], node.ghost.opacity);
13693
+ }
13694
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, resolveSkinPose(node, rig.clip), node.opacity ?? 1);
13695
+ };
13696
+ }
13697
+ });
13432
13698
  function applyMeshAnimation(node, timeMs) {
13433
13699
  const anim = node.animation;
13434
13700
  if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
@@ -13753,6 +14019,9 @@ function paintDrawable(painter, node, dctx) {
13753
14019
  case "draw-mesh":
13754
14020
  warnUnsupported2d("draw-mesh");
13755
14021
  break;
14022
+ case "draw-skinned-mesh":
14023
+ paintSkinnedMesh(painter, node, dctx);
14024
+ break;
13756
14025
  case "draw-sprite-layer":
13757
14026
  paintSpriteLayer(painter, node, dctx);
13758
14027
  break;
@@ -13777,6 +14046,7 @@ var init_paintDispatch = __esm({
13777
14046
  init_DrawShape();
13778
14047
  init_DrawText();
13779
14048
  init_DrawGroup();
14049
+ init_DrawSkinnedMesh();
13780
14050
  init_DrawMesh();
13781
14051
  init_DrawSpriteLayer();
13782
14052
  init_DrawShapeLayer();
@@ -13797,6 +14067,21 @@ function shapeDrawnItem(n) {
13797
14067
  if (n.shape !== "rect") return { pos: n.position, id: n.id };
13798
14068
  return { pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotate };
13799
14069
  }
14070
+ function skinnedMeshDrawnItem(n) {
14071
+ const mesh = n.mesh;
14072
+ if (!mesh || n.bones.length === 0) return { pos: n.position, id: n.id };
14073
+ const bindWorld = computeWorldMatrices(n.bones, {});
14074
+ const curWorld = computeWorldMatrices(n.bones, resolveSkinPose(n));
14075
+ const bounds = vertexBounds(skinVertices(mesh, bindWorld, curWorld));
14076
+ if (!bounds) return { pos: n.position, id: n.id };
14077
+ return {
14078
+ pos: { ...n.position, x: n.position.x + bounds.minX, y: n.position.y + bounds.minY },
14079
+ id: n.id,
14080
+ anchor: "top-left",
14081
+ width: bounds.maxX - bounds.minX,
14082
+ height: bounds.maxY - bounds.minY
14083
+ };
14084
+ }
13800
14085
  function collectDrawnItems(nodes) {
13801
14086
  const out = [];
13802
14087
  for (const n of nodes) {
@@ -13807,6 +14092,9 @@ function collectDrawnItems(nodes) {
13807
14092
  case "draw-shape":
13808
14093
  if (isValidScenePos(n.position)) out.push(shapeDrawnItem(n));
13809
14094
  break;
14095
+ case "draw-skinned-mesh":
14096
+ if (isValidScenePos(n.position)) out.push(skinnedMeshDrawnItem(n));
14097
+ break;
13810
14098
  case "draw-text":
13811
14099
  case "draw-group":
13812
14100
  case "draw-mesh":
@@ -13874,6 +14162,8 @@ function rotatePoint(p, center, radians) {
13874
14162
  var init_hitTest = __esm({
13875
14163
  "lib/drawable/hitTest.ts"() {
13876
14164
  init_contract();
14165
+ init_DrawSkinnedMesh();
14166
+ init_skinning();
13877
14167
  }
13878
14168
  });
13879
14169
  function normalizeBackdrop(bg) {
@@ -15063,7 +15353,7 @@ var init_StateIndicator = __esm({
15063
15353
  init_cn();
15064
15354
  init_GameIcon();
15065
15355
  DEFAULT_ASSET_URL = {
15066
- url: "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png",
15356
+ url: "https://almadar-kflow-assets.web.app/shared/ui-topdown-dungeon-board/default/features/chest/chest.png",
15067
15357
  role: "ui",
15068
15358
  category: "state"
15069
15359
  };
@@ -23623,9 +23913,6 @@ function getEffectSummary(effects) {
23623
23913
  case "emit":
23624
23914
  summaries.push(`\u2191 ${effect[1] || "event"}`);
23625
23915
  break;
23626
- case "notify":
23627
- summaries.push(`\u{1F4E7} ${effect[1] || ""}`);
23628
- break;
23629
23916
  case "persist":
23630
23917
  summaries.push(`\u{1F4BE} ${effect[1] || "save"}`);
23631
23918
  break;
@@ -23651,7 +23938,7 @@ function extractOutputsFromTransitions(transitions) {
23651
23938
  t.effects.forEach((effect) => {
23652
23939
  if (Array.isArray(effect)) {
23653
23940
  const op = effect[0];
23654
- if (["emit", "notify", "persist", "navigate", "call-service"].includes(op)) {
23941
+ if (["emit", "persist", "navigate", "call-service"].includes(op)) {
23655
23942
  if (isArraySExpr(effect)) {
23656
23943
  const humanText = formatSExprEffectToDomain(effect);
23657
23944
  outputs.add(humanText);
@@ -29035,6 +29322,18 @@ var init_useAuthContext = __esm({
29035
29322
  "hooks/useAuthContext.ts"() {
29036
29323
  }
29037
29324
  });
29325
+ function resolveActiveNavHref(items, currentPath) {
29326
+ let best;
29327
+ for (const item of items) {
29328
+ for (const href of item.children?.length ? [item.href, ...item.children.map((c) => c.href)] : [item.href]) {
29329
+ if (currentPath === href) return href;
29330
+ if (currentPath.startsWith(href + "/") && (!best || href.length > best.length)) {
29331
+ best = href;
29332
+ }
29333
+ }
29334
+ }
29335
+ return best;
29336
+ }
29038
29337
  var DashboardLayout, NavLink, NavLinkTopnav, NavLinkBottom;
29039
29338
  var init_DashboardLayout = __esm({
29040
29339
  "components/core/templates/DashboardLayout.tsx"() {
@@ -29106,6 +29405,7 @@ var init_DashboardLayout = __esm({
29106
29405
  const location = reactRouterDom.useLocation();
29107
29406
  const ctxPagePath = providers.useCurrentPagePath();
29108
29407
  const activePath = (currentPath || void 0) ?? ctxPagePath ?? location.pathname;
29408
+ const activeHref = React96.useMemo(() => resolveActiveNavHref(navItems, activePath), [navItems, activePath]);
29109
29409
  const { signOut: authSignOut } = useAuthContext();
29110
29410
  const user = userProp || (null);
29111
29411
  const { t } = hooks.useTranslate();
@@ -29198,7 +29498,7 @@ var init_DashboardLayout = __esm({
29198
29498
  NavLink,
29199
29499
  {
29200
29500
  item,
29201
- currentPath: activePath,
29501
+ activeHref,
29202
29502
  compact: isRail
29203
29503
  },
29204
29504
  item.href
@@ -29265,7 +29565,7 @@ var init_DashboardLayout = __esm({
29265
29565
  NavLinkTopnav,
29266
29566
  {
29267
29567
  item,
29268
- currentPath: activePath
29568
+ activeHref
29269
29569
  },
29270
29570
  item.href
29271
29571
  ))
@@ -29441,7 +29741,7 @@ var init_DashboardLayout = __esm({
29441
29741
  NavLinkBottom,
29442
29742
  {
29443
29743
  item,
29444
- currentPath: activePath
29744
+ activeHref
29445
29745
  },
29446
29746
  item.href
29447
29747
  )) })
@@ -29455,15 +29755,71 @@ var init_DashboardLayout = __esm({
29455
29755
  DashboardLayout.displayName = "DashboardLayout";
29456
29756
  NavLink = ({
29457
29757
  item,
29458
- currentPath,
29758
+ activeHref,
29459
29759
  compact = false
29460
29760
  }) => {
29461
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
29761
+ const hasChildren = !!item.children?.length;
29762
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
29763
+ const isActive = item.href === activeHref || childActive;
29764
+ const [open, setOpen] = React96.useState(childActive);
29462
29765
  const iconClassName = cn(
29463
29766
  "h-5 w-5",
29464
29767
  isActive ? "text-primary-foreground" : "text-muted-foreground"
29465
29768
  );
29466
29769
  if (compact) {
29770
+ if (hasChildren) {
29771
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
29772
+ /* @__PURE__ */ jsxRuntime.jsx(
29773
+ Button,
29774
+ {
29775
+ variant: "ghost",
29776
+ title: item.label,
29777
+ "aria-label": item.label,
29778
+ "aria-expanded": open,
29779
+ className: cn(
29780
+ "flex items-center justify-center w-full px-2 py-2 rounded-lg transition-colors",
29781
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29782
+ ),
29783
+ onClick: () => setOpen(!open),
29784
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { as: "span", className: "relative inline-flex", children: [
29785
+ item.icon ? typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, className: iconClassName }) : /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: iconClassName }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "font-semibold", as: "span", children: item.label.charAt(0).toUpperCase() }),
29786
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(
29787
+ Badge,
29788
+ {
29789
+ variant: isActive ? "primary" : "default",
29790
+ size: "sm",
29791
+ className: "absolute -top-2 -right-2 px-1 py-0 text-[10px] leading-4",
29792
+ children: item.badge
29793
+ }
29794
+ )
29795
+ ] })
29796
+ }
29797
+ ),
29798
+ open && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
29799
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
29800
+ /* @__PURE__ */ jsxRuntime.jsx(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) => {
29801
+ const childIsActive = child.href === activeHref;
29802
+ return /* @__PURE__ */ jsxRuntime.jsxs(
29803
+ reactRouterDom.Link,
29804
+ {
29805
+ to: child.href,
29806
+ onClick: () => setOpen(false),
29807
+ className: cn(
29808
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
29809
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29810
+ ),
29811
+ children: [
29812
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(child.icon, { className: "h-4 w-4" })),
29813
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
29814
+ child.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
29815
+ ]
29816
+ },
29817
+ child.href
29818
+ );
29819
+ }) })
29820
+ ] })
29821
+ ] });
29822
+ }
29467
29823
  return /* @__PURE__ */ jsxRuntime.jsx(
29468
29824
  reactRouterDom.Link,
29469
29825
  {
@@ -29489,6 +29845,78 @@ var init_DashboardLayout = __esm({
29489
29845
  }
29490
29846
  );
29491
29847
  }
29848
+ if (hasChildren) {
29849
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
29850
+ /* @__PURE__ */ jsxRuntime.jsxs(
29851
+ Button,
29852
+ {
29853
+ variant: "ghost",
29854
+ "aria-expanded": open,
29855
+ className: cn(
29856
+ "flex items-center gap-3 w-full px-3 py-2 rounded-lg text-sm font-medium transition-colors",
29857
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29858
+ ),
29859
+ onClick: () => setOpen(!open),
29860
+ children: [
29861
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, className: iconClassName }) : /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: iconClassName })),
29862
+ /* @__PURE__ */ jsxRuntime.jsx(
29863
+ Typography,
29864
+ {
29865
+ variant: "small",
29866
+ color: isActive ? "inherit" : "primary",
29867
+ className: "flex-1 text-left",
29868
+ as: "span",
29869
+ children: item.label
29870
+ }
29871
+ ),
29872
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
29873
+ /* @__PURE__ */ jsxRuntime.jsx(
29874
+ Icon,
29875
+ {
29876
+ name: "chevron-down",
29877
+ className: cn("h-4 w-4 shrink-0 transition-transform", open && "rotate-180")
29878
+ }
29879
+ )
29880
+ ]
29881
+ }
29882
+ ),
29883
+ open && /* @__PURE__ */ jsxRuntime.jsx(
29884
+ VStack,
29885
+ {
29886
+ gap: "none",
29887
+ className: "mt-1 ml-4 pl-4 border-l border-border dark:border-border space-y-1",
29888
+ children: item.children.map((child) => {
29889
+ const childIsActive = child.href === activeHref;
29890
+ return /* @__PURE__ */ jsxRuntime.jsxs(
29891
+ reactRouterDom.Link,
29892
+ {
29893
+ to: child.href,
29894
+ className: cn(
29895
+ "flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm transition-colors",
29896
+ childIsActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29897
+ ),
29898
+ children: [
29899
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(child.icon, { className: "h-4 w-4" })),
29900
+ /* @__PURE__ */ jsxRuntime.jsx(
29901
+ Typography,
29902
+ {
29903
+ variant: "small",
29904
+ color: childIsActive ? "inherit" : "primary",
29905
+ className: "flex-1",
29906
+ as: "span",
29907
+ children: child.label
29908
+ }
29909
+ ),
29910
+ child.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
29911
+ ]
29912
+ },
29913
+ child.href
29914
+ );
29915
+ })
29916
+ }
29917
+ )
29918
+ ] });
29919
+ }
29492
29920
  return /* @__PURE__ */ jsxRuntime.jsxs(
29493
29921
  reactRouterDom.Link,
29494
29922
  {
@@ -29517,9 +29945,63 @@ var init_DashboardLayout = __esm({
29517
29945
  NavLink.displayName = "NavLink";
29518
29946
  NavLinkTopnav = ({
29519
29947
  item,
29520
- currentPath
29948
+ activeHref
29521
29949
  }) => {
29522
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
29950
+ const hasChildren = !!item.children?.length;
29951
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
29952
+ const isActive = item.href === activeHref || childActive;
29953
+ const [open, setOpen] = React96.useState(false);
29954
+ if (hasChildren) {
29955
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
29956
+ /* @__PURE__ */ jsxRuntime.jsxs(
29957
+ Button,
29958
+ {
29959
+ variant: "ghost",
29960
+ "aria-expanded": open,
29961
+ className: cn(
29962
+ "flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors whitespace-nowrap",
29963
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29964
+ ),
29965
+ onClick: () => setOpen(!open),
29966
+ children: [
29967
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: "h-4 w-4" })),
29968
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "inherit", className: "flex-1", as: "span", children: item.label }),
29969
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
29970
+ /* @__PURE__ */ jsxRuntime.jsx(
29971
+ Icon,
29972
+ {
29973
+ name: "chevron-down",
29974
+ className: cn("h-3.5 w-3.5 shrink-0 transition-transform", open && "rotate-180")
29975
+ }
29976
+ )
29977
+ ]
29978
+ }
29979
+ ),
29980
+ open && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
29981
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
29982
+ /* @__PURE__ */ jsxRuntime.jsx(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) => {
29983
+ const childIsActive = child.href === activeHref;
29984
+ return /* @__PURE__ */ jsxRuntime.jsxs(
29985
+ reactRouterDom.Link,
29986
+ {
29987
+ to: child.href,
29988
+ onClick: () => setOpen(false),
29989
+ className: cn(
29990
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
29991
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29992
+ ),
29993
+ children: [
29994
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(child.icon, { className: "h-4 w-4" })),
29995
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
29996
+ child.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
29997
+ ]
29998
+ },
29999
+ child.href
30000
+ );
30001
+ }) })
30002
+ ] })
30003
+ ] });
30004
+ }
29523
30005
  return /* @__PURE__ */ jsxRuntime.jsxs(
29524
30006
  reactRouterDom.Link,
29525
30007
  {
@@ -29539,9 +30021,9 @@ var init_DashboardLayout = __esm({
29539
30021
  NavLinkTopnav.displayName = "NavLinkTopnav";
29540
30022
  NavLinkBottom = ({
29541
30023
  item,
29542
- currentPath
30024
+ activeHref
29543
30025
  }) => {
29544
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
30026
+ const isActive = item.href === activeHref;
29545
30027
  const iconClassName = cn(
29546
30028
  "h-5 w-5",
29547
30029
  isActive ? "text-primary" : "text-muted-foreground"
@@ -30480,7 +30962,9 @@ function DataGrid({
30480
30962
  const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
30481
30963
  const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
30482
30964
  const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
30483
- const hasRenderProp = typeof children === "function";
30965
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
30966
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
30967
+ const hasRenderProp = typeof itemRenderer === "function";
30484
30968
  React96.useEffect(() => {
30485
30969
  if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
30486
30970
  const schemaArr = Array.isArray(schemaRenderItem) ? schemaRenderItem : null;
@@ -30554,7 +31038,7 @@ function DataGrid({
30554
31038
  onClick: handleCardClick?.(itemData),
30555
31039
  className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
30556
31040
  children: [
30557
- children(itemData, index),
31041
+ itemRenderer(itemData, index),
30558
31042
  actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(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(HStack, { gap: "xs", className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: [
30559
31043
  inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
30560
31044
  Button,
@@ -30887,7 +31371,9 @@ function DataList({
30887
31371
  );
30888
31372
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
30889
31373
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
30890
- const hasRenderProp = typeof children === "function";
31374
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
31375
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
31376
+ const hasRenderProp = typeof itemRenderer === "function";
30891
31377
  React96__namespace.default.useEffect(() => {
30892
31378
  const renderItemTypeOf = typeof schemaRenderItem;
30893
31379
  const childrenTypeOf = typeof children;
@@ -31105,7 +31591,7 @@ function DataList({
31105
31591
  const actions = renderItemActions(itemData);
31106
31592
  return wrapDnd(
31107
31593
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn("relative group/rowactions", rowClickEvent && "cursor-pointer"), children: [
31108
- children(itemData, index),
31594
+ itemRenderer(itemData, index),
31109
31595
  actions && /* @__PURE__ */ jsxRuntime.jsxs(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: [
31110
31596
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5 [@media(pointer:coarse)]:hidden", children: actions }),
31111
31597
  /* @__PURE__ */ jsxRuntime.jsx(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(
@@ -37649,7 +38135,7 @@ function TableView({
37649
38135
  columns,
37650
38136
  fields,
37651
38137
  itemActions,
37652
- maxInlineActions: _maxInlineActions,
38138
+ maxInlineActions,
37653
38139
  itemClickEvent = "",
37654
38140
  selectable = false,
37655
38141
  selectEvent,
@@ -37682,6 +38168,12 @@ function TableView({
37682
38168
  const [localSelected, setLocalSelected] = React96__namespace.default.useState(/* @__PURE__ */ new Set());
37683
38169
  const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
37684
38170
  const actionDefs = Array.isArray(itemActions) ? itemActions : [];
38171
+ const inlineActions = maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : [];
38172
+ const overflowActions = maxInlineActions != null ? actionDefs.slice(maxInlineActions) : actionDefs;
38173
+ const fireAction = (action, row) => eventBus.emit(`UI:${action.event}`, {
38174
+ id: row.id,
38175
+ row
38176
+ });
37685
38177
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
37686
38178
  const dnd = useDataDnd({
37687
38179
  items: allDataRaw,
@@ -37857,7 +38349,7 @@ function TableView({
37857
38349
  }
37858
38350
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
37859
38351
  }),
37860
- hasActions && /* @__PURE__ */ jsxRuntime.jsx(
38352
+ hasActions && /* @__PURE__ */ jsxRuntime.jsxs(
37861
38353
  HStack,
37862
38354
  {
37863
38355
  gap: "xs",
@@ -37871,23 +38363,38 @@ function TableView({
37871
38363
  "border-l border-[var(--color-border)]",
37872
38364
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
37873
38365
  ),
37874
- children: /* @__PURE__ */ jsxRuntime.jsx(
37875
- Menu,
37876
- {
37877
- position: "bottom-end",
37878
- trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
37879
- items: actionDefs.map((action) => ({
37880
- label: action.label,
37881
- icon: action.icon,
37882
- event: action.event,
37883
- variant: action.variant === "danger" ? "danger" : "default",
37884
- onClick: () => eventBus.emit(`UI:${action.event}`, {
37885
- id: row.id,
37886
- row
37887
- })
37888
- }))
37889
- }
37890
- )
38366
+ children: [
38367
+ inlineActions.map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
38368
+ Button,
38369
+ {
38370
+ variant: action.variant ?? "ghost",
38371
+ size: "sm",
38372
+ onClick: () => fireAction(action, row),
38373
+ "data-testid": `action-${action.event}`,
38374
+ "data-row-id": String(row.id),
38375
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
38376
+ children: [
38377
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
38378
+ action.label
38379
+ ]
38380
+ },
38381
+ action.event
38382
+ )),
38383
+ overflowActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
38384
+ Menu,
38385
+ {
38386
+ position: "bottom-end",
38387
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
38388
+ items: overflowActions.map((action) => ({
38389
+ label: action.label,
38390
+ icon: action.icon,
38391
+ event: action.event,
38392
+ variant: action.variant === "danger" ? "danger" : "default",
38393
+ onClick: () => fireAction(action, row)
38394
+ }))
38395
+ }
38396
+ )
38397
+ ]
37891
38398
  }
37892
38399
  )
37893
38400
  ]
@@ -53329,6 +53836,7 @@ var init_component_registry_generated = __esm({
53329
53836
  init_DrawMesh();
53330
53837
  init_DrawShape();
53331
53838
  init_DrawShapeLayer();
53839
+ init_DrawSkinnedMesh();
53332
53840
  init_DrawSprite();
53333
53841
  init_DrawSpriteLayer();
53334
53842
  init_DrawText();
@@ -53607,6 +54115,7 @@ var init_component_registry_generated = __esm({
53607
54115
  "DrawMesh": DrawMesh,
53608
54116
  "DrawShape": DrawShape,
53609
54117
  "DrawShapeLayer": DrawShapeLayer,
54118
+ "DrawSkinnedMesh": DrawSkinnedMesh,
53610
54119
  "DrawSprite": DrawSprite,
53611
54120
  "DrawSpriteLayer": DrawSpriteLayer,
53612
54121
  "DrawText": DrawText,
@@ -53935,7 +54444,7 @@ function renderContainedPortal(t, slot, content, onDismiss) {
53935
54444
  const slotId = `slot-${slot}`;
53936
54445
  switch (slot) {
53937
54446
  case "modal":
53938
- if (SELF_OVERLAY_PATTERNS.has(content.pattern)) {
54447
+ if (patterns.SELF_OVERLAY_PATTERN_TYPES.has(content.pattern)) {
53939
54448
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { id: slotId, className: "contents", children: slotContent });
53940
54449
  }
53941
54450
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -54278,7 +54787,7 @@ function CompiledPortal({ slot, className, pattern, sourceTrait, children }) {
54278
54787
  children
54279
54788
  }
54280
54789
  );
54281
- wrapper = pattern !== void 0 && SELF_OVERLAY_PATTERNS.has(pattern) ? innerContent : /* @__PURE__ */ jsxRuntime.jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
54790
+ wrapper = pattern !== void 0 && patterns.SELF_OVERLAY_PATTERN_TYPES.has(pattern) ? innerContent : /* @__PURE__ */ jsxRuntime.jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
54282
54791
  break;
54283
54792
  }
54284
54793
  case "drawer":
@@ -54338,7 +54847,7 @@ function SlotPortal({
54338
54847
  let wrapper;
54339
54848
  switch (slot) {
54340
54849
  case "modal":
54341
- wrapper = SELF_OVERLAY_PATTERNS.has(content.pattern) ? /* @__PURE__ */ jsxRuntime.jsx(Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsxRuntime.jsx(
54850
+ wrapper = patterns.SELF_OVERLAY_PATTERN_TYPES.has(content.pattern) ? /* @__PURE__ */ jsxRuntime.jsx(Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsxRuntime.jsx(
54342
54851
  Modal,
54343
54852
  {
54344
54853
  isOpen: true,
@@ -54845,7 +55354,7 @@ function UISlotRenderer({
54845
55354
  }
54846
55355
  return wrapped;
54847
55356
  }
54848
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
55357
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
54849
55358
  var init_UISlotRenderer = __esm({
54850
55359
  "components/core/organisms/UISlotRenderer.tsx"() {
54851
55360
  "use client";
@@ -54876,7 +55385,6 @@ var init_UISlotRenderer = __esm({
54876
55385
  modal: "form",
54877
55386
  drawer: "form"
54878
55387
  };
54879
- SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
54880
55388
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
54881
55389
  "logo",
54882
55390
  "master",
@@ -58535,7 +59043,7 @@ function runTickFrame(entityId, orderedWriters, store) {
58535
59043
  }
58536
59044
  var log9 = logger.createLogger("almadar:ui:effects:client-handlers");
58537
59045
  function createClientEffectHandlers(options) {
58538
- const { eventBus, slotSetter, navigate, navigateBack, notify, callService, liveEntity, persistDelegated } = options;
59046
+ const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated } = options;
58539
59047
  return {
58540
59048
  emit: (event, payload, source) => {
58541
59049
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
@@ -58591,9 +59099,6 @@ function createClientEffectHandlers(options) {
58591
59099
  }),
58592
59100
  navigateBack: navigateBack ?? (() => {
58593
59101
  log9.warn("No navigate-back handler, ignoring");
58594
- }),
58595
- notify: notify ?? ((msg, type) => {
58596
- log9.debug("notify", { type, message: msg });
58597
59102
  })
58598
59103
  };
58599
59104
  }
@@ -58792,6 +59297,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
58792
59297
  sharedEntityLog.debug("useTraitStateMachine start", { traitBindingsCount: traitBindings.length, traitNames: traitBindings.map((b) => b.trait.name) });
58793
59298
  const eventBus = useEventBus();
58794
59299
  const { entities } = providers.useEntitySchema();
59300
+ const { user: viewer } = providers.useUser();
58795
59301
  const traitConfigsByName = options?.traitConfigsByName;
58796
59302
  const orbitalsByTrait = options?.orbitalsByTrait;
58797
59303
  const callsiteCaptureChildrenByTrait = options?.callsiteCaptureChildrenByTrait;
@@ -59105,7 +59611,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59105
59611
  },
59106
59612
  navigate: optionsRef.current?.navigate,
59107
59613
  navigateBack: optionsRef.current?.navigateBack,
59108
- notify: optionsRef.current?.notify,
59109
59614
  callService: optionsRef.current?.callService,
59110
59615
  // The canonical client `set` writes `(set @entity.X)` straight into
59111
59616
  // the trait's one live store — the same object bound below. This is
@@ -59172,8 +59677,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59172
59677
  emit: clientHandlers.emit,
59173
59678
  renderUI: clientHandlers.renderUI,
59174
59679
  navigate: clientHandlers.navigate,
59175
- navigateBack: clientHandlers.navigateBack,
59176
- notify: clientHandlers.notify
59680
+ navigateBack: clientHandlers.navigateBack
59177
59681
  };
59178
59682
  }
59179
59683
  const sharedWrites = [];
@@ -59198,7 +59702,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59198
59702
  const bindingCtx = {
59199
59703
  entity: liveEntity,
59200
59704
  payload: payload || {},
59201
- state: previousState
59705
+ state: previousState,
59706
+ ...viewer ? { user: viewer } : {}
59202
59707
  };
59203
59708
  if (callsitePayload) {
59204
59709
  bindingCtx.callsitePayload = callsitePayload;
@@ -59295,7 +59800,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59295
59800
  });
59296
59801
  }
59297
59802
  return { emitted: emittedDuringExec, serverEffectResults };
59298
- }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait]);
59803
+ }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait, viewer]);
59299
59804
  const reRenderCallsiteCaptureChildren = React96.useCallback(async (traitName, callsitePayload, entityByTrait, log12, visited = /* @__PURE__ */ new Set()) => {
59300
59805
  const children = callsiteCaptureChildrenByTrait?.get(traitName);
59301
59806
  if (!children || children.size === 0) return;
@@ -59340,7 +59845,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59340
59845
  const guardCtx = {
59341
59846
  entity,
59342
59847
  payload: {},
59343
- state: currentState
59848
+ state: currentState,
59849
+ ...viewer ? { user: viewer } : {}
59344
59850
  };
59345
59851
  const bindingCfg = getBindingConfig(binding) ?? traitConfigsByName?.[traitName];
59346
59852
  if (bindingCfg) {
@@ -59368,7 +59874,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59368
59874
  const emitFromSharedWriter = React96.useCallback((event, payload, source) => {
59369
59875
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
59370
59876
  eventBus.emit(prefixedEvent, payload, source);
59371
- }, [eventBus]);
59877
+ }, [eventBus, viewer]);
59372
59878
  React96.useEffect(() => {
59373
59879
  const scheduler = runtime.createTickScheduler();
59374
59880
  const timedTick = (key, fn) => () => ui.perfTimeAsync(key, fn);
@@ -60235,7 +60741,8 @@ function OrbPreview({
60235
60741
  transport,
60236
60742
  getAccessToken,
60237
60743
  initialPagePath,
60238
- isolated = false
60744
+ isolated = false,
60745
+ user = null
60239
60746
  }) {
60240
60747
  if (serverUrl && transport) {
60241
60748
  throw new Error("OrbPreview accepts serverUrl OR transport, not both");
@@ -60245,11 +60752,7 @@ function OrbPreview({
60245
60752
  const handleLocalFallback = React96.useCallback(() => {
60246
60753
  if (localFallback) return;
60247
60754
  setLocalFallback(true);
60248
- eventBus.emit("UI:NOTIFY", {
60249
- message: "Preview server unreachable \u2014 running locally without server-side state.",
60250
- severity: "warning"
60251
- });
60252
- }, [localFallback, eventBus]);
60755
+ }, [localFallback]);
60253
60756
  const parseResult = React96.useMemo(() => {
60254
60757
  let parsed;
60255
60758
  if (typeof schema === "string") {
@@ -60408,7 +60911,7 @@ function OrbPreview({
60408
60911
  storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
60409
60912
  children: [
60410
60913
  /* @__PURE__ */ jsxRuntime.jsx(NavStackRefBridge, { apiRef: navStackRef }),
60411
- /* @__PURE__ */ jsxRuntime.jsx(providers.OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsxRuntime.jsx(context.UISlotProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
60914
+ /* @__PURE__ */ jsxRuntime.jsx(providers.OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsxRuntime.jsx(context.UISlotProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
60412
60915
  SchemaRunner,
60413
60916
  {
60414
60917
  schema: parseResult.schema,
@@ -61871,7 +62374,6 @@ var KNOWN_EFFECTS = /* @__PURE__ */ new Set([
61871
62374
  "fetch",
61872
62375
  "emit",
61873
62376
  "navigate",
61874
- "notify",
61875
62377
  "call-service",
61876
62378
  "spawn",
61877
62379
  "despawn",
@@ -64321,7 +64823,6 @@ function mapEffectType(label) {
64321
64823
  "fetch",
64322
64824
  "emit",
64323
64825
  "navigate",
64324
- "notify",
64325
64826
  "call-service",
64326
64827
  "spawn",
64327
64828
  "despawn",