@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
@@ -7,12 +7,12 @@ import { EventBusContext, useTraitScopeChain, useCurrentPagePath, useGameAudioCo
7
7
  export { GameAudioContext, GameAudioProvider, useGameAudioContext } from '@almadar/ui/providers';
8
8
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
9
9
  import * as LucideIcons2 from 'lucide-react';
10
- import { X, List, Printer, ChevronRight, ChevronLeft, Check, Copy, RotateCcw, Play, Terminal, CheckCircle, XCircle, ChevronDown, Search, ChevronUp, MoreHorizontal, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ZoomIn, TrendingUp, TrendingDown, Minus, AlertCircle, Circle, Clock, CheckCircle2, Loader2, Code, FileText, WrapText, HelpCircle, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, GitBranch, Plus, ArrowRight, Trash, ArrowLeft, Menu as Menu$1, AlertTriangle, Trash2, Eraser, ZoomOut, Download, Lightbulb, PauseCircle, Link2, Tag, User, DollarSign } from 'lucide-react';
10
+ import { X, List, Printer, ChevronRight, ChevronLeft, Check, Copy, RotateCcw, Play, Terminal, CheckCircle, XCircle, ChevronDown, Search, ChevronUp, MoreHorizontal, ArrowLeft, DollarSign, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ZoomIn, TrendingUp, TrendingDown, Minus, AlertCircle, Circle, Clock, CheckCircle2, Loader2, Code, FileText, WrapText, HelpCircle, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, GitBranch, Plus, ArrowRight, Trash, Menu as Menu$1, AlertTriangle, Trash2, Eraser, ZoomOut, Download, Lightbulb, PauseCircle, Link2, Tag, User } from 'lucide-react';
11
11
  import { useTranslate } from '@almadar/ui/hooks';
12
12
  import { useUISlots, useTheme } from '@almadar/ui/context';
13
13
  import { evaluate, createMinimalContext } from '@almadar/evaluator';
14
14
  import { createContextFromBindings, interpolateValue } from '@almadar/runtime';
15
- import { mergeEntityFrame, isInlineTrait, isRenderBindingMarker, ANIMATION_NAMES } from '@almadar/core';
15
+ import { mergeEntityFrame, isFileValue, isInlineTrait, isRenderBindingMarker, ANIMATION_NAMES } from '@almadar/core';
16
16
  import { createPortal } from 'react-dom';
17
17
  import { wrapCallbackForEvent } from '@almadar/runtime/ui';
18
18
  import { Link, Outlet, useLocation } from 'react-router-dom';
@@ -76,7 +76,7 @@ import { CSS } from '@dnd-kit/utilities';
76
76
  import { ordered, lib } from 'emojilib';
77
77
  import { useNodeId, ReactFlowProvider, Handle, Position } from '@xyflow/react';
78
78
  import { forceSimulation, forceLink, forceManyBody, forceCollide, forceX, forceY } from 'd3-force';
79
- import { isDrawHostPattern, getPatternDefinition as getPatternDefinition$1, getComponentForPattern as getComponentForPattern$1 } from '@almadar/core/patterns';
79
+ import { isDrawHostPattern, getPatternDefinition as getPatternDefinition$1, getPatternFieldsContract, getComponentForPattern as getComponentForPattern$1 } from '@almadar/core/patterns';
80
80
  import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
81
81
 
82
82
  var __defProp = Object.defineProperty;
@@ -5149,27 +5149,61 @@ var init_InfiniteScrollSentinel = __esm({
5149
5149
  InfiniteScrollSentinel.displayName = "InfiniteScrollSentinel";
5150
5150
  }
5151
5151
  });
5152
- function createParticles(count) {
5153
- return Array.from({ length: count }, () => {
5154
- particleIdCounter += 1;
5155
- return {
5156
- id: particleIdCounter,
5157
- color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)],
5158
- left: 30 + Math.random() * 40,
5159
- delay: Math.random() * 300,
5160
- angle: Math.random() * 360,
5161
- distance: 40 + Math.random() * 80,
5162
- rotation: Math.random() * 720 - 360,
5163
- size: 4 + Math.random() * 6
5164
- };
5165
- });
5152
+
5153
+ // components/core/atoms/fx.ts
5154
+ function resolveFxView(item, presets) {
5155
+ const row = presets?.find((p) => p.type === item.type);
5156
+ if (!row) return item;
5157
+ return {
5158
+ ...item,
5159
+ space: item.space ?? row.space,
5160
+ effect: item.effect ?? row.effect,
5161
+ color: item.color ?? row.color,
5162
+ size: item.size ?? row.size,
5163
+ vx: item.vx ?? row.vx,
5164
+ vy: item.vy ?? row.vy,
5165
+ vz: item.vz ?? row.vz,
5166
+ particleCount: item.particleCount ?? row.particleCount,
5167
+ shape: row.shape,
5168
+ count: row.count,
5169
+ glow: row.glow,
5170
+ gravity: row.gravity,
5171
+ color2: row.color2
5172
+ };
5166
5173
  }
5167
- var CONFETTI_COLORS, particleIdCounter, ConfettiEffect;
5168
- var init_ConfettiEffect = __esm({
5169
- "components/core/atoms/ConfettiEffect.tsx"() {
5170
- "use client";
5171
- init_cn();
5172
- init_Box();
5174
+ function fxLifecycle(item, epochNowMs, tickMs) {
5175
+ const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
5176
+ const lifeMs = maxTtl * tickMs;
5177
+ const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
5178
+ const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
5179
+ return { lifeMs, ageMs, progress, fade: 1 - progress };
5180
+ }
5181
+ function fxHash01(seed, salt) {
5182
+ let h = 2166136261 ^ salt;
5183
+ for (let i = 0; i < seed.length; i++) {
5184
+ h ^= seed.charCodeAt(i);
5185
+ h = Math.imul(h, 16777619);
5186
+ }
5187
+ h ^= h >>> 13;
5188
+ h = Math.imul(h, 1274126177);
5189
+ h ^= h >>> 16;
5190
+ return (h >>> 0) / 4294967296;
5191
+ }
5192
+ function createConfettiParticles(count, seed) {
5193
+ return Array.from({ length: count }, (_, i) => ({
5194
+ id: i,
5195
+ color: CONFETTI_COLORS[Math.floor(fxHash01(seed, i * 7 + 1) * CONFETTI_COLORS.length)],
5196
+ left: 30 + fxHash01(seed, i * 7 + 2) * 40,
5197
+ delay: fxHash01(seed, i * 7 + 3) * 300,
5198
+ angle: fxHash01(seed, i * 7 + 4) * 360,
5199
+ distance: 40 + fxHash01(seed, i * 7 + 5) * 80,
5200
+ rotation: fxHash01(seed, i * 7 + 6) * 720 - 360,
5201
+ size: 4 + fxHash01(seed, i * 7 + 7) * 6
5202
+ }));
5203
+ }
5204
+ var CONFETTI_COLORS, CONFETTI_BURST_KEYFRAMES;
5205
+ var init_fx = __esm({
5206
+ "components/core/atoms/fx.ts"() {
5173
5207
  CONFETTI_COLORS = [
5174
5208
  "var(--color-primary)",
5175
5209
  "var(--color-success)",
@@ -5178,7 +5212,30 @@ var init_ConfettiEffect = __esm({
5178
5212
  "gold",
5179
5213
  "dodgerblue"
5180
5214
  ];
5181
- particleIdCounter = 0;
5215
+ CONFETTI_BURST_KEYFRAMES = `
5216
+ @keyframes confetti-burst {
5217
+ 0% {
5218
+ opacity: 1;
5219
+ transform: translate(0, 0) rotate(0deg) scale(1);
5220
+ }
5221
+ 70% {
5222
+ opacity: 1;
5223
+ }
5224
+ 100% {
5225
+ opacity: 0;
5226
+ transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
5227
+ }
5228
+ }
5229
+ `;
5230
+ }
5231
+ });
5232
+ var ConfettiEffect;
5233
+ var init_ConfettiEffect = __esm({
5234
+ "components/core/atoms/ConfettiEffect.tsx"() {
5235
+ "use client";
5236
+ init_cn();
5237
+ init_Box();
5238
+ init_fx();
5182
5239
  ConfettiEffect = ({
5183
5240
  trigger,
5184
5241
  duration = 2e3,
@@ -5187,11 +5244,13 @@ var init_ConfettiEffect = __esm({
5187
5244
  }) => {
5188
5245
  const [particles, setParticles] = useState([]);
5189
5246
  const previousTriggerRef = useRef(false);
5247
+ const burstRef = useRef(0);
5190
5248
  useEffect(() => {
5191
5249
  const wasFalse = !previousTriggerRef.current;
5192
5250
  previousTriggerRef.current = trigger;
5193
5251
  if (trigger && wasFalse) {
5194
- const newParticles = createParticles(particleCount);
5252
+ burstRef.current += 1;
5253
+ const newParticles = createConfettiParticles(particleCount, `confetti-${burstRef.current}`);
5195
5254
  setParticles(newParticles);
5196
5255
  const timer = window.setTimeout(() => {
5197
5256
  setParticles([]);
@@ -5239,21 +5298,7 @@ var init_ConfettiEffect = __esm({
5239
5298
  p.id
5240
5299
  );
5241
5300
  }),
5242
- /* @__PURE__ */ jsx("style", { children: `
5243
- @keyframes confetti-burst {
5244
- 0% {
5245
- opacity: 1;
5246
- transform: translate(0, 0) rotate(0deg) scale(1);
5247
- }
5248
- 70% {
5249
- opacity: 1;
5250
- }
5251
- 100% {
5252
- opacity: 0;
5253
- transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
5254
- }
5255
- }
5256
- ` })
5301
+ /* @__PURE__ */ jsx("style", { children: CONFETTI_BURST_KEYFRAMES })
5257
5302
  ]
5258
5303
  }
5259
5304
  );
@@ -9139,7 +9184,7 @@ var init_AlgoGraphCanvas = __esm({
9139
9184
  }
9140
9185
  const badgeGeoms = [];
9141
9186
  for (const g of nodeGeoms) {
9142
- if (!g.badge) continue;
9187
+ if (!g.badge || g.badge.text === "") continue;
9143
9188
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
9144
9189
  badgeGeoms.push({
9145
9190
  cx: g.x + g.radius * 0.75,
@@ -9205,7 +9250,7 @@ var init_AlgoGraphCanvas = __esm({
9205
9250
  };
9206
9251
  }
9207
9252
  });
9208
- 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;
9253
+ 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;
9209
9254
  var init_AlgorithmCanvas = __esm({
9210
9255
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
9211
9256
  "use client";
@@ -9232,9 +9277,18 @@ var init_AlgorithmCanvas = __esm({
9232
9277
  FRAME_ACTIVE_COLOR = "#3b82f6";
9233
9278
  FRAME_RETURNING_COLOR = "#f59e0b";
9234
9279
  FRAME_DONE_COLOR = "#94a3b8";
9280
+ FRAME_RIM_COLOR = {
9281
+ active: "#1d4ed8",
9282
+ returning: "#b45309",
9283
+ done: "#64748b"
9284
+ };
9235
9285
  FRAME_LABEL_COLOR = "#ffffff";
9236
9286
  FRAME_DETAIL_COLOR = "#e2e8f0";
9237
9287
  FRAME_TWO_LINE_MIN_H = 22;
9288
+ FRAME_STRIP_H = 44;
9289
+ FRAME_GAP = 6;
9290
+ FRAME_BOTTOM_PAD = 8;
9291
+ FRAME_MIN_H = 12;
9238
9292
  BUCKET_INDEX_FILL = "#e2e8f0";
9239
9293
  BUCKET_INDEX_STROKE = "#9ca3af";
9240
9294
  BUCKET_INDEX_TEXT = "#374151";
@@ -9731,14 +9785,18 @@ var init_AlgorithmCanvas = __esm({
9731
9785
  if (frames.length > 0) {
9732
9786
  const panelYFrames = panelY.frames;
9733
9787
  const n = frames.length;
9734
- const frameH = panelHeight / n;
9788
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
9789
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
9790
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
9735
9791
  const x = 8;
9736
9792
  const w = width - 16;
9793
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
9737
9794
  frames.forEach((f3, i) => {
9738
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
9795
+ const y = bottom - i * stride - frameH;
9739
9796
  const state = f3.state ?? "active";
9740
9797
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
9741
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
9798
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
9799
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
9742
9800
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
9743
9801
  out.push({
9744
9802
  type: "text",
@@ -9746,14 +9804,14 @@ var init_AlgorithmCanvas = __esm({
9746
9804
  y: y + frameH * 0.35,
9747
9805
  text: f3.label,
9748
9806
  color: FRAME_LABEL_COLOR,
9749
- fontSize: 10,
9807
+ fontSize: 11,
9750
9808
  align: "left"
9751
9809
  });
9752
9810
  if (f3.detail) {
9753
9811
  out.push({
9754
9812
  type: "text",
9755
9813
  x: 16,
9756
- y: y + frameH * 0.7,
9814
+ y: y + frameH * 0.72,
9757
9815
  text: f3.detail,
9758
9816
  color: FRAME_DETAIL_COLOR,
9759
9817
  fontSize: 10,
@@ -16269,8 +16327,6 @@ var init_ButtonGroup = __esm({
16269
16327
  ButtonGroup.displayName = "ButtonGroup";
16270
16328
  }
16271
16329
  });
16272
-
16273
- // lib/getNestedValue.ts
16274
16330
  function getNestedValue(obj, path) {
16275
16331
  if (obj === null || obj === void 0 || !path) {
16276
16332
  return void 0;
@@ -16291,6 +16347,15 @@ function getNestedValue(obj, path) {
16291
16347
  }
16292
16348
  return value;
16293
16349
  }
16350
+ function resolveImageUrl(value) {
16351
+ if (typeof value === "string") {
16352
+ return value;
16353
+ }
16354
+ if (isFileValue(value)) {
16355
+ return value.url;
16356
+ }
16357
+ return void 0;
16358
+ }
16294
16359
  var init_getNestedValue = __esm({
16295
16360
  "lib/getNestedValue.ts"() {
16296
16361
  }
@@ -18037,7 +18102,7 @@ var init_DrawShape = __esm({
18037
18102
  const cx = p.x + (node.offsetX ?? 0) * tw;
18038
18103
  const cy = p.y + (node.offsetY ?? 0) * tw;
18039
18104
  const rx = (node.radiusX ?? 0) * tw;
18040
- const ry = (node.radiusY ?? rx) * tw;
18105
+ const ry = (node.radiusY ?? node.radiusX ?? 0) * tw;
18041
18106
  if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
18042
18107
  if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
18043
18108
  if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
@@ -18227,6 +18292,172 @@ var init_DrawTextLayer = __esm({
18227
18292
  };
18228
18293
  }
18229
18294
  });
18295
+
18296
+ // components/game/molecules/DrawFxLayer.tsx
18297
+ function fxPosition(view, dim, ageSec) {
18298
+ const g = view.gravity ?? 0;
18299
+ const fall = 0.5 * g * ageSec * ageSec;
18300
+ {
18301
+ const base2 = view.position ?? { x: view.x, y: view.z ?? view.y ?? 0 };
18302
+ if (!Number.isFinite(base2.x) || !Number.isFinite(base2.y)) return void 0;
18303
+ return {
18304
+ x: base2.x + (view.vx ?? 0) * ageSec,
18305
+ y: base2.y + (view.vz ?? 0) * ageSec + fall
18306
+ };
18307
+ }
18308
+ }
18309
+ function sparkShape(view, pos, i, fade, progress) {
18310
+ const size = view.size ?? 0.5;
18311
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
18312
+ const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut2(progress);
18313
+ const r = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
18314
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
18315
+ return {
18316
+ type: "draw-shape",
18317
+ shape: "ellipse",
18318
+ position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
18319
+ anchor: "center",
18320
+ radiusX: r,
18321
+ fill: color,
18322
+ blendMode: "lighter",
18323
+ opacity: fade,
18324
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
18325
+ };
18326
+ }
18327
+ function proceduralShapes(view, pos, fade, progress) {
18328
+ const size = view.size ?? 0.5;
18329
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
18330
+ switch (view.shape ?? "spark") {
18331
+ case "ring": {
18332
+ return [
18333
+ {
18334
+ type: "draw-shape",
18335
+ shape: "ellipse",
18336
+ position: pos,
18337
+ anchor: "center",
18338
+ radiusX: size * easeOut2(progress),
18339
+ stroke: view.color2 ?? color,
18340
+ strokeWidth: 2,
18341
+ blendMode: "lighter",
18342
+ opacity: fade,
18343
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
18344
+ }
18345
+ ];
18346
+ }
18347
+ case "puff": {
18348
+ const count = view.count ?? 3;
18349
+ return Array.from({ length: count }, (_, i) => {
18350
+ const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
18351
+ const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut2(progress);
18352
+ return {
18353
+ type: "draw-shape",
18354
+ shape: "ellipse",
18355
+ position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
18356
+ anchor: "center",
18357
+ radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
18358
+ fill: i === 0 && view.color2 ? view.color2 : color,
18359
+ opacity: fade * 0.6,
18360
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
18361
+ };
18362
+ });
18363
+ }
18364
+ case "streak": {
18365
+ const count = view.count ?? 5;
18366
+ return Array.from({ length: count }, (_, i) => {
18367
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
18368
+ const inner = size * (0.2 + 0.8 * easeOut2(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
18369
+ const len = size * 0.35;
18370
+ return {
18371
+ type: "draw-shape",
18372
+ shape: "ellipse",
18373
+ position: pos,
18374
+ anchor: "center",
18375
+ offsetX: inner + len / 2,
18376
+ offsetY: 0,
18377
+ radiusX: len / 2,
18378
+ radiusY: size * 0.04,
18379
+ rotate: angle,
18380
+ fill: color,
18381
+ blendMode: "lighter",
18382
+ opacity: fade,
18383
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
18384
+ };
18385
+ });
18386
+ }
18387
+ default: {
18388
+ const count = view.count ?? 6;
18389
+ return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
18390
+ }
18391
+ }
18392
+ }
18393
+ function expandFxItem(view, node, epochNowMs, dim) {
18394
+ if (view.space === "screen") return [];
18395
+ const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
18396
+ const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
18397
+ if (progress >= 1) return [];
18398
+ const pos = fxPosition(view, dim, ageMs / 1e3);
18399
+ if (!pos) return [];
18400
+ const out = [];
18401
+ const artItems = node.art?.[view.type]?.["idle"];
18402
+ const sprite = node.sprites?.[view.type];
18403
+ if (Array.isArray(artItems)) {
18404
+ out.push({
18405
+ type: "draw-group",
18406
+ position: pos,
18407
+ opacity: fade,
18408
+ ...view.size !== void 0 ? { scale: view.size } : {},
18409
+ items: artItems
18410
+ });
18411
+ } else if (sprite?.url) {
18412
+ const spriteSize = view.size ?? 0.6;
18413
+ out.push({
18414
+ type: "draw-sprite",
18415
+ position: pos,
18416
+ asset: sprite,
18417
+ anchor: "center",
18418
+ width: spriteSize,
18419
+ height: spriteSize,
18420
+ opacity: fade
18421
+ });
18422
+ } else {
18423
+ out.push(...proceduralShapes(view, pos, fade, progress));
18424
+ }
18425
+ if (view.message) {
18426
+ const text = {
18427
+ type: "draw-text",
18428
+ text: view.message,
18429
+ position: pos,
18430
+ offsetY: -(0.15 + 0.55 * progress),
18431
+ color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
18432
+ opacity: fade
18433
+ };
18434
+ out.push(text);
18435
+ }
18436
+ return out;
18437
+ }
18438
+ function expandFxLayer(node, epochNowMs, dim) {
18439
+ if (!Array.isArray(node.items)) return [];
18440
+ const out = [];
18441
+ for (const item of node.items) {
18442
+ if (!item || typeof item.id !== "string") continue;
18443
+ out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim));
18444
+ }
18445
+ return out;
18446
+ }
18447
+ function DrawFxLayer(_props) {
18448
+ return null;
18449
+ }
18450
+ var DEFAULT_TICK_MS, DEFAULT_TEXT_COLOR, DEFAULT_SPARK_COLOR, easeOut2;
18451
+ var init_DrawFxLayer = __esm({
18452
+ "components/game/molecules/DrawFxLayer.tsx"() {
18453
+ "use client";
18454
+ init_fx();
18455
+ DEFAULT_TICK_MS = 500;
18456
+ DEFAULT_TEXT_COLOR = "#ffe066";
18457
+ DEFAULT_SPARK_COLOR = "#ffffff";
18458
+ easeOut2 = (t) => 1 - (1 - t) * (1 - t);
18459
+ }
18460
+ });
18230
18461
  function paintDrawable(painter, node, dctx) {
18231
18462
  switch (node.type) {
18232
18463
  case "draw-sprite":
@@ -18274,6 +18505,11 @@ function paintDrawable(painter, node, dctx) {
18274
18505
  case "draw-text-layer":
18275
18506
  paintTextLayer(painter, node, dctx);
18276
18507
  break;
18508
+ case "draw-fx-layer": {
18509
+ const epochNow = dctx.time > 0 && typeof performance !== "undefined" ? performance.timeOrigin + dctx.time : 0;
18510
+ for (const child of expandFxLayer(node, epochNow, "2d")) paintDrawable(painter, child, dctx);
18511
+ break;
18512
+ }
18277
18513
  }
18278
18514
  }
18279
18515
  var paint2dLog, warnedUnsupported2d, warnUnsupported2d;
@@ -18288,6 +18524,7 @@ var init_paintDispatch = __esm({
18288
18524
  init_DrawSpriteLayer();
18289
18525
  init_DrawShapeLayer();
18290
18526
  init_DrawTextLayer();
18527
+ init_DrawFxLayer();
18291
18528
  paint2dLog = createLogger("almadar:ui:drawable-2d");
18292
18529
  warnedUnsupported2d = /* @__PURE__ */ new Set();
18293
18530
  warnUnsupported2d = (kind) => {
@@ -18536,6 +18773,7 @@ function Canvas2D({
18536
18773
  return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
18537
18774
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
18538
18775
  if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
18776
+ if (node.type === "draw-fx-layer") return Array.isArray(node.items) && node.items.length > 0;
18539
18777
  return false;
18540
18778
  };
18541
18779
  const animRafRef = useRef(0);
@@ -19333,8 +19571,8 @@ var init_CardGrid = __esm({
19333
19571
  actionPayload: { row: itemData },
19334
19572
  children: [
19335
19573
  imageField && (() => {
19336
- const imgUrl = getNestedValue(itemData, imageField);
19337
- if (!imgUrl || typeof imgUrl !== "string") return null;
19574
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
19575
+ if (!imgUrl) return null;
19338
19576
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
19339
19577
  "img",
19340
19578
  {
@@ -23737,8 +23975,8 @@ function DataGrid({
23737
23975
  ),
23738
23976
  children: [
23739
23977
  imageField && (() => {
23740
- const imgUrl = getNestedValue(itemData, imageField);
23741
- if (!imgUrl || typeof imgUrl !== "string") return null;
23978
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
23979
+ if (!imgUrl) return null;
23742
23980
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
23743
23981
  "img",
23744
23982
  {
@@ -26458,6 +26696,196 @@ var init_PageTransition = __esm({
26458
26696
  PageTransition.displayName = "PageTransition";
26459
26697
  }
26460
26698
  });
26699
+ function ConfettiBurst({ item, lifeMs }) {
26700
+ const particles = createConfettiParticles(item.particleCount ?? 30, item.id);
26701
+ const left = (item.x ?? 0.5) * 100;
26702
+ const top = (item.z ?? item.y ?? 0.4) * 100;
26703
+ return /* @__PURE__ */ jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: particles.map((p) => {
26704
+ const rad = p.angle * Math.PI / 180;
26705
+ const tx = Math.cos(rad) * p.distance * (item.size ?? 1);
26706
+ const ty = Math.sin(rad) * p.distance * (item.size ?? 1) - 20;
26707
+ return /* @__PURE__ */ jsx(
26708
+ Box,
26709
+ {
26710
+ className: "absolute rounded-sm",
26711
+ style: {
26712
+ left: (p.left - 50) * 2,
26713
+ top: -10,
26714
+ width: p.size,
26715
+ height: p.size,
26716
+ backgroundColor: item.color ?? p.color,
26717
+ animation: `confetti-burst ${Math.max(lifeMs - p.delay, 200)}ms ease-out ${p.delay}ms forwards`,
26718
+ opacity: 0,
26719
+ "--confetti-tx": `${tx}px`,
26720
+ "--confetti-ty": `${ty}px`,
26721
+ "--confetti-rotate": `${p.rotation}deg`
26722
+ }
26723
+ },
26724
+ p.id
26725
+ );
26726
+ }) });
26727
+ }
26728
+ function SparkleBurst({ item, lifeMs }) {
26729
+ const count = item.particleCount ?? 8;
26730
+ const scale = item.size ?? 1;
26731
+ const left = (item.x ?? 0.5) * 100;
26732
+ const top = (item.z ?? item.y ?? 0.4) * 100;
26733
+ return /* @__PURE__ */ jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: Array.from({ length: count }, (_, i) => {
26734
+ const dx = (fxHash01(item.id, i * 4) - 0.5) * 160 * scale;
26735
+ const dy = (fxHash01(item.id, i * 4 + 1) - 0.5) * 120 * scale;
26736
+ const dot = (4 + fxHash01(item.id, i * 4 + 2) * 5) * scale;
26737
+ const delay = fxHash01(item.id, i * 4 + 3) * lifeMs * 0.4;
26738
+ return /* @__PURE__ */ jsx(
26739
+ Box,
26740
+ {
26741
+ className: "absolute rounded-full",
26742
+ style: {
26743
+ left: dx,
26744
+ top: dy,
26745
+ width: dot,
26746
+ height: dot,
26747
+ backgroundColor: item.color ?? "gold",
26748
+ opacity: 0,
26749
+ animation: `fx-overlay-sparkle ${Math.max(lifeMs - delay, 200)}ms ease-in-out ${delay}ms forwards`
26750
+ }
26751
+ },
26752
+ i
26753
+ );
26754
+ }) });
26755
+ }
26756
+ function OverlayFxNode({ item, tickMs }) {
26757
+ const lifeMs = lifeMsOf(item, tickMs);
26758
+ switch (item.effect ?? "sparkle") {
26759
+ case "confetti":
26760
+ return /* @__PURE__ */ jsx(ConfettiBurst, { item, lifeMs });
26761
+ case "flash":
26762
+ return /* @__PURE__ */ jsx(
26763
+ Box,
26764
+ {
26765
+ position: "absolute",
26766
+ className: "inset-0",
26767
+ style: {
26768
+ backgroundColor: item.color ?? "#ffffff",
26769
+ opacity: 0,
26770
+ animation: `fx-overlay-flash ${lifeMs}ms ease-out forwards`
26771
+ }
26772
+ }
26773
+ );
26774
+ case "streak-glow":
26775
+ return /* @__PURE__ */ jsx(
26776
+ Box,
26777
+ {
26778
+ position: "absolute",
26779
+ className: "inset-0",
26780
+ style: {
26781
+ boxShadow: `inset 0 0 ${60 * (item.size ?? 1)}px ${item.color ?? "var(--color-warning)"}`,
26782
+ opacity: 0,
26783
+ animation: `fx-overlay-glow ${lifeMs}ms ease-in-out forwards`
26784
+ }
26785
+ }
26786
+ );
26787
+ case "float-text": {
26788
+ if (!item.message) return null;
26789
+ return /* @__PURE__ */ jsx(
26790
+ Box,
26791
+ {
26792
+ position: "absolute",
26793
+ style: {
26794
+ left: `${(item.x ?? 0.5) * 100}%`,
26795
+ top: `${(item.z ?? item.y ?? 0.4) * 100}%`,
26796
+ color: item.color ?? "var(--color-success)",
26797
+ fontWeight: 700,
26798
+ fontSize: 16 * (item.size ?? 1),
26799
+ textShadow: "0 1px 2px rgba(0,0,0,0.4)",
26800
+ opacity: 0,
26801
+ animation: `fx-overlay-float ${lifeMs}ms ease-out forwards`,
26802
+ whiteSpace: "nowrap"
26803
+ },
26804
+ children: item.message
26805
+ }
26806
+ );
26807
+ }
26808
+ case "shake":
26809
+ return null;
26810
+ default:
26811
+ return /* @__PURE__ */ jsx(SparkleBurst, { item, lifeMs });
26812
+ }
26813
+ }
26814
+ var DEFAULT_TICK_MS2, FX_OVERLAY_KEYFRAMES, lifeMsOf, FxOverlay;
26815
+ var init_FxOverlay = __esm({
26816
+ "components/core/molecules/FxOverlay.tsx"() {
26817
+ "use client";
26818
+ init_cn();
26819
+ init_Box();
26820
+ init_fx();
26821
+ DEFAULT_TICK_MS2 = 500;
26822
+ FX_OVERLAY_KEYFRAMES = `
26823
+ ${CONFETTI_BURST_KEYFRAMES}
26824
+ @keyframes fx-overlay-flash {
26825
+ 0% { opacity: 0.75; }
26826
+ 100% { opacity: 0; }
26827
+ }
26828
+ @keyframes fx-overlay-glow {
26829
+ 0% { opacity: 0.9; }
26830
+ 60% { opacity: 0.6; }
26831
+ 100% { opacity: 0; }
26832
+ }
26833
+ @keyframes fx-overlay-sparkle {
26834
+ 0% { opacity: 0; transform: scale(0); }
26835
+ 30% { opacity: 1; transform: scale(1); }
26836
+ 100% { opacity: 0; transform: scale(0.3); }
26837
+ }
26838
+ @keyframes fx-overlay-float {
26839
+ 0% { opacity: 0; transform: translate(-50%, 8px); }
26840
+ 15% { opacity: 1; }
26841
+ 100% { opacity: 0; transform: translate(-50%, -40px); }
26842
+ }
26843
+ @keyframes fx-overlay-shake {
26844
+ 0% { transform: translateX(0); }
26845
+ 15% { transform: translateX(-6px); }
26846
+ 30% { transform: translateX(5px); }
26847
+ 45% { transform: translateX(-4px); }
26848
+ 60% { transform: translateX(3px); }
26849
+ 75% { transform: translateX(-2px); }
26850
+ 100% { transform: translateX(0); }
26851
+ }
26852
+ `;
26853
+ lifeMsOf = (it, tickMs) => Math.max(it.maxTtl ?? it.ttl, it.ttl, 1) * tickMs;
26854
+ FxOverlay = ({ items, tickMs = DEFAULT_TICK_MS2, children, className }) => {
26855
+ const screenItems = (Array.isArray(items) ? items : []).filter(
26856
+ (it) => Boolean(it) && typeof it.id === "string" && it.space !== "world"
26857
+ );
26858
+ const shakeItem = [...screenItems].reverse().find((it) => it.effect === "shake");
26859
+ const overlay = /* @__PURE__ */ jsxs(
26860
+ Box,
26861
+ {
26862
+ position: "absolute",
26863
+ className: cn("inset-0 pointer-events-none overflow-hidden z-50", !children && className),
26864
+ "aria-hidden": "true",
26865
+ children: [
26866
+ screenItems.map((it) => /* @__PURE__ */ jsx(OverlayFxNode, { item: it, tickMs }, it.id)),
26867
+ /* @__PURE__ */ jsx("style", { children: FX_OVERLAY_KEYFRAMES })
26868
+ ]
26869
+ }
26870
+ );
26871
+ if (children !== void 0 && children !== null) {
26872
+ return /* @__PURE__ */ jsxs(Box, { position: "relative", className, children: [
26873
+ /* @__PURE__ */ jsx(
26874
+ Box,
26875
+ {
26876
+ style: shakeItem ? { animation: `fx-overlay-shake ${Math.min(lifeMsOf(shakeItem, tickMs), 600)}ms ease-in-out` } : void 0,
26877
+ children
26878
+ },
26879
+ shakeItem?.id ?? "steady"
26880
+ ),
26881
+ overlay
26882
+ ] });
26883
+ }
26884
+ return overlay;
26885
+ };
26886
+ FxOverlay.displayName = "FxOverlay";
26887
+ }
26888
+ });
26461
26889
  function computePopoverStyle(position, triggerRect, popoverWidth) {
26462
26890
  if (position === "left" || position === "right") {
26463
26891
  return {
@@ -27264,7 +27692,7 @@ var init_SearchInput = __esm({
27264
27692
  /* @__PURE__ */ jsx(
27265
27693
  Input,
27266
27694
  {
27267
- type: "text",
27695
+ type: "search",
27268
27696
  value: searchValue,
27269
27697
  onChange: handleChange,
27270
27698
  placeholder: resolvedPlaceholder,
@@ -31024,6 +31452,7 @@ var MathCanvas;
31024
31452
  var init_MathCanvas = __esm({
31025
31453
  "components/learning/molecules/MathCanvas.tsx"() {
31026
31454
  "use client";
31455
+ init_useEventBus();
31027
31456
  init_atoms();
31028
31457
  init_Stack();
31029
31458
  init_LearningCanvas();
@@ -31055,9 +31484,32 @@ var init_MathCanvas = __esm({
31055
31484
  interactive = false,
31056
31485
  animate = false,
31057
31486
  onShapeClick,
31487
+ keyMap,
31488
+ keyUpMap,
31058
31489
  isLoading,
31059
31490
  error
31060
31491
  }) => {
31492
+ const eventBus = useEventBus();
31493
+ useEffect(() => {
31494
+ if (!keyMap && !keyUpMap) return;
31495
+ const onDown = (e) => {
31496
+ const ev = keyMap?.[e.code];
31497
+ if (ev) {
31498
+ eventBus.emit(`UI:${ev}`, {});
31499
+ e.preventDefault();
31500
+ }
31501
+ };
31502
+ const onUp = (e) => {
31503
+ const ev = keyUpMap?.[e.code];
31504
+ if (ev) eventBus.emit(`UI:${ev}`, {});
31505
+ };
31506
+ window.addEventListener("keydown", onDown);
31507
+ window.addEventListener("keyup", onUp);
31508
+ return () => {
31509
+ window.removeEventListener("keydown", onDown);
31510
+ window.removeEventListener("keyup", onUp);
31511
+ };
31512
+ }, [keyMap, keyUpMap, eventBus]);
31061
31513
  const derivedShapes = useMemo(() => {
31062
31514
  const out = [];
31063
31515
  const margin = 24;
@@ -31070,11 +31522,11 @@ var init_MathCanvas = __esm({
31070
31522
  if (showGrid) {
31071
31523
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
31072
31524
  const px = mapX(x);
31073
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
31525
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
31074
31526
  }
31075
31527
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
31076
31528
  const py = mapY(y);
31077
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
31529
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
31078
31530
  }
31079
31531
  }
31080
31532
  if (showTickLabels) {
@@ -31123,7 +31575,7 @@ var init_MathCanvas = __esm({
31123
31575
  x: mapX((first.x + last.x) / 2),
31124
31576
  y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
31125
31577
  text: region.label,
31126
- color: "#111827",
31578
+ color,
31127
31579
  fontSize: 11
31128
31580
  });
31129
31581
  }
@@ -31156,14 +31608,14 @@ var init_MathCanvas = __esm({
31156
31608
  const px = mapX(guide.at);
31157
31609
  out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
31158
31610
  if (guide.label) {
31159
- out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color: "#111827", fontSize: 11 });
31611
+ out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
31160
31612
  }
31161
31613
  } else {
31162
31614
  if (guide.at < yMin || guide.at > yMax) continue;
31163
31615
  const py = mapY(guide.at);
31164
31616
  out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
31165
31617
  if (guide.label) {
31166
- out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color: "#111827", fontSize: 11, align: "right" });
31618
+ out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: 11, align: "right" });
31167
31619
  }
31168
31620
  }
31169
31621
  }
@@ -31229,7 +31681,7 @@ var init_MathCanvas = __esm({
31229
31681
  x: (x1 + x2) / 2,
31230
31682
  y: xAxisY - peak - 8,
31231
31683
  text: hop.label,
31232
- color: "#111827",
31684
+ color,
31233
31685
  fontSize: 10,
31234
31686
  align: "center"
31235
31687
  });
@@ -31256,7 +31708,7 @@ var init_MathCanvas = __esm({
31256
31708
  x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
31257
31709
  y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
31258
31710
  text: angle.label,
31259
- color: "#111827",
31711
+ color,
31260
31712
  fontSize: 11,
31261
31713
  align: "center"
31262
31714
  });
@@ -31274,7 +31726,7 @@ var init_MathCanvas = __esm({
31274
31726
  fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
31275
31727
  });
31276
31728
  if (p.label) {
31277
- out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
31729
+ out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: 12 });
31278
31730
  }
31279
31731
  }
31280
31732
  for (const v of vectors) {
@@ -31285,7 +31737,7 @@ var init_MathCanvas = __esm({
31285
31737
  const y2 = mapY(v.y + v.vy);
31286
31738
  out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
31287
31739
  if (v.label) {
31288
- out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
31740
+ out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: 12 });
31289
31741
  }
31290
31742
  }
31291
31743
  out.push(...shapes);
@@ -32549,13 +33001,13 @@ var init_MapView = __esm({
32549
33001
  shadowSize: [41, 41]
32550
33002
  });
32551
33003
  L.Marker.prototype.options.icon = defaultIcon;
32552
- const { useEffect: useEffect66, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__default;
33004
+ const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__default;
32553
33005
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32554
33006
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32555
33007
  function MapUpdater({ centerLat, centerLng, zoom }) {
32556
33008
  const map = useMap();
32557
33009
  const prevRef = useRef65({ centerLat, centerLng, zoom });
32558
- useEffect66(() => {
33010
+ useEffect67(() => {
32559
33011
  const prev = prevRef.current;
32560
33012
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
32561
33013
  map.setView([centerLat, centerLng], zoom);
@@ -32566,7 +33018,7 @@ var init_MapView = __esm({
32566
33018
  }
32567
33019
  function MapClickHandler({ onMapClick }) {
32568
33020
  const map = useMap();
32569
- useEffect66(() => {
33021
+ useEffect67(() => {
32570
33022
  if (!onMapClick) return;
32571
33023
  const handler = (e) => {
32572
33024
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -39382,6 +39834,7 @@ var init_PageHeader = __esm({
39382
39834
  className
39383
39835
  }) => {
39384
39836
  const eventBus = useEventBus();
39837
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
39385
39838
  const handleBack = () => {
39386
39839
  eventBus.emit(`UI:${backEvent}`);
39387
39840
  };
@@ -39429,15 +39882,15 @@ var init_PageHeader = __esm({
39429
39882
  /* @__PURE__ */ jsxs(Box, { children: [
39430
39883
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3", children: [
39431
39884
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
39432
- status && /* @__PURE__ */ jsx(
39885
+ statusBadge && /* @__PURE__ */ jsx(
39433
39886
  Typography,
39434
39887
  {
39435
39888
  variant: "small",
39436
39889
  className: cn(
39437
39890
  "px-2.5 py-1 rounded-full text-xs font-medium",
39438
- statusColors2[status.variant || "default"]
39891
+ statusColors2[statusBadge.variant || "default"]
39439
39892
  ),
39440
- children: status.label
39893
+ children: statusBadge.label
39441
39894
  }
39442
39895
  )
39443
39896
  ] }),
@@ -42260,6 +42713,7 @@ var init_molecules2 = __esm({
42260
42713
  init_Menu();
42261
42714
  init_Modal();
42262
42715
  init_PageTransition();
42716
+ init_FxOverlay();
42263
42717
  init_Pagination();
42264
42718
  init_Popover();
42265
42719
  init_Coachmark();
@@ -42890,7 +43344,7 @@ function formatFieldValue2(value, fieldName) {
42890
43344
  }
42891
43345
  return String(value);
42892
43346
  }
42893
- function renderRichFieldValue(value, fieldName, fieldType) {
43347
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
42894
43348
  if (value === void 0 || value === null) return "\u2014";
42895
43349
  const str2 = String(value);
42896
43350
  switch (fieldType) {
@@ -42957,19 +43411,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
42957
43411
  }
42958
43412
  );
42959
43413
  case "date":
42960
- case "datetime": {
43414
+ case "datetime":
43415
+ case "timestamp": {
42961
43416
  const d = new Date(str2);
42962
43417
  if (!isNaN(d.getTime())) {
42963
43418
  return d.toLocaleDateString(void 0, {
42964
43419
  year: "numeric",
42965
43420
  month: "long",
42966
43421
  day: "numeric",
42967
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
43422
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
42968
43423
  });
42969
43424
  }
42970
43425
  return str2;
42971
43426
  }
43427
+ case "money": {
43428
+ const n = typeof value === "number" ? value : Number(str2);
43429
+ if (!isNaN(n)) {
43430
+ return new Intl.NumberFormat(void 0, {
43431
+ style: "currency",
43432
+ currency: "USD"
43433
+ }).format(n);
43434
+ }
43435
+ return str2;
43436
+ }
43437
+ case "relation": {
43438
+ const match = meta?.options?.find((opt) => opt.value === str2);
43439
+ return match ? match.label : str2;
43440
+ }
43441
+ case "file": {
43442
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
43443
+ const file = value;
43444
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
43445
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
43446
+ return /* @__PURE__ */ jsxs(
43447
+ "a",
43448
+ {
43449
+ href: typeof file.url === "string" ? file.url : void 0,
43450
+ download: label,
43451
+ 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",
43452
+ children: [
43453
+ /* @__PURE__ */ jsx(Icon, { icon: FileText, size: "sm", className: "text-muted-foreground" }),
43454
+ label,
43455
+ size && /* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
43456
+ ]
43457
+ }
43458
+ );
43459
+ }
43460
+ return str2;
43461
+ }
42972
43462
  default:
43463
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str2)) {
43464
+ return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str2), children: humanizeEnumValue(str2) });
43465
+ }
42973
43466
  return formatFieldValue2(value, fieldName);
42974
43467
  }
42975
43468
  }
@@ -42996,8 +43489,18 @@ function buildFieldTypeMap(fields) {
42996
43489
  const map = {};
42997
43490
  if (!fields) return map;
42998
43491
  for (const f3 of fields) {
42999
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
43000
- map[f3.name] = f3.type;
43492
+ if (typeof f3 === "object" && f3.type !== void 0) {
43493
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
43494
+ }
43495
+ }
43496
+ return map;
43497
+ }
43498
+ function buildFieldMetaMap(fields) {
43499
+ const map = {};
43500
+ if (!fields) return map;
43501
+ for (const f3 of fields) {
43502
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
43503
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
43001
43504
  }
43002
43505
  }
43003
43506
  return map;
@@ -43017,6 +43520,7 @@ var init_DetailPanel = __esm({
43017
43520
  init_format();
43018
43521
  init_getNestedValue();
43019
43522
  init_useEventBus();
43523
+ init_UploadDropZone();
43020
43524
  formatFieldLabel = humanizeFieldName;
43021
43525
  ReactMarkdown2 = lazy(() => import('react-markdown'));
43022
43526
  DetailPanel = ({
@@ -43026,6 +43530,7 @@ var init_DetailPanel = __esm({
43026
43530
  avatar,
43027
43531
  sections: propSections,
43028
43532
  actions,
43533
+ backAction,
43029
43534
  footer,
43030
43535
  slideOver = false,
43031
43536
  className,
@@ -43034,7 +43539,8 @@ var init_DetailPanel = __esm({
43034
43539
  fieldNames,
43035
43540
  initialData,
43036
43541
  isLoading = false,
43037
- error
43542
+ error,
43543
+ relationsData
43038
43544
  }) => {
43039
43545
  const eventBus = useEventBus();
43040
43546
  const { t } = useTranslate();
@@ -43045,8 +43551,15 @@ var init_DetailPanel = __esm({
43045
43551
  };
43046
43552
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
43047
43553
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
43554
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
43048
43555
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
43049
43556
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
43557
+ const metaFor = (field) => {
43558
+ const base = fieldMetaMap[field];
43559
+ const options = relationsData?.[field];
43560
+ if (!base && !options) return void 0;
43561
+ return { ...base, options };
43562
+ };
43050
43563
  const handleActionClick = useCallback(
43051
43564
  (action, data2) => {
43052
43565
  if (action.navigatesTo) {
@@ -43125,7 +43638,7 @@ var init_DetailPanel = __esm({
43125
43638
  if (value !== void 0 && value !== null) {
43126
43639
  overviewFields.push({
43127
43640
  label: labelFor(field),
43128
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43641
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43129
43642
  icon: getFieldIcon(field)
43130
43643
  });
43131
43644
  }
@@ -43141,7 +43654,7 @@ var init_DetailPanel = __esm({
43141
43654
  if (value !== void 0 && value !== null) {
43142
43655
  metricsFields.push({
43143
43656
  label: labelFor(field),
43144
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43657
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43145
43658
  icon: getFieldIcon(field)
43146
43659
  });
43147
43660
  }
@@ -43157,7 +43670,7 @@ var init_DetailPanel = __esm({
43157
43670
  if (value !== void 0 && value !== null) {
43158
43671
  timelineFields.push({
43159
43672
  label: labelFor(field),
43160
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43673
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43161
43674
  icon: getFieldIcon(field)
43162
43675
  });
43163
43676
  }
@@ -43173,7 +43686,7 @@ var init_DetailPanel = __esm({
43173
43686
  if (value !== void 0 && value !== null) {
43174
43687
  descFields.push({
43175
43688
  label: labelFor(field),
43176
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43689
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43177
43690
  icon: getFieldIcon(field)
43178
43691
  });
43179
43692
  }
@@ -43221,7 +43734,7 @@ var init_DetailPanel = __esm({
43221
43734
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
43222
43735
  allFields.push({
43223
43736
  label: labelFor(field),
43224
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43737
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43225
43738
  icon: getFieldIcon(field)
43226
43739
  });
43227
43740
  } else {
@@ -43236,34 +43749,49 @@ var init_DetailPanel = __esm({
43236
43749
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
43237
43750
  const effectiveCloseAction = closeAction ?? { event: void 0};
43238
43751
  const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
43239
- /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
43240
- otherActions.map((action, idx) => /* @__PURE__ */ jsx(
43241
- Button,
43242
- {
43243
- variant: action.variant || "secondary",
43244
- size: "sm",
43245
- action: action.navigatesTo ? void 0 : action.event,
43246
- actionPayload: { row: normalizedData },
43247
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43248
- icon: action.icon,
43249
- "data-testid": action.event ? `action-${action.event}` : void 0,
43250
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43251
- children: action.label
43252
- },
43253
- idx
43254
- )),
43255
- /* @__PURE__ */ jsx(
43752
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
43753
+ /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
43256
43754
  Button,
43257
43755
  {
43258
- variant: "ghost",
43756
+ variant: backAction.variant || "ghost",
43259
43757
  size: "sm",
43260
- action: effectiveCloseAction.event,
43758
+ action: backAction.navigatesTo ? void 0 : backAction.event,
43261
43759
  actionPayload: { row: normalizedData },
43262
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
43263
- icon: X,
43264
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
43760
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
43761
+ icon: backAction.icon ?? ArrowLeft,
43762
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
43763
+ children: backAction.label
43265
43764
  }
43266
- )
43765
+ ) }),
43766
+ /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
43767
+ otherActions.map((action, idx) => /* @__PURE__ */ jsx(
43768
+ Button,
43769
+ {
43770
+ variant: action.variant || "secondary",
43771
+ size: "sm",
43772
+ action: action.navigatesTo ? void 0 : action.event,
43773
+ actionPayload: { row: normalizedData },
43774
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43775
+ icon: action.icon,
43776
+ "data-testid": action.event ? `action-${action.event}` : void 0,
43777
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43778
+ children: action.label
43779
+ },
43780
+ idx
43781
+ )),
43782
+ /* @__PURE__ */ jsx(
43783
+ Button,
43784
+ {
43785
+ variant: "ghost",
43786
+ size: "sm",
43787
+ action: effectiveCloseAction.event,
43788
+ actionPayload: { row: normalizedData },
43789
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
43790
+ icon: X,
43791
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
43792
+ }
43793
+ )
43794
+ ] })
43267
43795
  ] }),
43268
43796
  avatar,
43269
43797
  /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -43578,6 +44106,10 @@ function determineInputType(field) {
43578
44106
  return "password";
43579
44107
  case "url":
43580
44108
  return "url";
44109
+ case "file":
44110
+ return "file";
44111
+ case "money":
44112
+ return "currency";
43581
44113
  case "number":
43582
44114
  case "integer":
43583
44115
  case "float":
@@ -43639,6 +44171,7 @@ var init_Form = __esm({
43639
44171
  init_Typography();
43640
44172
  init_Icon();
43641
44173
  init_RelationSelect();
44174
+ init_UploadDropZone();
43642
44175
  init_Alert();
43643
44176
  init_useEventBus();
43644
44177
  init_debug();
@@ -43681,6 +44214,7 @@ var init_Form = __esm({
43681
44214
  cancelEvent = "CANCEL",
43682
44215
  relationsData = {},
43683
44216
  relationsLoading = {},
44217
+ fieldOverrides,
43684
44218
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
43685
44219
  conditionalFields: conditionalFieldsRaw = {},
43686
44220
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -43927,7 +44461,8 @@ var init_Form = __esm({
43927
44461
  label,
43928
44462
  field.required && /* @__PURE__ */ jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
43929
44463
  ] }),
43930
- renderFieldInput(field, fieldName, inputType, currentValue2, label)
44464
+ renderFieldInput(field, fieldName, inputType, currentValue2, label),
44465
+ field.hint && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
43931
44466
  ] }, fieldName);
43932
44467
  },
43933
44468
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -43943,6 +44478,7 @@ var init_Form = __esm({
43943
44478
  name: field,
43944
44479
  type: entityField.type,
43945
44480
  required: entityField.required,
44481
+ hint: entityField.description,
43946
44482
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
43947
44483
  defaultValue: entityField.default,
43948
44484
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -43955,8 +44491,18 @@ var init_Form = __esm({
43955
44491
  return { name: field, type: "string" };
43956
44492
  }
43957
44493
  return field;
44494
+ }).map((field) => {
44495
+ const fieldName = field.name || field.field;
44496
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
44497
+ if (!override) return field;
44498
+ return {
44499
+ ...field,
44500
+ ...override.label !== void 0 ? { label: override.label } : {},
44501
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
44502
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
44503
+ };
43958
44504
  });
43959
- }, [effectiveFields, resolvedEntity]);
44505
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
43960
44506
  const schemaFields = React77__default.useMemo(() => {
43961
44507
  if (normalizedFields.length === 0) return null;
43962
44508
  if (isDebugEnabled()) {
@@ -44080,6 +44626,44 @@ var init_Form = __esm({
44080
44626
  max: field.max
44081
44627
  }
44082
44628
  );
44629
+ case "currency":
44630
+ return /* @__PURE__ */ jsx(
44631
+ Input,
44632
+ {
44633
+ ...commonProps,
44634
+ type: "number",
44635
+ step: "0.01",
44636
+ icon: DollarSign,
44637
+ value: currentValue2 !== void 0 && currentValue2 !== "" ? String(currentValue2) : "",
44638
+ onChange: (e) => handleChange(
44639
+ fieldName,
44640
+ e.target.value ? Number(e.target.value) : void 0
44641
+ ),
44642
+ min: field.min,
44643
+ max: field.max
44644
+ }
44645
+ );
44646
+ case "file":
44647
+ return /* @__PURE__ */ jsx(
44648
+ UploadDropZone,
44649
+ {
44650
+ accept: field.pattern,
44651
+ maxFiles: 1,
44652
+ disabled: isLoading,
44653
+ onFiles: (files) => {
44654
+ const f3 = files[0];
44655
+ if (!f3) return;
44656
+ const reader = new FileReader();
44657
+ reader.onload = () => handleChange(fieldName, {
44658
+ name: f3.name,
44659
+ mimeType: f3.type,
44660
+ sizeBytes: f3.size,
44661
+ url: typeof reader.result === "string" ? reader.result : ""
44662
+ });
44663
+ reader.readAsDataURL(f3);
44664
+ }
44665
+ }
44666
+ );
44083
44667
  case "date":
44084
44668
  return /* @__PURE__ */ jsx(
44085
44669
  Input,
@@ -45112,6 +45696,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
45112
45696
  var init_MediaGallery = __esm({
45113
45697
  "components/core/organisms/MediaGallery.tsx"() {
45114
45698
  "use client";
45699
+ init_getNestedValue();
45115
45700
  init_cn();
45116
45701
  init_atoms();
45117
45702
  init_Stack();
@@ -45180,9 +45765,9 @@ var init_MediaGallery = __esm({
45180
45765
  return entityData.map((record, idx) => {
45181
45766
  return {
45182
45767
  id: String(record.id ?? idx),
45183
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
45768
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
45184
45769
  alt: record.alt ? String(record.alt) : void 0,
45185
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
45770
+ thumbnail: resolveImageUrl(record.thumbnail),
45186
45771
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
45187
45772
  };
45188
45773
  });
@@ -49050,6 +49635,7 @@ var init_component_registry_generated = __esm({
49050
49635
  init_DocSidebar();
49051
49636
  init_DocTOC();
49052
49637
  init_DocumentViewer();
49638
+ init_DrawFxLayer();
49053
49639
  init_DrawGroup();
49054
49640
  init_DrawMesh();
49055
49641
  init_DrawShape();
@@ -49080,6 +49666,7 @@ var init_component_registry_generated = __esm({
49080
49666
  init_FormField();
49081
49667
  init_FormSection();
49082
49668
  init_FormSectionHeader();
49669
+ init_FxOverlay();
49083
49670
  init_GameAudioToggle();
49084
49671
  init_GameHud();
49085
49672
  init_GameIcon();
@@ -49320,6 +49907,7 @@ var init_component_registry_generated = __esm({
49320
49907
  "DocSidebar": DocSidebar,
49321
49908
  "DocTOC": DocTOC,
49322
49909
  "DocumentViewer": DocumentViewer,
49910
+ "DrawFxLayer": DrawFxLayer,
49323
49911
  "DrawGroup": DrawGroup,
49324
49912
  "DrawMesh": DrawMesh,
49325
49913
  "DrawShape": DrawShape,
@@ -49350,6 +49938,7 @@ var init_component_registry_generated = __esm({
49350
49938
  "FormField": FormField,
49351
49939
  "FormLayout": FormLayout,
49352
49940
  "FormSectionHeader": FormSectionHeader,
49941
+ "FxOverlay": FxOverlay,
49353
49942
  "GameAudioToggle": GameAudioToggle,
49354
49943
  "GameHud": GameHud,
49355
49944
  "GameIcon": GameIcon,
@@ -49525,7 +50114,8 @@ __export(UISlotRenderer_exports, {
49525
50114
  SlotContentRenderer: () => SlotContentRenderer,
49526
50115
  SuspenseConfigProvider: () => SuspenseConfigProvider,
49527
50116
  UISlotComponent: () => UISlotComponent,
49528
- UISlotRenderer: () => UISlotRenderer
50117
+ UISlotRenderer: () => UISlotRenderer,
50118
+ renderPatternValue: () => renderPatternValue
49529
50119
  });
49530
50120
  function SuspenseConfigProvider({
49531
50121
  config,
@@ -49561,6 +50151,9 @@ function enrichFormFields(fields, entityDef) {
49561
50151
  type: entityField.type,
49562
50152
  required: entityField.required ?? false
49563
50153
  };
50154
+ if (entityField.description) {
50155
+ enriched.hint = entityField.description;
50156
+ }
49564
50157
  if (entityField.values && entityField.values.length > 0) {
49565
50158
  enriched.values = entityField.values;
49566
50159
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -49584,6 +50177,9 @@ function enrichFormFields(fields, entityDef) {
49584
50177
  if (entityField.required && !("required" in obj)) {
49585
50178
  enriched.required = true;
49586
50179
  }
50180
+ if (entityField.description && !obj.hint && !obj.help) {
50181
+ enriched.hint = entityField.description;
50182
+ }
49587
50183
  if (!obj.values && !obj.options) {
49588
50184
  if (entityField.values && entityField.values.length > 0) {
49589
50185
  enriched.values = entityField.values;
@@ -49599,6 +50195,32 @@ function enrichFormFields(fields, entityDef) {
49599
50195
  return field;
49600
50196
  });
49601
50197
  }
50198
+ function enrichDetailFields(fields, entityDef) {
50199
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
50200
+ const metaFor = (name) => {
50201
+ const entityField = fieldMap.get(name);
50202
+ if (!entityField) return void 0;
50203
+ const meta = { type: entityField.type };
50204
+ const values = entityField.values ?? entityField.enumValues;
50205
+ if (values && values.length > 0) meta.values = values;
50206
+ if (entityField.relation) meta.relation = entityField.relation.entity;
50207
+ return meta;
50208
+ };
50209
+ return fields.map((field) => {
50210
+ if (typeof field === "string") {
50211
+ const meta = metaFor(field);
50212
+ return meta ? { key: field, ...meta } : field;
50213
+ }
50214
+ if (field && typeof field === "object" && !Array.isArray(field) && !React77__default.isValidElement(field) && !(field instanceof Date)) {
50215
+ const obj = field;
50216
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
50217
+ if (!fieldName || obj.type) return field;
50218
+ const meta = metaFor(fieldName);
50219
+ return meta ? { ...obj, ...meta } : field;
50220
+ }
50221
+ return field;
50222
+ });
50223
+ }
49602
50224
  function renderContainedPortal(t, slot, content, onDismiss) {
49603
50225
  const slotContent = /* @__PURE__ */ jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsx(SlotContentRenderer, { content, onDismiss }) });
49604
50226
  const slotId = `slot-${slot}`;
@@ -50108,6 +50730,24 @@ function isPatternConfig(value) {
50108
50730
  const record = value;
50109
50731
  return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
50110
50732
  }
50733
+ function renderPatternValue(value) {
50734
+ if (value === null || value === void 0) return null;
50735
+ if (typeof value === "boolean" || typeof value === "function") return null;
50736
+ if (typeof value === "number") return value;
50737
+ if (typeof value === "string") return renderPatternChildren(value, () => {
50738
+ });
50739
+ if (value instanceof Date) return value.toLocaleString();
50740
+ if (React77__default.isValidElement(value)) return value;
50741
+ if (Array.isArray(value)) {
50742
+ return value.map((item, index) => /* @__PURE__ */ jsx(React77__default.Fragment, { children: renderPatternValue(item) }, index));
50743
+ }
50744
+ if (isPatternConfig(value)) {
50745
+ const { type, ...props } = value;
50746
+ return renderPatternChildren({ type, props }, () => {
50747
+ });
50748
+ }
50749
+ return null;
50750
+ }
50111
50751
  function isPlainConfigObject(value) {
50112
50752
  if (React77__default.isValidElement(value)) return false;
50113
50753
  if (value instanceof Date) return false;
@@ -50309,9 +50949,9 @@ function SlotContentRenderer({
50309
50949
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
50310
50950
  }
50311
50951
  }
50312
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
50313
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
50314
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
50952
+ const fieldsContract = getPatternFieldsContract(content.pattern) ?? (getPatternDefinition$1(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
50953
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
50954
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
50315
50955
  }
50316
50956
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
50317
50957
  return /* @__PURE__ */ jsx(
@@ -50421,7 +51061,7 @@ function UISlotRenderer({
50421
51061
  }
50422
51062
  return wrapped;
50423
51063
  }
50424
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51064
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
50425
51065
  var init_UISlotRenderer = __esm({
50426
51066
  "components/core/organisms/UISlotRenderer.tsx"() {
50427
51067
  "use client";
@@ -50452,12 +51092,6 @@ var init_UISlotRenderer = __esm({
50452
51092
  modal: "form",
50453
51093
  drawer: "form"
50454
51094
  };
50455
- FORM_PATTERNS = /* @__PURE__ */ new Set([
50456
- "form",
50457
- "form-section",
50458
- "inline-edit-form",
50459
- "wizard-step"
50460
- ]);
50461
51095
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
50462
51096
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
50463
51097
  "logo",
@@ -52980,4 +53614,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
52980
53614
  });
52981
53615
  }
52982
53616
 
52983
- export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgoGraphCanvas, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmojiPicker, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, LearningScene3D, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichBlockEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, arrowBetween, billboardLabel, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, cylinderBetween, get3DClickPayload, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, meshSphere, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate114 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };
53617
+ export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgoGraphCanvas, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmojiPicker, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, FxOverlay, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, LearningScene3D, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichBlockEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, arrowBetween, billboardLabel, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, cylinderBetween, get3DClickPayload, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, meshSphere, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, renderPatternValue, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate114 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };