@almadar/ui 5.150.0 → 5.152.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.
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
 
@@ -17259,7 +17259,7 @@ var init_AlgoGraphCanvas = __esm({
17259
17259
  }
17260
17260
  const badgeGeoms = [];
17261
17261
  for (const g of nodeGeoms) {
17262
- if (!g.badge) continue;
17262
+ if (!g.badge || g.badge.text === "") continue;
17263
17263
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
17264
17264
  badgeGeoms.push({
17265
17265
  cx: g.x + g.radius * 0.75,
@@ -17325,7 +17325,7 @@ var init_AlgoGraphCanvas = __esm({
17325
17325
  };
17326
17326
  }
17327
17327
  });
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;
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_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
17329
  var init_AlgorithmCanvas = __esm({
17330
17330
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
17331
17331
  "use client";
@@ -17352,9 +17352,18 @@ var init_AlgorithmCanvas = __esm({
17352
17352
  FRAME_ACTIVE_COLOR = "#3b82f6";
17353
17353
  FRAME_RETURNING_COLOR = "#f59e0b";
17354
17354
  FRAME_DONE_COLOR = "#94a3b8";
17355
+ FRAME_RIM_COLOR = {
17356
+ active: "#1d4ed8",
17357
+ returning: "#b45309",
17358
+ done: "#64748b"
17359
+ };
17355
17360
  FRAME_LABEL_COLOR = "#ffffff";
17356
17361
  FRAME_DETAIL_COLOR = "#e2e8f0";
17357
17362
  FRAME_TWO_LINE_MIN_H = 22;
17363
+ FRAME_STRIP_H = 44;
17364
+ FRAME_GAP = 6;
17365
+ FRAME_BOTTOM_PAD = 8;
17366
+ FRAME_MIN_H = 12;
17358
17367
  BUCKET_INDEX_FILL = "#e2e8f0";
17359
17368
  BUCKET_INDEX_STROKE = "#9ca3af";
17360
17369
  BUCKET_INDEX_TEXT = "#374151";
@@ -17851,14 +17860,18 @@ var init_AlgorithmCanvas = __esm({
17851
17860
  if (frames.length > 0) {
17852
17861
  const panelYFrames = panelY.frames;
17853
17862
  const n = frames.length;
17854
- const frameH = panelHeight / n;
17863
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
17864
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
17865
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
17855
17866
  const x = 8;
17856
17867
  const w = width - 16;
17868
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
17857
17869
  frames.forEach((f3, i) => {
17858
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
17870
+ const y = bottom - i * stride - frameH;
17859
17871
  const state = f3.state ?? "active";
17860
17872
  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 });
17873
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
17874
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
17862
17875
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
17863
17876
  out.push({
17864
17877
  type: "text",
@@ -17866,14 +17879,14 @@ var init_AlgorithmCanvas = __esm({
17866
17879
  y: y + frameH * 0.35,
17867
17880
  text: f3.label,
17868
17881
  color: FRAME_LABEL_COLOR,
17869
- fontSize: 10,
17882
+ fontSize: 11,
17870
17883
  align: "left"
17871
17884
  });
17872
17885
  if (f3.detail) {
17873
17886
  out.push({
17874
17887
  type: "text",
17875
17888
  x: 16,
17876
- y: y + frameH * 0.7,
17889
+ y: y + frameH * 0.72,
17877
17890
  text: f3.detail,
17878
17891
  color: FRAME_DETAIL_COLOR,
17879
17892
  fontSize: 10,
@@ -23559,8 +23572,6 @@ var init_ButtonGroup = __esm({
23559
23572
  ButtonGroup.displayName = "ButtonGroup";
23560
23573
  }
23561
23574
  });
23562
-
23563
- // lib/getNestedValue.ts
23564
23575
  function getNestedValue(obj, path) {
23565
23576
  if (obj === null || obj === void 0 || !path) {
23566
23577
  return void 0;
@@ -23581,6 +23592,15 @@ function getNestedValue(obj, path) {
23581
23592
  }
23582
23593
  return value;
23583
23594
  }
23595
+ function resolveImageUrl(value) {
23596
+ if (typeof value === "string") {
23597
+ return value;
23598
+ }
23599
+ if (isFileValue(value)) {
23600
+ return value.url;
23601
+ }
23602
+ return void 0;
23603
+ }
23584
23604
  var init_getNestedValue = __esm({
23585
23605
  "lib/getNestedValue.ts"() {
23586
23606
  }
@@ -24285,8 +24305,8 @@ var init_CardGrid = __esm({
24285
24305
  actionPayload: { row: itemData },
24286
24306
  children: [
24287
24307
  imageField && (() => {
24288
- const imgUrl = getNestedValue(itemData, imageField);
24289
- if (!imgUrl || typeof imgUrl !== "string") return null;
24308
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
24309
+ if (!imgUrl) return null;
24290
24310
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
24291
24311
  "img",
24292
24312
  {
@@ -28316,8 +28336,8 @@ function DataGrid({
28316
28336
  ),
28317
28337
  children: [
28318
28338
  imageField && (() => {
28319
- const imgUrl = getNestedValue(itemData, imageField);
28320
- if (!imgUrl || typeof imgUrl !== "string") return null;
28339
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
28340
+ if (!imgUrl) return null;
28321
28341
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
28322
28342
  "img",
28323
28343
  {
@@ -30927,7 +30947,7 @@ var init_SearchInput = __esm({
30927
30947
  /* @__PURE__ */ jsx(
30928
30948
  Input,
30929
30949
  {
30930
- type: "text",
30950
+ type: "search",
30931
30951
  value: searchValue,
30932
30952
  onChange: handleChange,
30933
30953
  placeholder: resolvedPlaceholder,
@@ -32253,6 +32273,7 @@ var MathCanvas;
32253
32273
  var init_MathCanvas = __esm({
32254
32274
  "components/learning/molecules/MathCanvas.tsx"() {
32255
32275
  "use client";
32276
+ init_useEventBus();
32256
32277
  init_atoms();
32257
32278
  init_Stack();
32258
32279
  init_LearningCanvas();
@@ -32284,9 +32305,32 @@ var init_MathCanvas = __esm({
32284
32305
  interactive = false,
32285
32306
  animate = false,
32286
32307
  onShapeClick,
32308
+ keyMap,
32309
+ keyUpMap,
32287
32310
  isLoading,
32288
32311
  error
32289
32312
  }) => {
32313
+ const eventBus = useEventBus();
32314
+ useEffect(() => {
32315
+ if (!keyMap && !keyUpMap) return;
32316
+ const onDown = (e) => {
32317
+ const ev = keyMap?.[e.code];
32318
+ if (ev) {
32319
+ eventBus.emit(`UI:${ev}`, {});
32320
+ e.preventDefault();
32321
+ }
32322
+ };
32323
+ const onUp = (e) => {
32324
+ const ev = keyUpMap?.[e.code];
32325
+ if (ev) eventBus.emit(`UI:${ev}`, {});
32326
+ };
32327
+ window.addEventListener("keydown", onDown);
32328
+ window.addEventListener("keyup", onUp);
32329
+ return () => {
32330
+ window.removeEventListener("keydown", onDown);
32331
+ window.removeEventListener("keyup", onUp);
32332
+ };
32333
+ }, [keyMap, keyUpMap, eventBus]);
32290
32334
  const derivedShapes = useMemo(() => {
32291
32335
  const out = [];
32292
32336
  const margin = 24;
@@ -32299,11 +32343,11 @@ var init_MathCanvas = __esm({
32299
32343
  if (showGrid) {
32300
32344
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
32301
32345
  const px = mapX(x);
32302
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
32346
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32303
32347
  }
32304
32348
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
32305
32349
  const py = mapY(y);
32306
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
32350
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32307
32351
  }
32308
32352
  }
32309
32353
  if (showTickLabels) {
@@ -33778,13 +33822,13 @@ var init_MapView = __esm({
33778
33822
  shadowSize: [41, 41]
33779
33823
  });
33780
33824
  L.Marker.prototype.options.icon = defaultIcon;
33781
- const { useEffect: useEffect64, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__default;
33825
+ const { useEffect: useEffect65, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__default;
33782
33826
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33783
33827
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33784
33828
  function MapUpdater({ centerLat, centerLng, zoom }) {
33785
33829
  const map = useMap();
33786
33830
  const prevRef = useRef65({ centerLat, centerLng, zoom });
33787
- useEffect64(() => {
33831
+ useEffect65(() => {
33788
33832
  const prev = prevRef.current;
33789
33833
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
33790
33834
  map.setView([centerLat, centerLng], zoom);
@@ -33795,7 +33839,7 @@ var init_MapView = __esm({
33795
33839
  }
33796
33840
  function MapClickHandler({ onMapClick }) {
33797
33841
  const map = useMap();
33798
- useEffect64(() => {
33842
+ useEffect65(() => {
33799
33843
  if (!onMapClick) return;
33800
33844
  const handler = (e) => {
33801
33845
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -40202,6 +40246,7 @@ var init_PageHeader = __esm({
40202
40246
  className
40203
40247
  }) => {
40204
40248
  const eventBus = useEventBus();
40249
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
40205
40250
  const handleBack = () => {
40206
40251
  eventBus.emit(`UI:${backEvent}`);
40207
40252
  };
@@ -40249,15 +40294,15 @@ var init_PageHeader = __esm({
40249
40294
  /* @__PURE__ */ jsxs(Box, { children: [
40250
40295
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3", children: [
40251
40296
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
40252
- status && /* @__PURE__ */ jsx(
40297
+ statusBadge && /* @__PURE__ */ jsx(
40253
40298
  Typography,
40254
40299
  {
40255
40300
  variant: "small",
40256
40301
  className: cn(
40257
40302
  "px-2.5 py-1 rounded-full text-xs font-medium",
40258
- statusColors2[status.variant || "default"]
40303
+ statusColors2[statusBadge.variant || "default"]
40259
40304
  ),
40260
- children: status.label
40305
+ children: statusBadge.label
40261
40306
  }
40262
40307
  )
40263
40308
  ] }),
@@ -43486,7 +43531,7 @@ function formatFieldValue2(value, fieldName) {
43486
43531
  }
43487
43532
  return String(value);
43488
43533
  }
43489
- function renderRichFieldValue(value, fieldName, fieldType) {
43534
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
43490
43535
  if (value === void 0 || value === null) return "\u2014";
43491
43536
  const str = String(value);
43492
43537
  switch (fieldType) {
@@ -43553,19 +43598,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
43553
43598
  }
43554
43599
  );
43555
43600
  case "date":
43556
- case "datetime": {
43601
+ case "datetime":
43602
+ case "timestamp": {
43557
43603
  const d = new Date(str);
43558
43604
  if (!isNaN(d.getTime())) {
43559
43605
  return d.toLocaleDateString(void 0, {
43560
43606
  year: "numeric",
43561
43607
  month: "long",
43562
43608
  day: "numeric",
43563
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
43609
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
43564
43610
  });
43565
43611
  }
43566
43612
  return str;
43567
43613
  }
43614
+ case "money": {
43615
+ const n = typeof value === "number" ? value : Number(str);
43616
+ if (!isNaN(n)) {
43617
+ return new Intl.NumberFormat(void 0, {
43618
+ style: "currency",
43619
+ currency: "USD"
43620
+ }).format(n);
43621
+ }
43622
+ return str;
43623
+ }
43624
+ case "relation": {
43625
+ const match = meta?.options?.find((opt) => opt.value === str);
43626
+ return match ? match.label : str;
43627
+ }
43628
+ case "file": {
43629
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
43630
+ const file = value;
43631
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
43632
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
43633
+ return /* @__PURE__ */ jsxs(
43634
+ "a",
43635
+ {
43636
+ href: typeof file.url === "string" ? file.url : void 0,
43637
+ download: label,
43638
+ 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",
43639
+ children: [
43640
+ /* @__PURE__ */ jsx(Icon, { icon: FileText, size: "sm", className: "text-muted-foreground" }),
43641
+ label,
43642
+ size && /* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
43643
+ ]
43644
+ }
43645
+ );
43646
+ }
43647
+ return str;
43648
+ }
43568
43649
  default:
43650
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
43651
+ return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
43652
+ }
43569
43653
  return formatFieldValue2(value, fieldName);
43570
43654
  }
43571
43655
  }
@@ -43592,8 +43676,18 @@ function buildFieldTypeMap(fields) {
43592
43676
  const map = {};
43593
43677
  if (!fields) return map;
43594
43678
  for (const f3 of fields) {
43595
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
43596
- map[f3.name] = f3.type;
43679
+ if (typeof f3 === "object" && f3.type !== void 0) {
43680
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
43681
+ }
43682
+ }
43683
+ return map;
43684
+ }
43685
+ function buildFieldMetaMap(fields) {
43686
+ const map = {};
43687
+ if (!fields) return map;
43688
+ for (const f3 of fields) {
43689
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
43690
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
43597
43691
  }
43598
43692
  }
43599
43693
  return map;
@@ -43613,6 +43707,7 @@ var init_DetailPanel = __esm({
43613
43707
  init_format();
43614
43708
  init_getNestedValue();
43615
43709
  init_useEventBus();
43710
+ init_UploadDropZone();
43616
43711
  formatFieldLabel = humanizeFieldName;
43617
43712
  ReactMarkdown2 = lazy(() => import('react-markdown'));
43618
43713
  DetailPanel = ({
@@ -43622,6 +43717,7 @@ var init_DetailPanel = __esm({
43622
43717
  avatar,
43623
43718
  sections: propSections,
43624
43719
  actions,
43720
+ backAction,
43625
43721
  footer,
43626
43722
  slideOver = false,
43627
43723
  className,
@@ -43630,7 +43726,8 @@ var init_DetailPanel = __esm({
43630
43726
  fieldNames,
43631
43727
  initialData,
43632
43728
  isLoading = false,
43633
- error
43729
+ error,
43730
+ relationsData
43634
43731
  }) => {
43635
43732
  const eventBus = useEventBus();
43636
43733
  const { t } = useTranslate();
@@ -43641,8 +43738,15 @@ var init_DetailPanel = __esm({
43641
43738
  };
43642
43739
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
43643
43740
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
43741
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
43644
43742
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
43645
43743
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
43744
+ const metaFor = (field) => {
43745
+ const base = fieldMetaMap[field];
43746
+ const options = relationsData?.[field];
43747
+ if (!base && !options) return void 0;
43748
+ return { ...base, options };
43749
+ };
43646
43750
  const handleActionClick = useCallback(
43647
43751
  (action, data2) => {
43648
43752
  if (action.navigatesTo) {
@@ -43721,7 +43825,7 @@ var init_DetailPanel = __esm({
43721
43825
  if (value !== void 0 && value !== null) {
43722
43826
  overviewFields.push({
43723
43827
  label: labelFor(field),
43724
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43828
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43725
43829
  icon: getFieldIcon(field)
43726
43830
  });
43727
43831
  }
@@ -43737,7 +43841,7 @@ var init_DetailPanel = __esm({
43737
43841
  if (value !== void 0 && value !== null) {
43738
43842
  metricsFields.push({
43739
43843
  label: labelFor(field),
43740
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43844
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43741
43845
  icon: getFieldIcon(field)
43742
43846
  });
43743
43847
  }
@@ -43753,7 +43857,7 @@ var init_DetailPanel = __esm({
43753
43857
  if (value !== void 0 && value !== null) {
43754
43858
  timelineFields.push({
43755
43859
  label: labelFor(field),
43756
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43860
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43757
43861
  icon: getFieldIcon(field)
43758
43862
  });
43759
43863
  }
@@ -43769,7 +43873,7 @@ var init_DetailPanel = __esm({
43769
43873
  if (value !== void 0 && value !== null) {
43770
43874
  descFields.push({
43771
43875
  label: labelFor(field),
43772
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43876
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43773
43877
  icon: getFieldIcon(field)
43774
43878
  });
43775
43879
  }
@@ -43817,7 +43921,7 @@ var init_DetailPanel = __esm({
43817
43921
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
43818
43922
  allFields.push({
43819
43923
  label: labelFor(field),
43820
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43924
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43821
43925
  icon: getFieldIcon(field)
43822
43926
  });
43823
43927
  } else {
@@ -43832,34 +43936,49 @@ var init_DetailPanel = __esm({
43832
43936
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
43833
43937
  const effectiveCloseAction = closeAction ?? { event: void 0};
43834
43938
  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(
43939
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
43940
+ /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
43852
43941
  Button,
43853
43942
  {
43854
- variant: "ghost",
43943
+ variant: backAction.variant || "ghost",
43855
43944
  size: "sm",
43856
- action: effectiveCloseAction.event,
43945
+ action: backAction.navigatesTo ? void 0 : backAction.event,
43857
43946
  actionPayload: { row: normalizedData },
43858
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
43859
- icon: X,
43860
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
43947
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
43948
+ icon: backAction.icon ?? ArrowLeft,
43949
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
43950
+ children: backAction.label
43861
43951
  }
43862
- )
43952
+ ) }),
43953
+ /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
43954
+ otherActions.map((action, idx) => /* @__PURE__ */ jsx(
43955
+ Button,
43956
+ {
43957
+ variant: action.variant || "secondary",
43958
+ size: "sm",
43959
+ action: action.navigatesTo ? void 0 : action.event,
43960
+ actionPayload: { row: normalizedData },
43961
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43962
+ icon: action.icon,
43963
+ "data-testid": action.event ? `action-${action.event}` : void 0,
43964
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43965
+ children: action.label
43966
+ },
43967
+ idx
43968
+ )),
43969
+ /* @__PURE__ */ jsx(
43970
+ Button,
43971
+ {
43972
+ variant: "ghost",
43973
+ size: "sm",
43974
+ action: effectiveCloseAction.event,
43975
+ actionPayload: { row: normalizedData },
43976
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
43977
+ icon: X,
43978
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
43979
+ }
43980
+ )
43981
+ ] })
43863
43982
  ] }),
43864
43983
  avatar,
43865
43984
  /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -44174,6 +44293,10 @@ function determineInputType(field) {
44174
44293
  return "password";
44175
44294
  case "url":
44176
44295
  return "url";
44296
+ case "file":
44297
+ return "file";
44298
+ case "money":
44299
+ return "currency";
44177
44300
  case "number":
44178
44301
  case "integer":
44179
44302
  case "float":
@@ -44235,6 +44358,7 @@ var init_Form = __esm({
44235
44358
  init_Typography();
44236
44359
  init_Icon();
44237
44360
  init_RelationSelect();
44361
+ init_UploadDropZone();
44238
44362
  init_Alert();
44239
44363
  init_useEventBus();
44240
44364
  init_debug();
@@ -44277,6 +44401,7 @@ var init_Form = __esm({
44277
44401
  cancelEvent = "CANCEL",
44278
44402
  relationsData = {},
44279
44403
  relationsLoading = {},
44404
+ fieldOverrides,
44280
44405
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
44281
44406
  conditionalFields: conditionalFieldsRaw = {},
44282
44407
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -44523,7 +44648,8 @@ var init_Form = __esm({
44523
44648
  label,
44524
44649
  field.required && /* @__PURE__ */ jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
44525
44650
  ] }),
44526
- renderFieldInput(field, fieldName, inputType, currentValue, label)
44651
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
44652
+ field.hint && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
44527
44653
  ] }, fieldName);
44528
44654
  },
44529
44655
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -44539,6 +44665,7 @@ var init_Form = __esm({
44539
44665
  name: field,
44540
44666
  type: entityField.type,
44541
44667
  required: entityField.required,
44668
+ hint: entityField.description,
44542
44669
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
44543
44670
  defaultValue: entityField.default,
44544
44671
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -44551,8 +44678,18 @@ var init_Form = __esm({
44551
44678
  return { name: field, type: "string" };
44552
44679
  }
44553
44680
  return field;
44681
+ }).map((field) => {
44682
+ const fieldName = field.name || field.field;
44683
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
44684
+ if (!override) return field;
44685
+ return {
44686
+ ...field,
44687
+ ...override.label !== void 0 ? { label: override.label } : {},
44688
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
44689
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
44690
+ };
44554
44691
  });
44555
- }, [effectiveFields, resolvedEntity]);
44692
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
44556
44693
  const schemaFields = React94__default.useMemo(() => {
44557
44694
  if (normalizedFields.length === 0) return null;
44558
44695
  if (isDebugEnabled()) {
@@ -44676,6 +44813,44 @@ var init_Form = __esm({
44676
44813
  max: field.max
44677
44814
  }
44678
44815
  );
44816
+ case "currency":
44817
+ return /* @__PURE__ */ jsx(
44818
+ Input,
44819
+ {
44820
+ ...commonProps,
44821
+ type: "number",
44822
+ step: "0.01",
44823
+ icon: DollarSign,
44824
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
44825
+ onChange: (e) => handleChange(
44826
+ fieldName,
44827
+ e.target.value ? Number(e.target.value) : void 0
44828
+ ),
44829
+ min: field.min,
44830
+ max: field.max
44831
+ }
44832
+ );
44833
+ case "file":
44834
+ return /* @__PURE__ */ jsx(
44835
+ UploadDropZone,
44836
+ {
44837
+ accept: field.pattern,
44838
+ maxFiles: 1,
44839
+ disabled: isLoading,
44840
+ onFiles: (files) => {
44841
+ const f3 = files[0];
44842
+ if (!f3) return;
44843
+ const reader = new FileReader();
44844
+ reader.onload = () => handleChange(fieldName, {
44845
+ name: f3.name,
44846
+ mimeType: f3.type,
44847
+ sizeBytes: f3.size,
44848
+ url: typeof reader.result === "string" ? reader.result : ""
44849
+ });
44850
+ reader.readAsDataURL(f3);
44851
+ }
44852
+ }
44853
+ );
44679
44854
  case "date":
44680
44855
  return /* @__PURE__ */ jsx(
44681
44856
  Input,
@@ -45708,6 +45883,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
45708
45883
  var init_MediaGallery = __esm({
45709
45884
  "components/core/organisms/MediaGallery.tsx"() {
45710
45885
  "use client";
45886
+ init_getNestedValue();
45711
45887
  init_cn();
45712
45888
  init_atoms();
45713
45889
  init_Stack();
@@ -45776,9 +45952,9 @@ var init_MediaGallery = __esm({
45776
45952
  return entityData.map((record, idx) => {
45777
45953
  return {
45778
45954
  id: String(record.id ?? idx),
45779
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
45955
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
45780
45956
  alt: record.alt ? String(record.alt) : void 0,
45781
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
45957
+ thumbnail: resolveImageUrl(record.thumbnail),
45782
45958
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
45783
45959
  };
45784
45960
  });
@@ -50140,7 +50316,8 @@ __export(UISlotRenderer_exports, {
50140
50316
  SlotContentRenderer: () => SlotContentRenderer,
50141
50317
  SuspenseConfigProvider: () => SuspenseConfigProvider,
50142
50318
  UISlotComponent: () => UISlotComponent,
50143
- UISlotRenderer: () => UISlotRenderer
50319
+ UISlotRenderer: () => UISlotRenderer,
50320
+ renderPatternValue: () => renderPatternValue
50144
50321
  });
50145
50322
  function SuspenseConfigProvider({
50146
50323
  config,
@@ -50176,6 +50353,9 @@ function enrichFormFields(fields, entityDef) {
50176
50353
  type: entityField.type,
50177
50354
  required: entityField.required ?? false
50178
50355
  };
50356
+ if (entityField.description) {
50357
+ enriched.hint = entityField.description;
50358
+ }
50179
50359
  if (entityField.values && entityField.values.length > 0) {
50180
50360
  enriched.values = entityField.values;
50181
50361
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -50199,6 +50379,9 @@ function enrichFormFields(fields, entityDef) {
50199
50379
  if (entityField.required && !("required" in obj)) {
50200
50380
  enriched.required = true;
50201
50381
  }
50382
+ if (entityField.description && !obj.hint && !obj.help) {
50383
+ enriched.hint = entityField.description;
50384
+ }
50202
50385
  if (!obj.values && !obj.options) {
50203
50386
  if (entityField.values && entityField.values.length > 0) {
50204
50387
  enriched.values = entityField.values;
@@ -50214,6 +50397,32 @@ function enrichFormFields(fields, entityDef) {
50214
50397
  return field;
50215
50398
  });
50216
50399
  }
50400
+ function enrichDetailFields(fields, entityDef) {
50401
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
50402
+ const metaFor = (name) => {
50403
+ const entityField = fieldMap.get(name);
50404
+ if (!entityField) return void 0;
50405
+ const meta = { type: entityField.type };
50406
+ const values = entityField.values ?? entityField.enumValues;
50407
+ if (values && values.length > 0) meta.values = values;
50408
+ if (entityField.relation) meta.relation = entityField.relation.entity;
50409
+ return meta;
50410
+ };
50411
+ return fields.map((field) => {
50412
+ if (typeof field === "string") {
50413
+ const meta = metaFor(field);
50414
+ return meta ? { key: field, ...meta } : field;
50415
+ }
50416
+ if (field && typeof field === "object" && !Array.isArray(field) && !React94__default.isValidElement(field) && !(field instanceof Date)) {
50417
+ const obj = field;
50418
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
50419
+ if (!fieldName || obj.type) return field;
50420
+ const meta = metaFor(fieldName);
50421
+ return meta ? { ...obj, ...meta } : field;
50422
+ }
50423
+ return field;
50424
+ });
50425
+ }
50217
50426
  function renderContainedPortal(t, slot, content, onDismiss) {
50218
50427
  const slotContent = /* @__PURE__ */ jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsx(SlotContentRenderer, { content, onDismiss }) });
50219
50428
  const slotId = `slot-${slot}`;
@@ -50723,6 +50932,24 @@ function isPatternConfig(value) {
50723
50932
  const record = value;
50724
50933
  return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
50725
50934
  }
50935
+ function renderPatternValue(value) {
50936
+ if (value === null || value === void 0) return null;
50937
+ if (typeof value === "boolean" || typeof value === "function") return null;
50938
+ if (typeof value === "number") return value;
50939
+ if (typeof value === "string") return renderPatternChildren(value, () => {
50940
+ });
50941
+ if (value instanceof Date) return value.toLocaleString();
50942
+ if (React94__default.isValidElement(value)) return value;
50943
+ if (Array.isArray(value)) {
50944
+ return value.map((item, index) => /* @__PURE__ */ jsx(React94__default.Fragment, { children: renderPatternValue(item) }, index));
50945
+ }
50946
+ if (isPatternConfig(value)) {
50947
+ const { type, ...props } = value;
50948
+ return renderPatternChildren({ type, props }, () => {
50949
+ });
50950
+ }
50951
+ return null;
50952
+ }
50726
50953
  function isPlainConfigObject(value) {
50727
50954
  if (React94__default.isValidElement(value)) return false;
50728
50955
  if (value instanceof Date) return false;
@@ -50924,9 +51151,9 @@ function SlotContentRenderer({
50924
51151
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
50925
51152
  }
50926
51153
  }
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);
51154
+ const fieldsContract = getPatternFieldsContract(content.pattern) ?? (getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
51155
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
51156
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
50930
51157
  }
50931
51158
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
50932
51159
  return /* @__PURE__ */ jsx(
@@ -51036,7 +51263,7 @@ function UISlotRenderer({
51036
51263
  }
51037
51264
  return wrapped;
51038
51265
  }
51039
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51266
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51040
51267
  var init_UISlotRenderer = __esm({
51041
51268
  "components/core/organisms/UISlotRenderer.tsx"() {
51042
51269
  "use client";
@@ -51067,12 +51294,6 @@ var init_UISlotRenderer = __esm({
51067
51294
  modal: "form",
51068
51295
  drawer: "form"
51069
51296
  };
51070
- FORM_PATTERNS = /* @__PURE__ */ new Set([
51071
- "form",
51072
- "form-section",
51073
- "inline-edit-form",
51074
- "wizard-step"
51075
- ]);
51076
51297
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
51077
51298
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
51078
51299
  "logo",
@@ -56218,11 +56439,11 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, routeP
56218
56439
  for (const orb of schema.orbitals) {
56219
56440
  for (const pageRef of orb.pages ?? []) {
56220
56441
  const name = typeof pageRef === "object" && pageRef !== null ? pageRef.name : void 0;
56221
- if (name === pageName) return orb.theme;
56442
+ if (name === pageName) return orb.theme ?? schema.theme;
56222
56443
  }
56223
56444
  }
56224
56445
  }
56225
- return schema.orbitals[0]?.theme;
56446
+ return schema.orbitals[0]?.theme ?? schema.theme;
56226
56447
  }, [schema, pageName]);
56227
56448
  const inner = /* @__PURE__ */ jsx(VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsx(
56228
56449
  EntitySchemaProvider,