@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
@@ -5803,27 +5803,61 @@ var init_InfiniteScrollSentinel = __esm({
5803
5803
  InfiniteScrollSentinel.displayName = "InfiniteScrollSentinel";
5804
5804
  }
5805
5805
  });
5806
- function createParticles(count) {
5807
- return Array.from({ length: count }, () => {
5808
- particleIdCounter += 1;
5809
- return {
5810
- id: particleIdCounter,
5811
- color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)],
5812
- left: 30 + Math.random() * 40,
5813
- delay: Math.random() * 300,
5814
- angle: Math.random() * 360,
5815
- distance: 40 + Math.random() * 80,
5816
- rotation: Math.random() * 720 - 360,
5817
- size: 4 + Math.random() * 6
5818
- };
5819
- });
5806
+
5807
+ // components/core/atoms/fx.ts
5808
+ function resolveFxView(item, presets) {
5809
+ const row = presets?.find((p) => p.type === item.type);
5810
+ if (!row) return item;
5811
+ return {
5812
+ ...item,
5813
+ space: item.space ?? row.space,
5814
+ effect: item.effect ?? row.effect,
5815
+ color: item.color ?? row.color,
5816
+ size: item.size ?? row.size,
5817
+ vx: item.vx ?? row.vx,
5818
+ vy: item.vy ?? row.vy,
5819
+ vz: item.vz ?? row.vz,
5820
+ particleCount: item.particleCount ?? row.particleCount,
5821
+ shape: row.shape,
5822
+ count: row.count,
5823
+ glow: row.glow,
5824
+ gravity: row.gravity,
5825
+ color2: row.color2
5826
+ };
5820
5827
  }
5821
- var CONFETTI_COLORS, particleIdCounter, ConfettiEffect;
5822
- var init_ConfettiEffect = __esm({
5823
- "components/core/atoms/ConfettiEffect.tsx"() {
5824
- "use client";
5825
- init_cn();
5826
- init_Box();
5828
+ function fxLifecycle(item, epochNowMs, tickMs) {
5829
+ const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
5830
+ const lifeMs = maxTtl * tickMs;
5831
+ const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
5832
+ const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
5833
+ return { lifeMs, ageMs, progress, fade: 1 - progress };
5834
+ }
5835
+ function fxHash01(seed, salt) {
5836
+ let h = 2166136261 ^ salt;
5837
+ for (let i = 0; i < seed.length; i++) {
5838
+ h ^= seed.charCodeAt(i);
5839
+ h = Math.imul(h, 16777619);
5840
+ }
5841
+ h ^= h >>> 13;
5842
+ h = Math.imul(h, 1274126177);
5843
+ h ^= h >>> 16;
5844
+ return (h >>> 0) / 4294967296;
5845
+ }
5846
+ function createConfettiParticles(count, seed) {
5847
+ return Array.from({ length: count }, (_, i) => ({
5848
+ id: i,
5849
+ color: CONFETTI_COLORS[Math.floor(fxHash01(seed, i * 7 + 1) * CONFETTI_COLORS.length)],
5850
+ left: 30 + fxHash01(seed, i * 7 + 2) * 40,
5851
+ delay: fxHash01(seed, i * 7 + 3) * 300,
5852
+ angle: fxHash01(seed, i * 7 + 4) * 360,
5853
+ distance: 40 + fxHash01(seed, i * 7 + 5) * 80,
5854
+ rotation: fxHash01(seed, i * 7 + 6) * 720 - 360,
5855
+ size: 4 + fxHash01(seed, i * 7 + 7) * 6
5856
+ }));
5857
+ }
5858
+ var CONFETTI_COLORS, CONFETTI_BURST_KEYFRAMES;
5859
+ var init_fx = __esm({
5860
+ "components/core/atoms/fx.ts"() {
5827
5861
  CONFETTI_COLORS = [
5828
5862
  "var(--color-primary)",
5829
5863
  "var(--color-success)",
@@ -5832,7 +5866,30 @@ var init_ConfettiEffect = __esm({
5832
5866
  "gold",
5833
5867
  "dodgerblue"
5834
5868
  ];
5835
- particleIdCounter = 0;
5869
+ CONFETTI_BURST_KEYFRAMES = `
5870
+ @keyframes confetti-burst {
5871
+ 0% {
5872
+ opacity: 1;
5873
+ transform: translate(0, 0) rotate(0deg) scale(1);
5874
+ }
5875
+ 70% {
5876
+ opacity: 1;
5877
+ }
5878
+ 100% {
5879
+ opacity: 0;
5880
+ transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
5881
+ }
5882
+ }
5883
+ `;
5884
+ }
5885
+ });
5886
+ var ConfettiEffect;
5887
+ var init_ConfettiEffect = __esm({
5888
+ "components/core/atoms/ConfettiEffect.tsx"() {
5889
+ "use client";
5890
+ init_cn();
5891
+ init_Box();
5892
+ init_fx();
5836
5893
  ConfettiEffect = ({
5837
5894
  trigger,
5838
5895
  duration = 2e3,
@@ -5841,11 +5898,13 @@ var init_ConfettiEffect = __esm({
5841
5898
  }) => {
5842
5899
  const [particles, setParticles] = React87.useState([]);
5843
5900
  const previousTriggerRef = React87.useRef(false);
5901
+ const burstRef = React87.useRef(0);
5844
5902
  React87.useEffect(() => {
5845
5903
  const wasFalse = !previousTriggerRef.current;
5846
5904
  previousTriggerRef.current = trigger;
5847
5905
  if (trigger && wasFalse) {
5848
- const newParticles = createParticles(particleCount);
5906
+ burstRef.current += 1;
5907
+ const newParticles = createConfettiParticles(particleCount, `confetti-${burstRef.current}`);
5849
5908
  setParticles(newParticles);
5850
5909
  const timer = window.setTimeout(() => {
5851
5910
  setParticles([]);
@@ -5893,21 +5952,7 @@ var init_ConfettiEffect = __esm({
5893
5952
  p.id
5894
5953
  );
5895
5954
  }),
5896
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
5897
- @keyframes confetti-burst {
5898
- 0% {
5899
- opacity: 1;
5900
- transform: translate(0, 0) rotate(0deg) scale(1);
5901
- }
5902
- 70% {
5903
- opacity: 1;
5904
- }
5905
- 100% {
5906
- opacity: 0;
5907
- transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
5908
- }
5909
- }
5910
- ` })
5955
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: CONFETTI_BURST_KEYFRAMES })
5911
5956
  ]
5912
5957
  }
5913
5958
  );
@@ -9464,7 +9509,7 @@ var init_DrawShape = __esm({
9464
9509
  const cx = p.x + (node.offsetX ?? 0) * tw;
9465
9510
  const cy = p.y + (node.offsetY ?? 0) * tw;
9466
9511
  const rx = (node.radiusX ?? 0) * tw;
9467
- const ry = (node.radiusY ?? rx) * tw;
9512
+ const ry = (node.radiusY ?? node.radiusX ?? 0) * tw;
9468
9513
  if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
9469
9514
  if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
9470
9515
  if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
@@ -9654,6 +9699,172 @@ var init_DrawTextLayer = __esm({
9654
9699
  };
9655
9700
  }
9656
9701
  });
9702
+
9703
+ // components/game/molecules/DrawFxLayer.tsx
9704
+ function fxPosition(view, dim, ageSec) {
9705
+ const g = view.gravity ?? 0;
9706
+ const fall = 0.5 * g * ageSec * ageSec;
9707
+ {
9708
+ const base2 = view.position ?? { x: view.x, y: view.z ?? view.y ?? 0 };
9709
+ if (!Number.isFinite(base2.x) || !Number.isFinite(base2.y)) return void 0;
9710
+ return {
9711
+ x: base2.x + (view.vx ?? 0) * ageSec,
9712
+ y: base2.y + (view.vz ?? 0) * ageSec + fall
9713
+ };
9714
+ }
9715
+ }
9716
+ function sparkShape(view, pos, i, fade, progress) {
9717
+ const size = view.size ?? 0.5;
9718
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
9719
+ const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut2(progress);
9720
+ const r = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
9721
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
9722
+ return {
9723
+ type: "draw-shape",
9724
+ shape: "ellipse",
9725
+ position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
9726
+ anchor: "center",
9727
+ radiusX: r,
9728
+ fill: color,
9729
+ blendMode: "lighter",
9730
+ opacity: fade,
9731
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
9732
+ };
9733
+ }
9734
+ function proceduralShapes(view, pos, fade, progress) {
9735
+ const size = view.size ?? 0.5;
9736
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
9737
+ switch (view.shape ?? "spark") {
9738
+ case "ring": {
9739
+ return [
9740
+ {
9741
+ type: "draw-shape",
9742
+ shape: "ellipse",
9743
+ position: pos,
9744
+ anchor: "center",
9745
+ radiusX: size * easeOut2(progress),
9746
+ stroke: view.color2 ?? color,
9747
+ strokeWidth: 2,
9748
+ blendMode: "lighter",
9749
+ opacity: fade,
9750
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
9751
+ }
9752
+ ];
9753
+ }
9754
+ case "puff": {
9755
+ const count = view.count ?? 3;
9756
+ return Array.from({ length: count }, (_, i) => {
9757
+ const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
9758
+ const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut2(progress);
9759
+ return {
9760
+ type: "draw-shape",
9761
+ shape: "ellipse",
9762
+ position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
9763
+ anchor: "center",
9764
+ radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
9765
+ fill: i === 0 && view.color2 ? view.color2 : color,
9766
+ opacity: fade * 0.6,
9767
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
9768
+ };
9769
+ });
9770
+ }
9771
+ case "streak": {
9772
+ const count = view.count ?? 5;
9773
+ return Array.from({ length: count }, (_, i) => {
9774
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
9775
+ const inner = size * (0.2 + 0.8 * easeOut2(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
9776
+ const len = size * 0.35;
9777
+ return {
9778
+ type: "draw-shape",
9779
+ shape: "ellipse",
9780
+ position: pos,
9781
+ anchor: "center",
9782
+ offsetX: inner + len / 2,
9783
+ offsetY: 0,
9784
+ radiusX: len / 2,
9785
+ radiusY: size * 0.04,
9786
+ rotate: angle,
9787
+ fill: color,
9788
+ blendMode: "lighter",
9789
+ opacity: fade,
9790
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
9791
+ };
9792
+ });
9793
+ }
9794
+ default: {
9795
+ const count = view.count ?? 6;
9796
+ return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
9797
+ }
9798
+ }
9799
+ }
9800
+ function expandFxItem(view, node, epochNowMs, dim) {
9801
+ if (view.space === "screen") return [];
9802
+ const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
9803
+ const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
9804
+ if (progress >= 1) return [];
9805
+ const pos = fxPosition(view, dim, ageMs / 1e3);
9806
+ if (!pos) return [];
9807
+ const out = [];
9808
+ const artItems = node.art?.[view.type]?.["idle"];
9809
+ const sprite = node.sprites?.[view.type];
9810
+ if (Array.isArray(artItems)) {
9811
+ out.push({
9812
+ type: "draw-group",
9813
+ position: pos,
9814
+ opacity: fade,
9815
+ ...view.size !== void 0 ? { scale: view.size } : {},
9816
+ items: artItems
9817
+ });
9818
+ } else if (sprite?.url) {
9819
+ const spriteSize = view.size ?? 0.6;
9820
+ out.push({
9821
+ type: "draw-sprite",
9822
+ position: pos,
9823
+ asset: sprite,
9824
+ anchor: "center",
9825
+ width: spriteSize,
9826
+ height: spriteSize,
9827
+ opacity: fade
9828
+ });
9829
+ } else {
9830
+ out.push(...proceduralShapes(view, pos, fade, progress));
9831
+ }
9832
+ if (view.message) {
9833
+ const text = {
9834
+ type: "draw-text",
9835
+ text: view.message,
9836
+ position: pos,
9837
+ offsetY: -(0.15 + 0.55 * progress),
9838
+ color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
9839
+ opacity: fade
9840
+ };
9841
+ out.push(text);
9842
+ }
9843
+ return out;
9844
+ }
9845
+ function expandFxLayer(node, epochNowMs, dim) {
9846
+ if (!Array.isArray(node.items)) return [];
9847
+ const out = [];
9848
+ for (const item of node.items) {
9849
+ if (!item || typeof item.id !== "string") continue;
9850
+ out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim));
9851
+ }
9852
+ return out;
9853
+ }
9854
+ function DrawFxLayer(_props) {
9855
+ return null;
9856
+ }
9857
+ var DEFAULT_TICK_MS, DEFAULT_TEXT_COLOR, DEFAULT_SPARK_COLOR, easeOut2;
9858
+ var init_DrawFxLayer = __esm({
9859
+ "components/game/molecules/DrawFxLayer.tsx"() {
9860
+ "use client";
9861
+ init_fx();
9862
+ DEFAULT_TICK_MS = 500;
9863
+ DEFAULT_TEXT_COLOR = "#ffe066";
9864
+ DEFAULT_SPARK_COLOR = "#ffffff";
9865
+ easeOut2 = (t) => 1 - (1 - t) * (1 - t);
9866
+ }
9867
+ });
9657
9868
  function paintDrawable(painter, node, dctx) {
9658
9869
  switch (node.type) {
9659
9870
  case "draw-sprite":
@@ -9701,6 +9912,11 @@ function paintDrawable(painter, node, dctx) {
9701
9912
  case "draw-text-layer":
9702
9913
  paintTextLayer(painter, node, dctx);
9703
9914
  break;
9915
+ case "draw-fx-layer": {
9916
+ const epochNow = dctx.time > 0 && typeof performance !== "undefined" ? performance.timeOrigin + dctx.time : 0;
9917
+ for (const child of expandFxLayer(node, epochNow, "2d")) paintDrawable(painter, child, dctx);
9918
+ break;
9919
+ }
9704
9920
  }
9705
9921
  }
9706
9922
  var paint2dLog, warnedUnsupported2d, warnUnsupported2d;
@@ -9715,6 +9931,7 @@ var init_paintDispatch = __esm({
9715
9931
  init_DrawSpriteLayer();
9716
9932
  init_DrawShapeLayer();
9717
9933
  init_DrawTextLayer();
9934
+ init_DrawFxLayer();
9718
9935
  paint2dLog = logger.createLogger("almadar:ui:drawable-2d");
9719
9936
  warnedUnsupported2d = /* @__PURE__ */ new Set();
9720
9937
  warnUnsupported2d = (kind) => {
@@ -9963,6 +10180,7 @@ function Canvas2D({
9963
10180
  return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
9964
10181
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
9965
10182
  if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
10183
+ if (node.type === "draw-fx-layer") return Array.isArray(node.items) && node.items.length > 0;
9966
10184
  return false;
9967
10185
  };
9968
10186
  const animRafRef = React87.useRef(0);
@@ -14243,7 +14461,7 @@ var init_AlgoGraphCanvas = __esm({
14243
14461
  }
14244
14462
  const badgeGeoms = [];
14245
14463
  for (const g of nodeGeoms) {
14246
- if (!g.badge) continue;
14464
+ if (!g.badge || g.badge.text === "") continue;
14247
14465
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
14248
14466
  badgeGeoms.push({
14249
14467
  cx: g.x + g.radius * 0.75,
@@ -14309,7 +14527,7 @@ var init_AlgoGraphCanvas = __esm({
14309
14527
  };
14310
14528
  }
14311
14529
  });
14312
- 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;
14530
+ 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;
14313
14531
  var init_AlgorithmCanvas = __esm({
14314
14532
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
14315
14533
  "use client";
@@ -14336,9 +14554,18 @@ var init_AlgorithmCanvas = __esm({
14336
14554
  FRAME_ACTIVE_COLOR = "#3b82f6";
14337
14555
  FRAME_RETURNING_COLOR = "#f59e0b";
14338
14556
  FRAME_DONE_COLOR = "#94a3b8";
14557
+ FRAME_RIM_COLOR = {
14558
+ active: "#1d4ed8",
14559
+ returning: "#b45309",
14560
+ done: "#64748b"
14561
+ };
14339
14562
  FRAME_LABEL_COLOR = "#ffffff";
14340
14563
  FRAME_DETAIL_COLOR = "#e2e8f0";
14341
14564
  FRAME_TWO_LINE_MIN_H = 22;
14565
+ FRAME_STRIP_H = 44;
14566
+ FRAME_GAP = 6;
14567
+ FRAME_BOTTOM_PAD = 8;
14568
+ FRAME_MIN_H = 12;
14342
14569
  BUCKET_INDEX_FILL = "#e2e8f0";
14343
14570
  BUCKET_INDEX_STROKE = "#9ca3af";
14344
14571
  BUCKET_INDEX_TEXT = "#374151";
@@ -14835,14 +15062,18 @@ var init_AlgorithmCanvas = __esm({
14835
15062
  if (frames.length > 0) {
14836
15063
  const panelYFrames = panelY.frames;
14837
15064
  const n = frames.length;
14838
- const frameH = panelHeight / n;
15065
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
15066
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
15067
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
14839
15068
  const x = 8;
14840
15069
  const w = width - 16;
15070
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
14841
15071
  frames.forEach((f3, i) => {
14842
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
15072
+ const y = bottom - i * stride - frameH;
14843
15073
  const state = f3.state ?? "active";
14844
15074
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
14845
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
15075
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
15076
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
14846
15077
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
14847
15078
  out.push({
14848
15079
  type: "text",
@@ -14850,14 +15081,14 @@ var init_AlgorithmCanvas = __esm({
14850
15081
  y: y + frameH * 0.35,
14851
15082
  text: f3.label,
14852
15083
  color: FRAME_LABEL_COLOR,
14853
- fontSize: 10,
15084
+ fontSize: 11,
14854
15085
  align: "left"
14855
15086
  });
14856
15087
  if (f3.detail) {
14857
15088
  out.push({
14858
15089
  type: "text",
14859
15090
  x: 16,
14860
- y: y + frameH * 0.7,
15091
+ y: y + frameH * 0.72,
14861
15092
  text: f3.detail,
14862
15093
  color: FRAME_DETAIL_COLOR,
14863
15094
  fontSize: 10,
@@ -21370,8 +21601,6 @@ var init_ButtonGroup = __esm({
21370
21601
  ButtonGroup.displayName = "ButtonGroup";
21371
21602
  }
21372
21603
  });
21373
-
21374
- // lib/getNestedValue.ts
21375
21604
  function getNestedValue(obj, path) {
21376
21605
  if (obj === null || obj === void 0 || !path) {
21377
21606
  return void 0;
@@ -21392,6 +21621,15 @@ function getNestedValue(obj, path) {
21392
21621
  }
21393
21622
  return value;
21394
21623
  }
21624
+ function resolveImageUrl(value) {
21625
+ if (typeof value === "string") {
21626
+ return value;
21627
+ }
21628
+ if (core.isFileValue(value)) {
21629
+ return value.url;
21630
+ }
21631
+ return void 0;
21632
+ }
21395
21633
  var init_getNestedValue = __esm({
21396
21634
  "lib/getNestedValue.ts"() {
21397
21635
  }
@@ -22096,8 +22334,8 @@ var init_CardGrid = __esm({
22096
22334
  actionPayload: { row: itemData },
22097
22335
  children: [
22098
22336
  imageField && (() => {
22099
- const imgUrl = getNestedValue(itemData, imageField);
22100
- if (!imgUrl || typeof imgUrl !== "string") return null;
22337
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
22338
+ if (!imgUrl) return null;
22101
22339
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
22102
22340
  "img",
22103
22341
  {
@@ -26127,8 +26365,8 @@ function DataGrid({
26127
26365
  ),
26128
26366
  children: [
26129
26367
  imageField && (() => {
26130
- const imgUrl = getNestedValue(itemData, imageField);
26131
- if (!imgUrl || typeof imgUrl !== "string") return null;
26368
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
26369
+ if (!imgUrl) return null;
26132
26370
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
26133
26371
  "img",
26134
26372
  {
@@ -28182,6 +28420,196 @@ var init_PageTransition = __esm({
28182
28420
  PageTransition.displayName = "PageTransition";
28183
28421
  }
28184
28422
  });
28423
+ function ConfettiBurst({ item, lifeMs }) {
28424
+ const particles = createConfettiParticles(item.particleCount ?? 30, item.id);
28425
+ const left = (item.x ?? 0.5) * 100;
28426
+ const top = (item.z ?? item.y ?? 0.4) * 100;
28427
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: particles.map((p) => {
28428
+ const rad = p.angle * Math.PI / 180;
28429
+ const tx = Math.cos(rad) * p.distance * (item.size ?? 1);
28430
+ const ty = Math.sin(rad) * p.distance * (item.size ?? 1) - 20;
28431
+ return /* @__PURE__ */ jsxRuntime.jsx(
28432
+ Box,
28433
+ {
28434
+ className: "absolute rounded-sm",
28435
+ style: {
28436
+ left: (p.left - 50) * 2,
28437
+ top: -10,
28438
+ width: p.size,
28439
+ height: p.size,
28440
+ backgroundColor: item.color ?? p.color,
28441
+ animation: `confetti-burst ${Math.max(lifeMs - p.delay, 200)}ms ease-out ${p.delay}ms forwards`,
28442
+ opacity: 0,
28443
+ "--confetti-tx": `${tx}px`,
28444
+ "--confetti-ty": `${ty}px`,
28445
+ "--confetti-rotate": `${p.rotation}deg`
28446
+ }
28447
+ },
28448
+ p.id
28449
+ );
28450
+ }) });
28451
+ }
28452
+ function SparkleBurst({ item, lifeMs }) {
28453
+ const count = item.particleCount ?? 8;
28454
+ const scale = item.size ?? 1;
28455
+ const left = (item.x ?? 0.5) * 100;
28456
+ const top = (item.z ?? item.y ?? 0.4) * 100;
28457
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: Array.from({ length: count }, (_, i) => {
28458
+ const dx = (fxHash01(item.id, i * 4) - 0.5) * 160 * scale;
28459
+ const dy = (fxHash01(item.id, i * 4 + 1) - 0.5) * 120 * scale;
28460
+ const dot = (4 + fxHash01(item.id, i * 4 + 2) * 5) * scale;
28461
+ const delay = fxHash01(item.id, i * 4 + 3) * lifeMs * 0.4;
28462
+ return /* @__PURE__ */ jsxRuntime.jsx(
28463
+ Box,
28464
+ {
28465
+ className: "absolute rounded-full",
28466
+ style: {
28467
+ left: dx,
28468
+ top: dy,
28469
+ width: dot,
28470
+ height: dot,
28471
+ backgroundColor: item.color ?? "gold",
28472
+ opacity: 0,
28473
+ animation: `fx-overlay-sparkle ${Math.max(lifeMs - delay, 200)}ms ease-in-out ${delay}ms forwards`
28474
+ }
28475
+ },
28476
+ i
28477
+ );
28478
+ }) });
28479
+ }
28480
+ function OverlayFxNode({ item, tickMs }) {
28481
+ const lifeMs = lifeMsOf(item, tickMs);
28482
+ switch (item.effect ?? "sparkle") {
28483
+ case "confetti":
28484
+ return /* @__PURE__ */ jsxRuntime.jsx(ConfettiBurst, { item, lifeMs });
28485
+ case "flash":
28486
+ return /* @__PURE__ */ jsxRuntime.jsx(
28487
+ Box,
28488
+ {
28489
+ position: "absolute",
28490
+ className: "inset-0",
28491
+ style: {
28492
+ backgroundColor: item.color ?? "#ffffff",
28493
+ opacity: 0,
28494
+ animation: `fx-overlay-flash ${lifeMs}ms ease-out forwards`
28495
+ }
28496
+ }
28497
+ );
28498
+ case "streak-glow":
28499
+ return /* @__PURE__ */ jsxRuntime.jsx(
28500
+ Box,
28501
+ {
28502
+ position: "absolute",
28503
+ className: "inset-0",
28504
+ style: {
28505
+ boxShadow: `inset 0 0 ${60 * (item.size ?? 1)}px ${item.color ?? "var(--color-warning)"}`,
28506
+ opacity: 0,
28507
+ animation: `fx-overlay-glow ${lifeMs}ms ease-in-out forwards`
28508
+ }
28509
+ }
28510
+ );
28511
+ case "float-text": {
28512
+ if (!item.message) return null;
28513
+ return /* @__PURE__ */ jsxRuntime.jsx(
28514
+ Box,
28515
+ {
28516
+ position: "absolute",
28517
+ style: {
28518
+ left: `${(item.x ?? 0.5) * 100}%`,
28519
+ top: `${(item.z ?? item.y ?? 0.4) * 100}%`,
28520
+ color: item.color ?? "var(--color-success)",
28521
+ fontWeight: 700,
28522
+ fontSize: 16 * (item.size ?? 1),
28523
+ textShadow: "0 1px 2px rgba(0,0,0,0.4)",
28524
+ opacity: 0,
28525
+ animation: `fx-overlay-float ${lifeMs}ms ease-out forwards`,
28526
+ whiteSpace: "nowrap"
28527
+ },
28528
+ children: item.message
28529
+ }
28530
+ );
28531
+ }
28532
+ case "shake":
28533
+ return null;
28534
+ default:
28535
+ return /* @__PURE__ */ jsxRuntime.jsx(SparkleBurst, { item, lifeMs });
28536
+ }
28537
+ }
28538
+ var DEFAULT_TICK_MS2, FX_OVERLAY_KEYFRAMES, lifeMsOf, FxOverlay;
28539
+ var init_FxOverlay = __esm({
28540
+ "components/core/molecules/FxOverlay.tsx"() {
28541
+ "use client";
28542
+ init_cn();
28543
+ init_Box();
28544
+ init_fx();
28545
+ DEFAULT_TICK_MS2 = 500;
28546
+ FX_OVERLAY_KEYFRAMES = `
28547
+ ${CONFETTI_BURST_KEYFRAMES}
28548
+ @keyframes fx-overlay-flash {
28549
+ 0% { opacity: 0.75; }
28550
+ 100% { opacity: 0; }
28551
+ }
28552
+ @keyframes fx-overlay-glow {
28553
+ 0% { opacity: 0.9; }
28554
+ 60% { opacity: 0.6; }
28555
+ 100% { opacity: 0; }
28556
+ }
28557
+ @keyframes fx-overlay-sparkle {
28558
+ 0% { opacity: 0; transform: scale(0); }
28559
+ 30% { opacity: 1; transform: scale(1); }
28560
+ 100% { opacity: 0; transform: scale(0.3); }
28561
+ }
28562
+ @keyframes fx-overlay-float {
28563
+ 0% { opacity: 0; transform: translate(-50%, 8px); }
28564
+ 15% { opacity: 1; }
28565
+ 100% { opacity: 0; transform: translate(-50%, -40px); }
28566
+ }
28567
+ @keyframes fx-overlay-shake {
28568
+ 0% { transform: translateX(0); }
28569
+ 15% { transform: translateX(-6px); }
28570
+ 30% { transform: translateX(5px); }
28571
+ 45% { transform: translateX(-4px); }
28572
+ 60% { transform: translateX(3px); }
28573
+ 75% { transform: translateX(-2px); }
28574
+ 100% { transform: translateX(0); }
28575
+ }
28576
+ `;
28577
+ lifeMsOf = (it, tickMs) => Math.max(it.maxTtl ?? it.ttl, it.ttl, 1) * tickMs;
28578
+ FxOverlay = ({ items, tickMs = DEFAULT_TICK_MS2, children, className }) => {
28579
+ const screenItems = (Array.isArray(items) ? items : []).filter(
28580
+ (it) => Boolean(it) && typeof it.id === "string" && it.space !== "world"
28581
+ );
28582
+ const shakeItem = [...screenItems].reverse().find((it) => it.effect === "shake");
28583
+ const overlay = /* @__PURE__ */ jsxRuntime.jsxs(
28584
+ Box,
28585
+ {
28586
+ position: "absolute",
28587
+ className: cn("inset-0 pointer-events-none overflow-hidden z-50", !children && className),
28588
+ "aria-hidden": "true",
28589
+ children: [
28590
+ screenItems.map((it) => /* @__PURE__ */ jsxRuntime.jsx(OverlayFxNode, { item: it, tickMs }, it.id)),
28591
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: FX_OVERLAY_KEYFRAMES })
28592
+ ]
28593
+ }
28594
+ );
28595
+ if (children !== void 0 && children !== null) {
28596
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { position: "relative", className, children: [
28597
+ /* @__PURE__ */ jsxRuntime.jsx(
28598
+ Box,
28599
+ {
28600
+ style: shakeItem ? { animation: `fx-overlay-shake ${Math.min(lifeMsOf(shakeItem, tickMs), 600)}ms ease-in-out` } : void 0,
28601
+ children
28602
+ },
28603
+ shakeItem?.id ?? "steady"
28604
+ ),
28605
+ overlay
28606
+ ] });
28607
+ }
28608
+ return overlay;
28609
+ };
28610
+ FxOverlay.displayName = "FxOverlay";
28611
+ }
28612
+ });
28185
28613
  function computePopoverStyle(position, triggerRect, popoverWidth) {
28186
28614
  if (position === "left" || position === "right") {
28187
28615
  return {
@@ -28738,7 +29166,7 @@ var init_SearchInput = __esm({
28738
29166
  /* @__PURE__ */ jsxRuntime.jsx(
28739
29167
  Input,
28740
29168
  {
28741
- type: "text",
29169
+ type: "search",
28742
29170
  value: searchValue,
28743
29171
  onChange: handleChange,
28744
29172
  placeholder: resolvedPlaceholder,
@@ -30064,6 +30492,7 @@ var MathCanvas;
30064
30492
  var init_MathCanvas = __esm({
30065
30493
  "components/learning/molecules/MathCanvas.tsx"() {
30066
30494
  "use client";
30495
+ init_useEventBus();
30067
30496
  init_atoms();
30068
30497
  init_Stack();
30069
30498
  init_LearningCanvas();
@@ -30095,9 +30524,32 @@ var init_MathCanvas = __esm({
30095
30524
  interactive = false,
30096
30525
  animate = false,
30097
30526
  onShapeClick,
30527
+ keyMap,
30528
+ keyUpMap,
30098
30529
  isLoading,
30099
30530
  error
30100
30531
  }) => {
30532
+ const eventBus = useEventBus();
30533
+ React87.useEffect(() => {
30534
+ if (!keyMap && !keyUpMap) return;
30535
+ const onDown = (e) => {
30536
+ const ev = keyMap?.[e.code];
30537
+ if (ev) {
30538
+ eventBus.emit(`UI:${ev}`, {});
30539
+ e.preventDefault();
30540
+ }
30541
+ };
30542
+ const onUp = (e) => {
30543
+ const ev = keyUpMap?.[e.code];
30544
+ if (ev) eventBus.emit(`UI:${ev}`, {});
30545
+ };
30546
+ window.addEventListener("keydown", onDown);
30547
+ window.addEventListener("keyup", onUp);
30548
+ return () => {
30549
+ window.removeEventListener("keydown", onDown);
30550
+ window.removeEventListener("keyup", onUp);
30551
+ };
30552
+ }, [keyMap, keyUpMap, eventBus]);
30101
30553
  const derivedShapes = React87.useMemo(() => {
30102
30554
  const out = [];
30103
30555
  const margin = 24;
@@ -30110,11 +30562,11 @@ var init_MathCanvas = __esm({
30110
30562
  if (showGrid) {
30111
30563
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
30112
30564
  const px = mapX(x);
30113
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
30565
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30114
30566
  }
30115
30567
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
30116
30568
  const py = mapY(y);
30117
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
30569
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30118
30570
  }
30119
30571
  }
30120
30572
  if (showTickLabels) {
@@ -30163,7 +30615,7 @@ var init_MathCanvas = __esm({
30163
30615
  x: mapX((first.x + last.x) / 2),
30164
30616
  y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
30165
30617
  text: region.label,
30166
- color: "#111827",
30618
+ color,
30167
30619
  fontSize: 11
30168
30620
  });
30169
30621
  }
@@ -30196,14 +30648,14 @@ var init_MathCanvas = __esm({
30196
30648
  const px = mapX(guide.at);
30197
30649
  out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
30198
30650
  if (guide.label) {
30199
- out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color: "#111827", fontSize: 11 });
30651
+ out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
30200
30652
  }
30201
30653
  } else {
30202
30654
  if (guide.at < yMin || guide.at > yMax) continue;
30203
30655
  const py = mapY(guide.at);
30204
30656
  out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
30205
30657
  if (guide.label) {
30206
- out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color: "#111827", fontSize: 11, align: "right" });
30658
+ out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: 11, align: "right" });
30207
30659
  }
30208
30660
  }
30209
30661
  }
@@ -30269,7 +30721,7 @@ var init_MathCanvas = __esm({
30269
30721
  x: (x1 + x2) / 2,
30270
30722
  y: xAxisY - peak - 8,
30271
30723
  text: hop.label,
30272
- color: "#111827",
30724
+ color,
30273
30725
  fontSize: 10,
30274
30726
  align: "center"
30275
30727
  });
@@ -30296,7 +30748,7 @@ var init_MathCanvas = __esm({
30296
30748
  x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
30297
30749
  y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
30298
30750
  text: angle.label,
30299
- color: "#111827",
30751
+ color,
30300
30752
  fontSize: 11,
30301
30753
  align: "center"
30302
30754
  });
@@ -30314,7 +30766,7 @@ var init_MathCanvas = __esm({
30314
30766
  fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
30315
30767
  });
30316
30768
  if (p.label) {
30317
- out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
30769
+ out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: 12 });
30318
30770
  }
30319
30771
  }
30320
30772
  for (const v of vectors) {
@@ -30325,7 +30777,7 @@ var init_MathCanvas = __esm({
30325
30777
  const y2 = mapY(v.y + v.vy);
30326
30778
  out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
30327
30779
  if (v.label) {
30328
- out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
30780
+ out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: 12 });
30329
30781
  }
30330
30782
  }
30331
30783
  out.push(...shapes);
@@ -31589,13 +32041,13 @@ var init_MapView = __esm({
31589
32041
  shadowSize: [41, 41]
31590
32042
  });
31591
32043
  L.Marker.prototype.options.icon = defaultIcon;
31592
- const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__namespace.default;
32044
+ const { useEffect: useEffect64, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__namespace.default;
31593
32045
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31594
32046
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31595
32047
  function MapUpdater({ centerLat, centerLng, zoom }) {
31596
32048
  const map = useMap();
31597
32049
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31598
- useEffect63(() => {
32050
+ useEffect64(() => {
31599
32051
  const prev = prevRef.current;
31600
32052
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31601
32053
  map.setView([centerLat, centerLng], zoom);
@@ -31606,7 +32058,7 @@ var init_MapView = __esm({
31606
32058
  }
31607
32059
  function MapClickHandler({ onMapClick }) {
31608
32060
  const map = useMap();
31609
- useEffect63(() => {
32061
+ useEffect64(() => {
31610
32062
  if (!onMapClick) return;
31611
32063
  const handler = (e) => {
31612
32064
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -38422,6 +38874,7 @@ var init_PageHeader = __esm({
38422
38874
  className
38423
38875
  }) => {
38424
38876
  const eventBus = useEventBus();
38877
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
38425
38878
  const handleBack = () => {
38426
38879
  eventBus.emit(`UI:${backEvent}`);
38427
38880
  };
@@ -38469,15 +38922,15 @@ var init_PageHeader = __esm({
38469
38922
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
38470
38923
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3", children: [
38471
38924
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
38472
- status && /* @__PURE__ */ jsxRuntime.jsx(
38925
+ statusBadge && /* @__PURE__ */ jsxRuntime.jsx(
38473
38926
  Typography,
38474
38927
  {
38475
38928
  variant: "small",
38476
38929
  className: cn(
38477
38930
  "px-2.5 py-1 rounded-full text-xs font-medium",
38478
- statusColors2[status.variant || "default"]
38931
+ statusColors2[statusBadge.variant || "default"]
38479
38932
  ),
38480
- children: status.label
38933
+ children: statusBadge.label
38481
38934
  }
38482
38935
  )
38483
38936
  ] }),
@@ -41706,7 +42159,7 @@ function formatFieldValue2(value, fieldName) {
41706
42159
  }
41707
42160
  return String(value);
41708
42161
  }
41709
- function renderRichFieldValue(value, fieldName, fieldType) {
42162
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41710
42163
  if (value === void 0 || value === null) return "\u2014";
41711
42164
  const str = String(value);
41712
42165
  switch (fieldType) {
@@ -41773,19 +42226,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
41773
42226
  }
41774
42227
  );
41775
42228
  case "date":
41776
- case "datetime": {
42229
+ case "datetime":
42230
+ case "timestamp": {
41777
42231
  const d = new Date(str);
41778
42232
  if (!isNaN(d.getTime())) {
41779
42233
  return d.toLocaleDateString(void 0, {
41780
42234
  year: "numeric",
41781
42235
  month: "long",
41782
42236
  day: "numeric",
41783
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
42237
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
41784
42238
  });
41785
42239
  }
41786
42240
  return str;
41787
42241
  }
42242
+ case "money": {
42243
+ const n = typeof value === "number" ? value : Number(str);
42244
+ if (!isNaN(n)) {
42245
+ return new Intl.NumberFormat(void 0, {
42246
+ style: "currency",
42247
+ currency: "USD"
42248
+ }).format(n);
42249
+ }
42250
+ return str;
42251
+ }
42252
+ case "relation": {
42253
+ const match = meta?.options?.find((opt) => opt.value === str);
42254
+ return match ? match.label : str;
42255
+ }
42256
+ case "file": {
42257
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
42258
+ const file = value;
42259
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
42260
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
42261
+ return /* @__PURE__ */ jsxRuntime.jsxs(
42262
+ "a",
42263
+ {
42264
+ href: typeof file.url === "string" ? file.url : void 0,
42265
+ download: label,
42266
+ 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",
42267
+ children: [
42268
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.FileText, size: "sm", className: "text-muted-foreground" }),
42269
+ label,
42270
+ size && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
42271
+ ]
42272
+ }
42273
+ );
42274
+ }
42275
+ return str;
42276
+ }
41788
42277
  default:
42278
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
42279
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
42280
+ }
41789
42281
  return formatFieldValue2(value, fieldName);
41790
42282
  }
41791
42283
  }
@@ -41812,8 +42304,18 @@ function buildFieldTypeMap(fields) {
41812
42304
  const map = {};
41813
42305
  if (!fields) return map;
41814
42306
  for (const f3 of fields) {
41815
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
41816
- map[f3.name] = f3.type;
42307
+ if (typeof f3 === "object" && f3.type !== void 0) {
42308
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
42309
+ }
42310
+ }
42311
+ return map;
42312
+ }
42313
+ function buildFieldMetaMap(fields) {
42314
+ const map = {};
42315
+ if (!fields) return map;
42316
+ for (const f3 of fields) {
42317
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
42318
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
41817
42319
  }
41818
42320
  }
41819
42321
  return map;
@@ -41833,6 +42335,7 @@ var init_DetailPanel = __esm({
41833
42335
  init_format();
41834
42336
  init_getNestedValue();
41835
42337
  init_useEventBus();
42338
+ init_UploadDropZone();
41836
42339
  formatFieldLabel = humanizeFieldName;
41837
42340
  ReactMarkdown2 = React87.lazy(() => import('react-markdown'));
41838
42341
  DetailPanel = ({
@@ -41842,6 +42345,7 @@ var init_DetailPanel = __esm({
41842
42345
  avatar,
41843
42346
  sections: propSections,
41844
42347
  actions,
42348
+ backAction,
41845
42349
  footer,
41846
42350
  slideOver = false,
41847
42351
  className,
@@ -41850,7 +42354,8 @@ var init_DetailPanel = __esm({
41850
42354
  fieldNames,
41851
42355
  initialData,
41852
42356
  isLoading = false,
41853
- error
42357
+ error,
42358
+ relationsData
41854
42359
  }) => {
41855
42360
  const eventBus = useEventBus();
41856
42361
  const { t } = hooks.useTranslate();
@@ -41861,8 +42366,15 @@ var init_DetailPanel = __esm({
41861
42366
  };
41862
42367
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
41863
42368
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
42369
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
41864
42370
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
41865
42371
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
42372
+ const metaFor = (field) => {
42373
+ const base = fieldMetaMap[field];
42374
+ const options = relationsData?.[field];
42375
+ if (!base && !options) return void 0;
42376
+ return { ...base, options };
42377
+ };
41866
42378
  const handleActionClick = React87.useCallback(
41867
42379
  (action, data2) => {
41868
42380
  if (action.navigatesTo) {
@@ -41941,7 +42453,7 @@ var init_DetailPanel = __esm({
41941
42453
  if (value !== void 0 && value !== null) {
41942
42454
  overviewFields.push({
41943
42455
  label: labelFor(field),
41944
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42456
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41945
42457
  icon: getFieldIcon(field)
41946
42458
  });
41947
42459
  }
@@ -41957,7 +42469,7 @@ var init_DetailPanel = __esm({
41957
42469
  if (value !== void 0 && value !== null) {
41958
42470
  metricsFields.push({
41959
42471
  label: labelFor(field),
41960
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42472
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41961
42473
  icon: getFieldIcon(field)
41962
42474
  });
41963
42475
  }
@@ -41973,7 +42485,7 @@ var init_DetailPanel = __esm({
41973
42485
  if (value !== void 0 && value !== null) {
41974
42486
  timelineFields.push({
41975
42487
  label: labelFor(field),
41976
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42488
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41977
42489
  icon: getFieldIcon(field)
41978
42490
  });
41979
42491
  }
@@ -41989,7 +42501,7 @@ var init_DetailPanel = __esm({
41989
42501
  if (value !== void 0 && value !== null) {
41990
42502
  descFields.push({
41991
42503
  label: labelFor(field),
41992
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42504
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41993
42505
  icon: getFieldIcon(field)
41994
42506
  });
41995
42507
  }
@@ -42037,7 +42549,7 @@ var init_DetailPanel = __esm({
42037
42549
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
42038
42550
  allFields.push({
42039
42551
  label: labelFor(field),
42040
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42552
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42041
42553
  icon: getFieldIcon(field)
42042
42554
  });
42043
42555
  } else {
@@ -42052,34 +42564,49 @@ var init_DetailPanel = __esm({
42052
42564
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
42053
42565
  const effectiveCloseAction = closeAction ?? { event: void 0};
42054
42566
  const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
42055
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
42056
- otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
42057
- Button,
42058
- {
42059
- variant: action.variant || "secondary",
42060
- size: "sm",
42061
- action: action.navigatesTo ? void 0 : action.event,
42062
- actionPayload: { row: normalizedData },
42063
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
42064
- icon: action.icon,
42065
- "data-testid": action.event ? `action-${action.event}` : void 0,
42066
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
42067
- children: action.label
42068
- },
42069
- idx
42070
- )),
42071
- /* @__PURE__ */ jsxRuntime.jsx(
42567
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
42568
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
42072
42569
  Button,
42073
42570
  {
42074
- variant: "ghost",
42571
+ variant: backAction.variant || "ghost",
42075
42572
  size: "sm",
42076
- action: effectiveCloseAction.event,
42573
+ action: backAction.navigatesTo ? void 0 : backAction.event,
42077
42574
  actionPayload: { row: normalizedData },
42078
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
42079
- icon: LucideIcons2.X,
42080
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42575
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
42576
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
42577
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42578
+ children: backAction.label
42081
42579
  }
42082
- )
42580
+ ) }),
42581
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
42582
+ otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
42583
+ Button,
42584
+ {
42585
+ variant: action.variant || "secondary",
42586
+ size: "sm",
42587
+ action: action.navigatesTo ? void 0 : action.event,
42588
+ actionPayload: { row: normalizedData },
42589
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
42590
+ icon: action.icon,
42591
+ "data-testid": action.event ? `action-${action.event}` : void 0,
42592
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
42593
+ children: action.label
42594
+ },
42595
+ idx
42596
+ )),
42597
+ /* @__PURE__ */ jsxRuntime.jsx(
42598
+ Button,
42599
+ {
42600
+ variant: "ghost",
42601
+ size: "sm",
42602
+ action: effectiveCloseAction.event,
42603
+ actionPayload: { row: normalizedData },
42604
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
42605
+ icon: LucideIcons2.X,
42606
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42607
+ }
42608
+ )
42609
+ ] })
42083
42610
  ] }),
42084
42611
  avatar,
42085
42612
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -42394,6 +42921,10 @@ function determineInputType(field) {
42394
42921
  return "password";
42395
42922
  case "url":
42396
42923
  return "url";
42924
+ case "file":
42925
+ return "file";
42926
+ case "money":
42927
+ return "currency";
42397
42928
  case "number":
42398
42929
  case "integer":
42399
42930
  case "float":
@@ -42455,6 +42986,7 @@ var init_Form = __esm({
42455
42986
  init_Typography();
42456
42987
  init_Icon();
42457
42988
  init_RelationSelect();
42989
+ init_UploadDropZone();
42458
42990
  init_Alert();
42459
42991
  init_useEventBus();
42460
42992
  init_debug();
@@ -42497,6 +43029,7 @@ var init_Form = __esm({
42497
43029
  cancelEvent = "CANCEL",
42498
43030
  relationsData = {},
42499
43031
  relationsLoading = {},
43032
+ fieldOverrides,
42500
43033
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
42501
43034
  conditionalFields: conditionalFieldsRaw = {},
42502
43035
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -42743,7 +43276,8 @@ var init_Form = __esm({
42743
43276
  label,
42744
43277
  field.required && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
42745
43278
  ] }),
42746
- renderFieldInput(field, fieldName, inputType, currentValue, label)
43279
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
43280
+ field.hint && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
42747
43281
  ] }, fieldName);
42748
43282
  },
42749
43283
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -42759,6 +43293,7 @@ var init_Form = __esm({
42759
43293
  name: field,
42760
43294
  type: entityField.type,
42761
43295
  required: entityField.required,
43296
+ hint: entityField.description,
42762
43297
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
42763
43298
  defaultValue: entityField.default,
42764
43299
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -42771,8 +43306,18 @@ var init_Form = __esm({
42771
43306
  return { name: field, type: "string" };
42772
43307
  }
42773
43308
  return field;
43309
+ }).map((field) => {
43310
+ const fieldName = field.name || field.field;
43311
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
43312
+ if (!override) return field;
43313
+ return {
43314
+ ...field,
43315
+ ...override.label !== void 0 ? { label: override.label } : {},
43316
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
43317
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
43318
+ };
42774
43319
  });
42775
- }, [effectiveFields, resolvedEntity]);
43320
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
42776
43321
  const schemaFields = React87__namespace.default.useMemo(() => {
42777
43322
  if (normalizedFields.length === 0) return null;
42778
43323
  if (isDebugEnabled()) {
@@ -42896,6 +43441,44 @@ var init_Form = __esm({
42896
43441
  max: field.max
42897
43442
  }
42898
43443
  );
43444
+ case "currency":
43445
+ return /* @__PURE__ */ jsxRuntime.jsx(
43446
+ Input,
43447
+ {
43448
+ ...commonProps,
43449
+ type: "number",
43450
+ step: "0.01",
43451
+ icon: LucideIcons2.DollarSign,
43452
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
43453
+ onChange: (e) => handleChange(
43454
+ fieldName,
43455
+ e.target.value ? Number(e.target.value) : void 0
43456
+ ),
43457
+ min: field.min,
43458
+ max: field.max
43459
+ }
43460
+ );
43461
+ case "file":
43462
+ return /* @__PURE__ */ jsxRuntime.jsx(
43463
+ UploadDropZone,
43464
+ {
43465
+ accept: field.pattern,
43466
+ maxFiles: 1,
43467
+ disabled: isLoading,
43468
+ onFiles: (files) => {
43469
+ const f3 = files[0];
43470
+ if (!f3) return;
43471
+ const reader = new FileReader();
43472
+ reader.onload = () => handleChange(fieldName, {
43473
+ name: f3.name,
43474
+ mimeType: f3.type,
43475
+ sizeBytes: f3.size,
43476
+ url: typeof reader.result === "string" ? reader.result : ""
43477
+ });
43478
+ reader.readAsDataURL(f3);
43479
+ }
43480
+ }
43481
+ );
42899
43482
  case "date":
42900
43483
  return /* @__PURE__ */ jsxRuntime.jsx(
42901
43484
  Input,
@@ -43928,6 +44511,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
43928
44511
  var init_MediaGallery = __esm({
43929
44512
  "components/core/organisms/MediaGallery.tsx"() {
43930
44513
  "use client";
44514
+ init_getNestedValue();
43931
44515
  init_cn();
43932
44516
  init_atoms();
43933
44517
  init_Stack();
@@ -43996,9 +44580,9 @@ var init_MediaGallery = __esm({
43996
44580
  return entityData.map((record, idx) => {
43997
44581
  return {
43998
44582
  id: String(record.id ?? idx),
43999
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
44583
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
44000
44584
  alt: record.alt ? String(record.alt) : void 0,
44001
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
44585
+ thumbnail: resolveImageUrl(record.thumbnail),
44002
44586
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
44003
44587
  };
44004
44588
  });
@@ -47866,6 +48450,7 @@ var init_component_registry_generated = __esm({
47866
48450
  init_DocSidebar();
47867
48451
  init_DocTOC();
47868
48452
  init_DocumentViewer();
48453
+ init_DrawFxLayer();
47869
48454
  init_DrawGroup();
47870
48455
  init_DrawMesh();
47871
48456
  init_DrawShape();
@@ -47896,6 +48481,7 @@ var init_component_registry_generated = __esm({
47896
48481
  init_FormField();
47897
48482
  init_FormSection();
47898
48483
  init_FormSectionHeader();
48484
+ init_FxOverlay();
47899
48485
  init_GameAudioToggle();
47900
48486
  init_GameHud();
47901
48487
  init_GameIcon();
@@ -48136,6 +48722,7 @@ var init_component_registry_generated = __esm({
48136
48722
  "DocSidebar": DocSidebar,
48137
48723
  "DocTOC": DocTOC,
48138
48724
  "DocumentViewer": DocumentViewer,
48725
+ "DrawFxLayer": DrawFxLayer,
48139
48726
  "DrawGroup": DrawGroup,
48140
48727
  "DrawMesh": DrawMesh,
48141
48728
  "DrawShape": DrawShape,
@@ -48166,6 +48753,7 @@ var init_component_registry_generated = __esm({
48166
48753
  "FormField": FormField,
48167
48754
  "FormLayout": FormLayout,
48168
48755
  "FormSectionHeader": FormSectionHeader,
48756
+ "FxOverlay": FxOverlay,
48169
48757
  "GameAudioToggle": GameAudioToggle,
48170
48758
  "GameHud": GameHud,
48171
48759
  "GameIcon": GameIcon,
@@ -48341,7 +48929,8 @@ __export(UISlotRenderer_exports, {
48341
48929
  SlotContentRenderer: () => SlotContentRenderer,
48342
48930
  SuspenseConfigProvider: () => SuspenseConfigProvider,
48343
48931
  UISlotComponent: () => UISlotComponent,
48344
- UISlotRenderer: () => UISlotRenderer
48932
+ UISlotRenderer: () => UISlotRenderer,
48933
+ renderPatternValue: () => renderPatternValue
48345
48934
  });
48346
48935
  function SuspenseConfigProvider({
48347
48936
  config,
@@ -48377,6 +48966,9 @@ function enrichFormFields(fields, entityDef) {
48377
48966
  type: entityField.type,
48378
48967
  required: entityField.required ?? false
48379
48968
  };
48969
+ if (entityField.description) {
48970
+ enriched.hint = entityField.description;
48971
+ }
48380
48972
  if (entityField.values && entityField.values.length > 0) {
48381
48973
  enriched.values = entityField.values;
48382
48974
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -48400,6 +48992,9 @@ function enrichFormFields(fields, entityDef) {
48400
48992
  if (entityField.required && !("required" in obj)) {
48401
48993
  enriched.required = true;
48402
48994
  }
48995
+ if (entityField.description && !obj.hint && !obj.help) {
48996
+ enriched.hint = entityField.description;
48997
+ }
48403
48998
  if (!obj.values && !obj.options) {
48404
48999
  if (entityField.values && entityField.values.length > 0) {
48405
49000
  enriched.values = entityField.values;
@@ -48415,6 +49010,32 @@ function enrichFormFields(fields, entityDef) {
48415
49010
  return field;
48416
49011
  });
48417
49012
  }
49013
+ function enrichDetailFields(fields, entityDef) {
49014
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
49015
+ const metaFor = (name) => {
49016
+ const entityField = fieldMap.get(name);
49017
+ if (!entityField) return void 0;
49018
+ const meta = { type: entityField.type };
49019
+ const values = entityField.values ?? entityField.enumValues;
49020
+ if (values && values.length > 0) meta.values = values;
49021
+ if (entityField.relation) meta.relation = entityField.relation.entity;
49022
+ return meta;
49023
+ };
49024
+ return fields.map((field) => {
49025
+ if (typeof field === "string") {
49026
+ const meta = metaFor(field);
49027
+ return meta ? { key: field, ...meta } : field;
49028
+ }
49029
+ if (field && typeof field === "object" && !Array.isArray(field) && !React87__namespace.default.isValidElement(field) && !(field instanceof Date)) {
49030
+ const obj = field;
49031
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
49032
+ if (!fieldName || obj.type) return field;
49033
+ const meta = metaFor(fieldName);
49034
+ return meta ? { ...obj, ...meta } : field;
49035
+ }
49036
+ return field;
49037
+ });
49038
+ }
48418
49039
  function renderContainedPortal(t, slot, content, onDismiss) {
48419
49040
  const slotContent = /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content, onDismiss }) });
48420
49041
  const slotId = `slot-${slot}`;
@@ -48924,6 +49545,24 @@ function isPatternConfig(value) {
48924
49545
  const record = value;
48925
49546
  return "type" in record && typeof record.type === "string" && patterns.getComponentForPattern(record.type) !== null;
48926
49547
  }
49548
+ function renderPatternValue(value) {
49549
+ if (value === null || value === void 0) return null;
49550
+ if (typeof value === "boolean" || typeof value === "function") return null;
49551
+ if (typeof value === "number") return value;
49552
+ if (typeof value === "string") return renderPatternChildren(value, () => {
49553
+ });
49554
+ if (value instanceof Date) return value.toLocaleString();
49555
+ if (React87__namespace.default.isValidElement(value)) return value;
49556
+ if (Array.isArray(value)) {
49557
+ return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React87__namespace.default.Fragment, { children: renderPatternValue(item) }, index));
49558
+ }
49559
+ if (isPatternConfig(value)) {
49560
+ const { type, ...props } = value;
49561
+ return renderPatternChildren({ type, props }, () => {
49562
+ });
49563
+ }
49564
+ return null;
49565
+ }
48927
49566
  function isPlainConfigObject(value) {
48928
49567
  if (React87__namespace.default.isValidElement(value)) return false;
48929
49568
  if (value instanceof Date) return false;
@@ -49125,9 +49764,9 @@ function SlotContentRenderer({
49125
49764
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
49126
49765
  }
49127
49766
  }
49128
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
49129
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
49130
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
49767
+ const fieldsContract = patterns.getPatternFieldsContract(content.pattern) ?? (patterns.getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
49768
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
49769
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
49131
49770
  }
49132
49771
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
49133
49772
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -49237,7 +49876,7 @@ function UISlotRenderer({
49237
49876
  }
49238
49877
  return wrapped;
49239
49878
  }
49240
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49879
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49241
49880
  var init_UISlotRenderer = __esm({
49242
49881
  "components/core/organisms/UISlotRenderer.tsx"() {
49243
49882
  "use client";
@@ -49268,12 +49907,6 @@ var init_UISlotRenderer = __esm({
49268
49907
  modal: "form",
49269
49908
  drawer: "form"
49270
49909
  };
49271
- FORM_PATTERNS = /* @__PURE__ */ new Set([
49272
- "form",
49273
- "form-section",
49274
- "inline-edit-form",
49275
- "wizard-step"
49276
- ]);
49277
49910
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
49278
49911
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
49279
49912
  "logo",