@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.
@@ -13927,7 +13927,7 @@ var init_AlgoGraphCanvas = __esm({
13927
13927
  }
13928
13928
  const badgeGeoms = [];
13929
13929
  for (const g of nodeGeoms) {
13930
- if (!g.badge) continue;
13930
+ if (!g.badge || g.badge.text === "") continue;
13931
13931
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
13932
13932
  badgeGeoms.push({
13933
13933
  cx: g.x + g.radius * 0.75,
@@ -13993,7 +13993,7 @@ var init_AlgoGraphCanvas = __esm({
13993
13993
  };
13994
13994
  }
13995
13995
  });
13996
- 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;
13996
+ 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;
13997
13997
  var init_AlgorithmCanvas = __esm({
13998
13998
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
13999
13999
  "use client";
@@ -14020,9 +14020,18 @@ var init_AlgorithmCanvas = __esm({
14020
14020
  FRAME_ACTIVE_COLOR = "#3b82f6";
14021
14021
  FRAME_RETURNING_COLOR = "#f59e0b";
14022
14022
  FRAME_DONE_COLOR = "#94a3b8";
14023
+ FRAME_RIM_COLOR = {
14024
+ active: "#1d4ed8",
14025
+ returning: "#b45309",
14026
+ done: "#64748b"
14027
+ };
14023
14028
  FRAME_LABEL_COLOR = "#ffffff";
14024
14029
  FRAME_DETAIL_COLOR = "#e2e8f0";
14025
14030
  FRAME_TWO_LINE_MIN_H = 22;
14031
+ FRAME_STRIP_H = 44;
14032
+ FRAME_GAP = 6;
14033
+ FRAME_BOTTOM_PAD = 8;
14034
+ FRAME_MIN_H = 12;
14026
14035
  BUCKET_INDEX_FILL = "#e2e8f0";
14027
14036
  BUCKET_INDEX_STROKE = "#9ca3af";
14028
14037
  BUCKET_INDEX_TEXT = "#374151";
@@ -14519,14 +14528,18 @@ var init_AlgorithmCanvas = __esm({
14519
14528
  if (frames.length > 0) {
14520
14529
  const panelYFrames = panelY.frames;
14521
14530
  const n = frames.length;
14522
- const frameH = panelHeight / n;
14531
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
14532
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
14533
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
14523
14534
  const x = 8;
14524
14535
  const w = width - 16;
14536
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
14525
14537
  frames.forEach((f3, i) => {
14526
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
14538
+ const y = bottom - i * stride - frameH;
14527
14539
  const state = f3.state ?? "active";
14528
14540
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
14529
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
14541
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
14542
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
14530
14543
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
14531
14544
  out.push({
14532
14545
  type: "text",
@@ -14534,14 +14547,14 @@ var init_AlgorithmCanvas = __esm({
14534
14547
  y: y + frameH * 0.35,
14535
14548
  text: f3.label,
14536
14549
  color: FRAME_LABEL_COLOR,
14537
- fontSize: 10,
14550
+ fontSize: 11,
14538
14551
  align: "left"
14539
14552
  });
14540
14553
  if (f3.detail) {
14541
14554
  out.push({
14542
14555
  type: "text",
14543
14556
  x: 16,
14544
- y: y + frameH * 0.7,
14557
+ y: y + frameH * 0.72,
14545
14558
  text: f3.detail,
14546
14559
  color: FRAME_DETAIL_COLOR,
14547
14560
  fontSize: 10,
@@ -21004,8 +21017,6 @@ var init_ButtonGroup = __esm({
21004
21017
  ButtonGroup.displayName = "ButtonGroup";
21005
21018
  }
21006
21019
  });
21007
-
21008
- // lib/getNestedValue.ts
21009
21020
  function getNestedValue(obj, path) {
21010
21021
  if (obj === null || obj === void 0 || !path) {
21011
21022
  return void 0;
@@ -21026,6 +21037,15 @@ function getNestedValue(obj, path) {
21026
21037
  }
21027
21038
  return value;
21028
21039
  }
21040
+ function resolveImageUrl(value) {
21041
+ if (typeof value === "string") {
21042
+ return value;
21043
+ }
21044
+ if (core.isFileValue(value)) {
21045
+ return value.url;
21046
+ }
21047
+ return void 0;
21048
+ }
21029
21049
  var init_getNestedValue = __esm({
21030
21050
  "lib/getNestedValue.ts"() {
21031
21051
  }
@@ -21663,8 +21683,8 @@ var init_CardGrid = __esm({
21663
21683
  actionPayload: { row: itemData },
21664
21684
  children: [
21665
21685
  imageField && (() => {
21666
- const imgUrl = getNestedValue(itemData, imageField);
21667
- if (!imgUrl || typeof imgUrl !== "string") return null;
21686
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
21687
+ if (!imgUrl) return null;
21668
21688
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
21669
21689
  "img",
21670
21690
  {
@@ -25680,8 +25700,8 @@ function DataGrid({
25680
25700
  ),
25681
25701
  children: [
25682
25702
  imageField && (() => {
25683
- const imgUrl = getNestedValue(itemData, imageField);
25684
- if (!imgUrl || typeof imgUrl !== "string") return null;
25703
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
25704
+ if (!imgUrl) return null;
25685
25705
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
25686
25706
  "img",
25687
25707
  {
@@ -28227,7 +28247,7 @@ var init_SearchInput = __esm({
28227
28247
  /* @__PURE__ */ jsxRuntime.jsx(
28228
28248
  Input,
28229
28249
  {
28230
- type: "text",
28250
+ type: "search",
28231
28251
  value: searchValue,
28232
28252
  onChange: handleChange,
28233
28253
  placeholder: resolvedPlaceholder,
@@ -29553,6 +29573,7 @@ var MathCanvas;
29553
29573
  var init_MathCanvas = __esm({
29554
29574
  "components/learning/molecules/MathCanvas.tsx"() {
29555
29575
  "use client";
29576
+ init_useEventBus();
29556
29577
  init_atoms();
29557
29578
  init_Stack();
29558
29579
  init_LearningCanvas();
@@ -29584,9 +29605,32 @@ var init_MathCanvas = __esm({
29584
29605
  interactive = false,
29585
29606
  animate = false,
29586
29607
  onShapeClick,
29608
+ keyMap,
29609
+ keyUpMap,
29587
29610
  isLoading,
29588
29611
  error
29589
29612
  }) => {
29613
+ const eventBus = useEventBus();
29614
+ React85.useEffect(() => {
29615
+ if (!keyMap && !keyUpMap) return;
29616
+ const onDown = (e) => {
29617
+ const ev = keyMap?.[e.code];
29618
+ if (ev) {
29619
+ eventBus.emit(`UI:${ev}`, {});
29620
+ e.preventDefault();
29621
+ }
29622
+ };
29623
+ const onUp = (e) => {
29624
+ const ev = keyUpMap?.[e.code];
29625
+ if (ev) eventBus.emit(`UI:${ev}`, {});
29626
+ };
29627
+ window.addEventListener("keydown", onDown);
29628
+ window.addEventListener("keyup", onUp);
29629
+ return () => {
29630
+ window.removeEventListener("keydown", onDown);
29631
+ window.removeEventListener("keyup", onUp);
29632
+ };
29633
+ }, [keyMap, keyUpMap, eventBus]);
29590
29634
  const derivedShapes = React85.useMemo(() => {
29591
29635
  const out = [];
29592
29636
  const margin = 24;
@@ -29599,11 +29643,11 @@ var init_MathCanvas = __esm({
29599
29643
  if (showGrid) {
29600
29644
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
29601
29645
  const px = mapX(x);
29602
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
29646
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
29603
29647
  }
29604
29648
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
29605
29649
  const py = mapY(y);
29606
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
29650
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
29607
29651
  }
29608
29652
  }
29609
29653
  if (showTickLabels) {
@@ -31078,13 +31122,13 @@ var init_MapView = __esm({
31078
31122
  shadowSize: [41, 41]
31079
31123
  });
31080
31124
  L.Marker.prototype.options.icon = defaultIcon;
31081
- const { useEffect: useEffect60, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__namespace.default;
31125
+ const { useEffect: useEffect61, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__namespace.default;
31082
31126
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31083
31127
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31084
31128
  function MapUpdater({ centerLat, centerLng, zoom }) {
31085
31129
  const map = useMap();
31086
31130
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31087
- useEffect60(() => {
31131
+ useEffect61(() => {
31088
31132
  const prev = prevRef.current;
31089
31133
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31090
31134
  map.setView([centerLat, centerLng], zoom);
@@ -31095,7 +31139,7 @@ var init_MapView = __esm({
31095
31139
  }
31096
31140
  function MapClickHandler({ onMapClick }) {
31097
31141
  const map = useMap();
31098
- useEffect60(() => {
31142
+ useEffect61(() => {
31099
31143
  if (!onMapClick) return;
31100
31144
  const handler = (e) => {
31101
31145
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -37773,6 +37817,7 @@ var init_PageHeader = __esm({
37773
37817
  className
37774
37818
  }) => {
37775
37819
  const eventBus = useEventBus();
37820
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
37776
37821
  const handleBack = () => {
37777
37822
  eventBus.emit(`UI:${backEvent}`);
37778
37823
  };
@@ -37820,15 +37865,15 @@ var init_PageHeader = __esm({
37820
37865
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
37821
37866
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3", children: [
37822
37867
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
37823
- status && /* @__PURE__ */ jsxRuntime.jsx(
37868
+ statusBadge && /* @__PURE__ */ jsxRuntime.jsx(
37824
37869
  Typography,
37825
37870
  {
37826
37871
  variant: "small",
37827
37872
  className: cn(
37828
37873
  "px-2.5 py-1 rounded-full text-xs font-medium",
37829
- statusColors2[status.variant || "default"]
37874
+ statusColors2[statusBadge.variant || "default"]
37830
37875
  ),
37831
- children: status.label
37876
+ children: statusBadge.label
37832
37877
  }
37833
37878
  )
37834
37879
  ] }),
@@ -41057,7 +41102,7 @@ function formatFieldValue2(value, fieldName) {
41057
41102
  }
41058
41103
  return String(value);
41059
41104
  }
41060
- function renderRichFieldValue(value, fieldName, fieldType) {
41105
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41061
41106
  if (value === void 0 || value === null) return "\u2014";
41062
41107
  const str = String(value);
41063
41108
  switch (fieldType) {
@@ -41124,19 +41169,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
41124
41169
  }
41125
41170
  );
41126
41171
  case "date":
41127
- case "datetime": {
41172
+ case "datetime":
41173
+ case "timestamp": {
41128
41174
  const d = new Date(str);
41129
41175
  if (!isNaN(d.getTime())) {
41130
41176
  return d.toLocaleDateString(void 0, {
41131
41177
  year: "numeric",
41132
41178
  month: "long",
41133
41179
  day: "numeric",
41134
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
41180
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
41135
41181
  });
41136
41182
  }
41137
41183
  return str;
41138
41184
  }
41185
+ case "money": {
41186
+ const n = typeof value === "number" ? value : Number(str);
41187
+ if (!isNaN(n)) {
41188
+ return new Intl.NumberFormat(void 0, {
41189
+ style: "currency",
41190
+ currency: "USD"
41191
+ }).format(n);
41192
+ }
41193
+ return str;
41194
+ }
41195
+ case "relation": {
41196
+ const match = meta?.options?.find((opt) => opt.value === str);
41197
+ return match ? match.label : str;
41198
+ }
41199
+ case "file": {
41200
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
41201
+ const file = value;
41202
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
41203
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
41204
+ return /* @__PURE__ */ jsxRuntime.jsxs(
41205
+ "a",
41206
+ {
41207
+ href: typeof file.url === "string" ? file.url : void 0,
41208
+ download: label,
41209
+ 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",
41210
+ children: [
41211
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.FileText, size: "sm", className: "text-muted-foreground" }),
41212
+ label,
41213
+ size && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
41214
+ ]
41215
+ }
41216
+ );
41217
+ }
41218
+ return str;
41219
+ }
41139
41220
  default:
41221
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
41222
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
41223
+ }
41140
41224
  return formatFieldValue2(value, fieldName);
41141
41225
  }
41142
41226
  }
@@ -41163,8 +41247,18 @@ function buildFieldTypeMap(fields) {
41163
41247
  const map = {};
41164
41248
  if (!fields) return map;
41165
41249
  for (const f3 of fields) {
41166
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
41167
- map[f3.name] = f3.type;
41250
+ if (typeof f3 === "object" && f3.type !== void 0) {
41251
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
41252
+ }
41253
+ }
41254
+ return map;
41255
+ }
41256
+ function buildFieldMetaMap(fields) {
41257
+ const map = {};
41258
+ if (!fields) return map;
41259
+ for (const f3 of fields) {
41260
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
41261
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
41168
41262
  }
41169
41263
  }
41170
41264
  return map;
@@ -41184,6 +41278,7 @@ var init_DetailPanel = __esm({
41184
41278
  init_format();
41185
41279
  init_getNestedValue();
41186
41280
  init_useEventBus();
41281
+ init_UploadDropZone();
41187
41282
  formatFieldLabel = humanizeFieldName;
41188
41283
  ReactMarkdown2 = React85.lazy(() => import('react-markdown'));
41189
41284
  DetailPanel = ({
@@ -41193,6 +41288,7 @@ var init_DetailPanel = __esm({
41193
41288
  avatar,
41194
41289
  sections: propSections,
41195
41290
  actions,
41291
+ backAction,
41196
41292
  footer,
41197
41293
  slideOver = false,
41198
41294
  className,
@@ -41201,7 +41297,8 @@ var init_DetailPanel = __esm({
41201
41297
  fieldNames,
41202
41298
  initialData,
41203
41299
  isLoading = false,
41204
- error
41300
+ error,
41301
+ relationsData
41205
41302
  }) => {
41206
41303
  const eventBus = useEventBus();
41207
41304
  const { t } = hooks.useTranslate();
@@ -41212,8 +41309,15 @@ var init_DetailPanel = __esm({
41212
41309
  };
41213
41310
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
41214
41311
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
41312
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
41215
41313
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
41216
41314
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
41315
+ const metaFor = (field) => {
41316
+ const base = fieldMetaMap[field];
41317
+ const options = relationsData?.[field];
41318
+ if (!base && !options) return void 0;
41319
+ return { ...base, options };
41320
+ };
41217
41321
  const handleActionClick = React85.useCallback(
41218
41322
  (action, data2) => {
41219
41323
  if (action.navigatesTo) {
@@ -41292,7 +41396,7 @@ var init_DetailPanel = __esm({
41292
41396
  if (value !== void 0 && value !== null) {
41293
41397
  overviewFields.push({
41294
41398
  label: labelFor(field),
41295
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41399
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41296
41400
  icon: getFieldIcon(field)
41297
41401
  });
41298
41402
  }
@@ -41308,7 +41412,7 @@ var init_DetailPanel = __esm({
41308
41412
  if (value !== void 0 && value !== null) {
41309
41413
  metricsFields.push({
41310
41414
  label: labelFor(field),
41311
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41415
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41312
41416
  icon: getFieldIcon(field)
41313
41417
  });
41314
41418
  }
@@ -41324,7 +41428,7 @@ var init_DetailPanel = __esm({
41324
41428
  if (value !== void 0 && value !== null) {
41325
41429
  timelineFields.push({
41326
41430
  label: labelFor(field),
41327
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41431
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41328
41432
  icon: getFieldIcon(field)
41329
41433
  });
41330
41434
  }
@@ -41340,7 +41444,7 @@ var init_DetailPanel = __esm({
41340
41444
  if (value !== void 0 && value !== null) {
41341
41445
  descFields.push({
41342
41446
  label: labelFor(field),
41343
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41447
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41344
41448
  icon: getFieldIcon(field)
41345
41449
  });
41346
41450
  }
@@ -41388,7 +41492,7 @@ var init_DetailPanel = __esm({
41388
41492
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
41389
41493
  allFields.push({
41390
41494
  label: labelFor(field),
41391
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41495
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41392
41496
  icon: getFieldIcon(field)
41393
41497
  });
41394
41498
  } else {
@@ -41403,34 +41507,49 @@ var init_DetailPanel = __esm({
41403
41507
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
41404
41508
  const effectiveCloseAction = closeAction ?? { event: void 0};
41405
41509
  const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
41406
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41407
- otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
41408
- Button,
41409
- {
41410
- variant: action.variant || "secondary",
41411
- size: "sm",
41412
- action: action.navigatesTo ? void 0 : action.event,
41413
- actionPayload: { row: normalizedData },
41414
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
41415
- icon: action.icon,
41416
- "data-testid": action.event ? `action-${action.event}` : void 0,
41417
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
41418
- children: action.label
41419
- },
41420
- idx
41421
- )),
41422
- /* @__PURE__ */ jsxRuntime.jsx(
41510
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
41511
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
41423
41512
  Button,
41424
41513
  {
41425
- variant: "ghost",
41514
+ variant: backAction.variant || "ghost",
41426
41515
  size: "sm",
41427
- action: effectiveCloseAction.event,
41516
+ action: backAction.navigatesTo ? void 0 : backAction.event,
41428
41517
  actionPayload: { row: normalizedData },
41429
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
41430
- icon: LucideIcons2.X,
41431
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
41518
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
41519
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
41520
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
41521
+ children: backAction.label
41432
41522
  }
41433
- )
41523
+ ) }),
41524
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41525
+ otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
41526
+ Button,
41527
+ {
41528
+ variant: action.variant || "secondary",
41529
+ size: "sm",
41530
+ action: action.navigatesTo ? void 0 : action.event,
41531
+ actionPayload: { row: normalizedData },
41532
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
41533
+ icon: action.icon,
41534
+ "data-testid": action.event ? `action-${action.event}` : void 0,
41535
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
41536
+ children: action.label
41537
+ },
41538
+ idx
41539
+ )),
41540
+ /* @__PURE__ */ jsxRuntime.jsx(
41541
+ Button,
41542
+ {
41543
+ variant: "ghost",
41544
+ size: "sm",
41545
+ action: effectiveCloseAction.event,
41546
+ actionPayload: { row: normalizedData },
41547
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
41548
+ icon: LucideIcons2.X,
41549
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
41550
+ }
41551
+ )
41552
+ ] })
41434
41553
  ] }),
41435
41554
  avatar,
41436
41555
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -41745,6 +41864,10 @@ function determineInputType(field) {
41745
41864
  return "password";
41746
41865
  case "url":
41747
41866
  return "url";
41867
+ case "file":
41868
+ return "file";
41869
+ case "money":
41870
+ return "currency";
41748
41871
  case "number":
41749
41872
  case "integer":
41750
41873
  case "float":
@@ -41806,6 +41929,7 @@ var init_Form = __esm({
41806
41929
  init_Typography();
41807
41930
  init_Icon();
41808
41931
  init_RelationSelect();
41932
+ init_UploadDropZone();
41809
41933
  init_Alert();
41810
41934
  init_useEventBus();
41811
41935
  init_debug();
@@ -41848,6 +41972,7 @@ var init_Form = __esm({
41848
41972
  cancelEvent = "CANCEL",
41849
41973
  relationsData = {},
41850
41974
  relationsLoading = {},
41975
+ fieldOverrides,
41851
41976
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
41852
41977
  conditionalFields: conditionalFieldsRaw = {},
41853
41978
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -42094,7 +42219,8 @@ var init_Form = __esm({
42094
42219
  label,
42095
42220
  field.required && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
42096
42221
  ] }),
42097
- renderFieldInput(field, fieldName, inputType, currentValue, label)
42222
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
42223
+ field.hint && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
42098
42224
  ] }, fieldName);
42099
42225
  },
42100
42226
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -42110,6 +42236,7 @@ var init_Form = __esm({
42110
42236
  name: field,
42111
42237
  type: entityField.type,
42112
42238
  required: entityField.required,
42239
+ hint: entityField.description,
42113
42240
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
42114
42241
  defaultValue: entityField.default,
42115
42242
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -42122,8 +42249,18 @@ var init_Form = __esm({
42122
42249
  return { name: field, type: "string" };
42123
42250
  }
42124
42251
  return field;
42252
+ }).map((field) => {
42253
+ const fieldName = field.name || field.field;
42254
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
42255
+ if (!override) return field;
42256
+ return {
42257
+ ...field,
42258
+ ...override.label !== void 0 ? { label: override.label } : {},
42259
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
42260
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
42261
+ };
42125
42262
  });
42126
- }, [effectiveFields, resolvedEntity]);
42263
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
42127
42264
  const schemaFields = React85__namespace.default.useMemo(() => {
42128
42265
  if (normalizedFields.length === 0) return null;
42129
42266
  if (isDebugEnabled()) {
@@ -42247,6 +42384,44 @@ var init_Form = __esm({
42247
42384
  max: field.max
42248
42385
  }
42249
42386
  );
42387
+ case "currency":
42388
+ return /* @__PURE__ */ jsxRuntime.jsx(
42389
+ Input,
42390
+ {
42391
+ ...commonProps,
42392
+ type: "number",
42393
+ step: "0.01",
42394
+ icon: LucideIcons2.DollarSign,
42395
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
42396
+ onChange: (e) => handleChange(
42397
+ fieldName,
42398
+ e.target.value ? Number(e.target.value) : void 0
42399
+ ),
42400
+ min: field.min,
42401
+ max: field.max
42402
+ }
42403
+ );
42404
+ case "file":
42405
+ return /* @__PURE__ */ jsxRuntime.jsx(
42406
+ UploadDropZone,
42407
+ {
42408
+ accept: field.pattern,
42409
+ maxFiles: 1,
42410
+ disabled: isLoading,
42411
+ onFiles: (files) => {
42412
+ const f3 = files[0];
42413
+ if (!f3) return;
42414
+ const reader = new FileReader();
42415
+ reader.onload = () => handleChange(fieldName, {
42416
+ name: f3.name,
42417
+ mimeType: f3.type,
42418
+ sizeBytes: f3.size,
42419
+ url: typeof reader.result === "string" ? reader.result : ""
42420
+ });
42421
+ reader.readAsDataURL(f3);
42422
+ }
42423
+ }
42424
+ );
42250
42425
  case "date":
42251
42426
  return /* @__PURE__ */ jsxRuntime.jsx(
42252
42427
  Input,
@@ -43279,6 +43454,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
43279
43454
  var init_MediaGallery = __esm({
43280
43455
  "components/core/organisms/MediaGallery.tsx"() {
43281
43456
  "use client";
43457
+ init_getNestedValue();
43282
43458
  init_cn();
43283
43459
  init_atoms();
43284
43460
  init_Stack();
@@ -43347,9 +43523,9 @@ var init_MediaGallery = __esm({
43347
43523
  return entityData.map((record, idx) => {
43348
43524
  return {
43349
43525
  id: String(record.id ?? idx),
43350
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
43526
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
43351
43527
  alt: record.alt ? String(record.alt) : void 0,
43352
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
43528
+ thumbnail: resolveImageUrl(record.thumbnail),
43353
43529
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
43354
43530
  };
43355
43531
  });
@@ -47711,7 +47887,8 @@ __export(UISlotRenderer_exports, {
47711
47887
  SlotContentRenderer: () => SlotContentRenderer,
47712
47888
  SuspenseConfigProvider: () => SuspenseConfigProvider,
47713
47889
  UISlotComponent: () => UISlotComponent,
47714
- UISlotRenderer: () => UISlotRenderer
47890
+ UISlotRenderer: () => UISlotRenderer,
47891
+ renderPatternValue: () => renderPatternValue
47715
47892
  });
47716
47893
  function SuspenseConfigProvider({
47717
47894
  config,
@@ -47747,6 +47924,9 @@ function enrichFormFields(fields, entityDef) {
47747
47924
  type: entityField.type,
47748
47925
  required: entityField.required ?? false
47749
47926
  };
47927
+ if (entityField.description) {
47928
+ enriched.hint = entityField.description;
47929
+ }
47750
47930
  if (entityField.values && entityField.values.length > 0) {
47751
47931
  enriched.values = entityField.values;
47752
47932
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -47770,6 +47950,9 @@ function enrichFormFields(fields, entityDef) {
47770
47950
  if (entityField.required && !("required" in obj)) {
47771
47951
  enriched.required = true;
47772
47952
  }
47953
+ if (entityField.description && !obj.hint && !obj.help) {
47954
+ enriched.hint = entityField.description;
47955
+ }
47773
47956
  if (!obj.values && !obj.options) {
47774
47957
  if (entityField.values && entityField.values.length > 0) {
47775
47958
  enriched.values = entityField.values;
@@ -47785,6 +47968,32 @@ function enrichFormFields(fields, entityDef) {
47785
47968
  return field;
47786
47969
  });
47787
47970
  }
47971
+ function enrichDetailFields(fields, entityDef) {
47972
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
47973
+ const metaFor = (name) => {
47974
+ const entityField = fieldMap.get(name);
47975
+ if (!entityField) return void 0;
47976
+ const meta = { type: entityField.type };
47977
+ const values = entityField.values ?? entityField.enumValues;
47978
+ if (values && values.length > 0) meta.values = values;
47979
+ if (entityField.relation) meta.relation = entityField.relation.entity;
47980
+ return meta;
47981
+ };
47982
+ return fields.map((field) => {
47983
+ if (typeof field === "string") {
47984
+ const meta = metaFor(field);
47985
+ return meta ? { key: field, ...meta } : field;
47986
+ }
47987
+ if (field && typeof field === "object" && !Array.isArray(field) && !React85__namespace.default.isValidElement(field) && !(field instanceof Date)) {
47988
+ const obj = field;
47989
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
47990
+ if (!fieldName || obj.type) return field;
47991
+ const meta = metaFor(fieldName);
47992
+ return meta ? { ...obj, ...meta } : field;
47993
+ }
47994
+ return field;
47995
+ });
47996
+ }
47788
47997
  function renderContainedPortal(t, slot, content, onDismiss) {
47789
47998
  const slotContent = /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content, onDismiss }) });
47790
47999
  const slotId = `slot-${slot}`;
@@ -48294,6 +48503,24 @@ function isPatternConfig(value) {
48294
48503
  const record = value;
48295
48504
  return "type" in record && typeof record.type === "string" && patterns.getComponentForPattern(record.type) !== null;
48296
48505
  }
48506
+ function renderPatternValue(value) {
48507
+ if (value === null || value === void 0) return null;
48508
+ if (typeof value === "boolean" || typeof value === "function") return null;
48509
+ if (typeof value === "number") return value;
48510
+ if (typeof value === "string") return renderPatternChildren(value, () => {
48511
+ });
48512
+ if (value instanceof Date) return value.toLocaleString();
48513
+ if (React85__namespace.default.isValidElement(value)) return value;
48514
+ if (Array.isArray(value)) {
48515
+ return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React85__namespace.default.Fragment, { children: renderPatternValue(item) }, index));
48516
+ }
48517
+ if (isPatternConfig(value)) {
48518
+ const { type, ...props } = value;
48519
+ return renderPatternChildren({ type, props }, () => {
48520
+ });
48521
+ }
48522
+ return null;
48523
+ }
48297
48524
  function isPlainConfigObject(value) {
48298
48525
  if (React85__namespace.default.isValidElement(value)) return false;
48299
48526
  if (value instanceof Date) return false;
@@ -48495,9 +48722,9 @@ function SlotContentRenderer({
48495
48722
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
48496
48723
  }
48497
48724
  }
48498
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
48499
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
48500
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
48725
+ const fieldsContract = patterns.getPatternFieldsContract(content.pattern) ?? (patterns.getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
48726
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
48727
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
48501
48728
  }
48502
48729
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
48503
48730
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -48607,7 +48834,7 @@ function UISlotRenderer({
48607
48834
  }
48608
48835
  return wrapped;
48609
48836
  }
48610
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
48837
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
48611
48838
  var init_UISlotRenderer = __esm({
48612
48839
  "components/core/organisms/UISlotRenderer.tsx"() {
48613
48840
  "use client";
@@ -48638,12 +48865,6 @@ var init_UISlotRenderer = __esm({
48638
48865
  modal: "form",
48639
48866
  drawer: "form"
48640
48867
  };
48641
- FORM_PATTERNS = /* @__PURE__ */ new Set([
48642
- "form",
48643
- "form-section",
48644
- "inline-edit-form",
48645
- "wizard-step"
48646
- ]);
48647
48868
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
48648
48869
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
48649
48870
  "logo",
@@ -50516,11 +50737,11 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, routeP
50516
50737
  for (const orb of schema.orbitals) {
50517
50738
  for (const pageRef of orb.pages ?? []) {
50518
50739
  const name = typeof pageRef === "object" && pageRef !== null ? pageRef.name : void 0;
50519
- if (name === pageName) return orb.theme;
50740
+ if (name === pageName) return orb.theme ?? schema.theme;
50520
50741
  }
50521
50742
  }
50522
50743
  }
50523
- return schema.orbitals[0]?.theme;
50744
+ return schema.orbitals[0]?.theme ?? schema.theme;
50524
50745
  }, [schema, pageName]);
50525
50746
  const inner = /* @__PURE__ */ jsxRuntime.jsx(providers.VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsxRuntime.jsx(
50526
50747
  providers.EntitySchemaProvider,