@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
@@ -1,6 +1,6 @@
1
1
  import * as React87 from 'react';
2
2
  import React87__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, useSyncExternalStore, Suspense, useState, useLayoutEffect, lazy, useId } from 'react';
3
- import { EventBusContext, useTraitScopeChain, RenderSlotProvider, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, useEntitySchema, getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, TraitScopeProvider, useNavStack, ServerBridgeProvider, useCurrentPagePath, useRenderSlot, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext } from '@almadar/ui/providers';
3
+ import { EventBusContext, useTraitScopeChain, RenderSlotProvider, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, useEntitySchema, useUser, getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, TraitScopeProvider, useNavStack, ServerBridgeProvider, useCurrentPagePath, useRenderSlot, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext } from '@almadar/ui/providers';
4
4
  export { EntitySchemaProvider, ServerBridgeProvider, TraitContext, TraitProvider, useEntitySchema, useEntitySchemaOptional, useServerBridge, useTrait, useTraitContext } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import { perfStore, pushPerfEntry, wrapCallbackForEvent, perfStart, perfEnd, perfGauge, prepareSchemaForPreview, perfTimeAsync, collectTraitRefsFromResolvedTrait, buildOrbitalsByTrait, collectEmbeddedTraits } from '@almadar/runtime/ui';
@@ -78,7 +78,7 @@ import { CSS } from '@dnd-kit/utilities';
78
78
  import { ordered, lib } from 'emojilib';
79
79
  import { useNodeId, ReactFlowProvider, Handle, Position } from '@xyflow/react';
80
80
  import { forceSimulation, forceLink, forceManyBody, forceCollide, forceX, forceY } from 'd3-force';
81
- import { isDrawHostPattern, getPatternDefinition, getPatternFieldsContract, getComponentForPattern as getComponentForPattern$1 } from '@almadar/core/patterns';
81
+ import { SELF_OVERLAY_PATTERN_TYPES, isDrawHostPattern, getPatternDefinition, getPatternFieldsContract, getComponentForPattern as getComponentForPattern$1 } from '@almadar/core/patterns';
82
82
  import { isKnownStdOperator } from '@almadar/std/registry';
83
83
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
84
84
 
@@ -8346,7 +8346,7 @@ var init_DialogueBubble = __esm({
8346
8346
  init_Typography();
8347
8347
  init_GameIcon();
8348
8348
  DEFAULT_PORTRAIT = {
8349
- url: "https://almadar-kflow-assets.web.app/shared/characters/archetypes/04_hero.png",
8349
+ url: "https://almadar-kflow-assets.web.app/shared/ui-winter-ski-board/default/units/hero/hero.png",
8350
8350
  role: "effect",
8351
8351
  category: "character"
8352
8352
  };
@@ -9563,6 +9563,14 @@ function createWebPainter(ctx, onAssetLoad) {
9563
9563
  ctx.drawImage(img, dest.x, dest.y, dw, dh);
9564
9564
  }
9565
9565
  },
9566
+ blitTransformed(tex, src, affine) {
9567
+ const img = imageByHandle.get(tex);
9568
+ if (!img) return;
9569
+ ctx.save();
9570
+ ctx.transform(affine[0], affine[1], affine[2], affine[3], affine[4], affine[5]);
9571
+ ctx.drawImage(img, src.x, src.y, src.w, src.h, src.x, src.y, src.w, src.h);
9572
+ ctx.restore();
9573
+ },
9566
9574
  fillRect(x, y, w, h, style) {
9567
9575
  ctx.fillStyle = toCanvasStyle(style);
9568
9576
  ctx.fillRect(x, y, w, h);
@@ -10069,6 +10077,271 @@ var init_DrawGroup = __esm({
10069
10077
  init_registry();
10070
10078
  }
10071
10079
  });
10080
+
10081
+ // lib/skinning.ts
10082
+ function composeAffine(a, b) {
10083
+ return [
10084
+ a[0] * b[0] + a[2] * b[1],
10085
+ a[1] * b[0] + a[3] * b[1],
10086
+ a[0] * b[2] + a[2] * b[3],
10087
+ a[1] * b[2] + a[3] * b[3],
10088
+ a[0] * b[4] + a[2] * b[5] + a[4],
10089
+ a[1] * b[4] + a[3] * b[5] + a[5]
10090
+ ];
10091
+ }
10092
+ function invertAffine(m) {
10093
+ const det = m[0] * m[3] - m[1] * m[2];
10094
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return IDENTITY;
10095
+ const inv = 1 / det;
10096
+ return [
10097
+ m[3] * inv,
10098
+ -m[1] * inv,
10099
+ -m[2] * inv,
10100
+ m[0] * inv,
10101
+ (m[2] * m[5] - m[3] * m[4]) * inv,
10102
+ (m[1] * m[4] - m[0] * m[5]) * inv
10103
+ ];
10104
+ }
10105
+ function applyAffine(m, x, y) {
10106
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
10107
+ }
10108
+ function rotationAbout(pivot, degrees) {
10109
+ const r = degrees * Math.PI / 180;
10110
+ const cos = Math.cos(r);
10111
+ const sin = Math.sin(r);
10112
+ const [px, py] = pivot;
10113
+ return [cos, sin, -sin, cos, px - cos * px + sin * py, py - sin * px - cos * py];
10114
+ }
10115
+ function computeWorldMatrices(bones, pose) {
10116
+ const indexByName = /* @__PURE__ */ new Map();
10117
+ bones.forEach((b, i) => indexByName.set(b.name, i));
10118
+ const world = new Array(bones.length);
10119
+ const visiting = /* @__PURE__ */ new Set();
10120
+ const resolve = (i) => {
10121
+ const cached = world[i];
10122
+ if (cached) return cached;
10123
+ if (visiting.has(i)) return IDENTITY;
10124
+ visiting.add(i);
10125
+ const b = bones[i];
10126
+ const parentIndex = b.parent === null ? void 0 : indexByName.get(b.parent);
10127
+ const parentWorld = parentIndex === void 0 ? IDENTITY : resolve(parentIndex);
10128
+ const local = rotationAbout(b.pivot, pose[b.name] ?? 0);
10129
+ const m = composeAffine(parentWorld, local);
10130
+ visiting.delete(i);
10131
+ world[i] = m;
10132
+ return m;
10133
+ };
10134
+ return bones.map((_, i) => resolve(i));
10135
+ }
10136
+ function skinVertices(mesh, bindWorld, curWorld) {
10137
+ const skinMats = /* @__PURE__ */ new Map();
10138
+ const skinMat = (bone) => {
10139
+ let m = skinMats.get(bone);
10140
+ if (!m) {
10141
+ m = composeAffine(curWorld[bone] ?? IDENTITY, invertAffine(bindWorld[bone] ?? IDENTITY));
10142
+ skinMats.set(bone, m);
10143
+ }
10144
+ return m;
10145
+ };
10146
+ return mesh.vertices.map((v) => {
10147
+ if (v.weights.length === 0) return [v.x, v.y];
10148
+ let x = 0;
10149
+ let y = 0;
10150
+ for (const { bone, w } of v.weights) {
10151
+ const [bx, by] = applyAffine(skinMat(bone), v.x, v.y);
10152
+ x += w * bx;
10153
+ y += w * by;
10154
+ }
10155
+ return [x, y];
10156
+ });
10157
+ }
10158
+ function triangleAffine(src, dst) {
10159
+ const [s0, s1, s2] = src;
10160
+ const det = s0[0] * (s1[1] - s2[1]) + s1[0] * (s2[1] - s0[1]) + s2[0] * (s0[1] - s1[1]);
10161
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return null;
10162
+ const [d0, d1, d2] = dst;
10163
+ const a = (d0[0] * (s1[1] - s2[1]) + d1[0] * (s2[1] - s0[1]) + d2[0] * (s0[1] - s1[1])) / det;
10164
+ const c = (d0[0] * (s2[0] - s1[0]) + d1[0] * (s0[0] - s2[0]) + d2[0] * (s1[0] - s0[0])) / det;
10165
+ 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;
10166
+ const b = (d0[1] * (s1[1] - s2[1]) + d1[1] * (s2[1] - s0[1]) + d2[1] * (s0[1] - s1[1])) / det;
10167
+ const d = (d0[1] * (s2[0] - s1[0]) + d1[1] * (s0[0] - s2[0]) + d2[1] * (s1[0] - s0[0])) / det;
10168
+ 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;
10169
+ return [a, b, c, d, e, f3];
10170
+ }
10171
+ function isRigBundle(json) {
10172
+ return json.mesh !== void 0 || json.bones !== void 0;
10173
+ }
10174
+ function getRigBundle(url, onReady) {
10175
+ if (meshCache.has(url)) return meshCache.get(url) ?? void 0;
10176
+ meshCache.set(url, void 0);
10177
+ fetch(url).then((r) => {
10178
+ if (!r.ok) throw new Error(`rigbundle fetch failed: HTTP ${r.status}`);
10179
+ return r.json();
10180
+ }).then((json) => {
10181
+ const ok = isRigBundle(json) || Array.isArray(json.vertices);
10182
+ if (!ok) throw new Error("rigbundle payload is neither a SkinMesh nor a rigbundle");
10183
+ meshCache.set(url, json);
10184
+ onReady();
10185
+ }).catch(() => {
10186
+ meshCache.set(url, null);
10187
+ onReady();
10188
+ });
10189
+ return void 0;
10190
+ }
10191
+ function skinMeshFailed(url) {
10192
+ return meshCache.get(url) === null;
10193
+ }
10194
+ function vertexBounds(points) {
10195
+ if (points.length === 0) return null;
10196
+ let minX = Infinity;
10197
+ let minY = Infinity;
10198
+ let maxX = -Infinity;
10199
+ let maxY = -Infinity;
10200
+ for (const [x, y] of points) {
10201
+ if (x < minX) minX = x;
10202
+ if (y < minY) minY = y;
10203
+ if (x > maxX) maxX = x;
10204
+ if (y > maxY) maxY = y;
10205
+ }
10206
+ return { minX, minY, maxX, maxY };
10207
+ }
10208
+ var IDENTITY, meshCache;
10209
+ var init_skinning = __esm({
10210
+ "lib/skinning.ts"() {
10211
+ IDENTITY = [1, 0, 0, 1, 0, 0];
10212
+ meshCache = /* @__PURE__ */ new Map();
10213
+ }
10214
+ });
10215
+ function warnMissingOnce2(reason, node) {
10216
+ const key = `${reason}:${node.asset.url}:${String(node.meshUrl)}`;
10217
+ if (loggedMissing2.has(key)) return;
10218
+ loggedMissing2.add(key);
10219
+ skinnedLog.warn("draw-skinned-mesh unresolvable \u2014 painting fallback", { reason, url: node.asset.url, meshUrl: node.meshUrl });
10220
+ }
10221
+ function dominantBone(v) {
10222
+ let best = -1;
10223
+ let bestW = 0;
10224
+ for (const { bone, w } of v.weights) {
10225
+ if (w > bestW) {
10226
+ bestW = w;
10227
+ best = bone;
10228
+ }
10229
+ }
10230
+ return best;
10231
+ }
10232
+ function resolveSkinPose(node, clip) {
10233
+ const effectiveClip = clip ?? node.clip;
10234
+ if (effectiveClip && node.frame !== void 0 && effectiveClip.frames.length > 0) {
10235
+ return effectiveClip.frames[node.frame % effectiveClip.frames.length];
10236
+ }
10237
+ return node.pose ?? {};
10238
+ }
10239
+ function resolveRig(node, dctx) {
10240
+ const fetched = node.meshUrl ? getRigBundle(node.meshUrl, dctx.invalidate) : void 0;
10241
+ if (node.meshUrl && fetched === void 0) return void 0;
10242
+ const rb = fetched && isRigBundle(fetched) ? fetched : void 0;
10243
+ const mesh = node.mesh ?? rb?.mesh ?? (!rb ? fetched : void 0);
10244
+ if (!mesh) return void 0;
10245
+ const bones = node.bones.length > 0 ? node.bones : rb?.bones ?? [];
10246
+ if (bones.length === 0) return void 0;
10247
+ const clip = node.clip ?? (node.clipName ? rb?.clips?.[node.clipName] : void 0);
10248
+ return { mesh, bones, clip };
10249
+ }
10250
+ function meshHasPixelUVs(mesh) {
10251
+ const cached = pixelUVByMesh.get(mesh);
10252
+ if (cached !== void 0) return cached;
10253
+ let pixel = false;
10254
+ for (const v of mesh.vertices) {
10255
+ if (v.u < 0 || v.u > 1 || v.v < 0 || v.v > 1) {
10256
+ pixel = true;
10257
+ break;
10258
+ }
10259
+ }
10260
+ pixelUVByMesh.set(mesh, pixel);
10261
+ return pixel;
10262
+ }
10263
+ function paintMeshPass(painter, node, bones, mesh, tex, dctx, pose, opacity) {
10264
+ const bindWorld = computeWorldMatrices(bones, {});
10265
+ const curWorld = computeWorldMatrices(bones, pose);
10266
+ const skinned = skinVertices(mesh, bindWorld, curWorld);
10267
+ const origin = dctx.projector.project(node.position);
10268
+ const tw = dctx.projector.tileWidth;
10269
+ painter.save();
10270
+ if (opacity !== 1) painter.setAlpha(opacity);
10271
+ for (const [i0, i1, i2] of mesh.triangles) {
10272
+ const v0 = mesh.vertices[i0];
10273
+ const v1 = mesh.vertices[i1];
10274
+ const v2 = mesh.vertices[i2];
10275
+ const p0 = skinned[i0];
10276
+ const p1 = skinned[i1];
10277
+ const p2 = skinned[i2];
10278
+ if (!v0 || !v1 || !v2 || !p0 || !p1 || !p2) continue;
10279
+ const dst = [
10280
+ [origin.x + p0[0] * tw, origin.y + p0[1] * tw],
10281
+ [origin.x + p1[0] * tw, origin.y + p1[1] * tw],
10282
+ [origin.x + p2[0] * tw, origin.y + p2[1] * tw]
10283
+ ];
10284
+ if (node.weightsOverlay || !tex) {
10285
+ if (!node.weightsOverlay) continue;
10286
+ const bone = dominantBone(v0);
10287
+ painter.fillPoly(
10288
+ dst.map(([x, y]) => ({ x, y })),
10289
+ WEIGHT_PALETTE[(bone % WEIGHT_PALETTE.length + WEIGHT_PALETTE.length) % WEIGHT_PALETTE.length]
10290
+ );
10291
+ continue;
10292
+ }
10293
+ const pixelUV = meshHasPixelUVs(mesh);
10294
+ const uv = pixelUV ? [[v0.u, v0.v], [v1.u, v1.v], [v2.u, v2.v]] : [
10295
+ [v0.u * tex.width, v0.v * tex.height],
10296
+ [v1.u * tex.width, v1.v * tex.height],
10297
+ [v2.u * tex.width, v2.v * tex.height]
10298
+ ];
10299
+ const affine = triangleAffine(uv, dst);
10300
+ if (!affine) continue;
10301
+ painter.save();
10302
+ painter.clipPath(`M ${dst[0][0]} ${dst[0][1]} L ${dst[1][0]} ${dst[1][1]} L ${dst[2][0]} ${dst[2][1]} Z`);
10303
+ painter.blitTransformed(tex, { x: 0, y: 0, w: tex.width, h: tex.height }, affine);
10304
+ painter.restore();
10305
+ }
10306
+ painter.restore();
10307
+ }
10308
+ function DrawSkinnedMesh(props) {
10309
+ const register = useContext(DrawableRegistryContext);
10310
+ if (register) register({ ...props, type: "draw-skinned-mesh" });
10311
+ return null;
10312
+ }
10313
+ var skinnedLog, loggedMissing2, WEIGHT_PALETTE, pixelUVByMesh, paintSkinnedMesh;
10314
+ var init_DrawSkinnedMesh = __esm({
10315
+ "components/game/atoms/DrawSkinnedMesh.tsx"() {
10316
+ "use client";
10317
+ init_imageCache();
10318
+ init_contract();
10319
+ init_registry();
10320
+ init_skinning();
10321
+ skinnedLog = createLogger("almadar:ui:draw-skinned-mesh");
10322
+ loggedMissing2 = /* @__PURE__ */ new Set();
10323
+ WEIGHT_PALETTE = Array.from({ length: 10 }, (_, i) => `hsl(${i * 36 % 360}, 70%, 55%)`);
10324
+ pixelUVByMesh = /* @__PURE__ */ new WeakMap();
10325
+ paintSkinnedMesh = (painter, node, dctx) => {
10326
+ if (!isValidScenePos(node.position)) return;
10327
+ const rig = resolveRig(node, dctx);
10328
+ if (!rig) {
10329
+ if (node.meshUrl && skinMeshFailed(node.meshUrl)) warnMissingOnce2("mesh-failed", node);
10330
+ else if (!node.mesh && !node.meshUrl) warnMissingOnce2("mesh-missing", node);
10331
+ return;
10332
+ }
10333
+ const tex = node.weightsOverlay ? null : painter.resolveTexture(node.asset.url);
10334
+ if (!node.weightsOverlay && !tex) {
10335
+ if (getImageStatus(node.asset.url) === "failed") warnMissingOnce2("texture-failed", node);
10336
+ return;
10337
+ }
10338
+ if (node.ghost && rig.clip && rig.clip.frames.length > 0) {
10339
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, rig.clip.frames[node.ghost.frame % rig.clip.frames.length], node.ghost.opacity);
10340
+ }
10341
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, resolveSkinPose(node, rig.clip), node.opacity ?? 1);
10342
+ };
10343
+ }
10344
+ });
10072
10345
  function applyMeshAnimation(node, timeMs) {
10073
10346
  const anim = node.animation;
10074
10347
  if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
@@ -10393,6 +10666,9 @@ function paintDrawable(painter, node, dctx) {
10393
10666
  case "draw-mesh":
10394
10667
  warnUnsupported2d("draw-mesh");
10395
10668
  break;
10669
+ case "draw-skinned-mesh":
10670
+ paintSkinnedMesh(painter, node, dctx);
10671
+ break;
10396
10672
  case "draw-sprite-layer":
10397
10673
  paintSpriteLayer(painter, node, dctx);
10398
10674
  break;
@@ -10417,6 +10693,7 @@ var init_paintDispatch = __esm({
10417
10693
  init_DrawShape();
10418
10694
  init_DrawText();
10419
10695
  init_DrawGroup();
10696
+ init_DrawSkinnedMesh();
10420
10697
  init_DrawMesh();
10421
10698
  init_DrawSpriteLayer();
10422
10699
  init_DrawShapeLayer();
@@ -10437,6 +10714,21 @@ function shapeDrawnItem(n) {
10437
10714
  if (n.shape !== "rect") return { pos: n.position, id: n.id };
10438
10715
  return { pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotate };
10439
10716
  }
10717
+ function skinnedMeshDrawnItem(n) {
10718
+ const mesh = n.mesh;
10719
+ if (!mesh || n.bones.length === 0) return { pos: n.position, id: n.id };
10720
+ const bindWorld = computeWorldMatrices(n.bones, {});
10721
+ const curWorld = computeWorldMatrices(n.bones, resolveSkinPose(n));
10722
+ const bounds = vertexBounds(skinVertices(mesh, bindWorld, curWorld));
10723
+ if (!bounds) return { pos: n.position, id: n.id };
10724
+ return {
10725
+ pos: { ...n.position, x: n.position.x + bounds.minX, y: n.position.y + bounds.minY },
10726
+ id: n.id,
10727
+ anchor: "top-left",
10728
+ width: bounds.maxX - bounds.minX,
10729
+ height: bounds.maxY - bounds.minY
10730
+ };
10731
+ }
10440
10732
  function collectDrawnItems(nodes) {
10441
10733
  const out = [];
10442
10734
  for (const n of nodes) {
@@ -10447,6 +10739,9 @@ function collectDrawnItems(nodes) {
10447
10739
  case "draw-shape":
10448
10740
  if (isValidScenePos(n.position)) out.push(shapeDrawnItem(n));
10449
10741
  break;
10742
+ case "draw-skinned-mesh":
10743
+ if (isValidScenePos(n.position)) out.push(skinnedMeshDrawnItem(n));
10744
+ break;
10450
10745
  case "draw-text":
10451
10746
  case "draw-group":
10452
10747
  case "draw-mesh":
@@ -10514,6 +10809,8 @@ function rotatePoint(p, center, radians) {
10514
10809
  var init_hitTest = __esm({
10515
10810
  "lib/drawable/hitTest.ts"() {
10516
10811
  init_contract();
10812
+ init_DrawSkinnedMesh();
10813
+ init_skinning();
10517
10814
  }
10518
10815
  });
10519
10816
  function normalizeBackdrop(bg) {
@@ -11703,7 +12000,7 @@ var init_StateIndicator = __esm({
11703
12000
  init_cn();
11704
12001
  init_GameIcon();
11705
12002
  DEFAULT_ASSET_URL = {
11706
- url: "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png",
12003
+ url: "https://almadar-kflow-assets.web.app/shared/ui-topdown-dungeon-board/default/features/chest/chest.png",
11707
12004
  role: "ui",
11708
12005
  category: "state"
11709
12006
  };
@@ -15670,7 +15967,6 @@ var init_avl_atom_types = __esm({
15670
15967
  "persist": "data",
15671
15968
  "fetch": "data",
15672
15969
  "emit": "communication",
15673
- "notify": "communication",
15674
15970
  "call-service": "communication",
15675
15971
  "spawn": "lifecycle",
15676
15972
  "despawn": "lifecycle",
@@ -15818,12 +16114,6 @@ function effectIcon(type, x, y, s, color) {
15818
16114
  /* @__PURE__ */ jsx("line", { x1: x - s, y1: y, x2: x + s * 0.6, y2: y, stroke: color, strokeWidth: 1.5 }),
15819
16115
  /* @__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" })
15820
16116
  ] });
15821
- case "notify":
15822
- return /* @__PURE__ */ jsxs("g", { children: [
15823
- /* @__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 }),
15824
- /* @__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 }),
15825
- /* @__PURE__ */ jsx("circle", { cx: x, cy: y + s * 0.7, r: s * 0.2, fill: color })
15826
- ] });
15827
16117
  case "call-service":
15828
16118
  return /* @__PURE__ */ jsxs("g", { children: [
15829
16119
  /* @__PURE__ */ jsx("line", { x1: x - s, y1: y - s * 0.3, x2: x + s, y2: y - s * 0.3, stroke: color, strokeWidth: 1.5 }),
@@ -21027,9 +21317,6 @@ function getEffectSummary(effects) {
21027
21317
  case "emit":
21028
21318
  summaries.push(`\u2191 ${effect[1] || "event"}`);
21029
21319
  break;
21030
- case "notify":
21031
- summaries.push(`\u{1F4E7} ${effect[1] || ""}`);
21032
- break;
21033
21320
  case "persist":
21034
21321
  summaries.push(`\u{1F4BE} ${effect[1] || "save"}`);
21035
21322
  break;
@@ -21055,7 +21342,7 @@ function extractOutputsFromTransitions(transitions) {
21055
21342
  t.effects.forEach((effect) => {
21056
21343
  if (Array.isArray(effect)) {
21057
21344
  const op = effect[0];
21058
- if (["emit", "notify", "persist", "navigate", "call-service"].includes(op)) {
21345
+ if (["emit", "persist", "navigate", "call-service"].includes(op)) {
21059
21346
  if (isArraySExpr(effect)) {
21060
21347
  const humanText = formatSExprEffectToDomain(effect);
21061
21348
  outputs.add(humanText);
@@ -26358,6 +26645,18 @@ var init_DashboardGrid = __esm({
26358
26645
  DashboardGrid.displayName = "DashboardGrid";
26359
26646
  }
26360
26647
  });
26648
+ function resolveActiveNavHref(items, currentPath) {
26649
+ let best;
26650
+ for (const item of items) {
26651
+ for (const href of item.children?.length ? [item.href, ...item.children.map((c) => c.href)] : [item.href]) {
26652
+ if (currentPath === href) return href;
26653
+ if (currentPath.startsWith(href + "/") && (!best || href.length > best.length)) {
26654
+ best = href;
26655
+ }
26656
+ }
26657
+ }
26658
+ return best;
26659
+ }
26361
26660
  var DashboardLayout, NavLink, NavLinkTopnav, NavLinkBottom;
26362
26661
  var init_DashboardLayout = __esm({
26363
26662
  "components/core/templates/DashboardLayout.tsx"() {
@@ -26429,6 +26728,7 @@ var init_DashboardLayout = __esm({
26429
26728
  const location = useLocation();
26430
26729
  const ctxPagePath = useCurrentPagePath();
26431
26730
  const activePath = (currentPath || void 0) ?? ctxPagePath ?? location.pathname;
26731
+ const activeHref = useMemo(() => resolveActiveNavHref(navItems, activePath), [navItems, activePath]);
26432
26732
  const { signOut: authSignOut } = useAuthContext();
26433
26733
  const user = userProp || (null);
26434
26734
  const { t } = useTranslate();
@@ -26521,7 +26821,7 @@ var init_DashboardLayout = __esm({
26521
26821
  NavLink,
26522
26822
  {
26523
26823
  item,
26524
- currentPath: activePath,
26824
+ activeHref,
26525
26825
  compact: isRail
26526
26826
  },
26527
26827
  item.href
@@ -26588,7 +26888,7 @@ var init_DashboardLayout = __esm({
26588
26888
  NavLinkTopnav,
26589
26889
  {
26590
26890
  item,
26591
- currentPath: activePath
26891
+ activeHref
26592
26892
  },
26593
26893
  item.href
26594
26894
  ))
@@ -26764,7 +27064,7 @@ var init_DashboardLayout = __esm({
26764
27064
  NavLinkBottom,
26765
27065
  {
26766
27066
  item,
26767
- currentPath: activePath
27067
+ activeHref
26768
27068
  },
26769
27069
  item.href
26770
27070
  )) })
@@ -26778,15 +27078,71 @@ var init_DashboardLayout = __esm({
26778
27078
  DashboardLayout.displayName = "DashboardLayout";
26779
27079
  NavLink = ({
26780
27080
  item,
26781
- currentPath,
27081
+ activeHref,
26782
27082
  compact = false
26783
27083
  }) => {
26784
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
27084
+ const hasChildren = !!item.children?.length;
27085
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
27086
+ const isActive = item.href === activeHref || childActive;
27087
+ const [open, setOpen] = useState(childActive);
26785
27088
  const iconClassName = cn(
26786
27089
  "h-5 w-5",
26787
27090
  isActive ? "text-primary-foreground" : "text-muted-foreground"
26788
27091
  );
26789
27092
  if (compact) {
27093
+ if (hasChildren) {
27094
+ return /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
27095
+ /* @__PURE__ */ jsx(
27096
+ Button,
27097
+ {
27098
+ variant: "ghost",
27099
+ title: item.label,
27100
+ "aria-label": item.label,
27101
+ "aria-expanded": open,
27102
+ className: cn(
27103
+ "flex items-center justify-center w-full px-2 py-2 rounded-lg transition-colors",
27104
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27105
+ ),
27106
+ onClick: () => setOpen(!open),
27107
+ children: /* @__PURE__ */ jsxs(Box, { as: "span", className: "relative inline-flex", children: [
27108
+ 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() }),
27109
+ item.badge && /* @__PURE__ */ jsx(
27110
+ Badge,
27111
+ {
27112
+ variant: isActive ? "primary" : "default",
27113
+ size: "sm",
27114
+ className: "absolute -top-2 -right-2 px-1 py-0 text-[10px] leading-4",
27115
+ children: item.badge
27116
+ }
27117
+ )
27118
+ ] })
27119
+ }
27120
+ ),
27121
+ open && /* @__PURE__ */ jsxs(Fragment, { children: [
27122
+ /* @__PURE__ */ jsx(Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
27123
+ /* @__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) => {
27124
+ const childIsActive = child.href === activeHref;
27125
+ return /* @__PURE__ */ jsxs(
27126
+ Link,
27127
+ {
27128
+ to: child.href,
27129
+ onClick: () => setOpen(false),
27130
+ className: cn(
27131
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
27132
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27133
+ ),
27134
+ children: [
27135
+ 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" })),
27136
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
27137
+ child.badge && /* @__PURE__ */ jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
27138
+ ]
27139
+ },
27140
+ child.href
27141
+ );
27142
+ }) })
27143
+ ] })
27144
+ ] });
27145
+ }
26790
27146
  return /* @__PURE__ */ jsx(
26791
27147
  Link,
26792
27148
  {
@@ -26812,6 +27168,78 @@ var init_DashboardLayout = __esm({
26812
27168
  }
26813
27169
  );
26814
27170
  }
27171
+ if (hasChildren) {
27172
+ return /* @__PURE__ */ jsxs(Box, { children: [
27173
+ /* @__PURE__ */ jsxs(
27174
+ Button,
27175
+ {
27176
+ variant: "ghost",
27177
+ "aria-expanded": open,
27178
+ className: cn(
27179
+ "flex items-center gap-3 w-full px-3 py-2 rounded-lg text-sm font-medium transition-colors",
27180
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27181
+ ),
27182
+ onClick: () => setOpen(!open),
27183
+ children: [
27184
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, className: iconClassName }) : /* @__PURE__ */ jsx(item.icon, { className: iconClassName })),
27185
+ /* @__PURE__ */ jsx(
27186
+ Typography,
27187
+ {
27188
+ variant: "small",
27189
+ color: isActive ? "inherit" : "primary",
27190
+ className: "flex-1 text-left",
27191
+ as: "span",
27192
+ children: item.label
27193
+ }
27194
+ ),
27195
+ item.badge && /* @__PURE__ */ jsx(Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
27196
+ /* @__PURE__ */ jsx(
27197
+ Icon,
27198
+ {
27199
+ name: "chevron-down",
27200
+ className: cn("h-4 w-4 shrink-0 transition-transform", open && "rotate-180")
27201
+ }
27202
+ )
27203
+ ]
27204
+ }
27205
+ ),
27206
+ open && /* @__PURE__ */ jsx(
27207
+ VStack,
27208
+ {
27209
+ gap: "none",
27210
+ className: "mt-1 ml-4 pl-4 border-l border-border dark:border-border space-y-1",
27211
+ children: item.children.map((child) => {
27212
+ const childIsActive = child.href === activeHref;
27213
+ return /* @__PURE__ */ jsxs(
27214
+ Link,
27215
+ {
27216
+ to: child.href,
27217
+ className: cn(
27218
+ "flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm transition-colors",
27219
+ childIsActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27220
+ ),
27221
+ children: [
27222
+ 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" })),
27223
+ /* @__PURE__ */ jsx(
27224
+ Typography,
27225
+ {
27226
+ variant: "small",
27227
+ color: childIsActive ? "inherit" : "primary",
27228
+ className: "flex-1",
27229
+ as: "span",
27230
+ children: child.label
27231
+ }
27232
+ ),
27233
+ child.badge && /* @__PURE__ */ jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
27234
+ ]
27235
+ },
27236
+ child.href
27237
+ );
27238
+ })
27239
+ }
27240
+ )
27241
+ ] });
27242
+ }
26815
27243
  return /* @__PURE__ */ jsxs(
26816
27244
  Link,
26817
27245
  {
@@ -26840,9 +27268,63 @@ var init_DashboardLayout = __esm({
26840
27268
  NavLink.displayName = "NavLink";
26841
27269
  NavLinkTopnav = ({
26842
27270
  item,
26843
- currentPath
27271
+ activeHref
26844
27272
  }) => {
26845
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
27273
+ const hasChildren = !!item.children?.length;
27274
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
27275
+ const isActive = item.href === activeHref || childActive;
27276
+ const [open, setOpen] = useState(false);
27277
+ if (hasChildren) {
27278
+ return /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
27279
+ /* @__PURE__ */ jsxs(
27280
+ Button,
27281
+ {
27282
+ variant: "ghost",
27283
+ "aria-expanded": open,
27284
+ className: cn(
27285
+ "flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors whitespace-nowrap",
27286
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27287
+ ),
27288
+ onClick: () => setOpen(!open),
27289
+ children: [
27290
+ 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" })),
27291
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "inherit", className: "flex-1", as: "span", children: item.label }),
27292
+ item.badge && /* @__PURE__ */ jsx(Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
27293
+ /* @__PURE__ */ jsx(
27294
+ Icon,
27295
+ {
27296
+ name: "chevron-down",
27297
+ className: cn("h-3.5 w-3.5 shrink-0 transition-transform", open && "rotate-180")
27298
+ }
27299
+ )
27300
+ ]
27301
+ }
27302
+ ),
27303
+ open && /* @__PURE__ */ jsxs(Fragment, { children: [
27304
+ /* @__PURE__ */ jsx(Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
27305
+ /* @__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) => {
27306
+ const childIsActive = child.href === activeHref;
27307
+ return /* @__PURE__ */ jsxs(
27308
+ Link,
27309
+ {
27310
+ to: child.href,
27311
+ onClick: () => setOpen(false),
27312
+ className: cn(
27313
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
27314
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27315
+ ),
27316
+ children: [
27317
+ 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" })),
27318
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
27319
+ child.badge && /* @__PURE__ */ jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
27320
+ ]
27321
+ },
27322
+ child.href
27323
+ );
27324
+ }) })
27325
+ ] })
27326
+ ] });
27327
+ }
26846
27328
  return /* @__PURE__ */ jsxs(
26847
27329
  Link,
26848
27330
  {
@@ -26862,9 +27344,9 @@ var init_DashboardLayout = __esm({
26862
27344
  NavLinkTopnav.displayName = "NavLinkTopnav";
26863
27345
  NavLinkBottom = ({
26864
27346
  item,
26865
- currentPath
27347
+ activeHref
26866
27348
  }) => {
26867
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
27349
+ const isActive = item.href === activeHref;
26868
27350
  const iconClassName = cn(
26869
27351
  "h-5 w-5",
26870
27352
  isActive ? "text-primary" : "text-muted-foreground"
@@ -27803,7 +28285,9 @@ function DataGrid({
27803
28285
  const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
27804
28286
  const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
27805
28287
  const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
27806
- const hasRenderProp = typeof children === "function";
28288
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
28289
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
28290
+ const hasRenderProp = typeof itemRenderer === "function";
27807
28291
  useEffect(() => {
27808
28292
  if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
27809
28293
  const schemaArr = Array.isArray(schemaRenderItem) ? schemaRenderItem : null;
@@ -27877,7 +28361,7 @@ function DataGrid({
27877
28361
  onClick: handleCardClick?.(itemData),
27878
28362
  className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
27879
28363
  children: [
27880
- children(itemData, index),
28364
+ itemRenderer(itemData, index),
27881
28365
  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: [
27882
28366
  inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxs(
27883
28367
  Button,
@@ -28210,7 +28694,9 @@ function DataList({
28210
28694
  );
28211
28695
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
28212
28696
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
28213
- const hasRenderProp = typeof children === "function";
28697
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
28698
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
28699
+ const hasRenderProp = typeof itemRenderer === "function";
28214
28700
  React87__default.useEffect(() => {
28215
28701
  const renderItemTypeOf = typeof schemaRenderItem;
28216
28702
  const childrenTypeOf = typeof children;
@@ -28428,7 +28914,7 @@ function DataList({
28428
28914
  const actions = renderItemActions(itemData);
28429
28915
  return wrapDnd(
28430
28916
  /* @__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: [
28431
- children(itemData, index),
28917
+ itemRenderer(itemData, index),
28432
28918
  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: [
28433
28919
  /* @__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 }),
28434
28920
  /* @__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(
@@ -34802,7 +35288,7 @@ function TableView({
34802
35288
  columns,
34803
35289
  fields,
34804
35290
  itemActions,
34805
- maxInlineActions: _maxInlineActions,
35291
+ maxInlineActions,
34806
35292
  itemClickEvent = "",
34807
35293
  selectable = false,
34808
35294
  selectEvent,
@@ -34835,6 +35321,12 @@ function TableView({
34835
35321
  const [localSelected, setLocalSelected] = React87__default.useState(/* @__PURE__ */ new Set());
34836
35322
  const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
34837
35323
  const actionDefs = Array.isArray(itemActions) ? itemActions : [];
35324
+ const inlineActions = maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : [];
35325
+ const overflowActions = maxInlineActions != null ? actionDefs.slice(maxInlineActions) : actionDefs;
35326
+ const fireAction = (action, row) => eventBus.emit(`UI:${action.event}`, {
35327
+ id: row.id,
35328
+ row
35329
+ });
34838
35330
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
34839
35331
  const dnd = useDataDnd({
34840
35332
  items: allDataRaw,
@@ -35010,7 +35502,7 @@ function TableView({
35010
35502
  }
35011
35503
  return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
35012
35504
  }),
35013
- hasActions && /* @__PURE__ */ jsx(
35505
+ hasActions && /* @__PURE__ */ jsxs(
35014
35506
  HStack,
35015
35507
  {
35016
35508
  gap: "xs",
@@ -35024,23 +35516,38 @@ function TableView({
35024
35516
  "border-l border-[var(--color-border)]",
35025
35517
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
35026
35518
  ),
35027
- children: /* @__PURE__ */ jsx(
35028
- Menu,
35029
- {
35030
- position: "bottom-end",
35031
- 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" }) }),
35032
- items: actionDefs.map((action) => ({
35033
- label: action.label,
35034
- icon: action.icon,
35035
- event: action.event,
35036
- variant: action.variant === "danger" ? "danger" : "default",
35037
- onClick: () => eventBus.emit(`UI:${action.event}`, {
35038
- id: row.id,
35039
- row
35040
- })
35041
- }))
35042
- }
35043
- )
35519
+ children: [
35520
+ inlineActions.map((action) => /* @__PURE__ */ jsxs(
35521
+ Button,
35522
+ {
35523
+ variant: action.variant ?? "ghost",
35524
+ size: "sm",
35525
+ onClick: () => fireAction(action, row),
35526
+ "data-testid": `action-${action.event}`,
35527
+ "data-row-id": String(row.id),
35528
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
35529
+ children: [
35530
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
35531
+ action.label
35532
+ ]
35533
+ },
35534
+ action.event
35535
+ )),
35536
+ overflowActions.length > 0 && /* @__PURE__ */ jsx(
35537
+ Menu,
35538
+ {
35539
+ position: "bottom-end",
35540
+ 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" }) }),
35541
+ items: overflowActions.map((action) => ({
35542
+ label: action.label,
35543
+ icon: action.icon,
35544
+ event: action.event,
35545
+ variant: action.variant === "danger" ? "danger" : "default",
35546
+ onClick: () => fireAction(action, row)
35547
+ }))
35548
+ }
35549
+ )
35550
+ ]
35044
35551
  }
35045
35552
  )
35046
35553
  ]
@@ -50753,6 +51260,7 @@ var init_component_registry_generated = __esm({
50753
51260
  init_DrawMesh();
50754
51261
  init_DrawShape();
50755
51262
  init_DrawShapeLayer();
51263
+ init_DrawSkinnedMesh();
50756
51264
  init_DrawSprite();
50757
51265
  init_DrawSpriteLayer();
50758
51266
  init_DrawText();
@@ -51031,6 +51539,7 @@ var init_component_registry_generated = __esm({
51031
51539
  "DrawMesh": DrawMesh,
51032
51540
  "DrawShape": DrawShape,
51033
51541
  "DrawShapeLayer": DrawShapeLayer,
51542
+ "DrawSkinnedMesh": DrawSkinnedMesh,
51034
51543
  "DrawSprite": DrawSprite,
51035
51544
  "DrawSpriteLayer": DrawSpriteLayer,
51036
51545
  "DrawText": DrawText,
@@ -51359,7 +51868,7 @@ function renderContainedPortal(t, slot, content, onDismiss) {
51359
51868
  const slotId = `slot-${slot}`;
51360
51869
  switch (slot) {
51361
51870
  case "modal":
51362
- if (SELF_OVERLAY_PATTERNS.has(content.pattern)) {
51871
+ if (SELF_OVERLAY_PATTERN_TYPES.has(content.pattern)) {
51363
51872
  return /* @__PURE__ */ jsx(Box, { id: slotId, className: "contents", children: slotContent });
51364
51873
  }
51365
51874
  return /* @__PURE__ */ jsx(
@@ -51702,7 +52211,7 @@ function CompiledPortal({ slot, className, pattern, sourceTrait, children }) {
51702
52211
  children
51703
52212
  }
51704
52213
  );
51705
- wrapper = pattern !== void 0 && SELF_OVERLAY_PATTERNS.has(pattern) ? innerContent : /* @__PURE__ */ jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
52214
+ wrapper = pattern !== void 0 && SELF_OVERLAY_PATTERN_TYPES.has(pattern) ? innerContent : /* @__PURE__ */ jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
51706
52215
  break;
51707
52216
  }
51708
52217
  case "drawer":
@@ -51762,7 +52271,7 @@ function SlotPortal({
51762
52271
  let wrapper;
51763
52272
  switch (slot) {
51764
52273
  case "modal":
51765
- wrapper = SELF_OVERLAY_PATTERNS.has(content.pattern) ? /* @__PURE__ */ jsx(Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsx(
52274
+ wrapper = SELF_OVERLAY_PATTERN_TYPES.has(content.pattern) ? /* @__PURE__ */ jsx(Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsx(
51766
52275
  Modal,
51767
52276
  {
51768
52277
  isOpen: true,
@@ -52269,7 +52778,7 @@ function UISlotRenderer({
52269
52778
  }
52270
52779
  return wrapped;
52271
52780
  }
52272
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
52781
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
52273
52782
  var init_UISlotRenderer = __esm({
52274
52783
  "components/core/organisms/UISlotRenderer.tsx"() {
52275
52784
  "use client";
@@ -52300,7 +52809,6 @@ var init_UISlotRenderer = __esm({
52300
52809
  modal: "form",
52301
52810
  drawer: "form"
52302
52811
  };
52303
- SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
52304
52812
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
52305
52813
  "logo",
52306
52814
  "master",
@@ -52413,7 +52921,7 @@ function runTickFrame(entityId, orderedWriters, store) {
52413
52921
  }
52414
52922
  var log3 = createLogger("almadar:ui:effects:client-handlers");
52415
52923
  function createClientEffectHandlers(options) {
52416
- const { eventBus, slotSetter, navigate, navigateBack, notify, callService, liveEntity, persistDelegated } = options;
52924
+ const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated } = options;
52417
52925
  return {
52418
52926
  emit: (event, payload, source) => {
52419
52927
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
@@ -52469,9 +52977,6 @@ function createClientEffectHandlers(options) {
52469
52977
  }),
52470
52978
  navigateBack: navigateBack ?? (() => {
52471
52979
  log3.warn("No navigate-back handler, ignoring");
52472
- }),
52473
- notify: notify ?? ((msg, type) => {
52474
- log3.debug("notify", { type, message: msg });
52475
52980
  })
52476
52981
  };
52477
52982
  }
@@ -52842,6 +53347,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52842
53347
  sharedEntityLog.debug("useTraitStateMachine start", { traitBindingsCount: traitBindings.length, traitNames: traitBindings.map((b) => b.trait.name) });
52843
53348
  const eventBus = useEventBus();
52844
53349
  const { entities } = useEntitySchema();
53350
+ const { user: viewer } = useUser();
52845
53351
  const traitConfigsByName = options?.traitConfigsByName;
52846
53352
  const orbitalsByTrait = options?.orbitalsByTrait;
52847
53353
  const callsiteCaptureChildrenByTrait = options?.callsiteCaptureChildrenByTrait;
@@ -53155,7 +53661,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53155
53661
  },
53156
53662
  navigate: optionsRef.current?.navigate,
53157
53663
  navigateBack: optionsRef.current?.navigateBack,
53158
- notify: optionsRef.current?.notify,
53159
53664
  callService: optionsRef.current?.callService,
53160
53665
  // The canonical client `set` writes `(set @entity.X)` straight into
53161
53666
  // the trait's one live store — the same object bound below. This is
@@ -53222,8 +53727,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53222
53727
  emit: clientHandlers.emit,
53223
53728
  renderUI: clientHandlers.renderUI,
53224
53729
  navigate: clientHandlers.navigate,
53225
- navigateBack: clientHandlers.navigateBack,
53226
- notify: clientHandlers.notify
53730
+ navigateBack: clientHandlers.navigateBack
53227
53731
  };
53228
53732
  }
53229
53733
  const sharedWrites = [];
@@ -53248,7 +53752,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53248
53752
  const bindingCtx = {
53249
53753
  entity: liveEntity,
53250
53754
  payload: payload || {},
53251
- state: previousState
53755
+ state: previousState,
53756
+ ...viewer ? { user: viewer } : {}
53252
53757
  };
53253
53758
  if (callsitePayload) {
53254
53759
  bindingCtx.callsitePayload = callsitePayload;
@@ -53345,7 +53850,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53345
53850
  });
53346
53851
  }
53347
53852
  return { emitted: emittedDuringExec, serverEffectResults };
53348
- }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait]);
53853
+ }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait, viewer]);
53349
53854
  const reRenderCallsiteCaptureChildren = useCallback(async (traitName, callsitePayload, entityByTrait, log11, visited = /* @__PURE__ */ new Set()) => {
53350
53855
  const children = callsiteCaptureChildrenByTrait?.get(traitName);
53351
53856
  if (!children || children.size === 0) return;
@@ -53390,7 +53895,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53390
53895
  const guardCtx = {
53391
53896
  entity,
53392
53897
  payload: {},
53393
- state: currentState
53898
+ state: currentState,
53899
+ ...viewer ? { user: viewer } : {}
53394
53900
  };
53395
53901
  const bindingCfg = getBindingConfig(binding) ?? traitConfigsByName?.[traitName];
53396
53902
  if (bindingCfg) {
@@ -53418,7 +53924,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53418
53924
  const emitFromSharedWriter = useCallback((event, payload, source) => {
53419
53925
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
53420
53926
  eventBus.emit(prefixedEvent, payload, source);
53421
- }, [eventBus]);
53927
+ }, [eventBus, viewer]);
53422
53928
  useEffect(() => {
53423
53929
  const scheduler = createTickScheduler();
53424
53930
  const timedTick = (key, fn) => () => perfTimeAsync(key, fn);
@@ -54388,7 +54894,8 @@ function OrbPreview({
54388
54894
  transport,
54389
54895
  getAccessToken,
54390
54896
  initialPagePath,
54391
- isolated = false
54897
+ isolated = false,
54898
+ user = null
54392
54899
  }) {
54393
54900
  if (serverUrl && transport) {
54394
54901
  throw new Error("OrbPreview accepts serverUrl OR transport, not both");
@@ -54398,11 +54905,7 @@ function OrbPreview({
54398
54905
  const handleLocalFallback = useCallback(() => {
54399
54906
  if (localFallback) return;
54400
54907
  setLocalFallback(true);
54401
- eventBus.emit("UI:NOTIFY", {
54402
- message: "Preview server unreachable \u2014 running locally without server-side state.",
54403
- severity: "warning"
54404
- });
54405
- }, [localFallback, eventBus]);
54908
+ }, [localFallback]);
54406
54909
  const parseResult = useMemo(() => {
54407
54910
  let parsed;
54408
54911
  if (typeof schema === "string") {
@@ -54561,7 +55064,7 @@ function OrbPreview({
54561
55064
  storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
54562
55065
  children: [
54563
55066
  /* @__PURE__ */ jsx(NavStackRefBridge, { apiRef: navStackRef }),
54564
- /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
55067
+ /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
54565
55068
  SchemaRunner,
54566
55069
  {
54567
55070
  schema: parseResult.schema,
@@ -54736,7 +55239,7 @@ function useOrbitalPluginHost() {
54736
55239
  return { getState: ctx.getState, lastEvent: ctx.lastEvent, errors: ctx.errors };
54737
55240
  }
54738
55241
  function buildMockEffectHandlers(opts) {
54739
- const { pluginId, denySet, slotsRef, renderedSlotsRef, navigateRef, notifyRef } = opts;
55242
+ const { pluginId, denySet, slotsRef, renderedSlotsRef, navigateRef } = opts;
54740
55243
  const handlers = {
54741
55244
  renderUI: (slot, pattern, props, priority) => {
54742
55245
  if (!isUISlot(slot)) {
@@ -54756,11 +55259,6 @@ function buildMockEffectHandlers(opts) {
54756
55259
  } else if (navigateRef.current) {
54757
55260
  handlers.navigate = (path) => navigateRef.current?.(path);
54758
55261
  }
54759
- if (denySet.has("notify")) {
54760
- handlers.notify = (message, type) => log10.warn("notify:denied", { pluginId, message, type });
54761
- } else if (notifyRef.current) {
54762
- handlers.notify = (message, type) => notifyRef.current?.(message, type);
54763
- }
54764
55262
  if (denySet.has("persist")) {
54765
55263
  handlers.persist = async (action, entityType) => {
54766
55264
  log10.warn("persist:denied", { pluginId, action, entityType });
@@ -54781,7 +55279,6 @@ function PluginRuntimeMount({
54781
55279
  transport,
54782
55280
  deny,
54783
55281
  navigate,
54784
- notify,
54785
55282
  onTransition,
54786
55283
  onDispatched,
54787
55284
  onError
@@ -54793,8 +55290,6 @@ function PluginRuntimeMount({
54793
55290
  const renderedSlotsRef = useRef(/* @__PURE__ */ new Set());
54794
55291
  const navigateRef = useRef(navigate);
54795
55292
  navigateRef.current = navigate;
54796
- const notifyRef = useRef(notify);
54797
- notifyRef.current = notify;
54798
55293
  const [mockRuntime] = useState(() => {
54799
55294
  if (mode !== "mock") return void 0;
54800
55295
  return new OrbitalServerRuntime({
@@ -54805,8 +55300,7 @@ function PluginRuntimeMount({
54805
55300
  denySet: new Set(deny ?? []),
54806
55301
  slotsRef,
54807
55302
  renderedSlotsRef,
54808
- navigateRef,
54809
- notifyRef
55303
+ navigateRef
54810
55304
  })
54811
55305
  });
54812
55306
  });
@@ -54965,7 +55459,6 @@ function OrbitalPluginHost({
54965
55459
  transport,
54966
55460
  deny,
54967
55461
  navigate,
54968
- notify,
54969
55462
  onTransition,
54970
55463
  children
54971
55464
  }) {
@@ -55016,7 +55509,6 @@ function OrbitalPluginHost({
55016
55509
  transport,
55017
55510
  deny,
55018
55511
  navigate,
55019
- notify,
55020
55512
  onTransition,
55021
55513
  onDispatched: handleDispatched,
55022
55514
  onError: handleError