@almadar/ui 5.151.0 → 5.153.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 (35) hide show
  1. package/dist/{UISlotContext-D8_SoDsD.d.cts → UISlotContext-BlRDbHDy.d.cts} +1 -1
  2. package/dist/{UISlotContext-C1FsU9GB.d.ts → UISlotContext-CB89mv7N.d.ts} +1 -1
  3. package/dist/avl/index.cjs +749 -116
  4. package/dist/avl/index.js +752 -119
  5. package/dist/{avl-schema-parser-Cx_4SVg9.d.ts → avl-schema-parser-CLIm28Wa.d.ts} +1 -1
  6. package/dist/{avl-schema-parser-CVzkNzg7.d.cts → avl-schema-parser-Do_LPV1o.d.cts} +1 -1
  7. package/dist/{cn-DJTUjk1M.d.ts → cn-CFurBb2q.d.ts} +1 -1
  8. package/dist/{cn-BnAJZcNb.d.cts → cn-TH-RHihd.d.cts} +1 -1
  9. package/dist/components/index.cjs +751 -116
  10. package/dist/components/index.d.cts +114 -16
  11. package/dist/components/index.d.ts +114 -16
  12. package/dist/components/index.js +754 -120
  13. package/dist/context/index.d.cts +2 -2
  14. package/dist/context/index.d.ts +2 -2
  15. package/dist/hooks/index.d.cts +1 -1
  16. package/dist/hooks/index.d.ts +1 -1
  17. package/dist/lib/drawable/three/index.cjs +212 -0
  18. package/dist/lib/drawable/three/index.d.cts +3 -3
  19. package/dist/lib/drawable/three/index.d.ts +3 -3
  20. package/dist/lib/drawable/three/index.js +212 -0
  21. package/dist/lib/index.cjs +11 -2
  22. package/dist/lib/index.d.cts +10 -3
  23. package/dist/lib/index.d.ts +10 -3
  24. package/dist/lib/index.js +11 -3
  25. package/dist/{paintDispatch-CxdLjHQq.d.ts → paintDispatch-B5n2DTB-.d.ts} +178 -2
  26. package/dist/{paintDispatch-BjZjUbcb.d.cts → paintDispatch-DgBctxIq.d.cts} +178 -2
  27. package/dist/providers/index.cjs +749 -116
  28. package/dist/providers/index.js +752 -119
  29. package/dist/runtime/index.cjs +749 -116
  30. package/dist/runtime/index.d.cts +2 -2
  31. package/dist/runtime/index.d.ts +2 -2
  32. package/dist/runtime/index.js +752 -119
  33. package/dist/{useUISlots-BesZYMks.d.cts → useUISlots-GNwGLlW2.d.cts} +1 -1
  34. package/dist/{useUISlots-BesZYMks.d.ts → useUISlots-GNwGLlW2.d.ts} +1 -1
  35. package/package.json +4 -4
@@ -5224,27 +5224,61 @@ var init_InfiniteScrollSentinel = __esm({
5224
5224
  exports.InfiniteScrollSentinel.displayName = "InfiniteScrollSentinel";
5225
5225
  }
5226
5226
  });
5227
- function createParticles(count) {
5228
- return Array.from({ length: count }, () => {
5229
- particleIdCounter += 1;
5230
- return {
5231
- id: particleIdCounter,
5232
- color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)],
5233
- left: 30 + Math.random() * 40,
5234
- delay: Math.random() * 300,
5235
- angle: Math.random() * 360,
5236
- distance: 40 + Math.random() * 80,
5237
- rotation: Math.random() * 720 - 360,
5238
- size: 4 + Math.random() * 6
5239
- };
5240
- });
5227
+
5228
+ // components/core/atoms/fx.ts
5229
+ function resolveFxView(item, presets) {
5230
+ const row = presets?.find((p) => p.type === item.type);
5231
+ if (!row) return item;
5232
+ return {
5233
+ ...item,
5234
+ space: item.space ?? row.space,
5235
+ effect: item.effect ?? row.effect,
5236
+ color: item.color ?? row.color,
5237
+ size: item.size ?? row.size,
5238
+ vx: item.vx ?? row.vx,
5239
+ vy: item.vy ?? row.vy,
5240
+ vz: item.vz ?? row.vz,
5241
+ particleCount: item.particleCount ?? row.particleCount,
5242
+ shape: row.shape,
5243
+ count: row.count,
5244
+ glow: row.glow,
5245
+ gravity: row.gravity,
5246
+ color2: row.color2
5247
+ };
5241
5248
  }
5242
- var CONFETTI_COLORS, particleIdCounter; exports.ConfettiEffect = void 0;
5243
- var init_ConfettiEffect = __esm({
5244
- "components/core/atoms/ConfettiEffect.tsx"() {
5245
- "use client";
5246
- init_cn();
5247
- init_Box();
5249
+ function fxLifecycle(item, epochNowMs, tickMs) {
5250
+ const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
5251
+ const lifeMs = maxTtl * tickMs;
5252
+ const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
5253
+ const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
5254
+ return { lifeMs, ageMs, progress, fade: 1 - progress };
5255
+ }
5256
+ function fxHash01(seed, salt) {
5257
+ let h = 2166136261 ^ salt;
5258
+ for (let i = 0; i < seed.length; i++) {
5259
+ h ^= seed.charCodeAt(i);
5260
+ h = Math.imul(h, 16777619);
5261
+ }
5262
+ h ^= h >>> 13;
5263
+ h = Math.imul(h, 1274126177);
5264
+ h ^= h >>> 16;
5265
+ return (h >>> 0) / 4294967296;
5266
+ }
5267
+ function createConfettiParticles(count, seed) {
5268
+ return Array.from({ length: count }, (_, i) => ({
5269
+ id: i,
5270
+ color: CONFETTI_COLORS[Math.floor(fxHash01(seed, i * 7 + 1) * CONFETTI_COLORS.length)],
5271
+ left: 30 + fxHash01(seed, i * 7 + 2) * 40,
5272
+ delay: fxHash01(seed, i * 7 + 3) * 300,
5273
+ angle: fxHash01(seed, i * 7 + 4) * 360,
5274
+ distance: 40 + fxHash01(seed, i * 7 + 5) * 80,
5275
+ rotation: fxHash01(seed, i * 7 + 6) * 720 - 360,
5276
+ size: 4 + fxHash01(seed, i * 7 + 7) * 6
5277
+ }));
5278
+ }
5279
+ var CONFETTI_COLORS, CONFETTI_BURST_KEYFRAMES;
5280
+ var init_fx = __esm({
5281
+ "components/core/atoms/fx.ts"() {
5248
5282
  CONFETTI_COLORS = [
5249
5283
  "var(--color-primary)",
5250
5284
  "var(--color-success)",
@@ -5253,7 +5287,30 @@ var init_ConfettiEffect = __esm({
5253
5287
  "gold",
5254
5288
  "dodgerblue"
5255
5289
  ];
5256
- particleIdCounter = 0;
5290
+ CONFETTI_BURST_KEYFRAMES = `
5291
+ @keyframes confetti-burst {
5292
+ 0% {
5293
+ opacity: 1;
5294
+ transform: translate(0, 0) rotate(0deg) scale(1);
5295
+ }
5296
+ 70% {
5297
+ opacity: 1;
5298
+ }
5299
+ 100% {
5300
+ opacity: 0;
5301
+ transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
5302
+ }
5303
+ }
5304
+ `;
5305
+ }
5306
+ });
5307
+ exports.ConfettiEffect = void 0;
5308
+ var init_ConfettiEffect = __esm({
5309
+ "components/core/atoms/ConfettiEffect.tsx"() {
5310
+ "use client";
5311
+ init_cn();
5312
+ init_Box();
5313
+ init_fx();
5257
5314
  exports.ConfettiEffect = ({
5258
5315
  trigger,
5259
5316
  duration = 2e3,
@@ -5262,11 +5319,13 @@ var init_ConfettiEffect = __esm({
5262
5319
  }) => {
5263
5320
  const [particles, setParticles] = React77.useState([]);
5264
5321
  const previousTriggerRef = React77.useRef(false);
5322
+ const burstRef = React77.useRef(0);
5265
5323
  React77.useEffect(() => {
5266
5324
  const wasFalse = !previousTriggerRef.current;
5267
5325
  previousTriggerRef.current = trigger;
5268
5326
  if (trigger && wasFalse) {
5269
- const newParticles = createParticles(particleCount);
5327
+ burstRef.current += 1;
5328
+ const newParticles = createConfettiParticles(particleCount, `confetti-${burstRef.current}`);
5270
5329
  setParticles(newParticles);
5271
5330
  const timer = window.setTimeout(() => {
5272
5331
  setParticles([]);
@@ -5314,21 +5373,7 @@ var init_ConfettiEffect = __esm({
5314
5373
  p.id
5315
5374
  );
5316
5375
  }),
5317
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
5318
- @keyframes confetti-burst {
5319
- 0% {
5320
- opacity: 1;
5321
- transform: translate(0, 0) rotate(0deg) scale(1);
5322
- }
5323
- 70% {
5324
- opacity: 1;
5325
- }
5326
- 100% {
5327
- opacity: 0;
5328
- transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
5329
- }
5330
- }
5331
- ` })
5376
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: CONFETTI_BURST_KEYFRAMES })
5332
5377
  ]
5333
5378
  }
5334
5379
  );
@@ -9214,7 +9259,7 @@ var init_AlgoGraphCanvas = __esm({
9214
9259
  }
9215
9260
  const badgeGeoms = [];
9216
9261
  for (const g of nodeGeoms) {
9217
- if (!g.badge) continue;
9262
+ if (!g.badge || g.badge.text === "") continue;
9218
9263
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
9219
9264
  badgeGeoms.push({
9220
9265
  cx: g.x + g.radius * 0.75,
@@ -9280,7 +9325,7 @@ var init_AlgoGraphCanvas = __esm({
9280
9325
  };
9281
9326
  }
9282
9327
  });
9283
- var DEFAULT_BAR_COLOR, DEFAULT_CELL_COLOR, DEFAULT_POINTER_COLOR, POINTER_BAND, TOP_PAD, PANEL_FAMILY_ORDER, RANGE_COLOR_DEFAULT, RANGE_FILL_OPACITY, BRACKET_TOP_OFFSET, BRACKET_ROW_H, BRACKET_TICK_H, BRACKET_LABEL_OFFSET, SLOT_EMPTY_FILL, SLOT_EMPTY_STROKE, SLOT_FILLED_STROKE, SLOT_HIGHLIGHT_DEFAULT, SLOT_VALUE_TEXT_COLOR, FRAME_ACTIVE_COLOR, FRAME_RETURNING_COLOR, FRAME_DONE_COLOR, FRAME_LABEL_COLOR, FRAME_DETAIL_COLOR, FRAME_TWO_LINE_MIN_H, BUCKET_INDEX_FILL, BUCKET_INDEX_STROKE, BUCKET_INDEX_TEXT, BUCKET_ENTRY_TEXT, BUCKET_ENTRY_DEFAULT, BUCKET_ENTRY_HIGHLIGHT, BUCKET_ENTRY_PROBING, BUCKET_ENTRY_MIN_W, BUCKET_ENTRY_MAX_W, AXIS_LABEL_COLOR, AXIS_LABEL_FONT_SIZE, CORNER_TEXT_COLOR, CORNER_FONT_SIZE, CORNER_MIN_CELL, CORNER_INSET_X, CORNER_INSET_Y, AUX_PRIMARY_RATIO, AUX_LABEL_BAND, AUX_BASELINE_PAD; exports.AlgorithmCanvas = void 0;
9328
+ var DEFAULT_BAR_COLOR, DEFAULT_CELL_COLOR, DEFAULT_POINTER_COLOR, POINTER_BAND, TOP_PAD, PANEL_FAMILY_ORDER, RANGE_COLOR_DEFAULT, RANGE_FILL_OPACITY, BRACKET_TOP_OFFSET, BRACKET_ROW_H, BRACKET_TICK_H, BRACKET_LABEL_OFFSET, SLOT_EMPTY_FILL, SLOT_EMPTY_STROKE, SLOT_FILLED_STROKE, SLOT_HIGHLIGHT_DEFAULT, SLOT_VALUE_TEXT_COLOR, FRAME_ACTIVE_COLOR, FRAME_RETURNING_COLOR, FRAME_DONE_COLOR, FRAME_RIM_COLOR, FRAME_LABEL_COLOR, FRAME_DETAIL_COLOR, FRAME_TWO_LINE_MIN_H, FRAME_STRIP_H, FRAME_GAP, FRAME_BOTTOM_PAD, FRAME_MIN_H, BUCKET_INDEX_FILL, BUCKET_INDEX_STROKE, BUCKET_INDEX_TEXT, BUCKET_ENTRY_TEXT, BUCKET_ENTRY_DEFAULT, BUCKET_ENTRY_HIGHLIGHT, BUCKET_ENTRY_PROBING, BUCKET_ENTRY_MIN_W, BUCKET_ENTRY_MAX_W, AXIS_LABEL_COLOR, AXIS_LABEL_FONT_SIZE, CORNER_TEXT_COLOR, CORNER_FONT_SIZE, CORNER_MIN_CELL, CORNER_INSET_X, CORNER_INSET_Y, AUX_PRIMARY_RATIO, AUX_LABEL_BAND, AUX_BASELINE_PAD; exports.AlgorithmCanvas = void 0;
9284
9329
  var init_AlgorithmCanvas = __esm({
9285
9330
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
9286
9331
  "use client";
@@ -9307,9 +9352,18 @@ var init_AlgorithmCanvas = __esm({
9307
9352
  FRAME_ACTIVE_COLOR = "#3b82f6";
9308
9353
  FRAME_RETURNING_COLOR = "#f59e0b";
9309
9354
  FRAME_DONE_COLOR = "#94a3b8";
9355
+ FRAME_RIM_COLOR = {
9356
+ active: "#1d4ed8",
9357
+ returning: "#b45309",
9358
+ done: "#64748b"
9359
+ };
9310
9360
  FRAME_LABEL_COLOR = "#ffffff";
9311
9361
  FRAME_DETAIL_COLOR = "#e2e8f0";
9312
9362
  FRAME_TWO_LINE_MIN_H = 22;
9363
+ FRAME_STRIP_H = 44;
9364
+ FRAME_GAP = 6;
9365
+ FRAME_BOTTOM_PAD = 8;
9366
+ FRAME_MIN_H = 12;
9313
9367
  BUCKET_INDEX_FILL = "#e2e8f0";
9314
9368
  BUCKET_INDEX_STROKE = "#9ca3af";
9315
9369
  BUCKET_INDEX_TEXT = "#374151";
@@ -9806,14 +9860,18 @@ var init_AlgorithmCanvas = __esm({
9806
9860
  if (frames.length > 0) {
9807
9861
  const panelYFrames = panelY.frames;
9808
9862
  const n = frames.length;
9809
- const frameH = panelHeight / n;
9863
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
9864
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
9865
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
9810
9866
  const x = 8;
9811
9867
  const w = width - 16;
9868
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
9812
9869
  frames.forEach((f3, i) => {
9813
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
9870
+ const y = bottom - i * stride - frameH;
9814
9871
  const state = f3.state ?? "active";
9815
9872
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
9816
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
9873
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
9874
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
9817
9875
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
9818
9876
  out.push({
9819
9877
  type: "text",
@@ -9821,14 +9879,14 @@ var init_AlgorithmCanvas = __esm({
9821
9879
  y: y + frameH * 0.35,
9822
9880
  text: f3.label,
9823
9881
  color: FRAME_LABEL_COLOR,
9824
- fontSize: 10,
9882
+ fontSize: 11,
9825
9883
  align: "left"
9826
9884
  });
9827
9885
  if (f3.detail) {
9828
9886
  out.push({
9829
9887
  type: "text",
9830
9888
  x: 16,
9831
- y: y + frameH * 0.7,
9889
+ y: y + frameH * 0.72,
9832
9890
  text: f3.detail,
9833
9891
  color: FRAME_DETAIL_COLOR,
9834
9892
  fontSize: 10,
@@ -16344,8 +16402,6 @@ var init_ButtonGroup = __esm({
16344
16402
  exports.ButtonGroup.displayName = "ButtonGroup";
16345
16403
  }
16346
16404
  });
16347
-
16348
- // lib/getNestedValue.ts
16349
16405
  function getNestedValue(obj, path) {
16350
16406
  if (obj === null || obj === void 0 || !path) {
16351
16407
  return void 0;
@@ -16366,6 +16422,15 @@ function getNestedValue(obj, path) {
16366
16422
  }
16367
16423
  return value;
16368
16424
  }
16425
+ function resolveImageUrl(value) {
16426
+ if (typeof value === "string") {
16427
+ return value;
16428
+ }
16429
+ if (core.isFileValue(value)) {
16430
+ return value.url;
16431
+ }
16432
+ return void 0;
16433
+ }
16369
16434
  var init_getNestedValue = __esm({
16370
16435
  "lib/getNestedValue.ts"() {
16371
16436
  }
@@ -18112,7 +18177,7 @@ var init_DrawShape = __esm({
18112
18177
  const cx = p.x + (node.offsetX ?? 0) * tw;
18113
18178
  const cy = p.y + (node.offsetY ?? 0) * tw;
18114
18179
  const rx = (node.radiusX ?? 0) * tw;
18115
- const ry = (node.radiusY ?? rx) * tw;
18180
+ const ry = (node.radiusY ?? node.radiusX ?? 0) * tw;
18116
18181
  if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
18117
18182
  if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
18118
18183
  if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
@@ -18302,6 +18367,172 @@ var init_DrawTextLayer = __esm({
18302
18367
  };
18303
18368
  }
18304
18369
  });
18370
+
18371
+ // components/game/molecules/DrawFxLayer.tsx
18372
+ function fxPosition(view, dim, ageSec) {
18373
+ const g = view.gravity ?? 0;
18374
+ const fall = 0.5 * g * ageSec * ageSec;
18375
+ {
18376
+ const base2 = view.position ?? { x: view.x, y: view.z ?? view.y ?? 0 };
18377
+ if (!Number.isFinite(base2.x) || !Number.isFinite(base2.y)) return void 0;
18378
+ return {
18379
+ x: base2.x + (view.vx ?? 0) * ageSec,
18380
+ y: base2.y + (view.vz ?? 0) * ageSec + fall
18381
+ };
18382
+ }
18383
+ }
18384
+ function sparkShape(view, pos, i, fade, progress) {
18385
+ const size = view.size ?? 0.5;
18386
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
18387
+ const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut2(progress);
18388
+ const r = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
18389
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
18390
+ return {
18391
+ type: "draw-shape",
18392
+ shape: "ellipse",
18393
+ position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
18394
+ anchor: "center",
18395
+ radiusX: r,
18396
+ fill: color,
18397
+ blendMode: "lighter",
18398
+ opacity: fade,
18399
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
18400
+ };
18401
+ }
18402
+ function proceduralShapes(view, pos, fade, progress) {
18403
+ const size = view.size ?? 0.5;
18404
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
18405
+ switch (view.shape ?? "spark") {
18406
+ case "ring": {
18407
+ return [
18408
+ {
18409
+ type: "draw-shape",
18410
+ shape: "ellipse",
18411
+ position: pos,
18412
+ anchor: "center",
18413
+ radiusX: size * easeOut2(progress),
18414
+ stroke: view.color2 ?? color,
18415
+ strokeWidth: 2,
18416
+ blendMode: "lighter",
18417
+ opacity: fade,
18418
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
18419
+ }
18420
+ ];
18421
+ }
18422
+ case "puff": {
18423
+ const count = view.count ?? 3;
18424
+ return Array.from({ length: count }, (_, i) => {
18425
+ const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
18426
+ const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut2(progress);
18427
+ return {
18428
+ type: "draw-shape",
18429
+ shape: "ellipse",
18430
+ position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
18431
+ anchor: "center",
18432
+ radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
18433
+ fill: i === 0 && view.color2 ? view.color2 : color,
18434
+ opacity: fade * 0.6,
18435
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
18436
+ };
18437
+ });
18438
+ }
18439
+ case "streak": {
18440
+ const count = view.count ?? 5;
18441
+ return Array.from({ length: count }, (_, i) => {
18442
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
18443
+ const inner = size * (0.2 + 0.8 * easeOut2(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
18444
+ const len = size * 0.35;
18445
+ return {
18446
+ type: "draw-shape",
18447
+ shape: "ellipse",
18448
+ position: pos,
18449
+ anchor: "center",
18450
+ offsetX: inner + len / 2,
18451
+ offsetY: 0,
18452
+ radiusX: len / 2,
18453
+ radiusY: size * 0.04,
18454
+ rotate: angle,
18455
+ fill: color,
18456
+ blendMode: "lighter",
18457
+ opacity: fade,
18458
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
18459
+ };
18460
+ });
18461
+ }
18462
+ default: {
18463
+ const count = view.count ?? 6;
18464
+ return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
18465
+ }
18466
+ }
18467
+ }
18468
+ function expandFxItem(view, node, epochNowMs, dim) {
18469
+ if (view.space === "screen") return [];
18470
+ const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
18471
+ const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
18472
+ if (progress >= 1) return [];
18473
+ const pos = fxPosition(view, dim, ageMs / 1e3);
18474
+ if (!pos) return [];
18475
+ const out = [];
18476
+ const artItems = node.art?.[view.type]?.["idle"];
18477
+ const sprite = node.sprites?.[view.type];
18478
+ if (Array.isArray(artItems)) {
18479
+ out.push({
18480
+ type: "draw-group",
18481
+ position: pos,
18482
+ opacity: fade,
18483
+ ...view.size !== void 0 ? { scale: view.size } : {},
18484
+ items: artItems
18485
+ });
18486
+ } else if (sprite?.url) {
18487
+ const spriteSize = view.size ?? 0.6;
18488
+ out.push({
18489
+ type: "draw-sprite",
18490
+ position: pos,
18491
+ asset: sprite,
18492
+ anchor: "center",
18493
+ width: spriteSize,
18494
+ height: spriteSize,
18495
+ opacity: fade
18496
+ });
18497
+ } else {
18498
+ out.push(...proceduralShapes(view, pos, fade, progress));
18499
+ }
18500
+ if (view.message) {
18501
+ const text = {
18502
+ type: "draw-text",
18503
+ text: view.message,
18504
+ position: pos,
18505
+ offsetY: -(0.15 + 0.55 * progress),
18506
+ color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
18507
+ opacity: fade
18508
+ };
18509
+ out.push(text);
18510
+ }
18511
+ return out;
18512
+ }
18513
+ function expandFxLayer(node, epochNowMs, dim) {
18514
+ if (!Array.isArray(node.items)) return [];
18515
+ const out = [];
18516
+ for (const item of node.items) {
18517
+ if (!item || typeof item.id !== "string") continue;
18518
+ out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim));
18519
+ }
18520
+ return out;
18521
+ }
18522
+ function DrawFxLayer(_props) {
18523
+ return null;
18524
+ }
18525
+ var DEFAULT_TICK_MS, DEFAULT_TEXT_COLOR, DEFAULT_SPARK_COLOR, easeOut2;
18526
+ var init_DrawFxLayer = __esm({
18527
+ "components/game/molecules/DrawFxLayer.tsx"() {
18528
+ "use client";
18529
+ init_fx();
18530
+ DEFAULT_TICK_MS = 500;
18531
+ DEFAULT_TEXT_COLOR = "#ffe066";
18532
+ DEFAULT_SPARK_COLOR = "#ffffff";
18533
+ easeOut2 = (t) => 1 - (1 - t) * (1 - t);
18534
+ }
18535
+ });
18305
18536
  function paintDrawable(painter, node, dctx) {
18306
18537
  switch (node.type) {
18307
18538
  case "draw-sprite":
@@ -18349,6 +18580,11 @@ function paintDrawable(painter, node, dctx) {
18349
18580
  case "draw-text-layer":
18350
18581
  paintTextLayer(painter, node, dctx);
18351
18582
  break;
18583
+ case "draw-fx-layer": {
18584
+ const epochNow = dctx.time > 0 && typeof performance !== "undefined" ? performance.timeOrigin + dctx.time : 0;
18585
+ for (const child of expandFxLayer(node, epochNow, "2d")) paintDrawable(painter, child, dctx);
18586
+ break;
18587
+ }
18352
18588
  }
18353
18589
  }
18354
18590
  var paint2dLog, warnedUnsupported2d, warnUnsupported2d;
@@ -18363,6 +18599,7 @@ var init_paintDispatch = __esm({
18363
18599
  init_DrawSpriteLayer();
18364
18600
  init_DrawShapeLayer();
18365
18601
  init_DrawTextLayer();
18602
+ init_DrawFxLayer();
18366
18603
  paint2dLog = logger.createLogger("almadar:ui:drawable-2d");
18367
18604
  warnedUnsupported2d = /* @__PURE__ */ new Set();
18368
18605
  warnUnsupported2d = (kind) => {
@@ -18611,6 +18848,7 @@ function Canvas2D({
18611
18848
  return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
18612
18849
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
18613
18850
  if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
18851
+ if (node.type === "draw-fx-layer") return Array.isArray(node.items) && node.items.length > 0;
18614
18852
  return false;
18615
18853
  };
18616
18854
  const animRafRef = React77.useRef(0);
@@ -19408,8 +19646,8 @@ var init_CardGrid = __esm({
19408
19646
  actionPayload: { row: itemData },
19409
19647
  children: [
19410
19648
  imageField && (() => {
19411
- const imgUrl = getNestedValue(itemData, imageField);
19412
- if (!imgUrl || typeof imgUrl !== "string") return null;
19649
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
19650
+ if (!imgUrl) return null;
19413
19651
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
19414
19652
  "img",
19415
19653
  {
@@ -23812,8 +24050,8 @@ function DataGrid({
23812
24050
  ),
23813
24051
  children: [
23814
24052
  imageField && (() => {
23815
- const imgUrl = getNestedValue(itemData, imageField);
23816
- if (!imgUrl || typeof imgUrl !== "string") return null;
24053
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
24054
+ if (!imgUrl) return null;
23817
24055
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
23818
24056
  "img",
23819
24057
  {
@@ -26533,6 +26771,196 @@ var init_PageTransition = __esm({
26533
26771
  exports.PageTransition.displayName = "PageTransition";
26534
26772
  }
26535
26773
  });
26774
+ function ConfettiBurst({ item, lifeMs }) {
26775
+ const particles = createConfettiParticles(item.particleCount ?? 30, item.id);
26776
+ const left = (item.x ?? 0.5) * 100;
26777
+ const top = (item.z ?? item.y ?? 0.4) * 100;
26778
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: particles.map((p) => {
26779
+ const rad = p.angle * Math.PI / 180;
26780
+ const tx = Math.cos(rad) * p.distance * (item.size ?? 1);
26781
+ const ty = Math.sin(rad) * p.distance * (item.size ?? 1) - 20;
26782
+ return /* @__PURE__ */ jsxRuntime.jsx(
26783
+ exports.Box,
26784
+ {
26785
+ className: "absolute rounded-sm",
26786
+ style: {
26787
+ left: (p.left - 50) * 2,
26788
+ top: -10,
26789
+ width: p.size,
26790
+ height: p.size,
26791
+ backgroundColor: item.color ?? p.color,
26792
+ animation: `confetti-burst ${Math.max(lifeMs - p.delay, 200)}ms ease-out ${p.delay}ms forwards`,
26793
+ opacity: 0,
26794
+ "--confetti-tx": `${tx}px`,
26795
+ "--confetti-ty": `${ty}px`,
26796
+ "--confetti-rotate": `${p.rotation}deg`
26797
+ }
26798
+ },
26799
+ p.id
26800
+ );
26801
+ }) });
26802
+ }
26803
+ function SparkleBurst({ item, lifeMs }) {
26804
+ const count = item.particleCount ?? 8;
26805
+ const scale = item.size ?? 1;
26806
+ const left = (item.x ?? 0.5) * 100;
26807
+ const top = (item.z ?? item.y ?? 0.4) * 100;
26808
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: Array.from({ length: count }, (_, i) => {
26809
+ const dx = (fxHash01(item.id, i * 4) - 0.5) * 160 * scale;
26810
+ const dy = (fxHash01(item.id, i * 4 + 1) - 0.5) * 120 * scale;
26811
+ const dot = (4 + fxHash01(item.id, i * 4 + 2) * 5) * scale;
26812
+ const delay = fxHash01(item.id, i * 4 + 3) * lifeMs * 0.4;
26813
+ return /* @__PURE__ */ jsxRuntime.jsx(
26814
+ exports.Box,
26815
+ {
26816
+ className: "absolute rounded-full",
26817
+ style: {
26818
+ left: dx,
26819
+ top: dy,
26820
+ width: dot,
26821
+ height: dot,
26822
+ backgroundColor: item.color ?? "gold",
26823
+ opacity: 0,
26824
+ animation: `fx-overlay-sparkle ${Math.max(lifeMs - delay, 200)}ms ease-in-out ${delay}ms forwards`
26825
+ }
26826
+ },
26827
+ i
26828
+ );
26829
+ }) });
26830
+ }
26831
+ function OverlayFxNode({ item, tickMs }) {
26832
+ const lifeMs = lifeMsOf(item, tickMs);
26833
+ switch (item.effect ?? "sparkle") {
26834
+ case "confetti":
26835
+ return /* @__PURE__ */ jsxRuntime.jsx(ConfettiBurst, { item, lifeMs });
26836
+ case "flash":
26837
+ return /* @__PURE__ */ jsxRuntime.jsx(
26838
+ exports.Box,
26839
+ {
26840
+ position: "absolute",
26841
+ className: "inset-0",
26842
+ style: {
26843
+ backgroundColor: item.color ?? "#ffffff",
26844
+ opacity: 0,
26845
+ animation: `fx-overlay-flash ${lifeMs}ms ease-out forwards`
26846
+ }
26847
+ }
26848
+ );
26849
+ case "streak-glow":
26850
+ return /* @__PURE__ */ jsxRuntime.jsx(
26851
+ exports.Box,
26852
+ {
26853
+ position: "absolute",
26854
+ className: "inset-0",
26855
+ style: {
26856
+ boxShadow: `inset 0 0 ${60 * (item.size ?? 1)}px ${item.color ?? "var(--color-warning)"}`,
26857
+ opacity: 0,
26858
+ animation: `fx-overlay-glow ${lifeMs}ms ease-in-out forwards`
26859
+ }
26860
+ }
26861
+ );
26862
+ case "float-text": {
26863
+ if (!item.message) return null;
26864
+ return /* @__PURE__ */ jsxRuntime.jsx(
26865
+ exports.Box,
26866
+ {
26867
+ position: "absolute",
26868
+ style: {
26869
+ left: `${(item.x ?? 0.5) * 100}%`,
26870
+ top: `${(item.z ?? item.y ?? 0.4) * 100}%`,
26871
+ color: item.color ?? "var(--color-success)",
26872
+ fontWeight: 700,
26873
+ fontSize: 16 * (item.size ?? 1),
26874
+ textShadow: "0 1px 2px rgba(0,0,0,0.4)",
26875
+ opacity: 0,
26876
+ animation: `fx-overlay-float ${lifeMs}ms ease-out forwards`,
26877
+ whiteSpace: "nowrap"
26878
+ },
26879
+ children: item.message
26880
+ }
26881
+ );
26882
+ }
26883
+ case "shake":
26884
+ return null;
26885
+ default:
26886
+ return /* @__PURE__ */ jsxRuntime.jsx(SparkleBurst, { item, lifeMs });
26887
+ }
26888
+ }
26889
+ var DEFAULT_TICK_MS2, FX_OVERLAY_KEYFRAMES, lifeMsOf; exports.FxOverlay = void 0;
26890
+ var init_FxOverlay = __esm({
26891
+ "components/core/molecules/FxOverlay.tsx"() {
26892
+ "use client";
26893
+ init_cn();
26894
+ init_Box();
26895
+ init_fx();
26896
+ DEFAULT_TICK_MS2 = 500;
26897
+ FX_OVERLAY_KEYFRAMES = `
26898
+ ${CONFETTI_BURST_KEYFRAMES}
26899
+ @keyframes fx-overlay-flash {
26900
+ 0% { opacity: 0.75; }
26901
+ 100% { opacity: 0; }
26902
+ }
26903
+ @keyframes fx-overlay-glow {
26904
+ 0% { opacity: 0.9; }
26905
+ 60% { opacity: 0.6; }
26906
+ 100% { opacity: 0; }
26907
+ }
26908
+ @keyframes fx-overlay-sparkle {
26909
+ 0% { opacity: 0; transform: scale(0); }
26910
+ 30% { opacity: 1; transform: scale(1); }
26911
+ 100% { opacity: 0; transform: scale(0.3); }
26912
+ }
26913
+ @keyframes fx-overlay-float {
26914
+ 0% { opacity: 0; transform: translate(-50%, 8px); }
26915
+ 15% { opacity: 1; }
26916
+ 100% { opacity: 0; transform: translate(-50%, -40px); }
26917
+ }
26918
+ @keyframes fx-overlay-shake {
26919
+ 0% { transform: translateX(0); }
26920
+ 15% { transform: translateX(-6px); }
26921
+ 30% { transform: translateX(5px); }
26922
+ 45% { transform: translateX(-4px); }
26923
+ 60% { transform: translateX(3px); }
26924
+ 75% { transform: translateX(-2px); }
26925
+ 100% { transform: translateX(0); }
26926
+ }
26927
+ `;
26928
+ lifeMsOf = (it, tickMs) => Math.max(it.maxTtl ?? it.ttl, it.ttl, 1) * tickMs;
26929
+ exports.FxOverlay = ({ items, tickMs = DEFAULT_TICK_MS2, children, className }) => {
26930
+ const screenItems = (Array.isArray(items) ? items : []).filter(
26931
+ (it) => Boolean(it) && typeof it.id === "string" && it.space !== "world"
26932
+ );
26933
+ const shakeItem = [...screenItems].reverse().find((it) => it.effect === "shake");
26934
+ const overlay = /* @__PURE__ */ jsxRuntime.jsxs(
26935
+ exports.Box,
26936
+ {
26937
+ position: "absolute",
26938
+ className: cn("inset-0 pointer-events-none overflow-hidden z-50", !children && className),
26939
+ "aria-hidden": "true",
26940
+ children: [
26941
+ screenItems.map((it) => /* @__PURE__ */ jsxRuntime.jsx(OverlayFxNode, { item: it, tickMs }, it.id)),
26942
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: FX_OVERLAY_KEYFRAMES })
26943
+ ]
26944
+ }
26945
+ );
26946
+ if (children !== void 0 && children !== null) {
26947
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { position: "relative", className, children: [
26948
+ /* @__PURE__ */ jsxRuntime.jsx(
26949
+ exports.Box,
26950
+ {
26951
+ style: shakeItem ? { animation: `fx-overlay-shake ${Math.min(lifeMsOf(shakeItem, tickMs), 600)}ms ease-in-out` } : void 0,
26952
+ children
26953
+ },
26954
+ shakeItem?.id ?? "steady"
26955
+ ),
26956
+ overlay
26957
+ ] });
26958
+ }
26959
+ return overlay;
26960
+ };
26961
+ exports.FxOverlay.displayName = "FxOverlay";
26962
+ }
26963
+ });
26536
26964
  function computePopoverStyle(position, triggerRect, popoverWidth) {
26537
26965
  if (position === "left" || position === "right") {
26538
26966
  return {
@@ -27339,7 +27767,7 @@ var init_SearchInput = __esm({
27339
27767
  /* @__PURE__ */ jsxRuntime.jsx(
27340
27768
  exports.Input,
27341
27769
  {
27342
- type: "text",
27770
+ type: "search",
27343
27771
  value: searchValue,
27344
27772
  onChange: handleChange,
27345
27773
  placeholder: resolvedPlaceholder,
@@ -31099,6 +31527,7 @@ exports.MathCanvas = void 0;
31099
31527
  var init_MathCanvas = __esm({
31100
31528
  "components/learning/molecules/MathCanvas.tsx"() {
31101
31529
  "use client";
31530
+ init_useEventBus();
31102
31531
  init_atoms();
31103
31532
  init_Stack();
31104
31533
  init_LearningCanvas();
@@ -31130,9 +31559,32 @@ var init_MathCanvas = __esm({
31130
31559
  interactive = false,
31131
31560
  animate = false,
31132
31561
  onShapeClick,
31562
+ keyMap,
31563
+ keyUpMap,
31133
31564
  isLoading,
31134
31565
  error
31135
31566
  }) => {
31567
+ const eventBus = useEventBus();
31568
+ React77.useEffect(() => {
31569
+ if (!keyMap && !keyUpMap) return;
31570
+ const onDown = (e) => {
31571
+ const ev = keyMap?.[e.code];
31572
+ if (ev) {
31573
+ eventBus.emit(`UI:${ev}`, {});
31574
+ e.preventDefault();
31575
+ }
31576
+ };
31577
+ const onUp = (e) => {
31578
+ const ev = keyUpMap?.[e.code];
31579
+ if (ev) eventBus.emit(`UI:${ev}`, {});
31580
+ };
31581
+ window.addEventListener("keydown", onDown);
31582
+ window.addEventListener("keyup", onUp);
31583
+ return () => {
31584
+ window.removeEventListener("keydown", onDown);
31585
+ window.removeEventListener("keyup", onUp);
31586
+ };
31587
+ }, [keyMap, keyUpMap, eventBus]);
31136
31588
  const derivedShapes = React77.useMemo(() => {
31137
31589
  const out = [];
31138
31590
  const margin = 24;
@@ -31145,11 +31597,11 @@ var init_MathCanvas = __esm({
31145
31597
  if (showGrid) {
31146
31598
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
31147
31599
  const px = mapX(x);
31148
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
31600
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
31149
31601
  }
31150
31602
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
31151
31603
  const py = mapY(y);
31152
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
31604
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
31153
31605
  }
31154
31606
  }
31155
31607
  if (showTickLabels) {
@@ -31198,7 +31650,7 @@ var init_MathCanvas = __esm({
31198
31650
  x: mapX((first.x + last.x) / 2),
31199
31651
  y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
31200
31652
  text: region.label,
31201
- color: "#111827",
31653
+ color,
31202
31654
  fontSize: 11
31203
31655
  });
31204
31656
  }
@@ -31231,14 +31683,14 @@ var init_MathCanvas = __esm({
31231
31683
  const px = mapX(guide.at);
31232
31684
  out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
31233
31685
  if (guide.label) {
31234
- out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color: "#111827", fontSize: 11 });
31686
+ out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
31235
31687
  }
31236
31688
  } else {
31237
31689
  if (guide.at < yMin || guide.at > yMax) continue;
31238
31690
  const py = mapY(guide.at);
31239
31691
  out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
31240
31692
  if (guide.label) {
31241
- out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color: "#111827", fontSize: 11, align: "right" });
31693
+ out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: 11, align: "right" });
31242
31694
  }
31243
31695
  }
31244
31696
  }
@@ -31304,7 +31756,7 @@ var init_MathCanvas = __esm({
31304
31756
  x: (x1 + x2) / 2,
31305
31757
  y: xAxisY - peak - 8,
31306
31758
  text: hop.label,
31307
- color: "#111827",
31759
+ color,
31308
31760
  fontSize: 10,
31309
31761
  align: "center"
31310
31762
  });
@@ -31331,7 +31783,7 @@ var init_MathCanvas = __esm({
31331
31783
  x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
31332
31784
  y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
31333
31785
  text: angle.label,
31334
- color: "#111827",
31786
+ color,
31335
31787
  fontSize: 11,
31336
31788
  align: "center"
31337
31789
  });
@@ -31349,7 +31801,7 @@ var init_MathCanvas = __esm({
31349
31801
  fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
31350
31802
  });
31351
31803
  if (p.label) {
31352
- out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
31804
+ out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: 12 });
31353
31805
  }
31354
31806
  }
31355
31807
  for (const v of vectors) {
@@ -31360,7 +31812,7 @@ var init_MathCanvas = __esm({
31360
31812
  const y2 = mapY(v.y + v.vy);
31361
31813
  out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
31362
31814
  if (v.label) {
31363
- out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
31815
+ out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: 12 });
31364
31816
  }
31365
31817
  }
31366
31818
  out.push(...shapes);
@@ -32624,13 +33076,13 @@ var init_MapView = __esm({
32624
33076
  shadowSize: [41, 41]
32625
33077
  });
32626
33078
  L.Marker.prototype.options.icon = defaultIcon;
32627
- const { useEffect: useEffect66, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__namespace.default;
33079
+ const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__namespace.default;
32628
33080
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32629
33081
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32630
33082
  function MapUpdater({ centerLat, centerLng, zoom }) {
32631
33083
  const map = useMap();
32632
33084
  const prevRef = useRef65({ centerLat, centerLng, zoom });
32633
- useEffect66(() => {
33085
+ useEffect67(() => {
32634
33086
  const prev = prevRef.current;
32635
33087
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
32636
33088
  map.setView([centerLat, centerLng], zoom);
@@ -32641,7 +33093,7 @@ var init_MapView = __esm({
32641
33093
  }
32642
33094
  function MapClickHandler({ onMapClick }) {
32643
33095
  const map = useMap();
32644
- useEffect66(() => {
33096
+ useEffect67(() => {
32645
33097
  if (!onMapClick) return;
32646
33098
  const handler = (e) => {
32647
33099
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -39457,6 +39909,7 @@ var init_PageHeader = __esm({
39457
39909
  className
39458
39910
  }) => {
39459
39911
  const eventBus = useEventBus();
39912
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
39460
39913
  const handleBack = () => {
39461
39914
  eventBus.emit(`UI:${backEvent}`);
39462
39915
  };
@@ -39504,15 +39957,15 @@ var init_PageHeader = __esm({
39504
39957
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { children: [
39505
39958
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center gap-3", children: [
39506
39959
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
39507
- status && /* @__PURE__ */ jsxRuntime.jsx(
39960
+ statusBadge && /* @__PURE__ */ jsxRuntime.jsx(
39508
39961
  exports.Typography,
39509
39962
  {
39510
39963
  variant: "small",
39511
39964
  className: cn(
39512
39965
  "px-2.5 py-1 rounded-full text-xs font-medium",
39513
- statusColors2[status.variant || "default"]
39966
+ statusColors2[statusBadge.variant || "default"]
39514
39967
  ),
39515
- children: status.label
39968
+ children: statusBadge.label
39516
39969
  }
39517
39970
  )
39518
39971
  ] }),
@@ -42335,6 +42788,7 @@ var init_molecules2 = __esm({
42335
42788
  init_Menu();
42336
42789
  init_Modal();
42337
42790
  init_PageTransition();
42791
+ init_FxOverlay();
42338
42792
  init_Pagination();
42339
42793
  init_Popover();
42340
42794
  init_Coachmark();
@@ -42965,7 +43419,7 @@ function formatFieldValue2(value, fieldName) {
42965
43419
  }
42966
43420
  return String(value);
42967
43421
  }
42968
- function renderRichFieldValue(value, fieldName, fieldType) {
43422
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
42969
43423
  if (value === void 0 || value === null) return "\u2014";
42970
43424
  const str2 = String(value);
42971
43425
  switch (fieldType) {
@@ -43032,19 +43486,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
43032
43486
  }
43033
43487
  );
43034
43488
  case "date":
43035
- case "datetime": {
43489
+ case "datetime":
43490
+ case "timestamp": {
43036
43491
  const d = new Date(str2);
43037
43492
  if (!isNaN(d.getTime())) {
43038
43493
  return d.toLocaleDateString(void 0, {
43039
43494
  year: "numeric",
43040
43495
  month: "long",
43041
43496
  day: "numeric",
43042
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
43497
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
43043
43498
  });
43044
43499
  }
43045
43500
  return str2;
43046
43501
  }
43502
+ case "money": {
43503
+ const n = typeof value === "number" ? value : Number(str2);
43504
+ if (!isNaN(n)) {
43505
+ return new Intl.NumberFormat(void 0, {
43506
+ style: "currency",
43507
+ currency: "USD"
43508
+ }).format(n);
43509
+ }
43510
+ return str2;
43511
+ }
43512
+ case "relation": {
43513
+ const match = meta?.options?.find((opt) => opt.value === str2);
43514
+ return match ? match.label : str2;
43515
+ }
43516
+ case "file": {
43517
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
43518
+ const file = value;
43519
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
43520
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
43521
+ return /* @__PURE__ */ jsxRuntime.jsxs(
43522
+ "a",
43523
+ {
43524
+ href: typeof file.url === "string" ? file.url : void 0,
43525
+ download: label,
43526
+ className: "inline-flex items-center gap-1.5 rounded-md border border-border bg-muted px-2 py-1 text-sm text-foreground no-underline hover:bg-accent",
43527
+ children: [
43528
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.FileText, size: "sm", className: "text-muted-foreground" }),
43529
+ label,
43530
+ size && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { as: "span", variant: "caption", color: "muted", children: size })
43531
+ ]
43532
+ }
43533
+ );
43534
+ }
43535
+ return str2;
43536
+ }
43047
43537
  default:
43538
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str2)) {
43539
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: getBadgeVariant(fieldName, str2), children: humanizeEnumValue(str2) });
43540
+ }
43048
43541
  return formatFieldValue2(value, fieldName);
43049
43542
  }
43050
43543
  }
@@ -43071,8 +43564,18 @@ function buildFieldTypeMap(fields) {
43071
43564
  const map = {};
43072
43565
  if (!fields) return map;
43073
43566
  for (const f3 of fields) {
43074
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
43075
- map[f3.name] = f3.type;
43567
+ if (typeof f3 === "object" && f3.type !== void 0) {
43568
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
43569
+ }
43570
+ }
43571
+ return map;
43572
+ }
43573
+ function buildFieldMetaMap(fields) {
43574
+ const map = {};
43575
+ if (!fields) return map;
43576
+ for (const f3 of fields) {
43577
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
43578
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
43076
43579
  }
43077
43580
  }
43078
43581
  return map;
@@ -43092,6 +43595,7 @@ var init_DetailPanel = __esm({
43092
43595
  init_format();
43093
43596
  init_getNestedValue();
43094
43597
  init_useEventBus();
43598
+ init_UploadDropZone();
43095
43599
  formatFieldLabel = humanizeFieldName;
43096
43600
  ReactMarkdown2 = React77.lazy(() => import('react-markdown'));
43097
43601
  exports.DetailPanel = ({
@@ -43101,6 +43605,7 @@ var init_DetailPanel = __esm({
43101
43605
  avatar,
43102
43606
  sections: propSections,
43103
43607
  actions,
43608
+ backAction,
43104
43609
  footer,
43105
43610
  slideOver = false,
43106
43611
  className,
@@ -43109,7 +43614,8 @@ var init_DetailPanel = __esm({
43109
43614
  fieldNames,
43110
43615
  initialData,
43111
43616
  isLoading = false,
43112
- error
43617
+ error,
43618
+ relationsData
43113
43619
  }) => {
43114
43620
  const eventBus = useEventBus();
43115
43621
  const { t } = hooks.useTranslate();
@@ -43120,8 +43626,15 @@ var init_DetailPanel = __esm({
43120
43626
  };
43121
43627
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
43122
43628
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
43629
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
43123
43630
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
43124
43631
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
43632
+ const metaFor = (field) => {
43633
+ const base = fieldMetaMap[field];
43634
+ const options = relationsData?.[field];
43635
+ if (!base && !options) return void 0;
43636
+ return { ...base, options };
43637
+ };
43125
43638
  const handleActionClick = React77.useCallback(
43126
43639
  (action, data2) => {
43127
43640
  if (action.navigatesTo) {
@@ -43200,7 +43713,7 @@ var init_DetailPanel = __esm({
43200
43713
  if (value !== void 0 && value !== null) {
43201
43714
  overviewFields.push({
43202
43715
  label: labelFor(field),
43203
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43716
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43204
43717
  icon: getFieldIcon(field)
43205
43718
  });
43206
43719
  }
@@ -43216,7 +43729,7 @@ var init_DetailPanel = __esm({
43216
43729
  if (value !== void 0 && value !== null) {
43217
43730
  metricsFields.push({
43218
43731
  label: labelFor(field),
43219
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43732
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43220
43733
  icon: getFieldIcon(field)
43221
43734
  });
43222
43735
  }
@@ -43232,7 +43745,7 @@ var init_DetailPanel = __esm({
43232
43745
  if (value !== void 0 && value !== null) {
43233
43746
  timelineFields.push({
43234
43747
  label: labelFor(field),
43235
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43748
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43236
43749
  icon: getFieldIcon(field)
43237
43750
  });
43238
43751
  }
@@ -43248,7 +43761,7 @@ var init_DetailPanel = __esm({
43248
43761
  if (value !== void 0 && value !== null) {
43249
43762
  descFields.push({
43250
43763
  label: labelFor(field),
43251
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43764
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43252
43765
  icon: getFieldIcon(field)
43253
43766
  });
43254
43767
  }
@@ -43296,7 +43809,7 @@ var init_DetailPanel = __esm({
43296
43809
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
43297
43810
  allFields.push({
43298
43811
  label: labelFor(field),
43299
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43812
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43300
43813
  icon: getFieldIcon(field)
43301
43814
  });
43302
43815
  } else {
@@ -43311,34 +43824,49 @@ var init_DetailPanel = __esm({
43311
43824
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
43312
43825
  const effectiveCloseAction = closeAction ?? { event: void 0};
43313
43826
  const content = /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "md", className: "p-6", children: [
43314
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "end", align: "center", gap: "xs", children: [
43315
- otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
43316
- exports.Button,
43317
- {
43318
- variant: action.variant || "secondary",
43319
- size: "sm",
43320
- action: action.navigatesTo ? void 0 : action.event,
43321
- actionPayload: { row: normalizedData },
43322
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43323
- icon: action.icon,
43324
- "data-testid": action.event ? `action-${action.event}` : void 0,
43325
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43326
- children: action.label
43327
- },
43328
- idx
43329
- )),
43330
- /* @__PURE__ */ jsxRuntime.jsx(
43827
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "center", gap: "xs", children: [
43828
+ /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
43331
43829
  exports.Button,
43332
43830
  {
43333
- variant: "ghost",
43831
+ variant: backAction.variant || "ghost",
43334
43832
  size: "sm",
43335
- action: effectiveCloseAction.event,
43833
+ action: backAction.navigatesTo ? void 0 : backAction.event,
43336
43834
  actionPayload: { row: normalizedData },
43337
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
43338
- icon: LucideIcons2.X,
43339
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
43835
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
43836
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
43837
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
43838
+ children: backAction.label
43340
43839
  }
43341
- )
43840
+ ) }),
43841
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "end", align: "center", gap: "xs", children: [
43842
+ otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
43843
+ exports.Button,
43844
+ {
43845
+ variant: action.variant || "secondary",
43846
+ size: "sm",
43847
+ action: action.navigatesTo ? void 0 : action.event,
43848
+ actionPayload: { row: normalizedData },
43849
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43850
+ icon: action.icon,
43851
+ "data-testid": action.event ? `action-${action.event}` : void 0,
43852
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43853
+ children: action.label
43854
+ },
43855
+ idx
43856
+ )),
43857
+ /* @__PURE__ */ jsxRuntime.jsx(
43858
+ exports.Button,
43859
+ {
43860
+ variant: "ghost",
43861
+ size: "sm",
43862
+ action: effectiveCloseAction.event,
43863
+ actionPayload: { row: normalizedData },
43864
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
43865
+ icon: LucideIcons2.X,
43866
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
43867
+ }
43868
+ )
43869
+ ] })
43342
43870
  ] }),
43343
43871
  avatar,
43344
43872
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -43653,6 +44181,10 @@ function determineInputType(field) {
43653
44181
  return "password";
43654
44182
  case "url":
43655
44183
  return "url";
44184
+ case "file":
44185
+ return "file";
44186
+ case "money":
44187
+ return "currency";
43656
44188
  case "number":
43657
44189
  case "integer":
43658
44190
  case "float":
@@ -43714,6 +44246,7 @@ var init_Form = __esm({
43714
44246
  init_Typography();
43715
44247
  init_Icon();
43716
44248
  init_RelationSelect();
44249
+ init_UploadDropZone();
43717
44250
  init_Alert();
43718
44251
  init_useEventBus();
43719
44252
  init_debug();
@@ -43756,6 +44289,7 @@ var init_Form = __esm({
43756
44289
  cancelEvent = "CANCEL",
43757
44290
  relationsData = {},
43758
44291
  relationsLoading = {},
44292
+ fieldOverrides,
43759
44293
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
43760
44294
  conditionalFields: conditionalFieldsRaw = {},
43761
44295
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -44002,7 +44536,8 @@ var init_Form = __esm({
44002
44536
  label,
44003
44537
  field.required && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
44004
44538
  ] }),
44005
- renderFieldInput(field, fieldName, inputType, currentValue2, label)
44539
+ renderFieldInput(field, fieldName, inputType, currentValue2, label),
44540
+ field.hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", children: field.hint })
44006
44541
  ] }, fieldName);
44007
44542
  },
44008
44543
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -44018,6 +44553,7 @@ var init_Form = __esm({
44018
44553
  name: field,
44019
44554
  type: entityField.type,
44020
44555
  required: entityField.required,
44556
+ hint: entityField.description,
44021
44557
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
44022
44558
  defaultValue: entityField.default,
44023
44559
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -44030,8 +44566,18 @@ var init_Form = __esm({
44030
44566
  return { name: field, type: "string" };
44031
44567
  }
44032
44568
  return field;
44569
+ }).map((field) => {
44570
+ const fieldName = field.name || field.field;
44571
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
44572
+ if (!override) return field;
44573
+ return {
44574
+ ...field,
44575
+ ...override.label !== void 0 ? { label: override.label } : {},
44576
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
44577
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
44578
+ };
44033
44579
  });
44034
- }, [effectiveFields, resolvedEntity]);
44580
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
44035
44581
  const schemaFields = React77__namespace.default.useMemo(() => {
44036
44582
  if (normalizedFields.length === 0) return null;
44037
44583
  if (isDebugEnabled()) {
@@ -44155,6 +44701,44 @@ var init_Form = __esm({
44155
44701
  max: field.max
44156
44702
  }
44157
44703
  );
44704
+ case "currency":
44705
+ return /* @__PURE__ */ jsxRuntime.jsx(
44706
+ exports.Input,
44707
+ {
44708
+ ...commonProps,
44709
+ type: "number",
44710
+ step: "0.01",
44711
+ icon: LucideIcons2.DollarSign,
44712
+ value: currentValue2 !== void 0 && currentValue2 !== "" ? String(currentValue2) : "",
44713
+ onChange: (e) => handleChange(
44714
+ fieldName,
44715
+ e.target.value ? Number(e.target.value) : void 0
44716
+ ),
44717
+ min: field.min,
44718
+ max: field.max
44719
+ }
44720
+ );
44721
+ case "file":
44722
+ return /* @__PURE__ */ jsxRuntime.jsx(
44723
+ exports.UploadDropZone,
44724
+ {
44725
+ accept: field.pattern,
44726
+ maxFiles: 1,
44727
+ disabled: isLoading,
44728
+ onFiles: (files) => {
44729
+ const f3 = files[0];
44730
+ if (!f3) return;
44731
+ const reader = new FileReader();
44732
+ reader.onload = () => handleChange(fieldName, {
44733
+ name: f3.name,
44734
+ mimeType: f3.type,
44735
+ sizeBytes: f3.size,
44736
+ url: typeof reader.result === "string" ? reader.result : ""
44737
+ });
44738
+ reader.readAsDataURL(f3);
44739
+ }
44740
+ }
44741
+ );
44158
44742
  case "date":
44159
44743
  return /* @__PURE__ */ jsxRuntime.jsx(
44160
44744
  exports.Input,
@@ -45187,6 +45771,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES; exports.MediaGallery = void 0;
45187
45771
  var init_MediaGallery = __esm({
45188
45772
  "components/core/organisms/MediaGallery.tsx"() {
45189
45773
  "use client";
45774
+ init_getNestedValue();
45190
45775
  init_cn();
45191
45776
  init_atoms();
45192
45777
  init_Stack();
@@ -45255,9 +45840,9 @@ var init_MediaGallery = __esm({
45255
45840
  return entityData.map((record, idx) => {
45256
45841
  return {
45257
45842
  id: String(record.id ?? idx),
45258
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
45843
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
45259
45844
  alt: record.alt ? String(record.alt) : void 0,
45260
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
45845
+ thumbnail: resolveImageUrl(record.thumbnail),
45261
45846
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
45262
45847
  };
45263
45848
  });
@@ -49125,6 +49710,7 @@ var init_component_registry_generated = __esm({
49125
49710
  init_DocSidebar();
49126
49711
  init_DocTOC();
49127
49712
  init_DocumentViewer();
49713
+ init_DrawFxLayer();
49128
49714
  init_DrawGroup();
49129
49715
  init_DrawMesh();
49130
49716
  init_DrawShape();
@@ -49155,6 +49741,7 @@ var init_component_registry_generated = __esm({
49155
49741
  init_FormField();
49156
49742
  init_FormSection();
49157
49743
  init_FormSectionHeader();
49744
+ init_FxOverlay();
49158
49745
  init_GameAudioToggle();
49159
49746
  init_GameHud();
49160
49747
  init_GameIcon();
@@ -49395,6 +49982,7 @@ var init_component_registry_generated = __esm({
49395
49982
  "DocSidebar": exports.DocSidebar,
49396
49983
  "DocTOC": exports.DocTOC,
49397
49984
  "DocumentViewer": exports.DocumentViewer,
49985
+ "DrawFxLayer": DrawFxLayer,
49398
49986
  "DrawGroup": DrawGroup,
49399
49987
  "DrawMesh": DrawMesh,
49400
49988
  "DrawShape": DrawShape,
@@ -49425,6 +50013,7 @@ var init_component_registry_generated = __esm({
49425
50013
  "FormField": exports.FormField,
49426
50014
  "FormLayout": exports.FormLayout,
49427
50015
  "FormSectionHeader": exports.FormSectionHeader,
50016
+ "FxOverlay": exports.FxOverlay,
49428
50017
  "GameAudioToggle": GameAudioToggle,
49429
50018
  "GameHud": GameHud,
49430
50019
  "GameIcon": GameIcon,
@@ -49600,7 +50189,8 @@ __export(UISlotRenderer_exports, {
49600
50189
  SlotContentRenderer: () => SlotContentRenderer,
49601
50190
  SuspenseConfigProvider: () => SuspenseConfigProvider,
49602
50191
  UISlotComponent: () => UISlotComponent,
49603
- UISlotRenderer: () => UISlotRenderer
50192
+ UISlotRenderer: () => UISlotRenderer,
50193
+ renderPatternValue: () => renderPatternValue
49604
50194
  });
49605
50195
  function SuspenseConfigProvider({
49606
50196
  config,
@@ -49636,6 +50226,9 @@ function enrichFormFields(fields, entityDef) {
49636
50226
  type: entityField.type,
49637
50227
  required: entityField.required ?? false
49638
50228
  };
50229
+ if (entityField.description) {
50230
+ enriched.hint = entityField.description;
50231
+ }
49639
50232
  if (entityField.values && entityField.values.length > 0) {
49640
50233
  enriched.values = entityField.values;
49641
50234
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -49659,6 +50252,9 @@ function enrichFormFields(fields, entityDef) {
49659
50252
  if (entityField.required && !("required" in obj)) {
49660
50253
  enriched.required = true;
49661
50254
  }
50255
+ if (entityField.description && !obj.hint && !obj.help) {
50256
+ enriched.hint = entityField.description;
50257
+ }
49662
50258
  if (!obj.values && !obj.options) {
49663
50259
  if (entityField.values && entityField.values.length > 0) {
49664
50260
  enriched.values = entityField.values;
@@ -49674,6 +50270,32 @@ function enrichFormFields(fields, entityDef) {
49674
50270
  return field;
49675
50271
  });
49676
50272
  }
50273
+ function enrichDetailFields(fields, entityDef) {
50274
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
50275
+ const metaFor = (name) => {
50276
+ const entityField = fieldMap.get(name);
50277
+ if (!entityField) return void 0;
50278
+ const meta = { type: entityField.type };
50279
+ const values = entityField.values ?? entityField.enumValues;
50280
+ if (values && values.length > 0) meta.values = values;
50281
+ if (entityField.relation) meta.relation = entityField.relation.entity;
50282
+ return meta;
50283
+ };
50284
+ return fields.map((field) => {
50285
+ if (typeof field === "string") {
50286
+ const meta = metaFor(field);
50287
+ return meta ? { key: field, ...meta } : field;
50288
+ }
50289
+ if (field && typeof field === "object" && !Array.isArray(field) && !React77__namespace.default.isValidElement(field) && !(field instanceof Date)) {
50290
+ const obj = field;
50291
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
50292
+ if (!fieldName || obj.type) return field;
50293
+ const meta = metaFor(fieldName);
50294
+ return meta ? { ...obj, ...meta } : field;
50295
+ }
50296
+ return field;
50297
+ });
50298
+ }
49677
50299
  function renderContainedPortal(t, slot, content, onDismiss) {
49678
50300
  const slotContent = /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content, onDismiss }) });
49679
50301
  const slotId = `slot-${slot}`;
@@ -50183,6 +50805,24 @@ function isPatternConfig(value) {
50183
50805
  const record = value;
50184
50806
  return "type" in record && typeof record.type === "string" && patterns.getComponentForPattern(record.type) !== null;
50185
50807
  }
50808
+ function renderPatternValue(value) {
50809
+ if (value === null || value === void 0) return null;
50810
+ if (typeof value === "boolean" || typeof value === "function") return null;
50811
+ if (typeof value === "number") return value;
50812
+ if (typeof value === "string") return renderPatternChildren(value, () => {
50813
+ });
50814
+ if (value instanceof Date) return value.toLocaleString();
50815
+ if (React77__namespace.default.isValidElement(value)) return value;
50816
+ if (Array.isArray(value)) {
50817
+ return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React77__namespace.default.Fragment, { children: renderPatternValue(item) }, index));
50818
+ }
50819
+ if (isPatternConfig(value)) {
50820
+ const { type, ...props } = value;
50821
+ return renderPatternChildren({ type, props }, () => {
50822
+ });
50823
+ }
50824
+ return null;
50825
+ }
50186
50826
  function isPlainConfigObject(value) {
50187
50827
  if (React77__namespace.default.isValidElement(value)) return false;
50188
50828
  if (value instanceof Date) return false;
@@ -50384,9 +51024,9 @@ function SlotContentRenderer({
50384
51024
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
50385
51025
  }
50386
51026
  }
50387
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
50388
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
50389
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
51027
+ const fieldsContract = patterns.getPatternFieldsContract(content.pattern) ?? (patterns.getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
51028
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
51029
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
50390
51030
  }
50391
51031
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
50392
51032
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -50496,7 +51136,7 @@ function UISlotRenderer({
50496
51136
  }
50497
51137
  return wrapped;
50498
51138
  }
50499
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51139
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
50500
51140
  var init_UISlotRenderer = __esm({
50501
51141
  "components/core/organisms/UISlotRenderer.tsx"() {
50502
51142
  "use client";
@@ -50527,12 +51167,6 @@ var init_UISlotRenderer = __esm({
50527
51167
  modal: "form",
50528
51168
  drawer: "form"
50529
51169
  };
50530
- FORM_PATTERNS = /* @__PURE__ */ new Set([
50531
- "form",
50532
- "form-section",
50533
- "inline-edit-form",
50534
- "wizard-step"
50535
- ]);
50536
51170
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
50537
51171
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
50538
51172
  "logo",
@@ -53163,6 +53797,7 @@ exports.parseLessonSegments = parseLessonSegments;
53163
53797
  exports.parseMarkdownWithCodeBlocks = parseMarkdownWithCodeBlocks;
53164
53798
  exports.parseQueryBinding = parseQueryBinding;
53165
53799
  exports.registerCodeLanguageLoader = registerCodeLanguageLoader;
53800
+ exports.renderPatternValue = renderPatternValue;
53166
53801
  exports.resolveFieldMap = resolveFieldMap;
53167
53802
  exports.resolveFrame = resolveFrame;
53168
53803
  exports.resolveSheetDirection = resolveSheetDirection;