@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.
@@ -9214,7 +9214,7 @@ var init_AlgoGraphCanvas = __esm({
9214
9214
  }
9215
9215
  const badgeGeoms = [];
9216
9216
  for (const g of nodeGeoms) {
9217
- if (!g.badge) continue;
9217
+ if (!g.badge || g.badge.text === "") continue;
9218
9218
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
9219
9219
  badgeGeoms.push({
9220
9220
  cx: g.x + g.radius * 0.75,
@@ -9280,7 +9280,7 @@ var init_AlgoGraphCanvas = __esm({
9280
9280
  };
9281
9281
  }
9282
9282
  });
9283
- 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; exports.AlgorithmCanvas = void 0;
9283
+ 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; exports.AlgorithmCanvas = void 0;
9284
9284
  var init_AlgorithmCanvas = __esm({
9285
9285
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
9286
9286
  "use client";
@@ -9307,9 +9307,18 @@ var init_AlgorithmCanvas = __esm({
9307
9307
  FRAME_ACTIVE_COLOR = "#3b82f6";
9308
9308
  FRAME_RETURNING_COLOR = "#f59e0b";
9309
9309
  FRAME_DONE_COLOR = "#94a3b8";
9310
+ FRAME_RIM_COLOR = {
9311
+ active: "#1d4ed8",
9312
+ returning: "#b45309",
9313
+ done: "#64748b"
9314
+ };
9310
9315
  FRAME_LABEL_COLOR = "#ffffff";
9311
9316
  FRAME_DETAIL_COLOR = "#e2e8f0";
9312
9317
  FRAME_TWO_LINE_MIN_H = 22;
9318
+ FRAME_STRIP_H = 44;
9319
+ FRAME_GAP = 6;
9320
+ FRAME_BOTTOM_PAD = 8;
9321
+ FRAME_MIN_H = 12;
9313
9322
  BUCKET_INDEX_FILL = "#e2e8f0";
9314
9323
  BUCKET_INDEX_STROKE = "#9ca3af";
9315
9324
  BUCKET_INDEX_TEXT = "#374151";
@@ -9806,14 +9815,18 @@ var init_AlgorithmCanvas = __esm({
9806
9815
  if (frames.length > 0) {
9807
9816
  const panelYFrames = panelY.frames;
9808
9817
  const n = frames.length;
9809
- const frameH = panelHeight / n;
9818
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
9819
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
9820
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
9810
9821
  const x = 8;
9811
9822
  const w = width - 16;
9823
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
9812
9824
  frames.forEach((f3, i) => {
9813
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
9825
+ const y = bottom - i * stride - frameH;
9814
9826
  const state = f3.state ?? "active";
9815
9827
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
9816
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
9828
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
9829
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
9817
9830
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
9818
9831
  out.push({
9819
9832
  type: "text",
@@ -9821,14 +9834,14 @@ var init_AlgorithmCanvas = __esm({
9821
9834
  y: y + frameH * 0.35,
9822
9835
  text: f3.label,
9823
9836
  color: FRAME_LABEL_COLOR,
9824
- fontSize: 10,
9837
+ fontSize: 11,
9825
9838
  align: "left"
9826
9839
  });
9827
9840
  if (f3.detail) {
9828
9841
  out.push({
9829
9842
  type: "text",
9830
9843
  x: 16,
9831
- y: y + frameH * 0.7,
9844
+ y: y + frameH * 0.72,
9832
9845
  text: f3.detail,
9833
9846
  color: FRAME_DETAIL_COLOR,
9834
9847
  fontSize: 10,
@@ -16344,8 +16357,6 @@ var init_ButtonGroup = __esm({
16344
16357
  exports.ButtonGroup.displayName = "ButtonGroup";
16345
16358
  }
16346
16359
  });
16347
-
16348
- // lib/getNestedValue.ts
16349
16360
  function getNestedValue(obj, path) {
16350
16361
  if (obj === null || obj === void 0 || !path) {
16351
16362
  return void 0;
@@ -16366,6 +16377,15 @@ function getNestedValue(obj, path) {
16366
16377
  }
16367
16378
  return value;
16368
16379
  }
16380
+ function resolveImageUrl(value) {
16381
+ if (typeof value === "string") {
16382
+ return value;
16383
+ }
16384
+ if (core.isFileValue(value)) {
16385
+ return value.url;
16386
+ }
16387
+ return void 0;
16388
+ }
16369
16389
  var init_getNestedValue = __esm({
16370
16390
  "lib/getNestedValue.ts"() {
16371
16391
  }
@@ -19408,8 +19428,8 @@ var init_CardGrid = __esm({
19408
19428
  actionPayload: { row: itemData },
19409
19429
  children: [
19410
19430
  imageField && (() => {
19411
- const imgUrl = getNestedValue(itemData, imageField);
19412
- if (!imgUrl || typeof imgUrl !== "string") return null;
19431
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
19432
+ if (!imgUrl) return null;
19413
19433
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
19414
19434
  "img",
19415
19435
  {
@@ -23812,8 +23832,8 @@ function DataGrid({
23812
23832
  ),
23813
23833
  children: [
23814
23834
  imageField && (() => {
23815
- const imgUrl = getNestedValue(itemData, imageField);
23816
- if (!imgUrl || typeof imgUrl !== "string") return null;
23835
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
23836
+ if (!imgUrl) return null;
23817
23837
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
23818
23838
  "img",
23819
23839
  {
@@ -27339,7 +27359,7 @@ var init_SearchInput = __esm({
27339
27359
  /* @__PURE__ */ jsxRuntime.jsx(
27340
27360
  exports.Input,
27341
27361
  {
27342
- type: "text",
27362
+ type: "search",
27343
27363
  value: searchValue,
27344
27364
  onChange: handleChange,
27345
27365
  placeholder: resolvedPlaceholder,
@@ -31099,6 +31119,7 @@ exports.MathCanvas = void 0;
31099
31119
  var init_MathCanvas = __esm({
31100
31120
  "components/learning/molecules/MathCanvas.tsx"() {
31101
31121
  "use client";
31122
+ init_useEventBus();
31102
31123
  init_atoms();
31103
31124
  init_Stack();
31104
31125
  init_LearningCanvas();
@@ -31130,9 +31151,32 @@ var init_MathCanvas = __esm({
31130
31151
  interactive = false,
31131
31152
  animate = false,
31132
31153
  onShapeClick,
31154
+ keyMap,
31155
+ keyUpMap,
31133
31156
  isLoading,
31134
31157
  error
31135
31158
  }) => {
31159
+ const eventBus = useEventBus();
31160
+ React77.useEffect(() => {
31161
+ if (!keyMap && !keyUpMap) return;
31162
+ const onDown = (e) => {
31163
+ const ev = keyMap?.[e.code];
31164
+ if (ev) {
31165
+ eventBus.emit(`UI:${ev}`, {});
31166
+ e.preventDefault();
31167
+ }
31168
+ };
31169
+ const onUp = (e) => {
31170
+ const ev = keyUpMap?.[e.code];
31171
+ if (ev) eventBus.emit(`UI:${ev}`, {});
31172
+ };
31173
+ window.addEventListener("keydown", onDown);
31174
+ window.addEventListener("keyup", onUp);
31175
+ return () => {
31176
+ window.removeEventListener("keydown", onDown);
31177
+ window.removeEventListener("keyup", onUp);
31178
+ };
31179
+ }, [keyMap, keyUpMap, eventBus]);
31136
31180
  const derivedShapes = React77.useMemo(() => {
31137
31181
  const out = [];
31138
31182
  const margin = 24;
@@ -31145,11 +31189,11 @@ var init_MathCanvas = __esm({
31145
31189
  if (showGrid) {
31146
31190
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
31147
31191
  const px = mapX(x);
31148
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
31192
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
31149
31193
  }
31150
31194
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
31151
31195
  const py = mapY(y);
31152
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
31196
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
31153
31197
  }
31154
31198
  }
31155
31199
  if (showTickLabels) {
@@ -32624,13 +32668,13 @@ var init_MapView = __esm({
32624
32668
  shadowSize: [41, 41]
32625
32669
  });
32626
32670
  L.Marker.prototype.options.icon = defaultIcon;
32627
- const { useEffect: useEffect66, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__namespace.default;
32671
+ const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__namespace.default;
32628
32672
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32629
32673
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32630
32674
  function MapUpdater({ centerLat, centerLng, zoom }) {
32631
32675
  const map = useMap();
32632
32676
  const prevRef = useRef65({ centerLat, centerLng, zoom });
32633
- useEffect66(() => {
32677
+ useEffect67(() => {
32634
32678
  const prev = prevRef.current;
32635
32679
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
32636
32680
  map.setView([centerLat, centerLng], zoom);
@@ -32641,7 +32685,7 @@ var init_MapView = __esm({
32641
32685
  }
32642
32686
  function MapClickHandler({ onMapClick }) {
32643
32687
  const map = useMap();
32644
- useEffect66(() => {
32688
+ useEffect67(() => {
32645
32689
  if (!onMapClick) return;
32646
32690
  const handler = (e) => {
32647
32691
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -39457,6 +39501,7 @@ var init_PageHeader = __esm({
39457
39501
  className
39458
39502
  }) => {
39459
39503
  const eventBus = useEventBus();
39504
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
39460
39505
  const handleBack = () => {
39461
39506
  eventBus.emit(`UI:${backEvent}`);
39462
39507
  };
@@ -39504,15 +39549,15 @@ var init_PageHeader = __esm({
39504
39549
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { children: [
39505
39550
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center gap-3", children: [
39506
39551
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
39507
- status && /* @__PURE__ */ jsxRuntime.jsx(
39552
+ statusBadge && /* @__PURE__ */ jsxRuntime.jsx(
39508
39553
  exports.Typography,
39509
39554
  {
39510
39555
  variant: "small",
39511
39556
  className: cn(
39512
39557
  "px-2.5 py-1 rounded-full text-xs font-medium",
39513
- statusColors2[status.variant || "default"]
39558
+ statusColors2[statusBadge.variant || "default"]
39514
39559
  ),
39515
- children: status.label
39560
+ children: statusBadge.label
39516
39561
  }
39517
39562
  )
39518
39563
  ] }),
@@ -42965,7 +43010,7 @@ function formatFieldValue2(value, fieldName) {
42965
43010
  }
42966
43011
  return String(value);
42967
43012
  }
42968
- function renderRichFieldValue(value, fieldName, fieldType) {
43013
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
42969
43014
  if (value === void 0 || value === null) return "\u2014";
42970
43015
  const str2 = String(value);
42971
43016
  switch (fieldType) {
@@ -43032,19 +43077,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
43032
43077
  }
43033
43078
  );
43034
43079
  case "date":
43035
- case "datetime": {
43080
+ case "datetime":
43081
+ case "timestamp": {
43036
43082
  const d = new Date(str2);
43037
43083
  if (!isNaN(d.getTime())) {
43038
43084
  return d.toLocaleDateString(void 0, {
43039
43085
  year: "numeric",
43040
43086
  month: "long",
43041
43087
  day: "numeric",
43042
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
43088
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
43043
43089
  });
43044
43090
  }
43045
43091
  return str2;
43046
43092
  }
43093
+ case "money": {
43094
+ const n = typeof value === "number" ? value : Number(str2);
43095
+ if (!isNaN(n)) {
43096
+ return new Intl.NumberFormat(void 0, {
43097
+ style: "currency",
43098
+ currency: "USD"
43099
+ }).format(n);
43100
+ }
43101
+ return str2;
43102
+ }
43103
+ case "relation": {
43104
+ const match = meta?.options?.find((opt) => opt.value === str2);
43105
+ return match ? match.label : str2;
43106
+ }
43107
+ case "file": {
43108
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
43109
+ const file = value;
43110
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
43111
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
43112
+ return /* @__PURE__ */ jsxRuntime.jsxs(
43113
+ "a",
43114
+ {
43115
+ href: typeof file.url === "string" ? file.url : void 0,
43116
+ download: label,
43117
+ 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",
43118
+ children: [
43119
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.FileText, size: "sm", className: "text-muted-foreground" }),
43120
+ label,
43121
+ size && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { as: "span", variant: "caption", color: "muted", children: size })
43122
+ ]
43123
+ }
43124
+ );
43125
+ }
43126
+ return str2;
43127
+ }
43047
43128
  default:
43129
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str2)) {
43130
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: getBadgeVariant(fieldName, str2), children: humanizeEnumValue(str2) });
43131
+ }
43048
43132
  return formatFieldValue2(value, fieldName);
43049
43133
  }
43050
43134
  }
@@ -43071,8 +43155,18 @@ function buildFieldTypeMap(fields) {
43071
43155
  const map = {};
43072
43156
  if (!fields) return map;
43073
43157
  for (const f3 of fields) {
43074
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
43075
- map[f3.name] = f3.type;
43158
+ if (typeof f3 === "object" && f3.type !== void 0) {
43159
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
43160
+ }
43161
+ }
43162
+ return map;
43163
+ }
43164
+ function buildFieldMetaMap(fields) {
43165
+ const map = {};
43166
+ if (!fields) return map;
43167
+ for (const f3 of fields) {
43168
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
43169
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
43076
43170
  }
43077
43171
  }
43078
43172
  return map;
@@ -43092,6 +43186,7 @@ var init_DetailPanel = __esm({
43092
43186
  init_format();
43093
43187
  init_getNestedValue();
43094
43188
  init_useEventBus();
43189
+ init_UploadDropZone();
43095
43190
  formatFieldLabel = humanizeFieldName;
43096
43191
  ReactMarkdown2 = React77.lazy(() => import('react-markdown'));
43097
43192
  exports.DetailPanel = ({
@@ -43101,6 +43196,7 @@ var init_DetailPanel = __esm({
43101
43196
  avatar,
43102
43197
  sections: propSections,
43103
43198
  actions,
43199
+ backAction,
43104
43200
  footer,
43105
43201
  slideOver = false,
43106
43202
  className,
@@ -43109,7 +43205,8 @@ var init_DetailPanel = __esm({
43109
43205
  fieldNames,
43110
43206
  initialData,
43111
43207
  isLoading = false,
43112
- error
43208
+ error,
43209
+ relationsData
43113
43210
  }) => {
43114
43211
  const eventBus = useEventBus();
43115
43212
  const { t } = hooks.useTranslate();
@@ -43120,8 +43217,15 @@ var init_DetailPanel = __esm({
43120
43217
  };
43121
43218
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
43122
43219
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
43220
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
43123
43221
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
43124
43222
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
43223
+ const metaFor = (field) => {
43224
+ const base = fieldMetaMap[field];
43225
+ const options = relationsData?.[field];
43226
+ if (!base && !options) return void 0;
43227
+ return { ...base, options };
43228
+ };
43125
43229
  const handleActionClick = React77.useCallback(
43126
43230
  (action, data2) => {
43127
43231
  if (action.navigatesTo) {
@@ -43200,7 +43304,7 @@ var init_DetailPanel = __esm({
43200
43304
  if (value !== void 0 && value !== null) {
43201
43305
  overviewFields.push({
43202
43306
  label: labelFor(field),
43203
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43307
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43204
43308
  icon: getFieldIcon(field)
43205
43309
  });
43206
43310
  }
@@ -43216,7 +43320,7 @@ var init_DetailPanel = __esm({
43216
43320
  if (value !== void 0 && value !== null) {
43217
43321
  metricsFields.push({
43218
43322
  label: labelFor(field),
43219
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43323
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43220
43324
  icon: getFieldIcon(field)
43221
43325
  });
43222
43326
  }
@@ -43232,7 +43336,7 @@ var init_DetailPanel = __esm({
43232
43336
  if (value !== void 0 && value !== null) {
43233
43337
  timelineFields.push({
43234
43338
  label: labelFor(field),
43235
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43339
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43236
43340
  icon: getFieldIcon(field)
43237
43341
  });
43238
43342
  }
@@ -43248,7 +43352,7 @@ var init_DetailPanel = __esm({
43248
43352
  if (value !== void 0 && value !== null) {
43249
43353
  descFields.push({
43250
43354
  label: labelFor(field),
43251
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43355
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43252
43356
  icon: getFieldIcon(field)
43253
43357
  });
43254
43358
  }
@@ -43296,7 +43400,7 @@ var init_DetailPanel = __esm({
43296
43400
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
43297
43401
  allFields.push({
43298
43402
  label: labelFor(field),
43299
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
43403
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43300
43404
  icon: getFieldIcon(field)
43301
43405
  });
43302
43406
  } else {
@@ -43311,34 +43415,49 @@ var init_DetailPanel = __esm({
43311
43415
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
43312
43416
  const effectiveCloseAction = closeAction ?? { event: void 0};
43313
43417
  const content = /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "md", className: "p-6", children: [
43314
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "end", align: "center", gap: "xs", children: [
43315
- otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
43316
- exports.Button,
43317
- {
43318
- variant: action.variant || "secondary",
43319
- size: "sm",
43320
- action: action.navigatesTo ? void 0 : action.event,
43321
- actionPayload: { row: normalizedData },
43322
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43323
- icon: action.icon,
43324
- "data-testid": action.event ? `action-${action.event}` : void 0,
43325
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43326
- children: action.label
43327
- },
43328
- idx
43329
- )),
43330
- /* @__PURE__ */ jsxRuntime.jsx(
43418
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "center", gap: "xs", children: [
43419
+ /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
43331
43420
  exports.Button,
43332
43421
  {
43333
- variant: "ghost",
43422
+ variant: backAction.variant || "ghost",
43334
43423
  size: "sm",
43335
- action: effectiveCloseAction.event,
43424
+ action: backAction.navigatesTo ? void 0 : backAction.event,
43336
43425
  actionPayload: { row: normalizedData },
43337
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
43338
- icon: LucideIcons2.X,
43339
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
43426
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
43427
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
43428
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
43429
+ children: backAction.label
43340
43430
  }
43341
- )
43431
+ ) }),
43432
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "end", align: "center", gap: "xs", children: [
43433
+ otherActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
43434
+ exports.Button,
43435
+ {
43436
+ variant: action.variant || "secondary",
43437
+ size: "sm",
43438
+ action: action.navigatesTo ? void 0 : action.event,
43439
+ actionPayload: { row: normalizedData },
43440
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
43441
+ icon: action.icon,
43442
+ "data-testid": action.event ? `action-${action.event}` : void 0,
43443
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
43444
+ children: action.label
43445
+ },
43446
+ idx
43447
+ )),
43448
+ /* @__PURE__ */ jsxRuntime.jsx(
43449
+ exports.Button,
43450
+ {
43451
+ variant: "ghost",
43452
+ size: "sm",
43453
+ action: effectiveCloseAction.event,
43454
+ actionPayload: { row: normalizedData },
43455
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
43456
+ icon: LucideIcons2.X,
43457
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
43458
+ }
43459
+ )
43460
+ ] })
43342
43461
  ] }),
43343
43462
  avatar,
43344
43463
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -43653,6 +43772,10 @@ function determineInputType(field) {
43653
43772
  return "password";
43654
43773
  case "url":
43655
43774
  return "url";
43775
+ case "file":
43776
+ return "file";
43777
+ case "money":
43778
+ return "currency";
43656
43779
  case "number":
43657
43780
  case "integer":
43658
43781
  case "float":
@@ -43714,6 +43837,7 @@ var init_Form = __esm({
43714
43837
  init_Typography();
43715
43838
  init_Icon();
43716
43839
  init_RelationSelect();
43840
+ init_UploadDropZone();
43717
43841
  init_Alert();
43718
43842
  init_useEventBus();
43719
43843
  init_debug();
@@ -43756,6 +43880,7 @@ var init_Form = __esm({
43756
43880
  cancelEvent = "CANCEL",
43757
43881
  relationsData = {},
43758
43882
  relationsLoading = {},
43883
+ fieldOverrides,
43759
43884
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
43760
43885
  conditionalFields: conditionalFieldsRaw = {},
43761
43886
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -44002,7 +44127,8 @@ var init_Form = __esm({
44002
44127
  label,
44003
44128
  field.required && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
44004
44129
  ] }),
44005
- renderFieldInput(field, fieldName, inputType, currentValue2, label)
44130
+ renderFieldInput(field, fieldName, inputType, currentValue2, label),
44131
+ field.hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", children: field.hint })
44006
44132
  ] }, fieldName);
44007
44133
  },
44008
44134
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -44018,6 +44144,7 @@ var init_Form = __esm({
44018
44144
  name: field,
44019
44145
  type: entityField.type,
44020
44146
  required: entityField.required,
44147
+ hint: entityField.description,
44021
44148
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
44022
44149
  defaultValue: entityField.default,
44023
44150
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -44030,8 +44157,18 @@ var init_Form = __esm({
44030
44157
  return { name: field, type: "string" };
44031
44158
  }
44032
44159
  return field;
44160
+ }).map((field) => {
44161
+ const fieldName = field.name || field.field;
44162
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
44163
+ if (!override) return field;
44164
+ return {
44165
+ ...field,
44166
+ ...override.label !== void 0 ? { label: override.label } : {},
44167
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
44168
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
44169
+ };
44033
44170
  });
44034
- }, [effectiveFields, resolvedEntity]);
44171
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
44035
44172
  const schemaFields = React77__namespace.default.useMemo(() => {
44036
44173
  if (normalizedFields.length === 0) return null;
44037
44174
  if (isDebugEnabled()) {
@@ -44155,6 +44292,44 @@ var init_Form = __esm({
44155
44292
  max: field.max
44156
44293
  }
44157
44294
  );
44295
+ case "currency":
44296
+ return /* @__PURE__ */ jsxRuntime.jsx(
44297
+ exports.Input,
44298
+ {
44299
+ ...commonProps,
44300
+ type: "number",
44301
+ step: "0.01",
44302
+ icon: LucideIcons2.DollarSign,
44303
+ value: currentValue2 !== void 0 && currentValue2 !== "" ? String(currentValue2) : "",
44304
+ onChange: (e) => handleChange(
44305
+ fieldName,
44306
+ e.target.value ? Number(e.target.value) : void 0
44307
+ ),
44308
+ min: field.min,
44309
+ max: field.max
44310
+ }
44311
+ );
44312
+ case "file":
44313
+ return /* @__PURE__ */ jsxRuntime.jsx(
44314
+ exports.UploadDropZone,
44315
+ {
44316
+ accept: field.pattern,
44317
+ maxFiles: 1,
44318
+ disabled: isLoading,
44319
+ onFiles: (files) => {
44320
+ const f3 = files[0];
44321
+ if (!f3) return;
44322
+ const reader = new FileReader();
44323
+ reader.onload = () => handleChange(fieldName, {
44324
+ name: f3.name,
44325
+ mimeType: f3.type,
44326
+ sizeBytes: f3.size,
44327
+ url: typeof reader.result === "string" ? reader.result : ""
44328
+ });
44329
+ reader.readAsDataURL(f3);
44330
+ }
44331
+ }
44332
+ );
44158
44333
  case "date":
44159
44334
  return /* @__PURE__ */ jsxRuntime.jsx(
44160
44335
  exports.Input,
@@ -45187,6 +45362,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES; exports.MediaGallery = void 0;
45187
45362
  var init_MediaGallery = __esm({
45188
45363
  "components/core/organisms/MediaGallery.tsx"() {
45189
45364
  "use client";
45365
+ init_getNestedValue();
45190
45366
  init_cn();
45191
45367
  init_atoms();
45192
45368
  init_Stack();
@@ -45255,9 +45431,9 @@ var init_MediaGallery = __esm({
45255
45431
  return entityData.map((record, idx) => {
45256
45432
  return {
45257
45433
  id: String(record.id ?? idx),
45258
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
45434
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
45259
45435
  alt: record.alt ? String(record.alt) : void 0,
45260
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
45436
+ thumbnail: resolveImageUrl(record.thumbnail),
45261
45437
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
45262
45438
  };
45263
45439
  });
@@ -49600,7 +49776,8 @@ __export(UISlotRenderer_exports, {
49600
49776
  SlotContentRenderer: () => SlotContentRenderer,
49601
49777
  SuspenseConfigProvider: () => SuspenseConfigProvider,
49602
49778
  UISlotComponent: () => UISlotComponent,
49603
- UISlotRenderer: () => UISlotRenderer
49779
+ UISlotRenderer: () => UISlotRenderer,
49780
+ renderPatternValue: () => renderPatternValue
49604
49781
  });
49605
49782
  function SuspenseConfigProvider({
49606
49783
  config,
@@ -49636,6 +49813,9 @@ function enrichFormFields(fields, entityDef) {
49636
49813
  type: entityField.type,
49637
49814
  required: entityField.required ?? false
49638
49815
  };
49816
+ if (entityField.description) {
49817
+ enriched.hint = entityField.description;
49818
+ }
49639
49819
  if (entityField.values && entityField.values.length > 0) {
49640
49820
  enriched.values = entityField.values;
49641
49821
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -49659,6 +49839,9 @@ function enrichFormFields(fields, entityDef) {
49659
49839
  if (entityField.required && !("required" in obj)) {
49660
49840
  enriched.required = true;
49661
49841
  }
49842
+ if (entityField.description && !obj.hint && !obj.help) {
49843
+ enriched.hint = entityField.description;
49844
+ }
49662
49845
  if (!obj.values && !obj.options) {
49663
49846
  if (entityField.values && entityField.values.length > 0) {
49664
49847
  enriched.values = entityField.values;
@@ -49674,6 +49857,32 @@ function enrichFormFields(fields, entityDef) {
49674
49857
  return field;
49675
49858
  });
49676
49859
  }
49860
+ function enrichDetailFields(fields, entityDef) {
49861
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
49862
+ const metaFor = (name) => {
49863
+ const entityField = fieldMap.get(name);
49864
+ if (!entityField) return void 0;
49865
+ const meta = { type: entityField.type };
49866
+ const values = entityField.values ?? entityField.enumValues;
49867
+ if (values && values.length > 0) meta.values = values;
49868
+ if (entityField.relation) meta.relation = entityField.relation.entity;
49869
+ return meta;
49870
+ };
49871
+ return fields.map((field) => {
49872
+ if (typeof field === "string") {
49873
+ const meta = metaFor(field);
49874
+ return meta ? { key: field, ...meta } : field;
49875
+ }
49876
+ if (field && typeof field === "object" && !Array.isArray(field) && !React77__namespace.default.isValidElement(field) && !(field instanceof Date)) {
49877
+ const obj = field;
49878
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
49879
+ if (!fieldName || obj.type) return field;
49880
+ const meta = metaFor(fieldName);
49881
+ return meta ? { ...obj, ...meta } : field;
49882
+ }
49883
+ return field;
49884
+ });
49885
+ }
49677
49886
  function renderContainedPortal(t, slot, content, onDismiss) {
49678
49887
  const slotContent = /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content, onDismiss }) });
49679
49888
  const slotId = `slot-${slot}`;
@@ -50183,6 +50392,24 @@ function isPatternConfig(value) {
50183
50392
  const record = value;
50184
50393
  return "type" in record && typeof record.type === "string" && patterns.getComponentForPattern(record.type) !== null;
50185
50394
  }
50395
+ function renderPatternValue(value) {
50396
+ if (value === null || value === void 0) return null;
50397
+ if (typeof value === "boolean" || typeof value === "function") return null;
50398
+ if (typeof value === "number") return value;
50399
+ if (typeof value === "string") return renderPatternChildren(value, () => {
50400
+ });
50401
+ if (value instanceof Date) return value.toLocaleString();
50402
+ if (React77__namespace.default.isValidElement(value)) return value;
50403
+ if (Array.isArray(value)) {
50404
+ return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React77__namespace.default.Fragment, { children: renderPatternValue(item) }, index));
50405
+ }
50406
+ if (isPatternConfig(value)) {
50407
+ const { type, ...props } = value;
50408
+ return renderPatternChildren({ type, props }, () => {
50409
+ });
50410
+ }
50411
+ return null;
50412
+ }
50186
50413
  function isPlainConfigObject(value) {
50187
50414
  if (React77__namespace.default.isValidElement(value)) return false;
50188
50415
  if (value instanceof Date) return false;
@@ -50384,9 +50611,9 @@ function SlotContentRenderer({
50384
50611
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
50385
50612
  }
50386
50613
  }
50387
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
50388
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
50389
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
50614
+ const fieldsContract = patterns.getPatternFieldsContract(content.pattern) ?? (patterns.getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
50615
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
50616
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
50390
50617
  }
50391
50618
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
50392
50619
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -50496,7 +50723,7 @@ function UISlotRenderer({
50496
50723
  }
50497
50724
  return wrapped;
50498
50725
  }
50499
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
50726
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
50500
50727
  var init_UISlotRenderer = __esm({
50501
50728
  "components/core/organisms/UISlotRenderer.tsx"() {
50502
50729
  "use client";
@@ -50527,12 +50754,6 @@ var init_UISlotRenderer = __esm({
50527
50754
  modal: "form",
50528
50755
  drawer: "form"
50529
50756
  };
50530
- FORM_PATTERNS = /* @__PURE__ */ new Set([
50531
- "form",
50532
- "form-section",
50533
- "inline-edit-form",
50534
- "wizard-step"
50535
- ]);
50536
50757
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
50537
50758
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
50538
50759
  "logo",
@@ -53163,6 +53384,7 @@ exports.parseLessonSegments = parseLessonSegments;
53163
53384
  exports.parseMarkdownWithCodeBlocks = parseMarkdownWithCodeBlocks;
53164
53385
  exports.parseQueryBinding = parseQueryBinding;
53165
53386
  exports.registerCodeLanguageLoader = registerCodeLanguageLoader;
53387
+ exports.renderPatternValue = renderPatternValue;
53166
53388
  exports.resolveFieldMap = resolveFieldMap;
53167
53389
  exports.resolveFrame = resolveFrame;
53168
53390
  exports.resolveSheetDirection = resolveSheetDirection;