@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
@@ -9546,27 +9546,61 @@ var init_InfiniteScrollSentinel = __esm({
9546
9546
  InfiniteScrollSentinel.displayName = "InfiniteScrollSentinel";
9547
9547
  }
9548
9548
  });
9549
- function createParticles(count) {
9550
- return Array.from({ length: count }, () => {
9551
- particleIdCounter += 1;
9552
- return {
9553
- id: particleIdCounter,
9554
- color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)],
9555
- left: 30 + Math.random() * 40,
9556
- delay: Math.random() * 300,
9557
- angle: Math.random() * 360,
9558
- distance: 40 + Math.random() * 80,
9559
- rotation: Math.random() * 720 - 360,
9560
- size: 4 + Math.random() * 6
9561
- };
9562
- });
9549
+
9550
+ // components/core/atoms/fx.ts
9551
+ function resolveFxView(item, presets) {
9552
+ const row = presets?.find((p) => p.type === item.type);
9553
+ if (!row) return item;
9554
+ return {
9555
+ ...item,
9556
+ space: item.space ?? row.space,
9557
+ effect: item.effect ?? row.effect,
9558
+ color: item.color ?? row.color,
9559
+ size: item.size ?? row.size,
9560
+ vx: item.vx ?? row.vx,
9561
+ vy: item.vy ?? row.vy,
9562
+ vz: item.vz ?? row.vz,
9563
+ particleCount: item.particleCount ?? row.particleCount,
9564
+ shape: row.shape,
9565
+ count: row.count,
9566
+ glow: row.glow,
9567
+ gravity: row.gravity,
9568
+ color2: row.color2
9569
+ };
9563
9570
  }
9564
- var CONFETTI_COLORS, particleIdCounter, ConfettiEffect;
9565
- var init_ConfettiEffect = __esm({
9566
- "components/core/atoms/ConfettiEffect.tsx"() {
9567
- "use client";
9568
- init_cn();
9569
- init_Box();
9571
+ function fxLifecycle(item, epochNowMs, tickMs) {
9572
+ const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
9573
+ const lifeMs = maxTtl * tickMs;
9574
+ const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
9575
+ const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
9576
+ return { lifeMs, ageMs, progress, fade: 1 - progress };
9577
+ }
9578
+ function fxHash01(seed, salt) {
9579
+ let h = 2166136261 ^ salt;
9580
+ for (let i = 0; i < seed.length; i++) {
9581
+ h ^= seed.charCodeAt(i);
9582
+ h = Math.imul(h, 16777619);
9583
+ }
9584
+ h ^= h >>> 13;
9585
+ h = Math.imul(h, 1274126177);
9586
+ h ^= h >>> 16;
9587
+ return (h >>> 0) / 4294967296;
9588
+ }
9589
+ function createConfettiParticles(count, seed) {
9590
+ return Array.from({ length: count }, (_, i) => ({
9591
+ id: i,
9592
+ color: CONFETTI_COLORS[Math.floor(fxHash01(seed, i * 7 + 1) * CONFETTI_COLORS.length)],
9593
+ left: 30 + fxHash01(seed, i * 7 + 2) * 40,
9594
+ delay: fxHash01(seed, i * 7 + 3) * 300,
9595
+ angle: fxHash01(seed, i * 7 + 4) * 360,
9596
+ distance: 40 + fxHash01(seed, i * 7 + 5) * 80,
9597
+ rotation: fxHash01(seed, i * 7 + 6) * 720 - 360,
9598
+ size: 4 + fxHash01(seed, i * 7 + 7) * 6
9599
+ }));
9600
+ }
9601
+ var CONFETTI_COLORS, CONFETTI_BURST_KEYFRAMES;
9602
+ var init_fx = __esm({
9603
+ "components/core/atoms/fx.ts"() {
9570
9604
  CONFETTI_COLORS = [
9571
9605
  "var(--color-primary)",
9572
9606
  "var(--color-success)",
@@ -9575,7 +9609,30 @@ var init_ConfettiEffect = __esm({
9575
9609
  "gold",
9576
9610
  "dodgerblue"
9577
9611
  ];
9578
- particleIdCounter = 0;
9612
+ CONFETTI_BURST_KEYFRAMES = `
9613
+ @keyframes confetti-burst {
9614
+ 0% {
9615
+ opacity: 1;
9616
+ transform: translate(0, 0) rotate(0deg) scale(1);
9617
+ }
9618
+ 70% {
9619
+ opacity: 1;
9620
+ }
9621
+ 100% {
9622
+ opacity: 0;
9623
+ transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
9624
+ }
9625
+ }
9626
+ `;
9627
+ }
9628
+ });
9629
+ var ConfettiEffect;
9630
+ var init_ConfettiEffect = __esm({
9631
+ "components/core/atoms/ConfettiEffect.tsx"() {
9632
+ "use client";
9633
+ init_cn();
9634
+ init_Box();
9635
+ init_fx();
9579
9636
  ConfettiEffect = ({
9580
9637
  trigger,
9581
9638
  duration = 2e3,
@@ -9584,11 +9641,13 @@ var init_ConfettiEffect = __esm({
9584
9641
  }) => {
9585
9642
  const [particles, setParticles] = React94.useState([]);
9586
9643
  const previousTriggerRef = React94.useRef(false);
9644
+ const burstRef = React94.useRef(0);
9587
9645
  React94.useEffect(() => {
9588
9646
  const wasFalse = !previousTriggerRef.current;
9589
9647
  previousTriggerRef.current = trigger;
9590
9648
  if (trigger && wasFalse) {
9591
- const newParticles = createParticles(particleCount);
9649
+ burstRef.current += 1;
9650
+ const newParticles = createConfettiParticles(particleCount, `confetti-${burstRef.current}`);
9592
9651
  setParticles(newParticles);
9593
9652
  const timer = window.setTimeout(() => {
9594
9653
  setParticles([]);
@@ -9636,21 +9695,7 @@ var init_ConfettiEffect = __esm({
9636
9695
  p.id
9637
9696
  );
9638
9697
  }),
9639
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
9640
- @keyframes confetti-burst {
9641
- 0% {
9642
- opacity: 1;
9643
- transform: translate(0, 0) rotate(0deg) scale(1);
9644
- }
9645
- 70% {
9646
- opacity: 1;
9647
- }
9648
- 100% {
9649
- opacity: 0;
9650
- transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
9651
- }
9652
- }
9653
- ` })
9698
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: CONFETTI_BURST_KEYFRAMES })
9654
9699
  ]
9655
9700
  }
9656
9701
  );
@@ -13206,7 +13251,7 @@ var init_DrawShape = __esm({
13206
13251
  const cx = p.x + (node.offsetX ?? 0) * tw;
13207
13252
  const cy = p.y + (node.offsetY ?? 0) * tw;
13208
13253
  const rx = (node.radiusX ?? 0) * tw;
13209
- const ry = (node.radiusY ?? rx) * tw;
13254
+ const ry = (node.radiusY ?? node.radiusX ?? 0) * tw;
13210
13255
  if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
13211
13256
  if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
13212
13257
  if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
@@ -13396,6 +13441,172 @@ var init_DrawTextLayer = __esm({
13396
13441
  };
13397
13442
  }
13398
13443
  });
13444
+
13445
+ // components/game/molecules/DrawFxLayer.tsx
13446
+ function fxPosition(view, dim, ageSec) {
13447
+ const g = view.gravity ?? 0;
13448
+ const fall = 0.5 * g * ageSec * ageSec;
13449
+ {
13450
+ const base2 = view.position ?? { x: view.x, y: view.z ?? view.y ?? 0 };
13451
+ if (!Number.isFinite(base2.x) || !Number.isFinite(base2.y)) return void 0;
13452
+ return {
13453
+ x: base2.x + (view.vx ?? 0) * ageSec,
13454
+ y: base2.y + (view.vz ?? 0) * ageSec + fall
13455
+ };
13456
+ }
13457
+ }
13458
+ function sparkShape(view, pos, i, fade, progress) {
13459
+ const size = view.size ?? 0.5;
13460
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
13461
+ const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut2(progress);
13462
+ const r2 = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
13463
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
13464
+ return {
13465
+ type: "draw-shape",
13466
+ shape: "ellipse",
13467
+ position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
13468
+ anchor: "center",
13469
+ radiusX: r2,
13470
+ fill: color,
13471
+ blendMode: "lighter",
13472
+ opacity: fade,
13473
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
13474
+ };
13475
+ }
13476
+ function proceduralShapes(view, pos, fade, progress) {
13477
+ const size = view.size ?? 0.5;
13478
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
13479
+ switch (view.shape ?? "spark") {
13480
+ case "ring": {
13481
+ return [
13482
+ {
13483
+ type: "draw-shape",
13484
+ shape: "ellipse",
13485
+ position: pos,
13486
+ anchor: "center",
13487
+ radiusX: size * easeOut2(progress),
13488
+ stroke: view.color2 ?? color,
13489
+ strokeWidth: 2,
13490
+ blendMode: "lighter",
13491
+ opacity: fade,
13492
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
13493
+ }
13494
+ ];
13495
+ }
13496
+ case "puff": {
13497
+ const count = view.count ?? 3;
13498
+ return Array.from({ length: count }, (_, i) => {
13499
+ const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
13500
+ const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut2(progress);
13501
+ return {
13502
+ type: "draw-shape",
13503
+ shape: "ellipse",
13504
+ position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
13505
+ anchor: "center",
13506
+ radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
13507
+ fill: i === 0 && view.color2 ? view.color2 : color,
13508
+ opacity: fade * 0.6,
13509
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
13510
+ };
13511
+ });
13512
+ }
13513
+ case "streak": {
13514
+ const count = view.count ?? 5;
13515
+ return Array.from({ length: count }, (_, i) => {
13516
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
13517
+ const inner = size * (0.2 + 0.8 * easeOut2(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
13518
+ const len = size * 0.35;
13519
+ return {
13520
+ type: "draw-shape",
13521
+ shape: "ellipse",
13522
+ position: pos,
13523
+ anchor: "center",
13524
+ offsetX: inner + len / 2,
13525
+ offsetY: 0,
13526
+ radiusX: len / 2,
13527
+ radiusY: size * 0.04,
13528
+ rotate: angle,
13529
+ fill: color,
13530
+ blendMode: "lighter",
13531
+ opacity: fade,
13532
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
13533
+ };
13534
+ });
13535
+ }
13536
+ default: {
13537
+ const count = view.count ?? 6;
13538
+ return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
13539
+ }
13540
+ }
13541
+ }
13542
+ function expandFxItem(view, node, epochNowMs, dim) {
13543
+ if (view.space === "screen") return [];
13544
+ const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
13545
+ const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
13546
+ if (progress >= 1) return [];
13547
+ const pos = fxPosition(view, dim, ageMs / 1e3);
13548
+ if (!pos) return [];
13549
+ const out = [];
13550
+ const artItems = node.art?.[view.type]?.["idle"];
13551
+ const sprite = node.sprites?.[view.type];
13552
+ if (Array.isArray(artItems)) {
13553
+ out.push({
13554
+ type: "draw-group",
13555
+ position: pos,
13556
+ opacity: fade,
13557
+ ...view.size !== void 0 ? { scale: view.size } : {},
13558
+ items: artItems
13559
+ });
13560
+ } else if (sprite?.url) {
13561
+ const spriteSize = view.size ?? 0.6;
13562
+ out.push({
13563
+ type: "draw-sprite",
13564
+ position: pos,
13565
+ asset: sprite,
13566
+ anchor: "center",
13567
+ width: spriteSize,
13568
+ height: spriteSize,
13569
+ opacity: fade
13570
+ });
13571
+ } else {
13572
+ out.push(...proceduralShapes(view, pos, fade, progress));
13573
+ }
13574
+ if (view.message) {
13575
+ const text = {
13576
+ type: "draw-text",
13577
+ text: view.message,
13578
+ position: pos,
13579
+ offsetY: -(0.15 + 0.55 * progress),
13580
+ color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
13581
+ opacity: fade
13582
+ };
13583
+ out.push(text);
13584
+ }
13585
+ return out;
13586
+ }
13587
+ function expandFxLayer(node, epochNowMs, dim) {
13588
+ if (!Array.isArray(node.items)) return [];
13589
+ const out = [];
13590
+ for (const item of node.items) {
13591
+ if (!item || typeof item.id !== "string") continue;
13592
+ out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim));
13593
+ }
13594
+ return out;
13595
+ }
13596
+ function DrawFxLayer(_props) {
13597
+ return null;
13598
+ }
13599
+ var DEFAULT_TICK_MS, DEFAULT_TEXT_COLOR, DEFAULT_SPARK_COLOR, easeOut2;
13600
+ var init_DrawFxLayer = __esm({
13601
+ "components/game/molecules/DrawFxLayer.tsx"() {
13602
+ "use client";
13603
+ init_fx();
13604
+ DEFAULT_TICK_MS = 500;
13605
+ DEFAULT_TEXT_COLOR = "#ffe066";
13606
+ DEFAULT_SPARK_COLOR = "#ffffff";
13607
+ easeOut2 = (t) => 1 - (1 - t) * (1 - t);
13608
+ }
13609
+ });
13399
13610
  function paintDrawable(painter, node, dctx) {
13400
13611
  switch (node.type) {
13401
13612
  case "draw-sprite":
@@ -13443,6 +13654,11 @@ function paintDrawable(painter, node, dctx) {
13443
13654
  case "draw-text-layer":
13444
13655
  paintTextLayer(painter, node, dctx);
13445
13656
  break;
13657
+ case "draw-fx-layer": {
13658
+ const epochNow = dctx.time > 0 && typeof performance !== "undefined" ? performance.timeOrigin + dctx.time : 0;
13659
+ for (const child of expandFxLayer(node, epochNow, "2d")) paintDrawable(painter, child, dctx);
13660
+ break;
13661
+ }
13446
13662
  }
13447
13663
  }
13448
13664
  var paint2dLog, warnedUnsupported2d, warnUnsupported2d;
@@ -13457,6 +13673,7 @@ var init_paintDispatch = __esm({
13457
13673
  init_DrawSpriteLayer();
13458
13674
  init_DrawShapeLayer();
13459
13675
  init_DrawTextLayer();
13676
+ init_DrawFxLayer();
13460
13677
  paint2dLog = logger.createLogger("almadar:ui:drawable-2d");
13461
13678
  warnedUnsupported2d = /* @__PURE__ */ new Set();
13462
13679
  warnUnsupported2d = (kind) => {
@@ -13705,6 +13922,7 @@ function Canvas2D({
13705
13922
  return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
13706
13923
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
13707
13924
  if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
13925
+ if (node.type === "draw-fx-layer") return Array.isArray(node.items) && node.items.length > 0;
13708
13926
  return false;
13709
13927
  };
13710
13928
  const animRafRef = React94.useRef(0);
@@ -17335,7 +17553,7 @@ var init_AlgoGraphCanvas = __esm({
17335
17553
  }
17336
17554
  const badgeGeoms = [];
17337
17555
  for (const g of nodeGeoms) {
17338
- if (!g.badge) continue;
17556
+ if (!g.badge || g.badge.text === "") continue;
17339
17557
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
17340
17558
  badgeGeoms.push({
17341
17559
  cx: g.x + g.radius * 0.75,
@@ -17401,7 +17619,7 @@ var init_AlgoGraphCanvas = __esm({
17401
17619
  };
17402
17620
  }
17403
17621
  });
17404
- 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, AlgorithmCanvas;
17622
+ 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, AlgorithmCanvas;
17405
17623
  var init_AlgorithmCanvas = __esm({
17406
17624
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
17407
17625
  "use client";
@@ -17428,9 +17646,18 @@ var init_AlgorithmCanvas = __esm({
17428
17646
  FRAME_ACTIVE_COLOR = "#3b82f6";
17429
17647
  FRAME_RETURNING_COLOR = "#f59e0b";
17430
17648
  FRAME_DONE_COLOR = "#94a3b8";
17649
+ FRAME_RIM_COLOR = {
17650
+ active: "#1d4ed8",
17651
+ returning: "#b45309",
17652
+ done: "#64748b"
17653
+ };
17431
17654
  FRAME_LABEL_COLOR = "#ffffff";
17432
17655
  FRAME_DETAIL_COLOR = "#e2e8f0";
17433
17656
  FRAME_TWO_LINE_MIN_H = 22;
17657
+ FRAME_STRIP_H = 44;
17658
+ FRAME_GAP = 6;
17659
+ FRAME_BOTTOM_PAD = 8;
17660
+ FRAME_MIN_H = 12;
17434
17661
  BUCKET_INDEX_FILL = "#e2e8f0";
17435
17662
  BUCKET_INDEX_STROKE = "#9ca3af";
17436
17663
  BUCKET_INDEX_TEXT = "#374151";
@@ -17927,14 +18154,18 @@ var init_AlgorithmCanvas = __esm({
17927
18154
  if (frames.length > 0) {
17928
18155
  const panelYFrames = panelY.frames;
17929
18156
  const n = frames.length;
17930
- const frameH = panelHeight / n;
18157
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
18158
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
18159
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
17931
18160
  const x = 8;
17932
18161
  const w = width - 16;
18162
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
17933
18163
  frames.forEach((f3, i) => {
17934
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
18164
+ const y = bottom - i * stride - frameH;
17935
18165
  const state = f3.state ?? "active";
17936
18166
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
17937
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
18167
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
18168
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
17938
18169
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
17939
18170
  out.push({
17940
18171
  type: "text",
@@ -17942,14 +18173,14 @@ var init_AlgorithmCanvas = __esm({
17942
18173
  y: y + frameH * 0.35,
17943
18174
  text: f3.label,
17944
18175
  color: FRAME_LABEL_COLOR,
17945
- fontSize: 10,
18176
+ fontSize: 11,
17946
18177
  align: "left"
17947
18178
  });
17948
18179
  if (f3.detail) {
17949
18180
  out.push({
17950
18181
  type: "text",
17951
18182
  x: 16,
17952
- y: y + frameH * 0.7,
18183
+ y: y + frameH * 0.72,
17953
18184
  text: f3.detail,
17954
18185
  color: FRAME_DETAIL_COLOR,
17955
18186
  fontSize: 10,
@@ -23635,8 +23866,6 @@ var init_ButtonGroup = __esm({
23635
23866
  ButtonGroup.displayName = "ButtonGroup";
23636
23867
  }
23637
23868
  });
23638
-
23639
- // lib/getNestedValue.ts
23640
23869
  function getNestedValue(obj, path) {
23641
23870
  if (obj === null || obj === void 0 || !path) {
23642
23871
  return void 0;
@@ -23657,6 +23886,15 @@ function getNestedValue(obj, path) {
23657
23886
  }
23658
23887
  return value;
23659
23888
  }
23889
+ function resolveImageUrl(value) {
23890
+ if (typeof value === "string") {
23891
+ return value;
23892
+ }
23893
+ if (core.isFileValue(value)) {
23894
+ return value.url;
23895
+ }
23896
+ return void 0;
23897
+ }
23660
23898
  var init_getNestedValue = __esm({
23661
23899
  "lib/getNestedValue.ts"() {
23662
23900
  }
@@ -24361,8 +24599,8 @@ var init_CardGrid = __esm({
24361
24599
  actionPayload: { row: itemData },
24362
24600
  children: [
24363
24601
  imageField && (() => {
24364
- const imgUrl = getNestedValue(itemData, imageField);
24365
- if (!imgUrl || typeof imgUrl !== "string") return null;
24602
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
24603
+ if (!imgUrl) return null;
24366
24604
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
24367
24605
  "img",
24368
24606
  {
@@ -28392,8 +28630,8 @@ function DataGrid({
28392
28630
  ),
28393
28631
  children: [
28394
28632
  imageField && (() => {
28395
- const imgUrl = getNestedValue(itemData, imageField);
28396
- if (!imgUrl || typeof imgUrl !== "string") return null;
28633
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
28634
+ if (!imgUrl) return null;
28397
28635
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
28398
28636
  "img",
28399
28637
  {
@@ -30447,6 +30685,196 @@ var init_PageTransition = __esm({
30447
30685
  PageTransition.displayName = "PageTransition";
30448
30686
  }
30449
30687
  });
30688
+ function ConfettiBurst({ item, lifeMs }) {
30689
+ const particles = createConfettiParticles(item.particleCount ?? 30, item.id);
30690
+ const left = (item.x ?? 0.5) * 100;
30691
+ const top = (item.z ?? item.y ?? 0.4) * 100;
30692
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: particles.map((p) => {
30693
+ const rad = p.angle * Math.PI / 180;
30694
+ const tx = Math.cos(rad) * p.distance * (item.size ?? 1);
30695
+ const ty = Math.sin(rad) * p.distance * (item.size ?? 1) - 20;
30696
+ return /* @__PURE__ */ jsxRuntime.jsx(
30697
+ Box,
30698
+ {
30699
+ className: "absolute rounded-sm",
30700
+ style: {
30701
+ left: (p.left - 50) * 2,
30702
+ top: -10,
30703
+ width: p.size,
30704
+ height: p.size,
30705
+ backgroundColor: item.color ?? p.color,
30706
+ animation: `confetti-burst ${Math.max(lifeMs - p.delay, 200)}ms ease-out ${p.delay}ms forwards`,
30707
+ opacity: 0,
30708
+ "--confetti-tx": `${tx}px`,
30709
+ "--confetti-ty": `${ty}px`,
30710
+ "--confetti-rotate": `${p.rotation}deg`
30711
+ }
30712
+ },
30713
+ p.id
30714
+ );
30715
+ }) });
30716
+ }
30717
+ function SparkleBurst({ item, lifeMs }) {
30718
+ const count = item.particleCount ?? 8;
30719
+ const scale = item.size ?? 1;
30720
+ const left = (item.x ?? 0.5) * 100;
30721
+ const top = (item.z ?? item.y ?? 0.4) * 100;
30722
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: Array.from({ length: count }, (_, i) => {
30723
+ const dx = (fxHash01(item.id, i * 4) - 0.5) * 160 * scale;
30724
+ const dy = (fxHash01(item.id, i * 4 + 1) - 0.5) * 120 * scale;
30725
+ const dot = (4 + fxHash01(item.id, i * 4 + 2) * 5) * scale;
30726
+ const delay = fxHash01(item.id, i * 4 + 3) * lifeMs * 0.4;
30727
+ return /* @__PURE__ */ jsxRuntime.jsx(
30728
+ Box,
30729
+ {
30730
+ className: "absolute rounded-full",
30731
+ style: {
30732
+ left: dx,
30733
+ top: dy,
30734
+ width: dot,
30735
+ height: dot,
30736
+ backgroundColor: item.color ?? "gold",
30737
+ opacity: 0,
30738
+ animation: `fx-overlay-sparkle ${Math.max(lifeMs - delay, 200)}ms ease-in-out ${delay}ms forwards`
30739
+ }
30740
+ },
30741
+ i
30742
+ );
30743
+ }) });
30744
+ }
30745
+ function OverlayFxNode({ item, tickMs }) {
30746
+ const lifeMs = lifeMsOf(item, tickMs);
30747
+ switch (item.effect ?? "sparkle") {
30748
+ case "confetti":
30749
+ return /* @__PURE__ */ jsxRuntime.jsx(ConfettiBurst, { item, lifeMs });
30750
+ case "flash":
30751
+ return /* @__PURE__ */ jsxRuntime.jsx(
30752
+ Box,
30753
+ {
30754
+ position: "absolute",
30755
+ className: "inset-0",
30756
+ style: {
30757
+ backgroundColor: item.color ?? "#ffffff",
30758
+ opacity: 0,
30759
+ animation: `fx-overlay-flash ${lifeMs}ms ease-out forwards`
30760
+ }
30761
+ }
30762
+ );
30763
+ case "streak-glow":
30764
+ return /* @__PURE__ */ jsxRuntime.jsx(
30765
+ Box,
30766
+ {
30767
+ position: "absolute",
30768
+ className: "inset-0",
30769
+ style: {
30770
+ boxShadow: `inset 0 0 ${60 * (item.size ?? 1)}px ${item.color ?? "var(--color-warning)"}`,
30771
+ opacity: 0,
30772
+ animation: `fx-overlay-glow ${lifeMs}ms ease-in-out forwards`
30773
+ }
30774
+ }
30775
+ );
30776
+ case "float-text": {
30777
+ if (!item.message) return null;
30778
+ return /* @__PURE__ */ jsxRuntime.jsx(
30779
+ Box,
30780
+ {
30781
+ position: "absolute",
30782
+ style: {
30783
+ left: `${(item.x ?? 0.5) * 100}%`,
30784
+ top: `${(item.z ?? item.y ?? 0.4) * 100}%`,
30785
+ color: item.color ?? "var(--color-success)",
30786
+ fontWeight: 700,
30787
+ fontSize: 16 * (item.size ?? 1),
30788
+ textShadow: "0 1px 2px rgba(0,0,0,0.4)",
30789
+ opacity: 0,
30790
+ animation: `fx-overlay-float ${lifeMs}ms ease-out forwards`,
30791
+ whiteSpace: "nowrap"
30792
+ },
30793
+ children: item.message
30794
+ }
30795
+ );
30796
+ }
30797
+ case "shake":
30798
+ return null;
30799
+ default:
30800
+ return /* @__PURE__ */ jsxRuntime.jsx(SparkleBurst, { item, lifeMs });
30801
+ }
30802
+ }
30803
+ var DEFAULT_TICK_MS2, FX_OVERLAY_KEYFRAMES, lifeMsOf, FxOverlay;
30804
+ var init_FxOverlay = __esm({
30805
+ "components/core/molecules/FxOverlay.tsx"() {
30806
+ "use client";
30807
+ init_cn();
30808
+ init_Box();
30809
+ init_fx();
30810
+ DEFAULT_TICK_MS2 = 500;
30811
+ FX_OVERLAY_KEYFRAMES = `
30812
+ ${CONFETTI_BURST_KEYFRAMES}
30813
+ @keyframes fx-overlay-flash {
30814
+ 0% { opacity: 0.75; }
30815
+ 100% { opacity: 0; }
30816
+ }
30817
+ @keyframes fx-overlay-glow {
30818
+ 0% { opacity: 0.9; }
30819
+ 60% { opacity: 0.6; }
30820
+ 100% { opacity: 0; }
30821
+ }
30822
+ @keyframes fx-overlay-sparkle {
30823
+ 0% { opacity: 0; transform: scale(0); }
30824
+ 30% { opacity: 1; transform: scale(1); }
30825
+ 100% { opacity: 0; transform: scale(0.3); }
30826
+ }
30827
+ @keyframes fx-overlay-float {
30828
+ 0% { opacity: 0; transform: translate(-50%, 8px); }
30829
+ 15% { opacity: 1; }
30830
+ 100% { opacity: 0; transform: translate(-50%, -40px); }
30831
+ }
30832
+ @keyframes fx-overlay-shake {
30833
+ 0% { transform: translateX(0); }
30834
+ 15% { transform: translateX(-6px); }
30835
+ 30% { transform: translateX(5px); }
30836
+ 45% { transform: translateX(-4px); }
30837
+ 60% { transform: translateX(3px); }
30838
+ 75% { transform: translateX(-2px); }
30839
+ 100% { transform: translateX(0); }
30840
+ }
30841
+ `;
30842
+ lifeMsOf = (it, tickMs) => Math.max(it.maxTtl ?? it.ttl, it.ttl, 1) * tickMs;
30843
+ FxOverlay = ({ items, tickMs = DEFAULT_TICK_MS2, children, className }) => {
30844
+ const screenItems = (Array.isArray(items) ? items : []).filter(
30845
+ (it) => Boolean(it) && typeof it.id === "string" && it.space !== "world"
30846
+ );
30847
+ const shakeItem = [...screenItems].reverse().find((it) => it.effect === "shake");
30848
+ const overlay = /* @__PURE__ */ jsxRuntime.jsxs(
30849
+ Box,
30850
+ {
30851
+ position: "absolute",
30852
+ className: cn("inset-0 pointer-events-none overflow-hidden z-50", !children && className),
30853
+ "aria-hidden": "true",
30854
+ children: [
30855
+ screenItems.map((it) => /* @__PURE__ */ jsxRuntime.jsx(OverlayFxNode, { item: it, tickMs }, it.id)),
30856
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: FX_OVERLAY_KEYFRAMES })
30857
+ ]
30858
+ }
30859
+ );
30860
+ if (children !== void 0 && children !== null) {
30861
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { position: "relative", className, children: [
30862
+ /* @__PURE__ */ jsxRuntime.jsx(
30863
+ Box,
30864
+ {
30865
+ style: shakeItem ? { animation: `fx-overlay-shake ${Math.min(lifeMsOf(shakeItem, tickMs), 600)}ms ease-in-out` } : void 0,
30866
+ children
30867
+ },
30868
+ shakeItem?.id ?? "steady"
30869
+ ),
30870
+ overlay
30871
+ ] });
30872
+ }
30873
+ return overlay;
30874
+ };
30875
+ FxOverlay.displayName = "FxOverlay";
30876
+ }
30877
+ });
30450
30878
  function computePopoverStyle(position, triggerRect, popoverWidth) {
30451
30879
  if (position === "left" || position === "right") {
30452
30880
  return {
@@ -31003,7 +31431,7 @@ var init_SearchInput = __esm({
31003
31431
  /* @__PURE__ */ jsxRuntime.jsx(
31004
31432
  Input,
31005
31433
  {
31006
- type: "text",
31434
+ type: "search",
31007
31435
  value: searchValue,
31008
31436
  onChange: handleChange,
31009
31437
  placeholder: resolvedPlaceholder,
@@ -32329,6 +32757,7 @@ var MathCanvas;
32329
32757
  var init_MathCanvas = __esm({
32330
32758
  "components/learning/molecules/MathCanvas.tsx"() {
32331
32759
  "use client";
32760
+ init_useEventBus();
32332
32761
  init_atoms();
32333
32762
  init_Stack();
32334
32763
  init_LearningCanvas();
@@ -32360,9 +32789,32 @@ var init_MathCanvas = __esm({
32360
32789
  interactive = false,
32361
32790
  animate = false,
32362
32791
  onShapeClick,
32792
+ keyMap,
32793
+ keyUpMap,
32363
32794
  isLoading,
32364
32795
  error
32365
32796
  }) => {
32797
+ const eventBus = useEventBus();
32798
+ React94.useEffect(() => {
32799
+ if (!keyMap && !keyUpMap) return;
32800
+ const onDown = (e) => {
32801
+ const ev = keyMap?.[e.code];
32802
+ if (ev) {
32803
+ eventBus.emit(`UI:${ev}`, {});
32804
+ e.preventDefault();
32805
+ }
32806
+ };
32807
+ const onUp = (e) => {
32808
+ const ev = keyUpMap?.[e.code];
32809
+ if (ev) eventBus.emit(`UI:${ev}`, {});
32810
+ };
32811
+ window.addEventListener("keydown", onDown);
32812
+ window.addEventListener("keyup", onUp);
32813
+ return () => {
32814
+ window.removeEventListener("keydown", onDown);
32815
+ window.removeEventListener("keyup", onUp);
32816
+ };
32817
+ }, [keyMap, keyUpMap, eventBus]);
32366
32818
  const derivedShapes = React94.useMemo(() => {
32367
32819
  const out = [];
32368
32820
  const margin = 24;
@@ -32375,11 +32827,11 @@ var init_MathCanvas = __esm({
32375
32827
  if (showGrid) {
32376
32828
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
32377
32829
  const px = mapX(x);
32378
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
32830
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32379
32831
  }
32380
32832
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
32381
32833
  const py = mapY(y);
32382
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
32834
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32383
32835
  }
32384
32836
  }
32385
32837
  if (showTickLabels) {
@@ -32428,7 +32880,7 @@ var init_MathCanvas = __esm({
32428
32880
  x: mapX((first.x + last.x) / 2),
32429
32881
  y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
32430
32882
  text: region.label,
32431
- color: "#111827",
32883
+ color,
32432
32884
  fontSize: 11
32433
32885
  });
32434
32886
  }
@@ -32461,14 +32913,14 @@ var init_MathCanvas = __esm({
32461
32913
  const px = mapX(guide.at);
32462
32914
  out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
32463
32915
  if (guide.label) {
32464
- out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color: "#111827", fontSize: 11 });
32916
+ out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
32465
32917
  }
32466
32918
  } else {
32467
32919
  if (guide.at < yMin || guide.at > yMax) continue;
32468
32920
  const py = mapY(guide.at);
32469
32921
  out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
32470
32922
  if (guide.label) {
32471
- out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color: "#111827", fontSize: 11, align: "right" });
32923
+ out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: 11, align: "right" });
32472
32924
  }
32473
32925
  }
32474
32926
  }
@@ -32534,7 +32986,7 @@ var init_MathCanvas = __esm({
32534
32986
  x: (x1 + x2) / 2,
32535
32987
  y: xAxisY - peak - 8,
32536
32988
  text: hop.label,
32537
- color: "#111827",
32989
+ color,
32538
32990
  fontSize: 10,
32539
32991
  align: "center"
32540
32992
  });
@@ -32561,7 +33013,7 @@ var init_MathCanvas = __esm({
32561
33013
  x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
32562
33014
  y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
32563
33015
  text: angle.label,
32564
- color: "#111827",
33016
+ color,
32565
33017
  fontSize: 11,
32566
33018
  align: "center"
32567
33019
  });
@@ -32579,7 +33031,7 @@ var init_MathCanvas = __esm({
32579
33031
  fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
32580
33032
  });
32581
33033
  if (p.label) {
32582
- out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
33034
+ out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: 12 });
32583
33035
  }
32584
33036
  }
32585
33037
  for (const v of vectors) {
@@ -32590,7 +33042,7 @@ var init_MathCanvas = __esm({
32590
33042
  const y2 = mapY(v.y + v.vy);
32591
33043
  out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
32592
33044
  if (v.label) {
32593
- out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
33045
+ out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: 12 });
32594
33046
  }
32595
33047
  }
32596
33048
  out.push(...shapes);
@@ -33854,13 +34306,13 @@ var init_MapView = __esm({
33854
34306
  shadowSize: [41, 41]
33855
34307
  });
33856
34308
  L.Marker.prototype.options.icon = defaultIcon;
33857
- const { useEffect: useEffect64, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__namespace.default;
34309
+ const { useEffect: useEffect65, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__namespace.default;
33858
34310
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33859
34311
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33860
34312
  function MapUpdater({ centerLat, centerLng, zoom }) {
33861
34313
  const map = useMap();
33862
34314
  const prevRef = useRef65({ centerLat, centerLng, zoom });
33863
- useEffect64(() => {
34315
+ useEffect65(() => {
33864
34316
  const prev = prevRef.current;
33865
34317
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
33866
34318
  map.setView([centerLat, centerLng], zoom);
@@ -33871,7 +34323,7 @@ var init_MapView = __esm({
33871
34323
  }
33872
34324
  function MapClickHandler({ onMapClick }) {
33873
34325
  const map = useMap();
33874
- useEffect64(() => {
34326
+ useEffect65(() => {
33875
34327
  if (!onMapClick) return;
33876
34328
  const handler = (e) => {
33877
34329
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -40278,6 +40730,7 @@ var init_PageHeader = __esm({
40278
40730
  className
40279
40731
  }) => {
40280
40732
  const eventBus = useEventBus();
40733
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
40281
40734
  const handleBack = () => {
40282
40735
  eventBus.emit(`UI:${backEvent}`);
40283
40736
  };
@@ -40325,15 +40778,15 @@ var init_PageHeader = __esm({
40325
40778
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
40326
40779
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3", children: [
40327
40780
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
40328
- status && /* @__PURE__ */ jsxRuntime.jsx(
40781
+ statusBadge && /* @__PURE__ */ jsxRuntime.jsx(
40329
40782
  Typography,
40330
40783
  {
40331
40784
  variant: "small",
40332
40785
  className: cn(
40333
40786
  "px-2.5 py-1 rounded-full text-xs font-medium",
40334
- statusColors2[status.variant || "default"]
40787
+ statusColors2[statusBadge.variant || "default"]
40335
40788
  ),
40336
- children: status.label
40789
+ children: statusBadge.label
40337
40790
  }
40338
40791
  )
40339
40792
  ] }),
@@ -43562,7 +44015,7 @@ function formatFieldValue2(value, fieldName) {
43562
44015
  }
43563
44016
  return String(value);
43564
44017
  }
43565
- function renderRichFieldValue(value, fieldName, fieldType) {
44018
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
43566
44019
  if (value === void 0 || value === null) return "\u2014";
43567
44020
  const str = String(value);
43568
44021
  switch (fieldType) {
@@ -43629,19 +44082,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
43629
44082
  }
43630
44083
  );
43631
44084
  case "date":
43632
- case "datetime": {
44085
+ case "datetime":
44086
+ case "timestamp": {
43633
44087
  const d = new Date(str);
43634
44088
  if (!isNaN(d.getTime())) {
43635
44089
  return d.toLocaleDateString(void 0, {
43636
44090
  year: "numeric",
43637
44091
  month: "long",
43638
44092
  day: "numeric",
43639
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
44093
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
43640
44094
  });
43641
44095
  }
43642
44096
  return str;
43643
44097
  }
44098
+ case "money": {
44099
+ const n = typeof value === "number" ? value : Number(str);
44100
+ if (!isNaN(n)) {
44101
+ return new Intl.NumberFormat(void 0, {
44102
+ style: "currency",
44103
+ currency: "USD"
44104
+ }).format(n);
44105
+ }
44106
+ return str;
44107
+ }
44108
+ case "relation": {
44109
+ const match = meta?.options?.find((opt) => opt.value === str);
44110
+ return match ? match.label : str;
44111
+ }
44112
+ case "file": {
44113
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
44114
+ const file = value;
44115
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
44116
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
44117
+ return /* @__PURE__ */ jsxRuntime.jsxs(
44118
+ "a",
44119
+ {
44120
+ href: typeof file.url === "string" ? file.url : void 0,
44121
+ download: label,
44122
+ 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",
44123
+ children: [
44124
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.FileText, size: "sm", className: "text-muted-foreground" }),
44125
+ label,
44126
+ size && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
44127
+ ]
44128
+ }
44129
+ );
44130
+ }
44131
+ return str;
44132
+ }
43644
44133
  default:
44134
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
44135
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
44136
+ }
43645
44137
  return formatFieldValue2(value, fieldName);
43646
44138
  }
43647
44139
  }
@@ -43668,8 +44160,18 @@ function buildFieldTypeMap(fields) {
43668
44160
  const map = {};
43669
44161
  if (!fields) return map;
43670
44162
  for (const f3 of fields) {
43671
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
43672
- map[f3.name] = f3.type;
44163
+ if (typeof f3 === "object" && f3.type !== void 0) {
44164
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
44165
+ }
44166
+ }
44167
+ return map;
44168
+ }
44169
+ function buildFieldMetaMap(fields) {
44170
+ const map = {};
44171
+ if (!fields) return map;
44172
+ for (const f3 of fields) {
44173
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
44174
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
43673
44175
  }
43674
44176
  }
43675
44177
  return map;
@@ -43689,6 +44191,7 @@ var init_DetailPanel = __esm({
43689
44191
  init_format();
43690
44192
  init_getNestedValue();
43691
44193
  init_useEventBus();
44194
+ init_UploadDropZone();
43692
44195
  formatFieldLabel = humanizeFieldName;
43693
44196
  ReactMarkdown2 = React94.lazy(() => import('react-markdown'));
43694
44197
  DetailPanel = ({
@@ -43698,6 +44201,7 @@ var init_DetailPanel = __esm({
43698
44201
  avatar,
43699
44202
  sections: propSections,
43700
44203
  actions,
44204
+ backAction,
43701
44205
  footer,
43702
44206
  slideOver = false,
43703
44207
  className,
@@ -43706,7 +44210,8 @@ var init_DetailPanel = __esm({
43706
44210
  fieldNames,
43707
44211
  initialData,
43708
44212
  isLoading = false,
43709
- error
44213
+ error,
44214
+ relationsData
43710
44215
  }) => {
43711
44216
  const eventBus = useEventBus();
43712
44217
  const { t } = hooks.useTranslate();
@@ -43717,8 +44222,15 @@ var init_DetailPanel = __esm({
43717
44222
  };
43718
44223
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
43719
44224
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
44225
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
43720
44226
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
43721
44227
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
44228
+ const metaFor = (field) => {
44229
+ const base = fieldMetaMap[field];
44230
+ const options = relationsData?.[field];
44231
+ if (!base && !options) return void 0;
44232
+ return { ...base, options };
44233
+ };
43722
44234
  const handleActionClick = React94.useCallback(
43723
44235
  (action, data2) => {
43724
44236
  if (action.navigatesTo) {
@@ -43797,7 +44309,7 @@ var init_DetailPanel = __esm({
43797
44309
  if (value !== void 0 && value !== null) {
43798
44310
  overviewFields.push({
43799
44311
  label: labelFor(field),
43800
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44312
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43801
44313
  icon: getFieldIcon(field)
43802
44314
  });
43803
44315
  }
@@ -43813,7 +44325,7 @@ var init_DetailPanel = __esm({
43813
44325
  if (value !== void 0 && value !== null) {
43814
44326
  metricsFields.push({
43815
44327
  label: labelFor(field),
43816
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44328
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43817
44329
  icon: getFieldIcon(field)
43818
44330
  });
43819
44331
  }
@@ -43829,7 +44341,7 @@ var init_DetailPanel = __esm({
43829
44341
  if (value !== void 0 && value !== null) {
43830
44342
  timelineFields.push({
43831
44343
  label: labelFor(field),
43832
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44344
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43833
44345
  icon: getFieldIcon(field)
43834
44346
  });
43835
44347
  }
@@ -43845,7 +44357,7 @@ var init_DetailPanel = __esm({
43845
44357
  if (value !== void 0 && value !== null) {
43846
44358
  descFields.push({
43847
44359
  label: labelFor(field),
43848
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44360
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43849
44361
  icon: getFieldIcon(field)
43850
44362
  });
43851
44363
  }
@@ -43893,7 +44405,7 @@ var init_DetailPanel = __esm({
43893
44405
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
43894
44406
  allFields.push({
43895
44407
  label: labelFor(field),
43896
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44408
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43897
44409
  icon: getFieldIcon(field)
43898
44410
  });
43899
44411
  } else {
@@ -43908,34 +44420,49 @@ var init_DetailPanel = __esm({
43908
44420
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
43909
44421
  const effectiveCloseAction = closeAction ?? { event: void 0};
43910
44422
  const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
43911
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
43912
- otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
43913
- Button,
43914
- {
43915
- variant: action.variant || "secondary",
43916
- size: "sm",
43917
- action: action.navigatesTo ? void 0 : action.event,
43918
- actionPayload: { row: normalizedData },
43919
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43920
- icon: action.icon,
43921
- "data-testid": action.event ? `action-${action.event}` : void 0,
43922
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43923
- children: action.label
43924
- },
43925
- idx
43926
- )),
43927
- /* @__PURE__ */ jsxRuntime.jsx(
44423
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
44424
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
43928
44425
  Button,
43929
44426
  {
43930
- variant: "ghost",
44427
+ variant: backAction.variant || "ghost",
43931
44428
  size: "sm",
43932
- action: effectiveCloseAction.event,
44429
+ action: backAction.navigatesTo ? void 0 : backAction.event,
43933
44430
  actionPayload: { row: normalizedData },
43934
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
43935
- icon: LucideIcons2.X,
43936
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
44431
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
44432
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
44433
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
44434
+ children: backAction.label
43937
44435
  }
43938
- )
44436
+ ) }),
44437
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
44438
+ otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
44439
+ Button,
44440
+ {
44441
+ variant: action.variant || "secondary",
44442
+ size: "sm",
44443
+ action: action.navigatesTo ? void 0 : action.event,
44444
+ actionPayload: { row: normalizedData },
44445
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
44446
+ icon: action.icon,
44447
+ "data-testid": action.event ? `action-${action.event}` : void 0,
44448
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
44449
+ children: action.label
44450
+ },
44451
+ idx
44452
+ )),
44453
+ /* @__PURE__ */ jsxRuntime.jsx(
44454
+ Button,
44455
+ {
44456
+ variant: "ghost",
44457
+ size: "sm",
44458
+ action: effectiveCloseAction.event,
44459
+ actionPayload: { row: normalizedData },
44460
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
44461
+ icon: LucideIcons2.X,
44462
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
44463
+ }
44464
+ )
44465
+ ] })
43939
44466
  ] }),
43940
44467
  avatar,
43941
44468
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -44250,6 +44777,10 @@ function determineInputType(field) {
44250
44777
  return "password";
44251
44778
  case "url":
44252
44779
  return "url";
44780
+ case "file":
44781
+ return "file";
44782
+ case "money":
44783
+ return "currency";
44253
44784
  case "number":
44254
44785
  case "integer":
44255
44786
  case "float":
@@ -44311,6 +44842,7 @@ var init_Form = __esm({
44311
44842
  init_Typography();
44312
44843
  init_Icon();
44313
44844
  init_RelationSelect();
44845
+ init_UploadDropZone();
44314
44846
  init_Alert();
44315
44847
  init_useEventBus();
44316
44848
  init_debug();
@@ -44353,6 +44885,7 @@ var init_Form = __esm({
44353
44885
  cancelEvent = "CANCEL",
44354
44886
  relationsData = {},
44355
44887
  relationsLoading = {},
44888
+ fieldOverrides,
44356
44889
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
44357
44890
  conditionalFields: conditionalFieldsRaw = {},
44358
44891
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -44599,7 +45132,8 @@ var init_Form = __esm({
44599
45132
  label,
44600
45133
  field.required && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
44601
45134
  ] }),
44602
- renderFieldInput(field, fieldName, inputType, currentValue, label)
45135
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
45136
+ field.hint && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
44603
45137
  ] }, fieldName);
44604
45138
  },
44605
45139
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -44615,6 +45149,7 @@ var init_Form = __esm({
44615
45149
  name: field,
44616
45150
  type: entityField.type,
44617
45151
  required: entityField.required,
45152
+ hint: entityField.description,
44618
45153
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
44619
45154
  defaultValue: entityField.default,
44620
45155
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -44627,8 +45162,18 @@ var init_Form = __esm({
44627
45162
  return { name: field, type: "string" };
44628
45163
  }
44629
45164
  return field;
45165
+ }).map((field) => {
45166
+ const fieldName = field.name || field.field;
45167
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
45168
+ if (!override) return field;
45169
+ return {
45170
+ ...field,
45171
+ ...override.label !== void 0 ? { label: override.label } : {},
45172
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
45173
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
45174
+ };
44630
45175
  });
44631
- }, [effectiveFields, resolvedEntity]);
45176
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
44632
45177
  const schemaFields = React94__namespace.default.useMemo(() => {
44633
45178
  if (normalizedFields.length === 0) return null;
44634
45179
  if (isDebugEnabled()) {
@@ -44752,6 +45297,44 @@ var init_Form = __esm({
44752
45297
  max: field.max
44753
45298
  }
44754
45299
  );
45300
+ case "currency":
45301
+ return /* @__PURE__ */ jsxRuntime.jsx(
45302
+ Input,
45303
+ {
45304
+ ...commonProps,
45305
+ type: "number",
45306
+ step: "0.01",
45307
+ icon: LucideIcons2.DollarSign,
45308
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
45309
+ onChange: (e) => handleChange(
45310
+ fieldName,
45311
+ e.target.value ? Number(e.target.value) : void 0
45312
+ ),
45313
+ min: field.min,
45314
+ max: field.max
45315
+ }
45316
+ );
45317
+ case "file":
45318
+ return /* @__PURE__ */ jsxRuntime.jsx(
45319
+ UploadDropZone,
45320
+ {
45321
+ accept: field.pattern,
45322
+ maxFiles: 1,
45323
+ disabled: isLoading,
45324
+ onFiles: (files) => {
45325
+ const f3 = files[0];
45326
+ if (!f3) return;
45327
+ const reader = new FileReader();
45328
+ reader.onload = () => handleChange(fieldName, {
45329
+ name: f3.name,
45330
+ mimeType: f3.type,
45331
+ sizeBytes: f3.size,
45332
+ url: typeof reader.result === "string" ? reader.result : ""
45333
+ });
45334
+ reader.readAsDataURL(f3);
45335
+ }
45336
+ }
45337
+ );
44755
45338
  case "date":
44756
45339
  return /* @__PURE__ */ jsxRuntime.jsx(
44757
45340
  Input,
@@ -45784,6 +46367,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
45784
46367
  var init_MediaGallery = __esm({
45785
46368
  "components/core/organisms/MediaGallery.tsx"() {
45786
46369
  "use client";
46370
+ init_getNestedValue();
45787
46371
  init_cn();
45788
46372
  init_atoms();
45789
46373
  init_Stack();
@@ -45852,9 +46436,9 @@ var init_MediaGallery = __esm({
45852
46436
  return entityData.map((record, idx) => {
45853
46437
  return {
45854
46438
  id: String(record.id ?? idx),
45855
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
46439
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
45856
46440
  alt: record.alt ? String(record.alt) : void 0,
45857
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
46441
+ thumbnail: resolveImageUrl(record.thumbnail),
45858
46442
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
45859
46443
  };
45860
46444
  });
@@ -49741,6 +50325,7 @@ var init_component_registry_generated = __esm({
49741
50325
  init_DocSidebar();
49742
50326
  init_DocTOC();
49743
50327
  init_DocumentViewer();
50328
+ init_DrawFxLayer();
49744
50329
  init_DrawGroup();
49745
50330
  init_DrawMesh();
49746
50331
  init_DrawShape();
@@ -49771,6 +50356,7 @@ var init_component_registry_generated = __esm({
49771
50356
  init_FormField();
49772
50357
  init_FormSection();
49773
50358
  init_FormSectionHeader();
50359
+ init_FxOverlay();
49774
50360
  init_GameAudioToggle();
49775
50361
  init_GameHud();
49776
50362
  init_GameIcon();
@@ -50011,6 +50597,7 @@ var init_component_registry_generated = __esm({
50011
50597
  "DocSidebar": DocSidebar,
50012
50598
  "DocTOC": DocTOC,
50013
50599
  "DocumentViewer": DocumentViewer,
50600
+ "DrawFxLayer": DrawFxLayer,
50014
50601
  "DrawGroup": DrawGroup,
50015
50602
  "DrawMesh": DrawMesh,
50016
50603
  "DrawShape": DrawShape,
@@ -50041,6 +50628,7 @@ var init_component_registry_generated = __esm({
50041
50628
  "FormField": FormField,
50042
50629
  "FormLayout": FormLayout,
50043
50630
  "FormSectionHeader": FormSectionHeader,
50631
+ "FxOverlay": FxOverlay,
50044
50632
  "GameAudioToggle": GameAudioToggle,
50045
50633
  "GameHud": GameHud,
50046
50634
  "GameIcon": GameIcon,
@@ -50216,7 +50804,8 @@ __export(UISlotRenderer_exports, {
50216
50804
  SlotContentRenderer: () => SlotContentRenderer,
50217
50805
  SuspenseConfigProvider: () => SuspenseConfigProvider,
50218
50806
  UISlotComponent: () => UISlotComponent,
50219
- UISlotRenderer: () => UISlotRenderer
50807
+ UISlotRenderer: () => UISlotRenderer,
50808
+ renderPatternValue: () => renderPatternValue
50220
50809
  });
50221
50810
  function SuspenseConfigProvider({
50222
50811
  config,
@@ -50252,6 +50841,9 @@ function enrichFormFields(fields, entityDef) {
50252
50841
  type: entityField.type,
50253
50842
  required: entityField.required ?? false
50254
50843
  };
50844
+ if (entityField.description) {
50845
+ enriched.hint = entityField.description;
50846
+ }
50255
50847
  if (entityField.values && entityField.values.length > 0) {
50256
50848
  enriched.values = entityField.values;
50257
50849
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -50275,6 +50867,9 @@ function enrichFormFields(fields, entityDef) {
50275
50867
  if (entityField.required && !("required" in obj)) {
50276
50868
  enriched.required = true;
50277
50869
  }
50870
+ if (entityField.description && !obj.hint && !obj.help) {
50871
+ enriched.hint = entityField.description;
50872
+ }
50278
50873
  if (!obj.values && !obj.options) {
50279
50874
  if (entityField.values && entityField.values.length > 0) {
50280
50875
  enriched.values = entityField.values;
@@ -50290,6 +50885,32 @@ function enrichFormFields(fields, entityDef) {
50290
50885
  return field;
50291
50886
  });
50292
50887
  }
50888
+ function enrichDetailFields(fields, entityDef) {
50889
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
50890
+ const metaFor = (name) => {
50891
+ const entityField = fieldMap.get(name);
50892
+ if (!entityField) return void 0;
50893
+ const meta = { type: entityField.type };
50894
+ const values = entityField.values ?? entityField.enumValues;
50895
+ if (values && values.length > 0) meta.values = values;
50896
+ if (entityField.relation) meta.relation = entityField.relation.entity;
50897
+ return meta;
50898
+ };
50899
+ return fields.map((field) => {
50900
+ if (typeof field === "string") {
50901
+ const meta = metaFor(field);
50902
+ return meta ? { key: field, ...meta } : field;
50903
+ }
50904
+ if (field && typeof field === "object" && !Array.isArray(field) && !React94__namespace.default.isValidElement(field) && !(field instanceof Date)) {
50905
+ const obj = field;
50906
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
50907
+ if (!fieldName || obj.type) return field;
50908
+ const meta = metaFor(fieldName);
50909
+ return meta ? { ...obj, ...meta } : field;
50910
+ }
50911
+ return field;
50912
+ });
50913
+ }
50293
50914
  function renderContainedPortal(t, slot, content, onDismiss) {
50294
50915
  const slotContent = /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content, onDismiss }) });
50295
50916
  const slotId = `slot-${slot}`;
@@ -50799,6 +51420,24 @@ function isPatternConfig(value) {
50799
51420
  const record = value;
50800
51421
  return "type" in record && typeof record.type === "string" && patterns.getComponentForPattern(record.type) !== null;
50801
51422
  }
51423
+ function renderPatternValue(value) {
51424
+ if (value === null || value === void 0) return null;
51425
+ if (typeof value === "boolean" || typeof value === "function") return null;
51426
+ if (typeof value === "number") return value;
51427
+ if (typeof value === "string") return renderPatternChildren(value, () => {
51428
+ });
51429
+ if (value instanceof Date) return value.toLocaleString();
51430
+ if (React94__namespace.default.isValidElement(value)) return value;
51431
+ if (Array.isArray(value)) {
51432
+ return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React94__namespace.default.Fragment, { children: renderPatternValue(item) }, index));
51433
+ }
51434
+ if (isPatternConfig(value)) {
51435
+ const { type, ...props } = value;
51436
+ return renderPatternChildren({ type, props }, () => {
51437
+ });
51438
+ }
51439
+ return null;
51440
+ }
50802
51441
  function isPlainConfigObject(value) {
50803
51442
  if (React94__namespace.default.isValidElement(value)) return false;
50804
51443
  if (value instanceof Date) return false;
@@ -51000,9 +51639,9 @@ function SlotContentRenderer({
51000
51639
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
51001
51640
  }
51002
51641
  }
51003
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
51004
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
51005
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
51642
+ const fieldsContract = patterns.getPatternFieldsContract(content.pattern) ?? (patterns.getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
51643
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
51644
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
51006
51645
  }
51007
51646
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
51008
51647
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -51112,7 +51751,7 @@ function UISlotRenderer({
51112
51751
  }
51113
51752
  return wrapped;
51114
51753
  }
51115
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51754
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51116
51755
  var init_UISlotRenderer = __esm({
51117
51756
  "components/core/organisms/UISlotRenderer.tsx"() {
51118
51757
  "use client";
@@ -51143,12 +51782,6 @@ var init_UISlotRenderer = __esm({
51143
51782
  modal: "form",
51144
51783
  drawer: "form"
51145
51784
  };
51146
- FORM_PATTERNS = /* @__PURE__ */ new Set([
51147
- "form",
51148
- "form-section",
51149
- "inline-edit-form",
51150
- "wizard-step"
51151
- ]);
51152
51785
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
51153
51786
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
51154
51787
  "logo",