@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
@@ -6245,27 +6245,61 @@ var init_InfiniteScrollSentinel = __esm({
6245
6245
  InfiniteScrollSentinel.displayName = "InfiniteScrollSentinel";
6246
6246
  }
6247
6247
  });
6248
- function createParticles(count) {
6249
- return Array.from({ length: count }, () => {
6250
- particleIdCounter += 1;
6251
- return {
6252
- id: particleIdCounter,
6253
- color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)],
6254
- left: 30 + Math.random() * 40,
6255
- delay: Math.random() * 300,
6256
- angle: Math.random() * 360,
6257
- distance: 40 + Math.random() * 80,
6258
- rotation: Math.random() * 720 - 360,
6259
- size: 4 + Math.random() * 6
6260
- };
6261
- });
6248
+
6249
+ // components/core/atoms/fx.ts
6250
+ function resolveFxView(item, presets) {
6251
+ const row = presets?.find((p) => p.type === item.type);
6252
+ if (!row) return item;
6253
+ return {
6254
+ ...item,
6255
+ space: item.space ?? row.space,
6256
+ effect: item.effect ?? row.effect,
6257
+ color: item.color ?? row.color,
6258
+ size: item.size ?? row.size,
6259
+ vx: item.vx ?? row.vx,
6260
+ vy: item.vy ?? row.vy,
6261
+ vz: item.vz ?? row.vz,
6262
+ particleCount: item.particleCount ?? row.particleCount,
6263
+ shape: row.shape,
6264
+ count: row.count,
6265
+ glow: row.glow,
6266
+ gravity: row.gravity,
6267
+ color2: row.color2
6268
+ };
6262
6269
  }
6263
- var CONFETTI_COLORS, particleIdCounter, ConfettiEffect;
6264
- var init_ConfettiEffect = __esm({
6265
- "components/core/atoms/ConfettiEffect.tsx"() {
6266
- "use client";
6267
- init_cn();
6268
- init_Box();
6270
+ function fxLifecycle(item, epochNowMs, tickMs) {
6271
+ const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
6272
+ const lifeMs = maxTtl * tickMs;
6273
+ const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
6274
+ const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
6275
+ return { lifeMs, ageMs, progress, fade: 1 - progress };
6276
+ }
6277
+ function fxHash01(seed, salt) {
6278
+ let h = 2166136261 ^ salt;
6279
+ for (let i = 0; i < seed.length; i++) {
6280
+ h ^= seed.charCodeAt(i);
6281
+ h = Math.imul(h, 16777619);
6282
+ }
6283
+ h ^= h >>> 13;
6284
+ h = Math.imul(h, 1274126177);
6285
+ h ^= h >>> 16;
6286
+ return (h >>> 0) / 4294967296;
6287
+ }
6288
+ function createConfettiParticles(count, seed) {
6289
+ return Array.from({ length: count }, (_, i) => ({
6290
+ id: i,
6291
+ color: CONFETTI_COLORS[Math.floor(fxHash01(seed, i * 7 + 1) * CONFETTI_COLORS.length)],
6292
+ left: 30 + fxHash01(seed, i * 7 + 2) * 40,
6293
+ delay: fxHash01(seed, i * 7 + 3) * 300,
6294
+ angle: fxHash01(seed, i * 7 + 4) * 360,
6295
+ distance: 40 + fxHash01(seed, i * 7 + 5) * 80,
6296
+ rotation: fxHash01(seed, i * 7 + 6) * 720 - 360,
6297
+ size: 4 + fxHash01(seed, i * 7 + 7) * 6
6298
+ }));
6299
+ }
6300
+ var CONFETTI_COLORS, CONFETTI_BURST_KEYFRAMES;
6301
+ var init_fx = __esm({
6302
+ "components/core/atoms/fx.ts"() {
6269
6303
  CONFETTI_COLORS = [
6270
6304
  "var(--color-primary)",
6271
6305
  "var(--color-success)",
@@ -6274,7 +6308,30 @@ var init_ConfettiEffect = __esm({
6274
6308
  "gold",
6275
6309
  "dodgerblue"
6276
6310
  ];
6277
- particleIdCounter = 0;
6311
+ CONFETTI_BURST_KEYFRAMES = `
6312
+ @keyframes confetti-burst {
6313
+ 0% {
6314
+ opacity: 1;
6315
+ transform: translate(0, 0) rotate(0deg) scale(1);
6316
+ }
6317
+ 70% {
6318
+ opacity: 1;
6319
+ }
6320
+ 100% {
6321
+ opacity: 0;
6322
+ transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
6323
+ }
6324
+ }
6325
+ `;
6326
+ }
6327
+ });
6328
+ var ConfettiEffect;
6329
+ var init_ConfettiEffect = __esm({
6330
+ "components/core/atoms/ConfettiEffect.tsx"() {
6331
+ "use client";
6332
+ init_cn();
6333
+ init_Box();
6334
+ init_fx();
6278
6335
  ConfettiEffect = ({
6279
6336
  trigger,
6280
6337
  duration = 2e3,
@@ -6283,11 +6340,13 @@ var init_ConfettiEffect = __esm({
6283
6340
  }) => {
6284
6341
  const [particles, setParticles] = React85.useState([]);
6285
6342
  const previousTriggerRef = React85.useRef(false);
6343
+ const burstRef = React85.useRef(0);
6286
6344
  React85.useEffect(() => {
6287
6345
  const wasFalse = !previousTriggerRef.current;
6288
6346
  previousTriggerRef.current = trigger;
6289
6347
  if (trigger && wasFalse) {
6290
- const newParticles = createParticles(particleCount);
6348
+ burstRef.current += 1;
6349
+ const newParticles = createConfettiParticles(particleCount, `confetti-${burstRef.current}`);
6291
6350
  setParticles(newParticles);
6292
6351
  const timer = window.setTimeout(() => {
6293
6352
  setParticles([]);
@@ -6335,21 +6394,7 @@ var init_ConfettiEffect = __esm({
6335
6394
  p.id
6336
6395
  );
6337
6396
  }),
6338
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
6339
- @keyframes confetti-burst {
6340
- 0% {
6341
- opacity: 1;
6342
- transform: translate(0, 0) rotate(0deg) scale(1);
6343
- }
6344
- 70% {
6345
- opacity: 1;
6346
- }
6347
- 100% {
6348
- opacity: 0;
6349
- transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
6350
- }
6351
- }
6352
- ` })
6397
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: CONFETTI_BURST_KEYFRAMES })
6353
6398
  ]
6354
6399
  }
6355
6400
  );
@@ -9798,7 +9843,7 @@ var init_DrawShape = __esm({
9798
9843
  const cx = p.x + (node.offsetX ?? 0) * tw;
9799
9844
  const cy = p.y + (node.offsetY ?? 0) * tw;
9800
9845
  const rx = (node.radiusX ?? 0) * tw;
9801
- const ry = (node.radiusY ?? rx) * tw;
9846
+ const ry = (node.radiusY ?? node.radiusX ?? 0) * tw;
9802
9847
  if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
9803
9848
  if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
9804
9849
  if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
@@ -9988,6 +10033,172 @@ var init_DrawTextLayer = __esm({
9988
10033
  };
9989
10034
  }
9990
10035
  });
10036
+
10037
+ // components/game/molecules/DrawFxLayer.tsx
10038
+ function fxPosition(view, dim, ageSec) {
10039
+ const g = view.gravity ?? 0;
10040
+ const fall = 0.5 * g * ageSec * ageSec;
10041
+ {
10042
+ const base2 = view.position ?? { x: view.x, y: view.z ?? view.y ?? 0 };
10043
+ if (!Number.isFinite(base2.x) || !Number.isFinite(base2.y)) return void 0;
10044
+ return {
10045
+ x: base2.x + (view.vx ?? 0) * ageSec,
10046
+ y: base2.y + (view.vz ?? 0) * ageSec + fall
10047
+ };
10048
+ }
10049
+ }
10050
+ function sparkShape(view, pos, i, fade, progress) {
10051
+ const size = view.size ?? 0.5;
10052
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
10053
+ const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut2(progress);
10054
+ const r = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
10055
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
10056
+ return {
10057
+ type: "draw-shape",
10058
+ shape: "ellipse",
10059
+ position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
10060
+ anchor: "center",
10061
+ radiusX: r,
10062
+ fill: color,
10063
+ blendMode: "lighter",
10064
+ opacity: fade,
10065
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
10066
+ };
10067
+ }
10068
+ function proceduralShapes(view, pos, fade, progress) {
10069
+ const size = view.size ?? 0.5;
10070
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
10071
+ switch (view.shape ?? "spark") {
10072
+ case "ring": {
10073
+ return [
10074
+ {
10075
+ type: "draw-shape",
10076
+ shape: "ellipse",
10077
+ position: pos,
10078
+ anchor: "center",
10079
+ radiusX: size * easeOut2(progress),
10080
+ stroke: view.color2 ?? color,
10081
+ strokeWidth: 2,
10082
+ blendMode: "lighter",
10083
+ opacity: fade,
10084
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
10085
+ }
10086
+ ];
10087
+ }
10088
+ case "puff": {
10089
+ const count = view.count ?? 3;
10090
+ return Array.from({ length: count }, (_, i) => {
10091
+ const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
10092
+ const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut2(progress);
10093
+ return {
10094
+ type: "draw-shape",
10095
+ shape: "ellipse",
10096
+ position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
10097
+ anchor: "center",
10098
+ radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
10099
+ fill: i === 0 && view.color2 ? view.color2 : color,
10100
+ opacity: fade * 0.6,
10101
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
10102
+ };
10103
+ });
10104
+ }
10105
+ case "streak": {
10106
+ const count = view.count ?? 5;
10107
+ return Array.from({ length: count }, (_, i) => {
10108
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
10109
+ const inner = size * (0.2 + 0.8 * easeOut2(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
10110
+ const len = size * 0.35;
10111
+ return {
10112
+ type: "draw-shape",
10113
+ shape: "ellipse",
10114
+ position: pos,
10115
+ anchor: "center",
10116
+ offsetX: inner + len / 2,
10117
+ offsetY: 0,
10118
+ radiusX: len / 2,
10119
+ radiusY: size * 0.04,
10120
+ rotate: angle,
10121
+ fill: color,
10122
+ blendMode: "lighter",
10123
+ opacity: fade,
10124
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
10125
+ };
10126
+ });
10127
+ }
10128
+ default: {
10129
+ const count = view.count ?? 6;
10130
+ return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
10131
+ }
10132
+ }
10133
+ }
10134
+ function expandFxItem(view, node, epochNowMs, dim) {
10135
+ if (view.space === "screen") return [];
10136
+ const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
10137
+ const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
10138
+ if (progress >= 1) return [];
10139
+ const pos = fxPosition(view, dim, ageMs / 1e3);
10140
+ if (!pos) return [];
10141
+ const out = [];
10142
+ const artItems = node.art?.[view.type]?.["idle"];
10143
+ const sprite = node.sprites?.[view.type];
10144
+ if (Array.isArray(artItems)) {
10145
+ out.push({
10146
+ type: "draw-group",
10147
+ position: pos,
10148
+ opacity: fade,
10149
+ ...view.size !== void 0 ? { scale: view.size } : {},
10150
+ items: artItems
10151
+ });
10152
+ } else if (sprite?.url) {
10153
+ const spriteSize = view.size ?? 0.6;
10154
+ out.push({
10155
+ type: "draw-sprite",
10156
+ position: pos,
10157
+ asset: sprite,
10158
+ anchor: "center",
10159
+ width: spriteSize,
10160
+ height: spriteSize,
10161
+ opacity: fade
10162
+ });
10163
+ } else {
10164
+ out.push(...proceduralShapes(view, pos, fade, progress));
10165
+ }
10166
+ if (view.message) {
10167
+ const text = {
10168
+ type: "draw-text",
10169
+ text: view.message,
10170
+ position: pos,
10171
+ offsetY: -(0.15 + 0.55 * progress),
10172
+ color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
10173
+ opacity: fade
10174
+ };
10175
+ out.push(text);
10176
+ }
10177
+ return out;
10178
+ }
10179
+ function expandFxLayer(node, epochNowMs, dim) {
10180
+ if (!Array.isArray(node.items)) return [];
10181
+ const out = [];
10182
+ for (const item of node.items) {
10183
+ if (!item || typeof item.id !== "string") continue;
10184
+ out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim));
10185
+ }
10186
+ return out;
10187
+ }
10188
+ function DrawFxLayer(_props) {
10189
+ return null;
10190
+ }
10191
+ var DEFAULT_TICK_MS, DEFAULT_TEXT_COLOR, DEFAULT_SPARK_COLOR, easeOut2;
10192
+ var init_DrawFxLayer = __esm({
10193
+ "components/game/molecules/DrawFxLayer.tsx"() {
10194
+ "use client";
10195
+ init_fx();
10196
+ DEFAULT_TICK_MS = 500;
10197
+ DEFAULT_TEXT_COLOR = "#ffe066";
10198
+ DEFAULT_SPARK_COLOR = "#ffffff";
10199
+ easeOut2 = (t) => 1 - (1 - t) * (1 - t);
10200
+ }
10201
+ });
9991
10202
  function paintDrawable(painter, node, dctx) {
9992
10203
  switch (node.type) {
9993
10204
  case "draw-sprite":
@@ -10035,6 +10246,11 @@ function paintDrawable(painter, node, dctx) {
10035
10246
  case "draw-text-layer":
10036
10247
  paintTextLayer(painter, node, dctx);
10037
10248
  break;
10249
+ case "draw-fx-layer": {
10250
+ const epochNow = dctx.time > 0 && typeof performance !== "undefined" ? performance.timeOrigin + dctx.time : 0;
10251
+ for (const child of expandFxLayer(node, epochNow, "2d")) paintDrawable(painter, child, dctx);
10252
+ break;
10253
+ }
10038
10254
  }
10039
10255
  }
10040
10256
  var paint2dLog, warnedUnsupported2d, warnUnsupported2d;
@@ -10049,6 +10265,7 @@ var init_paintDispatch = __esm({
10049
10265
  init_DrawSpriteLayer();
10050
10266
  init_DrawShapeLayer();
10051
10267
  init_DrawTextLayer();
10268
+ init_DrawFxLayer();
10052
10269
  paint2dLog = logger.createLogger("almadar:ui:drawable-2d");
10053
10270
  warnedUnsupported2d = /* @__PURE__ */ new Set();
10054
10271
  warnUnsupported2d = (kind) => {
@@ -10297,6 +10514,7 @@ function Canvas2D({
10297
10514
  return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
10298
10515
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
10299
10516
  if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
10517
+ if (node.type === "draw-fx-layer") return Array.isArray(node.items) && node.items.length > 0;
10300
10518
  return false;
10301
10519
  };
10302
10520
  const animRafRef = React85.useRef(0);
@@ -13927,7 +14145,7 @@ var init_AlgoGraphCanvas = __esm({
13927
14145
  }
13928
14146
  const badgeGeoms = [];
13929
14147
  for (const g of nodeGeoms) {
13930
- if (!g.badge) continue;
14148
+ if (!g.badge || g.badge.text === "") continue;
13931
14149
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
13932
14150
  badgeGeoms.push({
13933
14151
  cx: g.x + g.radius * 0.75,
@@ -13993,7 +14211,7 @@ var init_AlgoGraphCanvas = __esm({
13993
14211
  };
13994
14212
  }
13995
14213
  });
13996
- 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;
14214
+ 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;
13997
14215
  var init_AlgorithmCanvas = __esm({
13998
14216
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
13999
14217
  "use client";
@@ -14020,9 +14238,18 @@ var init_AlgorithmCanvas = __esm({
14020
14238
  FRAME_ACTIVE_COLOR = "#3b82f6";
14021
14239
  FRAME_RETURNING_COLOR = "#f59e0b";
14022
14240
  FRAME_DONE_COLOR = "#94a3b8";
14241
+ FRAME_RIM_COLOR = {
14242
+ active: "#1d4ed8",
14243
+ returning: "#b45309",
14244
+ done: "#64748b"
14245
+ };
14023
14246
  FRAME_LABEL_COLOR = "#ffffff";
14024
14247
  FRAME_DETAIL_COLOR = "#e2e8f0";
14025
14248
  FRAME_TWO_LINE_MIN_H = 22;
14249
+ FRAME_STRIP_H = 44;
14250
+ FRAME_GAP = 6;
14251
+ FRAME_BOTTOM_PAD = 8;
14252
+ FRAME_MIN_H = 12;
14026
14253
  BUCKET_INDEX_FILL = "#e2e8f0";
14027
14254
  BUCKET_INDEX_STROKE = "#9ca3af";
14028
14255
  BUCKET_INDEX_TEXT = "#374151";
@@ -14519,14 +14746,18 @@ var init_AlgorithmCanvas = __esm({
14519
14746
  if (frames.length > 0) {
14520
14747
  const panelYFrames = panelY.frames;
14521
14748
  const n = frames.length;
14522
- const frameH = panelHeight / n;
14749
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
14750
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
14751
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
14523
14752
  const x = 8;
14524
14753
  const w = width - 16;
14754
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
14525
14755
  frames.forEach((f3, i) => {
14526
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
14756
+ const y = bottom - i * stride - frameH;
14527
14757
  const state = f3.state ?? "active";
14528
14758
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
14529
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
14759
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
14760
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
14530
14761
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
14531
14762
  out.push({
14532
14763
  type: "text",
@@ -14534,14 +14765,14 @@ var init_AlgorithmCanvas = __esm({
14534
14765
  y: y + frameH * 0.35,
14535
14766
  text: f3.label,
14536
14767
  color: FRAME_LABEL_COLOR,
14537
- fontSize: 10,
14768
+ fontSize: 11,
14538
14769
  align: "left"
14539
14770
  });
14540
14771
  if (f3.detail) {
14541
14772
  out.push({
14542
14773
  type: "text",
14543
14774
  x: 16,
14544
- y: y + frameH * 0.7,
14775
+ y: y + frameH * 0.72,
14545
14776
  text: f3.detail,
14546
14777
  color: FRAME_DETAIL_COLOR,
14547
14778
  fontSize: 10,
@@ -21004,8 +21235,6 @@ var init_ButtonGroup = __esm({
21004
21235
  ButtonGroup.displayName = "ButtonGroup";
21005
21236
  }
21006
21237
  });
21007
-
21008
- // lib/getNestedValue.ts
21009
21238
  function getNestedValue(obj, path) {
21010
21239
  if (obj === null || obj === void 0 || !path) {
21011
21240
  return void 0;
@@ -21026,6 +21255,15 @@ function getNestedValue(obj, path) {
21026
21255
  }
21027
21256
  return value;
21028
21257
  }
21258
+ function resolveImageUrl(value) {
21259
+ if (typeof value === "string") {
21260
+ return value;
21261
+ }
21262
+ if (core.isFileValue(value)) {
21263
+ return value.url;
21264
+ }
21265
+ return void 0;
21266
+ }
21029
21267
  var init_getNestedValue = __esm({
21030
21268
  "lib/getNestedValue.ts"() {
21031
21269
  }
@@ -21663,8 +21901,8 @@ var init_CardGrid = __esm({
21663
21901
  actionPayload: { row: itemData },
21664
21902
  children: [
21665
21903
  imageField && (() => {
21666
- const imgUrl = getNestedValue(itemData, imageField);
21667
- if (!imgUrl || typeof imgUrl !== "string") return null;
21904
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
21905
+ if (!imgUrl) return null;
21668
21906
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
21669
21907
  "img",
21670
21908
  {
@@ -25680,8 +25918,8 @@ function DataGrid({
25680
25918
  ),
25681
25919
  children: [
25682
25920
  imageField && (() => {
25683
- const imgUrl = getNestedValue(itemData, imageField);
25684
- if (!imgUrl || typeof imgUrl !== "string") return null;
25921
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
25922
+ if (!imgUrl) return null;
25685
25923
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
25686
25924
  "img",
25687
25925
  {
@@ -27671,6 +27909,196 @@ var init_PageTransition = __esm({
27671
27909
  PageTransition.displayName = "PageTransition";
27672
27910
  }
27673
27911
  });
27912
+ function ConfettiBurst({ item, lifeMs }) {
27913
+ const particles = createConfettiParticles(item.particleCount ?? 30, item.id);
27914
+ const left = (item.x ?? 0.5) * 100;
27915
+ const top = (item.z ?? item.y ?? 0.4) * 100;
27916
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: particles.map((p) => {
27917
+ const rad = p.angle * Math.PI / 180;
27918
+ const tx = Math.cos(rad) * p.distance * (item.size ?? 1);
27919
+ const ty = Math.sin(rad) * p.distance * (item.size ?? 1) - 20;
27920
+ return /* @__PURE__ */ jsxRuntime.jsx(
27921
+ Box,
27922
+ {
27923
+ className: "absolute rounded-sm",
27924
+ style: {
27925
+ left: (p.left - 50) * 2,
27926
+ top: -10,
27927
+ width: p.size,
27928
+ height: p.size,
27929
+ backgroundColor: item.color ?? p.color,
27930
+ animation: `confetti-burst ${Math.max(lifeMs - p.delay, 200)}ms ease-out ${p.delay}ms forwards`,
27931
+ opacity: 0,
27932
+ "--confetti-tx": `${tx}px`,
27933
+ "--confetti-ty": `${ty}px`,
27934
+ "--confetti-rotate": `${p.rotation}deg`
27935
+ }
27936
+ },
27937
+ p.id
27938
+ );
27939
+ }) });
27940
+ }
27941
+ function SparkleBurst({ item, lifeMs }) {
27942
+ const count = item.particleCount ?? 8;
27943
+ const scale = item.size ?? 1;
27944
+ const left = (item.x ?? 0.5) * 100;
27945
+ const top = (item.z ?? item.y ?? 0.4) * 100;
27946
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: Array.from({ length: count }, (_, i) => {
27947
+ const dx = (fxHash01(item.id, i * 4) - 0.5) * 160 * scale;
27948
+ const dy = (fxHash01(item.id, i * 4 + 1) - 0.5) * 120 * scale;
27949
+ const dot = (4 + fxHash01(item.id, i * 4 + 2) * 5) * scale;
27950
+ const delay = fxHash01(item.id, i * 4 + 3) * lifeMs * 0.4;
27951
+ return /* @__PURE__ */ jsxRuntime.jsx(
27952
+ Box,
27953
+ {
27954
+ className: "absolute rounded-full",
27955
+ style: {
27956
+ left: dx,
27957
+ top: dy,
27958
+ width: dot,
27959
+ height: dot,
27960
+ backgroundColor: item.color ?? "gold",
27961
+ opacity: 0,
27962
+ animation: `fx-overlay-sparkle ${Math.max(lifeMs - delay, 200)}ms ease-in-out ${delay}ms forwards`
27963
+ }
27964
+ },
27965
+ i
27966
+ );
27967
+ }) });
27968
+ }
27969
+ function OverlayFxNode({ item, tickMs }) {
27970
+ const lifeMs = lifeMsOf(item, tickMs);
27971
+ switch (item.effect ?? "sparkle") {
27972
+ case "confetti":
27973
+ return /* @__PURE__ */ jsxRuntime.jsx(ConfettiBurst, { item, lifeMs });
27974
+ case "flash":
27975
+ return /* @__PURE__ */ jsxRuntime.jsx(
27976
+ Box,
27977
+ {
27978
+ position: "absolute",
27979
+ className: "inset-0",
27980
+ style: {
27981
+ backgroundColor: item.color ?? "#ffffff",
27982
+ opacity: 0,
27983
+ animation: `fx-overlay-flash ${lifeMs}ms ease-out forwards`
27984
+ }
27985
+ }
27986
+ );
27987
+ case "streak-glow":
27988
+ return /* @__PURE__ */ jsxRuntime.jsx(
27989
+ Box,
27990
+ {
27991
+ position: "absolute",
27992
+ className: "inset-0",
27993
+ style: {
27994
+ boxShadow: `inset 0 0 ${60 * (item.size ?? 1)}px ${item.color ?? "var(--color-warning)"}`,
27995
+ opacity: 0,
27996
+ animation: `fx-overlay-glow ${lifeMs}ms ease-in-out forwards`
27997
+ }
27998
+ }
27999
+ );
28000
+ case "float-text": {
28001
+ if (!item.message) return null;
28002
+ return /* @__PURE__ */ jsxRuntime.jsx(
28003
+ Box,
28004
+ {
28005
+ position: "absolute",
28006
+ style: {
28007
+ left: `${(item.x ?? 0.5) * 100}%`,
28008
+ top: `${(item.z ?? item.y ?? 0.4) * 100}%`,
28009
+ color: item.color ?? "var(--color-success)",
28010
+ fontWeight: 700,
28011
+ fontSize: 16 * (item.size ?? 1),
28012
+ textShadow: "0 1px 2px rgba(0,0,0,0.4)",
28013
+ opacity: 0,
28014
+ animation: `fx-overlay-float ${lifeMs}ms ease-out forwards`,
28015
+ whiteSpace: "nowrap"
28016
+ },
28017
+ children: item.message
28018
+ }
28019
+ );
28020
+ }
28021
+ case "shake":
28022
+ return null;
28023
+ default:
28024
+ return /* @__PURE__ */ jsxRuntime.jsx(SparkleBurst, { item, lifeMs });
28025
+ }
28026
+ }
28027
+ var DEFAULT_TICK_MS2, FX_OVERLAY_KEYFRAMES, lifeMsOf, FxOverlay;
28028
+ var init_FxOverlay = __esm({
28029
+ "components/core/molecules/FxOverlay.tsx"() {
28030
+ "use client";
28031
+ init_cn();
28032
+ init_Box();
28033
+ init_fx();
28034
+ DEFAULT_TICK_MS2 = 500;
28035
+ FX_OVERLAY_KEYFRAMES = `
28036
+ ${CONFETTI_BURST_KEYFRAMES}
28037
+ @keyframes fx-overlay-flash {
28038
+ 0% { opacity: 0.75; }
28039
+ 100% { opacity: 0; }
28040
+ }
28041
+ @keyframes fx-overlay-glow {
28042
+ 0% { opacity: 0.9; }
28043
+ 60% { opacity: 0.6; }
28044
+ 100% { opacity: 0; }
28045
+ }
28046
+ @keyframes fx-overlay-sparkle {
28047
+ 0% { opacity: 0; transform: scale(0); }
28048
+ 30% { opacity: 1; transform: scale(1); }
28049
+ 100% { opacity: 0; transform: scale(0.3); }
28050
+ }
28051
+ @keyframes fx-overlay-float {
28052
+ 0% { opacity: 0; transform: translate(-50%, 8px); }
28053
+ 15% { opacity: 1; }
28054
+ 100% { opacity: 0; transform: translate(-50%, -40px); }
28055
+ }
28056
+ @keyframes fx-overlay-shake {
28057
+ 0% { transform: translateX(0); }
28058
+ 15% { transform: translateX(-6px); }
28059
+ 30% { transform: translateX(5px); }
28060
+ 45% { transform: translateX(-4px); }
28061
+ 60% { transform: translateX(3px); }
28062
+ 75% { transform: translateX(-2px); }
28063
+ 100% { transform: translateX(0); }
28064
+ }
28065
+ `;
28066
+ lifeMsOf = (it, tickMs) => Math.max(it.maxTtl ?? it.ttl, it.ttl, 1) * tickMs;
28067
+ FxOverlay = ({ items, tickMs = DEFAULT_TICK_MS2, children, className }) => {
28068
+ const screenItems = (Array.isArray(items) ? items : []).filter(
28069
+ (it) => Boolean(it) && typeof it.id === "string" && it.space !== "world"
28070
+ );
28071
+ const shakeItem = [...screenItems].reverse().find((it) => it.effect === "shake");
28072
+ const overlay = /* @__PURE__ */ jsxRuntime.jsxs(
28073
+ Box,
28074
+ {
28075
+ position: "absolute",
28076
+ className: cn("inset-0 pointer-events-none overflow-hidden z-50", !children && className),
28077
+ "aria-hidden": "true",
28078
+ children: [
28079
+ screenItems.map((it) => /* @__PURE__ */ jsxRuntime.jsx(OverlayFxNode, { item: it, tickMs }, it.id)),
28080
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: FX_OVERLAY_KEYFRAMES })
28081
+ ]
28082
+ }
28083
+ );
28084
+ if (children !== void 0 && children !== null) {
28085
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { position: "relative", className, children: [
28086
+ /* @__PURE__ */ jsxRuntime.jsx(
28087
+ Box,
28088
+ {
28089
+ style: shakeItem ? { animation: `fx-overlay-shake ${Math.min(lifeMsOf(shakeItem, tickMs), 600)}ms ease-in-out` } : void 0,
28090
+ children
28091
+ },
28092
+ shakeItem?.id ?? "steady"
28093
+ ),
28094
+ overlay
28095
+ ] });
28096
+ }
28097
+ return overlay;
28098
+ };
28099
+ FxOverlay.displayName = "FxOverlay";
28100
+ }
28101
+ });
27674
28102
  function computePopoverStyle(position, triggerRect, popoverWidth) {
27675
28103
  if (position === "left" || position === "right") {
27676
28104
  return {
@@ -28227,7 +28655,7 @@ var init_SearchInput = __esm({
28227
28655
  /* @__PURE__ */ jsxRuntime.jsx(
28228
28656
  Input,
28229
28657
  {
28230
- type: "text",
28658
+ type: "search",
28231
28659
  value: searchValue,
28232
28660
  onChange: handleChange,
28233
28661
  placeholder: resolvedPlaceholder,
@@ -29553,6 +29981,7 @@ var MathCanvas;
29553
29981
  var init_MathCanvas = __esm({
29554
29982
  "components/learning/molecules/MathCanvas.tsx"() {
29555
29983
  "use client";
29984
+ init_useEventBus();
29556
29985
  init_atoms();
29557
29986
  init_Stack();
29558
29987
  init_LearningCanvas();
@@ -29584,9 +30013,32 @@ var init_MathCanvas = __esm({
29584
30013
  interactive = false,
29585
30014
  animate = false,
29586
30015
  onShapeClick,
30016
+ keyMap,
30017
+ keyUpMap,
29587
30018
  isLoading,
29588
30019
  error
29589
30020
  }) => {
30021
+ const eventBus = useEventBus();
30022
+ React85.useEffect(() => {
30023
+ if (!keyMap && !keyUpMap) return;
30024
+ const onDown = (e) => {
30025
+ const ev = keyMap?.[e.code];
30026
+ if (ev) {
30027
+ eventBus.emit(`UI:${ev}`, {});
30028
+ e.preventDefault();
30029
+ }
30030
+ };
30031
+ const onUp = (e) => {
30032
+ const ev = keyUpMap?.[e.code];
30033
+ if (ev) eventBus.emit(`UI:${ev}`, {});
30034
+ };
30035
+ window.addEventListener("keydown", onDown);
30036
+ window.addEventListener("keyup", onUp);
30037
+ return () => {
30038
+ window.removeEventListener("keydown", onDown);
30039
+ window.removeEventListener("keyup", onUp);
30040
+ };
30041
+ }, [keyMap, keyUpMap, eventBus]);
29590
30042
  const derivedShapes = React85.useMemo(() => {
29591
30043
  const out = [];
29592
30044
  const margin = 24;
@@ -29599,11 +30051,11 @@ var init_MathCanvas = __esm({
29599
30051
  if (showGrid) {
29600
30052
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
29601
30053
  const px = mapX(x);
29602
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
30054
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
29603
30055
  }
29604
30056
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
29605
30057
  const py = mapY(y);
29606
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
30058
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
29607
30059
  }
29608
30060
  }
29609
30061
  if (showTickLabels) {
@@ -29652,7 +30104,7 @@ var init_MathCanvas = __esm({
29652
30104
  x: mapX((first.x + last.x) / 2),
29653
30105
  y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
29654
30106
  text: region.label,
29655
- color: "#111827",
30107
+ color,
29656
30108
  fontSize: 11
29657
30109
  });
29658
30110
  }
@@ -29685,14 +30137,14 @@ var init_MathCanvas = __esm({
29685
30137
  const px = mapX(guide.at);
29686
30138
  out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
29687
30139
  if (guide.label) {
29688
- out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color: "#111827", fontSize: 11 });
30140
+ out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
29689
30141
  }
29690
30142
  } else {
29691
30143
  if (guide.at < yMin || guide.at > yMax) continue;
29692
30144
  const py = mapY(guide.at);
29693
30145
  out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
29694
30146
  if (guide.label) {
29695
- out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color: "#111827", fontSize: 11, align: "right" });
30147
+ out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: 11, align: "right" });
29696
30148
  }
29697
30149
  }
29698
30150
  }
@@ -29758,7 +30210,7 @@ var init_MathCanvas = __esm({
29758
30210
  x: (x1 + x2) / 2,
29759
30211
  y: xAxisY - peak - 8,
29760
30212
  text: hop.label,
29761
- color: "#111827",
30213
+ color,
29762
30214
  fontSize: 10,
29763
30215
  align: "center"
29764
30216
  });
@@ -29785,7 +30237,7 @@ var init_MathCanvas = __esm({
29785
30237
  x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
29786
30238
  y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
29787
30239
  text: angle.label,
29788
- color: "#111827",
30240
+ color,
29789
30241
  fontSize: 11,
29790
30242
  align: "center"
29791
30243
  });
@@ -29803,7 +30255,7 @@ var init_MathCanvas = __esm({
29803
30255
  fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
29804
30256
  });
29805
30257
  if (p.label) {
29806
- out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
30258
+ out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: 12 });
29807
30259
  }
29808
30260
  }
29809
30261
  for (const v of vectors) {
@@ -29814,7 +30266,7 @@ var init_MathCanvas = __esm({
29814
30266
  const y2 = mapY(v.y + v.vy);
29815
30267
  out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
29816
30268
  if (v.label) {
29817
- out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
30269
+ out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: 12 });
29818
30270
  }
29819
30271
  }
29820
30272
  out.push(...shapes);
@@ -31078,13 +31530,13 @@ var init_MapView = __esm({
31078
31530
  shadowSize: [41, 41]
31079
31531
  });
31080
31532
  L.Marker.prototype.options.icon = defaultIcon;
31081
- const { useEffect: useEffect60, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__namespace.default;
31533
+ const { useEffect: useEffect61, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__namespace.default;
31082
31534
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31083
31535
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31084
31536
  function MapUpdater({ centerLat, centerLng, zoom }) {
31085
31537
  const map = useMap();
31086
31538
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31087
- useEffect60(() => {
31539
+ useEffect61(() => {
31088
31540
  const prev = prevRef.current;
31089
31541
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31090
31542
  map.setView([centerLat, centerLng], zoom);
@@ -31095,7 +31547,7 @@ var init_MapView = __esm({
31095
31547
  }
31096
31548
  function MapClickHandler({ onMapClick }) {
31097
31549
  const map = useMap();
31098
- useEffect60(() => {
31550
+ useEffect61(() => {
31099
31551
  if (!onMapClick) return;
31100
31552
  const handler = (e) => {
31101
31553
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -37773,6 +38225,7 @@ var init_PageHeader = __esm({
37773
38225
  className
37774
38226
  }) => {
37775
38227
  const eventBus = useEventBus();
38228
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
37776
38229
  const handleBack = () => {
37777
38230
  eventBus.emit(`UI:${backEvent}`);
37778
38231
  };
@@ -37820,15 +38273,15 @@ var init_PageHeader = __esm({
37820
38273
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
37821
38274
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3", children: [
37822
38275
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
37823
- status && /* @__PURE__ */ jsxRuntime.jsx(
38276
+ statusBadge && /* @__PURE__ */ jsxRuntime.jsx(
37824
38277
  Typography,
37825
38278
  {
37826
38279
  variant: "small",
37827
38280
  className: cn(
37828
38281
  "px-2.5 py-1 rounded-full text-xs font-medium",
37829
- statusColors2[status.variant || "default"]
38282
+ statusColors2[statusBadge.variant || "default"]
37830
38283
  ),
37831
- children: status.label
38284
+ children: statusBadge.label
37832
38285
  }
37833
38286
  )
37834
38287
  ] }),
@@ -41057,7 +41510,7 @@ function formatFieldValue2(value, fieldName) {
41057
41510
  }
41058
41511
  return String(value);
41059
41512
  }
41060
- function renderRichFieldValue(value, fieldName, fieldType) {
41513
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41061
41514
  if (value === void 0 || value === null) return "\u2014";
41062
41515
  const str = String(value);
41063
41516
  switch (fieldType) {
@@ -41124,19 +41577,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
41124
41577
  }
41125
41578
  );
41126
41579
  case "date":
41127
- case "datetime": {
41580
+ case "datetime":
41581
+ case "timestamp": {
41128
41582
  const d = new Date(str);
41129
41583
  if (!isNaN(d.getTime())) {
41130
41584
  return d.toLocaleDateString(void 0, {
41131
41585
  year: "numeric",
41132
41586
  month: "long",
41133
41587
  day: "numeric",
41134
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
41588
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
41135
41589
  });
41136
41590
  }
41137
41591
  return str;
41138
41592
  }
41593
+ case "money": {
41594
+ const n = typeof value === "number" ? value : Number(str);
41595
+ if (!isNaN(n)) {
41596
+ return new Intl.NumberFormat(void 0, {
41597
+ style: "currency",
41598
+ currency: "USD"
41599
+ }).format(n);
41600
+ }
41601
+ return str;
41602
+ }
41603
+ case "relation": {
41604
+ const match = meta?.options?.find((opt) => opt.value === str);
41605
+ return match ? match.label : str;
41606
+ }
41607
+ case "file": {
41608
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
41609
+ const file = value;
41610
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
41611
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
41612
+ return /* @__PURE__ */ jsxRuntime.jsxs(
41613
+ "a",
41614
+ {
41615
+ href: typeof file.url === "string" ? file.url : void 0,
41616
+ download: label,
41617
+ 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",
41618
+ children: [
41619
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.FileText, size: "sm", className: "text-muted-foreground" }),
41620
+ label,
41621
+ size && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
41622
+ ]
41623
+ }
41624
+ );
41625
+ }
41626
+ return str;
41627
+ }
41139
41628
  default:
41629
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
41630
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
41631
+ }
41140
41632
  return formatFieldValue2(value, fieldName);
41141
41633
  }
41142
41634
  }
@@ -41163,8 +41655,18 @@ function buildFieldTypeMap(fields) {
41163
41655
  const map = {};
41164
41656
  if (!fields) return map;
41165
41657
  for (const f3 of fields) {
41166
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
41167
- map[f3.name] = f3.type;
41658
+ if (typeof f3 === "object" && f3.type !== void 0) {
41659
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
41660
+ }
41661
+ }
41662
+ return map;
41663
+ }
41664
+ function buildFieldMetaMap(fields) {
41665
+ const map = {};
41666
+ if (!fields) return map;
41667
+ for (const f3 of fields) {
41668
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
41669
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
41168
41670
  }
41169
41671
  }
41170
41672
  return map;
@@ -41184,6 +41686,7 @@ var init_DetailPanel = __esm({
41184
41686
  init_format();
41185
41687
  init_getNestedValue();
41186
41688
  init_useEventBus();
41689
+ init_UploadDropZone();
41187
41690
  formatFieldLabel = humanizeFieldName;
41188
41691
  ReactMarkdown2 = React85.lazy(() => import('react-markdown'));
41189
41692
  DetailPanel = ({
@@ -41193,6 +41696,7 @@ var init_DetailPanel = __esm({
41193
41696
  avatar,
41194
41697
  sections: propSections,
41195
41698
  actions,
41699
+ backAction,
41196
41700
  footer,
41197
41701
  slideOver = false,
41198
41702
  className,
@@ -41201,7 +41705,8 @@ var init_DetailPanel = __esm({
41201
41705
  fieldNames,
41202
41706
  initialData,
41203
41707
  isLoading = false,
41204
- error
41708
+ error,
41709
+ relationsData
41205
41710
  }) => {
41206
41711
  const eventBus = useEventBus();
41207
41712
  const { t } = hooks.useTranslate();
@@ -41212,8 +41717,15 @@ var init_DetailPanel = __esm({
41212
41717
  };
41213
41718
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
41214
41719
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
41720
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
41215
41721
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
41216
41722
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
41723
+ const metaFor = (field) => {
41724
+ const base = fieldMetaMap[field];
41725
+ const options = relationsData?.[field];
41726
+ if (!base && !options) return void 0;
41727
+ return { ...base, options };
41728
+ };
41217
41729
  const handleActionClick = React85.useCallback(
41218
41730
  (action, data2) => {
41219
41731
  if (action.navigatesTo) {
@@ -41292,7 +41804,7 @@ var init_DetailPanel = __esm({
41292
41804
  if (value !== void 0 && value !== null) {
41293
41805
  overviewFields.push({
41294
41806
  label: labelFor(field),
41295
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41807
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41296
41808
  icon: getFieldIcon(field)
41297
41809
  });
41298
41810
  }
@@ -41308,7 +41820,7 @@ var init_DetailPanel = __esm({
41308
41820
  if (value !== void 0 && value !== null) {
41309
41821
  metricsFields.push({
41310
41822
  label: labelFor(field),
41311
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41823
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41312
41824
  icon: getFieldIcon(field)
41313
41825
  });
41314
41826
  }
@@ -41324,7 +41836,7 @@ var init_DetailPanel = __esm({
41324
41836
  if (value !== void 0 && value !== null) {
41325
41837
  timelineFields.push({
41326
41838
  label: labelFor(field),
41327
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41839
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41328
41840
  icon: getFieldIcon(field)
41329
41841
  });
41330
41842
  }
@@ -41340,7 +41852,7 @@ var init_DetailPanel = __esm({
41340
41852
  if (value !== void 0 && value !== null) {
41341
41853
  descFields.push({
41342
41854
  label: labelFor(field),
41343
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41855
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41344
41856
  icon: getFieldIcon(field)
41345
41857
  });
41346
41858
  }
@@ -41388,7 +41900,7 @@ var init_DetailPanel = __esm({
41388
41900
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
41389
41901
  allFields.push({
41390
41902
  label: labelFor(field),
41391
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41903
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41392
41904
  icon: getFieldIcon(field)
41393
41905
  });
41394
41906
  } else {
@@ -41403,34 +41915,49 @@ var init_DetailPanel = __esm({
41403
41915
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
41404
41916
  const effectiveCloseAction = closeAction ?? { event: void 0};
41405
41917
  const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
41406
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41407
- otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
41408
- Button,
41409
- {
41410
- variant: action.variant || "secondary",
41411
- size: "sm",
41412
- action: action.navigatesTo ? void 0 : action.event,
41413
- actionPayload: { row: normalizedData },
41414
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
41415
- icon: action.icon,
41416
- "data-testid": action.event ? `action-${action.event}` : void 0,
41417
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
41418
- children: action.label
41419
- },
41420
- idx
41421
- )),
41422
- /* @__PURE__ */ jsxRuntime.jsx(
41918
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
41919
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
41423
41920
  Button,
41424
41921
  {
41425
- variant: "ghost",
41922
+ variant: backAction.variant || "ghost",
41426
41923
  size: "sm",
41427
- action: effectiveCloseAction.event,
41924
+ action: backAction.navigatesTo ? void 0 : backAction.event,
41428
41925
  actionPayload: { row: normalizedData },
41429
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
41430
- icon: LucideIcons2.X,
41431
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
41926
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
41927
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
41928
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
41929
+ children: backAction.label
41432
41930
  }
41433
- )
41931
+ ) }),
41932
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41933
+ otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
41934
+ Button,
41935
+ {
41936
+ variant: action.variant || "secondary",
41937
+ size: "sm",
41938
+ action: action.navigatesTo ? void 0 : action.event,
41939
+ actionPayload: { row: normalizedData },
41940
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
41941
+ icon: action.icon,
41942
+ "data-testid": action.event ? `action-${action.event}` : void 0,
41943
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
41944
+ children: action.label
41945
+ },
41946
+ idx
41947
+ )),
41948
+ /* @__PURE__ */ jsxRuntime.jsx(
41949
+ Button,
41950
+ {
41951
+ variant: "ghost",
41952
+ size: "sm",
41953
+ action: effectiveCloseAction.event,
41954
+ actionPayload: { row: normalizedData },
41955
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
41956
+ icon: LucideIcons2.X,
41957
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
41958
+ }
41959
+ )
41960
+ ] })
41434
41961
  ] }),
41435
41962
  avatar,
41436
41963
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -41745,6 +42272,10 @@ function determineInputType(field) {
41745
42272
  return "password";
41746
42273
  case "url":
41747
42274
  return "url";
42275
+ case "file":
42276
+ return "file";
42277
+ case "money":
42278
+ return "currency";
41748
42279
  case "number":
41749
42280
  case "integer":
41750
42281
  case "float":
@@ -41806,6 +42337,7 @@ var init_Form = __esm({
41806
42337
  init_Typography();
41807
42338
  init_Icon();
41808
42339
  init_RelationSelect();
42340
+ init_UploadDropZone();
41809
42341
  init_Alert();
41810
42342
  init_useEventBus();
41811
42343
  init_debug();
@@ -41848,6 +42380,7 @@ var init_Form = __esm({
41848
42380
  cancelEvent = "CANCEL",
41849
42381
  relationsData = {},
41850
42382
  relationsLoading = {},
42383
+ fieldOverrides,
41851
42384
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
41852
42385
  conditionalFields: conditionalFieldsRaw = {},
41853
42386
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -42094,7 +42627,8 @@ var init_Form = __esm({
42094
42627
  label,
42095
42628
  field.required && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
42096
42629
  ] }),
42097
- renderFieldInput(field, fieldName, inputType, currentValue, label)
42630
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
42631
+ field.hint && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
42098
42632
  ] }, fieldName);
42099
42633
  },
42100
42634
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -42110,6 +42644,7 @@ var init_Form = __esm({
42110
42644
  name: field,
42111
42645
  type: entityField.type,
42112
42646
  required: entityField.required,
42647
+ hint: entityField.description,
42113
42648
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
42114
42649
  defaultValue: entityField.default,
42115
42650
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -42122,8 +42657,18 @@ var init_Form = __esm({
42122
42657
  return { name: field, type: "string" };
42123
42658
  }
42124
42659
  return field;
42660
+ }).map((field) => {
42661
+ const fieldName = field.name || field.field;
42662
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
42663
+ if (!override) return field;
42664
+ return {
42665
+ ...field,
42666
+ ...override.label !== void 0 ? { label: override.label } : {},
42667
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
42668
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
42669
+ };
42125
42670
  });
42126
- }, [effectiveFields, resolvedEntity]);
42671
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
42127
42672
  const schemaFields = React85__namespace.default.useMemo(() => {
42128
42673
  if (normalizedFields.length === 0) return null;
42129
42674
  if (isDebugEnabled()) {
@@ -42247,6 +42792,44 @@ var init_Form = __esm({
42247
42792
  max: field.max
42248
42793
  }
42249
42794
  );
42795
+ case "currency":
42796
+ return /* @__PURE__ */ jsxRuntime.jsx(
42797
+ Input,
42798
+ {
42799
+ ...commonProps,
42800
+ type: "number",
42801
+ step: "0.01",
42802
+ icon: LucideIcons2.DollarSign,
42803
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
42804
+ onChange: (e) => handleChange(
42805
+ fieldName,
42806
+ e.target.value ? Number(e.target.value) : void 0
42807
+ ),
42808
+ min: field.min,
42809
+ max: field.max
42810
+ }
42811
+ );
42812
+ case "file":
42813
+ return /* @__PURE__ */ jsxRuntime.jsx(
42814
+ UploadDropZone,
42815
+ {
42816
+ accept: field.pattern,
42817
+ maxFiles: 1,
42818
+ disabled: isLoading,
42819
+ onFiles: (files) => {
42820
+ const f3 = files[0];
42821
+ if (!f3) return;
42822
+ const reader = new FileReader();
42823
+ reader.onload = () => handleChange(fieldName, {
42824
+ name: f3.name,
42825
+ mimeType: f3.type,
42826
+ sizeBytes: f3.size,
42827
+ url: typeof reader.result === "string" ? reader.result : ""
42828
+ });
42829
+ reader.readAsDataURL(f3);
42830
+ }
42831
+ }
42832
+ );
42250
42833
  case "date":
42251
42834
  return /* @__PURE__ */ jsxRuntime.jsx(
42252
42835
  Input,
@@ -43279,6 +43862,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
43279
43862
  var init_MediaGallery = __esm({
43280
43863
  "components/core/organisms/MediaGallery.tsx"() {
43281
43864
  "use client";
43865
+ init_getNestedValue();
43282
43866
  init_cn();
43283
43867
  init_atoms();
43284
43868
  init_Stack();
@@ -43347,9 +43931,9 @@ var init_MediaGallery = __esm({
43347
43931
  return entityData.map((record, idx) => {
43348
43932
  return {
43349
43933
  id: String(record.id ?? idx),
43350
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
43934
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
43351
43935
  alt: record.alt ? String(record.alt) : void 0,
43352
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
43936
+ thumbnail: resolveImageUrl(record.thumbnail),
43353
43937
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
43354
43938
  };
43355
43939
  });
@@ -47236,6 +47820,7 @@ var init_component_registry_generated = __esm({
47236
47820
  init_DocSidebar();
47237
47821
  init_DocTOC();
47238
47822
  init_DocumentViewer();
47823
+ init_DrawFxLayer();
47239
47824
  init_DrawGroup();
47240
47825
  init_DrawMesh();
47241
47826
  init_DrawShape();
@@ -47266,6 +47851,7 @@ var init_component_registry_generated = __esm({
47266
47851
  init_FormField();
47267
47852
  init_FormSection();
47268
47853
  init_FormSectionHeader();
47854
+ init_FxOverlay();
47269
47855
  init_GameAudioToggle();
47270
47856
  init_GameHud();
47271
47857
  init_GameIcon();
@@ -47506,6 +48092,7 @@ var init_component_registry_generated = __esm({
47506
48092
  "DocSidebar": DocSidebar,
47507
48093
  "DocTOC": DocTOC,
47508
48094
  "DocumentViewer": DocumentViewer,
48095
+ "DrawFxLayer": DrawFxLayer,
47509
48096
  "DrawGroup": DrawGroup,
47510
48097
  "DrawMesh": DrawMesh,
47511
48098
  "DrawShape": DrawShape,
@@ -47536,6 +48123,7 @@ var init_component_registry_generated = __esm({
47536
48123
  "FormField": FormField,
47537
48124
  "FormLayout": FormLayout,
47538
48125
  "FormSectionHeader": FormSectionHeader,
48126
+ "FxOverlay": FxOverlay,
47539
48127
  "GameAudioToggle": GameAudioToggle,
47540
48128
  "GameHud": GameHud,
47541
48129
  "GameIcon": GameIcon,
@@ -47711,7 +48299,8 @@ __export(UISlotRenderer_exports, {
47711
48299
  SlotContentRenderer: () => SlotContentRenderer,
47712
48300
  SuspenseConfigProvider: () => SuspenseConfigProvider,
47713
48301
  UISlotComponent: () => UISlotComponent,
47714
- UISlotRenderer: () => UISlotRenderer
48302
+ UISlotRenderer: () => UISlotRenderer,
48303
+ renderPatternValue: () => renderPatternValue
47715
48304
  });
47716
48305
  function SuspenseConfigProvider({
47717
48306
  config,
@@ -47747,6 +48336,9 @@ function enrichFormFields(fields, entityDef) {
47747
48336
  type: entityField.type,
47748
48337
  required: entityField.required ?? false
47749
48338
  };
48339
+ if (entityField.description) {
48340
+ enriched.hint = entityField.description;
48341
+ }
47750
48342
  if (entityField.values && entityField.values.length > 0) {
47751
48343
  enriched.values = entityField.values;
47752
48344
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -47770,6 +48362,9 @@ function enrichFormFields(fields, entityDef) {
47770
48362
  if (entityField.required && !("required" in obj)) {
47771
48363
  enriched.required = true;
47772
48364
  }
48365
+ if (entityField.description && !obj.hint && !obj.help) {
48366
+ enriched.hint = entityField.description;
48367
+ }
47773
48368
  if (!obj.values && !obj.options) {
47774
48369
  if (entityField.values && entityField.values.length > 0) {
47775
48370
  enriched.values = entityField.values;
@@ -47785,6 +48380,32 @@ function enrichFormFields(fields, entityDef) {
47785
48380
  return field;
47786
48381
  });
47787
48382
  }
48383
+ function enrichDetailFields(fields, entityDef) {
48384
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
48385
+ const metaFor = (name) => {
48386
+ const entityField = fieldMap.get(name);
48387
+ if (!entityField) return void 0;
48388
+ const meta = { type: entityField.type };
48389
+ const values = entityField.values ?? entityField.enumValues;
48390
+ if (values && values.length > 0) meta.values = values;
48391
+ if (entityField.relation) meta.relation = entityField.relation.entity;
48392
+ return meta;
48393
+ };
48394
+ return fields.map((field) => {
48395
+ if (typeof field === "string") {
48396
+ const meta = metaFor(field);
48397
+ return meta ? { key: field, ...meta } : field;
48398
+ }
48399
+ if (field && typeof field === "object" && !Array.isArray(field) && !React85__namespace.default.isValidElement(field) && !(field instanceof Date)) {
48400
+ const obj = field;
48401
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
48402
+ if (!fieldName || obj.type) return field;
48403
+ const meta = metaFor(fieldName);
48404
+ return meta ? { ...obj, ...meta } : field;
48405
+ }
48406
+ return field;
48407
+ });
48408
+ }
47788
48409
  function renderContainedPortal(t, slot, content, onDismiss) {
47789
48410
  const slotContent = /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content, onDismiss }) });
47790
48411
  const slotId = `slot-${slot}`;
@@ -48294,6 +48915,24 @@ function isPatternConfig(value) {
48294
48915
  const record = value;
48295
48916
  return "type" in record && typeof record.type === "string" && patterns.getComponentForPattern(record.type) !== null;
48296
48917
  }
48918
+ function renderPatternValue(value) {
48919
+ if (value === null || value === void 0) return null;
48920
+ if (typeof value === "boolean" || typeof value === "function") return null;
48921
+ if (typeof value === "number") return value;
48922
+ if (typeof value === "string") return renderPatternChildren(value, () => {
48923
+ });
48924
+ if (value instanceof Date) return value.toLocaleString();
48925
+ if (React85__namespace.default.isValidElement(value)) return value;
48926
+ if (Array.isArray(value)) {
48927
+ return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React85__namespace.default.Fragment, { children: renderPatternValue(item) }, index));
48928
+ }
48929
+ if (isPatternConfig(value)) {
48930
+ const { type, ...props } = value;
48931
+ return renderPatternChildren({ type, props }, () => {
48932
+ });
48933
+ }
48934
+ return null;
48935
+ }
48297
48936
  function isPlainConfigObject(value) {
48298
48937
  if (React85__namespace.default.isValidElement(value)) return false;
48299
48938
  if (value instanceof Date) return false;
@@ -48495,9 +49134,9 @@ function SlotContentRenderer({
48495
49134
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
48496
49135
  }
48497
49136
  }
48498
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
48499
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
48500
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
49137
+ const fieldsContract = patterns.getPatternFieldsContract(content.pattern) ?? (patterns.getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
49138
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
49139
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
48501
49140
  }
48502
49141
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
48503
49142
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -48607,7 +49246,7 @@ function UISlotRenderer({
48607
49246
  }
48608
49247
  return wrapped;
48609
49248
  }
48610
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49249
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
48611
49250
  var init_UISlotRenderer = __esm({
48612
49251
  "components/core/organisms/UISlotRenderer.tsx"() {
48613
49252
  "use client";
@@ -48638,12 +49277,6 @@ var init_UISlotRenderer = __esm({
48638
49277
  modal: "form",
48639
49278
  drawer: "form"
48640
49279
  };
48641
- FORM_PATTERNS = /* @__PURE__ */ new Set([
48642
- "form",
48643
- "form-section",
48644
- "inline-edit-form",
48645
- "wizard-step"
48646
- ]);
48647
49280
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
48648
49281
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
48649
49282
  "logo",