@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
@@ -8420,7 +8420,7 @@ var init_DialogueBubble = __esm({
8420
8420
  init_Typography();
8421
8421
  init_GameIcon();
8422
8422
  DEFAULT_PORTRAIT = {
8423
- url: "https://almadar-kflow-assets.web.app/shared/characters/archetypes/04_hero.png",
8423
+ url: "https://almadar-kflow-assets.web.app/shared/ui-winter-ski-board/default/units/hero/hero.png",
8424
8424
  role: "effect",
8425
8425
  category: "character"
8426
8426
  };
@@ -9637,6 +9637,14 @@ function createWebPainter(ctx, onAssetLoad) {
9637
9637
  ctx.drawImage(img, dest.x, dest.y, dw, dh);
9638
9638
  }
9639
9639
  },
9640
+ blitTransformed(tex, src, affine) {
9641
+ const img = imageByHandle.get(tex);
9642
+ if (!img) return;
9643
+ ctx.save();
9644
+ ctx.transform(affine[0], affine[1], affine[2], affine[3], affine[4], affine[5]);
9645
+ ctx.drawImage(img, src.x, src.y, src.w, src.h, src.x, src.y, src.w, src.h);
9646
+ ctx.restore();
9647
+ },
9640
9648
  fillRect(x, y, w, h, style) {
9641
9649
  ctx.fillStyle = toCanvasStyle(style);
9642
9650
  ctx.fillRect(x, y, w, h);
@@ -10143,6 +10151,271 @@ var init_DrawGroup = __esm({
10143
10151
  init_registry();
10144
10152
  }
10145
10153
  });
10154
+
10155
+ // lib/skinning.ts
10156
+ function composeAffine(a, b) {
10157
+ return [
10158
+ a[0] * b[0] + a[2] * b[1],
10159
+ a[1] * b[0] + a[3] * b[1],
10160
+ a[0] * b[2] + a[2] * b[3],
10161
+ a[1] * b[2] + a[3] * b[3],
10162
+ a[0] * b[4] + a[2] * b[5] + a[4],
10163
+ a[1] * b[4] + a[3] * b[5] + a[5]
10164
+ ];
10165
+ }
10166
+ function invertAffine(m) {
10167
+ const det = m[0] * m[3] - m[1] * m[2];
10168
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return IDENTITY;
10169
+ const inv = 1 / det;
10170
+ return [
10171
+ m[3] * inv,
10172
+ -m[1] * inv,
10173
+ -m[2] * inv,
10174
+ m[0] * inv,
10175
+ (m[2] * m[5] - m[3] * m[4]) * inv,
10176
+ (m[1] * m[4] - m[0] * m[5]) * inv
10177
+ ];
10178
+ }
10179
+ function applyAffine(m, x, y) {
10180
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
10181
+ }
10182
+ function rotationAbout(pivot, degrees) {
10183
+ const r = degrees * Math.PI / 180;
10184
+ const cos = Math.cos(r);
10185
+ const sin = Math.sin(r);
10186
+ const [px, py] = pivot;
10187
+ return [cos, sin, -sin, cos, px - cos * px + sin * py, py - sin * px - cos * py];
10188
+ }
10189
+ function computeWorldMatrices(bones, pose) {
10190
+ const indexByName = /* @__PURE__ */ new Map();
10191
+ bones.forEach((b, i) => indexByName.set(b.name, i));
10192
+ const world = new Array(bones.length);
10193
+ const visiting = /* @__PURE__ */ new Set();
10194
+ const resolve = (i) => {
10195
+ const cached = world[i];
10196
+ if (cached) return cached;
10197
+ if (visiting.has(i)) return IDENTITY;
10198
+ visiting.add(i);
10199
+ const b = bones[i];
10200
+ const parentIndex = b.parent === null ? void 0 : indexByName.get(b.parent);
10201
+ const parentWorld = parentIndex === void 0 ? IDENTITY : resolve(parentIndex);
10202
+ const local = rotationAbout(b.pivot, pose[b.name] ?? 0);
10203
+ const m = composeAffine(parentWorld, local);
10204
+ visiting.delete(i);
10205
+ world[i] = m;
10206
+ return m;
10207
+ };
10208
+ return bones.map((_, i) => resolve(i));
10209
+ }
10210
+ function skinVertices(mesh, bindWorld, curWorld) {
10211
+ const skinMats = /* @__PURE__ */ new Map();
10212
+ const skinMat = (bone) => {
10213
+ let m = skinMats.get(bone);
10214
+ if (!m) {
10215
+ m = composeAffine(curWorld[bone] ?? IDENTITY, invertAffine(bindWorld[bone] ?? IDENTITY));
10216
+ skinMats.set(bone, m);
10217
+ }
10218
+ return m;
10219
+ };
10220
+ return mesh.vertices.map((v) => {
10221
+ if (v.weights.length === 0) return [v.x, v.y];
10222
+ let x = 0;
10223
+ let y = 0;
10224
+ for (const { bone, w } of v.weights) {
10225
+ const [bx, by] = applyAffine(skinMat(bone), v.x, v.y);
10226
+ x += w * bx;
10227
+ y += w * by;
10228
+ }
10229
+ return [x, y];
10230
+ });
10231
+ }
10232
+ function triangleAffine(src, dst) {
10233
+ const [s0, s1, s2] = src;
10234
+ const det = s0[0] * (s1[1] - s2[1]) + s1[0] * (s2[1] - s0[1]) + s2[0] * (s0[1] - s1[1]);
10235
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return null;
10236
+ const [d0, d1, d2] = dst;
10237
+ const a = (d0[0] * (s1[1] - s2[1]) + d1[0] * (s2[1] - s0[1]) + d2[0] * (s0[1] - s1[1])) / det;
10238
+ const c = (d0[0] * (s2[0] - s1[0]) + d1[0] * (s0[0] - s2[0]) + d2[0] * (s1[0] - s0[0])) / det;
10239
+ 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;
10240
+ const b = (d0[1] * (s1[1] - s2[1]) + d1[1] * (s2[1] - s0[1]) + d2[1] * (s0[1] - s1[1])) / det;
10241
+ const d = (d0[1] * (s2[0] - s1[0]) + d1[1] * (s0[0] - s2[0]) + d2[1] * (s1[0] - s0[0])) / det;
10242
+ 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;
10243
+ return [a, b, c, d, e, f3];
10244
+ }
10245
+ function isRigBundle(json) {
10246
+ return json.mesh !== void 0 || json.bones !== void 0;
10247
+ }
10248
+ function getRigBundle(url, onReady) {
10249
+ if (meshCache.has(url)) return meshCache.get(url) ?? void 0;
10250
+ meshCache.set(url, void 0);
10251
+ fetch(url).then((r) => {
10252
+ if (!r.ok) throw new Error(`rigbundle fetch failed: HTTP ${r.status}`);
10253
+ return r.json();
10254
+ }).then((json) => {
10255
+ const ok = isRigBundle(json) || Array.isArray(json.vertices);
10256
+ if (!ok) throw new Error("rigbundle payload is neither a SkinMesh nor a rigbundle");
10257
+ meshCache.set(url, json);
10258
+ onReady();
10259
+ }).catch(() => {
10260
+ meshCache.set(url, null);
10261
+ onReady();
10262
+ });
10263
+ return void 0;
10264
+ }
10265
+ function skinMeshFailed(url) {
10266
+ return meshCache.get(url) === null;
10267
+ }
10268
+ function vertexBounds(points) {
10269
+ if (points.length === 0) return null;
10270
+ let minX = Infinity;
10271
+ let minY = Infinity;
10272
+ let maxX = -Infinity;
10273
+ let maxY = -Infinity;
10274
+ for (const [x, y] of points) {
10275
+ if (x < minX) minX = x;
10276
+ if (y < minY) minY = y;
10277
+ if (x > maxX) maxX = x;
10278
+ if (y > maxY) maxY = y;
10279
+ }
10280
+ return { minX, minY, maxX, maxY };
10281
+ }
10282
+ var IDENTITY, meshCache;
10283
+ var init_skinning = __esm({
10284
+ "lib/skinning.ts"() {
10285
+ IDENTITY = [1, 0, 0, 1, 0, 0];
10286
+ meshCache = /* @__PURE__ */ new Map();
10287
+ }
10288
+ });
10289
+ function warnMissingOnce2(reason, node) {
10290
+ const key = `${reason}:${node.asset.url}:${String(node.meshUrl)}`;
10291
+ if (loggedMissing2.has(key)) return;
10292
+ loggedMissing2.add(key);
10293
+ skinnedLog.warn("draw-skinned-mesh unresolvable \u2014 painting fallback", { reason, url: node.asset.url, meshUrl: node.meshUrl });
10294
+ }
10295
+ function dominantBone(v) {
10296
+ let best = -1;
10297
+ let bestW = 0;
10298
+ for (const { bone, w } of v.weights) {
10299
+ if (w > bestW) {
10300
+ bestW = w;
10301
+ best = bone;
10302
+ }
10303
+ }
10304
+ return best;
10305
+ }
10306
+ function resolveSkinPose(node, clip) {
10307
+ const effectiveClip = clip ?? node.clip;
10308
+ if (effectiveClip && node.frame !== void 0 && effectiveClip.frames.length > 0) {
10309
+ return effectiveClip.frames[node.frame % effectiveClip.frames.length];
10310
+ }
10311
+ return node.pose ?? {};
10312
+ }
10313
+ function resolveRig(node, dctx) {
10314
+ const fetched = node.meshUrl ? getRigBundle(node.meshUrl, dctx.invalidate) : void 0;
10315
+ if (node.meshUrl && fetched === void 0) return void 0;
10316
+ const rb = fetched && isRigBundle(fetched) ? fetched : void 0;
10317
+ const mesh = node.mesh ?? rb?.mesh ?? (!rb ? fetched : void 0);
10318
+ if (!mesh) return void 0;
10319
+ const bones = node.bones.length > 0 ? node.bones : rb?.bones ?? [];
10320
+ if (bones.length === 0) return void 0;
10321
+ const clip = node.clip ?? (node.clipName ? rb?.clips?.[node.clipName] : void 0);
10322
+ return { mesh, bones, clip };
10323
+ }
10324
+ function meshHasPixelUVs(mesh) {
10325
+ const cached = pixelUVByMesh.get(mesh);
10326
+ if (cached !== void 0) return cached;
10327
+ let pixel = false;
10328
+ for (const v of mesh.vertices) {
10329
+ if (v.u < 0 || v.u > 1 || v.v < 0 || v.v > 1) {
10330
+ pixel = true;
10331
+ break;
10332
+ }
10333
+ }
10334
+ pixelUVByMesh.set(mesh, pixel);
10335
+ return pixel;
10336
+ }
10337
+ function paintMeshPass(painter, node, bones, mesh, tex, dctx, pose, opacity) {
10338
+ const bindWorld = computeWorldMatrices(bones, {});
10339
+ const curWorld = computeWorldMatrices(bones, pose);
10340
+ const skinned = skinVertices(mesh, bindWorld, curWorld);
10341
+ const origin = dctx.projector.project(node.position);
10342
+ const tw = dctx.projector.tileWidth;
10343
+ painter.save();
10344
+ if (opacity !== 1) painter.setAlpha(opacity);
10345
+ for (const [i0, i1, i2] of mesh.triangles) {
10346
+ const v0 = mesh.vertices[i0];
10347
+ const v1 = mesh.vertices[i1];
10348
+ const v2 = mesh.vertices[i2];
10349
+ const p0 = skinned[i0];
10350
+ const p1 = skinned[i1];
10351
+ const p2 = skinned[i2];
10352
+ if (!v0 || !v1 || !v2 || !p0 || !p1 || !p2) continue;
10353
+ const dst = [
10354
+ [origin.x + p0[0] * tw, origin.y + p0[1] * tw],
10355
+ [origin.x + p1[0] * tw, origin.y + p1[1] * tw],
10356
+ [origin.x + p2[0] * tw, origin.y + p2[1] * tw]
10357
+ ];
10358
+ if (node.weightsOverlay || !tex) {
10359
+ if (!node.weightsOverlay) continue;
10360
+ const bone = dominantBone(v0);
10361
+ painter.fillPoly(
10362
+ dst.map(([x, y]) => ({ x, y })),
10363
+ WEIGHT_PALETTE[(bone % WEIGHT_PALETTE.length + WEIGHT_PALETTE.length) % WEIGHT_PALETTE.length]
10364
+ );
10365
+ continue;
10366
+ }
10367
+ const pixelUV = meshHasPixelUVs(mesh);
10368
+ const uv = pixelUV ? [[v0.u, v0.v], [v1.u, v1.v], [v2.u, v2.v]] : [
10369
+ [v0.u * tex.width, v0.v * tex.height],
10370
+ [v1.u * tex.width, v1.v * tex.height],
10371
+ [v2.u * tex.width, v2.v * tex.height]
10372
+ ];
10373
+ const affine = triangleAffine(uv, dst);
10374
+ if (!affine) continue;
10375
+ painter.save();
10376
+ painter.clipPath(`M ${dst[0][0]} ${dst[0][1]} L ${dst[1][0]} ${dst[1][1]} L ${dst[2][0]} ${dst[2][1]} Z`);
10377
+ painter.blitTransformed(tex, { x: 0, y: 0, w: tex.width, h: tex.height }, affine);
10378
+ painter.restore();
10379
+ }
10380
+ painter.restore();
10381
+ }
10382
+ function DrawSkinnedMesh(props) {
10383
+ const register = React87.useContext(DrawableRegistryContext);
10384
+ if (register) register({ ...props, type: "draw-skinned-mesh" });
10385
+ return null;
10386
+ }
10387
+ var skinnedLog, loggedMissing2, WEIGHT_PALETTE, pixelUVByMesh, paintSkinnedMesh;
10388
+ var init_DrawSkinnedMesh = __esm({
10389
+ "components/game/atoms/DrawSkinnedMesh.tsx"() {
10390
+ "use client";
10391
+ init_imageCache();
10392
+ init_contract();
10393
+ init_registry();
10394
+ init_skinning();
10395
+ skinnedLog = logger.createLogger("almadar:ui:draw-skinned-mesh");
10396
+ loggedMissing2 = /* @__PURE__ */ new Set();
10397
+ WEIGHT_PALETTE = Array.from({ length: 10 }, (_, i) => `hsl(${i * 36 % 360}, 70%, 55%)`);
10398
+ pixelUVByMesh = /* @__PURE__ */ new WeakMap();
10399
+ paintSkinnedMesh = (painter, node, dctx) => {
10400
+ if (!isValidScenePos(node.position)) return;
10401
+ const rig = resolveRig(node, dctx);
10402
+ if (!rig) {
10403
+ if (node.meshUrl && skinMeshFailed(node.meshUrl)) warnMissingOnce2("mesh-failed", node);
10404
+ else if (!node.mesh && !node.meshUrl) warnMissingOnce2("mesh-missing", node);
10405
+ return;
10406
+ }
10407
+ const tex = node.weightsOverlay ? null : painter.resolveTexture(node.asset.url);
10408
+ if (!node.weightsOverlay && !tex) {
10409
+ if (getImageStatus(node.asset.url) === "failed") warnMissingOnce2("texture-failed", node);
10410
+ return;
10411
+ }
10412
+ if (node.ghost && rig.clip && rig.clip.frames.length > 0) {
10413
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, rig.clip.frames[node.ghost.frame % rig.clip.frames.length], node.ghost.opacity);
10414
+ }
10415
+ paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, resolveSkinPose(node, rig.clip), node.opacity ?? 1);
10416
+ };
10417
+ }
10418
+ });
10146
10419
  function applyMeshAnimation(node, timeMs) {
10147
10420
  const anim = node.animation;
10148
10421
  if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
@@ -10467,6 +10740,9 @@ function paintDrawable(painter, node, dctx) {
10467
10740
  case "draw-mesh":
10468
10741
  warnUnsupported2d("draw-mesh");
10469
10742
  break;
10743
+ case "draw-skinned-mesh":
10744
+ paintSkinnedMesh(painter, node, dctx);
10745
+ break;
10470
10746
  case "draw-sprite-layer":
10471
10747
  paintSpriteLayer(painter, node, dctx);
10472
10748
  break;
@@ -10491,6 +10767,7 @@ var init_paintDispatch = __esm({
10491
10767
  init_DrawShape();
10492
10768
  init_DrawText();
10493
10769
  init_DrawGroup();
10770
+ init_DrawSkinnedMesh();
10494
10771
  init_DrawMesh();
10495
10772
  init_DrawSpriteLayer();
10496
10773
  init_DrawShapeLayer();
@@ -10511,6 +10788,21 @@ function shapeDrawnItem(n) {
10511
10788
  if (n.shape !== "rect") return { pos: n.position, id: n.id };
10512
10789
  return { pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotate };
10513
10790
  }
10791
+ function skinnedMeshDrawnItem(n) {
10792
+ const mesh = n.mesh;
10793
+ if (!mesh || n.bones.length === 0) return { pos: n.position, id: n.id };
10794
+ const bindWorld = computeWorldMatrices(n.bones, {});
10795
+ const curWorld = computeWorldMatrices(n.bones, resolveSkinPose(n));
10796
+ const bounds = vertexBounds(skinVertices(mesh, bindWorld, curWorld));
10797
+ if (!bounds) return { pos: n.position, id: n.id };
10798
+ return {
10799
+ pos: { ...n.position, x: n.position.x + bounds.minX, y: n.position.y + bounds.minY },
10800
+ id: n.id,
10801
+ anchor: "top-left",
10802
+ width: bounds.maxX - bounds.minX,
10803
+ height: bounds.maxY - bounds.minY
10804
+ };
10805
+ }
10514
10806
  function collectDrawnItems(nodes) {
10515
10807
  const out = [];
10516
10808
  for (const n of nodes) {
@@ -10521,6 +10813,9 @@ function collectDrawnItems(nodes) {
10521
10813
  case "draw-shape":
10522
10814
  if (isValidScenePos(n.position)) out.push(shapeDrawnItem(n));
10523
10815
  break;
10816
+ case "draw-skinned-mesh":
10817
+ if (isValidScenePos(n.position)) out.push(skinnedMeshDrawnItem(n));
10818
+ break;
10524
10819
  case "draw-text":
10525
10820
  case "draw-group":
10526
10821
  case "draw-mesh":
@@ -10588,6 +10883,8 @@ function rotatePoint(p, center, radians) {
10588
10883
  var init_hitTest = __esm({
10589
10884
  "lib/drawable/hitTest.ts"() {
10590
10885
  init_contract();
10886
+ init_DrawSkinnedMesh();
10887
+ init_skinning();
10591
10888
  }
10592
10889
  });
10593
10890
  function normalizeBackdrop(bg) {
@@ -11777,7 +12074,7 @@ var init_StateIndicator = __esm({
11777
12074
  init_cn();
11778
12075
  init_GameIcon();
11779
12076
  DEFAULT_ASSET_URL = {
11780
- url: "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png",
12077
+ url: "https://almadar-kflow-assets.web.app/shared/ui-topdown-dungeon-board/default/features/chest/chest.png",
11781
12078
  role: "ui",
11782
12079
  category: "state"
11783
12080
  };
@@ -15744,7 +16041,6 @@ var init_avl_atom_types = __esm({
15744
16041
  "persist": "data",
15745
16042
  "fetch": "data",
15746
16043
  "emit": "communication",
15747
- "notify": "communication",
15748
16044
  "call-service": "communication",
15749
16045
  "spawn": "lifecycle",
15750
16046
  "despawn": "lifecycle",
@@ -15892,12 +16188,6 @@ function effectIcon(type, x, y, s, color) {
15892
16188
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - s, y1: y, x2: x + s * 0.6, y2: y, stroke: color, strokeWidth: 1.5 }),
15893
16189
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: `${x + s * 0.1},${y - s * 0.5} ${x + s},${y} ${x + s * 0.1},${y + s * 0.5}`, fill: "none", stroke: color, strokeWidth: 1.5, strokeLinejoin: "round" })
15894
16190
  ] });
15895
- case "notify":
15896
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15897
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: `M${x - s * 0.7},${y + s * 0.3} Q${x - s * 0.7},${y - s} ${x},${y - s} Q${x + s * 0.7},${y - s} ${x + s * 0.7},${y + s * 0.3} Z`, fill: "none", stroke: color, strokeWidth: 1.5 }),
15898
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - s * 0.8, y1: y + s * 0.3, x2: x + s * 0.8, y2: y + s * 0.3, stroke: color, strokeWidth: 1.5 }),
15899
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x, cy: y + s * 0.7, r: s * 0.2, fill: color })
15900
- ] });
15901
16191
  case "call-service":
15902
16192
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15903
16193
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - s, y1: y - s * 0.3, x2: x + s, y2: y - s * 0.3, stroke: color, strokeWidth: 1.5 }),
@@ -21101,9 +21391,6 @@ function getEffectSummary(effects) {
21101
21391
  case "emit":
21102
21392
  summaries.push(`\u2191 ${effect[1] || "event"}`);
21103
21393
  break;
21104
- case "notify":
21105
- summaries.push(`\u{1F4E7} ${effect[1] || ""}`);
21106
- break;
21107
21394
  case "persist":
21108
21395
  summaries.push(`\u{1F4BE} ${effect[1] || "save"}`);
21109
21396
  break;
@@ -21129,7 +21416,7 @@ function extractOutputsFromTransitions(transitions) {
21129
21416
  t.effects.forEach((effect) => {
21130
21417
  if (Array.isArray(effect)) {
21131
21418
  const op = effect[0];
21132
- if (["emit", "notify", "persist", "navigate", "call-service"].includes(op)) {
21419
+ if (["emit", "persist", "navigate", "call-service"].includes(op)) {
21133
21420
  if (isArraySExpr(effect)) {
21134
21421
  const humanText = formatSExprEffectToDomain(effect);
21135
21422
  outputs.add(humanText);
@@ -26432,6 +26719,18 @@ var init_DashboardGrid = __esm({
26432
26719
  DashboardGrid.displayName = "DashboardGrid";
26433
26720
  }
26434
26721
  });
26722
+ function resolveActiveNavHref(items, currentPath) {
26723
+ let best;
26724
+ for (const item of items) {
26725
+ for (const href of item.children?.length ? [item.href, ...item.children.map((c) => c.href)] : [item.href]) {
26726
+ if (currentPath === href) return href;
26727
+ if (currentPath.startsWith(href + "/") && (!best || href.length > best.length)) {
26728
+ best = href;
26729
+ }
26730
+ }
26731
+ }
26732
+ return best;
26733
+ }
26435
26734
  var DashboardLayout, NavLink, NavLinkTopnav, NavLinkBottom;
26436
26735
  var init_DashboardLayout = __esm({
26437
26736
  "components/core/templates/DashboardLayout.tsx"() {
@@ -26503,6 +26802,7 @@ var init_DashboardLayout = __esm({
26503
26802
  const location = reactRouterDom.useLocation();
26504
26803
  const ctxPagePath = providers.useCurrentPagePath();
26505
26804
  const activePath = (currentPath || void 0) ?? ctxPagePath ?? location.pathname;
26805
+ const activeHref = React87.useMemo(() => resolveActiveNavHref(navItems, activePath), [navItems, activePath]);
26506
26806
  const { signOut: authSignOut } = useAuthContext();
26507
26807
  const user = userProp || (null);
26508
26808
  const { t } = hooks.useTranslate();
@@ -26595,7 +26895,7 @@ var init_DashboardLayout = __esm({
26595
26895
  NavLink,
26596
26896
  {
26597
26897
  item,
26598
- currentPath: activePath,
26898
+ activeHref,
26599
26899
  compact: isRail
26600
26900
  },
26601
26901
  item.href
@@ -26662,7 +26962,7 @@ var init_DashboardLayout = __esm({
26662
26962
  NavLinkTopnav,
26663
26963
  {
26664
26964
  item,
26665
- currentPath: activePath
26965
+ activeHref
26666
26966
  },
26667
26967
  item.href
26668
26968
  ))
@@ -26838,7 +27138,7 @@ var init_DashboardLayout = __esm({
26838
27138
  NavLinkBottom,
26839
27139
  {
26840
27140
  item,
26841
- currentPath: activePath
27141
+ activeHref
26842
27142
  },
26843
27143
  item.href
26844
27144
  )) })
@@ -26852,15 +27152,71 @@ var init_DashboardLayout = __esm({
26852
27152
  DashboardLayout.displayName = "DashboardLayout";
26853
27153
  NavLink = ({
26854
27154
  item,
26855
- currentPath,
27155
+ activeHref,
26856
27156
  compact = false
26857
27157
  }) => {
26858
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
27158
+ const hasChildren = !!item.children?.length;
27159
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
27160
+ const isActive = item.href === activeHref || childActive;
27161
+ const [open, setOpen] = React87.useState(childActive);
26859
27162
  const iconClassName = cn(
26860
27163
  "h-5 w-5",
26861
27164
  isActive ? "text-primary-foreground" : "text-muted-foreground"
26862
27165
  );
26863
27166
  if (compact) {
27167
+ if (hasChildren) {
27168
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
27169
+ /* @__PURE__ */ jsxRuntime.jsx(
27170
+ Button,
27171
+ {
27172
+ variant: "ghost",
27173
+ title: item.label,
27174
+ "aria-label": item.label,
27175
+ "aria-expanded": open,
27176
+ className: cn(
27177
+ "flex items-center justify-center w-full px-2 py-2 rounded-lg transition-colors",
27178
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27179
+ ),
27180
+ onClick: () => setOpen(!open),
27181
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { as: "span", className: "relative inline-flex", children: [
27182
+ item.icon ? typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, className: iconClassName }) : /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: iconClassName }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "font-semibold", as: "span", children: item.label.charAt(0).toUpperCase() }),
27183
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(
27184
+ Badge,
27185
+ {
27186
+ variant: isActive ? "primary" : "default",
27187
+ size: "sm",
27188
+ className: "absolute -top-2 -right-2 px-1 py-0 text-[10px] leading-4",
27189
+ children: item.badge
27190
+ }
27191
+ )
27192
+ ] })
27193
+ }
27194
+ ),
27195
+ open && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
27196
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
27197
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute left-full top-0 ml-2 w-48 bg-card dark:bg-card rounded-lg shadow-lg border border-border dark:border-border py-1 z-30", children: item.children.map((child) => {
27198
+ const childIsActive = child.href === activeHref;
27199
+ return /* @__PURE__ */ jsxRuntime.jsxs(
27200
+ reactRouterDom.Link,
27201
+ {
27202
+ to: child.href,
27203
+ onClick: () => setOpen(false),
27204
+ className: cn(
27205
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
27206
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27207
+ ),
27208
+ children: [
27209
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(child.icon, { className: "h-4 w-4" })),
27210
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
27211
+ child.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
27212
+ ]
27213
+ },
27214
+ child.href
27215
+ );
27216
+ }) })
27217
+ ] })
27218
+ ] });
27219
+ }
26864
27220
  return /* @__PURE__ */ jsxRuntime.jsx(
26865
27221
  reactRouterDom.Link,
26866
27222
  {
@@ -26886,6 +27242,78 @@ var init_DashboardLayout = __esm({
26886
27242
  }
26887
27243
  );
26888
27244
  }
27245
+ if (hasChildren) {
27246
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
27247
+ /* @__PURE__ */ jsxRuntime.jsxs(
27248
+ Button,
27249
+ {
27250
+ variant: "ghost",
27251
+ "aria-expanded": open,
27252
+ className: cn(
27253
+ "flex items-center gap-3 w-full px-3 py-2 rounded-lg text-sm font-medium transition-colors",
27254
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27255
+ ),
27256
+ onClick: () => setOpen(!open),
27257
+ children: [
27258
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, className: iconClassName }) : /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: iconClassName })),
27259
+ /* @__PURE__ */ jsxRuntime.jsx(
27260
+ Typography,
27261
+ {
27262
+ variant: "small",
27263
+ color: isActive ? "inherit" : "primary",
27264
+ className: "flex-1 text-left",
27265
+ as: "span",
27266
+ children: item.label
27267
+ }
27268
+ ),
27269
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
27270
+ /* @__PURE__ */ jsxRuntime.jsx(
27271
+ Icon,
27272
+ {
27273
+ name: "chevron-down",
27274
+ className: cn("h-4 w-4 shrink-0 transition-transform", open && "rotate-180")
27275
+ }
27276
+ )
27277
+ ]
27278
+ }
27279
+ ),
27280
+ open && /* @__PURE__ */ jsxRuntime.jsx(
27281
+ VStack,
27282
+ {
27283
+ gap: "none",
27284
+ className: "mt-1 ml-4 pl-4 border-l border-border dark:border-border space-y-1",
27285
+ children: item.children.map((child) => {
27286
+ const childIsActive = child.href === activeHref;
27287
+ return /* @__PURE__ */ jsxRuntime.jsxs(
27288
+ reactRouterDom.Link,
27289
+ {
27290
+ to: child.href,
27291
+ className: cn(
27292
+ "flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm transition-colors",
27293
+ childIsActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27294
+ ),
27295
+ children: [
27296
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(child.icon, { className: "h-4 w-4" })),
27297
+ /* @__PURE__ */ jsxRuntime.jsx(
27298
+ Typography,
27299
+ {
27300
+ variant: "small",
27301
+ color: childIsActive ? "inherit" : "primary",
27302
+ className: "flex-1",
27303
+ as: "span",
27304
+ children: child.label
27305
+ }
27306
+ ),
27307
+ child.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
27308
+ ]
27309
+ },
27310
+ child.href
27311
+ );
27312
+ })
27313
+ }
27314
+ )
27315
+ ] });
27316
+ }
26889
27317
  return /* @__PURE__ */ jsxRuntime.jsxs(
26890
27318
  reactRouterDom.Link,
26891
27319
  {
@@ -26914,9 +27342,63 @@ var init_DashboardLayout = __esm({
26914
27342
  NavLink.displayName = "NavLink";
26915
27343
  NavLinkTopnav = ({
26916
27344
  item,
26917
- currentPath
27345
+ activeHref
26918
27346
  }) => {
26919
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
27347
+ const hasChildren = !!item.children?.length;
27348
+ const childActive = hasChildren && item.children.some((child) => child.href === activeHref);
27349
+ const isActive = item.href === activeHref || childActive;
27350
+ const [open, setOpen] = React87.useState(false);
27351
+ if (hasChildren) {
27352
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
27353
+ /* @__PURE__ */ jsxRuntime.jsxs(
27354
+ Button,
27355
+ {
27356
+ variant: "ghost",
27357
+ "aria-expanded": open,
27358
+ className: cn(
27359
+ "flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors whitespace-nowrap",
27360
+ isActive ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27361
+ ),
27362
+ onClick: () => setOpen(!open),
27363
+ children: [
27364
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: "h-4 w-4" })),
27365
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "inherit", className: "flex-1", as: "span", children: item.label }),
27366
+ item.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: isActive ? "primary" : "default", size: "sm", children: item.badge }),
27367
+ /* @__PURE__ */ jsxRuntime.jsx(
27368
+ Icon,
27369
+ {
27370
+ name: "chevron-down",
27371
+ className: cn("h-3.5 w-3.5 shrink-0 transition-transform", open && "rotate-180")
27372
+ }
27373
+ )
27374
+ ]
27375
+ }
27376
+ ),
27377
+ open && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
27378
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "fixed inset-0 z-20", onClick: () => setOpen(false) }),
27379
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute left-0 top-full mt-1 w-48 bg-card dark:bg-card rounded-lg shadow-lg border border-border dark:border-border py-1 z-30", children: item.children.map((child) => {
27380
+ const childIsActive = child.href === activeHref;
27381
+ return /* @__PURE__ */ jsxRuntime.jsxs(
27382
+ reactRouterDom.Link,
27383
+ {
27384
+ to: child.href,
27385
+ onClick: () => setOpen(false),
27386
+ className: cn(
27387
+ "flex items-center gap-2 px-3 py-2 text-sm transition-colors",
27388
+ childIsActive ? "bg-muted text-foreground font-medium" : "text-muted-foreground hover:bg-muted hover:text-foreground"
27389
+ ),
27390
+ children: [
27391
+ child.icon && (typeof child.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: child.icon, className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(child.icon, { className: "h-4 w-4" })),
27392
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "flex-1", as: "span", children: child.label }),
27393
+ child.badge && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: childIsActive ? "primary" : "default", size: "sm", children: child.badge })
27394
+ ]
27395
+ },
27396
+ child.href
27397
+ );
27398
+ }) })
27399
+ ] })
27400
+ ] });
27401
+ }
26920
27402
  return /* @__PURE__ */ jsxRuntime.jsxs(
26921
27403
  reactRouterDom.Link,
26922
27404
  {
@@ -26936,9 +27418,9 @@ var init_DashboardLayout = __esm({
26936
27418
  NavLinkTopnav.displayName = "NavLinkTopnav";
26937
27419
  NavLinkBottom = ({
26938
27420
  item,
26939
- currentPath
27421
+ activeHref
26940
27422
  }) => {
26941
- const isActive = currentPath === item.href || currentPath.startsWith(item.href + "/");
27423
+ const isActive = item.href === activeHref;
26942
27424
  const iconClassName = cn(
26943
27425
  "h-5 w-5",
26944
27426
  isActive ? "text-primary" : "text-muted-foreground"
@@ -27877,7 +28359,9 @@ function DataGrid({
27877
28359
  const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
27878
28360
  const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
27879
28361
  const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
27880
- const hasRenderProp = typeof children === "function";
28362
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
28363
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
28364
+ const hasRenderProp = typeof itemRenderer === "function";
27881
28365
  React87.useEffect(() => {
27882
28366
  if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
27883
28367
  const schemaArr = Array.isArray(schemaRenderItem) ? schemaRenderItem : null;
@@ -27951,7 +28435,7 @@ function DataGrid({
27951
28435
  onClick: handleCardClick?.(itemData),
27952
28436
  className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
27953
28437
  children: [
27954
- children(itemData, index),
28438
+ itemRenderer(itemData, index),
27955
28439
  actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { onClick: stopCardClick, className: "absolute top-2 right-2 z-10 opacity-0 group-hover/rowactions:opacity-100 focus-within:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity duration-fast", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: [
27956
28440
  inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
27957
28441
  Button,
@@ -28284,7 +28768,9 @@ function DataList({
28284
28768
  );
28285
28769
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
28286
28770
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
28287
- const hasRenderProp = typeof children === "function";
28771
+ const renderItemFn = typeof schemaRenderItem === "function" ? schemaRenderItem : void 0;
28772
+ const itemRenderer = typeof children === "function" ? children : renderItemFn;
28773
+ const hasRenderProp = typeof itemRenderer === "function";
28288
28774
  React87__namespace.default.useEffect(() => {
28289
28775
  const renderItemTypeOf = typeof schemaRenderItem;
28290
28776
  const childrenTypeOf = typeof children;
@@ -28502,7 +28988,7 @@ function DataList({
28502
28988
  const actions = renderItemActions(itemData);
28503
28989
  return wrapDnd(
28504
28990
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn("relative group/rowactions", rowClickEvent && "cursor-pointer"), children: [
28505
- children(itemData, index),
28991
+ itemRenderer(itemData, index),
28506
28992
  actions && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "absolute top-2 right-2 z-10 opacity-0 group-hover/rowactions:opacity-100 focus-within:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity duration-fast", children: [
28507
28993
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5 [@media(pointer:coarse)]:hidden", children: actions }),
28508
28994
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "hidden [@media(pointer:coarse)]:block rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -34876,7 +35362,7 @@ function TableView({
34876
35362
  columns,
34877
35363
  fields,
34878
35364
  itemActions,
34879
- maxInlineActions: _maxInlineActions,
35365
+ maxInlineActions,
34880
35366
  itemClickEvent = "",
34881
35367
  selectable = false,
34882
35368
  selectEvent,
@@ -34909,6 +35395,12 @@ function TableView({
34909
35395
  const [localSelected, setLocalSelected] = React87__namespace.default.useState(/* @__PURE__ */ new Set());
34910
35396
  const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
34911
35397
  const actionDefs = Array.isArray(itemActions) ? itemActions : [];
35398
+ const inlineActions = maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : [];
35399
+ const overflowActions = maxInlineActions != null ? actionDefs.slice(maxInlineActions) : actionDefs;
35400
+ const fireAction = (action, row) => eventBus.emit(`UI:${action.event}`, {
35401
+ id: row.id,
35402
+ row
35403
+ });
34912
35404
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
34913
35405
  const dnd = useDataDnd({
34914
35406
  items: allDataRaw,
@@ -35084,7 +35576,7 @@ function TableView({
35084
35576
  }
35085
35577
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
35086
35578
  }),
35087
- hasActions && /* @__PURE__ */ jsxRuntime.jsx(
35579
+ hasActions && /* @__PURE__ */ jsxRuntime.jsxs(
35088
35580
  HStack,
35089
35581
  {
35090
35582
  gap: "xs",
@@ -35098,23 +35590,38 @@ function TableView({
35098
35590
  "border-l border-[var(--color-border)]",
35099
35591
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
35100
35592
  ),
35101
- children: /* @__PURE__ */ jsxRuntime.jsx(
35102
- Menu,
35103
- {
35104
- position: "bottom-end",
35105
- trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
35106
- items: actionDefs.map((action) => ({
35107
- label: action.label,
35108
- icon: action.icon,
35109
- event: action.event,
35110
- variant: action.variant === "danger" ? "danger" : "default",
35111
- onClick: () => eventBus.emit(`UI:${action.event}`, {
35112
- id: row.id,
35113
- row
35114
- })
35115
- }))
35116
- }
35117
- )
35593
+ children: [
35594
+ inlineActions.map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
35595
+ Button,
35596
+ {
35597
+ variant: action.variant ?? "ghost",
35598
+ size: "sm",
35599
+ onClick: () => fireAction(action, row),
35600
+ "data-testid": `action-${action.event}`,
35601
+ "data-row-id": String(row.id),
35602
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
35603
+ children: [
35604
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
35605
+ action.label
35606
+ ]
35607
+ },
35608
+ action.event
35609
+ )),
35610
+ overflowActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
35611
+ Menu,
35612
+ {
35613
+ position: "bottom-end",
35614
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
35615
+ items: overflowActions.map((action) => ({
35616
+ label: action.label,
35617
+ icon: action.icon,
35618
+ event: action.event,
35619
+ variant: action.variant === "danger" ? "danger" : "default",
35620
+ onClick: () => fireAction(action, row)
35621
+ }))
35622
+ }
35623
+ )
35624
+ ]
35118
35625
  }
35119
35626
  )
35120
35627
  ]
@@ -50827,6 +51334,7 @@ var init_component_registry_generated = __esm({
50827
51334
  init_DrawMesh();
50828
51335
  init_DrawShape();
50829
51336
  init_DrawShapeLayer();
51337
+ init_DrawSkinnedMesh();
50830
51338
  init_DrawSprite();
50831
51339
  init_DrawSpriteLayer();
50832
51340
  init_DrawText();
@@ -51105,6 +51613,7 @@ var init_component_registry_generated = __esm({
51105
51613
  "DrawMesh": DrawMesh,
51106
51614
  "DrawShape": DrawShape,
51107
51615
  "DrawShapeLayer": DrawShapeLayer,
51616
+ "DrawSkinnedMesh": DrawSkinnedMesh,
51108
51617
  "DrawSprite": DrawSprite,
51109
51618
  "DrawSpriteLayer": DrawSpriteLayer,
51110
51619
  "DrawText": DrawText,
@@ -51433,7 +51942,7 @@ function renderContainedPortal(t, slot, content, onDismiss) {
51433
51942
  const slotId = `slot-${slot}`;
51434
51943
  switch (slot) {
51435
51944
  case "modal":
51436
- if (SELF_OVERLAY_PATTERNS.has(content.pattern)) {
51945
+ if (patterns.SELF_OVERLAY_PATTERN_TYPES.has(content.pattern)) {
51437
51946
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { id: slotId, className: "contents", children: slotContent });
51438
51947
  }
51439
51948
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -51776,7 +52285,7 @@ function CompiledPortal({ slot, className, pattern, sourceTrait, children }) {
51776
52285
  children
51777
52286
  }
51778
52287
  );
51779
- wrapper = pattern !== void 0 && SELF_OVERLAY_PATTERNS.has(pattern) ? innerContent : /* @__PURE__ */ jsxRuntime.jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
52288
+ wrapper = pattern !== void 0 && patterns.SELF_OVERLAY_PATTERN_TYPES.has(pattern) ? innerContent : /* @__PURE__ */ jsxRuntime.jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: innerContent });
51780
52289
  break;
51781
52290
  }
51782
52291
  case "drawer":
@@ -51836,7 +52345,7 @@ function SlotPortal({
51836
52345
  let wrapper;
51837
52346
  switch (slot) {
51838
52347
  case "modal":
51839
- wrapper = SELF_OVERLAY_PATTERNS.has(content.pattern) ? /* @__PURE__ */ jsxRuntime.jsx(Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsxRuntime.jsx(
52348
+ wrapper = patterns.SELF_OVERLAY_PATTERN_TYPES.has(content.pattern) ? /* @__PURE__ */ jsxRuntime.jsx(Box, { id: slotId, children: slotContent }) : /* @__PURE__ */ jsxRuntime.jsx(
51840
52349
  Modal,
51841
52350
  {
51842
52351
  isOpen: true,
@@ -52343,7 +52852,7 @@ function UISlotRenderer({
52343
52852
  }
52344
52853
  return wrapped;
52345
52854
  }
52346
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
52855
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
52347
52856
  var init_UISlotRenderer = __esm({
52348
52857
  "components/core/organisms/UISlotRenderer.tsx"() {
52349
52858
  "use client";
@@ -52374,7 +52883,6 @@ var init_UISlotRenderer = __esm({
52374
52883
  modal: "form",
52375
52884
  drawer: "form"
52376
52885
  };
52377
- SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
52378
52886
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
52379
52887
  "logo",
52380
52888
  "master",
@@ -52487,7 +52995,7 @@ function runTickFrame(entityId, orderedWriters, store) {
52487
52995
  }
52488
52996
  var log3 = logger.createLogger("almadar:ui:effects:client-handlers");
52489
52997
  function createClientEffectHandlers(options) {
52490
- const { eventBus, slotSetter, navigate, navigateBack, notify, callService, liveEntity, persistDelegated } = options;
52998
+ const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated } = options;
52491
52999
  return {
52492
53000
  emit: (event, payload, source) => {
52493
53001
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
@@ -52543,9 +53051,6 @@ function createClientEffectHandlers(options) {
52543
53051
  }),
52544
53052
  navigateBack: navigateBack ?? (() => {
52545
53053
  log3.warn("No navigate-back handler, ignoring");
52546
- }),
52547
- notify: notify ?? ((msg, type) => {
52548
- log3.debug("notify", { type, message: msg });
52549
53054
  })
52550
53055
  };
52551
53056
  }
@@ -52916,6 +53421,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52916
53421
  sharedEntityLog.debug("useTraitStateMachine start", { traitBindingsCount: traitBindings.length, traitNames: traitBindings.map((b) => b.trait.name) });
52917
53422
  const eventBus = useEventBus();
52918
53423
  const { entities } = providers.useEntitySchema();
53424
+ const { user: viewer } = providers.useUser();
52919
53425
  const traitConfigsByName = options?.traitConfigsByName;
52920
53426
  const orbitalsByTrait = options?.orbitalsByTrait;
52921
53427
  const callsiteCaptureChildrenByTrait = options?.callsiteCaptureChildrenByTrait;
@@ -53229,7 +53735,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53229
53735
  },
53230
53736
  navigate: optionsRef.current?.navigate,
53231
53737
  navigateBack: optionsRef.current?.navigateBack,
53232
- notify: optionsRef.current?.notify,
53233
53738
  callService: optionsRef.current?.callService,
53234
53739
  // The canonical client `set` writes `(set @entity.X)` straight into
53235
53740
  // the trait's one live store — the same object bound below. This is
@@ -53296,8 +53801,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53296
53801
  emit: clientHandlers.emit,
53297
53802
  renderUI: clientHandlers.renderUI,
53298
53803
  navigate: clientHandlers.navigate,
53299
- navigateBack: clientHandlers.navigateBack,
53300
- notify: clientHandlers.notify
53804
+ navigateBack: clientHandlers.navigateBack
53301
53805
  };
53302
53806
  }
53303
53807
  const sharedWrites = [];
@@ -53322,7 +53826,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53322
53826
  const bindingCtx = {
53323
53827
  entity: liveEntity,
53324
53828
  payload: payload || {},
53325
- state: previousState
53829
+ state: previousState,
53830
+ ...viewer ? { user: viewer } : {}
53326
53831
  };
53327
53832
  if (callsitePayload) {
53328
53833
  bindingCtx.callsitePayload = callsitePayload;
@@ -53419,7 +53924,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53419
53924
  });
53420
53925
  }
53421
53926
  return { emitted: emittedDuringExec, serverEffectResults };
53422
- }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait]);
53927
+ }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait, viewer]);
53423
53928
  const reRenderCallsiteCaptureChildren = React87.useCallback(async (traitName, callsitePayload, entityByTrait, log11, visited = /* @__PURE__ */ new Set()) => {
53424
53929
  const children = callsiteCaptureChildrenByTrait?.get(traitName);
53425
53930
  if (!children || children.size === 0) return;
@@ -53464,7 +53969,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53464
53969
  const guardCtx = {
53465
53970
  entity,
53466
53971
  payload: {},
53467
- state: currentState
53972
+ state: currentState,
53973
+ ...viewer ? { user: viewer } : {}
53468
53974
  };
53469
53975
  const bindingCfg = getBindingConfig(binding) ?? traitConfigsByName?.[traitName];
53470
53976
  if (bindingCfg) {
@@ -53492,7 +53998,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53492
53998
  const emitFromSharedWriter = React87.useCallback((event, payload, source) => {
53493
53999
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
53494
54000
  eventBus.emit(prefixedEvent, payload, source);
53495
- }, [eventBus]);
54001
+ }, [eventBus, viewer]);
53496
54002
  React87.useEffect(() => {
53497
54003
  const scheduler = runtime.createTickScheduler();
53498
54004
  const timedTick = (key, fn) => () => ui.perfTimeAsync(key, fn);
@@ -54462,7 +54968,8 @@ function OrbPreview({
54462
54968
  transport,
54463
54969
  getAccessToken,
54464
54970
  initialPagePath,
54465
- isolated = false
54971
+ isolated = false,
54972
+ user = null
54466
54973
  }) {
54467
54974
  if (serverUrl && transport) {
54468
54975
  throw new Error("OrbPreview accepts serverUrl OR transport, not both");
@@ -54472,11 +54979,7 @@ function OrbPreview({
54472
54979
  const handleLocalFallback = React87.useCallback(() => {
54473
54980
  if (localFallback) return;
54474
54981
  setLocalFallback(true);
54475
- eventBus.emit("UI:NOTIFY", {
54476
- message: "Preview server unreachable \u2014 running locally without server-side state.",
54477
- severity: "warning"
54478
- });
54479
- }, [localFallback, eventBus]);
54982
+ }, [localFallback]);
54480
54983
  const parseResult = React87.useMemo(() => {
54481
54984
  let parsed;
54482
54985
  if (typeof schema === "string") {
@@ -54635,7 +55138,7 @@ function OrbPreview({
54635
55138
  storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
54636
55139
  children: [
54637
55140
  /* @__PURE__ */ jsxRuntime.jsx(NavStackRefBridge, { apiRef: navStackRef }),
54638
- /* @__PURE__ */ jsxRuntime.jsx(providers.OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, children: /* @__PURE__ */ jsxRuntime.jsx(context.UISlotProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
55141
+ /* @__PURE__ */ jsxRuntime.jsx(providers.OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsxRuntime.jsx(context.UISlotProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
54639
55142
  SchemaRunner,
54640
55143
  {
54641
55144
  schema: parseResult.schema,
@@ -54810,7 +55313,7 @@ function useOrbitalPluginHost() {
54810
55313
  return { getState: ctx.getState, lastEvent: ctx.lastEvent, errors: ctx.errors };
54811
55314
  }
54812
55315
  function buildMockEffectHandlers(opts) {
54813
- const { pluginId, denySet, slotsRef, renderedSlotsRef, navigateRef, notifyRef } = opts;
55316
+ const { pluginId, denySet, slotsRef, renderedSlotsRef, navigateRef } = opts;
54814
55317
  const handlers = {
54815
55318
  renderUI: (slot, pattern, props, priority) => {
54816
55319
  if (!isUISlot(slot)) {
@@ -54830,11 +55333,6 @@ function buildMockEffectHandlers(opts) {
54830
55333
  } else if (navigateRef.current) {
54831
55334
  handlers.navigate = (path) => navigateRef.current?.(path);
54832
55335
  }
54833
- if (denySet.has("notify")) {
54834
- handlers.notify = (message, type) => log10.warn("notify:denied", { pluginId, message, type });
54835
- } else if (notifyRef.current) {
54836
- handlers.notify = (message, type) => notifyRef.current?.(message, type);
54837
- }
54838
55336
  if (denySet.has("persist")) {
54839
55337
  handlers.persist = async (action, entityType) => {
54840
55338
  log10.warn("persist:denied", { pluginId, action, entityType });
@@ -54855,7 +55353,6 @@ function PluginRuntimeMount({
54855
55353
  transport,
54856
55354
  deny,
54857
55355
  navigate,
54858
- notify,
54859
55356
  onTransition,
54860
55357
  onDispatched,
54861
55358
  onError
@@ -54867,8 +55364,6 @@ function PluginRuntimeMount({
54867
55364
  const renderedSlotsRef = React87.useRef(/* @__PURE__ */ new Set());
54868
55365
  const navigateRef = React87.useRef(navigate);
54869
55366
  navigateRef.current = navigate;
54870
- const notifyRef = React87.useRef(notify);
54871
- notifyRef.current = notify;
54872
55367
  const [mockRuntime] = React87.useState(() => {
54873
55368
  if (mode !== "mock") return void 0;
54874
55369
  return new OrbitalServerRuntime.OrbitalServerRuntime({
@@ -54879,8 +55374,7 @@ function PluginRuntimeMount({
54879
55374
  denySet: new Set(deny ?? []),
54880
55375
  slotsRef,
54881
55376
  renderedSlotsRef,
54882
- navigateRef,
54883
- notifyRef
55377
+ navigateRef
54884
55378
  })
54885
55379
  });
54886
55380
  });
@@ -55039,7 +55533,6 @@ function OrbitalPluginHost({
55039
55533
  transport,
55040
55534
  deny,
55041
55535
  navigate,
55042
- notify,
55043
55536
  onTransition,
55044
55537
  children
55045
55538
  }) {
@@ -55090,7 +55583,6 @@ function OrbitalPluginHost({
55090
55583
  transport,
55091
55584
  deny,
55092
55585
  navigate,
55093
- notify,
55094
55586
  onTransition,
55095
55587
  onDispatched: handleDispatched,
55096
55588
  onError: handleError