@almadar/ui 5.151.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.
@@ -14243,7 +14243,7 @@ var init_AlgoGraphCanvas = __esm({
14243
14243
  }
14244
14244
  const badgeGeoms = [];
14245
14245
  for (const g of nodeGeoms) {
14246
- if (!g.badge) continue;
14246
+ if (!g.badge || g.badge.text === "") continue;
14247
14247
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
14248
14248
  badgeGeoms.push({
14249
14249
  cx: g.x + g.radius * 0.75,
@@ -14309,7 +14309,7 @@ var init_AlgoGraphCanvas = __esm({
14309
14309
  };
14310
14310
  }
14311
14311
  });
14312
- 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;
14312
+ 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;
14313
14313
  var init_AlgorithmCanvas = __esm({
14314
14314
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
14315
14315
  "use client";
@@ -14336,9 +14336,18 @@ var init_AlgorithmCanvas = __esm({
14336
14336
  FRAME_ACTIVE_COLOR = "#3b82f6";
14337
14337
  FRAME_RETURNING_COLOR = "#f59e0b";
14338
14338
  FRAME_DONE_COLOR = "#94a3b8";
14339
+ FRAME_RIM_COLOR = {
14340
+ active: "#1d4ed8",
14341
+ returning: "#b45309",
14342
+ done: "#64748b"
14343
+ };
14339
14344
  FRAME_LABEL_COLOR = "#ffffff";
14340
14345
  FRAME_DETAIL_COLOR = "#e2e8f0";
14341
14346
  FRAME_TWO_LINE_MIN_H = 22;
14347
+ FRAME_STRIP_H = 44;
14348
+ FRAME_GAP = 6;
14349
+ FRAME_BOTTOM_PAD = 8;
14350
+ FRAME_MIN_H = 12;
14342
14351
  BUCKET_INDEX_FILL = "#e2e8f0";
14343
14352
  BUCKET_INDEX_STROKE = "#9ca3af";
14344
14353
  BUCKET_INDEX_TEXT = "#374151";
@@ -14835,14 +14844,18 @@ var init_AlgorithmCanvas = __esm({
14835
14844
  if (frames.length > 0) {
14836
14845
  const panelYFrames = panelY.frames;
14837
14846
  const n = frames.length;
14838
- const frameH = panelHeight / n;
14847
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
14848
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
14849
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
14839
14850
  const x = 8;
14840
14851
  const w = width - 16;
14852
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
14841
14853
  frames.forEach((f3, i) => {
14842
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
14854
+ const y = bottom - i * stride - frameH;
14843
14855
  const state = f3.state ?? "active";
14844
14856
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
14845
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
14857
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
14858
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
14846
14859
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
14847
14860
  out.push({
14848
14861
  type: "text",
@@ -14850,14 +14863,14 @@ var init_AlgorithmCanvas = __esm({
14850
14863
  y: y + frameH * 0.35,
14851
14864
  text: f3.label,
14852
14865
  color: FRAME_LABEL_COLOR,
14853
- fontSize: 10,
14866
+ fontSize: 11,
14854
14867
  align: "left"
14855
14868
  });
14856
14869
  if (f3.detail) {
14857
14870
  out.push({
14858
14871
  type: "text",
14859
14872
  x: 16,
14860
- y: y + frameH * 0.7,
14873
+ y: y + frameH * 0.72,
14861
14874
  text: f3.detail,
14862
14875
  color: FRAME_DETAIL_COLOR,
14863
14876
  fontSize: 10,
@@ -21370,8 +21383,6 @@ var init_ButtonGroup = __esm({
21370
21383
  ButtonGroup.displayName = "ButtonGroup";
21371
21384
  }
21372
21385
  });
21373
-
21374
- // lib/getNestedValue.ts
21375
21386
  function getNestedValue(obj, path) {
21376
21387
  if (obj === null || obj === void 0 || !path) {
21377
21388
  return void 0;
@@ -21392,6 +21403,15 @@ function getNestedValue(obj, path) {
21392
21403
  }
21393
21404
  return value;
21394
21405
  }
21406
+ function resolveImageUrl(value) {
21407
+ if (typeof value === "string") {
21408
+ return value;
21409
+ }
21410
+ if (core.isFileValue(value)) {
21411
+ return value.url;
21412
+ }
21413
+ return void 0;
21414
+ }
21395
21415
  var init_getNestedValue = __esm({
21396
21416
  "lib/getNestedValue.ts"() {
21397
21417
  }
@@ -22096,8 +22116,8 @@ var init_CardGrid = __esm({
22096
22116
  actionPayload: { row: itemData },
22097
22117
  children: [
22098
22118
  imageField && (() => {
22099
- const imgUrl = getNestedValue(itemData, imageField);
22100
- if (!imgUrl || typeof imgUrl !== "string") return null;
22119
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
22120
+ if (!imgUrl) return null;
22101
22121
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
22102
22122
  "img",
22103
22123
  {
@@ -26127,8 +26147,8 @@ function DataGrid({
26127
26147
  ),
26128
26148
  children: [
26129
26149
  imageField && (() => {
26130
- const imgUrl = getNestedValue(itemData, imageField);
26131
- if (!imgUrl || typeof imgUrl !== "string") return null;
26150
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
26151
+ if (!imgUrl) return null;
26132
26152
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
26133
26153
  "img",
26134
26154
  {
@@ -28738,7 +28758,7 @@ var init_SearchInput = __esm({
28738
28758
  /* @__PURE__ */ jsxRuntime.jsx(
28739
28759
  Input,
28740
28760
  {
28741
- type: "text",
28761
+ type: "search",
28742
28762
  value: searchValue,
28743
28763
  onChange: handleChange,
28744
28764
  placeholder: resolvedPlaceholder,
@@ -30064,6 +30084,7 @@ var MathCanvas;
30064
30084
  var init_MathCanvas = __esm({
30065
30085
  "components/learning/molecules/MathCanvas.tsx"() {
30066
30086
  "use client";
30087
+ init_useEventBus();
30067
30088
  init_atoms();
30068
30089
  init_Stack();
30069
30090
  init_LearningCanvas();
@@ -30095,9 +30116,32 @@ var init_MathCanvas = __esm({
30095
30116
  interactive = false,
30096
30117
  animate = false,
30097
30118
  onShapeClick,
30119
+ keyMap,
30120
+ keyUpMap,
30098
30121
  isLoading,
30099
30122
  error
30100
30123
  }) => {
30124
+ const eventBus = useEventBus();
30125
+ React87.useEffect(() => {
30126
+ if (!keyMap && !keyUpMap) return;
30127
+ const onDown = (e) => {
30128
+ const ev = keyMap?.[e.code];
30129
+ if (ev) {
30130
+ eventBus.emit(`UI:${ev}`, {});
30131
+ e.preventDefault();
30132
+ }
30133
+ };
30134
+ const onUp = (e) => {
30135
+ const ev = keyUpMap?.[e.code];
30136
+ if (ev) eventBus.emit(`UI:${ev}`, {});
30137
+ };
30138
+ window.addEventListener("keydown", onDown);
30139
+ window.addEventListener("keyup", onUp);
30140
+ return () => {
30141
+ window.removeEventListener("keydown", onDown);
30142
+ window.removeEventListener("keyup", onUp);
30143
+ };
30144
+ }, [keyMap, keyUpMap, eventBus]);
30101
30145
  const derivedShapes = React87.useMemo(() => {
30102
30146
  const out = [];
30103
30147
  const margin = 24;
@@ -30110,11 +30154,11 @@ var init_MathCanvas = __esm({
30110
30154
  if (showGrid) {
30111
30155
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
30112
30156
  const px = mapX(x);
30113
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
30157
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30114
30158
  }
30115
30159
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
30116
30160
  const py = mapY(y);
30117
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
30161
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30118
30162
  }
30119
30163
  }
30120
30164
  if (showTickLabels) {
@@ -31589,13 +31633,13 @@ var init_MapView = __esm({
31589
31633
  shadowSize: [41, 41]
31590
31634
  });
31591
31635
  L.Marker.prototype.options.icon = defaultIcon;
31592
- const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__namespace.default;
31636
+ const { useEffect: useEffect64, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__namespace.default;
31593
31637
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31594
31638
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31595
31639
  function MapUpdater({ centerLat, centerLng, zoom }) {
31596
31640
  const map = useMap();
31597
31641
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31598
- useEffect63(() => {
31642
+ useEffect64(() => {
31599
31643
  const prev = prevRef.current;
31600
31644
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31601
31645
  map.setView([centerLat, centerLng], zoom);
@@ -31606,7 +31650,7 @@ var init_MapView = __esm({
31606
31650
  }
31607
31651
  function MapClickHandler({ onMapClick }) {
31608
31652
  const map = useMap();
31609
- useEffect63(() => {
31653
+ useEffect64(() => {
31610
31654
  if (!onMapClick) return;
31611
31655
  const handler = (e) => {
31612
31656
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -38422,6 +38466,7 @@ var init_PageHeader = __esm({
38422
38466
  className
38423
38467
  }) => {
38424
38468
  const eventBus = useEventBus();
38469
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
38425
38470
  const handleBack = () => {
38426
38471
  eventBus.emit(`UI:${backEvent}`);
38427
38472
  };
@@ -38469,15 +38514,15 @@ var init_PageHeader = __esm({
38469
38514
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
38470
38515
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3", children: [
38471
38516
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
38472
- status && /* @__PURE__ */ jsxRuntime.jsx(
38517
+ statusBadge && /* @__PURE__ */ jsxRuntime.jsx(
38473
38518
  Typography,
38474
38519
  {
38475
38520
  variant: "small",
38476
38521
  className: cn(
38477
38522
  "px-2.5 py-1 rounded-full text-xs font-medium",
38478
- statusColors2[status.variant || "default"]
38523
+ statusColors2[statusBadge.variant || "default"]
38479
38524
  ),
38480
- children: status.label
38525
+ children: statusBadge.label
38481
38526
  }
38482
38527
  )
38483
38528
  ] }),
@@ -41706,7 +41751,7 @@ function formatFieldValue2(value, fieldName) {
41706
41751
  }
41707
41752
  return String(value);
41708
41753
  }
41709
- function renderRichFieldValue(value, fieldName, fieldType) {
41754
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41710
41755
  if (value === void 0 || value === null) return "\u2014";
41711
41756
  const str = String(value);
41712
41757
  switch (fieldType) {
@@ -41773,19 +41818,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
41773
41818
  }
41774
41819
  );
41775
41820
  case "date":
41776
- case "datetime": {
41821
+ case "datetime":
41822
+ case "timestamp": {
41777
41823
  const d = new Date(str);
41778
41824
  if (!isNaN(d.getTime())) {
41779
41825
  return d.toLocaleDateString(void 0, {
41780
41826
  year: "numeric",
41781
41827
  month: "long",
41782
41828
  day: "numeric",
41783
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
41829
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
41784
41830
  });
41785
41831
  }
41786
41832
  return str;
41787
41833
  }
41834
+ case "money": {
41835
+ const n = typeof value === "number" ? value : Number(str);
41836
+ if (!isNaN(n)) {
41837
+ return new Intl.NumberFormat(void 0, {
41838
+ style: "currency",
41839
+ currency: "USD"
41840
+ }).format(n);
41841
+ }
41842
+ return str;
41843
+ }
41844
+ case "relation": {
41845
+ const match = meta?.options?.find((opt) => opt.value === str);
41846
+ return match ? match.label : str;
41847
+ }
41848
+ case "file": {
41849
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
41850
+ const file = value;
41851
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
41852
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
41853
+ return /* @__PURE__ */ jsxRuntime.jsxs(
41854
+ "a",
41855
+ {
41856
+ href: typeof file.url === "string" ? file.url : void 0,
41857
+ download: label,
41858
+ 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",
41859
+ children: [
41860
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.FileText, size: "sm", className: "text-muted-foreground" }),
41861
+ label,
41862
+ size && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
41863
+ ]
41864
+ }
41865
+ );
41866
+ }
41867
+ return str;
41868
+ }
41788
41869
  default:
41870
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
41871
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
41872
+ }
41789
41873
  return formatFieldValue2(value, fieldName);
41790
41874
  }
41791
41875
  }
@@ -41812,8 +41896,18 @@ function buildFieldTypeMap(fields) {
41812
41896
  const map = {};
41813
41897
  if (!fields) return map;
41814
41898
  for (const f3 of fields) {
41815
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
41816
- map[f3.name] = f3.type;
41899
+ if (typeof f3 === "object" && f3.type !== void 0) {
41900
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
41901
+ }
41902
+ }
41903
+ return map;
41904
+ }
41905
+ function buildFieldMetaMap(fields) {
41906
+ const map = {};
41907
+ if (!fields) return map;
41908
+ for (const f3 of fields) {
41909
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
41910
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
41817
41911
  }
41818
41912
  }
41819
41913
  return map;
@@ -41833,6 +41927,7 @@ var init_DetailPanel = __esm({
41833
41927
  init_format();
41834
41928
  init_getNestedValue();
41835
41929
  init_useEventBus();
41930
+ init_UploadDropZone();
41836
41931
  formatFieldLabel = humanizeFieldName;
41837
41932
  ReactMarkdown2 = React87.lazy(() => import('react-markdown'));
41838
41933
  DetailPanel = ({
@@ -41842,6 +41937,7 @@ var init_DetailPanel = __esm({
41842
41937
  avatar,
41843
41938
  sections: propSections,
41844
41939
  actions,
41940
+ backAction,
41845
41941
  footer,
41846
41942
  slideOver = false,
41847
41943
  className,
@@ -41850,7 +41946,8 @@ var init_DetailPanel = __esm({
41850
41946
  fieldNames,
41851
41947
  initialData,
41852
41948
  isLoading = false,
41853
- error
41949
+ error,
41950
+ relationsData
41854
41951
  }) => {
41855
41952
  const eventBus = useEventBus();
41856
41953
  const { t } = hooks.useTranslate();
@@ -41861,8 +41958,15 @@ var init_DetailPanel = __esm({
41861
41958
  };
41862
41959
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
41863
41960
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
41961
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
41864
41962
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
41865
41963
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
41964
+ const metaFor = (field) => {
41965
+ const base = fieldMetaMap[field];
41966
+ const options = relationsData?.[field];
41967
+ if (!base && !options) return void 0;
41968
+ return { ...base, options };
41969
+ };
41866
41970
  const handleActionClick = React87.useCallback(
41867
41971
  (action, data2) => {
41868
41972
  if (action.navigatesTo) {
@@ -41941,7 +42045,7 @@ var init_DetailPanel = __esm({
41941
42045
  if (value !== void 0 && value !== null) {
41942
42046
  overviewFields.push({
41943
42047
  label: labelFor(field),
41944
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42048
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41945
42049
  icon: getFieldIcon(field)
41946
42050
  });
41947
42051
  }
@@ -41957,7 +42061,7 @@ var init_DetailPanel = __esm({
41957
42061
  if (value !== void 0 && value !== null) {
41958
42062
  metricsFields.push({
41959
42063
  label: labelFor(field),
41960
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42064
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41961
42065
  icon: getFieldIcon(field)
41962
42066
  });
41963
42067
  }
@@ -41973,7 +42077,7 @@ var init_DetailPanel = __esm({
41973
42077
  if (value !== void 0 && value !== null) {
41974
42078
  timelineFields.push({
41975
42079
  label: labelFor(field),
41976
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42080
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41977
42081
  icon: getFieldIcon(field)
41978
42082
  });
41979
42083
  }
@@ -41989,7 +42093,7 @@ var init_DetailPanel = __esm({
41989
42093
  if (value !== void 0 && value !== null) {
41990
42094
  descFields.push({
41991
42095
  label: labelFor(field),
41992
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42096
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41993
42097
  icon: getFieldIcon(field)
41994
42098
  });
41995
42099
  }
@@ -42037,7 +42141,7 @@ var init_DetailPanel = __esm({
42037
42141
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
42038
42142
  allFields.push({
42039
42143
  label: labelFor(field),
42040
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42144
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42041
42145
  icon: getFieldIcon(field)
42042
42146
  });
42043
42147
  } else {
@@ -42052,34 +42156,49 @@ var init_DetailPanel = __esm({
42052
42156
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
42053
42157
  const effectiveCloseAction = closeAction ?? { event: void 0};
42054
42158
  const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
42055
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
42056
- otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
42057
- Button,
42058
- {
42059
- variant: action.variant || "secondary",
42060
- size: "sm",
42061
- action: action.navigatesTo ? void 0 : action.event,
42062
- actionPayload: { row: normalizedData },
42063
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
42064
- icon: action.icon,
42065
- "data-testid": action.event ? `action-${action.event}` : void 0,
42066
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
42067
- children: action.label
42068
- },
42069
- idx
42070
- )),
42071
- /* @__PURE__ */ jsxRuntime.jsx(
42159
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
42160
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
42072
42161
  Button,
42073
42162
  {
42074
- variant: "ghost",
42163
+ variant: backAction.variant || "ghost",
42075
42164
  size: "sm",
42076
- action: effectiveCloseAction.event,
42165
+ action: backAction.navigatesTo ? void 0 : backAction.event,
42077
42166
  actionPayload: { row: normalizedData },
42078
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
42079
- icon: LucideIcons2.X,
42080
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42167
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
42168
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
42169
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42170
+ children: backAction.label
42081
42171
  }
42082
- )
42172
+ ) }),
42173
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
42174
+ otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
42175
+ Button,
42176
+ {
42177
+ variant: action.variant || "secondary",
42178
+ size: "sm",
42179
+ action: action.navigatesTo ? void 0 : action.event,
42180
+ actionPayload: { row: normalizedData },
42181
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
42182
+ icon: action.icon,
42183
+ "data-testid": action.event ? `action-${action.event}` : void 0,
42184
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
42185
+ children: action.label
42186
+ },
42187
+ idx
42188
+ )),
42189
+ /* @__PURE__ */ jsxRuntime.jsx(
42190
+ Button,
42191
+ {
42192
+ variant: "ghost",
42193
+ size: "sm",
42194
+ action: effectiveCloseAction.event,
42195
+ actionPayload: { row: normalizedData },
42196
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
42197
+ icon: LucideIcons2.X,
42198
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42199
+ }
42200
+ )
42201
+ ] })
42083
42202
  ] }),
42084
42203
  avatar,
42085
42204
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -42394,6 +42513,10 @@ function determineInputType(field) {
42394
42513
  return "password";
42395
42514
  case "url":
42396
42515
  return "url";
42516
+ case "file":
42517
+ return "file";
42518
+ case "money":
42519
+ return "currency";
42397
42520
  case "number":
42398
42521
  case "integer":
42399
42522
  case "float":
@@ -42455,6 +42578,7 @@ var init_Form = __esm({
42455
42578
  init_Typography();
42456
42579
  init_Icon();
42457
42580
  init_RelationSelect();
42581
+ init_UploadDropZone();
42458
42582
  init_Alert();
42459
42583
  init_useEventBus();
42460
42584
  init_debug();
@@ -42497,6 +42621,7 @@ var init_Form = __esm({
42497
42621
  cancelEvent = "CANCEL",
42498
42622
  relationsData = {},
42499
42623
  relationsLoading = {},
42624
+ fieldOverrides,
42500
42625
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
42501
42626
  conditionalFields: conditionalFieldsRaw = {},
42502
42627
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -42743,7 +42868,8 @@ var init_Form = __esm({
42743
42868
  label,
42744
42869
  field.required && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
42745
42870
  ] }),
42746
- renderFieldInput(field, fieldName, inputType, currentValue, label)
42871
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
42872
+ field.hint && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
42747
42873
  ] }, fieldName);
42748
42874
  },
42749
42875
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -42759,6 +42885,7 @@ var init_Form = __esm({
42759
42885
  name: field,
42760
42886
  type: entityField.type,
42761
42887
  required: entityField.required,
42888
+ hint: entityField.description,
42762
42889
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
42763
42890
  defaultValue: entityField.default,
42764
42891
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -42771,8 +42898,18 @@ var init_Form = __esm({
42771
42898
  return { name: field, type: "string" };
42772
42899
  }
42773
42900
  return field;
42901
+ }).map((field) => {
42902
+ const fieldName = field.name || field.field;
42903
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
42904
+ if (!override) return field;
42905
+ return {
42906
+ ...field,
42907
+ ...override.label !== void 0 ? { label: override.label } : {},
42908
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
42909
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
42910
+ };
42774
42911
  });
42775
- }, [effectiveFields, resolvedEntity]);
42912
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
42776
42913
  const schemaFields = React87__namespace.default.useMemo(() => {
42777
42914
  if (normalizedFields.length === 0) return null;
42778
42915
  if (isDebugEnabled()) {
@@ -42896,6 +43033,44 @@ var init_Form = __esm({
42896
43033
  max: field.max
42897
43034
  }
42898
43035
  );
43036
+ case "currency":
43037
+ return /* @__PURE__ */ jsxRuntime.jsx(
43038
+ Input,
43039
+ {
43040
+ ...commonProps,
43041
+ type: "number",
43042
+ step: "0.01",
43043
+ icon: LucideIcons2.DollarSign,
43044
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
43045
+ onChange: (e) => handleChange(
43046
+ fieldName,
43047
+ e.target.value ? Number(e.target.value) : void 0
43048
+ ),
43049
+ min: field.min,
43050
+ max: field.max
43051
+ }
43052
+ );
43053
+ case "file":
43054
+ return /* @__PURE__ */ jsxRuntime.jsx(
43055
+ UploadDropZone,
43056
+ {
43057
+ accept: field.pattern,
43058
+ maxFiles: 1,
43059
+ disabled: isLoading,
43060
+ onFiles: (files) => {
43061
+ const f3 = files[0];
43062
+ if (!f3) return;
43063
+ const reader = new FileReader();
43064
+ reader.onload = () => handleChange(fieldName, {
43065
+ name: f3.name,
43066
+ mimeType: f3.type,
43067
+ sizeBytes: f3.size,
43068
+ url: typeof reader.result === "string" ? reader.result : ""
43069
+ });
43070
+ reader.readAsDataURL(f3);
43071
+ }
43072
+ }
43073
+ );
42899
43074
  case "date":
42900
43075
  return /* @__PURE__ */ jsxRuntime.jsx(
42901
43076
  Input,
@@ -43928,6 +44103,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
43928
44103
  var init_MediaGallery = __esm({
43929
44104
  "components/core/organisms/MediaGallery.tsx"() {
43930
44105
  "use client";
44106
+ init_getNestedValue();
43931
44107
  init_cn();
43932
44108
  init_atoms();
43933
44109
  init_Stack();
@@ -43996,9 +44172,9 @@ var init_MediaGallery = __esm({
43996
44172
  return entityData.map((record, idx) => {
43997
44173
  return {
43998
44174
  id: String(record.id ?? idx),
43999
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
44175
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
44000
44176
  alt: record.alt ? String(record.alt) : void 0,
44001
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
44177
+ thumbnail: resolveImageUrl(record.thumbnail),
44002
44178
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
44003
44179
  };
44004
44180
  });
@@ -48341,7 +48517,8 @@ __export(UISlotRenderer_exports, {
48341
48517
  SlotContentRenderer: () => SlotContentRenderer,
48342
48518
  SuspenseConfigProvider: () => SuspenseConfigProvider,
48343
48519
  UISlotComponent: () => UISlotComponent,
48344
- UISlotRenderer: () => UISlotRenderer
48520
+ UISlotRenderer: () => UISlotRenderer,
48521
+ renderPatternValue: () => renderPatternValue
48345
48522
  });
48346
48523
  function SuspenseConfigProvider({
48347
48524
  config,
@@ -48377,6 +48554,9 @@ function enrichFormFields(fields, entityDef) {
48377
48554
  type: entityField.type,
48378
48555
  required: entityField.required ?? false
48379
48556
  };
48557
+ if (entityField.description) {
48558
+ enriched.hint = entityField.description;
48559
+ }
48380
48560
  if (entityField.values && entityField.values.length > 0) {
48381
48561
  enriched.values = entityField.values;
48382
48562
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -48400,6 +48580,9 @@ function enrichFormFields(fields, entityDef) {
48400
48580
  if (entityField.required && !("required" in obj)) {
48401
48581
  enriched.required = true;
48402
48582
  }
48583
+ if (entityField.description && !obj.hint && !obj.help) {
48584
+ enriched.hint = entityField.description;
48585
+ }
48403
48586
  if (!obj.values && !obj.options) {
48404
48587
  if (entityField.values && entityField.values.length > 0) {
48405
48588
  enriched.values = entityField.values;
@@ -48415,6 +48598,32 @@ function enrichFormFields(fields, entityDef) {
48415
48598
  return field;
48416
48599
  });
48417
48600
  }
48601
+ function enrichDetailFields(fields, entityDef) {
48602
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
48603
+ const metaFor = (name) => {
48604
+ const entityField = fieldMap.get(name);
48605
+ if (!entityField) return void 0;
48606
+ const meta = { type: entityField.type };
48607
+ const values = entityField.values ?? entityField.enumValues;
48608
+ if (values && values.length > 0) meta.values = values;
48609
+ if (entityField.relation) meta.relation = entityField.relation.entity;
48610
+ return meta;
48611
+ };
48612
+ return fields.map((field) => {
48613
+ if (typeof field === "string") {
48614
+ const meta = metaFor(field);
48615
+ return meta ? { key: field, ...meta } : field;
48616
+ }
48617
+ if (field && typeof field === "object" && !Array.isArray(field) && !React87__namespace.default.isValidElement(field) && !(field instanceof Date)) {
48618
+ const obj = field;
48619
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
48620
+ if (!fieldName || obj.type) return field;
48621
+ const meta = metaFor(fieldName);
48622
+ return meta ? { ...obj, ...meta } : field;
48623
+ }
48624
+ return field;
48625
+ });
48626
+ }
48418
48627
  function renderContainedPortal(t, slot, content, onDismiss) {
48419
48628
  const slotContent = /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content, onDismiss }) });
48420
48629
  const slotId = `slot-${slot}`;
@@ -48924,6 +49133,24 @@ function isPatternConfig(value) {
48924
49133
  const record = value;
48925
49134
  return "type" in record && typeof record.type === "string" && patterns.getComponentForPattern(record.type) !== null;
48926
49135
  }
49136
+ function renderPatternValue(value) {
49137
+ if (value === null || value === void 0) return null;
49138
+ if (typeof value === "boolean" || typeof value === "function") return null;
49139
+ if (typeof value === "number") return value;
49140
+ if (typeof value === "string") return renderPatternChildren(value, () => {
49141
+ });
49142
+ if (value instanceof Date) return value.toLocaleString();
49143
+ if (React87__namespace.default.isValidElement(value)) return value;
49144
+ if (Array.isArray(value)) {
49145
+ return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React87__namespace.default.Fragment, { children: renderPatternValue(item) }, index));
49146
+ }
49147
+ if (isPatternConfig(value)) {
49148
+ const { type, ...props } = value;
49149
+ return renderPatternChildren({ type, props }, () => {
49150
+ });
49151
+ }
49152
+ return null;
49153
+ }
48927
49154
  function isPlainConfigObject(value) {
48928
49155
  if (React87__namespace.default.isValidElement(value)) return false;
48929
49156
  if (value instanceof Date) return false;
@@ -49125,9 +49352,9 @@ function SlotContentRenderer({
49125
49352
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
49126
49353
  }
49127
49354
  }
49128
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
49129
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
49130
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
49355
+ const fieldsContract = patterns.getPatternFieldsContract(content.pattern) ?? (patterns.getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
49356
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
49357
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
49131
49358
  }
49132
49359
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
49133
49360
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -49237,7 +49464,7 @@ function UISlotRenderer({
49237
49464
  }
49238
49465
  return wrapped;
49239
49466
  }
49240
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49467
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49241
49468
  var init_UISlotRenderer = __esm({
49242
49469
  "components/core/organisms/UISlotRenderer.tsx"() {
49243
49470
  "use client";
@@ -49268,12 +49495,6 @@ var init_UISlotRenderer = __esm({
49268
49495
  modal: "form",
49269
49496
  drawer: "form"
49270
49497
  };
49271
- FORM_PATTERNS = /* @__PURE__ */ new Set([
49272
- "form",
49273
- "form-section",
49274
- "inline-edit-form",
49275
- "wizard-step"
49276
- ]);
49277
49498
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
49278
49499
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
49279
49500
  "logo",