@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.
@@ -17335,7 +17335,7 @@ var init_AlgoGraphCanvas = __esm({
17335
17335
  }
17336
17336
  const badgeGeoms = [];
17337
17337
  for (const g of nodeGeoms) {
17338
- if (!g.badge) continue;
17338
+ if (!g.badge || g.badge.text === "") continue;
17339
17339
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
17340
17340
  badgeGeoms.push({
17341
17341
  cx: g.x + g.radius * 0.75,
@@ -17401,7 +17401,7 @@ var init_AlgoGraphCanvas = __esm({
17401
17401
  };
17402
17402
  }
17403
17403
  });
17404
- 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;
17404
+ 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;
17405
17405
  var init_AlgorithmCanvas = __esm({
17406
17406
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
17407
17407
  "use client";
@@ -17428,9 +17428,18 @@ var init_AlgorithmCanvas = __esm({
17428
17428
  FRAME_ACTIVE_COLOR = "#3b82f6";
17429
17429
  FRAME_RETURNING_COLOR = "#f59e0b";
17430
17430
  FRAME_DONE_COLOR = "#94a3b8";
17431
+ FRAME_RIM_COLOR = {
17432
+ active: "#1d4ed8",
17433
+ returning: "#b45309",
17434
+ done: "#64748b"
17435
+ };
17431
17436
  FRAME_LABEL_COLOR = "#ffffff";
17432
17437
  FRAME_DETAIL_COLOR = "#e2e8f0";
17433
17438
  FRAME_TWO_LINE_MIN_H = 22;
17439
+ FRAME_STRIP_H = 44;
17440
+ FRAME_GAP = 6;
17441
+ FRAME_BOTTOM_PAD = 8;
17442
+ FRAME_MIN_H = 12;
17434
17443
  BUCKET_INDEX_FILL = "#e2e8f0";
17435
17444
  BUCKET_INDEX_STROKE = "#9ca3af";
17436
17445
  BUCKET_INDEX_TEXT = "#374151";
@@ -17927,14 +17936,18 @@ var init_AlgorithmCanvas = __esm({
17927
17936
  if (frames.length > 0) {
17928
17937
  const panelYFrames = panelY.frames;
17929
17938
  const n = frames.length;
17930
- const frameH = panelHeight / n;
17939
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
17940
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
17941
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
17931
17942
  const x = 8;
17932
17943
  const w = width - 16;
17944
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
17933
17945
  frames.forEach((f3, i) => {
17934
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
17946
+ const y = bottom - i * stride - frameH;
17935
17947
  const state = f3.state ?? "active";
17936
17948
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
17937
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
17949
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
17950
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
17938
17951
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
17939
17952
  out.push({
17940
17953
  type: "text",
@@ -17942,14 +17955,14 @@ var init_AlgorithmCanvas = __esm({
17942
17955
  y: y + frameH * 0.35,
17943
17956
  text: f3.label,
17944
17957
  color: FRAME_LABEL_COLOR,
17945
- fontSize: 10,
17958
+ fontSize: 11,
17946
17959
  align: "left"
17947
17960
  });
17948
17961
  if (f3.detail) {
17949
17962
  out.push({
17950
17963
  type: "text",
17951
17964
  x: 16,
17952
- y: y + frameH * 0.7,
17965
+ y: y + frameH * 0.72,
17953
17966
  text: f3.detail,
17954
17967
  color: FRAME_DETAIL_COLOR,
17955
17968
  fontSize: 10,
@@ -23635,8 +23648,6 @@ var init_ButtonGroup = __esm({
23635
23648
  ButtonGroup.displayName = "ButtonGroup";
23636
23649
  }
23637
23650
  });
23638
-
23639
- // lib/getNestedValue.ts
23640
23651
  function getNestedValue(obj, path) {
23641
23652
  if (obj === null || obj === void 0 || !path) {
23642
23653
  return void 0;
@@ -23657,6 +23668,15 @@ function getNestedValue(obj, path) {
23657
23668
  }
23658
23669
  return value;
23659
23670
  }
23671
+ function resolveImageUrl(value) {
23672
+ if (typeof value === "string") {
23673
+ return value;
23674
+ }
23675
+ if (core.isFileValue(value)) {
23676
+ return value.url;
23677
+ }
23678
+ return void 0;
23679
+ }
23660
23680
  var init_getNestedValue = __esm({
23661
23681
  "lib/getNestedValue.ts"() {
23662
23682
  }
@@ -24361,8 +24381,8 @@ var init_CardGrid = __esm({
24361
24381
  actionPayload: { row: itemData },
24362
24382
  children: [
24363
24383
  imageField && (() => {
24364
- const imgUrl = getNestedValue(itemData, imageField);
24365
- if (!imgUrl || typeof imgUrl !== "string") return null;
24384
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
24385
+ if (!imgUrl) return null;
24366
24386
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
24367
24387
  "img",
24368
24388
  {
@@ -28392,8 +28412,8 @@ function DataGrid({
28392
28412
  ),
28393
28413
  children: [
28394
28414
  imageField && (() => {
28395
- const imgUrl = getNestedValue(itemData, imageField);
28396
- if (!imgUrl || typeof imgUrl !== "string") return null;
28415
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
28416
+ if (!imgUrl) return null;
28397
28417
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
28398
28418
  "img",
28399
28419
  {
@@ -31003,7 +31023,7 @@ var init_SearchInput = __esm({
31003
31023
  /* @__PURE__ */ jsxRuntime.jsx(
31004
31024
  Input,
31005
31025
  {
31006
- type: "text",
31026
+ type: "search",
31007
31027
  value: searchValue,
31008
31028
  onChange: handleChange,
31009
31029
  placeholder: resolvedPlaceholder,
@@ -32329,6 +32349,7 @@ var MathCanvas;
32329
32349
  var init_MathCanvas = __esm({
32330
32350
  "components/learning/molecules/MathCanvas.tsx"() {
32331
32351
  "use client";
32352
+ init_useEventBus();
32332
32353
  init_atoms();
32333
32354
  init_Stack();
32334
32355
  init_LearningCanvas();
@@ -32360,9 +32381,32 @@ var init_MathCanvas = __esm({
32360
32381
  interactive = false,
32361
32382
  animate = false,
32362
32383
  onShapeClick,
32384
+ keyMap,
32385
+ keyUpMap,
32363
32386
  isLoading,
32364
32387
  error
32365
32388
  }) => {
32389
+ const eventBus = useEventBus();
32390
+ React94.useEffect(() => {
32391
+ if (!keyMap && !keyUpMap) return;
32392
+ const onDown = (e) => {
32393
+ const ev = keyMap?.[e.code];
32394
+ if (ev) {
32395
+ eventBus.emit(`UI:${ev}`, {});
32396
+ e.preventDefault();
32397
+ }
32398
+ };
32399
+ const onUp = (e) => {
32400
+ const ev = keyUpMap?.[e.code];
32401
+ if (ev) eventBus.emit(`UI:${ev}`, {});
32402
+ };
32403
+ window.addEventListener("keydown", onDown);
32404
+ window.addEventListener("keyup", onUp);
32405
+ return () => {
32406
+ window.removeEventListener("keydown", onDown);
32407
+ window.removeEventListener("keyup", onUp);
32408
+ };
32409
+ }, [keyMap, keyUpMap, eventBus]);
32366
32410
  const derivedShapes = React94.useMemo(() => {
32367
32411
  const out = [];
32368
32412
  const margin = 24;
@@ -32375,11 +32419,11 @@ var init_MathCanvas = __esm({
32375
32419
  if (showGrid) {
32376
32420
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
32377
32421
  const px = mapX(x);
32378
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
32422
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32379
32423
  }
32380
32424
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
32381
32425
  const py = mapY(y);
32382
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
32426
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32383
32427
  }
32384
32428
  }
32385
32429
  if (showTickLabels) {
@@ -33854,13 +33898,13 @@ var init_MapView = __esm({
33854
33898
  shadowSize: [41, 41]
33855
33899
  });
33856
33900
  L.Marker.prototype.options.icon = defaultIcon;
33857
- const { useEffect: useEffect64, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__namespace.default;
33901
+ const { useEffect: useEffect65, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__namespace.default;
33858
33902
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33859
33903
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33860
33904
  function MapUpdater({ centerLat, centerLng, zoom }) {
33861
33905
  const map = useMap();
33862
33906
  const prevRef = useRef65({ centerLat, centerLng, zoom });
33863
- useEffect64(() => {
33907
+ useEffect65(() => {
33864
33908
  const prev = prevRef.current;
33865
33909
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
33866
33910
  map.setView([centerLat, centerLng], zoom);
@@ -33871,7 +33915,7 @@ var init_MapView = __esm({
33871
33915
  }
33872
33916
  function MapClickHandler({ onMapClick }) {
33873
33917
  const map = useMap();
33874
- useEffect64(() => {
33918
+ useEffect65(() => {
33875
33919
  if (!onMapClick) return;
33876
33920
  const handler = (e) => {
33877
33921
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -40278,6 +40322,7 @@ var init_PageHeader = __esm({
40278
40322
  className
40279
40323
  }) => {
40280
40324
  const eventBus = useEventBus();
40325
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
40281
40326
  const handleBack = () => {
40282
40327
  eventBus.emit(`UI:${backEvent}`);
40283
40328
  };
@@ -40325,15 +40370,15 @@ var init_PageHeader = __esm({
40325
40370
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { children: [
40326
40371
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-3", children: [
40327
40372
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
40328
- status && /* @__PURE__ */ jsxRuntime.jsx(
40373
+ statusBadge && /* @__PURE__ */ jsxRuntime.jsx(
40329
40374
  Typography,
40330
40375
  {
40331
40376
  variant: "small",
40332
40377
  className: cn(
40333
40378
  "px-2.5 py-1 rounded-full text-xs font-medium",
40334
- statusColors2[status.variant || "default"]
40379
+ statusColors2[statusBadge.variant || "default"]
40335
40380
  ),
40336
- children: status.label
40381
+ children: statusBadge.label
40337
40382
  }
40338
40383
  )
40339
40384
  ] }),
@@ -43562,7 +43607,7 @@ function formatFieldValue2(value, fieldName) {
43562
43607
  }
43563
43608
  return String(value);
43564
43609
  }
43565
- function renderRichFieldValue(value, fieldName, fieldType) {
43610
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
43566
43611
  if (value === void 0 || value === null) return "\u2014";
43567
43612
  const str = String(value);
43568
43613
  switch (fieldType) {
@@ -43629,19 +43674,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
43629
43674
  }
43630
43675
  );
43631
43676
  case "date":
43632
- case "datetime": {
43677
+ case "datetime":
43678
+ case "timestamp": {
43633
43679
  const d = new Date(str);
43634
43680
  if (!isNaN(d.getTime())) {
43635
43681
  return d.toLocaleDateString(void 0, {
43636
43682
  year: "numeric",
43637
43683
  month: "long",
43638
43684
  day: "numeric",
43639
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
43685
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
43640
43686
  });
43641
43687
  }
43642
43688
  return str;
43643
43689
  }
43690
+ case "money": {
43691
+ const n = typeof value === "number" ? value : Number(str);
43692
+ if (!isNaN(n)) {
43693
+ return new Intl.NumberFormat(void 0, {
43694
+ style: "currency",
43695
+ currency: "USD"
43696
+ }).format(n);
43697
+ }
43698
+ return str;
43699
+ }
43700
+ case "relation": {
43701
+ const match = meta?.options?.find((opt) => opt.value === str);
43702
+ return match ? match.label : str;
43703
+ }
43704
+ case "file": {
43705
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
43706
+ const file = value;
43707
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
43708
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
43709
+ return /* @__PURE__ */ jsxRuntime.jsxs(
43710
+ "a",
43711
+ {
43712
+ href: typeof file.url === "string" ? file.url : void 0,
43713
+ download: label,
43714
+ 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",
43715
+ children: [
43716
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.FileText, size: "sm", className: "text-muted-foreground" }),
43717
+ label,
43718
+ size && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
43719
+ ]
43720
+ }
43721
+ );
43722
+ }
43723
+ return str;
43724
+ }
43644
43725
  default:
43726
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
43727
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
43728
+ }
43645
43729
  return formatFieldValue2(value, fieldName);
43646
43730
  }
43647
43731
  }
@@ -43668,8 +43752,18 @@ function buildFieldTypeMap(fields) {
43668
43752
  const map = {};
43669
43753
  if (!fields) return map;
43670
43754
  for (const f3 of fields) {
43671
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
43672
- map[f3.name] = f3.type;
43755
+ if (typeof f3 === "object" && f3.type !== void 0) {
43756
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
43757
+ }
43758
+ }
43759
+ return map;
43760
+ }
43761
+ function buildFieldMetaMap(fields) {
43762
+ const map = {};
43763
+ if (!fields) return map;
43764
+ for (const f3 of fields) {
43765
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
43766
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
43673
43767
  }
43674
43768
  }
43675
43769
  return map;
@@ -43689,6 +43783,7 @@ var init_DetailPanel = __esm({
43689
43783
  init_format();
43690
43784
  init_getNestedValue();
43691
43785
  init_useEventBus();
43786
+ init_UploadDropZone();
43692
43787
  formatFieldLabel = humanizeFieldName;
43693
43788
  ReactMarkdown2 = React94.lazy(() => import('react-markdown'));
43694
43789
  DetailPanel = ({
@@ -43698,6 +43793,7 @@ var init_DetailPanel = __esm({
43698
43793
  avatar,
43699
43794
  sections: propSections,
43700
43795
  actions,
43796
+ backAction,
43701
43797
  footer,
43702
43798
  slideOver = false,
43703
43799
  className,
@@ -43706,7 +43802,8 @@ var init_DetailPanel = __esm({
43706
43802
  fieldNames,
43707
43803
  initialData,
43708
43804
  isLoading = false,
43709
- error
43805
+ error,
43806
+ relationsData
43710
43807
  }) => {
43711
43808
  const eventBus = useEventBus();
43712
43809
  const { t } = hooks.useTranslate();
@@ -43717,8 +43814,15 @@ var init_DetailPanel = __esm({
43717
43814
  };
43718
43815
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
43719
43816
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
43817
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
43720
43818
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
43721
43819
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
43820
+ const metaFor = (field) => {
43821
+ const base = fieldMetaMap[field];
43822
+ const options = relationsData?.[field];
43823
+ if (!base && !options) return void 0;
43824
+ return { ...base, options };
43825
+ };
43722
43826
  const handleActionClick = React94.useCallback(
43723
43827
  (action, data2) => {
43724
43828
  if (action.navigatesTo) {
@@ -43797,7 +43901,7 @@ var init_DetailPanel = __esm({
43797
43901
  if (value !== void 0 && value !== null) {
43798
43902
  overviewFields.push({
43799
43903
  label: labelFor(field),
43800
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43904
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43801
43905
  icon: getFieldIcon(field)
43802
43906
  });
43803
43907
  }
@@ -43813,7 +43917,7 @@ var init_DetailPanel = __esm({
43813
43917
  if (value !== void 0 && value !== null) {
43814
43918
  metricsFields.push({
43815
43919
  label: labelFor(field),
43816
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43920
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43817
43921
  icon: getFieldIcon(field)
43818
43922
  });
43819
43923
  }
@@ -43829,7 +43933,7 @@ var init_DetailPanel = __esm({
43829
43933
  if (value !== void 0 && value !== null) {
43830
43934
  timelineFields.push({
43831
43935
  label: labelFor(field),
43832
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43936
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43833
43937
  icon: getFieldIcon(field)
43834
43938
  });
43835
43939
  }
@@ -43845,7 +43949,7 @@ var init_DetailPanel = __esm({
43845
43949
  if (value !== void 0 && value !== null) {
43846
43950
  descFields.push({
43847
43951
  label: labelFor(field),
43848
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43952
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43849
43953
  icon: getFieldIcon(field)
43850
43954
  });
43851
43955
  }
@@ -43893,7 +43997,7 @@ var init_DetailPanel = __esm({
43893
43997
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
43894
43998
  allFields.push({
43895
43999
  label: labelFor(field),
43896
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
44000
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43897
44001
  icon: getFieldIcon(field)
43898
44002
  });
43899
44003
  } else {
@@ -43908,34 +44012,49 @@ var init_DetailPanel = __esm({
43908
44012
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
43909
44013
  const effectiveCloseAction = closeAction ?? { event: void 0};
43910
44014
  const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
43911
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
43912
- otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
43913
- Button,
43914
- {
43915
- variant: action.variant || "secondary",
43916
- size: "sm",
43917
- action: action.navigatesTo ? void 0 : action.event,
43918
- actionPayload: { row: normalizedData },
43919
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43920
- icon: action.icon,
43921
- "data-testid": action.event ? `action-${action.event}` : void 0,
43922
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43923
- children: action.label
43924
- },
43925
- idx
43926
- )),
43927
- /* @__PURE__ */ jsxRuntime.jsx(
44015
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
44016
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
43928
44017
  Button,
43929
44018
  {
43930
- variant: "ghost",
44019
+ variant: backAction.variant || "ghost",
43931
44020
  size: "sm",
43932
- action: effectiveCloseAction.event,
44021
+ action: backAction.navigatesTo ? void 0 : backAction.event,
43933
44022
  actionPayload: { row: normalizedData },
43934
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
43935
- icon: LucideIcons2.X,
43936
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
44023
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
44024
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
44025
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
44026
+ children: backAction.label
43937
44027
  }
43938
- )
44028
+ ) }),
44029
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
44030
+ otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
44031
+ Button,
44032
+ {
44033
+ variant: action.variant || "secondary",
44034
+ size: "sm",
44035
+ action: action.navigatesTo ? void 0 : action.event,
44036
+ actionPayload: { row: normalizedData },
44037
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
44038
+ icon: action.icon,
44039
+ "data-testid": action.event ? `action-${action.event}` : void 0,
44040
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
44041
+ children: action.label
44042
+ },
44043
+ idx
44044
+ )),
44045
+ /* @__PURE__ */ jsxRuntime.jsx(
44046
+ Button,
44047
+ {
44048
+ variant: "ghost",
44049
+ size: "sm",
44050
+ action: effectiveCloseAction.event,
44051
+ actionPayload: { row: normalizedData },
44052
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
44053
+ icon: LucideIcons2.X,
44054
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
44055
+ }
44056
+ )
44057
+ ] })
43939
44058
  ] }),
43940
44059
  avatar,
43941
44060
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -44250,6 +44369,10 @@ function determineInputType(field) {
44250
44369
  return "password";
44251
44370
  case "url":
44252
44371
  return "url";
44372
+ case "file":
44373
+ return "file";
44374
+ case "money":
44375
+ return "currency";
44253
44376
  case "number":
44254
44377
  case "integer":
44255
44378
  case "float":
@@ -44311,6 +44434,7 @@ var init_Form = __esm({
44311
44434
  init_Typography();
44312
44435
  init_Icon();
44313
44436
  init_RelationSelect();
44437
+ init_UploadDropZone();
44314
44438
  init_Alert();
44315
44439
  init_useEventBus();
44316
44440
  init_debug();
@@ -44353,6 +44477,7 @@ var init_Form = __esm({
44353
44477
  cancelEvent = "CANCEL",
44354
44478
  relationsData = {},
44355
44479
  relationsLoading = {},
44480
+ fieldOverrides,
44356
44481
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
44357
44482
  conditionalFields: conditionalFieldsRaw = {},
44358
44483
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -44599,7 +44724,8 @@ var init_Form = __esm({
44599
44724
  label,
44600
44725
  field.required && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
44601
44726
  ] }),
44602
- renderFieldInput(field, fieldName, inputType, currentValue, label)
44727
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
44728
+ field.hint && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
44603
44729
  ] }, fieldName);
44604
44730
  },
44605
44731
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -44615,6 +44741,7 @@ var init_Form = __esm({
44615
44741
  name: field,
44616
44742
  type: entityField.type,
44617
44743
  required: entityField.required,
44744
+ hint: entityField.description,
44618
44745
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
44619
44746
  defaultValue: entityField.default,
44620
44747
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -44627,8 +44754,18 @@ var init_Form = __esm({
44627
44754
  return { name: field, type: "string" };
44628
44755
  }
44629
44756
  return field;
44757
+ }).map((field) => {
44758
+ const fieldName = field.name || field.field;
44759
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
44760
+ if (!override) return field;
44761
+ return {
44762
+ ...field,
44763
+ ...override.label !== void 0 ? { label: override.label } : {},
44764
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
44765
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
44766
+ };
44630
44767
  });
44631
- }, [effectiveFields, resolvedEntity]);
44768
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
44632
44769
  const schemaFields = React94__namespace.default.useMemo(() => {
44633
44770
  if (normalizedFields.length === 0) return null;
44634
44771
  if (isDebugEnabled()) {
@@ -44752,6 +44889,44 @@ var init_Form = __esm({
44752
44889
  max: field.max
44753
44890
  }
44754
44891
  );
44892
+ case "currency":
44893
+ return /* @__PURE__ */ jsxRuntime.jsx(
44894
+ Input,
44895
+ {
44896
+ ...commonProps,
44897
+ type: "number",
44898
+ step: "0.01",
44899
+ icon: LucideIcons2.DollarSign,
44900
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
44901
+ onChange: (e) => handleChange(
44902
+ fieldName,
44903
+ e.target.value ? Number(e.target.value) : void 0
44904
+ ),
44905
+ min: field.min,
44906
+ max: field.max
44907
+ }
44908
+ );
44909
+ case "file":
44910
+ return /* @__PURE__ */ jsxRuntime.jsx(
44911
+ UploadDropZone,
44912
+ {
44913
+ accept: field.pattern,
44914
+ maxFiles: 1,
44915
+ disabled: isLoading,
44916
+ onFiles: (files) => {
44917
+ const f3 = files[0];
44918
+ if (!f3) return;
44919
+ const reader = new FileReader();
44920
+ reader.onload = () => handleChange(fieldName, {
44921
+ name: f3.name,
44922
+ mimeType: f3.type,
44923
+ sizeBytes: f3.size,
44924
+ url: typeof reader.result === "string" ? reader.result : ""
44925
+ });
44926
+ reader.readAsDataURL(f3);
44927
+ }
44928
+ }
44929
+ );
44755
44930
  case "date":
44756
44931
  return /* @__PURE__ */ jsxRuntime.jsx(
44757
44932
  Input,
@@ -45784,6 +45959,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
45784
45959
  var init_MediaGallery = __esm({
45785
45960
  "components/core/organisms/MediaGallery.tsx"() {
45786
45961
  "use client";
45962
+ init_getNestedValue();
45787
45963
  init_cn();
45788
45964
  init_atoms();
45789
45965
  init_Stack();
@@ -45852,9 +46028,9 @@ var init_MediaGallery = __esm({
45852
46028
  return entityData.map((record, idx) => {
45853
46029
  return {
45854
46030
  id: String(record.id ?? idx),
45855
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
46031
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
45856
46032
  alt: record.alt ? String(record.alt) : void 0,
45857
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
46033
+ thumbnail: resolveImageUrl(record.thumbnail),
45858
46034
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
45859
46035
  };
45860
46036
  });
@@ -50216,7 +50392,8 @@ __export(UISlotRenderer_exports, {
50216
50392
  SlotContentRenderer: () => SlotContentRenderer,
50217
50393
  SuspenseConfigProvider: () => SuspenseConfigProvider,
50218
50394
  UISlotComponent: () => UISlotComponent,
50219
- UISlotRenderer: () => UISlotRenderer
50395
+ UISlotRenderer: () => UISlotRenderer,
50396
+ renderPatternValue: () => renderPatternValue
50220
50397
  });
50221
50398
  function SuspenseConfigProvider({
50222
50399
  config,
@@ -50252,6 +50429,9 @@ function enrichFormFields(fields, entityDef) {
50252
50429
  type: entityField.type,
50253
50430
  required: entityField.required ?? false
50254
50431
  };
50432
+ if (entityField.description) {
50433
+ enriched.hint = entityField.description;
50434
+ }
50255
50435
  if (entityField.values && entityField.values.length > 0) {
50256
50436
  enriched.values = entityField.values;
50257
50437
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -50275,6 +50455,9 @@ function enrichFormFields(fields, entityDef) {
50275
50455
  if (entityField.required && !("required" in obj)) {
50276
50456
  enriched.required = true;
50277
50457
  }
50458
+ if (entityField.description && !obj.hint && !obj.help) {
50459
+ enriched.hint = entityField.description;
50460
+ }
50278
50461
  if (!obj.values && !obj.options) {
50279
50462
  if (entityField.values && entityField.values.length > 0) {
50280
50463
  enriched.values = entityField.values;
@@ -50290,6 +50473,32 @@ function enrichFormFields(fields, entityDef) {
50290
50473
  return field;
50291
50474
  });
50292
50475
  }
50476
+ function enrichDetailFields(fields, entityDef) {
50477
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
50478
+ const metaFor = (name) => {
50479
+ const entityField = fieldMap.get(name);
50480
+ if (!entityField) return void 0;
50481
+ const meta = { type: entityField.type };
50482
+ const values = entityField.values ?? entityField.enumValues;
50483
+ if (values && values.length > 0) meta.values = values;
50484
+ if (entityField.relation) meta.relation = entityField.relation.entity;
50485
+ return meta;
50486
+ };
50487
+ return fields.map((field) => {
50488
+ if (typeof field === "string") {
50489
+ const meta = metaFor(field);
50490
+ return meta ? { key: field, ...meta } : field;
50491
+ }
50492
+ if (field && typeof field === "object" && !Array.isArray(field) && !React94__namespace.default.isValidElement(field) && !(field instanceof Date)) {
50493
+ const obj = field;
50494
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
50495
+ if (!fieldName || obj.type) return field;
50496
+ const meta = metaFor(fieldName);
50497
+ return meta ? { ...obj, ...meta } : field;
50498
+ }
50499
+ return field;
50500
+ });
50501
+ }
50293
50502
  function renderContainedPortal(t, slot, content, onDismiss) {
50294
50503
  const slotContent = /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content, onDismiss }) });
50295
50504
  const slotId = `slot-${slot}`;
@@ -50799,6 +51008,24 @@ function isPatternConfig(value) {
50799
51008
  const record = value;
50800
51009
  return "type" in record && typeof record.type === "string" && patterns.getComponentForPattern(record.type) !== null;
50801
51010
  }
51011
+ function renderPatternValue(value) {
51012
+ if (value === null || value === void 0) return null;
51013
+ if (typeof value === "boolean" || typeof value === "function") return null;
51014
+ if (typeof value === "number") return value;
51015
+ if (typeof value === "string") return renderPatternChildren(value, () => {
51016
+ });
51017
+ if (value instanceof Date) return value.toLocaleString();
51018
+ if (React94__namespace.default.isValidElement(value)) return value;
51019
+ if (Array.isArray(value)) {
51020
+ return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React94__namespace.default.Fragment, { children: renderPatternValue(item) }, index));
51021
+ }
51022
+ if (isPatternConfig(value)) {
51023
+ const { type, ...props } = value;
51024
+ return renderPatternChildren({ type, props }, () => {
51025
+ });
51026
+ }
51027
+ return null;
51028
+ }
50802
51029
  function isPlainConfigObject(value) {
50803
51030
  if (React94__namespace.default.isValidElement(value)) return false;
50804
51031
  if (value instanceof Date) return false;
@@ -51000,9 +51227,9 @@ function SlotContentRenderer({
51000
51227
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
51001
51228
  }
51002
51229
  }
51003
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
51004
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
51005
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
51230
+ const fieldsContract = patterns.getPatternFieldsContract(content.pattern) ?? (patterns.getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
51231
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
51232
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
51006
51233
  }
51007
51234
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
51008
51235
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -51112,7 +51339,7 @@ function UISlotRenderer({
51112
51339
  }
51113
51340
  return wrapped;
51114
51341
  }
51115
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51342
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51116
51343
  var init_UISlotRenderer = __esm({
51117
51344
  "components/core/organisms/UISlotRenderer.tsx"() {
51118
51345
  "use client";
@@ -51143,12 +51370,6 @@ var init_UISlotRenderer = __esm({
51143
51370
  modal: "form",
51144
51371
  drawer: "form"
51145
51372
  };
51146
- FORM_PATTERNS = /* @__PURE__ */ new Set([
51147
- "form",
51148
- "form-section",
51149
- "inline-edit-form",
51150
- "wizard-step"
51151
- ]);
51152
51373
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
51153
51374
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
51154
51375
  "logo",
@@ -56294,11 +56515,11 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, routeP
56294
56515
  for (const orb of schema.orbitals) {
56295
56516
  for (const pageRef of orb.pages ?? []) {
56296
56517
  const name = typeof pageRef === "object" && pageRef !== null ? pageRef.name : void 0;
56297
- if (name === pageName) return orb.theme;
56518
+ if (name === pageName) return orb.theme ?? schema.theme;
56298
56519
  }
56299
56520
  }
56300
56521
  }
56301
- return schema.orbitals[0]?.theme;
56522
+ return schema.orbitals[0]?.theme ?? schema.theme;
56302
56523
  }, [schema, pageName]);
56303
56524
  const inner = /* @__PURE__ */ jsxRuntime.jsx(providers.VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsxRuntime.jsx(
56304
56525
  providers.EntitySchemaProvider,