@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
package/dist/avl/index.js CHANGED
@@ -7,9 +7,9 @@ import ELK from 'elkjs/lib/elk.bundled.js';
7
7
  import { MarkerType, useReactFlow, Handle, Position, getBezierPath, EdgeLabelRenderer, useNodeId, ReactFlowProvider, BaseEdge, useNodesState, useEdgesState, ReactFlow, Controls, Background, BackgroundVariant } from '@xyflow/react';
8
8
  import { useTranslate } from '@almadar/ui/hooks';
9
9
  import { InMemoryPersistence, StateMachineManager, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, createServerEffectHandlers, EffectExecutor, createContextFromBindings, createTickScheduler, isValidCronExpression, parseDurationString, normalizeCallSiteConfigToValues, interpolateValue } from '@almadar/runtime';
10
- import { FieldTypeSchema, isInlineTrait, isPageReference, buildResolvedTraitConfigs, schemaToIR, getPage, mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, walkSExpr, isRenderBindingMarker, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER, ANIMATION_NAMES } from '@almadar/core';
10
+ import { FieldTypeSchema, isInlineTrait, isPageReference, buildResolvedTraitConfigs, schemaToIR, getPage, mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, walkSExpr, isRenderBindingMarker, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER, isFileValue, ANIMATION_NAMES } from '@almadar/core';
11
11
  import * as LucideIcons2 from 'lucide-react';
12
- import { Loader2, X, Code, FileText, WrapText, Check, Copy, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ZoomOut, ZoomIn, Download, ChevronDown, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, ArrowLeft, HelpCircle, PauseCircle, Search, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, Minus, Eraser, TrendingUp, TrendingDown, AlertCircle, Circle, Clock, CheckCircle2, ChevronUp, Tag, User, DollarSign } from 'lucide-react';
12
+ import { Loader2, X, Code, FileText, WrapText, Check, Copy, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ArrowLeft, ZoomOut, ZoomIn, Download, DollarSign, ChevronDown, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, HelpCircle, PauseCircle, Search, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, Minus, Eraser, TrendingUp, TrendingDown, AlertCircle, Circle, Clock, CheckCircle2, ChevronUp, Tag, User } from 'lucide-react';
13
13
  import { createPortal } from 'react-dom';
14
14
  import { UISlotProvider, useUISlots, useTheme } from '@almadar/ui/context';
15
15
  import { evaluateGuard, evaluateListenPayloadExpr, evaluate, createMinimalContext, executeEffects } from '@almadar/evaluator';
@@ -73,7 +73,7 @@ import { sortableKeyboardCoordinates, useSortable, arrayMove, SortableContext, r
73
73
  import { CSS } from '@dnd-kit/utilities';
74
74
  import { ordered, lib } from 'emojilib';
75
75
  import { forceSimulation, forceLink, forceManyBody, forceCollide, forceX, forceY } from 'd3-force';
76
- import { isDrawHostPattern, getPatternDefinition, getComponentForPattern as getComponentForPattern$1, isEntityAwarePattern } from '@almadar/core/patterns';
76
+ import { isDrawHostPattern, getPatternDefinition, getPatternFieldsContract, getComponentForPattern as getComponentForPattern$1, isEntityAwarePattern } from '@almadar/core/patterns';
77
77
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
78
78
  import { isKnownStdOperator } from '@almadar/std/registry';
79
79
 
@@ -9470,27 +9470,61 @@ var init_InfiniteScrollSentinel = __esm({
9470
9470
  InfiniteScrollSentinel.displayName = "InfiniteScrollSentinel";
9471
9471
  }
9472
9472
  });
9473
- function createParticles(count) {
9474
- return Array.from({ length: count }, () => {
9475
- particleIdCounter += 1;
9476
- return {
9477
- id: particleIdCounter,
9478
- color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)],
9479
- left: 30 + Math.random() * 40,
9480
- delay: Math.random() * 300,
9481
- angle: Math.random() * 360,
9482
- distance: 40 + Math.random() * 80,
9483
- rotation: Math.random() * 720 - 360,
9484
- size: 4 + Math.random() * 6
9485
- };
9486
- });
9473
+
9474
+ // components/core/atoms/fx.ts
9475
+ function resolveFxView(item, presets) {
9476
+ const row = presets?.find((p) => p.type === item.type);
9477
+ if (!row) return item;
9478
+ return {
9479
+ ...item,
9480
+ space: item.space ?? row.space,
9481
+ effect: item.effect ?? row.effect,
9482
+ color: item.color ?? row.color,
9483
+ size: item.size ?? row.size,
9484
+ vx: item.vx ?? row.vx,
9485
+ vy: item.vy ?? row.vy,
9486
+ vz: item.vz ?? row.vz,
9487
+ particleCount: item.particleCount ?? row.particleCount,
9488
+ shape: row.shape,
9489
+ count: row.count,
9490
+ glow: row.glow,
9491
+ gravity: row.gravity,
9492
+ color2: row.color2
9493
+ };
9487
9494
  }
9488
- var CONFETTI_COLORS, particleIdCounter, ConfettiEffect;
9489
- var init_ConfettiEffect = __esm({
9490
- "components/core/atoms/ConfettiEffect.tsx"() {
9491
- "use client";
9492
- init_cn();
9493
- init_Box();
9495
+ function fxLifecycle(item, epochNowMs, tickMs) {
9496
+ const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
9497
+ const lifeMs = maxTtl * tickMs;
9498
+ const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
9499
+ const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
9500
+ return { lifeMs, ageMs, progress, fade: 1 - progress };
9501
+ }
9502
+ function fxHash01(seed, salt) {
9503
+ let h = 2166136261 ^ salt;
9504
+ for (let i = 0; i < seed.length; i++) {
9505
+ h ^= seed.charCodeAt(i);
9506
+ h = Math.imul(h, 16777619);
9507
+ }
9508
+ h ^= h >>> 13;
9509
+ h = Math.imul(h, 1274126177);
9510
+ h ^= h >>> 16;
9511
+ return (h >>> 0) / 4294967296;
9512
+ }
9513
+ function createConfettiParticles(count, seed) {
9514
+ return Array.from({ length: count }, (_, i) => ({
9515
+ id: i,
9516
+ color: CONFETTI_COLORS[Math.floor(fxHash01(seed, i * 7 + 1) * CONFETTI_COLORS.length)],
9517
+ left: 30 + fxHash01(seed, i * 7 + 2) * 40,
9518
+ delay: fxHash01(seed, i * 7 + 3) * 300,
9519
+ angle: fxHash01(seed, i * 7 + 4) * 360,
9520
+ distance: 40 + fxHash01(seed, i * 7 + 5) * 80,
9521
+ rotation: fxHash01(seed, i * 7 + 6) * 720 - 360,
9522
+ size: 4 + fxHash01(seed, i * 7 + 7) * 6
9523
+ }));
9524
+ }
9525
+ var CONFETTI_COLORS, CONFETTI_BURST_KEYFRAMES;
9526
+ var init_fx = __esm({
9527
+ "components/core/atoms/fx.ts"() {
9494
9528
  CONFETTI_COLORS = [
9495
9529
  "var(--color-primary)",
9496
9530
  "var(--color-success)",
@@ -9499,7 +9533,30 @@ var init_ConfettiEffect = __esm({
9499
9533
  "gold",
9500
9534
  "dodgerblue"
9501
9535
  ];
9502
- particleIdCounter = 0;
9536
+ CONFETTI_BURST_KEYFRAMES = `
9537
+ @keyframes confetti-burst {
9538
+ 0% {
9539
+ opacity: 1;
9540
+ transform: translate(0, 0) rotate(0deg) scale(1);
9541
+ }
9542
+ 70% {
9543
+ opacity: 1;
9544
+ }
9545
+ 100% {
9546
+ opacity: 0;
9547
+ transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
9548
+ }
9549
+ }
9550
+ `;
9551
+ }
9552
+ });
9553
+ var ConfettiEffect;
9554
+ var init_ConfettiEffect = __esm({
9555
+ "components/core/atoms/ConfettiEffect.tsx"() {
9556
+ "use client";
9557
+ init_cn();
9558
+ init_Box();
9559
+ init_fx();
9503
9560
  ConfettiEffect = ({
9504
9561
  trigger,
9505
9562
  duration = 2e3,
@@ -9508,11 +9565,13 @@ var init_ConfettiEffect = __esm({
9508
9565
  }) => {
9509
9566
  const [particles, setParticles] = useState([]);
9510
9567
  const previousTriggerRef = useRef(false);
9568
+ const burstRef = useRef(0);
9511
9569
  useEffect(() => {
9512
9570
  const wasFalse = !previousTriggerRef.current;
9513
9571
  previousTriggerRef.current = trigger;
9514
9572
  if (trigger && wasFalse) {
9515
- const newParticles = createParticles(particleCount);
9573
+ burstRef.current += 1;
9574
+ const newParticles = createConfettiParticles(particleCount, `confetti-${burstRef.current}`);
9516
9575
  setParticles(newParticles);
9517
9576
  const timer = window.setTimeout(() => {
9518
9577
  setParticles([]);
@@ -9560,21 +9619,7 @@ var init_ConfettiEffect = __esm({
9560
9619
  p.id
9561
9620
  );
9562
9621
  }),
9563
- /* @__PURE__ */ jsx("style", { children: `
9564
- @keyframes confetti-burst {
9565
- 0% {
9566
- opacity: 1;
9567
- transform: translate(0, 0) rotate(0deg) scale(1);
9568
- }
9569
- 70% {
9570
- opacity: 1;
9571
- }
9572
- 100% {
9573
- opacity: 0;
9574
- transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
9575
- }
9576
- }
9577
- ` })
9622
+ /* @__PURE__ */ jsx("style", { children: CONFETTI_BURST_KEYFRAMES })
9578
9623
  ]
9579
9624
  }
9580
9625
  );
@@ -13130,7 +13175,7 @@ var init_DrawShape = __esm({
13130
13175
  const cx = p.x + (node.offsetX ?? 0) * tw;
13131
13176
  const cy = p.y + (node.offsetY ?? 0) * tw;
13132
13177
  const rx = (node.radiusX ?? 0) * tw;
13133
- const ry = (node.radiusY ?? rx) * tw;
13178
+ const ry = (node.radiusY ?? node.radiusX ?? 0) * tw;
13134
13179
  if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
13135
13180
  if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
13136
13181
  if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
@@ -13320,6 +13365,172 @@ var init_DrawTextLayer = __esm({
13320
13365
  };
13321
13366
  }
13322
13367
  });
13368
+
13369
+ // components/game/molecules/DrawFxLayer.tsx
13370
+ function fxPosition(view, dim, ageSec) {
13371
+ const g = view.gravity ?? 0;
13372
+ const fall = 0.5 * g * ageSec * ageSec;
13373
+ {
13374
+ const base2 = view.position ?? { x: view.x, y: view.z ?? view.y ?? 0 };
13375
+ if (!Number.isFinite(base2.x) || !Number.isFinite(base2.y)) return void 0;
13376
+ return {
13377
+ x: base2.x + (view.vx ?? 0) * ageSec,
13378
+ y: base2.y + (view.vz ?? 0) * ageSec + fall
13379
+ };
13380
+ }
13381
+ }
13382
+ function sparkShape(view, pos, i, fade, progress) {
13383
+ const size = view.size ?? 0.5;
13384
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
13385
+ const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut2(progress);
13386
+ const r2 = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
13387
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
13388
+ return {
13389
+ type: "draw-shape",
13390
+ shape: "ellipse",
13391
+ position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
13392
+ anchor: "center",
13393
+ radiusX: r2,
13394
+ fill: color,
13395
+ blendMode: "lighter",
13396
+ opacity: fade,
13397
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
13398
+ };
13399
+ }
13400
+ function proceduralShapes(view, pos, fade, progress) {
13401
+ const size = view.size ?? 0.5;
13402
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
13403
+ switch (view.shape ?? "spark") {
13404
+ case "ring": {
13405
+ return [
13406
+ {
13407
+ type: "draw-shape",
13408
+ shape: "ellipse",
13409
+ position: pos,
13410
+ anchor: "center",
13411
+ radiusX: size * easeOut2(progress),
13412
+ stroke: view.color2 ?? color,
13413
+ strokeWidth: 2,
13414
+ blendMode: "lighter",
13415
+ opacity: fade,
13416
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
13417
+ }
13418
+ ];
13419
+ }
13420
+ case "puff": {
13421
+ const count = view.count ?? 3;
13422
+ return Array.from({ length: count }, (_, i) => {
13423
+ const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
13424
+ const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut2(progress);
13425
+ return {
13426
+ type: "draw-shape",
13427
+ shape: "ellipse",
13428
+ position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
13429
+ anchor: "center",
13430
+ radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
13431
+ fill: i === 0 && view.color2 ? view.color2 : color,
13432
+ opacity: fade * 0.6,
13433
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
13434
+ };
13435
+ });
13436
+ }
13437
+ case "streak": {
13438
+ const count = view.count ?? 5;
13439
+ return Array.from({ length: count }, (_, i) => {
13440
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
13441
+ const inner = size * (0.2 + 0.8 * easeOut2(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
13442
+ const len = size * 0.35;
13443
+ return {
13444
+ type: "draw-shape",
13445
+ shape: "ellipse",
13446
+ position: pos,
13447
+ anchor: "center",
13448
+ offsetX: inner + len / 2,
13449
+ offsetY: 0,
13450
+ radiusX: len / 2,
13451
+ radiusY: size * 0.04,
13452
+ rotate: angle,
13453
+ fill: color,
13454
+ blendMode: "lighter",
13455
+ opacity: fade,
13456
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
13457
+ };
13458
+ });
13459
+ }
13460
+ default: {
13461
+ const count = view.count ?? 6;
13462
+ return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
13463
+ }
13464
+ }
13465
+ }
13466
+ function expandFxItem(view, node, epochNowMs, dim) {
13467
+ if (view.space === "screen") return [];
13468
+ const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
13469
+ const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
13470
+ if (progress >= 1) return [];
13471
+ const pos = fxPosition(view, dim, ageMs / 1e3);
13472
+ if (!pos) return [];
13473
+ const out = [];
13474
+ const artItems = node.art?.[view.type]?.["idle"];
13475
+ const sprite = node.sprites?.[view.type];
13476
+ if (Array.isArray(artItems)) {
13477
+ out.push({
13478
+ type: "draw-group",
13479
+ position: pos,
13480
+ opacity: fade,
13481
+ ...view.size !== void 0 ? { scale: view.size } : {},
13482
+ items: artItems
13483
+ });
13484
+ } else if (sprite?.url) {
13485
+ const spriteSize = view.size ?? 0.6;
13486
+ out.push({
13487
+ type: "draw-sprite",
13488
+ position: pos,
13489
+ asset: sprite,
13490
+ anchor: "center",
13491
+ width: spriteSize,
13492
+ height: spriteSize,
13493
+ opacity: fade
13494
+ });
13495
+ } else {
13496
+ out.push(...proceduralShapes(view, pos, fade, progress));
13497
+ }
13498
+ if (view.message) {
13499
+ const text = {
13500
+ type: "draw-text",
13501
+ text: view.message,
13502
+ position: pos,
13503
+ offsetY: -(0.15 + 0.55 * progress),
13504
+ color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
13505
+ opacity: fade
13506
+ };
13507
+ out.push(text);
13508
+ }
13509
+ return out;
13510
+ }
13511
+ function expandFxLayer(node, epochNowMs, dim) {
13512
+ if (!Array.isArray(node.items)) return [];
13513
+ const out = [];
13514
+ for (const item of node.items) {
13515
+ if (!item || typeof item.id !== "string") continue;
13516
+ out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim));
13517
+ }
13518
+ return out;
13519
+ }
13520
+ function DrawFxLayer(_props) {
13521
+ return null;
13522
+ }
13523
+ var DEFAULT_TICK_MS, DEFAULT_TEXT_COLOR, DEFAULT_SPARK_COLOR, easeOut2;
13524
+ var init_DrawFxLayer = __esm({
13525
+ "components/game/molecules/DrawFxLayer.tsx"() {
13526
+ "use client";
13527
+ init_fx();
13528
+ DEFAULT_TICK_MS = 500;
13529
+ DEFAULT_TEXT_COLOR = "#ffe066";
13530
+ DEFAULT_SPARK_COLOR = "#ffffff";
13531
+ easeOut2 = (t) => 1 - (1 - t) * (1 - t);
13532
+ }
13533
+ });
13323
13534
  function paintDrawable(painter, node, dctx) {
13324
13535
  switch (node.type) {
13325
13536
  case "draw-sprite":
@@ -13367,6 +13578,11 @@ function paintDrawable(painter, node, dctx) {
13367
13578
  case "draw-text-layer":
13368
13579
  paintTextLayer(painter, node, dctx);
13369
13580
  break;
13581
+ case "draw-fx-layer": {
13582
+ const epochNow = dctx.time > 0 && typeof performance !== "undefined" ? performance.timeOrigin + dctx.time : 0;
13583
+ for (const child of expandFxLayer(node, epochNow, "2d")) paintDrawable(painter, child, dctx);
13584
+ break;
13585
+ }
13370
13586
  }
13371
13587
  }
13372
13588
  var paint2dLog, warnedUnsupported2d, warnUnsupported2d;
@@ -13381,6 +13597,7 @@ var init_paintDispatch = __esm({
13381
13597
  init_DrawSpriteLayer();
13382
13598
  init_DrawShapeLayer();
13383
13599
  init_DrawTextLayer();
13600
+ init_DrawFxLayer();
13384
13601
  paint2dLog = createLogger("almadar:ui:drawable-2d");
13385
13602
  warnedUnsupported2d = /* @__PURE__ */ new Set();
13386
13603
  warnUnsupported2d = (kind) => {
@@ -13629,6 +13846,7 @@ function Canvas2D({
13629
13846
  return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
13630
13847
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
13631
13848
  if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
13849
+ if (node.type === "draw-fx-layer") return Array.isArray(node.items) && node.items.length > 0;
13632
13850
  return false;
13633
13851
  };
13634
13852
  const animRafRef = useRef(0);
@@ -17259,7 +17477,7 @@ var init_AlgoGraphCanvas = __esm({
17259
17477
  }
17260
17478
  const badgeGeoms = [];
17261
17479
  for (const g of nodeGeoms) {
17262
- if (!g.badge) continue;
17480
+ if (!g.badge || g.badge.text === "") continue;
17263
17481
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
17264
17482
  badgeGeoms.push({
17265
17483
  cx: g.x + g.radius * 0.75,
@@ -17325,7 +17543,7 @@ var init_AlgoGraphCanvas = __esm({
17325
17543
  };
17326
17544
  }
17327
17545
  });
17328
- 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;
17546
+ 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;
17329
17547
  var init_AlgorithmCanvas = __esm({
17330
17548
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
17331
17549
  "use client";
@@ -17352,9 +17570,18 @@ var init_AlgorithmCanvas = __esm({
17352
17570
  FRAME_ACTIVE_COLOR = "#3b82f6";
17353
17571
  FRAME_RETURNING_COLOR = "#f59e0b";
17354
17572
  FRAME_DONE_COLOR = "#94a3b8";
17573
+ FRAME_RIM_COLOR = {
17574
+ active: "#1d4ed8",
17575
+ returning: "#b45309",
17576
+ done: "#64748b"
17577
+ };
17355
17578
  FRAME_LABEL_COLOR = "#ffffff";
17356
17579
  FRAME_DETAIL_COLOR = "#e2e8f0";
17357
17580
  FRAME_TWO_LINE_MIN_H = 22;
17581
+ FRAME_STRIP_H = 44;
17582
+ FRAME_GAP = 6;
17583
+ FRAME_BOTTOM_PAD = 8;
17584
+ FRAME_MIN_H = 12;
17358
17585
  BUCKET_INDEX_FILL = "#e2e8f0";
17359
17586
  BUCKET_INDEX_STROKE = "#9ca3af";
17360
17587
  BUCKET_INDEX_TEXT = "#374151";
@@ -17851,14 +18078,18 @@ var init_AlgorithmCanvas = __esm({
17851
18078
  if (frames.length > 0) {
17852
18079
  const panelYFrames = panelY.frames;
17853
18080
  const n = frames.length;
17854
- const frameH = panelHeight / n;
18081
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
18082
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
18083
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
17855
18084
  const x = 8;
17856
18085
  const w = width - 16;
18086
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
17857
18087
  frames.forEach((f3, i) => {
17858
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
18088
+ const y = bottom - i * stride - frameH;
17859
18089
  const state = f3.state ?? "active";
17860
18090
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
17861
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
18091
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
18092
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
17862
18093
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
17863
18094
  out.push({
17864
18095
  type: "text",
@@ -17866,14 +18097,14 @@ var init_AlgorithmCanvas = __esm({
17866
18097
  y: y + frameH * 0.35,
17867
18098
  text: f3.label,
17868
18099
  color: FRAME_LABEL_COLOR,
17869
- fontSize: 10,
18100
+ fontSize: 11,
17870
18101
  align: "left"
17871
18102
  });
17872
18103
  if (f3.detail) {
17873
18104
  out.push({
17874
18105
  type: "text",
17875
18106
  x: 16,
17876
- y: y + frameH * 0.7,
18107
+ y: y + frameH * 0.72,
17877
18108
  text: f3.detail,
17878
18109
  color: FRAME_DETAIL_COLOR,
17879
18110
  fontSize: 10,
@@ -23559,8 +23790,6 @@ var init_ButtonGroup = __esm({
23559
23790
  ButtonGroup.displayName = "ButtonGroup";
23560
23791
  }
23561
23792
  });
23562
-
23563
- // lib/getNestedValue.ts
23564
23793
  function getNestedValue(obj, path) {
23565
23794
  if (obj === null || obj === void 0 || !path) {
23566
23795
  return void 0;
@@ -23581,6 +23810,15 @@ function getNestedValue(obj, path) {
23581
23810
  }
23582
23811
  return value;
23583
23812
  }
23813
+ function resolveImageUrl(value) {
23814
+ if (typeof value === "string") {
23815
+ return value;
23816
+ }
23817
+ if (isFileValue(value)) {
23818
+ return value.url;
23819
+ }
23820
+ return void 0;
23821
+ }
23584
23822
  var init_getNestedValue = __esm({
23585
23823
  "lib/getNestedValue.ts"() {
23586
23824
  }
@@ -24285,8 +24523,8 @@ var init_CardGrid = __esm({
24285
24523
  actionPayload: { row: itemData },
24286
24524
  children: [
24287
24525
  imageField && (() => {
24288
- const imgUrl = getNestedValue(itemData, imageField);
24289
- if (!imgUrl || typeof imgUrl !== "string") return null;
24526
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
24527
+ if (!imgUrl) return null;
24290
24528
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
24291
24529
  "img",
24292
24530
  {
@@ -28316,8 +28554,8 @@ function DataGrid({
28316
28554
  ),
28317
28555
  children: [
28318
28556
  imageField && (() => {
28319
- const imgUrl = getNestedValue(itemData, imageField);
28320
- if (!imgUrl || typeof imgUrl !== "string") return null;
28557
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
28558
+ if (!imgUrl) return null;
28321
28559
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
28322
28560
  "img",
28323
28561
  {
@@ -30371,6 +30609,196 @@ var init_PageTransition = __esm({
30371
30609
  PageTransition.displayName = "PageTransition";
30372
30610
  }
30373
30611
  });
30612
+ function ConfettiBurst({ item, lifeMs }) {
30613
+ const particles = createConfettiParticles(item.particleCount ?? 30, item.id);
30614
+ const left = (item.x ?? 0.5) * 100;
30615
+ const top = (item.z ?? item.y ?? 0.4) * 100;
30616
+ return /* @__PURE__ */ jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: particles.map((p) => {
30617
+ const rad = p.angle * Math.PI / 180;
30618
+ const tx = Math.cos(rad) * p.distance * (item.size ?? 1);
30619
+ const ty = Math.sin(rad) * p.distance * (item.size ?? 1) - 20;
30620
+ return /* @__PURE__ */ jsx(
30621
+ Box,
30622
+ {
30623
+ className: "absolute rounded-sm",
30624
+ style: {
30625
+ left: (p.left - 50) * 2,
30626
+ top: -10,
30627
+ width: p.size,
30628
+ height: p.size,
30629
+ backgroundColor: item.color ?? p.color,
30630
+ animation: `confetti-burst ${Math.max(lifeMs - p.delay, 200)}ms ease-out ${p.delay}ms forwards`,
30631
+ opacity: 0,
30632
+ "--confetti-tx": `${tx}px`,
30633
+ "--confetti-ty": `${ty}px`,
30634
+ "--confetti-rotate": `${p.rotation}deg`
30635
+ }
30636
+ },
30637
+ p.id
30638
+ );
30639
+ }) });
30640
+ }
30641
+ function SparkleBurst({ item, lifeMs }) {
30642
+ const count = item.particleCount ?? 8;
30643
+ const scale = item.size ?? 1;
30644
+ const left = (item.x ?? 0.5) * 100;
30645
+ const top = (item.z ?? item.y ?? 0.4) * 100;
30646
+ return /* @__PURE__ */ jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: Array.from({ length: count }, (_, i) => {
30647
+ const dx = (fxHash01(item.id, i * 4) - 0.5) * 160 * scale;
30648
+ const dy = (fxHash01(item.id, i * 4 + 1) - 0.5) * 120 * scale;
30649
+ const dot = (4 + fxHash01(item.id, i * 4 + 2) * 5) * scale;
30650
+ const delay = fxHash01(item.id, i * 4 + 3) * lifeMs * 0.4;
30651
+ return /* @__PURE__ */ jsx(
30652
+ Box,
30653
+ {
30654
+ className: "absolute rounded-full",
30655
+ style: {
30656
+ left: dx,
30657
+ top: dy,
30658
+ width: dot,
30659
+ height: dot,
30660
+ backgroundColor: item.color ?? "gold",
30661
+ opacity: 0,
30662
+ animation: `fx-overlay-sparkle ${Math.max(lifeMs - delay, 200)}ms ease-in-out ${delay}ms forwards`
30663
+ }
30664
+ },
30665
+ i
30666
+ );
30667
+ }) });
30668
+ }
30669
+ function OverlayFxNode({ item, tickMs }) {
30670
+ const lifeMs = lifeMsOf(item, tickMs);
30671
+ switch (item.effect ?? "sparkle") {
30672
+ case "confetti":
30673
+ return /* @__PURE__ */ jsx(ConfettiBurst, { item, lifeMs });
30674
+ case "flash":
30675
+ return /* @__PURE__ */ jsx(
30676
+ Box,
30677
+ {
30678
+ position: "absolute",
30679
+ className: "inset-0",
30680
+ style: {
30681
+ backgroundColor: item.color ?? "#ffffff",
30682
+ opacity: 0,
30683
+ animation: `fx-overlay-flash ${lifeMs}ms ease-out forwards`
30684
+ }
30685
+ }
30686
+ );
30687
+ case "streak-glow":
30688
+ return /* @__PURE__ */ jsx(
30689
+ Box,
30690
+ {
30691
+ position: "absolute",
30692
+ className: "inset-0",
30693
+ style: {
30694
+ boxShadow: `inset 0 0 ${60 * (item.size ?? 1)}px ${item.color ?? "var(--color-warning)"}`,
30695
+ opacity: 0,
30696
+ animation: `fx-overlay-glow ${lifeMs}ms ease-in-out forwards`
30697
+ }
30698
+ }
30699
+ );
30700
+ case "float-text": {
30701
+ if (!item.message) return null;
30702
+ return /* @__PURE__ */ jsx(
30703
+ Box,
30704
+ {
30705
+ position: "absolute",
30706
+ style: {
30707
+ left: `${(item.x ?? 0.5) * 100}%`,
30708
+ top: `${(item.z ?? item.y ?? 0.4) * 100}%`,
30709
+ color: item.color ?? "var(--color-success)",
30710
+ fontWeight: 700,
30711
+ fontSize: 16 * (item.size ?? 1),
30712
+ textShadow: "0 1px 2px rgba(0,0,0,0.4)",
30713
+ opacity: 0,
30714
+ animation: `fx-overlay-float ${lifeMs}ms ease-out forwards`,
30715
+ whiteSpace: "nowrap"
30716
+ },
30717
+ children: item.message
30718
+ }
30719
+ );
30720
+ }
30721
+ case "shake":
30722
+ return null;
30723
+ default:
30724
+ return /* @__PURE__ */ jsx(SparkleBurst, { item, lifeMs });
30725
+ }
30726
+ }
30727
+ var DEFAULT_TICK_MS2, FX_OVERLAY_KEYFRAMES, lifeMsOf, FxOverlay;
30728
+ var init_FxOverlay = __esm({
30729
+ "components/core/molecules/FxOverlay.tsx"() {
30730
+ "use client";
30731
+ init_cn();
30732
+ init_Box();
30733
+ init_fx();
30734
+ DEFAULT_TICK_MS2 = 500;
30735
+ FX_OVERLAY_KEYFRAMES = `
30736
+ ${CONFETTI_BURST_KEYFRAMES}
30737
+ @keyframes fx-overlay-flash {
30738
+ 0% { opacity: 0.75; }
30739
+ 100% { opacity: 0; }
30740
+ }
30741
+ @keyframes fx-overlay-glow {
30742
+ 0% { opacity: 0.9; }
30743
+ 60% { opacity: 0.6; }
30744
+ 100% { opacity: 0; }
30745
+ }
30746
+ @keyframes fx-overlay-sparkle {
30747
+ 0% { opacity: 0; transform: scale(0); }
30748
+ 30% { opacity: 1; transform: scale(1); }
30749
+ 100% { opacity: 0; transform: scale(0.3); }
30750
+ }
30751
+ @keyframes fx-overlay-float {
30752
+ 0% { opacity: 0; transform: translate(-50%, 8px); }
30753
+ 15% { opacity: 1; }
30754
+ 100% { opacity: 0; transform: translate(-50%, -40px); }
30755
+ }
30756
+ @keyframes fx-overlay-shake {
30757
+ 0% { transform: translateX(0); }
30758
+ 15% { transform: translateX(-6px); }
30759
+ 30% { transform: translateX(5px); }
30760
+ 45% { transform: translateX(-4px); }
30761
+ 60% { transform: translateX(3px); }
30762
+ 75% { transform: translateX(-2px); }
30763
+ 100% { transform: translateX(0); }
30764
+ }
30765
+ `;
30766
+ lifeMsOf = (it, tickMs) => Math.max(it.maxTtl ?? it.ttl, it.ttl, 1) * tickMs;
30767
+ FxOverlay = ({ items, tickMs = DEFAULT_TICK_MS2, children, className }) => {
30768
+ const screenItems = (Array.isArray(items) ? items : []).filter(
30769
+ (it) => Boolean(it) && typeof it.id === "string" && it.space !== "world"
30770
+ );
30771
+ const shakeItem = [...screenItems].reverse().find((it) => it.effect === "shake");
30772
+ const overlay = /* @__PURE__ */ jsxs(
30773
+ Box,
30774
+ {
30775
+ position: "absolute",
30776
+ className: cn("inset-0 pointer-events-none overflow-hidden z-50", !children && className),
30777
+ "aria-hidden": "true",
30778
+ children: [
30779
+ screenItems.map((it) => /* @__PURE__ */ jsx(OverlayFxNode, { item: it, tickMs }, it.id)),
30780
+ /* @__PURE__ */ jsx("style", { children: FX_OVERLAY_KEYFRAMES })
30781
+ ]
30782
+ }
30783
+ );
30784
+ if (children !== void 0 && children !== null) {
30785
+ return /* @__PURE__ */ jsxs(Box, { position: "relative", className, children: [
30786
+ /* @__PURE__ */ jsx(
30787
+ Box,
30788
+ {
30789
+ style: shakeItem ? { animation: `fx-overlay-shake ${Math.min(lifeMsOf(shakeItem, tickMs), 600)}ms ease-in-out` } : void 0,
30790
+ children
30791
+ },
30792
+ shakeItem?.id ?? "steady"
30793
+ ),
30794
+ overlay
30795
+ ] });
30796
+ }
30797
+ return overlay;
30798
+ };
30799
+ FxOverlay.displayName = "FxOverlay";
30800
+ }
30801
+ });
30374
30802
  function computePopoverStyle(position, triggerRect, popoverWidth) {
30375
30803
  if (position === "left" || position === "right") {
30376
30804
  return {
@@ -30927,7 +31355,7 @@ var init_SearchInput = __esm({
30927
31355
  /* @__PURE__ */ jsx(
30928
31356
  Input,
30929
31357
  {
30930
- type: "text",
31358
+ type: "search",
30931
31359
  value: searchValue,
30932
31360
  onChange: handleChange,
30933
31361
  placeholder: resolvedPlaceholder,
@@ -32253,6 +32681,7 @@ var MathCanvas;
32253
32681
  var init_MathCanvas = __esm({
32254
32682
  "components/learning/molecules/MathCanvas.tsx"() {
32255
32683
  "use client";
32684
+ init_useEventBus();
32256
32685
  init_atoms();
32257
32686
  init_Stack();
32258
32687
  init_LearningCanvas();
@@ -32284,9 +32713,32 @@ var init_MathCanvas = __esm({
32284
32713
  interactive = false,
32285
32714
  animate = false,
32286
32715
  onShapeClick,
32716
+ keyMap,
32717
+ keyUpMap,
32287
32718
  isLoading,
32288
32719
  error
32289
32720
  }) => {
32721
+ const eventBus = useEventBus();
32722
+ useEffect(() => {
32723
+ if (!keyMap && !keyUpMap) return;
32724
+ const onDown = (e) => {
32725
+ const ev = keyMap?.[e.code];
32726
+ if (ev) {
32727
+ eventBus.emit(`UI:${ev}`, {});
32728
+ e.preventDefault();
32729
+ }
32730
+ };
32731
+ const onUp = (e) => {
32732
+ const ev = keyUpMap?.[e.code];
32733
+ if (ev) eventBus.emit(`UI:${ev}`, {});
32734
+ };
32735
+ window.addEventListener("keydown", onDown);
32736
+ window.addEventListener("keyup", onUp);
32737
+ return () => {
32738
+ window.removeEventListener("keydown", onDown);
32739
+ window.removeEventListener("keyup", onUp);
32740
+ };
32741
+ }, [keyMap, keyUpMap, eventBus]);
32290
32742
  const derivedShapes = useMemo(() => {
32291
32743
  const out = [];
32292
32744
  const margin = 24;
@@ -32299,11 +32751,11 @@ var init_MathCanvas = __esm({
32299
32751
  if (showGrid) {
32300
32752
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
32301
32753
  const px = mapX(x);
32302
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
32754
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32303
32755
  }
32304
32756
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
32305
32757
  const py = mapY(y);
32306
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
32758
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32307
32759
  }
32308
32760
  }
32309
32761
  if (showTickLabels) {
@@ -32352,7 +32804,7 @@ var init_MathCanvas = __esm({
32352
32804
  x: mapX((first.x + last.x) / 2),
32353
32805
  y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
32354
32806
  text: region.label,
32355
- color: "#111827",
32807
+ color,
32356
32808
  fontSize: 11
32357
32809
  });
32358
32810
  }
@@ -32385,14 +32837,14 @@ var init_MathCanvas = __esm({
32385
32837
  const px = mapX(guide.at);
32386
32838
  out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
32387
32839
  if (guide.label) {
32388
- out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color: "#111827", fontSize: 11 });
32840
+ out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
32389
32841
  }
32390
32842
  } else {
32391
32843
  if (guide.at < yMin || guide.at > yMax) continue;
32392
32844
  const py = mapY(guide.at);
32393
32845
  out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
32394
32846
  if (guide.label) {
32395
- out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color: "#111827", fontSize: 11, align: "right" });
32847
+ out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: 11, align: "right" });
32396
32848
  }
32397
32849
  }
32398
32850
  }
@@ -32458,7 +32910,7 @@ var init_MathCanvas = __esm({
32458
32910
  x: (x1 + x2) / 2,
32459
32911
  y: xAxisY - peak - 8,
32460
32912
  text: hop.label,
32461
- color: "#111827",
32913
+ color,
32462
32914
  fontSize: 10,
32463
32915
  align: "center"
32464
32916
  });
@@ -32485,7 +32937,7 @@ var init_MathCanvas = __esm({
32485
32937
  x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
32486
32938
  y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
32487
32939
  text: angle.label,
32488
- color: "#111827",
32940
+ color,
32489
32941
  fontSize: 11,
32490
32942
  align: "center"
32491
32943
  });
@@ -32503,7 +32955,7 @@ var init_MathCanvas = __esm({
32503
32955
  fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
32504
32956
  });
32505
32957
  if (p.label) {
32506
- out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
32958
+ out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: 12 });
32507
32959
  }
32508
32960
  }
32509
32961
  for (const v of vectors) {
@@ -32514,7 +32966,7 @@ var init_MathCanvas = __esm({
32514
32966
  const y2 = mapY(v.y + v.vy);
32515
32967
  out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
32516
32968
  if (v.label) {
32517
- out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
32969
+ out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: 12 });
32518
32970
  }
32519
32971
  }
32520
32972
  out.push(...shapes);
@@ -33778,13 +34230,13 @@ var init_MapView = __esm({
33778
34230
  shadowSize: [41, 41]
33779
34231
  });
33780
34232
  L.Marker.prototype.options.icon = defaultIcon;
33781
- const { useEffect: useEffect64, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__default;
34233
+ const { useEffect: useEffect65, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__default;
33782
34234
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33783
34235
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33784
34236
  function MapUpdater({ centerLat, centerLng, zoom }) {
33785
34237
  const map = useMap();
33786
34238
  const prevRef = useRef65({ centerLat, centerLng, zoom });
33787
- useEffect64(() => {
34239
+ useEffect65(() => {
33788
34240
  const prev = prevRef.current;
33789
34241
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
33790
34242
  map.setView([centerLat, centerLng], zoom);
@@ -33795,7 +34247,7 @@ var init_MapView = __esm({
33795
34247
  }
33796
34248
  function MapClickHandler({ onMapClick }) {
33797
34249
  const map = useMap();
33798
- useEffect64(() => {
34250
+ useEffect65(() => {
33799
34251
  if (!onMapClick) return;
33800
34252
  const handler = (e) => {
33801
34253
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -40202,6 +40654,7 @@ var init_PageHeader = __esm({
40202
40654
  className
40203
40655
  }) => {
40204
40656
  const eventBus = useEventBus();
40657
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
40205
40658
  const handleBack = () => {
40206
40659
  eventBus.emit(`UI:${backEvent}`);
40207
40660
  };
@@ -40249,15 +40702,15 @@ var init_PageHeader = __esm({
40249
40702
  /* @__PURE__ */ jsxs(Box, { children: [
40250
40703
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3", children: [
40251
40704
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
40252
- status && /* @__PURE__ */ jsx(
40705
+ statusBadge && /* @__PURE__ */ jsx(
40253
40706
  Typography,
40254
40707
  {
40255
40708
  variant: "small",
40256
40709
  className: cn(
40257
40710
  "px-2.5 py-1 rounded-full text-xs font-medium",
40258
- statusColors2[status.variant || "default"]
40711
+ statusColors2[statusBadge.variant || "default"]
40259
40712
  ),
40260
- children: status.label
40713
+ children: statusBadge.label
40261
40714
  }
40262
40715
  )
40263
40716
  ] }),
@@ -43486,7 +43939,7 @@ function formatFieldValue2(value, fieldName) {
43486
43939
  }
43487
43940
  return String(value);
43488
43941
  }
43489
- function renderRichFieldValue(value, fieldName, fieldType) {
43942
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
43490
43943
  if (value === void 0 || value === null) return "\u2014";
43491
43944
  const str = String(value);
43492
43945
  switch (fieldType) {
@@ -43553,19 +44006,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
43553
44006
  }
43554
44007
  );
43555
44008
  case "date":
43556
- case "datetime": {
44009
+ case "datetime":
44010
+ case "timestamp": {
43557
44011
  const d = new Date(str);
43558
44012
  if (!isNaN(d.getTime())) {
43559
44013
  return d.toLocaleDateString(void 0, {
43560
44014
  year: "numeric",
43561
44015
  month: "long",
43562
44016
  day: "numeric",
43563
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
44017
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
43564
44018
  });
43565
44019
  }
43566
44020
  return str;
43567
44021
  }
44022
+ case "money": {
44023
+ const n = typeof value === "number" ? value : Number(str);
44024
+ if (!isNaN(n)) {
44025
+ return new Intl.NumberFormat(void 0, {
44026
+ style: "currency",
44027
+ currency: "USD"
44028
+ }).format(n);
44029
+ }
44030
+ return str;
44031
+ }
44032
+ case "relation": {
44033
+ const match = meta?.options?.find((opt) => opt.value === str);
44034
+ return match ? match.label : str;
44035
+ }
44036
+ case "file": {
44037
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
44038
+ const file = value;
44039
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
44040
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
44041
+ return /* @__PURE__ */ jsxs(
44042
+ "a",
44043
+ {
44044
+ href: typeof file.url === "string" ? file.url : void 0,
44045
+ download: label,
44046
+ 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",
44047
+ children: [
44048
+ /* @__PURE__ */ jsx(Icon, { icon: FileText, size: "sm", className: "text-muted-foreground" }),
44049
+ label,
44050
+ size && /* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
44051
+ ]
44052
+ }
44053
+ );
44054
+ }
44055
+ return str;
44056
+ }
43568
44057
  default:
44058
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
44059
+ return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
44060
+ }
43569
44061
  return formatFieldValue2(value, fieldName);
43570
44062
  }
43571
44063
  }
@@ -43592,8 +44084,18 @@ function buildFieldTypeMap(fields) {
43592
44084
  const map = {};
43593
44085
  if (!fields) return map;
43594
44086
  for (const f3 of fields) {
43595
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
43596
- map[f3.name] = f3.type;
44087
+ if (typeof f3 === "object" && f3.type !== void 0) {
44088
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
44089
+ }
44090
+ }
44091
+ return map;
44092
+ }
44093
+ function buildFieldMetaMap(fields) {
44094
+ const map = {};
44095
+ if (!fields) return map;
44096
+ for (const f3 of fields) {
44097
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
44098
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
43597
44099
  }
43598
44100
  }
43599
44101
  return map;
@@ -43613,6 +44115,7 @@ var init_DetailPanel = __esm({
43613
44115
  init_format();
43614
44116
  init_getNestedValue();
43615
44117
  init_useEventBus();
44118
+ init_UploadDropZone();
43616
44119
  formatFieldLabel = humanizeFieldName;
43617
44120
  ReactMarkdown2 = lazy(() => import('react-markdown'));
43618
44121
  DetailPanel = ({
@@ -43622,6 +44125,7 @@ var init_DetailPanel = __esm({
43622
44125
  avatar,
43623
44126
  sections: propSections,
43624
44127
  actions,
44128
+ backAction,
43625
44129
  footer,
43626
44130
  slideOver = false,
43627
44131
  className,
@@ -43630,7 +44134,8 @@ var init_DetailPanel = __esm({
43630
44134
  fieldNames,
43631
44135
  initialData,
43632
44136
  isLoading = false,
43633
- error
44137
+ error,
44138
+ relationsData
43634
44139
  }) => {
43635
44140
  const eventBus = useEventBus();
43636
44141
  const { t } = useTranslate();
@@ -43641,8 +44146,15 @@ var init_DetailPanel = __esm({
43641
44146
  };
43642
44147
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
43643
44148
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
44149
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
43644
44150
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
43645
44151
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
44152
+ const metaFor = (field) => {
44153
+ const base = fieldMetaMap[field];
44154
+ const options = relationsData?.[field];
44155
+ if (!base && !options) return void 0;
44156
+ return { ...base, options };
44157
+ };
43646
44158
  const handleActionClick = useCallback(
43647
44159
  (action, data2) => {
43648
44160
  if (action.navigatesTo) {
@@ -43721,7 +44233,7 @@ var init_DetailPanel = __esm({
43721
44233
  if (value !== void 0 && value !== null) {
43722
44234
  overviewFields.push({
43723
44235
  label: labelFor(field),
43724
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44236
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43725
44237
  icon: getFieldIcon(field)
43726
44238
  });
43727
44239
  }
@@ -43737,7 +44249,7 @@ var init_DetailPanel = __esm({
43737
44249
  if (value !== void 0 && value !== null) {
43738
44250
  metricsFields.push({
43739
44251
  label: labelFor(field),
43740
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44252
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43741
44253
  icon: getFieldIcon(field)
43742
44254
  });
43743
44255
  }
@@ -43753,7 +44265,7 @@ var init_DetailPanel = __esm({
43753
44265
  if (value !== void 0 && value !== null) {
43754
44266
  timelineFields.push({
43755
44267
  label: labelFor(field),
43756
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44268
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43757
44269
  icon: getFieldIcon(field)
43758
44270
  });
43759
44271
  }
@@ -43769,7 +44281,7 @@ var init_DetailPanel = __esm({
43769
44281
  if (value !== void 0 && value !== null) {
43770
44282
  descFields.push({
43771
44283
  label: labelFor(field),
43772
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44284
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43773
44285
  icon: getFieldIcon(field)
43774
44286
  });
43775
44287
  }
@@ -43817,7 +44329,7 @@ var init_DetailPanel = __esm({
43817
44329
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
43818
44330
  allFields.push({
43819
44331
  label: labelFor(field),
43820
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44332
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43821
44333
  icon: getFieldIcon(field)
43822
44334
  });
43823
44335
  } else {
@@ -43832,34 +44344,49 @@ var init_DetailPanel = __esm({
43832
44344
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
43833
44345
  const effectiveCloseAction = closeAction ?? { event: void 0};
43834
44346
  const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
43835
- /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
43836
- otherActions.map((action, idx) => /* @__PURE__ */ jsx(
43837
- Button,
43838
- {
43839
- variant: action.variant || "secondary",
43840
- size: "sm",
43841
- action: action.navigatesTo ? void 0 : action.event,
43842
- actionPayload: { row: normalizedData },
43843
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43844
- icon: action.icon,
43845
- "data-testid": action.event ? `action-${action.event}` : void 0,
43846
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43847
- children: action.label
43848
- },
43849
- idx
43850
- )),
43851
- /* @__PURE__ */ jsx(
44347
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
44348
+ /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
43852
44349
  Button,
43853
44350
  {
43854
- variant: "ghost",
44351
+ variant: backAction.variant || "ghost",
43855
44352
  size: "sm",
43856
- action: effectiveCloseAction.event,
44353
+ action: backAction.navigatesTo ? void 0 : backAction.event,
43857
44354
  actionPayload: { row: normalizedData },
43858
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
43859
- icon: X,
43860
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
44355
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
44356
+ icon: backAction.icon ?? ArrowLeft,
44357
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
44358
+ children: backAction.label
43861
44359
  }
43862
- )
44360
+ ) }),
44361
+ /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
44362
+ otherActions.map((action, idx) => /* @__PURE__ */ jsx(
44363
+ Button,
44364
+ {
44365
+ variant: action.variant || "secondary",
44366
+ size: "sm",
44367
+ action: action.navigatesTo ? void 0 : action.event,
44368
+ actionPayload: { row: normalizedData },
44369
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
44370
+ icon: action.icon,
44371
+ "data-testid": action.event ? `action-${action.event}` : void 0,
44372
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
44373
+ children: action.label
44374
+ },
44375
+ idx
44376
+ )),
44377
+ /* @__PURE__ */ jsx(
44378
+ Button,
44379
+ {
44380
+ variant: "ghost",
44381
+ size: "sm",
44382
+ action: effectiveCloseAction.event,
44383
+ actionPayload: { row: normalizedData },
44384
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
44385
+ icon: X,
44386
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
44387
+ }
44388
+ )
44389
+ ] })
43863
44390
  ] }),
43864
44391
  avatar,
43865
44392
  /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -44174,6 +44701,10 @@ function determineInputType(field) {
44174
44701
  return "password";
44175
44702
  case "url":
44176
44703
  return "url";
44704
+ case "file":
44705
+ return "file";
44706
+ case "money":
44707
+ return "currency";
44177
44708
  case "number":
44178
44709
  case "integer":
44179
44710
  case "float":
@@ -44235,6 +44766,7 @@ var init_Form = __esm({
44235
44766
  init_Typography();
44236
44767
  init_Icon();
44237
44768
  init_RelationSelect();
44769
+ init_UploadDropZone();
44238
44770
  init_Alert();
44239
44771
  init_useEventBus();
44240
44772
  init_debug();
@@ -44277,6 +44809,7 @@ var init_Form = __esm({
44277
44809
  cancelEvent = "CANCEL",
44278
44810
  relationsData = {},
44279
44811
  relationsLoading = {},
44812
+ fieldOverrides,
44280
44813
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
44281
44814
  conditionalFields: conditionalFieldsRaw = {},
44282
44815
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -44523,7 +45056,8 @@ var init_Form = __esm({
44523
45056
  label,
44524
45057
  field.required && /* @__PURE__ */ jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
44525
45058
  ] }),
44526
- renderFieldInput(field, fieldName, inputType, currentValue, label)
45059
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
45060
+ field.hint && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
44527
45061
  ] }, fieldName);
44528
45062
  },
44529
45063
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -44539,6 +45073,7 @@ var init_Form = __esm({
44539
45073
  name: field,
44540
45074
  type: entityField.type,
44541
45075
  required: entityField.required,
45076
+ hint: entityField.description,
44542
45077
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
44543
45078
  defaultValue: entityField.default,
44544
45079
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -44551,8 +45086,18 @@ var init_Form = __esm({
44551
45086
  return { name: field, type: "string" };
44552
45087
  }
44553
45088
  return field;
45089
+ }).map((field) => {
45090
+ const fieldName = field.name || field.field;
45091
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
45092
+ if (!override) return field;
45093
+ return {
45094
+ ...field,
45095
+ ...override.label !== void 0 ? { label: override.label } : {},
45096
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
45097
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
45098
+ };
44554
45099
  });
44555
- }, [effectiveFields, resolvedEntity]);
45100
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
44556
45101
  const schemaFields = React94__default.useMemo(() => {
44557
45102
  if (normalizedFields.length === 0) return null;
44558
45103
  if (isDebugEnabled()) {
@@ -44676,6 +45221,44 @@ var init_Form = __esm({
44676
45221
  max: field.max
44677
45222
  }
44678
45223
  );
45224
+ case "currency":
45225
+ return /* @__PURE__ */ jsx(
45226
+ Input,
45227
+ {
45228
+ ...commonProps,
45229
+ type: "number",
45230
+ step: "0.01",
45231
+ icon: DollarSign,
45232
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
45233
+ onChange: (e) => handleChange(
45234
+ fieldName,
45235
+ e.target.value ? Number(e.target.value) : void 0
45236
+ ),
45237
+ min: field.min,
45238
+ max: field.max
45239
+ }
45240
+ );
45241
+ case "file":
45242
+ return /* @__PURE__ */ jsx(
45243
+ UploadDropZone,
45244
+ {
45245
+ accept: field.pattern,
45246
+ maxFiles: 1,
45247
+ disabled: isLoading,
45248
+ onFiles: (files) => {
45249
+ const f3 = files[0];
45250
+ if (!f3) return;
45251
+ const reader = new FileReader();
45252
+ reader.onload = () => handleChange(fieldName, {
45253
+ name: f3.name,
45254
+ mimeType: f3.type,
45255
+ sizeBytes: f3.size,
45256
+ url: typeof reader.result === "string" ? reader.result : ""
45257
+ });
45258
+ reader.readAsDataURL(f3);
45259
+ }
45260
+ }
45261
+ );
44679
45262
  case "date":
44680
45263
  return /* @__PURE__ */ jsx(
44681
45264
  Input,
@@ -45708,6 +46291,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
45708
46291
  var init_MediaGallery = __esm({
45709
46292
  "components/core/organisms/MediaGallery.tsx"() {
45710
46293
  "use client";
46294
+ init_getNestedValue();
45711
46295
  init_cn();
45712
46296
  init_atoms();
45713
46297
  init_Stack();
@@ -45776,9 +46360,9 @@ var init_MediaGallery = __esm({
45776
46360
  return entityData.map((record, idx) => {
45777
46361
  return {
45778
46362
  id: String(record.id ?? idx),
45779
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
46363
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
45780
46364
  alt: record.alt ? String(record.alt) : void 0,
45781
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
46365
+ thumbnail: resolveImageUrl(record.thumbnail),
45782
46366
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
45783
46367
  };
45784
46368
  });
@@ -49665,6 +50249,7 @@ var init_component_registry_generated = __esm({
49665
50249
  init_DocSidebar();
49666
50250
  init_DocTOC();
49667
50251
  init_DocumentViewer();
50252
+ init_DrawFxLayer();
49668
50253
  init_DrawGroup();
49669
50254
  init_DrawMesh();
49670
50255
  init_DrawShape();
@@ -49695,6 +50280,7 @@ var init_component_registry_generated = __esm({
49695
50280
  init_FormField();
49696
50281
  init_FormSection();
49697
50282
  init_FormSectionHeader();
50283
+ init_FxOverlay();
49698
50284
  init_GameAudioToggle();
49699
50285
  init_GameHud();
49700
50286
  init_GameIcon();
@@ -49935,6 +50521,7 @@ var init_component_registry_generated = __esm({
49935
50521
  "DocSidebar": DocSidebar,
49936
50522
  "DocTOC": DocTOC,
49937
50523
  "DocumentViewer": DocumentViewer,
50524
+ "DrawFxLayer": DrawFxLayer,
49938
50525
  "DrawGroup": DrawGroup,
49939
50526
  "DrawMesh": DrawMesh,
49940
50527
  "DrawShape": DrawShape,
@@ -49965,6 +50552,7 @@ var init_component_registry_generated = __esm({
49965
50552
  "FormField": FormField,
49966
50553
  "FormLayout": FormLayout,
49967
50554
  "FormSectionHeader": FormSectionHeader,
50555
+ "FxOverlay": FxOverlay,
49968
50556
  "GameAudioToggle": GameAudioToggle,
49969
50557
  "GameHud": GameHud,
49970
50558
  "GameIcon": GameIcon,
@@ -50140,7 +50728,8 @@ __export(UISlotRenderer_exports, {
50140
50728
  SlotContentRenderer: () => SlotContentRenderer,
50141
50729
  SuspenseConfigProvider: () => SuspenseConfigProvider,
50142
50730
  UISlotComponent: () => UISlotComponent,
50143
- UISlotRenderer: () => UISlotRenderer
50731
+ UISlotRenderer: () => UISlotRenderer,
50732
+ renderPatternValue: () => renderPatternValue
50144
50733
  });
50145
50734
  function SuspenseConfigProvider({
50146
50735
  config,
@@ -50176,6 +50765,9 @@ function enrichFormFields(fields, entityDef) {
50176
50765
  type: entityField.type,
50177
50766
  required: entityField.required ?? false
50178
50767
  };
50768
+ if (entityField.description) {
50769
+ enriched.hint = entityField.description;
50770
+ }
50179
50771
  if (entityField.values && entityField.values.length > 0) {
50180
50772
  enriched.values = entityField.values;
50181
50773
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -50199,6 +50791,9 @@ function enrichFormFields(fields, entityDef) {
50199
50791
  if (entityField.required && !("required" in obj)) {
50200
50792
  enriched.required = true;
50201
50793
  }
50794
+ if (entityField.description && !obj.hint && !obj.help) {
50795
+ enriched.hint = entityField.description;
50796
+ }
50202
50797
  if (!obj.values && !obj.options) {
50203
50798
  if (entityField.values && entityField.values.length > 0) {
50204
50799
  enriched.values = entityField.values;
@@ -50214,6 +50809,32 @@ function enrichFormFields(fields, entityDef) {
50214
50809
  return field;
50215
50810
  });
50216
50811
  }
50812
+ function enrichDetailFields(fields, entityDef) {
50813
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
50814
+ const metaFor = (name) => {
50815
+ const entityField = fieldMap.get(name);
50816
+ if (!entityField) return void 0;
50817
+ const meta = { type: entityField.type };
50818
+ const values = entityField.values ?? entityField.enumValues;
50819
+ if (values && values.length > 0) meta.values = values;
50820
+ if (entityField.relation) meta.relation = entityField.relation.entity;
50821
+ return meta;
50822
+ };
50823
+ return fields.map((field) => {
50824
+ if (typeof field === "string") {
50825
+ const meta = metaFor(field);
50826
+ return meta ? { key: field, ...meta } : field;
50827
+ }
50828
+ if (field && typeof field === "object" && !Array.isArray(field) && !React94__default.isValidElement(field) && !(field instanceof Date)) {
50829
+ const obj = field;
50830
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
50831
+ if (!fieldName || obj.type) return field;
50832
+ const meta = metaFor(fieldName);
50833
+ return meta ? { ...obj, ...meta } : field;
50834
+ }
50835
+ return field;
50836
+ });
50837
+ }
50217
50838
  function renderContainedPortal(t, slot, content, onDismiss) {
50218
50839
  const slotContent = /* @__PURE__ */ jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsx(SlotContentRenderer, { content, onDismiss }) });
50219
50840
  const slotId = `slot-${slot}`;
@@ -50723,6 +51344,24 @@ function isPatternConfig(value) {
50723
51344
  const record = value;
50724
51345
  return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
50725
51346
  }
51347
+ function renderPatternValue(value) {
51348
+ if (value === null || value === void 0) return null;
51349
+ if (typeof value === "boolean" || typeof value === "function") return null;
51350
+ if (typeof value === "number") return value;
51351
+ if (typeof value === "string") return renderPatternChildren(value, () => {
51352
+ });
51353
+ if (value instanceof Date) return value.toLocaleString();
51354
+ if (React94__default.isValidElement(value)) return value;
51355
+ if (Array.isArray(value)) {
51356
+ return value.map((item, index) => /* @__PURE__ */ jsx(React94__default.Fragment, { children: renderPatternValue(item) }, index));
51357
+ }
51358
+ if (isPatternConfig(value)) {
51359
+ const { type, ...props } = value;
51360
+ return renderPatternChildren({ type, props }, () => {
51361
+ });
51362
+ }
51363
+ return null;
51364
+ }
50726
51365
  function isPlainConfigObject(value) {
50727
51366
  if (React94__default.isValidElement(value)) return false;
50728
51367
  if (value instanceof Date) return false;
@@ -50924,9 +51563,9 @@ function SlotContentRenderer({
50924
51563
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
50925
51564
  }
50926
51565
  }
50927
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
50928
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
50929
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
51566
+ const fieldsContract = getPatternFieldsContract(content.pattern) ?? (getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
51567
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
51568
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
50930
51569
  }
50931
51570
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
50932
51571
  return /* @__PURE__ */ jsx(
@@ -51036,7 +51675,7 @@ function UISlotRenderer({
51036
51675
  }
51037
51676
  return wrapped;
51038
51677
  }
51039
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51678
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51040
51679
  var init_UISlotRenderer = __esm({
51041
51680
  "components/core/organisms/UISlotRenderer.tsx"() {
51042
51681
  "use client";
@@ -51067,12 +51706,6 @@ var init_UISlotRenderer = __esm({
51067
51706
  modal: "form",
51068
51707
  drawer: "form"
51069
51708
  };
51070
- FORM_PATTERNS = /* @__PURE__ */ new Set([
51071
- "form",
51072
- "form-section",
51073
- "inline-edit-form",
51074
- "wizard-step"
51075
- ]);
51076
51709
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
51077
51710
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
51078
51711
  "logo",