@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
package/dist/avl/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import * as React96 from 'react';
3
3
  import React96__default, { createContext, useState, useEffect, useMemo, useRef, useContext, useCallback, Suspense, useLayoutEffect, Profiler, useReducer, useSyncExternalStore, lazy, useId } from 'react';
4
- import { getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, EventBusContext, useTraitScopeChain, useNavStack, ServerBridgeProvider, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext, useEntitySchema, RenderSlotProvider, useEntitySchemaOptional, useEntityBindingSnapshot, TraitScopeProvider, useTraitScope, useCurrentPagePath, useRenderSlot, useGameAudioContextOptional } from '@almadar/ui/providers';
4
+ import { getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, EventBusContext, useTraitScopeChain, useNavStack, ServerBridgeProvider, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext, useEntitySchema, useUser, RenderSlotProvider, useEntitySchemaOptional, useEntityBindingSnapshot, TraitScopeProvider, useTraitScope, useCurrentPagePath, useRenderSlot, useGameAudioContextOptional } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import ELK from 'elkjs/lib/elk.bundled.js';
7
7
  import { MarkerType, useReactFlow, Handle, Position, getBezierPath, EdgeLabelRenderer, useNodeId, ReactFlowProvider, BaseEdge, useNodesState, useEdgesState, ReactFlow, Controls, Background, BackgroundVariant } from '@xyflow/react';
@@ -74,7 +74,7 @@ import { sortableKeyboardCoordinates, useSortable, arrayMove, SortableContext, r
74
74
  import { CSS } from '@dnd-kit/utilities';
75
75
  import { ordered, lib } from 'emojilib';
76
76
  import { forceSimulation, forceLink, forceManyBody, forceCollide, forceX, forceY } from 'd3-force';
77
- import { isDrawHostPattern, getPatternDefinition, getPatternFieldsContract, getComponentForPattern as getComponentForPattern$1, isEntityAwarePattern } from '@almadar/core/patterns';
77
+ import { SELF_OVERLAY_PATTERN_TYPES, isDrawHostPattern, getPatternDefinition, getPatternFieldsContract, getComponentForPattern as getComponentForPattern$1, isEntityAwarePattern } from '@almadar/core/patterns';
78
78
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
79
79
  import { isKnownStdOperator } from '@almadar/std/registry';
80
80
 
@@ -152,7 +152,6 @@ var init_avl_atom_types = __esm({
152
152
  "persist": "data",
153
153
  "fetch": "data",
154
154
  "emit": "communication",
155
- "notify": "communication",
156
155
  "call-service": "communication",
157
156
  "spawn": "lifecycle",
158
157
  "despawn": "lifecycle",
@@ -440,12 +439,6 @@ function effectIcon(type, x, y, s, color) {
440
439
  /* @__PURE__ */ jsx("line", { x1: x - s, y1: y, x2: x + s * 0.6, y2: y, stroke: color, strokeWidth: 1.5 }),
441
440
  /* @__PURE__ */ 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" })
442
441
  ] });
443
- case "notify":
444
- return /* @__PURE__ */ jsxs("g", { children: [
445
- /* @__PURE__ */ 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 }),
446
- /* @__PURE__ */ 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 }),
447
- /* @__PURE__ */ jsx("circle", { cx: x, cy: y + s * 0.7, r: s * 0.2, fill: color })
448
- ] });
449
442
  case "call-service":
450
443
  return /* @__PURE__ */ jsxs("g", { children: [
451
444
  /* @__PURE__ */ jsx("line", { x1: x - s, y1: y - s * 0.3, x2: x + s, y2: y - s * 0.3, stroke: color, strokeWidth: 1.5 }),
@@ -11523,7 +11516,7 @@ var init_DialogueBubble = __esm({
11523
11516
  init_Typography();
11524
11517
  init_GameIcon();
11525
11518
  DEFAULT_PORTRAIT = {
11526
- url: "https://almadar-kflow-assets.web.app/shared/characters/archetypes/04_hero.png",
11519
+ url: "https://almadar-kflow-assets.web.app/shared/ui-winter-ski-board/default/units/hero/hero.png",
11527
11520
  role: "effect",
11528
11521
  category: "character"
11529
11522
  };
@@ -12847,6 +12840,14 @@ function createWebPainter(ctx, onAssetLoad) {
12847
12840
  ctx.drawImage(img, dest.x, dest.y, dw, dh);
12848
12841
  }
12849
12842
  },
12843
+ blitTransformed(tex, src, affine) {
12844
+ const img = imageByHandle.get(tex);
12845
+ if (!img) return;
12846
+ ctx.save();
12847
+ ctx.transform(affine[0], affine[1], affine[2], affine[3], affine[4], affine[5]);
12848
+ ctx.drawImage(img, src.x, src.y, src.w, src.h, src.x, src.y, src.w, src.h);
12849
+ ctx.restore();
12850
+ },
12850
12851
  fillRect(x, y, w, h, style) {
12851
12852
  ctx.fillStyle = toCanvasStyle(style);
12852
12853
  ctx.fillRect(x, y, w, h);
@@ -13353,6 +13354,271 @@ var init_DrawGroup = __esm({
13353
13354
  init_registry();
13354
13355
  }
13355
13356
  });
13357
+
13358
+ // lib/skinning.ts
13359
+ function composeAffine(a, b) {
13360
+ return [
13361
+ a[0] * b[0] + a[2] * b[1],
13362
+ a[1] * b[0] + a[3] * b[1],
13363
+ a[0] * b[2] + a[2] * b[3],
13364
+ a[1] * b[2] + a[3] * b[3],
13365
+ a[0] * b[4] + a[2] * b[5] + a[4],
13366
+ a[1] * b[4] + a[3] * b[5] + a[5]
13367
+ ];
13368
+ }
13369
+ function invertAffine(m) {
13370
+ const det = m[0] * m[3] - m[1] * m[2];
13371
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return IDENTITY;
13372
+ const inv = 1 / det;
13373
+ return [
13374
+ m[3] * inv,
13375
+ -m[1] * inv,
13376
+ -m[2] * inv,
13377
+ m[0] * inv,
13378
+ (m[2] * m[5] - m[3] * m[4]) * inv,
13379
+ (m[1] * m[4] - m[0] * m[5]) * inv
13380
+ ];
13381
+ }
13382
+ function applyAffine(m, x, y) {
13383
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
13384
+ }
13385
+ function rotationAbout(pivot, degrees) {
13386
+ const r2 = degrees * Math.PI / 180;
13387
+ const cos = Math.cos(r2);
13388
+ const sin = Math.sin(r2);
13389
+ const [px, py] = pivot;
13390
+ return [cos, sin, -sin, cos, px - cos * px + sin * py, py - sin * px - cos * py];
13391
+ }
13392
+ function computeWorldMatrices(bones, pose) {
13393
+ const indexByName = /* @__PURE__ */ new Map();
13394
+ bones.forEach((b, i) => indexByName.set(b.name, i));
13395
+ const world = new Array(bones.length);
13396
+ const visiting = /* @__PURE__ */ new Set();
13397
+ const resolve = (i) => {
13398
+ const cached = world[i];
13399
+ if (cached) return cached;
13400
+ if (visiting.has(i)) return IDENTITY;
13401
+ visiting.add(i);
13402
+ const b = bones[i];
13403
+ const parentIndex = b.parent === null ? void 0 : indexByName.get(b.parent);
13404
+ const parentWorld = parentIndex === void 0 ? IDENTITY : resolve(parentIndex);
13405
+ const local = rotationAbout(b.pivot, pose[b.name] ?? 0);
13406
+ const m = composeAffine(parentWorld, local);
13407
+ visiting.delete(i);
13408
+ world[i] = m;
13409
+ return m;
13410
+ };
13411
+ return bones.map((_, i) => resolve(i));
13412
+ }
13413
+ function skinVertices(mesh, bindWorld, curWorld) {
13414
+ const skinMats = /* @__PURE__ */ new Map();
13415
+ const skinMat = (bone) => {
13416
+ let m = skinMats.get(bone);
13417
+ if (!m) {
13418
+ m = composeAffine(curWorld[bone] ?? IDENTITY, invertAffine(bindWorld[bone] ?? IDENTITY));
13419
+ skinMats.set(bone, m);
13420
+ }
13421
+ return m;
13422
+ };
13423
+ return mesh.vertices.map((v) => {
13424
+ if (v.weights.length === 0) return [v.x, v.y];
13425
+ let x = 0;
13426
+ let y = 0;
13427
+ for (const { bone, w } of v.weights) {
13428
+ const [bx, by] = applyAffine(skinMat(bone), v.x, v.y);
13429
+ x += w * bx;
13430
+ y += w * by;
13431
+ }
13432
+ return [x, y];
13433
+ });
13434
+ }
13435
+ function triangleAffine(src, dst) {
13436
+ const [s0, s1, s2] = src;
13437
+ const det = s0[0] * (s1[1] - s2[1]) + s1[0] * (s2[1] - s0[1]) + s2[0] * (s0[1] - s1[1]);
13438
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return null;
13439
+ const [d0, d1, d2] = dst;
13440
+ const a = (d0[0] * (s1[1] - s2[1]) + d1[0] * (s2[1] - s0[1]) + d2[0] * (s0[1] - s1[1])) / det;
13441
+ const c = (d0[0] * (s2[0] - s1[0]) + d1[0] * (s0[0] - s2[0]) + d2[0] * (s1[0] - s0[0])) / det;
13442
+ 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;
13443
+ const b = (d0[1] * (s1[1] - s2[1]) + d1[1] * (s2[1] - s0[1]) + d2[1] * (s0[1] - s1[1])) / det;
13444
+ const d = (d0[1] * (s2[0] - s1[0]) + d1[1] * (s0[0] - s2[0]) + d2[1] * (s1[0] - s0[0])) / det;
13445
+ 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;
13446
+ return [a, b, c, d, e, f3];
13447
+ }
13448
+ function isRigBundle(json) {
13449
+ return json.mesh !== void 0 || json.bones !== void 0;
13450
+ }
13451
+ function getRigBundle(url, onReady) {
13452
+ if (meshCache.has(url)) return meshCache.get(url) ?? void 0;
13453
+ meshCache.set(url, void 0);
13454
+ fetch(url).then((r2) => {
13455
+ if (!r2.ok) throw new Error(`rigbundle fetch failed: HTTP ${r2.status}`);
13456
+ return r2.json();
13457
+ }).then((json) => {
13458
+ const ok = isRigBundle(json) || Array.isArray(json.vertices);
13459
+ if (!ok) throw new Error("rigbundle payload is neither a SkinMesh nor a rigbundle");
13460
+ meshCache.set(url, json);
13461
+ onReady();
13462
+ }).catch(() => {
13463
+ meshCache.set(url, null);
13464
+ onReady();
13465
+ });
13466
+ return void 0;
13467
+ }
13468
+ function skinMeshFailed(url) {
13469
+ return meshCache.get(url) === null;
13470
+ }
13471
+ function vertexBounds(points) {
13472
+ if (points.length === 0) return null;
13473
+ let minX = Infinity;
13474
+ let minY = Infinity;
13475
+ let maxX = -Infinity;
13476
+ let maxY = -Infinity;
13477
+ for (const [x, y] of points) {
13478
+ if (x < minX) minX = x;
13479
+ if (y < minY) minY = y;
13480
+ if (x > maxX) maxX = x;
13481
+ if (y > maxY) maxY = y;
13482
+ }
13483
+ return { minX, minY, maxX, maxY };
13484
+ }
13485
+ var IDENTITY, meshCache;
13486
+ var init_skinning = __esm({
13487
+ "lib/skinning.ts"() {
13488
+ IDENTITY = [1, 0, 0, 1, 0, 0];
13489
+ meshCache = /* @__PURE__ */ new Map();
13490
+ }
13491
+ });
13492
+ function warnMissingOnce2(reason, node) {
13493
+ const key = `${reason}:${node.asset.url}:${String(node.meshUrl)}`;
13494
+ if (loggedMissing2.has(key)) return;
13495
+ loggedMissing2.add(key);
13496
+ skinnedLog.warn("draw-skinned-mesh unresolvable \u2014 painting fallback", { reason, url: node.asset.url, meshUrl: node.meshUrl });
13497
+ }
13498
+ function dominantBone(v) {
13499
+ let best = -1;
13500
+ let bestW = 0;
13501
+ for (const { bone, w } of v.weights) {
13502
+ if (w > bestW) {
13503
+ bestW = w;
13504
+ best = bone;
13505
+ }
13506
+ }
13507
+ return best;
13508
+ }
13509
+ function resolveSkinPose(node, clip) {
13510
+ const effectiveClip = clip ?? node.clip;
13511
+ if (effectiveClip && node.frame !== void 0 && effectiveClip.frames.length > 0) {
13512
+ return effectiveClip.frames[node.frame % effectiveClip.frames.length];
13513
+ }
13514
+ return node.pose ?? {};
13515
+ }
13516
+ function resolveRig(node, dctx) {
13517
+ const fetched = node.meshUrl ? getRigBundle(node.meshUrl, dctx.invalidate) : void 0;
13518
+ if (node.meshUrl && fetched === void 0) return void 0;
13519
+ const rb = fetched && isRigBundle(fetched) ? fetched : void 0;
13520
+ const mesh = node.mesh ?? rb?.mesh ?? (!rb ? fetched : void 0);
13521
+ if (!mesh) return void 0;
13522
+ const bones = node.bones.length > 0 ? node.bones : rb?.bones ?? [];
13523
+ if (bones.length === 0) return void 0;
13524
+ const clip = node.clip ?? (node.clipName ? rb?.clips?.[node.clipName] : void 0);
13525
+ return { mesh, bones, clip };
13526
+ }
13527
+ function meshHasPixelUVs(mesh) {
13528
+ const cached = pixelUVByMesh.get(mesh);
13529
+ if (cached !== void 0) return cached;
13530
+ let pixel = false;
13531
+ for (const v of mesh.vertices) {
13532
+ if (v.u < 0 || v.u > 1 || v.v < 0 || v.v > 1) {
13533
+ pixel = true;
13534
+ break;
13535
+ }
13536
+ }
13537
+ pixelUVByMesh.set(mesh, pixel);
13538
+ return pixel;
13539
+ }
13540
+ function paintMeshPass(painter, node, bones, mesh, tex, dctx, pose, opacity) {
13541
+ const bindWorld = computeWorldMatrices(bones, {});
13542
+ const curWorld = computeWorldMatrices(bones, pose);
13543
+ const skinned = skinVertices(mesh, bindWorld, curWorld);
13544
+ const origin = dctx.projector.project(node.position);
13545
+ const tw = dctx.projector.tileWidth;
13546
+ painter.save();
13547
+ if (opacity !== 1) painter.setAlpha(opacity);
13548
+ for (const [i0, i1, i2] of mesh.triangles) {
13549
+ const v0 = mesh.vertices[i0];
13550
+ const v1 = mesh.vertices[i1];
13551
+ const v2 = mesh.vertices[i2];
13552
+ const p0 = skinned[i0];
13553
+ const p1 = skinned[i1];
13554
+ const p2 = skinned[i2];
13555
+ if (!v0 || !v1 || !v2 || !p0 || !p1 || !p2) continue;
13556
+ const dst = [
13557
+ [origin.x + p0[0] * tw, origin.y + p0[1] * tw],
13558
+ [origin.x + p1[0] * tw, origin.y + p1[1] * tw],
13559
+ [origin.x + p2[0] * tw, origin.y + p2[1] * tw]
13560
+ ];
13561
+ if (node.weightsOverlay || !tex) {
13562
+ if (!node.weightsOverlay) continue;
13563
+ const bone = dominantBone(v0);
13564
+ painter.fillPoly(
13565
+ dst.map(([x, y]) => ({ x, y })),
13566
+ WEIGHT_PALETTE[(bone % WEIGHT_PALETTE.length + WEIGHT_PALETTE.length) % WEIGHT_PALETTE.length]
13567
+ );
13568
+ continue;
13569
+ }
13570
+ const pixelUV = meshHasPixelUVs(mesh);
13571
+ const uv = pixelUV ? [[v0.u, v0.v], [v1.u, v1.v], [v2.u, v2.v]] : [
13572
+ [v0.u * tex.width, v0.v * tex.height],
13573
+ [v1.u * tex.width, v1.v * tex.height],
13574
+ [v2.u * tex.width, v2.v * tex.height]
13575
+ ];
13576
+ const affine = triangleAffine(uv, dst);
13577
+ if (!affine) continue;
13578
+ painter.save();
13579
+ painter.clipPath(`M ${dst[0][0]} ${dst[0][1]} L ${dst[1][0]} ${dst[1][1]} L ${dst[2][0]} ${dst[2][1]} Z`);
13580
+ painter.blitTransformed(tex, { x: 0, y: 0, w: tex.width, h: tex.height }, affine);
13581
+ painter.restore();
13582
+ }
13583
+ painter.restore();
13584
+ }
13585
+ function DrawSkinnedMesh(props) {
13586
+ const register = useContext(DrawableRegistryContext);
13587
+ if (register) register({ ...props, type: "draw-skinned-mesh" });
13588
+ return null;
13589
+ }
13590
+ var skinnedLog, loggedMissing2, WEIGHT_PALETTE, pixelUVByMesh, paintSkinnedMesh;
13591
+ var init_DrawSkinnedMesh = __esm({
13592
+ "components/game/atoms/DrawSkinnedMesh.tsx"() {
13593
+ "use client";
13594
+ init_imageCache();
13595
+ init_contract();
13596
+ init_registry();
13597
+ init_skinning();
13598
+ skinnedLog = createLogger("almadar:ui:draw-skinned-mesh");
13599
+ loggedMissing2 = /* @__PURE__ */ new Set();
13600
+ WEIGHT_PALETTE = Array.from({ length: 10 }, (_, i) => `hsl(${i * 36 % 360}, 70%, 55%)`);
13601
+ pixelUVByMesh = /* @__PURE__ */ new WeakMap();
13602
+ paintSkinnedMesh = (painter, node, dctx) => {
13603
+ if (!isValidScenePos(node.position)) return;
13604
+ const rig = resolveRig(node, dctx);
13605
+ if (!rig) {
13606
+ if (node.meshUrl && skinMeshFailed(node.meshUrl)) warnMissingOnce2("mesh-failed", node);
13607
+ else if (!node.mesh && !node.meshUrl) warnMissingOnce2("mesh-missing", node);
13608
+ return;
13609
+ }
13610
+ const tex = node.weightsOverlay ? null : painter.resolveTexture(node.asset.url);
13611
+ if (!node.weightsOverlay && !tex) {
13612
+ if (getImageStatus(node.asset.url) === "failed") warnMissingOnce2("texture-failed", node);
13613
+ return;
13614
+ }
13615
+ if (node.ghost && rig.clip && rig.clip.frames.length > 0) {
13616
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, rig.clip.frames[node.ghost.frame % rig.clip.frames.length], node.ghost.opacity);
13617
+ }
13618
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, resolveSkinPose(node, rig.clip), node.opacity ?? 1);
13619
+ };
13620
+ }
13621
+ });
13356
13622
  function applyMeshAnimation(node, timeMs) {
13357
13623
  const anim = node.animation;
13358
13624
  if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
@@ -13677,6 +13943,9 @@ function paintDrawable(painter, node, dctx) {
13677
13943
  case "draw-mesh":
13678
13944
  warnUnsupported2d("draw-mesh");
13679
13945
  break;
13946
+ case "draw-skinned-mesh":
13947
+ paintSkinnedMesh(painter, node, dctx);
13948
+ break;
13680
13949
  case "draw-sprite-layer":
13681
13950
  paintSpriteLayer(painter, node, dctx);
13682
13951
  break;
@@ -13701,6 +13970,7 @@ var init_paintDispatch = __esm({
13701
13970
  init_DrawShape();
13702
13971
  init_DrawText();
13703
13972
  init_DrawGroup();
13973
+ init_DrawSkinnedMesh();
13704
13974
  init_DrawMesh();
13705
13975
  init_DrawSpriteLayer();
13706
13976
  init_DrawShapeLayer();
@@ -13721,6 +13991,21 @@ function shapeDrawnItem(n) {
13721
13991
  if (n.shape !== "rect") return { pos: n.position, id: n.id };
13722
13992
  return { pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotate };
13723
13993
  }
13994
+ function skinnedMeshDrawnItem(n) {
13995
+ const mesh = n.mesh;
13996
+ if (!mesh || n.bones.length === 0) return { pos: n.position, id: n.id };
13997
+ const bindWorld = computeWorldMatrices(n.bones, {});
13998
+ const curWorld = computeWorldMatrices(n.bones, resolveSkinPose(n));
13999
+ const bounds = vertexBounds(skinVertices(mesh, bindWorld, curWorld));
14000
+ if (!bounds) return { pos: n.position, id: n.id };
14001
+ return {
14002
+ pos: { ...n.position, x: n.position.x + bounds.minX, y: n.position.y + bounds.minY },
14003
+ id: n.id,
14004
+ anchor: "top-left",
14005
+ width: bounds.maxX - bounds.minX,
14006
+ height: bounds.maxY - bounds.minY
14007
+ };
14008
+ }
13724
14009
  function collectDrawnItems(nodes) {
13725
14010
  const out = [];
13726
14011
  for (const n of nodes) {
@@ -13731,6 +14016,9 @@ function collectDrawnItems(nodes) {
13731
14016
  case "draw-shape":
13732
14017
  if (isValidScenePos(n.position)) out.push(shapeDrawnItem(n));
13733
14018
  break;
14019
+ case "draw-skinned-mesh":
14020
+ if (isValidScenePos(n.position)) out.push(skinnedMeshDrawnItem(n));
14021
+ break;
13734
14022
  case "draw-text":
13735
14023
  case "draw-group":
13736
14024
  case "draw-mesh":
@@ -13798,6 +14086,8 @@ function rotatePoint(p, center, radians) {
13798
14086
  var init_hitTest = __esm({
13799
14087
  "lib/drawable/hitTest.ts"() {
13800
14088
  init_contract();
14089
+ init_DrawSkinnedMesh();
14090
+ init_skinning();
13801
14091
  }
13802
14092
  });
13803
14093
  function normalizeBackdrop(bg) {
@@ -14987,7 +15277,7 @@ var init_StateIndicator = __esm({
14987
15277
  init_cn();
14988
15278
  init_GameIcon();
14989
15279
  DEFAULT_ASSET_URL = {
14990
- url: "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png",
15280
+ url: "https://almadar-kflow-assets.web.app/shared/ui-topdown-dungeon-board/default/features/chest/chest.png",
14991
15281
  role: "ui",
14992
15282
  category: "state"
14993
15283
  };
@@ -23547,9 +23837,6 @@ function getEffectSummary(effects) {
23547
23837
  case "emit":
23548
23838
  summaries.push(`\u2191 ${effect[1] || "event"}`);
23549
23839
  break;
23550
- case "notify":
23551
- summaries.push(`\u{1F4E7} ${effect[1] || ""}`);
23552
- break;
23553
23840
  case "persist":
23554
23841
  summaries.push(`\u{1F4BE} ${effect[1] || "save"}`);
23555
23842
  break;
@@ -23575,7 +23862,7 @@ function extractOutputsFromTransitions(transitions) {
23575
23862
  t.effects.forEach((effect) => {
23576
23863
  if (Array.isArray(effect)) {
23577
23864
  const op = effect[0];
23578
- if (["emit", "notify", "persist", "navigate", "call-service"].includes(op)) {
23865
+ if (["emit", "persist", "navigate", "call-service"].includes(op)) {
23579
23866
  if (isArraySExpr(effect)) {
23580
23867
  const humanText = formatSExprEffectToDomain(effect);
23581
23868
  outputs.add(humanText);
@@ -28959,6 +29246,18 @@ var init_useAuthContext = __esm({
28959
29246
  "hooks/useAuthContext.ts"() {
28960
29247
  }
28961
29248
  });
29249
+ function resolveActiveNavHref(items, currentPath) {
29250
+ let best;
29251
+ for (const item of items) {
29252
+ for (const href of item.children?.length ? [item.href, ...item.children.map((c) => c.href)] : [item.href]) {
29253
+ if (currentPath === href) return href;
29254
+ if (currentPath.startsWith(href + "/") && (!best || href.length > best.length)) {
29255
+ best = href;
29256
+ }
29257
+ }
29258
+ }
29259
+ return best;
29260
+ }
28962
29261
  var DashboardLayout, NavLink, NavLinkTopnav, NavLinkBottom;
28963
29262
  var init_DashboardLayout = __esm({
28964
29263
  "components/core/templates/DashboardLayout.tsx"() {
@@ -29030,6 +29329,7 @@ var init_DashboardLayout = __esm({
29030
29329
  const location = useLocation();
29031
29330
  const ctxPagePath = useCurrentPagePath();
29032
29331
  const activePath = (currentPath || void 0) ?? ctxPagePath ?? location.pathname;
29332
+ const activeHref = useMemo(() => resolveActiveNavHref(navItems, activePath), [navItems, activePath]);
29033
29333
  const { signOut: authSignOut } = useAuthContext();
29034
29334
  const user = userProp || (null);
29035
29335
  const { t } = useTranslate();
@@ -29122,7 +29422,7 @@ var init_DashboardLayout = __esm({
29122
29422
  NavLink,
29123
29423
  {
29124
29424
  item,
29125
- currentPath: activePath,
29425
+ activeHref,
29126
29426
  compact: isRail
29127
29427
  },
29128
29428
  item.href
@@ -29189,7 +29489,7 @@ var init_DashboardLayout = __esm({
29189
29489
  NavLinkTopnav,
29190
29490
  {
29191
29491
  item,
29192
- currentPath: activePath
29492
+ activeHref
29193
29493
  },
29194
29494
  item.href
29195
29495
  ))
@@ -29365,7 +29665,7 @@ var init_DashboardLayout = __esm({
29365
29665
  NavLinkBottom,
29366
29666
  {
29367
29667
  item,
29368
- currentPath: activePath
29668
+ activeHref
29369
29669
  },
29370
29670
  item.href
29371
29671
  )) })
@@ -29379,15 +29679,71 @@ var init_DashboardLayout = __esm({
29379
29679
  DashboardLayout.displayName = "DashboardLayout";
29380
29680
  NavLink = ({
29381
29681
  item,
29382
- currentPath,
29682
+ activeHref,
29383
29683
  compact = false
29384
29684
  }) => {
29385
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
29685
+ const hasChildren = !!item.children?.length;
29686
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
29687
+ const isActive = item.href === activeHref || childActive;
29688
+ const [open, setOpen] = useState(childActive);
29386
29689
  const iconClassName = cn(
29387
29690
  "h-5 w-5",
29388
29691
  isActive ? "text-primary-foreground" : "text-muted-foreground"
29389
29692
  );
29390
29693
  if (compact) {
29694
+ if (hasChildren) {
29695
+ return /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
29696
+ /* @__PURE__ */ jsx(
29697
+ Button,
29698
+ {
29699
+ variant: "ghost",
29700
+ title: item.label,
29701
+ "aria-label": item.label,
29702
+ "aria-expanded": open,
29703
+ className: cn(
29704
+ "flex items-center justify-center w-full px-2 py-2 rounded-lg transition-colors",
29705
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29706
+ ),
29707
+ onClick: () => setOpen(!open),
29708
+ children: /* @__PURE__ */ jsxs(Box, { as: "span", className: "relative inline-flex", children: [
29709
+ item.icon ? typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, className: iconClassName }) : /* @__PURE__ */ jsx(item.icon, { className: iconClassName }) : /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-semibold", as: "span", children: item.label.charAt(0).toUpperCase() }),
29710
+ item.badge && /* @__PURE__ */ jsx(
29711
+ Badge,
29712
+ {
29713
+ variant: isActive ? "primary" : "default",
29714
+ size: "sm",
29715
+ className: "absolute -top-2 -right-2 px-1 py-0 text-[10px] leading-4",
29716
+ children: item.badge
29717
+ }
29718
+ )
29719
+ ] })
29720
+ }
29721
+ ),
29722
+ open && /* @__PURE__ */ jsxs(Fragment, { children: [
29723
+ /* @__PURE__ */ jsx(Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
29724
+ /* @__PURE__ */ 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) => {
29725
+ const childIsActive = child.href === activeHref;
29726
+ return /* @__PURE__ */ jsxs(
29727
+ Link,
29728
+ {
29729
+ to: child.href,
29730
+ onClick: () => setOpen(false),
29731
+ className: cn(
29732
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
29733
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29734
+ ),
29735
+ children: [
29736
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsx(child.icon, { className: "h-4 w-4" })),
29737
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
29738
+ child.badge && /* @__PURE__ */ jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
29739
+ ]
29740
+ },
29741
+ child.href
29742
+ );
29743
+ }) })
29744
+ ] })
29745
+ ] });
29746
+ }
29391
29747
  return /* @__PURE__ */ jsx(
29392
29748
  Link,
29393
29749
  {
@@ -29413,6 +29769,78 @@ var init_DashboardLayout = __esm({
29413
29769
  }
29414
29770
  );
29415
29771
  }
29772
+ if (hasChildren) {
29773
+ return /* @__PURE__ */ jsxs(Box, { children: [
29774
+ /* @__PURE__ */ jsxs(
29775
+ Button,
29776
+ {
29777
+ variant: "ghost",
29778
+ "aria-expanded": open,
29779
+ className: cn(
29780
+ "flex items-center gap-3 w-full px-3 py-2 rounded-lg text-sm font-medium 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: [
29785
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, className: iconClassName }) : /* @__PURE__ */ jsx(item.icon, { className: iconClassName })),
29786
+ /* @__PURE__ */ jsx(
29787
+ Typography,
29788
+ {
29789
+ variant: "small",
29790
+ color: isActive ? "inherit" : "primary",
29791
+ className: "flex-1 text-left",
29792
+ as: "span",
29793
+ children: item.label
29794
+ }
29795
+ ),
29796
+ item.badge && /* @__PURE__ */ jsx(Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
29797
+ /* @__PURE__ */ jsx(
29798
+ Icon,
29799
+ {
29800
+ name: "chevron-down",
29801
+ className: cn("h-4 w-4 shrink-0 transition-transform", open && "rotate-180")
29802
+ }
29803
+ )
29804
+ ]
29805
+ }
29806
+ ),
29807
+ open && /* @__PURE__ */ jsx(
29808
+ VStack,
29809
+ {
29810
+ gap: "none",
29811
+ className: "mt-1 ml-4 pl-4 border-l border-border dark:border-border space-y-1",
29812
+ children: item.children.map((child) => {
29813
+ const childIsActive = child.href === activeHref;
29814
+ return /* @__PURE__ */ jsxs(
29815
+ Link,
29816
+ {
29817
+ to: child.href,
29818
+ className: cn(
29819
+ "flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm transition-colors",
29820
+ childIsActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29821
+ ),
29822
+ children: [
29823
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsx(child.icon, { className: "h-4 w-4" })),
29824
+ /* @__PURE__ */ jsx(
29825
+ Typography,
29826
+ {
29827
+ variant: "small",
29828
+ color: childIsActive ? "inherit" : "primary",
29829
+ className: "flex-1",
29830
+ as: "span",
29831
+ children: child.label
29832
+ }
29833
+ ),
29834
+ child.badge && /* @__PURE__ */ jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
29835
+ ]
29836
+ },
29837
+ child.href
29838
+ );
29839
+ })
29840
+ }
29841
+ )
29842
+ ] });
29843
+ }
29416
29844
  return /* @__PURE__ */ jsxs(
29417
29845
  Link,
29418
29846
  {
@@ -29441,9 +29869,63 @@ var init_DashboardLayout = __esm({
29441
29869
  NavLink.displayName = "NavLink";
29442
29870
  NavLinkTopnav = ({
29443
29871
  item,
29444
- currentPath
29872
+ activeHref
29445
29873
  }) => {
29446
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
29874
+ const hasChildren = !!item.children?.length;
29875
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
29876
+ const isActive = item.href === activeHref || childActive;
29877
+ const [open, setOpen] = useState(false);
29878
+ if (hasChildren) {
29879
+ return /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
29880
+ /* @__PURE__ */ jsxs(
29881
+ Button,
29882
+ {
29883
+ variant: "ghost",
29884
+ "aria-expanded": open,
29885
+ className: cn(
29886
+ "flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors whitespace-nowrap",
29887
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29888
+ ),
29889
+ onClick: () => setOpen(!open),
29890
+ children: [
29891
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4" })),
29892
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "inherit", className: "flex-1", as: "span", children: item.label }),
29893
+ item.badge && /* @__PURE__ */ jsx(Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
29894
+ /* @__PURE__ */ jsx(
29895
+ Icon,
29896
+ {
29897
+ name: "chevron-down",
29898
+ className: cn("h-3.5 w-3.5 shrink-0 transition-transform", open && "rotate-180")
29899
+ }
29900
+ )
29901
+ ]
29902
+ }
29903
+ ),
29904
+ open && /* @__PURE__ */ jsxs(Fragment, { children: [
29905
+ /* @__PURE__ */ jsx(Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
29906
+ /* @__PURE__ */ 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) => {
29907
+ const childIsActive = child.href === activeHref;
29908
+ return /* @__PURE__ */ jsxs(
29909
+ Link,
29910
+ {
29911
+ to: child.href,
29912
+ onClick: () => setOpen(false),
29913
+ className: cn(
29914
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
29915
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
29916
+ ),
29917
+ children: [
29918
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsx(child.icon, { className: "h-4 w-4" })),
29919
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
29920
+ child.badge && /* @__PURE__ */ jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
29921
+ ]
29922
+ },
29923
+ child.href
29924
+ );
29925
+ }) })
29926
+ ] })
29927
+ ] });
29928
+ }
29447
29929
  return /* @__PURE__ */ jsxs(
29448
29930
  Link,
29449
29931
  {
@@ -29463,9 +29945,9 @@ var init_DashboardLayout = __esm({
29463
29945
  NavLinkTopnav.displayName = "NavLinkTopnav";
29464
29946
  NavLinkBottom = ({
29465
29947
  item,
29466
- currentPath
29948
+ activeHref
29467
29949
  }) => {
29468
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
29950
+ const isActive = item.href === activeHref;
29469
29951
  const iconClassName = cn(
29470
29952
  "h-5 w-5",
29471
29953
  isActive ? "text-primary" : "text-muted-foreground"
@@ -30404,7 +30886,9 @@ function DataGrid({
30404
30886
  const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
30405
30887
  const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
30406
30888
  const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
30407
- const hasRenderProp = typeof children === "function";
30889
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
30890
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
30891
+ const hasRenderProp = typeof itemRenderer === "function";
30408
30892
  useEffect(() => {
30409
30893
  if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
30410
30894
  const schemaArr = Array.isArray(schemaRenderItem) ? schemaRenderItem : null;
@@ -30478,7 +30962,7 @@ function DataGrid({
30478
30962
  onClick: handleCardClick?.(itemData),
30479
30963
  className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
30480
30964
  children: [
30481
- children(itemData, index),
30965
+ itemRenderer(itemData, index),
30482
30966
  actionDefs.length > 0 && /* @__PURE__ */ 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__ */ jsxs(HStack, { gap: "xs", className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: [
30483
30967
  inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxs(
30484
30968
  Button,
@@ -30811,7 +31295,9 @@ function DataList({
30811
31295
  );
30812
31296
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
30813
31297
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
30814
- const hasRenderProp = typeof children === "function";
31298
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
31299
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
31300
+ const hasRenderProp = typeof itemRenderer === "function";
30815
31301
  React96__default.useEffect(() => {
30816
31302
  const renderItemTypeOf = typeof schemaRenderItem;
30817
31303
  const childrenTypeOf = typeof children;
@@ -31029,7 +31515,7 @@ function DataList({
31029
31515
  const actions = renderItemActions(itemData);
31030
31516
  return wrapDnd(
31031
31517
  /* @__PURE__ */ 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: [
31032
- children(itemData, index),
31518
+ itemRenderer(itemData, index),
31033
31519
  actions && /* @__PURE__ */ 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: [
31034
31520
  /* @__PURE__ */ 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 }),
31035
31521
  /* @__PURE__ */ 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__ */ jsx(
@@ -37573,7 +38059,7 @@ function TableView({
37573
38059
  columns,
37574
38060
  fields,
37575
38061
  itemActions,
37576
- maxInlineActions: _maxInlineActions,
38062
+ maxInlineActions,
37577
38063
  itemClickEvent = "",
37578
38064
  selectable = false,
37579
38065
  selectEvent,
@@ -37606,6 +38092,12 @@ function TableView({
37606
38092
  const [localSelected, setLocalSelected] = React96__default.useState(/* @__PURE__ */ new Set());
37607
38093
  const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
37608
38094
  const actionDefs = Array.isArray(itemActions) ? itemActions : [];
38095
+ const inlineActions = maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : [];
38096
+ const overflowActions = maxInlineActions != null ? actionDefs.slice(maxInlineActions) : actionDefs;
38097
+ const fireAction = (action, row) => eventBus.emit(`UI:${action.event}`, {
38098
+ id: row.id,
38099
+ row
38100
+ });
37609
38101
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
37610
38102
  const dnd = useDataDnd({
37611
38103
  items: allDataRaw,
@@ -37781,7 +38273,7 @@ function TableView({
37781
38273
  }
37782
38274
  return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
37783
38275
  }),
37784
- hasActions && /* @__PURE__ */ jsx(
38276
+ hasActions && /* @__PURE__ */ jsxs(
37785
38277
  HStack,
37786
38278
  {
37787
38279
  gap: "xs",
@@ -37795,23 +38287,38 @@ function TableView({
37795
38287
  "border-l border-[var(--color-border)]",
37796
38288
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
37797
38289
  ),
37798
- children: /* @__PURE__ */ jsx(
37799
- Menu,
37800
- {
37801
- position: "bottom-end",
37802
- trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
37803
- items: actionDefs.map((action) => ({
37804
- label: action.label,
37805
- icon: action.icon,
37806
- event: action.event,
37807
- variant: action.variant === "danger" ? "danger" : "default",
37808
- onClick: () => eventBus.emit(`UI:${action.event}`, {
37809
- id: row.id,
37810
- row
37811
- })
37812
- }))
37813
- }
37814
- )
38290
+ children: [
38291
+ inlineActions.map((action) => /* @__PURE__ */ jsxs(
38292
+ Button,
38293
+ {
38294
+ variant: action.variant ?? "ghost",
38295
+ size: "sm",
38296
+ onClick: () => fireAction(action, row),
38297
+ "data-testid": `action-${action.event}`,
38298
+ "data-row-id": String(row.id),
38299
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
38300
+ children: [
38301
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
38302
+ action.label
38303
+ ]
38304
+ },
38305
+ action.event
38306
+ )),
38307
+ overflowActions.length > 0 && /* @__PURE__ */ jsx(
38308
+ Menu,
38309
+ {
38310
+ position: "bottom-end",
38311
+ trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
38312
+ items: overflowActions.map((action) => ({
38313
+ label: action.label,
38314
+ icon: action.icon,
38315
+ event: action.event,
38316
+ variant: action.variant === "danger" ? "danger" : "default",
38317
+ onClick: () => fireAction(action, row)
38318
+ }))
38319
+ }
38320
+ )
38321
+ ]
37815
38322
  }
37816
38323
  )
37817
38324
  ]
@@ -53253,6 +53760,7 @@ var init_component_registry_generated = __esm({
53253
53760
  init_DrawMesh();
53254
53761
  init_DrawShape();
53255
53762
  init_DrawShapeLayer();
53763
+ init_DrawSkinnedMesh();
53256
53764
  init_DrawSprite();
53257
53765
  init_DrawSpriteLayer();
53258
53766
  init_DrawText();
@@ -53531,6 +54039,7 @@ var init_component_registry_generated = __esm({
53531
54039
  "DrawMesh": DrawMesh,
53532
54040
  "DrawShape": DrawShape,
53533
54041
  "DrawShapeLayer": DrawShapeLayer,
54042
+ "DrawSkinnedMesh": DrawSkinnedMesh,
53534
54043
  "DrawSprite": DrawSprite,
53535
54044
  "DrawSpriteLayer": DrawSpriteLayer,
53536
54045
  "DrawText": DrawText,
@@ -53859,7 +54368,7 @@ function renderContainedPortal(t, slot, content, onDismiss) {
53859
54368
  const slotId = `slot-${slot}`;
53860
54369
  switch (slot) {
53861
54370
  case "modal":
53862
- if (SELF_OVERLAY_PATTERNS.has(content.pattern)) {
54371
+ if (SELF_OVERLAY_PATTERN_TYPES.has(content.pattern)) {
53863
54372
  return /* @__PURE__ */ jsx(Box, { id: slotId, className: "contents", children: slotContent });
53864
54373
  }
53865
54374
  return /* @__PURE__ */ jsx(
@@ -54202,7 +54711,7 @@ function CompiledPortal({ slot, className, pattern, sourceTrait, children }) {
54202
54711
  children
54203
54712
  }
54204
54713
  );
54205
- wrapper = pattern !== void 0 && SELF_OVERLAY_PATTERNS.has(pattern) ? innerContent : /* @__PURE__ */ jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
54714
+ wrapper = pattern !== void 0 && SELF_OVERLAY_PATTERN_TYPES.has(pattern) ? innerContent : /* @__PURE__ */ jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
54206
54715
  break;
54207
54716
  }
54208
54717
  case "drawer":
@@ -54262,7 +54771,7 @@ function SlotPortal({
54262
54771
  let wrapper;
54263
54772
  switch (slot) {
54264
54773
  case "modal":
54265
- wrapper = SELF_OVERLAY_PATTERNS.has(content.pattern) ? /* @__PURE__ */ jsx(Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsx(
54774
+ wrapper = SELF_OVERLAY_PATTERN_TYPES.has(content.pattern) ? /* @__PURE__ */ jsx(Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsx(
54266
54775
  Modal,
54267
54776
  {
54268
54777
  isOpen: true,
@@ -54769,7 +55278,7 @@ function UISlotRenderer({
54769
55278
  }
54770
55279
  return wrapped;
54771
55280
  }
54772
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
55281
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
54773
55282
  var init_UISlotRenderer = __esm({
54774
55283
  "components/core/organisms/UISlotRenderer.tsx"() {
54775
55284
  "use client";
@@ -54800,7 +55309,6 @@ var init_UISlotRenderer = __esm({
54800
55309
  modal: "form",
54801
55310
  drawer: "form"
54802
55311
  };
54803
- SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
54804
55312
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
54805
55313
  "logo",
54806
55314
  "master",
@@ -58459,7 +58967,7 @@ function runTickFrame(entityId, orderedWriters, store) {
58459
58967
  }
58460
58968
  var log9 = createLogger("almadar:ui:effects:client-handlers");
58461
58969
  function createClientEffectHandlers(options) {
58462
- const { eventBus, slotSetter, navigate, navigateBack, notify, callService, liveEntity, persistDelegated } = options;
58970
+ const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated } = options;
58463
58971
  return {
58464
58972
  emit: (event, payload, source) => {
58465
58973
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
@@ -58515,9 +59023,6 @@ function createClientEffectHandlers(options) {
58515
59023
  }),
58516
59024
  navigateBack: navigateBack ?? (() => {
58517
59025
  log9.warn("No navigate-back handler, ignoring");
58518
- }),
58519
- notify: notify ?? ((msg, type) => {
58520
- log9.debug("notify", { type, message: msg });
58521
59026
  })
58522
59027
  };
58523
59028
  }
@@ -58716,6 +59221,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
58716
59221
  sharedEntityLog.debug("useTraitStateMachine start", { traitBindingsCount: traitBindings.length, traitNames: traitBindings.map((b) => b.trait.name) });
58717
59222
  const eventBus = useEventBus();
58718
59223
  const { entities } = useEntitySchema();
59224
+ const { user: viewer } = useUser();
58719
59225
  const traitConfigsByName = options?.traitConfigsByName;
58720
59226
  const orbitalsByTrait = options?.orbitalsByTrait;
58721
59227
  const callsiteCaptureChildrenByTrait = options?.callsiteCaptureChildrenByTrait;
@@ -59029,7 +59535,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59029
59535
  },
59030
59536
  navigate: optionsRef.current?.navigate,
59031
59537
  navigateBack: optionsRef.current?.navigateBack,
59032
- notify: optionsRef.current?.notify,
59033
59538
  callService: optionsRef.current?.callService,
59034
59539
  // The canonical client `set` writes `(set @entity.X)` straight into
59035
59540
  // the trait's one live store — the same object bound below. This is
@@ -59096,8 +59601,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59096
59601
  emit: clientHandlers.emit,
59097
59602
  renderUI: clientHandlers.renderUI,
59098
59603
  navigate: clientHandlers.navigate,
59099
- navigateBack: clientHandlers.navigateBack,
59100
- notify: clientHandlers.notify
59604
+ navigateBack: clientHandlers.navigateBack
59101
59605
  };
59102
59606
  }
59103
59607
  const sharedWrites = [];
@@ -59122,7 +59626,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59122
59626
  const bindingCtx = {
59123
59627
  entity: liveEntity,
59124
59628
  payload: payload || {},
59125
- state: previousState
59629
+ state: previousState,
59630
+ ...viewer ? { user: viewer } : {}
59126
59631
  };
59127
59632
  if (callsitePayload) {
59128
59633
  bindingCtx.callsitePayload = callsitePayload;
@@ -59219,7 +59724,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59219
59724
  });
59220
59725
  }
59221
59726
  return { emitted: emittedDuringExec, serverEffectResults };
59222
- }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait]);
59727
+ }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait, viewer]);
59223
59728
  const reRenderCallsiteCaptureChildren = useCallback(async (traitName, callsitePayload, entityByTrait, log12, visited = /* @__PURE__ */ new Set()) => {
59224
59729
  const children = callsiteCaptureChildrenByTrait?.get(traitName);
59225
59730
  if (!children || children.size === 0) return;
@@ -59264,7 +59769,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59264
59769
  const guardCtx = {
59265
59770
  entity,
59266
59771
  payload: {},
59267
- state: currentState
59772
+ state: currentState,
59773
+ ...viewer ? { user: viewer } : {}
59268
59774
  };
59269
59775
  const bindingCfg = getBindingConfig(binding) ?? traitConfigsByName?.[traitName];
59270
59776
  if (bindingCfg) {
@@ -59292,7 +59798,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59292
59798
  const emitFromSharedWriter = useCallback((event, payload, source) => {
59293
59799
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
59294
59800
  eventBus.emit(prefixedEvent, payload, source);
59295
- }, [eventBus]);
59801
+ }, [eventBus, viewer]);
59296
59802
  useEffect(() => {
59297
59803
  const scheduler = createTickScheduler();
59298
59804
  const timedTick = (key, fn) => () => perfTimeAsync(key, fn);
@@ -60159,7 +60665,8 @@ function OrbPreview({
60159
60665
  transport,
60160
60666
  getAccessToken,
60161
60667
  initialPagePath,
60162
- isolated = false
60668
+ isolated = false,
60669
+ user = null
60163
60670
  }) {
60164
60671
  if (serverUrl && transport) {
60165
60672
  throw new Error("OrbPreview accepts serverUrl OR transport, not both");
@@ -60169,11 +60676,7 @@ function OrbPreview({
60169
60676
  const handleLocalFallback = useCallback(() => {
60170
60677
  if (localFallback) return;
60171
60678
  setLocalFallback(true);
60172
- eventBus.emit("UI:NOTIFY", {
60173
- message: "Preview server unreachable \u2014 running locally without server-side state.",
60174
- severity: "warning"
60175
- });
60176
- }, [localFallback, eventBus]);
60679
+ }, [localFallback]);
60177
60680
  const parseResult = useMemo(() => {
60178
60681
  let parsed;
60179
60682
  if (typeof schema === "string") {
@@ -60332,7 +60835,7 @@ function OrbPreview({
60332
60835
  storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
60333
60836
  children: [
60334
60837
  /* @__PURE__ */ jsx(NavStackRefBridge, { apiRef: navStackRef }),
60335
- /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
60838
+ /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
60336
60839
  SchemaRunner,
60337
60840
  {
60338
60841
  schema: parseResult.schema,
@@ -61795,7 +62298,6 @@ var KNOWN_EFFECTS = /* @__PURE__ */ new Set([
61795
62298
  "fetch",
61796
62299
  "emit",
61797
62300
  "navigate",
61798
- "notify",
61799
62301
  "call-service",
61800
62302
  "spawn",
61801
62303
  "despawn",
@@ -64245,7 +64747,6 @@ function mapEffectType(label) {
64245
64747
  "fetch",
64246
64748
  "emit",
64247
64749
  "navigate",
64248
- "notify",
64249
64750
  "call-service",
64250
64751
  "spawn",
64251
64752
  "despawn",