@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.
@@ -3,12 +3,12 @@ import React87__default, { createContext, useContext, useMemo, useRef, useEffect
3
3
  import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, TraitScopeProvider, useCurrentPagePath, useGameAudioContextOptional } from '@almadar/ui/providers';
4
4
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
5
5
  import { createContextFromBindings, interpolateValue } from '@almadar/runtime';
6
- import { ANONYMOUS_USER, isRenderBindingMarker, isInlineTrait, ANIMATION_NAMES } from '@almadar/core';
6
+ import { ANONYMOUS_USER, isRenderBindingMarker, isFileValue, isInlineTrait, ANIMATION_NAMES } from '@almadar/core';
7
7
  export { ANONYMOUS_USER } from '@almadar/core';
8
8
  import { clsx } from 'clsx';
9
9
  import { twMerge } from 'tailwind-merge';
10
10
  import * as LucideIcons2 from 'lucide-react';
11
- import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ZoomOut, ZoomIn, Download, ChevronDown, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, ArrowLeft, HelpCircle, PauseCircle, Search, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, Minus, Eraser, TrendingUp, TrendingDown, AlertCircle, Circle, Clock, CheckCircle2, ChevronUp, Tag, User, DollarSign } from 'lucide-react';
11
+ import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ArrowLeft, ZoomOut, ZoomIn, Download, DollarSign, ChevronDown, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, HelpCircle, PauseCircle, Search, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, Minus, Eraser, TrendingUp, TrendingDown, AlertCircle, Circle, Clock, CheckCircle2, ChevronUp, Tag, User } from 'lucide-react';
12
12
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
13
  import { createPortal } from 'react-dom';
14
14
  import { useTranslate } from '@almadar/ui/hooks';
@@ -77,7 +77,7 @@ import { CSS } from '@dnd-kit/utilities';
77
77
  import { ordered, lib } from 'emojilib';
78
78
  import { useNodeId, ReactFlowProvider, Handle, Position } from '@xyflow/react';
79
79
  import { forceSimulation, forceLink, forceManyBody, forceCollide, forceX, forceY } from 'd3-force';
80
- import { isDrawHostPattern, getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/core/patterns';
80
+ import { isDrawHostPattern, getPatternDefinition, getPatternFieldsContract, getComponentForPattern as getComponentForPattern$1 } from '@almadar/core/patterns';
81
81
 
82
82
  var __defProp = Object.defineProperty;
83
83
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -14169,7 +14169,7 @@ var init_AlgoGraphCanvas = __esm({
14169
14169
  }
14170
14170
  const badgeGeoms = [];
14171
14171
  for (const g of nodeGeoms) {
14172
- if (!g.badge) continue;
14172
+ if (!g.badge || g.badge.text === "") continue;
14173
14173
  const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
14174
14174
  badgeGeoms.push({
14175
14175
  cx: g.x + g.radius * 0.75,
@@ -14235,7 +14235,7 @@ var init_AlgoGraphCanvas = __esm({
14235
14235
  };
14236
14236
  }
14237
14237
  });
14238
- 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;
14238
+ 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;
14239
14239
  var init_AlgorithmCanvas = __esm({
14240
14240
  "components/learning/molecules/AlgorithmCanvas.tsx"() {
14241
14241
  "use client";
@@ -14262,9 +14262,18 @@ var init_AlgorithmCanvas = __esm({
14262
14262
  FRAME_ACTIVE_COLOR = "#3b82f6";
14263
14263
  FRAME_RETURNING_COLOR = "#f59e0b";
14264
14264
  FRAME_DONE_COLOR = "#94a3b8";
14265
+ FRAME_RIM_COLOR = {
14266
+ active: "#1d4ed8",
14267
+ returning: "#b45309",
14268
+ done: "#64748b"
14269
+ };
14265
14270
  FRAME_LABEL_COLOR = "#ffffff";
14266
14271
  FRAME_DETAIL_COLOR = "#e2e8f0";
14267
14272
  FRAME_TWO_LINE_MIN_H = 22;
14273
+ FRAME_STRIP_H = 44;
14274
+ FRAME_GAP = 6;
14275
+ FRAME_BOTTOM_PAD = 8;
14276
+ FRAME_MIN_H = 12;
14268
14277
  BUCKET_INDEX_FILL = "#e2e8f0";
14269
14278
  BUCKET_INDEX_STROKE = "#9ca3af";
14270
14279
  BUCKET_INDEX_TEXT = "#374151";
@@ -14761,14 +14770,18 @@ var init_AlgorithmCanvas = __esm({
14761
14770
  if (frames.length > 0) {
14762
14771
  const panelYFrames = panelY.frames;
14763
14772
  const n = frames.length;
14764
- const frameH = panelHeight / n;
14773
+ const maxStride = (panelHeight - FRAME_BOTTOM_PAD - TOP_PAD) / n;
14774
+ const stride = Math.min(FRAME_STRIP_H + FRAME_GAP, maxStride);
14775
+ const frameH = Math.max(FRAME_MIN_H, stride - FRAME_GAP);
14765
14776
  const x = 8;
14766
14777
  const w = width - 16;
14778
+ const bottom = panelYFrames + panelHeight - FRAME_BOTTOM_PAD;
14767
14779
  frames.forEach((f3, i) => {
14768
- const y = panelYFrames + panelHeight - (i + 1) * frameH;
14780
+ const y = bottom - i * stride - frameH;
14769
14781
  const state = f3.state ?? "active";
14770
14782
  const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
14771
- out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
14783
+ const rim = f3.color ?? FRAME_RIM_COLOR[state] ?? FRAME_RIM_COLOR.active;
14784
+ out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: rim, fill });
14772
14785
  if (frameH >= FRAME_TWO_LINE_MIN_H) {
14773
14786
  out.push({
14774
14787
  type: "text",
@@ -14776,14 +14789,14 @@ var init_AlgorithmCanvas = __esm({
14776
14789
  y: y + frameH * 0.35,
14777
14790
  text: f3.label,
14778
14791
  color: FRAME_LABEL_COLOR,
14779
- fontSize: 10,
14792
+ fontSize: 11,
14780
14793
  align: "left"
14781
14794
  });
14782
14795
  if (f3.detail) {
14783
14796
  out.push({
14784
14797
  type: "text",
14785
14798
  x: 16,
14786
- y: y + frameH * 0.7,
14799
+ y: y + frameH * 0.72,
14787
14800
  text: f3.detail,
14788
14801
  color: FRAME_DETAIL_COLOR,
14789
14802
  fontSize: 10,
@@ -21296,8 +21309,6 @@ var init_ButtonGroup = __esm({
21296
21309
  ButtonGroup.displayName = "ButtonGroup";
21297
21310
  }
21298
21311
  });
21299
-
21300
- // lib/getNestedValue.ts
21301
21312
  function getNestedValue(obj, path) {
21302
21313
  if (obj === null || obj === void 0 || !path) {
21303
21314
  return void 0;
@@ -21318,6 +21329,15 @@ function getNestedValue(obj, path) {
21318
21329
  }
21319
21330
  return value;
21320
21331
  }
21332
+ function resolveImageUrl(value) {
21333
+ if (typeof value === "string") {
21334
+ return value;
21335
+ }
21336
+ if (isFileValue(value)) {
21337
+ return value.url;
21338
+ }
21339
+ return void 0;
21340
+ }
21321
21341
  var init_getNestedValue = __esm({
21322
21342
  "lib/getNestedValue.ts"() {
21323
21343
  }
@@ -22022,8 +22042,8 @@ var init_CardGrid = __esm({
22022
22042
  actionPayload: { row: itemData },
22023
22043
  children: [
22024
22044
  imageField && (() => {
22025
- const imgUrl = getNestedValue(itemData, imageField);
22026
- if (!imgUrl || typeof imgUrl !== "string") return null;
22045
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
22046
+ if (!imgUrl) return null;
22027
22047
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
22028
22048
  "img",
22029
22049
  {
@@ -26053,8 +26073,8 @@ function DataGrid({
26053
26073
  ),
26054
26074
  children: [
26055
26075
  imageField && (() => {
26056
- const imgUrl = getNestedValue(itemData, imageField);
26057
- if (!imgUrl || typeof imgUrl !== "string") return null;
26076
+ const imgUrl = resolveImageUrl(getNestedValue(itemData, imageField));
26077
+ if (!imgUrl) return null;
26058
26078
  return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
26059
26079
  "img",
26060
26080
  {
@@ -28664,7 +28684,7 @@ var init_SearchInput = __esm({
28664
28684
  /* @__PURE__ */ jsx(
28665
28685
  Input,
28666
28686
  {
28667
- type: "text",
28687
+ type: "search",
28668
28688
  value: searchValue,
28669
28689
  onChange: handleChange,
28670
28690
  placeholder: resolvedPlaceholder,
@@ -29990,6 +30010,7 @@ var MathCanvas;
29990
30010
  var init_MathCanvas = __esm({
29991
30011
  "components/learning/molecules/MathCanvas.tsx"() {
29992
30012
  "use client";
30013
+ init_useEventBus();
29993
30014
  init_atoms();
29994
30015
  init_Stack();
29995
30016
  init_LearningCanvas();
@@ -30021,9 +30042,32 @@ var init_MathCanvas = __esm({
30021
30042
  interactive = false,
30022
30043
  animate = false,
30023
30044
  onShapeClick,
30045
+ keyMap,
30046
+ keyUpMap,
30024
30047
  isLoading,
30025
30048
  error
30026
30049
  }) => {
30050
+ const eventBus = useEventBus();
30051
+ useEffect(() => {
30052
+ if (!keyMap && !keyUpMap) return;
30053
+ const onDown = (e) => {
30054
+ const ev = keyMap?.[e.code];
30055
+ if (ev) {
30056
+ eventBus.emit(`UI:${ev}`, {});
30057
+ e.preventDefault();
30058
+ }
30059
+ };
30060
+ const onUp = (e) => {
30061
+ const ev = keyUpMap?.[e.code];
30062
+ if (ev) eventBus.emit(`UI:${ev}`, {});
30063
+ };
30064
+ window.addEventListener("keydown", onDown);
30065
+ window.addEventListener("keyup", onUp);
30066
+ return () => {
30067
+ window.removeEventListener("keydown", onDown);
30068
+ window.removeEventListener("keyup", onUp);
30069
+ };
30070
+ }, [keyMap, keyUpMap, eventBus]);
30027
30071
  const derivedShapes = useMemo(() => {
30028
30072
  const out = [];
30029
30073
  const margin = 24;
@@ -30036,11 +30080,11 @@ var init_MathCanvas = __esm({
30036
30080
  if (showGrid) {
30037
30081
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
30038
30082
  const px = mapX(x);
30039
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#e5e7eb", lineWidth: 1 });
30083
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30040
30084
  }
30041
30085
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
30042
30086
  const py = mapY(y);
30043
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
30087
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30044
30088
  }
30045
30089
  }
30046
30090
  if (showTickLabels) {
@@ -31515,13 +31559,13 @@ var init_MapView = __esm({
31515
31559
  shadowSize: [41, 41]
31516
31560
  });
31517
31561
  L.Marker.prototype.options.icon = defaultIcon;
31518
- const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__default;
31562
+ const { useEffect: useEffect64, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__default;
31519
31563
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31520
31564
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31521
31565
  function MapUpdater({ centerLat, centerLng, zoom }) {
31522
31566
  const map = useMap();
31523
31567
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31524
- useEffect63(() => {
31568
+ useEffect64(() => {
31525
31569
  const prev = prevRef.current;
31526
31570
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31527
31571
  map.setView([centerLat, centerLng], zoom);
@@ -31532,7 +31576,7 @@ var init_MapView = __esm({
31532
31576
  }
31533
31577
  function MapClickHandler({ onMapClick }) {
31534
31578
  const map = useMap();
31535
- useEffect63(() => {
31579
+ useEffect64(() => {
31536
31580
  if (!onMapClick) return;
31537
31581
  const handler = (e) => {
31538
31582
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -38348,6 +38392,7 @@ var init_PageHeader = __esm({
38348
38392
  className
38349
38393
  }) => {
38350
38394
  const eventBus = useEventBus();
38395
+ const statusBadge = typeof status === "string" ? status ? { label: status } : void 0 : status;
38351
38396
  const handleBack = () => {
38352
38397
  eventBus.emit(`UI:${backEvent}`);
38353
38398
  };
@@ -38395,15 +38440,15 @@ var init_PageHeader = __esm({
38395
38440
  /* @__PURE__ */ jsxs(Box, { children: [
38396
38441
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-3", children: [
38397
38442
  /* @__PURE__ */ jsx(Typography, { variant: "h1", className: "text-2xl font-bold text-foreground", children: title != null ? String(title) : "" }),
38398
- status && /* @__PURE__ */ jsx(
38443
+ statusBadge && /* @__PURE__ */ jsx(
38399
38444
  Typography,
38400
38445
  {
38401
38446
  variant: "small",
38402
38447
  className: cn(
38403
38448
  "px-2.5 py-1 rounded-full text-xs font-medium",
38404
- statusColors2[status.variant || "default"]
38449
+ statusColors2[statusBadge.variant || "default"]
38405
38450
  ),
38406
- children: status.label
38451
+ children: statusBadge.label
38407
38452
  }
38408
38453
  )
38409
38454
  ] }),
@@ -41632,7 +41677,7 @@ function formatFieldValue2(value, fieldName) {
41632
41677
  }
41633
41678
  return String(value);
41634
41679
  }
41635
- function renderRichFieldValue(value, fieldName, fieldType) {
41680
+ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41636
41681
  if (value === void 0 || value === null) return "\u2014";
41637
41682
  const str = String(value);
41638
41683
  switch (fieldType) {
@@ -41699,19 +41744,58 @@ function renderRichFieldValue(value, fieldName, fieldType) {
41699
41744
  }
41700
41745
  );
41701
41746
  case "date":
41702
- case "datetime": {
41747
+ case "datetime":
41748
+ case "timestamp": {
41703
41749
  const d = new Date(str);
41704
41750
  if (!isNaN(d.getTime())) {
41705
41751
  return d.toLocaleDateString(void 0, {
41706
41752
  year: "numeric",
41707
41753
  month: "long",
41708
41754
  day: "numeric",
41709
- ...fieldType === "datetime" ? { hour: "2-digit", minute: "2-digit" } : {}
41755
+ ...fieldType !== "date" ? { hour: "2-digit", minute: "2-digit" } : {}
41710
41756
  });
41711
41757
  }
41712
41758
  return str;
41713
41759
  }
41760
+ case "money": {
41761
+ const n = typeof value === "number" ? value : Number(str);
41762
+ if (!isNaN(n)) {
41763
+ return new Intl.NumberFormat(void 0, {
41764
+ style: "currency",
41765
+ currency: "USD"
41766
+ }).format(n);
41767
+ }
41768
+ return str;
41769
+ }
41770
+ case "relation": {
41771
+ const match = meta?.options?.find((opt) => opt.value === str);
41772
+ return match ? match.label : str;
41773
+ }
41774
+ case "file": {
41775
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
41776
+ const file = value;
41777
+ const label = typeof file.name === "string" && file.name ? file.name : "file";
41778
+ const size = typeof file.sizeBytes === "number" ? ` \xB7 ${formatFileSize(file.sizeBytes)}` : "";
41779
+ return /* @__PURE__ */ jsxs(
41780
+ "a",
41781
+ {
41782
+ href: typeof file.url === "string" ? file.url : void 0,
41783
+ download: label,
41784
+ 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",
41785
+ children: [
41786
+ /* @__PURE__ */ jsx(Icon, { icon: FileText, size: "sm", className: "text-muted-foreground" }),
41787
+ label,
41788
+ size && /* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", color: "muted", children: size })
41789
+ ]
41790
+ }
41791
+ );
41792
+ }
41793
+ return str;
41794
+ }
41714
41795
  default:
41796
+ if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
41797
+ return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
41798
+ }
41715
41799
  return formatFieldValue2(value, fieldName);
41716
41800
  }
41717
41801
  }
@@ -41738,8 +41822,18 @@ function buildFieldTypeMap(fields) {
41738
41822
  const map = {};
41739
41823
  if (!fields) return map;
41740
41824
  for (const f3 of fields) {
41741
- if (typeof f3 === "object" && "name" in f3 && "type" in f3) {
41742
- map[f3.name] = f3.type;
41825
+ if (typeof f3 === "object" && f3.type !== void 0) {
41826
+ map["name" in f3 ? f3.name : f3.key] = f3.type;
41827
+ }
41828
+ }
41829
+ return map;
41830
+ }
41831
+ function buildFieldMetaMap(fields) {
41832
+ const map = {};
41833
+ if (!fields) return map;
41834
+ for (const f3 of fields) {
41835
+ if (typeof f3 === "object" && (f3.type !== void 0 || f3.values !== void 0 || f3.relation !== void 0)) {
41836
+ map["name" in f3 ? f3.name : f3.key] = { type: f3.type, values: f3.values, relation: f3.relation };
41743
41837
  }
41744
41838
  }
41745
41839
  return map;
@@ -41759,6 +41853,7 @@ var init_DetailPanel = __esm({
41759
41853
  init_format();
41760
41854
  init_getNestedValue();
41761
41855
  init_useEventBus();
41856
+ init_UploadDropZone();
41762
41857
  formatFieldLabel = humanizeFieldName;
41763
41858
  ReactMarkdown2 = lazy(() => import('react-markdown'));
41764
41859
  DetailPanel = ({
@@ -41768,6 +41863,7 @@ var init_DetailPanel = __esm({
41768
41863
  avatar,
41769
41864
  sections: propSections,
41770
41865
  actions,
41866
+ backAction,
41771
41867
  footer,
41772
41868
  slideOver = false,
41773
41869
  className,
@@ -41776,7 +41872,8 @@ var init_DetailPanel = __esm({
41776
41872
  fieldNames,
41777
41873
  initialData,
41778
41874
  isLoading = false,
41779
- error
41875
+ error,
41876
+ relationsData
41780
41877
  }) => {
41781
41878
  const eventBus = useEventBus();
41782
41879
  const { t } = useTranslate();
@@ -41787,8 +41884,15 @@ var init_DetailPanel = __esm({
41787
41884
  };
41788
41885
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
41789
41886
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
41887
+ const fieldMetaMap = isFieldDefArray(propFields) ? buildFieldMetaMap(propFields) : {};
41790
41888
  const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
41791
41889
  const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
41890
+ const metaFor = (field) => {
41891
+ const base = fieldMetaMap[field];
41892
+ const options = relationsData?.[field];
41893
+ if (!base && !options) return void 0;
41894
+ return { ...base, options };
41895
+ };
41792
41896
  const handleActionClick = useCallback(
41793
41897
  (action, data2) => {
41794
41898
  if (action.navigatesTo) {
@@ -41867,7 +41971,7 @@ var init_DetailPanel = __esm({
41867
41971
  if (value !== void 0 && value !== null) {
41868
41972
  overviewFields.push({
41869
41973
  label: labelFor(field),
41870
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41974
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41871
41975
  icon: getFieldIcon(field)
41872
41976
  });
41873
41977
  }
@@ -41883,7 +41987,7 @@ var init_DetailPanel = __esm({
41883
41987
  if (value !== void 0 && value !== null) {
41884
41988
  metricsFields.push({
41885
41989
  label: labelFor(field),
41886
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
41990
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41887
41991
  icon: getFieldIcon(field)
41888
41992
  });
41889
41993
  }
@@ -41899,7 +42003,7 @@ var init_DetailPanel = __esm({
41899
42003
  if (value !== void 0 && value !== null) {
41900
42004
  timelineFields.push({
41901
42005
  label: labelFor(field),
41902
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42006
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41903
42007
  icon: getFieldIcon(field)
41904
42008
  });
41905
42009
  }
@@ -41915,7 +42019,7 @@ var init_DetailPanel = __esm({
41915
42019
  if (value !== void 0 && value !== null) {
41916
42020
  descFields.push({
41917
42021
  label: labelFor(field),
41918
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42022
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41919
42023
  icon: getFieldIcon(field)
41920
42024
  });
41921
42025
  }
@@ -41963,7 +42067,7 @@ var init_DetailPanel = __esm({
41963
42067
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
41964
42068
  allFields.push({
41965
42069
  label: labelFor(field),
41966
- value: renderRichFieldValue(value, field, fieldTypeMap[field]),
42070
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41967
42071
  icon: getFieldIcon(field)
41968
42072
  });
41969
42073
  } else {
@@ -41978,34 +42082,49 @@ var init_DetailPanel = __esm({
41978
42082
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
41979
42083
  const effectiveCloseAction = closeAction ?? { event: void 0};
41980
42084
  const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
41981
- /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41982
- otherActions.map((action, idx) => /* @__PURE__ */ jsx(
41983
- Button,
41984
- {
41985
- variant: action.variant || "secondary",
41986
- size: "sm",
41987
- action: action.navigatesTo ? void 0 : action.event,
41988
- actionPayload: { row: normalizedData },
41989
- onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
41990
- icon: action.icon,
41991
- "data-testid": action.event ? `action-${action.event}` : void 0,
41992
- "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
41993
- children: action.label
41994
- },
41995
- idx
41996
- )),
41997
- /* @__PURE__ */ jsx(
42085
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
42086
+ /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
41998
42087
  Button,
41999
42088
  {
42000
- variant: "ghost",
42089
+ variant: backAction.variant || "ghost",
42001
42090
  size: "sm",
42002
- action: effectiveCloseAction.event,
42091
+ action: backAction.navigatesTo ? void 0 : backAction.event,
42003
42092
  actionPayload: { row: normalizedData },
42004
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
42005
- icon: X,
42006
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42093
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
42094
+ icon: backAction.icon ?? ArrowLeft,
42095
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42096
+ children: backAction.label
42007
42097
  }
42008
- )
42098
+ ) }),
42099
+ /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
42100
+ otherActions.map((action, idx) => /* @__PURE__ */ jsx(
42101
+ Button,
42102
+ {
42103
+ variant: action.variant || "secondary",
42104
+ size: "sm",
42105
+ action: action.navigatesTo ? void 0 : action.event,
42106
+ actionPayload: { row: normalizedData },
42107
+ onClick: action.navigatesTo ? () => handleActionClick(action, normalizedData) : void 0,
42108
+ icon: action.icon,
42109
+ "data-testid": action.event ? `action-${action.event}` : void 0,
42110
+ "data-row-id": normalizedData?.id !== void 0 ? String(normalizedData.id) : void 0,
42111
+ children: action.label
42112
+ },
42113
+ idx
42114
+ )),
42115
+ /* @__PURE__ */ jsx(
42116
+ Button,
42117
+ {
42118
+ variant: "ghost",
42119
+ size: "sm",
42120
+ action: effectiveCloseAction.event,
42121
+ actionPayload: { row: normalizedData },
42122
+ onClick: effectiveCloseAction.event ? void 0 : handleClose,
42123
+ icon: X,
42124
+ "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42125
+ }
42126
+ )
42127
+ ] })
42009
42128
  ] }),
42010
42129
  avatar,
42011
42130
  /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
@@ -42320,6 +42439,10 @@ function determineInputType(field) {
42320
42439
  return "password";
42321
42440
  case "url":
42322
42441
  return "url";
42442
+ case "file":
42443
+ return "file";
42444
+ case "money":
42445
+ return "currency";
42323
42446
  case "number":
42324
42447
  case "integer":
42325
42448
  case "float":
@@ -42381,6 +42504,7 @@ var init_Form = __esm({
42381
42504
  init_Typography();
42382
42505
  init_Icon();
42383
42506
  init_RelationSelect();
42507
+ init_UploadDropZone();
42384
42508
  init_Alert();
42385
42509
  init_useEventBus();
42386
42510
  init_debug();
@@ -42423,6 +42547,7 @@ var init_Form = __esm({
42423
42547
  cancelEvent = "CANCEL",
42424
42548
  relationsData = {},
42425
42549
  relationsLoading = {},
42550
+ fieldOverrides,
42426
42551
  // Inspection form extensions - may come as boolean true from generated code (meaning enabled but config loaded separately)
42427
42552
  conditionalFields: conditionalFieldsRaw = {},
42428
42553
  hiddenCalculations: hiddenCalculationsRaw = [],
@@ -42669,7 +42794,8 @@ var init_Form = __esm({
42669
42794
  label,
42670
42795
  field.required && /* @__PURE__ */ jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
42671
42796
  ] }),
42672
- renderFieldInput(field, fieldName, inputType, currentValue, label)
42797
+ renderFieldInput(field, fieldName, inputType, currentValue, label),
42798
+ field.hint && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
42673
42799
  ] }, fieldName);
42674
42800
  },
42675
42801
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
@@ -42685,6 +42811,7 @@ var init_Form = __esm({
42685
42811
  name: field,
42686
42812
  type: entityField.type,
42687
42813
  required: entityField.required,
42814
+ hint: entityField.description,
42688
42815
  // EntityField.default is typed `unknown` upstream — safe cast: schema defaults are always FieldValues.
42689
42816
  defaultValue: entityField.default,
42690
42817
  // EntityField is a discriminated union — `values` lives on Scalar/Enum, `relation` lives on Relation.
@@ -42697,8 +42824,18 @@ var init_Form = __esm({
42697
42824
  return { name: field, type: "string" };
42698
42825
  }
42699
42826
  return field;
42827
+ }).map((field) => {
42828
+ const fieldName = field.name || field.field;
42829
+ const override = fieldOverrides?.find((o) => o.name === fieldName);
42830
+ if (!override) return field;
42831
+ return {
42832
+ ...field,
42833
+ ...override.label !== void 0 ? { label: override.label } : {},
42834
+ ...override.placeholder !== void 0 ? { placeholder: override.placeholder } : {},
42835
+ ...override.hint !== void 0 ? { hint: override.hint } : {}
42836
+ };
42700
42837
  });
42701
- }, [effectiveFields, resolvedEntity]);
42838
+ }, [effectiveFields, resolvedEntity, fieldOverrides]);
42702
42839
  const schemaFields = React87__default.useMemo(() => {
42703
42840
  if (normalizedFields.length === 0) return null;
42704
42841
  if (isDebugEnabled()) {
@@ -42822,6 +42959,44 @@ var init_Form = __esm({
42822
42959
  max: field.max
42823
42960
  }
42824
42961
  );
42962
+ case "currency":
42963
+ return /* @__PURE__ */ jsx(
42964
+ Input,
42965
+ {
42966
+ ...commonProps,
42967
+ type: "number",
42968
+ step: "0.01",
42969
+ icon: DollarSign,
42970
+ value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
42971
+ onChange: (e) => handleChange(
42972
+ fieldName,
42973
+ e.target.value ? Number(e.target.value) : void 0
42974
+ ),
42975
+ min: field.min,
42976
+ max: field.max
42977
+ }
42978
+ );
42979
+ case "file":
42980
+ return /* @__PURE__ */ jsx(
42981
+ UploadDropZone,
42982
+ {
42983
+ accept: field.pattern,
42984
+ maxFiles: 1,
42985
+ disabled: isLoading,
42986
+ onFiles: (files) => {
42987
+ const f3 = files[0];
42988
+ if (!f3) return;
42989
+ const reader = new FileReader();
42990
+ reader.onload = () => handleChange(fieldName, {
42991
+ name: f3.name,
42992
+ mimeType: f3.type,
42993
+ sizeBytes: f3.size,
42994
+ url: typeof reader.result === "string" ? reader.result : ""
42995
+ });
42996
+ reader.readAsDataURL(f3);
42997
+ }
42998
+ }
42999
+ );
42825
43000
  case "date":
42826
43001
  return /* @__PURE__ */ jsx(
42827
43002
  Input,
@@ -43854,6 +44029,7 @@ var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
43854
44029
  var init_MediaGallery = __esm({
43855
44030
  "components/core/organisms/MediaGallery.tsx"() {
43856
44031
  "use client";
44032
+ init_getNestedValue();
43857
44033
  init_cn();
43858
44034
  init_atoms();
43859
44035
  init_Stack();
@@ -43922,9 +44098,9 @@ var init_MediaGallery = __esm({
43922
44098
  return entityData.map((record, idx) => {
43923
44099
  return {
43924
44100
  id: String(record.id ?? idx),
43925
- src: String(record.src ?? ("url" in record ? record.url : "") ?? ("image" in record ? record.image : "") ?? ""),
44101
+ src: resolveImageUrl(record.src ?? ("url" in record ? record.url : void 0) ?? ("image" in record ? record.image : void 0)) ?? "",
43926
44102
  alt: record.alt ? String(record.alt) : void 0,
43927
- thumbnail: record.thumbnail ? String(record.thumbnail) : void 0,
44103
+ thumbnail: resolveImageUrl(record.thumbnail),
43928
44104
  caption: record.caption ? String(record.caption) : "title" in record ? String(record.title) : void 0
43929
44105
  };
43930
44106
  });
@@ -48267,7 +48443,8 @@ __export(UISlotRenderer_exports, {
48267
48443
  SlotContentRenderer: () => SlotContentRenderer,
48268
48444
  SuspenseConfigProvider: () => SuspenseConfigProvider,
48269
48445
  UISlotComponent: () => UISlotComponent,
48270
- UISlotRenderer: () => UISlotRenderer
48446
+ UISlotRenderer: () => UISlotRenderer,
48447
+ renderPatternValue: () => renderPatternValue
48271
48448
  });
48272
48449
  function SuspenseConfigProvider({
48273
48450
  config,
@@ -48303,6 +48480,9 @@ function enrichFormFields(fields, entityDef) {
48303
48480
  type: entityField.type,
48304
48481
  required: entityField.required ?? false
48305
48482
  };
48483
+ if (entityField.description) {
48484
+ enriched.hint = entityField.description;
48485
+ }
48306
48486
  if (entityField.values && entityField.values.length > 0) {
48307
48487
  enriched.values = entityField.values;
48308
48488
  } else if (entityField.enumValues && entityField.enumValues.length > 0) {
@@ -48326,6 +48506,9 @@ function enrichFormFields(fields, entityDef) {
48326
48506
  if (entityField.required && !("required" in obj)) {
48327
48507
  enriched.required = true;
48328
48508
  }
48509
+ if (entityField.description && !obj.hint && !obj.help) {
48510
+ enriched.hint = entityField.description;
48511
+ }
48329
48512
  if (!obj.values && !obj.options) {
48330
48513
  if (entityField.values && entityField.values.length > 0) {
48331
48514
  enriched.values = entityField.values;
@@ -48341,6 +48524,32 @@ function enrichFormFields(fields, entityDef) {
48341
48524
  return field;
48342
48525
  });
48343
48526
  }
48527
+ function enrichDetailFields(fields, entityDef) {
48528
+ const fieldMap = new Map(entityDef.fields.map((f3) => [f3.name, f3]));
48529
+ const metaFor = (name) => {
48530
+ const entityField = fieldMap.get(name);
48531
+ if (!entityField) return void 0;
48532
+ const meta = { type: entityField.type };
48533
+ const values = entityField.values ?? entityField.enumValues;
48534
+ if (values && values.length > 0) meta.values = values;
48535
+ if (entityField.relation) meta.relation = entityField.relation.entity;
48536
+ return meta;
48537
+ };
48538
+ return fields.map((field) => {
48539
+ if (typeof field === "string") {
48540
+ const meta = metaFor(field);
48541
+ return meta ? { key: field, ...meta } : field;
48542
+ }
48543
+ if (field && typeof field === "object" && !Array.isArray(field) && !React87__default.isValidElement(field) && !(field instanceof Date)) {
48544
+ const obj = field;
48545
+ const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
48546
+ if (!fieldName || obj.type) return field;
48547
+ const meta = metaFor(fieldName);
48548
+ return meta ? { ...obj, ...meta } : field;
48549
+ }
48550
+ return field;
48551
+ });
48552
+ }
48344
48553
  function renderContainedPortal(t, slot, content, onDismiss) {
48345
48554
  const slotContent = /* @__PURE__ */ jsx(MaybeTraitScope, { sourceTrait: content.sourceTrait, children: /* @__PURE__ */ jsx(SlotContentRenderer, { content, onDismiss }) });
48346
48555
  const slotId = `slot-${slot}`;
@@ -48850,6 +49059,24 @@ function isPatternConfig(value) {
48850
49059
  const record = value;
48851
49060
  return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
48852
49061
  }
49062
+ function renderPatternValue(value) {
49063
+ if (value === null || value === void 0) return null;
49064
+ if (typeof value === "boolean" || typeof value === "function") return null;
49065
+ if (typeof value === "number") return value;
49066
+ if (typeof value === "string") return renderPatternChildren(value, () => {
49067
+ });
49068
+ if (value instanceof Date) return value.toLocaleString();
49069
+ if (React87__default.isValidElement(value)) return value;
49070
+ if (Array.isArray(value)) {
49071
+ return value.map((item, index) => /* @__PURE__ */ jsx(React87__default.Fragment, { children: renderPatternValue(item) }, index));
49072
+ }
49073
+ if (isPatternConfig(value)) {
49074
+ const { type, ...props } = value;
49075
+ return renderPatternChildren({ type, props }, () => {
49076
+ });
49077
+ }
49078
+ return null;
49079
+ }
48853
49080
  function isPlainConfigObject(value) {
48854
49081
  if (React87__default.isValidElement(value)) return false;
48855
49082
  if (value instanceof Date) return false;
@@ -49051,9 +49278,9 @@ function SlotContentRenderer({
49051
49278
  finalProps.fields = keys.map((k, i) => ({ name: k, variant: i === 0 ? "h4" : "body" }));
49052
49279
  }
49053
49280
  }
49054
- const isFormPattern = FORM_PATTERNS.has(content.pattern) || content.pattern.includes("form");
49055
- if (isFormPattern && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
49056
- finalProps.fields = enrichFormFields([...finalProps.fields], entityDef);
49281
+ const fieldsContract = getPatternFieldsContract(content.pattern) ?? (getPatternDefinition(content.pattern) === null && content.pattern.includes("form") ? "form" : void 0);
49282
+ if (fieldsContract && entityDef && Array.isArray(finalProps.fields) && finalProps.fields[0] !== "fn") {
49283
+ finalProps.fields = fieldsContract === "form" ? enrichFormFields([...finalProps.fields], entityDef) : enrichDetailFields([...finalProps.fields], entityDef);
49057
49284
  }
49058
49285
  const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
49059
49286
  return /* @__PURE__ */ jsx(
@@ -49163,7 +49390,7 @@ function UISlotRenderer({
49163
49390
  }
49164
49391
  return wrapped;
49165
49392
  }
49166
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, FORM_PATTERNS, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49393
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49167
49394
  var init_UISlotRenderer = __esm({
49168
49395
  "components/core/organisms/UISlotRenderer.tsx"() {
49169
49396
  "use client";
@@ -49194,12 +49421,6 @@ var init_UISlotRenderer = __esm({
49194
49421
  modal: "form",
49195
49422
  drawer: "form"
49196
49423
  };
49197
- FORM_PATTERNS = /* @__PURE__ */ new Set([
49198
- "form",
49199
- "form-section",
49200
- "inline-edit-form",
49201
- "wizard-step"
49202
- ]);
49203
49424
  SELF_OVERLAY_PATTERNS = /* @__PURE__ */ new Set(["modal", "confirm-dialog"]);
49204
49425
  CONTENT_NODE_SLOTS = /* @__PURE__ */ new Set([
49205
49426
  "logo",