@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.
@@ -4,11 +4,11 @@ import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntity
4
4
  export { EntitySchemaProvider, ServerBridgeProvider, TraitContext, TraitProvider, useEntitySchema, useEntitySchemaOptional, useServerBridge, useTrait, useTraitContext } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import { StateMachineManager, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, createServerEffectHandlers, EffectExecutor, createContextFromBindings, createTickScheduler, isValidCronExpression, parseDurationString, InMemoryPersistence, normalizeCallSiteConfigToValues, interpolateValue } from '@almadar/runtime';
7
- import { mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, walkSExpr, buildResolvedTraitConfigs, isRenderBindingMarker, isInlineTrait, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER, ANIMATION_NAMES } from '@almadar/core';
7
+ import { mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, walkSExpr, buildResolvedTraitConfigs, isRenderBindingMarker, isFileValue, isInlineTrait, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER, ANIMATION_NAMES } 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
  import { isKnownStdOperator } from '@almadar/std/registry';
82
82
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
83
83
 
@@ -13853,7 +13853,7 @@ var init_AlgoGraphCanvas = __esm({
13853
13853
  }
13854
13854
  const badgeGeoms = [];
13855
13855
  for (const g of nodeGeoms) {
13856
- if (!g.badge) continue;
13856
+ if (!g.badge || g.badge.text === "") continue;
13857
13857
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
13858
13858
  badgeGeoms.push({
13859
13859
  cx: g.x + g.radius * 0.75,
@@ -13919,7 +13919,7 @@ var init_AlgoGraphCanvas = __esm({
13919
13919
  };
13920
13920
  }
13921
13921
  });
13922
- 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;
13922
+ 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;
13923
13923
  var init_AlgorithmCanvas = __esm({
13924
13924
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
13925
13925
  "use client";
@@ -13946,9 +13946,18 @@ var init_AlgorithmCanvas = __esm({
13946
13946
  FRAME_ACTIVE_COLOR = "#3b82f6";
13947
13947
  FRAME_RETURNING_COLOR = "#f59e0b";
13948
13948
  FRAME_DONE_COLOR = "#94a3b8";
13949
+ FRAME_RIM_COLOR = {
13950
+ active: "#1d4ed8",
13951
+ returning: "#b45309",
13952
+ done: "#64748b"
13953
+ };
13949
13954
  FRAME_LABEL_COLOR = "#ffffff";
13950
13955
  FRAME_DETAIL_COLOR = "#e2e8f0";
13951
13956
  FRAME_TWO_LINE_MIN_H = 22;
13957
+ FRAME_STRIP_H = 44;
13958
+ FRAME_GAP = 6;
13959
+ FRAME_BOTTOM_PAD = 8;
13960
+ FRAME_MIN_H = 12;
13952
13961
  BUCKET_INDEX_FILL = "#e2e8f0";
13953
13962
  BUCKET_INDEX_STROKE = "#9ca3af";
13954
13963
  BUCKET_INDEX_TEXT = "#374151";
@@ -14445,14 +14454,18 @@ var init_AlgorithmCanvas = __esm({
14445
14454
  if (frames.length > 0) {
14446
14455
  const panelYFrames = panelY.frames;
14447
14456
  const n = frames.length;
14448
- const frameH = panelHeight / n;
14457
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
14458
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
14459
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
14449
14460
  const x = 8;
14450
14461
  const w = width - 16;
14462
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
14451
14463
  frames.forEach((f3, i) => {
14452
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
14464
+ const y = bottom - i * stride - frameH;
14453
14465
  const state = f3.state ?? "active";
14454
14466
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
14455
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
14467
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
14468
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
14456
14469
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
14457
14470
  out.push({
14458
14471
  type: "text",
@@ -14460,14 +14473,14 @@ var init_AlgorithmCanvas = __esm({
14460
14473
  y: y + frameH * 0.35,
14461
14474
  text: f3.label,
14462
14475
  color: FRAME_LABEL_COLOR,
14463
- fontSize: 10,
14476
+ fontSize: 11,
14464
14477
  align: "left"
14465
14478
  });
14466
14479
  if (f3.detail) {
14467
14480
  out.push({
14468
14481
  type: "text",
14469
14482
  x: 16,
14470
- y: y + frameH * 0.7,
14483
+ y: y + frameH * 0.72,
14471
14484
  text: f3.detail,
14472
14485
  color: FRAME_DETAIL_COLOR,
14473
14486
  fontSize: 10,
@@ -20930,8 +20943,6 @@ var init_ButtonGroup = __esm({
20930
20943
  ButtonGroup.displayName = "ButtonGroup";
20931
20944
  }
20932
20945
  });
20933
-
20934
- // lib/getNestedValue.ts
20935
20946
  function getNestedValue(obj, path) {
20936
20947
  if (obj === null || obj === void 0 || !path) {
20937
20948
  return void 0;
@@ -20952,6 +20963,15 @@ function getNestedValue(obj, path) {
20952
20963
  }
20953
20964
  return value;
20954
20965
  }
20966
+ function resolveImageUrl(value) {
20967
+ if (typeof value === "string") {
20968
+ return value;
20969
+ }
20970
+ if (isFileValue(value)) {
20971
+ return value.url;
20972
+ }
20973
+ return void 0;
20974
+ }
20955
20975
  var init_getNestedValue = __esm({
20956
20976
  "lib/getNestedValue.ts"() {
20957
20977
  }
@@ -21589,8 +21609,8 @@ var init_CardGrid = __esm({
21589
21609
  actionPayload: { row: itemData },
21590
21610
  children: [
21591
21611
  imageField && (() => {
21592
- const imgUrl = getNestedValue(itemData, imageField);
21593
- if (!imgUrl || typeof imgUrl !== "string") return null;
21612
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
21613
+ if (!imgUrl) return null;
21594
21614
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
21595
21615
  "img",
21596
21616
  {
@@ -25606,8 +25626,8 @@ function DataGrid({
25606
25626
  ),
25607
25627
  children: [
25608
25628
  imageField && (() => {
25609
- const imgUrl = getNestedValue(itemData, imageField);
25610
- if (!imgUrl || typeof imgUrl !== "string") return null;
25629
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
25630
+ if (!imgUrl) return null;
25611
25631
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
25612
25632
  "img",
25613
25633
  {
@@ -28153,7 +28173,7 @@ var init_SearchInput = __esm({
28153
28173
  /* @__PURE__ */ jsx(
28154
28174
  Input,
28155
28175
  {
28156
- type: "text",
28176
+ type: "search",
28157
28177
  value: searchValue,
28158
28178
  onChange: handleChange,
28159
28179
  placeholder: resolvedPlaceholder,
@@ -29479,6 +29499,7 @@ var MathCanvas;
29479
29499
  var init_MathCanvas = __esm({
29480
29500
  "components/learning/molecules/MathCanvas.tsx"() {
29481
29501
  "use client";
29502
+ init_useEventBus();
29482
29503
  init_atoms();
29483
29504
  init_Stack();
29484
29505
  init_LearningCanvas();
@@ -29510,9 +29531,32 @@ var init_MathCanvas = __esm({
29510
29531
  interactive = false,
29511
29532
  animate = false,
29512
29533
  onShapeClick,
29534
+ keyMap,
29535
+ keyUpMap,
29513
29536
  isLoading,
29514
29537
  error
29515
29538
  }) => {
29539
+ const eventBus = useEventBus();
29540
+ useEffect(() => {
29541
+ if (!keyMap && !keyUpMap) return;
29542
+ const onDown = (e) => {
29543
+ const ev = keyMap?.[e.code];
29544
+ if (ev) {
29545
+ eventBus.emit(`UI:${ev}`, {});
29546
+ e.preventDefault();
29547
+ }
29548
+ };
29549
+ const onUp = (e) => {
29550
+ const ev = keyUpMap?.[e.code];
29551
+ if (ev) eventBus.emit(`UI:${ev}`, {});
29552
+ };
29553
+ window.addEventListener("keydown", onDown);
29554
+ window.addEventListener("keyup", onUp);
29555
+ return () => {
29556
+ window.removeEventListener("keydown", onDown);
29557
+ window.removeEventListener("keyup", onUp);
29558
+ };
29559
+ }, [keyMap, keyUpMap, eventBus]);
29516
29560
  const derivedShapes = useMemo(() => {
29517
29561
  const out = [];
29518
29562
  const margin = 24;
@@ -29525,11 +29569,11 @@ var init_MathCanvas = __esm({
29525
29569
  if (showGrid) {
29526
29570
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
29527
29571
  const px = mapX(x);
29528
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
29572
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
29529
29573
  }
29530
29574
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
29531
29575
  const py = mapY(y);
29532
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
29576
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
29533
29577
  }
29534
29578
  }
29535
29579
  if (showTickLabels) {
@@ -31004,13 +31048,13 @@ var init_MapView = __esm({
31004
31048
  shadowSize: [41, 41]
31005
31049
  });
31006
31050
  L.Marker.prototype.options.icon = defaultIcon;
31007
- const { useEffect: useEffect60, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__default;
31051
+ const { useEffect: useEffect61, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__default;
31008
31052
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31009
31053
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31010
31054
  function MapUpdater({ centerLat, centerLng, zoom }) {
31011
31055
  const map = useMap();
31012
31056
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31013
- useEffect60(() => {
31057
+ useEffect61(() => {
31014
31058
  const prev = prevRef.current;
31015
31059
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31016
31060
  map.setView([centerLat, centerLng], zoom);
@@ -31021,7 +31065,7 @@ var init_MapView = __esm({
31021
31065
  }
31022
31066
  function MapClickHandler({ onMapClick }) {
31023
31067
  const map = useMap();
31024
- useEffect60(() => {
31068
+ useEffect61(() => {
31025
31069
  if (!onMapClick) return;
31026
31070
  const handler = (e) => {
31027
31071
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -37699,6 +37743,7 @@ var init_PageHeader = __esm({
37699
37743
  className
37700
37744
  }) => {
37701
37745
  const eventBus = useEventBus();
37746
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
37702
37747
  const handleBack = () => {
37703
37748
  eventBus.emit(`UI:${backEvent}`);
37704
37749
  };
@@ -37746,15 +37791,15 @@ var init_PageHeader = __esm({
37746
37791
  /* @__PURE__ */ jsxs(Box, { children: [
37747
37792
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3", children: [
37748
37793
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
37749
- status && /* @__PURE__ */ jsx(
37794
+ statusBadge && /* @__PURE__ */ jsx(
37750
37795
  Typography,
37751
37796
  {
37752
37797
  variant: "small",
37753
37798
  className: cn(
37754
37799
  "px-2.5 py-1 rounded-full text-xs font-medium",
37755
- statusColors2[status.variant || "default"]
37800
+ statusColors2[statusBadge.variant || "default"]
37756
37801
  ),
37757
- children: status.label
37802
+ children: statusBadge.label
37758
37803
  }
37759
37804
  )
37760
37805
  ] }),
@@ -40983,7 +41028,7 @@ function formatFieldValue2(value, fieldName) {
40983
41028
  }
40984
41029
  return String(value);
40985
41030
  }
40986
- function renderRichFieldValue(value, fieldName, fieldType) {
41031
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
40987
41032
  if (value === void 0 || value === null) return "\u2014";
40988
41033
  const str = String(value);
40989
41034
  switch (fieldType) {
@@ -41050,19 +41095,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
41050
41095
  }
41051
41096
  );
41052
41097
  case "date":
41053
- case "datetime": {
41098
+ case "datetime":
41099
+ case "timestamp": {
41054
41100
  const d = new Date(str);
41055
41101
  if (!isNaN(d.getTime())) {
41056
41102
  return d.toLocaleDateString(void 0, {
41057
41103
  year: "numeric",
41058
41104
  month: "long",
41059
41105
  day: "numeric",
41060
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
41106
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
41061
41107
  });
41062
41108
  }
41063
41109
  return str;
41064
41110
  }
41111
+ case "money": {
41112
+ const n = typeof value === "number" ? value : Number(str);
41113
+ if (!isNaN(n)) {
41114
+ return new Intl.NumberFormat(void 0, {
41115
+ style: "currency",
41116
+ currency: "USD"
41117
+ }).format(n);
41118
+ }
41119
+ return str;
41120
+ }
41121
+ case "relation": {
41122
+ const match = meta?.options?.find((opt) => opt.value === str);
41123
+ return match ? match.label : str;
41124
+ }
41125
+ case "file": {
41126
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
41127
+ const file = value;
41128
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
41129
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
41130
+ return /* @__PURE__ */ jsxs(
41131
+ "a",
41132
+ {
41133
+ href: typeof file.url === "string" ? file.url : void 0,
41134
+ download: label,
41135
+ 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",
41136
+ children: [
41137
+ /* @__PURE__ */ jsx(Icon, { icon: FileText, size: "sm", className: "text-muted-foreground" }),
41138
+ label,
41139
+ size && /* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
41140
+ ]
41141
+ }
41142
+ );
41143
+ }
41144
+ return str;
41145
+ }
41065
41146
  default:
41147
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
41148
+ return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
41149
+ }
41066
41150
  return formatFieldValue2(value, fieldName);
41067
41151
  }
41068
41152
  }
@@ -41089,8 +41173,18 @@ function buildFieldTypeMap(fields) {
41089
41173
  const map = {};
41090
41174
  if (!fields) return map;
41091
41175
  for (const f3 of fields) {
41092
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
41093
- map[f3.name] = f3.type;
41176
+ if (typeof f3 === "object" && f3.type !== void 0) {
41177
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
41178
+ }
41179
+ }
41180
+ return map;
41181
+ }
41182
+ function buildFieldMetaMap(fields) {
41183
+ const map = {};
41184
+ if (!fields) return map;
41185
+ for (const f3 of fields) {
41186
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
41187
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
41094
41188
  }
41095
41189
  }
41096
41190
  return map;
@@ -41110,6 +41204,7 @@ var init_DetailPanel = __esm({
41110
41204
  init_format();
41111
41205
  init_getNestedValue();
41112
41206
  init_useEventBus();
41207
+ init_UploadDropZone();
41113
41208
  formatFieldLabel = humanizeFieldName;
41114
41209
  ReactMarkdown2 = lazy(() => import('react-markdown'));
41115
41210
  DetailPanel = ({
@@ -41119,6 +41214,7 @@ var init_DetailPanel = __esm({
41119
41214
  avatar,
41120
41215
  sections: propSections,
41121
41216
  actions,
41217
+ backAction,
41122
41218
  footer,
41123
41219
  slideOver = false,
41124
41220
  className,
@@ -41127,7 +41223,8 @@ var init_DetailPanel = __esm({
41127
41223
  fieldNames,
41128
41224
  initialData,
41129
41225
  isLoading = false,
41130
- error
41226
+ error,
41227
+ relationsData
41131
41228
  }) => {
41132
41229
  const eventBus = useEventBus();
41133
41230
  const { t } = useTranslate();
@@ -41138,8 +41235,15 @@ var init_DetailPanel = __esm({
41138
41235
  };
41139
41236
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
41140
41237
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
41238
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
41141
41239
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
41142
41240
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
41241
+ const metaFor = (field) => {
41242
+ const base = fieldMetaMap[field];
41243
+ const options = relationsData?.[field];
41244
+ if (!base && !options) return void 0;
41245
+ return { ...base, options };
41246
+ };
41143
41247
  const handleActionClick = useCallback(
41144
41248
  (action, data2) => {
41145
41249
  if (action.navigatesTo) {
@@ -41218,7 +41322,7 @@ var init_DetailPanel = __esm({
41218
41322
  if (value !== void 0 && value !== null) {
41219
41323
  overviewFields.push({
41220
41324
  label: labelFor(field),
41221
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41325
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41222
41326
  icon: getFieldIcon(field)
41223
41327
  });
41224
41328
  }
@@ -41234,7 +41338,7 @@ var init_DetailPanel = __esm({
41234
41338
  if (value !== void 0 && value !== null) {
41235
41339
  metricsFields.push({
41236
41340
  label: labelFor(field),
41237
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41341
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41238
41342
  icon: getFieldIcon(field)
41239
41343
  });
41240
41344
  }
@@ -41250,7 +41354,7 @@ var init_DetailPanel = __esm({
41250
41354
  if (value !== void 0 && value !== null) {
41251
41355
  timelineFields.push({
41252
41356
  label: labelFor(field),
41253
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41357
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41254
41358
  icon: getFieldIcon(field)
41255
41359
  });
41256
41360
  }
@@ -41266,7 +41370,7 @@ var init_DetailPanel = __esm({
41266
41370
  if (value !== void 0 && value !== null) {
41267
41371
  descFields.push({
41268
41372
  label: labelFor(field),
41269
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41373
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41270
41374
  icon: getFieldIcon(field)
41271
41375
  });
41272
41376
  }
@@ -41314,7 +41418,7 @@ var init_DetailPanel = __esm({
41314
41418
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
41315
41419
  allFields.push({
41316
41420
  label: labelFor(field),
41317
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41421
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41318
41422
  icon: getFieldIcon(field)
41319
41423
  });
41320
41424
  } else {
@@ -41329,34 +41433,49 @@ var init_DetailPanel = __esm({
41329
41433
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
41330
41434
  const effectiveCloseAction = closeAction ?? { event: void 0};
41331
41435
  const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
41332
- /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41333
- otherActions.map((action, idx) => /* @__PURE__ */ jsx(
41334
- Button,
41335
- {
41336
- variant: action.variant || "secondary",
41337
- size: "sm",
41338
- action: action.navigatesTo ? void 0 : action.event,
41339
- actionPayload: { row: normalizedData },
41340
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
41341
- icon: action.icon,
41342
- "data-testid": action.event ? `action-${action.event}` : void 0,
41343
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
41344
- children: action.label
41345
- },
41346
- idx
41347
- )),
41348
- /* @__PURE__ */ jsx(
41436
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
41437
+ /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
41349
41438
  Button,
41350
41439
  {
41351
- variant: "ghost",
41440
+ variant: backAction.variant || "ghost",
41352
41441
  size: "sm",
41353
- action: effectiveCloseAction.event,
41442
+ action: backAction.navigatesTo ? void 0 : backAction.event,
41354
41443
  actionPayload: { row: normalizedData },
41355
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
41356
- icon: X,
41357
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
41444
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
41445
+ icon: backAction.icon ?? ArrowLeft,
41446
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
41447
+ children: backAction.label
41358
41448
  }
41359
- )
41449
+ ) }),
41450
+ /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41451
+ otherActions.map((action, idx) => /* @__PURE__ */ jsx(
41452
+ Button,
41453
+ {
41454
+ variant: action.variant || "secondary",
41455
+ size: "sm",
41456
+ action: action.navigatesTo ? void 0 : action.event,
41457
+ actionPayload: { row: normalizedData },
41458
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
41459
+ icon: action.icon,
41460
+ "data-testid": action.event ? `action-${action.event}` : void 0,
41461
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
41462
+ children: action.label
41463
+ },
41464
+ idx
41465
+ )),
41466
+ /* @__PURE__ */ jsx(
41467
+ Button,
41468
+ {
41469
+ variant: "ghost",
41470
+ size: "sm",
41471
+ action: effectiveCloseAction.event,
41472
+ actionPayload: { row: normalizedData },
41473
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
41474
+ icon: X,
41475
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
41476
+ }
41477
+ )
41478
+ ] })
41360
41479
  ] }),
41361
41480
  avatar,
41362
41481
  /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -41671,6 +41790,10 @@ function determineInputType(field) {
41671
41790
  return "password";
41672
41791
  case "url":
41673
41792
  return "url";
41793
+ case "file":
41794
+ return "file";
41795
+ case "money":
41796
+ return "currency";
41674
41797
  case "number":
41675
41798
  case "integer":
41676
41799
  case "float":
@@ -41732,6 +41855,7 @@ var init_Form = __esm({
41732
41855
  init_Typography();
41733
41856
  init_Icon();
41734
41857
  init_RelationSelect();
41858
+ init_UploadDropZone();
41735
41859
  init_Alert();
41736
41860
  init_useEventBus();
41737
41861
  init_debug();
@@ -41774,6 +41898,7 @@ var init_Form = __esm({
41774
41898
  cancelEvent = "CANCEL",
41775
41899
  relationsData = {},
41776
41900
  relationsLoading = {},
41901
+ fieldOverrides,
41777
41902
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
41778
41903
  conditionalFields: conditionalFieldsRaw = {},
41779
41904
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -42020,7 +42145,8 @@ var init_Form = __esm({
42020
42145
  label,
42021
42146
  field.required && /* @__PURE__ */ jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
42022
42147
  ] }),
42023
- renderFieldInput(field, fieldName, inputType, currentValue, label)
42148
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
42149
+ field.hint && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
42024
42150
  ] }, fieldName);
42025
42151
  },
42026
42152
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -42036,6 +42162,7 @@ var init_Form = __esm({
42036
42162
  name: field,
42037
42163
  type: entityField.type,
42038
42164
  required: entityField.required,
42165
+ hint: entityField.description,
42039
42166
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
42040
42167
  defaultValue: entityField.default,
42041
42168
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -42048,8 +42175,18 @@ var init_Form = __esm({
42048
42175
  return { name: field, type: "string" };
42049
42176
  }
42050
42177
  return field;
42178
+ }).map((field) => {
42179
+ const fieldName = field.name || field.field;
42180
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
42181
+ if (!override) return field;
42182
+ return {
42183
+ ...field,
42184
+ ...override.label !== void 0 ? { label: override.label } : {},
42185
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
42186
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
42187
+ };
42051
42188
  });
42052
- }, [effectiveFields, resolvedEntity]);
42189
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
42053
42190
  const schemaFields = React85__default.useMemo(() => {
42054
42191
  if (normalizedFields.length === 0) return null;
42055
42192
  if (isDebugEnabled()) {
@@ -42173,6 +42310,44 @@ var init_Form = __esm({
42173
42310
  max: field.max
42174
42311
  }
42175
42312
  );
42313
+ case "currency":
42314
+ return /* @__PURE__ */ jsx(
42315
+ Input,
42316
+ {
42317
+ ...commonProps,
42318
+ type: "number",
42319
+ step: "0.01",
42320
+ icon: DollarSign,
42321
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
42322
+ onChange: (e) => handleChange(
42323
+ fieldName,
42324
+ e.target.value ? Number(e.target.value) : void 0
42325
+ ),
42326
+ min: field.min,
42327
+ max: field.max
42328
+ }
42329
+ );
42330
+ case "file":
42331
+ return /* @__PURE__ */ jsx(
42332
+ UploadDropZone,
42333
+ {
42334
+ accept: field.pattern,
42335
+ maxFiles: 1,
42336
+ disabled: isLoading,
42337
+ onFiles: (files) => {
42338
+ const f3 = files[0];
42339
+ if (!f3) return;
42340
+ const reader = new FileReader();
42341
+ reader.onload = () => handleChange(fieldName, {
42342
+ name: f3.name,
42343
+ mimeType: f3.type,
42344
+ sizeBytes: f3.size,
42345
+ url: typeof reader.result === "string" ? reader.result : ""
42346
+ });
42347
+ reader.readAsDataURL(f3);
42348
+ }
42349
+ }
42350
+ );
42176
42351
  case "date":
42177
42352
  return /* @__PURE__ */ jsx(
42178
42353
  Input,
@@ -43205,6 +43380,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
43205
43380
  var init_MediaGallery = __esm({
43206
43381
  "components/core/organisms/MediaGallery.tsx"() {
43207
43382
  "use client";
43383
+ init_getNestedValue();
43208
43384
  init_cn();
43209
43385
  init_atoms();
43210
43386
  init_Stack();
@@ -43273,9 +43449,9 @@ var init_MediaGallery = __esm({
43273
43449
  return entityData.map((record, idx) => {
43274
43450
  return {
43275
43451
  id: String(record.id ?? idx),
43276
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
43452
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
43277
43453
  alt: record.alt ? String(record.alt) : void 0,
43278
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
43454
+ thumbnail: resolveImageUrl(record.thumbnail),
43279
43455
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
43280
43456
  };
43281
43457
  });
@@ -47637,7 +47813,8 @@ __export(UISlotRenderer_exports, {
47637
47813
  SlotContentRenderer: () => SlotContentRenderer,
47638
47814
  SuspenseConfigProvider: () => SuspenseConfigProvider,
47639
47815
  UISlotComponent: () => UISlotComponent,
47640
- UISlotRenderer: () => UISlotRenderer
47816
+ UISlotRenderer: () => UISlotRenderer,
47817
+ renderPatternValue: () => renderPatternValue
47641
47818
  });
47642
47819
  function SuspenseConfigProvider({
47643
47820
  config,
@@ -47673,6 +47850,9 @@ function enrichFormFields(fields, entityDef) {
47673
47850
  type: entityField.type,
47674
47851
  required: entityField.required ?? false
47675
47852
  };
47853
+ if (entityField.description) {
47854
+ enriched.hint = entityField.description;
47855
+ }
47676
47856
  if (entityField.values && entityField.values.length > 0) {
47677
47857
  enriched.values = entityField.values;
47678
47858
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -47696,6 +47876,9 @@ function enrichFormFields(fields, entityDef) {
47696
47876
  if (entityField.required && !("required" in obj)) {
47697
47877
  enriched.required = true;
47698
47878
  }
47879
+ if (entityField.description && !obj.hint && !obj.help) {
47880
+ enriched.hint = entityField.description;
47881
+ }
47699
47882
  if (!obj.values && !obj.options) {
47700
47883
  if (entityField.values && entityField.values.length > 0) {
47701
47884
  enriched.values = entityField.values;
@@ -47711,6 +47894,32 @@ function enrichFormFields(fields, entityDef) {
47711
47894
  return field;
47712
47895
  });
47713
47896
  }
47897
+ function enrichDetailFields(fields, entityDef) {
47898
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
47899
+ const metaFor = (name) => {
47900
+ const entityField = fieldMap.get(name);
47901
+ if (!entityField) return void 0;
47902
+ const meta = { type: entityField.type };
47903
+ const values = entityField.values ?? entityField.enumValues;
47904
+ if (values && values.length > 0) meta.values = values;
47905
+ if (entityField.relation) meta.relation = entityField.relation.entity;
47906
+ return meta;
47907
+ };
47908
+ return fields.map((field) => {
47909
+ if (typeof field === "string") {
47910
+ const meta = metaFor(field);
47911
+ return meta ? { key: field, ...meta } : field;
47912
+ }
47913
+ if (field && typeof field === "object" && !Array.isArray(field) && !React85__default.isValidElement(field) && !(field instanceof Date)) {
47914
+ const obj = field;
47915
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
47916
+ if (!fieldName || obj.type) return field;
47917
+ const meta = metaFor(fieldName);
47918
+ return meta ? { ...obj, ...meta } : field;
47919
+ }
47920
+ return field;
47921
+ });
47922
+ }
47714
47923
  function renderContainedPortal(t, slot, content, onDismiss) {
47715
47924
  const slotContent = /* @__PURE__ */ jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsx(SlotContentRenderer, { content, onDismiss }) });
47716
47925
  const slotId = `slot-${slot}`;
@@ -48220,6 +48429,24 @@ function isPatternConfig(value) {
48220
48429
  const record = value;
48221
48430
  return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
48222
48431
  }
48432
+ function renderPatternValue(value) {
48433
+ if (value === null || value === void 0) return null;
48434
+ if (typeof value === "boolean" || typeof value === "function") return null;
48435
+ if (typeof value === "number") return value;
48436
+ if (typeof value === "string") return renderPatternChildren(value, () => {
48437
+ });
48438
+ if (value instanceof Date) return value.toLocaleString();
48439
+ if (React85__default.isValidElement(value)) return value;
48440
+ if (Array.isArray(value)) {
48441
+ return value.map((item, index) => /* @__PURE__ */ jsx(React85__default.Fragment, { children: renderPatternValue(item) }, index));
48442
+ }
48443
+ if (isPatternConfig(value)) {
48444
+ const { type, ...props } = value;
48445
+ return renderPatternChildren({ type, props }, () => {
48446
+ });
48447
+ }
48448
+ return null;
48449
+ }
48223
48450
  function isPlainConfigObject(value) {
48224
48451
  if (React85__default.isValidElement(value)) return false;
48225
48452
  if (value instanceof Date) return false;
@@ -48421,9 +48648,9 @@ function SlotContentRenderer({
48421
48648
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
48422
48649
  }
48423
48650
  }
48424
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
48425
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
48426
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
48651
+ const fieldsContract = getPatternFieldsContract(content.pattern) ?? (getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
48652
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
48653
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
48427
48654
  }
48428
48655
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
48429
48656
  return /* @__PURE__ */ jsx(
@@ -48533,7 +48760,7 @@ function UISlotRenderer({
48533
48760
  }
48534
48761
  return wrapped;
48535
48762
  }
48536
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
48763
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
48537
48764
  var init_UISlotRenderer = __esm({
48538
48765
  "components/core/organisms/UISlotRenderer.tsx"() {
48539
48766
  "use client";
@@ -48564,12 +48791,6 @@ var init_UISlotRenderer = __esm({
48564
48791
  modal: "form",
48565
48792
  drawer: "form"
48566
48793
  };
48567
- FORM_PATTERNS = /* @__PURE__ */ new Set([
48568
- "form",
48569
- "form-section",
48570
- "inline-edit-form",
48571
- "wizard-step"
48572
- ]);
48573
48794
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
48574
48795
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
48575
48796
  "logo",
@@ -50442,11 +50663,11 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, routeP
50442
50663
  for (const orb of schema.orbitals) {
50443
50664
  for (const pageRef of orb.pages ?? []) {
50444
50665
  const name = typeof pageRef === "object" && pageRef !== null ? pageRef.name : void 0;
50445
- if (name === pageName) return orb.theme;
50666
+ if (name === pageName) return orb.theme ?? schema.theme;
50446
50667
  }
50447
50668
  }
50448
50669
  }
50449
- return schema.orbitals[0]?.theme;
50670
+ return schema.orbitals[0]?.theme ?? schema.theme;
50450
50671
  }, [schema, pageName]);
50451
50672
  const inner = /* @__PURE__ */ jsx(VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsx(
50452
50673
  EntitySchemaProvider,