@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
@@ -3,12 +3,12 @@ import React87__default, { createContext, useContext, useMemo, useRef, useEffect
3
3
  import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, TraitScopeProvider, useCurrentPagePath, useGameAudioContextOptional } from '@almadar/ui/providers';
4
4
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
5
5
  import { createContextFromBindings, interpolateValue } from '@almadar/runtime';
6
- import { ANONYMOUS_USER, isRenderBindingMarker, isInlineTrait, ANIMATION_NAMES } from '@almadar/core';
6
+ import { ANONYMOUS_USER, isRenderBindingMarker, isFileValue, isInlineTrait, ANIMATION_NAMES } from '@almadar/core';
7
7
  export { ANONYMOUS_USER } from '@almadar/core';
8
8
  import { clsx } from 'clsx';
9
9
  import { twMerge } from 'tailwind-merge';
10
10
  import * as LucideIcons2 from 'lucide-react';
11
- import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, 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';
11
+ import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, 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';
12
12
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
13
  import { createPortal } from 'react-dom';
14
14
  import { useTranslate } from '@almadar/ui/hooks';
@@ -77,7 +77,7 @@ import { CSS } from '@dnd-kit/utilities';
77
77
  import { ordered, lib } from 'emojilib';
78
78
  import { useNodeId, ReactFlowProvider, Handle, Position } from '@xyflow/react';
79
79
  import { forceSimulation, forceLink, forceManyBody, forceCollide, forceX, forceY } from 'd3-force';
80
- import { isDrawHostPattern, getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/core/patterns';
80
+ import { isDrawHostPattern, getPatternDefinition, getPatternFieldsContract, getComponentForPattern as getComponentForPattern$1 } from '@almadar/core/patterns';
81
81
 
82
82
  var __defProp = Object.defineProperty;
83
83
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -5729,27 +5729,61 @@ var init_InfiniteScrollSentinel = __esm({
5729
5729
  InfiniteScrollSentinel.displayName = "InfiniteScrollSentinel";
5730
5730
  }
5731
5731
  });
5732
- function createParticles(count) {
5733
- return Array.from({ length: count }, () => {
5734
- particleIdCounter += 1;
5735
- return {
5736
- id: particleIdCounter,
5737
- color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)],
5738
- left: 30 + Math.random() * 40,
5739
- delay: Math.random() * 300,
5740
- angle: Math.random() * 360,
5741
- distance: 40 + Math.random() * 80,
5742
- rotation: Math.random() * 720 - 360,
5743
- size: 4 + Math.random() * 6
5744
- };
5745
- });
5732
+
5733
+ // components/core/atoms/fx.ts
5734
+ function resolveFxView(item, presets) {
5735
+ const row = presets?.find((p) => p.type === item.type);
5736
+ if (!row) return item;
5737
+ return {
5738
+ ...item,
5739
+ space: item.space ?? row.space,
5740
+ effect: item.effect ?? row.effect,
5741
+ color: item.color ?? row.color,
5742
+ size: item.size ?? row.size,
5743
+ vx: item.vx ?? row.vx,
5744
+ vy: item.vy ?? row.vy,
5745
+ vz: item.vz ?? row.vz,
5746
+ particleCount: item.particleCount ?? row.particleCount,
5747
+ shape: row.shape,
5748
+ count: row.count,
5749
+ glow: row.glow,
5750
+ gravity: row.gravity,
5751
+ color2: row.color2
5752
+ };
5746
5753
  }
5747
- var CONFETTI_COLORS, particleIdCounter, ConfettiEffect;
5748
- var init_ConfettiEffect = __esm({
5749
- "components/core/atoms/ConfettiEffect.tsx"() {
5750
- "use client";
5751
- init_cn();
5752
- init_Box();
5754
+ function fxLifecycle(item, epochNowMs, tickMs) {
5755
+ const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
5756
+ const lifeMs = maxTtl * tickMs;
5757
+ const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
5758
+ const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
5759
+ return { lifeMs, ageMs, progress, fade: 1 - progress };
5760
+ }
5761
+ function fxHash01(seed, salt) {
5762
+ let h = 2166136261 ^ salt;
5763
+ for (let i = 0; i < seed.length; i++) {
5764
+ h ^= seed.charCodeAt(i);
5765
+ h = Math.imul(h, 16777619);
5766
+ }
5767
+ h ^= h >>> 13;
5768
+ h = Math.imul(h, 1274126177);
5769
+ h ^= h >>> 16;
5770
+ return (h >>> 0) / 4294967296;
5771
+ }
5772
+ function createConfettiParticles(count, seed) {
5773
+ return Array.from({ length: count }, (_, i) => ({
5774
+ id: i,
5775
+ color: CONFETTI_COLORS[Math.floor(fxHash01(seed, i * 7 + 1) * CONFETTI_COLORS.length)],
5776
+ left: 30 + fxHash01(seed, i * 7 + 2) * 40,
5777
+ delay: fxHash01(seed, i * 7 + 3) * 300,
5778
+ angle: fxHash01(seed, i * 7 + 4) * 360,
5779
+ distance: 40 + fxHash01(seed, i * 7 + 5) * 80,
5780
+ rotation: fxHash01(seed, i * 7 + 6) * 720 - 360,
5781
+ size: 4 + fxHash01(seed, i * 7 + 7) * 6
5782
+ }));
5783
+ }
5784
+ var CONFETTI_COLORS, CONFETTI_BURST_KEYFRAMES;
5785
+ var init_fx = __esm({
5786
+ "components/core/atoms/fx.ts"() {
5753
5787
  CONFETTI_COLORS = [
5754
5788
  "var(--color-primary)",
5755
5789
  "var(--color-success)",
@@ -5758,7 +5792,30 @@ var init_ConfettiEffect = __esm({
5758
5792
  "gold",
5759
5793
  "dodgerblue"
5760
5794
  ];
5761
- particleIdCounter = 0;
5795
+ CONFETTI_BURST_KEYFRAMES = `
5796
+ @keyframes confetti-burst {
5797
+ 0% {
5798
+ opacity: 1;
5799
+ transform: translate(0, 0) rotate(0deg) scale(1);
5800
+ }
5801
+ 70% {
5802
+ opacity: 1;
5803
+ }
5804
+ 100% {
5805
+ opacity: 0;
5806
+ transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
5807
+ }
5808
+ }
5809
+ `;
5810
+ }
5811
+ });
5812
+ var ConfettiEffect;
5813
+ var init_ConfettiEffect = __esm({
5814
+ "components/core/atoms/ConfettiEffect.tsx"() {
5815
+ "use client";
5816
+ init_cn();
5817
+ init_Box();
5818
+ init_fx();
5762
5819
  ConfettiEffect = ({
5763
5820
  trigger,
5764
5821
  duration = 2e3,
@@ -5767,11 +5824,13 @@ var init_ConfettiEffect = __esm({
5767
5824
  }) => {
5768
5825
  const [particles, setParticles] = useState([]);
5769
5826
  const previousTriggerRef = useRef(false);
5827
+ const burstRef = useRef(0);
5770
5828
  useEffect(() => {
5771
5829
  const wasFalse = !previousTriggerRef.current;
5772
5830
  previousTriggerRef.current = trigger;
5773
5831
  if (trigger && wasFalse) {
5774
- const newParticles = createParticles(particleCount);
5832
+ burstRef.current += 1;
5833
+ const newParticles = createConfettiParticles(particleCount, `confetti-${burstRef.current}`);
5775
5834
  setParticles(newParticles);
5776
5835
  const timer = window.setTimeout(() => {
5777
5836
  setParticles([]);
@@ -5819,21 +5878,7 @@ var init_ConfettiEffect = __esm({
5819
5878
  p.id
5820
5879
  );
5821
5880
  }),
5822
- /* @__PURE__ */ jsx("style", { children: `
5823
- @keyframes confetti-burst {
5824
- 0% {
5825
- opacity: 1;
5826
- transform: translate(0, 0) rotate(0deg) scale(1);
5827
- }
5828
- 70% {
5829
- opacity: 1;
5830
- }
5831
- 100% {
5832
- opacity: 0;
5833
- transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
5834
- }
5835
- }
5836
- ` })
5881
+ /* @__PURE__ */ jsx("style", { children: CONFETTI_BURST_KEYFRAMES })
5837
5882
  ]
5838
5883
  }
5839
5884
  );
@@ -9390,7 +9435,7 @@ var init_DrawShape = __esm({
9390
9435
  const cx = p.x + (node.offsetX ?? 0) * tw;
9391
9436
  const cy = p.y + (node.offsetY ?? 0) * tw;
9392
9437
  const rx = (node.radiusX ?? 0) * tw;
9393
- const ry = (node.radiusY ?? rx) * tw;
9438
+ const ry = (node.radiusY ?? node.radiusX ?? 0) * tw;
9394
9439
  if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
9395
9440
  if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
9396
9441
  if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
@@ -9580,6 +9625,172 @@ var init_DrawTextLayer = __esm({
9580
9625
  };
9581
9626
  }
9582
9627
  });
9628
+
9629
+ // components/game/molecules/DrawFxLayer.tsx
9630
+ function fxPosition(view, dim, ageSec) {
9631
+ const g = view.gravity ?? 0;
9632
+ const fall = 0.5 * g * ageSec * ageSec;
9633
+ {
9634
+ const base2 = view.position ?? { x: view.x, y: view.z ?? view.y ?? 0 };
9635
+ if (!Number.isFinite(base2.x) || !Number.isFinite(base2.y)) return void 0;
9636
+ return {
9637
+ x: base2.x + (view.vx ?? 0) * ageSec,
9638
+ y: base2.y + (view.vz ?? 0) * ageSec + fall
9639
+ };
9640
+ }
9641
+ }
9642
+ function sparkShape(view, pos, i, fade, progress) {
9643
+ const size = view.size ?? 0.5;
9644
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
9645
+ const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut2(progress);
9646
+ const r = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
9647
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
9648
+ return {
9649
+ type: "draw-shape",
9650
+ shape: "ellipse",
9651
+ position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
9652
+ anchor: "center",
9653
+ radiusX: r,
9654
+ fill: color,
9655
+ blendMode: "lighter",
9656
+ opacity: fade,
9657
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
9658
+ };
9659
+ }
9660
+ function proceduralShapes(view, pos, fade, progress) {
9661
+ const size = view.size ?? 0.5;
9662
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
9663
+ switch (view.shape ?? "spark") {
9664
+ case "ring": {
9665
+ return [
9666
+ {
9667
+ type: "draw-shape",
9668
+ shape: "ellipse",
9669
+ position: pos,
9670
+ anchor: "center",
9671
+ radiusX: size * easeOut2(progress),
9672
+ stroke: view.color2 ?? color,
9673
+ strokeWidth: 2,
9674
+ blendMode: "lighter",
9675
+ opacity: fade,
9676
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
9677
+ }
9678
+ ];
9679
+ }
9680
+ case "puff": {
9681
+ const count = view.count ?? 3;
9682
+ return Array.from({ length: count }, (_, i) => {
9683
+ const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
9684
+ const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut2(progress);
9685
+ return {
9686
+ type: "draw-shape",
9687
+ shape: "ellipse",
9688
+ position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
9689
+ anchor: "center",
9690
+ radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
9691
+ fill: i === 0 && view.color2 ? view.color2 : color,
9692
+ opacity: fade * 0.6,
9693
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
9694
+ };
9695
+ });
9696
+ }
9697
+ case "streak": {
9698
+ const count = view.count ?? 5;
9699
+ return Array.from({ length: count }, (_, i) => {
9700
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
9701
+ const inner = size * (0.2 + 0.8 * easeOut2(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
9702
+ const len = size * 0.35;
9703
+ return {
9704
+ type: "draw-shape",
9705
+ shape: "ellipse",
9706
+ position: pos,
9707
+ anchor: "center",
9708
+ offsetX: inner + len / 2,
9709
+ offsetY: 0,
9710
+ radiusX: len / 2,
9711
+ radiusY: size * 0.04,
9712
+ rotate: angle,
9713
+ fill: color,
9714
+ blendMode: "lighter",
9715
+ opacity: fade,
9716
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
9717
+ };
9718
+ });
9719
+ }
9720
+ default: {
9721
+ const count = view.count ?? 6;
9722
+ return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
9723
+ }
9724
+ }
9725
+ }
9726
+ function expandFxItem(view, node, epochNowMs, dim) {
9727
+ if (view.space === "screen") return [];
9728
+ const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
9729
+ const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
9730
+ if (progress >= 1) return [];
9731
+ const pos = fxPosition(view, dim, ageMs / 1e3);
9732
+ if (!pos) return [];
9733
+ const out = [];
9734
+ const artItems = node.art?.[view.type]?.["idle"];
9735
+ const sprite = node.sprites?.[view.type];
9736
+ if (Array.isArray(artItems)) {
9737
+ out.push({
9738
+ type: "draw-group",
9739
+ position: pos,
9740
+ opacity: fade,
9741
+ ...view.size !== void 0 ? { scale: view.size } : {},
9742
+ items: artItems
9743
+ });
9744
+ } else if (sprite?.url) {
9745
+ const spriteSize = view.size ?? 0.6;
9746
+ out.push({
9747
+ type: "draw-sprite",
9748
+ position: pos,
9749
+ asset: sprite,
9750
+ anchor: "center",
9751
+ width: spriteSize,
9752
+ height: spriteSize,
9753
+ opacity: fade
9754
+ });
9755
+ } else {
9756
+ out.push(...proceduralShapes(view, pos, fade, progress));
9757
+ }
9758
+ if (view.message) {
9759
+ const text = {
9760
+ type: "draw-text",
9761
+ text: view.message,
9762
+ position: pos,
9763
+ offsetY: -(0.15 + 0.55 * progress),
9764
+ color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
9765
+ opacity: fade
9766
+ };
9767
+ out.push(text);
9768
+ }
9769
+ return out;
9770
+ }
9771
+ function expandFxLayer(node, epochNowMs, dim) {
9772
+ if (!Array.isArray(node.items)) return [];
9773
+ const out = [];
9774
+ for (const item of node.items) {
9775
+ if (!item || typeof item.id !== "string") continue;
9776
+ out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim));
9777
+ }
9778
+ return out;
9779
+ }
9780
+ function DrawFxLayer(_props) {
9781
+ return null;
9782
+ }
9783
+ var DEFAULT_TICK_MS, DEFAULT_TEXT_COLOR, DEFAULT_SPARK_COLOR, easeOut2;
9784
+ var init_DrawFxLayer = __esm({
9785
+ "components/game/molecules/DrawFxLayer.tsx"() {
9786
+ "use client";
9787
+ init_fx();
9788
+ DEFAULT_TICK_MS = 500;
9789
+ DEFAULT_TEXT_COLOR = "#ffe066";
9790
+ DEFAULT_SPARK_COLOR = "#ffffff";
9791
+ easeOut2 = (t) => 1 - (1 - t) * (1 - t);
9792
+ }
9793
+ });
9583
9794
  function paintDrawable(painter, node, dctx) {
9584
9795
  switch (node.type) {
9585
9796
  case "draw-sprite":
@@ -9627,6 +9838,11 @@ function paintDrawable(painter, node, dctx) {
9627
9838
  case "draw-text-layer":
9628
9839
  paintTextLayer(painter, node, dctx);
9629
9840
  break;
9841
+ case "draw-fx-layer": {
9842
+ const epochNow = dctx.time > 0 && typeof performance !== "undefined" ? performance.timeOrigin + dctx.time : 0;
9843
+ for (const child of expandFxLayer(node, epochNow, "2d")) paintDrawable(painter, child, dctx);
9844
+ break;
9845
+ }
9630
9846
  }
9631
9847
  }
9632
9848
  var paint2dLog, warnedUnsupported2d, warnUnsupported2d;
@@ -9641,6 +9857,7 @@ var init_paintDispatch = __esm({
9641
9857
  init_DrawSpriteLayer();
9642
9858
  init_DrawShapeLayer();
9643
9859
  init_DrawTextLayer();
9860
+ init_DrawFxLayer();
9644
9861
  paint2dLog = createLogger("almadar:ui:drawable-2d");
9645
9862
  warnedUnsupported2d = /* @__PURE__ */ new Set();
9646
9863
  warnUnsupported2d = (kind) => {
@@ -9889,6 +10106,7 @@ function Canvas2D({
9889
10106
  return isAnimatedGroup(node) || Array.isArray(node.items) && node.items.some(drawableIsAnimated);
9890
10107
  if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
9891
10108
  if (node.type === "draw-sprite-layer") return Array.isArray(node.items) && node.items.some(isAnimatedSprite);
10109
+ if (node.type === "draw-fx-layer") return Array.isArray(node.items) && node.items.length > 0;
9892
10110
  return false;
9893
10111
  };
9894
10112
  const animRafRef = useRef(0);
@@ -14169,7 +14387,7 @@ var init_AlgoGraphCanvas = __esm({
14169
14387
  }
14170
14388
  const badgeGeoms = [];
14171
14389
  for (const g of nodeGeoms) {
14172
- if (!g.badge) continue;
14390
+ if (!g.badge || g.badge.text === "") continue;
14173
14391
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
14174
14392
  badgeGeoms.push({
14175
14393
  cx: g.x + g.radius * 0.75,
@@ -14235,7 +14453,7 @@ var init_AlgoGraphCanvas = __esm({
14235
14453
  };
14236
14454
  }
14237
14455
  });
14238
- 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;
14456
+ 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;
14239
14457
  var init_AlgorithmCanvas = __esm({
14240
14458
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
14241
14459
  "use client";
@@ -14262,9 +14480,18 @@ var init_AlgorithmCanvas = __esm({
14262
14480
  FRAME_ACTIVE_COLOR = "#3b82f6";
14263
14481
  FRAME_RETURNING_COLOR = "#f59e0b";
14264
14482
  FRAME_DONE_COLOR = "#94a3b8";
14483
+ FRAME_RIM_COLOR = {
14484
+ active: "#1d4ed8",
14485
+ returning: "#b45309",
14486
+ done: "#64748b"
14487
+ };
14265
14488
  FRAME_LABEL_COLOR = "#ffffff";
14266
14489
  FRAME_DETAIL_COLOR = "#e2e8f0";
14267
14490
  FRAME_TWO_LINE_MIN_H = 22;
14491
+ FRAME_STRIP_H = 44;
14492
+ FRAME_GAP = 6;
14493
+ FRAME_BOTTOM_PAD = 8;
14494
+ FRAME_MIN_H = 12;
14268
14495
  BUCKET_INDEX_FILL = "#e2e8f0";
14269
14496
  BUCKET_INDEX_STROKE = "#9ca3af";
14270
14497
  BUCKET_INDEX_TEXT = "#374151";
@@ -14761,14 +14988,18 @@ var init_AlgorithmCanvas = __esm({
14761
14988
  if (frames.length > 0) {
14762
14989
  const panelYFrames = panelY.frames;
14763
14990
  const n = frames.length;
14764
- const frameH = panelHeight / n;
14991
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
14992
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
14993
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
14765
14994
  const x = 8;
14766
14995
  const w = width - 16;
14996
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
14767
14997
  frames.forEach((f3, i) => {
14768
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
14998
+ const y = bottom - i * stride - frameH;
14769
14999
  const state = f3.state ?? "active";
14770
15000
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
14771
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
15001
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
15002
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
14772
15003
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
14773
15004
  out.push({
14774
15005
  type: "text",
@@ -14776,14 +15007,14 @@ var init_AlgorithmCanvas = __esm({
14776
15007
  y: y + frameH * 0.35,
14777
15008
  text: f3.label,
14778
15009
  color: FRAME_LABEL_COLOR,
14779
- fontSize: 10,
15010
+ fontSize: 11,
14780
15011
  align: "left"
14781
15012
  });
14782
15013
  if (f3.detail) {
14783
15014
  out.push({
14784
15015
  type: "text",
14785
15016
  x: 16,
14786
- y: y + frameH * 0.7,
15017
+ y: y + frameH * 0.72,
14787
15018
  text: f3.detail,
14788
15019
  color: FRAME_DETAIL_COLOR,
14789
15020
  fontSize: 10,
@@ -21296,8 +21527,6 @@ var init_ButtonGroup = __esm({
21296
21527
  ButtonGroup.displayName = "ButtonGroup";
21297
21528
  }
21298
21529
  });
21299
-
21300
- // lib/getNestedValue.ts
21301
21530
  function getNestedValue(obj, path) {
21302
21531
  if (obj === null || obj === void 0 || !path) {
21303
21532
  return void 0;
@@ -21318,6 +21547,15 @@ function getNestedValue(obj, path) {
21318
21547
  }
21319
21548
  return value;
21320
21549
  }
21550
+ function resolveImageUrl(value) {
21551
+ if (typeof value === "string") {
21552
+ return value;
21553
+ }
21554
+ if (isFileValue(value)) {
21555
+ return value.url;
21556
+ }
21557
+ return void 0;
21558
+ }
21321
21559
  var init_getNestedValue = __esm({
21322
21560
  "lib/getNestedValue.ts"() {
21323
21561
  }
@@ -22022,8 +22260,8 @@ var init_CardGrid = __esm({
22022
22260
  actionPayload: { row: itemData },
22023
22261
  children: [
22024
22262
  imageField && (() => {
22025
- const imgUrl = getNestedValue(itemData, imageField);
22026
- if (!imgUrl || typeof imgUrl !== "string") return null;
22263
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
22264
+ if (!imgUrl) return null;
22027
22265
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
22028
22266
  "img",
22029
22267
  {
@@ -26053,8 +26291,8 @@ function DataGrid({
26053
26291
  ),
26054
26292
  children: [
26055
26293
  imageField && (() => {
26056
- const imgUrl = getNestedValue(itemData, imageField);
26057
- if (!imgUrl || typeof imgUrl !== "string") return null;
26294
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
26295
+ if (!imgUrl) return null;
26058
26296
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
26059
26297
  "img",
26060
26298
  {
@@ -28108,6 +28346,196 @@ var init_PageTransition = __esm({
28108
28346
  PageTransition.displayName = "PageTransition";
28109
28347
  }
28110
28348
  });
28349
+ function ConfettiBurst({ item, lifeMs }) {
28350
+ const particles = createConfettiParticles(item.particleCount ?? 30, item.id);
28351
+ const left = (item.x ?? 0.5) * 100;
28352
+ const top = (item.z ?? item.y ?? 0.4) * 100;
28353
+ return /* @__PURE__ */ jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: particles.map((p) => {
28354
+ const rad = p.angle * Math.PI / 180;
28355
+ const tx = Math.cos(rad) * p.distance * (item.size ?? 1);
28356
+ const ty = Math.sin(rad) * p.distance * (item.size ?? 1) - 20;
28357
+ return /* @__PURE__ */ jsx(
28358
+ Box,
28359
+ {
28360
+ className: "absolute rounded-sm",
28361
+ style: {
28362
+ left: (p.left - 50) * 2,
28363
+ top: -10,
28364
+ width: p.size,
28365
+ height: p.size,
28366
+ backgroundColor: item.color ?? p.color,
28367
+ animation: `confetti-burst ${Math.max(lifeMs - p.delay, 200)}ms ease-out ${p.delay}ms forwards`,
28368
+ opacity: 0,
28369
+ "--confetti-tx": `${tx}px`,
28370
+ "--confetti-ty": `${ty}px`,
28371
+ "--confetti-rotate": `${p.rotation}deg`
28372
+ }
28373
+ },
28374
+ p.id
28375
+ );
28376
+ }) });
28377
+ }
28378
+ function SparkleBurst({ item, lifeMs }) {
28379
+ const count = item.particleCount ?? 8;
28380
+ const scale = item.size ?? 1;
28381
+ const left = (item.x ?? 0.5) * 100;
28382
+ const top = (item.z ?? item.y ?? 0.4) * 100;
28383
+ return /* @__PURE__ */ jsx(Box, { position: "absolute", style: { left: `${left}%`, top: `${top}%` }, children: Array.from({ length: count }, (_, i) => {
28384
+ const dx = (fxHash01(item.id, i * 4) - 0.5) * 160 * scale;
28385
+ const dy = (fxHash01(item.id, i * 4 + 1) - 0.5) * 120 * scale;
28386
+ const dot = (4 + fxHash01(item.id, i * 4 + 2) * 5) * scale;
28387
+ const delay = fxHash01(item.id, i * 4 + 3) * lifeMs * 0.4;
28388
+ return /* @__PURE__ */ jsx(
28389
+ Box,
28390
+ {
28391
+ className: "absolute rounded-full",
28392
+ style: {
28393
+ left: dx,
28394
+ top: dy,
28395
+ width: dot,
28396
+ height: dot,
28397
+ backgroundColor: item.color ?? "gold",
28398
+ opacity: 0,
28399
+ animation: `fx-overlay-sparkle ${Math.max(lifeMs - delay, 200)}ms ease-in-out ${delay}ms forwards`
28400
+ }
28401
+ },
28402
+ i
28403
+ );
28404
+ }) });
28405
+ }
28406
+ function OverlayFxNode({ item, tickMs }) {
28407
+ const lifeMs = lifeMsOf(item, tickMs);
28408
+ switch (item.effect ?? "sparkle") {
28409
+ case "confetti":
28410
+ return /* @__PURE__ */ jsx(ConfettiBurst, { item, lifeMs });
28411
+ case "flash":
28412
+ return /* @__PURE__ */ jsx(
28413
+ Box,
28414
+ {
28415
+ position: "absolute",
28416
+ className: "inset-0",
28417
+ style: {
28418
+ backgroundColor: item.color ?? "#ffffff",
28419
+ opacity: 0,
28420
+ animation: `fx-overlay-flash ${lifeMs}ms ease-out forwards`
28421
+ }
28422
+ }
28423
+ );
28424
+ case "streak-glow":
28425
+ return /* @__PURE__ */ jsx(
28426
+ Box,
28427
+ {
28428
+ position: "absolute",
28429
+ className: "inset-0",
28430
+ style: {
28431
+ boxShadow: `inset 0 0 ${60 * (item.size ?? 1)}px ${item.color ?? "var(--color-warning)"}`,
28432
+ opacity: 0,
28433
+ animation: `fx-overlay-glow ${lifeMs}ms ease-in-out forwards`
28434
+ }
28435
+ }
28436
+ );
28437
+ case "float-text": {
28438
+ if (!item.message) return null;
28439
+ return /* @__PURE__ */ jsx(
28440
+ Box,
28441
+ {
28442
+ position: "absolute",
28443
+ style: {
28444
+ left: `${(item.x ?? 0.5) * 100}%`,
28445
+ top: `${(item.z ?? item.y ?? 0.4) * 100}%`,
28446
+ color: item.color ?? "var(--color-success)",
28447
+ fontWeight: 700,
28448
+ fontSize: 16 * (item.size ?? 1),
28449
+ textShadow: "0 1px 2px rgba(0,0,0,0.4)",
28450
+ opacity: 0,
28451
+ animation: `fx-overlay-float ${lifeMs}ms ease-out forwards`,
28452
+ whiteSpace: "nowrap"
28453
+ },
28454
+ children: item.message
28455
+ }
28456
+ );
28457
+ }
28458
+ case "shake":
28459
+ return null;
28460
+ default:
28461
+ return /* @__PURE__ */ jsx(SparkleBurst, { item, lifeMs });
28462
+ }
28463
+ }
28464
+ var DEFAULT_TICK_MS2, FX_OVERLAY_KEYFRAMES, lifeMsOf, FxOverlay;
28465
+ var init_FxOverlay = __esm({
28466
+ "components/core/molecules/FxOverlay.tsx"() {
28467
+ "use client";
28468
+ init_cn();
28469
+ init_Box();
28470
+ init_fx();
28471
+ DEFAULT_TICK_MS2 = 500;
28472
+ FX_OVERLAY_KEYFRAMES = `
28473
+ ${CONFETTI_BURST_KEYFRAMES}
28474
+ @keyframes fx-overlay-flash {
28475
+ 0% { opacity: 0.75; }
28476
+ 100% { opacity: 0; }
28477
+ }
28478
+ @keyframes fx-overlay-glow {
28479
+ 0% { opacity: 0.9; }
28480
+ 60% { opacity: 0.6; }
28481
+ 100% { opacity: 0; }
28482
+ }
28483
+ @keyframes fx-overlay-sparkle {
28484
+ 0% { opacity: 0; transform: scale(0); }
28485
+ 30% { opacity: 1; transform: scale(1); }
28486
+ 100% { opacity: 0; transform: scale(0.3); }
28487
+ }
28488
+ @keyframes fx-overlay-float {
28489
+ 0% { opacity: 0; transform: translate(-50%, 8px); }
28490
+ 15% { opacity: 1; }
28491
+ 100% { opacity: 0; transform: translate(-50%, -40px); }
28492
+ }
28493
+ @keyframes fx-overlay-shake {
28494
+ 0% { transform: translateX(0); }
28495
+ 15% { transform: translateX(-6px); }
28496
+ 30% { transform: translateX(5px); }
28497
+ 45% { transform: translateX(-4px); }
28498
+ 60% { transform: translateX(3px); }
28499
+ 75% { transform: translateX(-2px); }
28500
+ 100% { transform: translateX(0); }
28501
+ }
28502
+ `;
28503
+ lifeMsOf = (it, tickMs) => Math.max(it.maxTtl ?? it.ttl, it.ttl, 1) * tickMs;
28504
+ FxOverlay = ({ items, tickMs = DEFAULT_TICK_MS2, children, className }) => {
28505
+ const screenItems = (Array.isArray(items) ? items : []).filter(
28506
+ (it) => Boolean(it) && typeof it.id === "string" && it.space !== "world"
28507
+ );
28508
+ const shakeItem = [...screenItems].reverse().find((it) => it.effect === "shake");
28509
+ const overlay = /* @__PURE__ */ jsxs(
28510
+ Box,
28511
+ {
28512
+ position: "absolute",
28513
+ className: cn("inset-0 pointer-events-none overflow-hidden z-50", !children && className),
28514
+ "aria-hidden": "true",
28515
+ children: [
28516
+ screenItems.map((it) => /* @__PURE__ */ jsx(OverlayFxNode, { item: it, tickMs }, it.id)),
28517
+ /* @__PURE__ */ jsx("style", { children: FX_OVERLAY_KEYFRAMES })
28518
+ ]
28519
+ }
28520
+ );
28521
+ if (children !== void 0 && children !== null) {
28522
+ return /* @__PURE__ */ jsxs(Box, { position: "relative", className, children: [
28523
+ /* @__PURE__ */ jsx(
28524
+ Box,
28525
+ {
28526
+ style: shakeItem ? { animation: `fx-overlay-shake ${Math.min(lifeMsOf(shakeItem, tickMs), 600)}ms ease-in-out` } : void 0,
28527
+ children
28528
+ },
28529
+ shakeItem?.id ?? "steady"
28530
+ ),
28531
+ overlay
28532
+ ] });
28533
+ }
28534
+ return overlay;
28535
+ };
28536
+ FxOverlay.displayName = "FxOverlay";
28537
+ }
28538
+ });
28111
28539
  function computePopoverStyle(position, triggerRect, popoverWidth) {
28112
28540
  if (position === "left" || position === "right") {
28113
28541
  return {
@@ -28664,7 +29092,7 @@ var init_SearchInput = __esm({
28664
29092
  /* @__PURE__ */ jsx(
28665
29093
  Input,
28666
29094
  {
28667
- type: "text",
29095
+ type: "search",
28668
29096
  value: searchValue,
28669
29097
  onChange: handleChange,
28670
29098
  placeholder: resolvedPlaceholder,
@@ -29990,6 +30418,7 @@ var MathCanvas;
29990
30418
  var init_MathCanvas = __esm({
29991
30419
  "components/learning/molecules/MathCanvas.tsx"() {
29992
30420
  "use client";
30421
+ init_useEventBus();
29993
30422
  init_atoms();
29994
30423
  init_Stack();
29995
30424
  init_LearningCanvas();
@@ -30021,9 +30450,32 @@ var init_MathCanvas = __esm({
30021
30450
  interactive = false,
30022
30451
  animate = false,
30023
30452
  onShapeClick,
30453
+ keyMap,
30454
+ keyUpMap,
30024
30455
  isLoading,
30025
30456
  error
30026
30457
  }) => {
30458
+ const eventBus = useEventBus();
30459
+ useEffect(() => {
30460
+ if (!keyMap && !keyUpMap) return;
30461
+ const onDown = (e) => {
30462
+ const ev = keyMap?.[e.code];
30463
+ if (ev) {
30464
+ eventBus.emit(`UI:${ev}`, {});
30465
+ e.preventDefault();
30466
+ }
30467
+ };
30468
+ const onUp = (e) => {
30469
+ const ev = keyUpMap?.[e.code];
30470
+ if (ev) eventBus.emit(`UI:${ev}`, {});
30471
+ };
30472
+ window.addEventListener("keydown", onDown);
30473
+ window.addEventListener("keyup", onUp);
30474
+ return () => {
30475
+ window.removeEventListener("keydown", onDown);
30476
+ window.removeEventListener("keyup", onUp);
30477
+ };
30478
+ }, [keyMap, keyUpMap, eventBus]);
30027
30479
  const derivedShapes = useMemo(() => {
30028
30480
  const out = [];
30029
30481
  const margin = 24;
@@ -30036,11 +30488,11 @@ var init_MathCanvas = __esm({
30036
30488
  if (showGrid) {
30037
30489
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
30038
30490
  const px = mapX(x);
30039
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
30491
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30040
30492
  }
30041
30493
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
30042
30494
  const py = mapY(y);
30043
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
30495
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30044
30496
  }
30045
30497
  }
30046
30498
  if (showTickLabels) {
@@ -30089,7 +30541,7 @@ var init_MathCanvas = __esm({
30089
30541
  x: mapX((first.x + last.x) / 2),
30090
30542
  y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
30091
30543
  text: region.label,
30092
- color: "#111827",
30544
+ color,
30093
30545
  fontSize: 11
30094
30546
  });
30095
30547
  }
@@ -30122,14 +30574,14 @@ var init_MathCanvas = __esm({
30122
30574
  const px = mapX(guide.at);
30123
30575
  out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
30124
30576
  if (guide.label) {
30125
- out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color: "#111827", fontSize: 11 });
30577
+ out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color, fontSize: 11 });
30126
30578
  }
30127
30579
  } else {
30128
30580
  if (guide.at < yMin || guide.at > yMax) continue;
30129
30581
  const py = mapY(guide.at);
30130
30582
  out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
30131
30583
  if (guide.label) {
30132
- out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color: "#111827", fontSize: 11, align: "right" });
30584
+ out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color, fontSize: 11, align: "right" });
30133
30585
  }
30134
30586
  }
30135
30587
  }
@@ -30195,7 +30647,7 @@ var init_MathCanvas = __esm({
30195
30647
  x: (x1 + x2) / 2,
30196
30648
  y: xAxisY - peak - 8,
30197
30649
  text: hop.label,
30198
- color: "#111827",
30650
+ color,
30199
30651
  fontSize: 10,
30200
30652
  align: "center"
30201
30653
  });
@@ -30222,7 +30674,7 @@ var init_MathCanvas = __esm({
30222
30674
  x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
30223
30675
  y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
30224
30676
  text: angle.label,
30225
- color: "#111827",
30677
+ color,
30226
30678
  fontSize: 11,
30227
30679
  align: "center"
30228
30680
  });
@@ -30240,7 +30692,7 @@ var init_MathCanvas = __esm({
30240
30692
  fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
30241
30693
  });
30242
30694
  if (p.label) {
30243
- out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
30695
+ out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: p.color ?? "#111827", fontSize: 12 });
30244
30696
  }
30245
30697
  }
30246
30698
  for (const v of vectors) {
@@ -30251,7 +30703,7 @@ var init_MathCanvas = __esm({
30251
30703
  const y2 = mapY(v.y + v.vy);
30252
30704
  out.push({ type: "arrow", x1, y1, x2, y2, color: v.color ?? "#7c3aed", lineWidth: 2 });
30253
30705
  if (v.label) {
30254
- out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: "#111827", fontSize: 12 });
30706
+ out.push({ type: "text", x: x2 + 6, y: y2 - 6, text: v.label, color: v.color ?? "#7c3aed", fontSize: 12 });
30255
30707
  }
30256
30708
  }
30257
30709
  out.push(...shapes);
@@ -31515,13 +31967,13 @@ var init_MapView = __esm({
31515
31967
  shadowSize: [41, 41]
31516
31968
  });
31517
31969
  L.Marker.prototype.options.icon = defaultIcon;
31518
- const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__default;
31970
+ const { useEffect: useEffect64, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__default;
31519
31971
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31520
31972
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31521
31973
  function MapUpdater({ centerLat, centerLng, zoom }) {
31522
31974
  const map = useMap();
31523
31975
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31524
- useEffect63(() => {
31976
+ useEffect64(() => {
31525
31977
  const prev = prevRef.current;
31526
31978
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31527
31979
  map.setView([centerLat, centerLng], zoom);
@@ -31532,7 +31984,7 @@ var init_MapView = __esm({
31532
31984
  }
31533
31985
  function MapClickHandler({ onMapClick }) {
31534
31986
  const map = useMap();
31535
- useEffect63(() => {
31987
+ useEffect64(() => {
31536
31988
  if (!onMapClick) return;
31537
31989
  const handler = (e) => {
31538
31990
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -38348,6 +38800,7 @@ var init_PageHeader = __esm({
38348
38800
  className
38349
38801
  }) => {
38350
38802
  const eventBus = useEventBus();
38803
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
38351
38804
  const handleBack = () => {
38352
38805
  eventBus.emit(`UI:${backEvent}`);
38353
38806
  };
@@ -38395,15 +38848,15 @@ var init_PageHeader = __esm({
38395
38848
  /* @__PURE__ */ jsxs(Box, { children: [
38396
38849
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3", children: [
38397
38850
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
38398
- status && /* @__PURE__ */ jsx(
38851
+ statusBadge && /* @__PURE__ */ jsx(
38399
38852
  Typography,
38400
38853
  {
38401
38854
  variant: "small",
38402
38855
  className: cn(
38403
38856
  "px-2.5 py-1 rounded-full text-xs font-medium",
38404
- statusColors2[status.variant || "default"]
38857
+ statusColors2[statusBadge.variant || "default"]
38405
38858
  ),
38406
- children: status.label
38859
+ children: statusBadge.label
38407
38860
  }
38408
38861
  )
38409
38862
  ] }),
@@ -41632,7 +42085,7 @@ function formatFieldValue2(value, fieldName) {
41632
42085
  }
41633
42086
  return String(value);
41634
42087
  }
41635
- function renderRichFieldValue(value, fieldName, fieldType) {
42088
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41636
42089
  if (value === void 0 || value === null) return "\u2014";
41637
42090
  const str = String(value);
41638
42091
  switch (fieldType) {
@@ -41699,19 +42152,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
41699
42152
  }
41700
42153
  );
41701
42154
  case "date":
41702
- case "datetime": {
42155
+ case "datetime":
42156
+ case "timestamp": {
41703
42157
  const d = new Date(str);
41704
42158
  if (!isNaN(d.getTime())) {
41705
42159
  return d.toLocaleDateString(void 0, {
41706
42160
  year: "numeric",
41707
42161
  month: "long",
41708
42162
  day: "numeric",
41709
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
42163
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
41710
42164
  });
41711
42165
  }
41712
42166
  return str;
41713
42167
  }
42168
+ case "money": {
42169
+ const n = typeof value === "number" ? value : Number(str);
42170
+ if (!isNaN(n)) {
42171
+ return new Intl.NumberFormat(void 0, {
42172
+ style: "currency",
42173
+ currency: "USD"
42174
+ }).format(n);
42175
+ }
42176
+ return str;
42177
+ }
42178
+ case "relation": {
42179
+ const match = meta?.options?.find((opt) => opt.value === str);
42180
+ return match ? match.label : str;
42181
+ }
42182
+ case "file": {
42183
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
42184
+ const file = value;
42185
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
42186
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
42187
+ return /* @__PURE__ */ jsxs(
42188
+ "a",
42189
+ {
42190
+ href: typeof file.url === "string" ? file.url : void 0,
42191
+ download: label,
42192
+ 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",
42193
+ children: [
42194
+ /* @__PURE__ */ jsx(Icon, { icon: FileText, size: "sm", className: "text-muted-foreground" }),
42195
+ label,
42196
+ size && /* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
42197
+ ]
42198
+ }
42199
+ );
42200
+ }
42201
+ return str;
42202
+ }
41714
42203
  default:
42204
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
42205
+ return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
42206
+ }
41715
42207
  return formatFieldValue2(value, fieldName);
41716
42208
  }
41717
42209
  }
@@ -41738,8 +42230,18 @@ function buildFieldTypeMap(fields) {
41738
42230
  const map = {};
41739
42231
  if (!fields) return map;
41740
42232
  for (const f3 of fields) {
41741
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
41742
- map[f3.name] = f3.type;
42233
+ if (typeof f3 === "object" && f3.type !== void 0) {
42234
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
42235
+ }
42236
+ }
42237
+ return map;
42238
+ }
42239
+ function buildFieldMetaMap(fields) {
42240
+ const map = {};
42241
+ if (!fields) return map;
42242
+ for (const f3 of fields) {
42243
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
42244
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
41743
42245
  }
41744
42246
  }
41745
42247
  return map;
@@ -41759,6 +42261,7 @@ var init_DetailPanel = __esm({
41759
42261
  init_format();
41760
42262
  init_getNestedValue();
41761
42263
  init_useEventBus();
42264
+ init_UploadDropZone();
41762
42265
  formatFieldLabel = humanizeFieldName;
41763
42266
  ReactMarkdown2 = lazy(() => import('react-markdown'));
41764
42267
  DetailPanel = ({
@@ -41768,6 +42271,7 @@ var init_DetailPanel = __esm({
41768
42271
  avatar,
41769
42272
  sections: propSections,
41770
42273
  actions,
42274
+ backAction,
41771
42275
  footer,
41772
42276
  slideOver = false,
41773
42277
  className,
@@ -41776,7 +42280,8 @@ var init_DetailPanel = __esm({
41776
42280
  fieldNames,
41777
42281
  initialData,
41778
42282
  isLoading = false,
41779
- error
42283
+ error,
42284
+ relationsData
41780
42285
  }) => {
41781
42286
  const eventBus = useEventBus();
41782
42287
  const { t } = useTranslate();
@@ -41787,8 +42292,15 @@ var init_DetailPanel = __esm({
41787
42292
  };
41788
42293
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
41789
42294
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
42295
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
41790
42296
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
41791
42297
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
42298
+ const metaFor = (field) => {
42299
+ const base = fieldMetaMap[field];
42300
+ const options = relationsData?.[field];
42301
+ if (!base && !options) return void 0;
42302
+ return { ...base, options };
42303
+ };
41792
42304
  const handleActionClick = useCallback(
41793
42305
  (action, data2) => {
41794
42306
  if (action.navigatesTo) {
@@ -41867,7 +42379,7 @@ var init_DetailPanel = __esm({
41867
42379
  if (value !== void 0 && value !== null) {
41868
42380
  overviewFields.push({
41869
42381
  label: labelFor(field),
41870
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42382
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41871
42383
  icon: getFieldIcon(field)
41872
42384
  });
41873
42385
  }
@@ -41883,7 +42395,7 @@ var init_DetailPanel = __esm({
41883
42395
  if (value !== void 0 && value !== null) {
41884
42396
  metricsFields.push({
41885
42397
  label: labelFor(field),
41886
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42398
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41887
42399
  icon: getFieldIcon(field)
41888
42400
  });
41889
42401
  }
@@ -41899,7 +42411,7 @@ var init_DetailPanel = __esm({
41899
42411
  if (value !== void 0 && value !== null) {
41900
42412
  timelineFields.push({
41901
42413
  label: labelFor(field),
41902
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42414
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41903
42415
  icon: getFieldIcon(field)
41904
42416
  });
41905
42417
  }
@@ -41915,7 +42427,7 @@ var init_DetailPanel = __esm({
41915
42427
  if (value !== void 0 && value !== null) {
41916
42428
  descFields.push({
41917
42429
  label: labelFor(field),
41918
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42430
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41919
42431
  icon: getFieldIcon(field)
41920
42432
  });
41921
42433
  }
@@ -41963,7 +42475,7 @@ var init_DetailPanel = __esm({
41963
42475
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
41964
42476
  allFields.push({
41965
42477
  label: labelFor(field),
41966
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42478
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41967
42479
  icon: getFieldIcon(field)
41968
42480
  });
41969
42481
  } else {
@@ -41978,34 +42490,49 @@ var init_DetailPanel = __esm({
41978
42490
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
41979
42491
  const effectiveCloseAction = closeAction ?? { event: void 0};
41980
42492
  const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
41981
- /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41982
- otherActions.map((action, idx) => /* @__PURE__ */ jsx(
41983
- Button,
41984
- {
41985
- variant: action.variant || "secondary",
41986
- size: "sm",
41987
- action: action.navigatesTo ? void 0 : action.event,
41988
- actionPayload: { row: normalizedData },
41989
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
41990
- icon: action.icon,
41991
- "data-testid": action.event ? `action-${action.event}` : void 0,
41992
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
41993
- children: action.label
41994
- },
41995
- idx
41996
- )),
41997
- /* @__PURE__ */ jsx(
42493
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
42494
+ /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
41998
42495
  Button,
41999
42496
  {
42000
- variant: "ghost",
42497
+ variant: backAction.variant || "ghost",
42001
42498
  size: "sm",
42002
- action: effectiveCloseAction.event,
42499
+ action: backAction.navigatesTo ? void 0 : backAction.event,
42003
42500
  actionPayload: { row: normalizedData },
42004
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
42005
- icon: X,
42006
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42501
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
42502
+ icon: backAction.icon ?? ArrowLeft,
42503
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42504
+ children: backAction.label
42007
42505
  }
42008
- )
42506
+ ) }),
42507
+ /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
42508
+ otherActions.map((action, idx) => /* @__PURE__ */ jsx(
42509
+ Button,
42510
+ {
42511
+ variant: action.variant || "secondary",
42512
+ size: "sm",
42513
+ action: action.navigatesTo ? void 0 : action.event,
42514
+ actionPayload: { row: normalizedData },
42515
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
42516
+ icon: action.icon,
42517
+ "data-testid": action.event ? `action-${action.event}` : void 0,
42518
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
42519
+ children: action.label
42520
+ },
42521
+ idx
42522
+ )),
42523
+ /* @__PURE__ */ jsx(
42524
+ Button,
42525
+ {
42526
+ variant: "ghost",
42527
+ size: "sm",
42528
+ action: effectiveCloseAction.event,
42529
+ actionPayload: { row: normalizedData },
42530
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
42531
+ icon: X,
42532
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42533
+ }
42534
+ )
42535
+ ] })
42009
42536
  ] }),
42010
42537
  avatar,
42011
42538
  /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -42320,6 +42847,10 @@ function determineInputType(field) {
42320
42847
  return "password";
42321
42848
  case "url":
42322
42849
  return "url";
42850
+ case "file":
42851
+ return "file";
42852
+ case "money":
42853
+ return "currency";
42323
42854
  case "number":
42324
42855
  case "integer":
42325
42856
  case "float":
@@ -42381,6 +42912,7 @@ var init_Form = __esm({
42381
42912
  init_Typography();
42382
42913
  init_Icon();
42383
42914
  init_RelationSelect();
42915
+ init_UploadDropZone();
42384
42916
  init_Alert();
42385
42917
  init_useEventBus();
42386
42918
  init_debug();
@@ -42423,6 +42955,7 @@ var init_Form = __esm({
42423
42955
  cancelEvent = "CANCEL",
42424
42956
  relationsData = {},
42425
42957
  relationsLoading = {},
42958
+ fieldOverrides,
42426
42959
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
42427
42960
  conditionalFields: conditionalFieldsRaw = {},
42428
42961
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -42669,7 +43202,8 @@ var init_Form = __esm({
42669
43202
  label,
42670
43203
  field.required && /* @__PURE__ */ jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
42671
43204
  ] }),
42672
- renderFieldInput(field, fieldName, inputType, currentValue, label)
43205
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
43206
+ field.hint && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
42673
43207
  ] }, fieldName);
42674
43208
  },
42675
43209
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -42685,6 +43219,7 @@ var init_Form = __esm({
42685
43219
  name: field,
42686
43220
  type: entityField.type,
42687
43221
  required: entityField.required,
43222
+ hint: entityField.description,
42688
43223
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
42689
43224
  defaultValue: entityField.default,
42690
43225
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -42697,8 +43232,18 @@ var init_Form = __esm({
42697
43232
  return { name: field, type: "string" };
42698
43233
  }
42699
43234
  return field;
43235
+ }).map((field) => {
43236
+ const fieldName = field.name || field.field;
43237
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
43238
+ if (!override) return field;
43239
+ return {
43240
+ ...field,
43241
+ ...override.label !== void 0 ? { label: override.label } : {},
43242
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
43243
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
43244
+ };
42700
43245
  });
42701
- }, [effectiveFields, resolvedEntity]);
43246
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
42702
43247
  const schemaFields = React87__default.useMemo(() => {
42703
43248
  if (normalizedFields.length === 0) return null;
42704
43249
  if (isDebugEnabled()) {
@@ -42822,6 +43367,44 @@ var init_Form = __esm({
42822
43367
  max: field.max
42823
43368
  }
42824
43369
  );
43370
+ case "currency":
43371
+ return /* @__PURE__ */ jsx(
43372
+ Input,
43373
+ {
43374
+ ...commonProps,
43375
+ type: "number",
43376
+ step: "0.01",
43377
+ icon: DollarSign,
43378
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
43379
+ onChange: (e) => handleChange(
43380
+ fieldName,
43381
+ e.target.value ? Number(e.target.value) : void 0
43382
+ ),
43383
+ min: field.min,
43384
+ max: field.max
43385
+ }
43386
+ );
43387
+ case "file":
43388
+ return /* @__PURE__ */ jsx(
43389
+ UploadDropZone,
43390
+ {
43391
+ accept: field.pattern,
43392
+ maxFiles: 1,
43393
+ disabled: isLoading,
43394
+ onFiles: (files) => {
43395
+ const f3 = files[0];
43396
+ if (!f3) return;
43397
+ const reader = new FileReader();
43398
+ reader.onload = () => handleChange(fieldName, {
43399
+ name: f3.name,
43400
+ mimeType: f3.type,
43401
+ sizeBytes: f3.size,
43402
+ url: typeof reader.result === "string" ? reader.result : ""
43403
+ });
43404
+ reader.readAsDataURL(f3);
43405
+ }
43406
+ }
43407
+ );
42825
43408
  case "date":
42826
43409
  return /* @__PURE__ */ jsx(
42827
43410
  Input,
@@ -43854,6 +44437,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
43854
44437
  var init_MediaGallery = __esm({
43855
44438
  "components/core/organisms/MediaGallery.tsx"() {
43856
44439
  "use client";
44440
+ init_getNestedValue();
43857
44441
  init_cn();
43858
44442
  init_atoms();
43859
44443
  init_Stack();
@@ -43922,9 +44506,9 @@ var init_MediaGallery = __esm({
43922
44506
  return entityData.map((record, idx) => {
43923
44507
  return {
43924
44508
  id: String(record.id ?? idx),
43925
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
44509
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
43926
44510
  alt: record.alt ? String(record.alt) : void 0,
43927
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
44511
+ thumbnail: resolveImageUrl(record.thumbnail),
43928
44512
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
43929
44513
  };
43930
44514
  });
@@ -47792,6 +48376,7 @@ var init_component_registry_generated = __esm({
47792
48376
  init_DocSidebar();
47793
48377
  init_DocTOC();
47794
48378
  init_DocumentViewer();
48379
+ init_DrawFxLayer();
47795
48380
  init_DrawGroup();
47796
48381
  init_DrawMesh();
47797
48382
  init_DrawShape();
@@ -47822,6 +48407,7 @@ var init_component_registry_generated = __esm({
47822
48407
  init_FormField();
47823
48408
  init_FormSection();
47824
48409
  init_FormSectionHeader();
48410
+ init_FxOverlay();
47825
48411
  init_GameAudioToggle();
47826
48412
  init_GameHud();
47827
48413
  init_GameIcon();
@@ -48062,6 +48648,7 @@ var init_component_registry_generated = __esm({
48062
48648
  "DocSidebar": DocSidebar,
48063
48649
  "DocTOC": DocTOC,
48064
48650
  "DocumentViewer": DocumentViewer,
48651
+ "DrawFxLayer": DrawFxLayer,
48065
48652
  "DrawGroup": DrawGroup,
48066
48653
  "DrawMesh": DrawMesh,
48067
48654
  "DrawShape": DrawShape,
@@ -48092,6 +48679,7 @@ var init_component_registry_generated = __esm({
48092
48679
  "FormField": FormField,
48093
48680
  "FormLayout": FormLayout,
48094
48681
  "FormSectionHeader": FormSectionHeader,
48682
+ "FxOverlay": FxOverlay,
48095
48683
  "GameAudioToggle": GameAudioToggle,
48096
48684
  "GameHud": GameHud,
48097
48685
  "GameIcon": GameIcon,
@@ -48267,7 +48855,8 @@ __export(UISlotRenderer_exports, {
48267
48855
  SlotContentRenderer: () => SlotContentRenderer,
48268
48856
  SuspenseConfigProvider: () => SuspenseConfigProvider,
48269
48857
  UISlotComponent: () => UISlotComponent,
48270
- UISlotRenderer: () => UISlotRenderer
48858
+ UISlotRenderer: () => UISlotRenderer,
48859
+ renderPatternValue: () => renderPatternValue
48271
48860
  });
48272
48861
  function SuspenseConfigProvider({
48273
48862
  config,
@@ -48303,6 +48892,9 @@ function enrichFormFields(fields, entityDef) {
48303
48892
  type: entityField.type,
48304
48893
  required: entityField.required ?? false
48305
48894
  };
48895
+ if (entityField.description) {
48896
+ enriched.hint = entityField.description;
48897
+ }
48306
48898
  if (entityField.values && entityField.values.length > 0) {
48307
48899
  enriched.values = entityField.values;
48308
48900
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -48326,6 +48918,9 @@ function enrichFormFields(fields, entityDef) {
48326
48918
  if (entityField.required && !("required" in obj)) {
48327
48919
  enriched.required = true;
48328
48920
  }
48921
+ if (entityField.description && !obj.hint && !obj.help) {
48922
+ enriched.hint = entityField.description;
48923
+ }
48329
48924
  if (!obj.values && !obj.options) {
48330
48925
  if (entityField.values && entityField.values.length > 0) {
48331
48926
  enriched.values = entityField.values;
@@ -48341,6 +48936,32 @@ function enrichFormFields(fields, entityDef) {
48341
48936
  return field;
48342
48937
  });
48343
48938
  }
48939
+ function enrichDetailFields(fields, entityDef) {
48940
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
48941
+ const metaFor = (name) => {
48942
+ const entityField = fieldMap.get(name);
48943
+ if (!entityField) return void 0;
48944
+ const meta = { type: entityField.type };
48945
+ const values = entityField.values ?? entityField.enumValues;
48946
+ if (values && values.length > 0) meta.values = values;
48947
+ if (entityField.relation) meta.relation = entityField.relation.entity;
48948
+ return meta;
48949
+ };
48950
+ return fields.map((field) => {
48951
+ if (typeof field === "string") {
48952
+ const meta = metaFor(field);
48953
+ return meta ? { key: field, ...meta } : field;
48954
+ }
48955
+ if (field && typeof field === "object" && !Array.isArray(field) && !React87__default.isValidElement(field) && !(field instanceof Date)) {
48956
+ const obj = field;
48957
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
48958
+ if (!fieldName || obj.type) return field;
48959
+ const meta = metaFor(fieldName);
48960
+ return meta ? { ...obj, ...meta } : field;
48961
+ }
48962
+ return field;
48963
+ });
48964
+ }
48344
48965
  function renderContainedPortal(t, slot, content, onDismiss) {
48345
48966
  const slotContent = /* @__PURE__ */ jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsx(SlotContentRenderer, { content, onDismiss }) });
48346
48967
  const slotId = `slot-${slot}`;
@@ -48850,6 +49471,24 @@ function isPatternConfig(value) {
48850
49471
  const record = value;
48851
49472
  return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
48852
49473
  }
49474
+ function renderPatternValue(value) {
49475
+ if (value === null || value === void 0) return null;
49476
+ if (typeof value === "boolean" || typeof value === "function") return null;
49477
+ if (typeof value === "number") return value;
49478
+ if (typeof value === "string") return renderPatternChildren(value, () => {
49479
+ });
49480
+ if (value instanceof Date) return value.toLocaleString();
49481
+ if (React87__default.isValidElement(value)) return value;
49482
+ if (Array.isArray(value)) {
49483
+ return value.map((item, index) => /* @__PURE__ */ jsx(React87__default.Fragment, { children: renderPatternValue(item) }, index));
49484
+ }
49485
+ if (isPatternConfig(value)) {
49486
+ const { type, ...props } = value;
49487
+ return renderPatternChildren({ type, props }, () => {
49488
+ });
49489
+ }
49490
+ return null;
49491
+ }
48853
49492
  function isPlainConfigObject(value) {
48854
49493
  if (React87__default.isValidElement(value)) return false;
48855
49494
  if (value instanceof Date) return false;
@@ -49051,9 +49690,9 @@ function SlotContentRenderer({
49051
49690
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
49052
49691
  }
49053
49692
  }
49054
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
49055
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
49056
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
49693
+ const fieldsContract = getPatternFieldsContract(content.pattern) ?? (getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
49694
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
49695
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
49057
49696
  }
49058
49697
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
49059
49698
  return /* @__PURE__ */ jsx(
@@ -49163,7 +49802,7 @@ function UISlotRenderer({
49163
49802
  }
49164
49803
  return wrapped;
49165
49804
  }
49166
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49805
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49167
49806
  var init_UISlotRenderer = __esm({
49168
49807
  "components/core/organisms/UISlotRenderer.tsx"() {
49169
49808
  "use client";
@@ -49194,12 +49833,6 @@ var init_UISlotRenderer = __esm({
49194
49833
  modal: "form",
49195
49834
  drawer: "form"
49196
49835
  };
49197
- FORM_PATTERNS = /* @__PURE__ */ new Set([
49198
- "form",
49199
- "form-section",
49200
- "inline-edit-form",
49201
- "wizard-step"
49202
- ]);
49203
49836
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
49204
49837
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
49205
49838
  "logo",