@almadar/ui 6.13.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 +367 -61
  10. package/dist/avl/index.d.cts +1 -1
  11. package/dist/avl/index.d.ts +1 -1
  12. package/dist/avl/index.js +369 -63
  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 +360 -108
  16. package/dist/components/index.d.cts +8 -24
  17. package/dist/components/index.d.ts +8 -24
  18. package/dist/components/index.js +362 -109
  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 +354 -53
  34. package/dist/providers/index.d.cts +10 -9
  35. package/dist/providers/index.d.ts +10 -9
  36. package/dist/providers/index.js +355 -54
  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 +369 -72
  42. package/dist/runtime/index.d.cts +14 -11
  43. package/dist/runtime/index.d.ts +14 -11
  44. package/dist/runtime/index.js +371 -74
  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);
@@ -30675,7 +30962,9 @@ function DataGrid({
30675
30962
  const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
30676
30963
  const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
30677
30964
  const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
30678
- 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";
30679
30968
  React96.useEffect(() => {
30680
30969
  if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
30681
30970
  const schemaArr = Array.isArray(schemaRenderItem) ? schemaRenderItem : null;
@@ -30749,7 +31038,7 @@ function DataGrid({
30749
31038
  onClick: handleCardClick?.(itemData),
30750
31039
  className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
30751
31040
  children: [
30752
- children(itemData, index),
31041
+ itemRenderer(itemData, index),
30753
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: [
30754
31043
  inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
30755
31044
  Button,
@@ -31082,7 +31371,9 @@ function DataList({
31082
31371
  );
31083
31372
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
31084
31373
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
31085
- 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";
31086
31377
  React96__namespace.default.useEffect(() => {
31087
31378
  const renderItemTypeOf = typeof schemaRenderItem;
31088
31379
  const childrenTypeOf = typeof children;
@@ -31300,7 +31591,7 @@ function DataList({
31300
31591
  const actions = renderItemActions(itemData);
31301
31592
  return wrapDnd(
31302
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: [
31303
- children(itemData, index),
31594
+ itemRenderer(itemData, index),
31304
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: [
31305
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 }),
31306
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(
@@ -37844,7 +38135,7 @@ function TableView({
37844
38135
  columns,
37845
38136
  fields,
37846
38137
  itemActions,
37847
- maxInlineActions: _maxInlineActions,
38138
+ maxInlineActions,
37848
38139
  itemClickEvent = "",
37849
38140
  selectable = false,
37850
38141
  selectEvent,
@@ -37877,6 +38168,12 @@ function TableView({
37877
38168
  const [localSelected, setLocalSelected] = React96__namespace.default.useState(/* @__PURE__ */ new Set());
37878
38169
  const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
37879
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
+ });
37880
38177
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
37881
38178
  const dnd = useDataDnd({
37882
38179
  items: allDataRaw,
@@ -38052,7 +38349,7 @@ function TableView({
38052
38349
  }
38053
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);
38054
38351
  }),
38055
- hasActions && /* @__PURE__ */ jsxRuntime.jsx(
38352
+ hasActions && /* @__PURE__ */ jsxRuntime.jsxs(
38056
38353
  HStack,
38057
38354
  {
38058
38355
  gap: "xs",
@@ -38066,23 +38363,38 @@ function TableView({
38066
38363
  "border-l border-[var(--color-border)]",
38067
38364
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
38068
38365
  ),
38069
- children: /* @__PURE__ */ jsxRuntime.jsx(
38070
- Menu,
38071
- {
38072
- position: "bottom-end",
38073
- 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" }) }),
38074
- items: actionDefs.map((action) => ({
38075
- label: action.label,
38076
- icon: action.icon,
38077
- event: action.event,
38078
- variant: action.variant === "danger" ? "danger" : "default",
38079
- onClick: () => eventBus.emit(`UI:${action.event}`, {
38080
- id: row.id,
38081
- row
38082
- })
38083
- }))
38084
- }
38085
- )
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
+ ]
38086
38398
  }
38087
38399
  )
38088
38400
  ]
@@ -53524,6 +53836,7 @@ var init_component_registry_generated = __esm({
53524
53836
  init_DrawMesh();
53525
53837
  init_DrawShape();
53526
53838
  init_DrawShapeLayer();
53839
+ init_DrawSkinnedMesh();
53527
53840
  init_DrawSprite();
53528
53841
  init_DrawSpriteLayer();
53529
53842
  init_DrawText();
@@ -53802,6 +54115,7 @@ var init_component_registry_generated = __esm({
53802
54115
  "DrawMesh": DrawMesh,
53803
54116
  "DrawShape": DrawShape,
53804
54117
  "DrawShapeLayer": DrawShapeLayer,
54118
+ "DrawSkinnedMesh": DrawSkinnedMesh,
53805
54119
  "DrawSprite": DrawSprite,
53806
54120
  "DrawSpriteLayer": DrawSpriteLayer,
53807
54121
  "DrawText": DrawText,
@@ -54130,7 +54444,7 @@ function renderContainedPortal(t, slot, content, onDismiss) {
54130
54444
  const slotId = `slot-${slot}`;
54131
54445
  switch (slot) {
54132
54446
  case "modal":
54133
- if (SELF_OVERLAY_PATTERNS.has(content.pattern)) {
54447
+ if (patterns.SELF_OVERLAY_PATTERN_TYPES.has(content.pattern)) {
54134
54448
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { id: slotId, className: "contents", children: slotContent });
54135
54449
  }
54136
54450
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -54473,7 +54787,7 @@ function CompiledPortal({ slot, className, pattern, sourceTrait, children }) {
54473
54787
  children
54474
54788
  }
54475
54789
  );
54476
- 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 });
54477
54791
  break;
54478
54792
  }
54479
54793
  case "drawer":
@@ -54533,7 +54847,7 @@ function SlotPortal({
54533
54847
  let wrapper;
54534
54848
  switch (slot) {
54535
54849
  case "modal":
54536
- 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(
54537
54851
  Modal,
54538
54852
  {
54539
54853
  isOpen: true,
@@ -55040,7 +55354,7 @@ function UISlotRenderer({
55040
55354
  }
55041
55355
  return wrapped;
55042
55356
  }
55043
- 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;
55044
55358
  var init_UISlotRenderer = __esm({
55045
55359
  "components/core/organisms/UISlotRenderer.tsx"() {
55046
55360
  "use client";
@@ -55071,7 +55385,6 @@ var init_UISlotRenderer = __esm({
55071
55385
  modal: "form",
55072
55386
  drawer: "form"
55073
55387
  };
55074
- SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
55075
55388
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
55076
55389
  "logo",
55077
55390
  "master",
@@ -58730,7 +59043,7 @@ function runTickFrame(entityId, orderedWriters, store) {
58730
59043
  }
58731
59044
  var log9 = logger.createLogger("almadar:ui:effects:client-handlers");
58732
59045
  function createClientEffectHandlers(options) {
58733
- const { eventBus, slotSetter, navigate, navigateBack, notify, callService, liveEntity, persistDelegated } = options;
59046
+ const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated } = options;
58734
59047
  return {
58735
59048
  emit: (event, payload, source) => {
58736
59049
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
@@ -58786,9 +59099,6 @@ function createClientEffectHandlers(options) {
58786
59099
  }),
58787
59100
  navigateBack: navigateBack ?? (() => {
58788
59101
  log9.warn("No navigate-back handler, ignoring");
58789
- }),
58790
- notify: notify ?? ((msg, type) => {
58791
- log9.debug("notify", { type, message: msg });
58792
59102
  })
58793
59103
  };
58794
59104
  }
@@ -58987,6 +59297,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
58987
59297
  sharedEntityLog.debug("useTraitStateMachine start", { traitBindingsCount: traitBindings.length, traitNames: traitBindings.map((b) => b.trait.name) });
58988
59298
  const eventBus = useEventBus();
58989
59299
  const { entities } = providers.useEntitySchema();
59300
+ const { user: viewer } = providers.useUser();
58990
59301
  const traitConfigsByName = options?.traitConfigsByName;
58991
59302
  const orbitalsByTrait = options?.orbitalsByTrait;
58992
59303
  const callsiteCaptureChildrenByTrait = options?.callsiteCaptureChildrenByTrait;
@@ -59300,7 +59611,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59300
59611
  },
59301
59612
  navigate: optionsRef.current?.navigate,
59302
59613
  navigateBack: optionsRef.current?.navigateBack,
59303
- notify: optionsRef.current?.notify,
59304
59614
  callService: optionsRef.current?.callService,
59305
59615
  // The canonical client `set` writes `(set @entity.X)` straight into
59306
59616
  // the trait's one live store — the same object bound below. This is
@@ -59367,8 +59677,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59367
59677
  emit: clientHandlers.emit,
59368
59678
  renderUI: clientHandlers.renderUI,
59369
59679
  navigate: clientHandlers.navigate,
59370
- navigateBack: clientHandlers.navigateBack,
59371
- notify: clientHandlers.notify
59680
+ navigateBack: clientHandlers.navigateBack
59372
59681
  };
59373
59682
  }
59374
59683
  const sharedWrites = [];
@@ -59393,7 +59702,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59393
59702
  const bindingCtx = {
59394
59703
  entity: liveEntity,
59395
59704
  payload: payload || {},
59396
- state: previousState
59705
+ state: previousState,
59706
+ ...viewer ? { user: viewer } : {}
59397
59707
  };
59398
59708
  if (callsitePayload) {
59399
59709
  bindingCtx.callsitePayload = callsitePayload;
@@ -59490,7 +59800,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59490
59800
  });
59491
59801
  }
59492
59802
  return { emitted: emittedDuringExec, serverEffectResults };
59493
- }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait]);
59803
+ }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait, viewer]);
59494
59804
  const reRenderCallsiteCaptureChildren = React96.useCallback(async (traitName, callsitePayload, entityByTrait, log12, visited = /* @__PURE__ */ new Set()) => {
59495
59805
  const children = callsiteCaptureChildrenByTrait?.get(traitName);
59496
59806
  if (!children || children.size === 0) return;
@@ -59535,7 +59845,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59535
59845
  const guardCtx = {
59536
59846
  entity,
59537
59847
  payload: {},
59538
- state: currentState
59848
+ state: currentState,
59849
+ ...viewer ? { user: viewer } : {}
59539
59850
  };
59540
59851
  const bindingCfg = getBindingConfig(binding) ?? traitConfigsByName?.[traitName];
59541
59852
  if (bindingCfg) {
@@ -59563,7 +59874,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59563
59874
  const emitFromSharedWriter = React96.useCallback((event, payload, source) => {
59564
59875
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
59565
59876
  eventBus.emit(prefixedEvent, payload, source);
59566
- }, [eventBus]);
59877
+ }, [eventBus, viewer]);
59567
59878
  React96.useEffect(() => {
59568
59879
  const scheduler = runtime.createTickScheduler();
59569
59880
  const timedTick = (key, fn) => () => ui.perfTimeAsync(key, fn);
@@ -60430,7 +60741,8 @@ function OrbPreview({
60430
60741
  transport,
60431
60742
  getAccessToken,
60432
60743
  initialPagePath,
60433
- isolated = false
60744
+ isolated = false,
60745
+ user = null
60434
60746
  }) {
60435
60747
  if (serverUrl && transport) {
60436
60748
  throw new Error("OrbPreview accepts serverUrl OR transport, not both");
@@ -60440,11 +60752,7 @@ function OrbPreview({
60440
60752
  const handleLocalFallback = React96.useCallback(() => {
60441
60753
  if (localFallback) return;
60442
60754
  setLocalFallback(true);
60443
- eventBus.emit("UI:NOTIFY", {
60444
- message: "Preview server unreachable \u2014 running locally without server-side state.",
60445
- severity: "warning"
60446
- });
60447
- }, [localFallback, eventBus]);
60755
+ }, [localFallback]);
60448
60756
  const parseResult = React96.useMemo(() => {
60449
60757
  let parsed;
60450
60758
  if (typeof schema === "string") {
@@ -60603,7 +60911,7 @@ function OrbPreview({
60603
60911
  storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
60604
60912
  children: [
60605
60913
  /* @__PURE__ */ jsxRuntime.jsx(NavStackRefBridge, { apiRef: navStackRef }),
60606
- /* @__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(
60607
60915
  SchemaRunner,
60608
60916
  {
60609
60917
  schema: parseResult.schema,
@@ -62066,7 +62374,6 @@ var KNOWN_EFFECTS = /* @__PURE__ */ new Set([
62066
62374
  "fetch",
62067
62375
  "emit",
62068
62376
  "navigate",
62069
- "notify",
62070
62377
  "call-service",
62071
62378
  "spawn",
62072
62379
  "despawn",
@@ -64516,7 +64823,6 @@ function mapEffectType(label) {
64516
64823
  "fetch",
64517
64824
  "emit",
64518
64825
  "navigate",
64519
- "notify",
64520
64826
  "call-service",
64521
64827
  "spawn",
64522
64828
  "despawn",