@almadar/ui 5.157.0 → 5.159.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.
@@ -6361,11 +6361,54 @@ function isPlainObject(value) {
6361
6361
  if (typeof value === "function") return false;
6362
6362
  return true;
6363
6363
  }
6364
+ function isEvaluatorResolvedData(value) {
6365
+ return evaluatorResolvedData.has(value);
6366
+ }
6367
+ function brandResolved(value) {
6368
+ if (value !== null && typeof value === "object" && !React77__namespace.default.isValidElement(value) && !(value instanceof Date)) {
6369
+ resolvedMarkerFree.add(value);
6370
+ }
6371
+ }
6372
+ function subtreeHasMarker(value) {
6373
+ if (resolvedMarkerFree.has(value)) return false;
6374
+ const cached = markerPresenceCache.get(value);
6375
+ if (cached !== void 0) return cached;
6376
+ let found = false;
6377
+ const children = Array.isArray(value) ? value : Object.values(value);
6378
+ for (const child of children) {
6379
+ if (core.isRenderBindingMarker(child)) {
6380
+ found = true;
6381
+ break;
6382
+ }
6383
+ if (Array.isArray(child) || isPlainObject(child)) {
6384
+ if (subtreeHasMarker(child)) {
6385
+ found = true;
6386
+ break;
6387
+ }
6388
+ }
6389
+ }
6390
+ markerPresenceCache.set(value, found);
6391
+ return found;
6392
+ }
6364
6393
  function walkValue(value, scopeTrait, entity, config, state) {
6365
6394
  if (core.isRenderBindingMarker(value)) {
6366
- return { resolved: resolveMarkerExpression(value.expression, entity, config, state), changed: true };
6395
+ const cached = markerResolutionCache.get(value);
6396
+ if (cached !== void 0 && cached.entity === entity && cached.config === config && cached.state === state) {
6397
+ return { resolved: cached.resolved, changed: false };
6398
+ }
6399
+ const resolved = resolveMarkerExpression(value.expression, entity, config, state);
6400
+ markerResolutionCache.set(value, { entity, config, state, resolved });
6401
+ if (resolved !== null && typeof resolved === "object" && !React77__namespace.default.isValidElement(resolved) && !(resolved instanceof Date)) {
6402
+ resolvedMarkerFree.add(resolved);
6403
+ evaluatorResolvedData.add(resolved);
6404
+ }
6405
+ return { resolved, changed: true };
6367
6406
  }
6368
6407
  if (Array.isArray(value)) {
6408
+ if (!subtreeHasMarker(value)) {
6409
+ brandResolved(value);
6410
+ return { resolved: value, changed: false };
6411
+ }
6369
6412
  const out = [];
6370
6413
  let changed = false;
6371
6414
  for (const item of value) {
@@ -6380,9 +6423,14 @@ function walkValue(value, scopeTrait, entity, config, state) {
6380
6423
  out.push(resolved);
6381
6424
  if (itemChanged) changed = true;
6382
6425
  }
6426
+ brandResolved(out);
6383
6427
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
6384
6428
  }
6385
6429
  if (isPlainObject(value)) {
6430
+ if (!subtreeHasMarker(value)) {
6431
+ brandResolved(value);
6432
+ return { resolved: value, changed: false };
6433
+ }
6386
6434
  const sourceTrait = value._sourceTrait;
6387
6435
  if (typeof sourceTrait === "string" && sourceTrait !== scopeTrait) {
6388
6436
  return { resolved: value, changed: false };
@@ -6394,11 +6442,13 @@ function walkValue(value, scopeTrait, entity, config, state) {
6394
6442
  out[key] = resolved;
6395
6443
  if (itemChanged) changed = true;
6396
6444
  }
6445
+ brandResolved(out);
6397
6446
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
6398
6447
  }
6399
6448
  return { resolved: value, changed: false };
6400
6449
  }
6401
6450
  function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
6451
+ if (resolvedMarkerFree.has(props)) return props;
6402
6452
  const out = {};
6403
6453
  let changed = false;
6404
6454
  for (const [key, value] of Object.entries(props)) {
@@ -6406,11 +6456,17 @@ function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
6406
6456
  out[key] = resolved;
6407
6457
  if (propChanged) changed = true;
6408
6458
  }
6459
+ brandResolved(out);
6409
6460
  return changed ? out : props;
6410
6461
  }
6462
+ var markerPresenceCache, markerResolutionCache, resolvedMarkerFree, evaluatorResolvedData;
6411
6463
  var init_resolve_render_bindings = __esm({
6412
6464
  "lib/resolve-render-bindings.ts"() {
6413
6465
  "use client";
6466
+ markerPresenceCache = /* @__PURE__ */ new WeakMap();
6467
+ markerResolutionCache = /* @__PURE__ */ new WeakMap();
6468
+ resolvedMarkerFree = /* @__PURE__ */ new WeakSet();
6469
+ evaluatorResolvedData = /* @__PURE__ */ new WeakSet();
6414
6470
  }
6415
6471
  });
6416
6472
  var sizeClasses6, minWidthClasses, lookStyles2; exports.Modal = void 0;
@@ -8496,6 +8552,10 @@ var init_ComponentPatterns = __esm({
8496
8552
  AlertPattern.displayName = "AlertPattern";
8497
8553
  }
8498
8554
  });
8555
+ var init_perf = __esm({
8556
+ "lib/perf.ts"() {
8557
+ }
8558
+ });
8499
8559
  function themeBodyFont(el) {
8500
8560
  if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
8501
8561
  const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
@@ -8844,6 +8904,7 @@ var init_LearningCanvas = __esm({
8844
8904
  "components/learning/atoms/LearningCanvas.tsx"() {
8845
8905
  "use client";
8846
8906
  init_cn();
8907
+ init_perf();
8847
8908
  init_useEventBus();
8848
8909
  DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
8849
8910
  TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
@@ -8887,6 +8948,7 @@ var init_LearningCanvas = __esm({
8887
8948
  return [...shapes, ...traceOut, ...readoutOut];
8888
8949
  }, [shapes, traces, readouts, width, height]);
8889
8950
  const draw = React77.useCallback(() => {
8951
+ const _perfT = ui.perfStart("learningcanvas:paint");
8890
8952
  const canvas = canvasRef.current;
8891
8953
  if (!canvas) return;
8892
8954
  const ctx = canvas.getContext("2d");
@@ -8908,6 +8970,7 @@ var init_LearningCanvas = __esm({
8908
8970
  for (const shape of derivedShapes) {
8909
8971
  if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
8910
8972
  }
8973
+ ui.perfEnd("learningcanvas:paint", _perfT);
8911
8974
  }, [width, height, backgroundColor, derivedShapes]);
8912
8975
  React77.useEffect(() => {
8913
8976
  draw();
@@ -25790,7 +25853,7 @@ function fileIcon(name) {
25790
25853
  return "file";
25791
25854
  }
25792
25855
  }
25793
- var TreeNodeItem; exports.FileTree = void 0;
25856
+ var TreeNodeItem, FlatTreeNodeItem; exports.FileTree = void 0;
25794
25857
  var init_FileTree = __esm({
25795
25858
  "components/core/molecules/FileTree.tsx"() {
25796
25859
  "use client";
@@ -25875,14 +25938,101 @@ var init_FileTree = __esm({
25875
25938
  )) })
25876
25939
  ] });
25877
25940
  };
25941
+ FlatTreeNodeItem = ({
25942
+ item,
25943
+ depth,
25944
+ indent,
25945
+ childrenByParent,
25946
+ onNodeSelect,
25947
+ defaultExpanded = false
25948
+ }) => {
25949
+ const [expanded, setExpanded] = React77.useState(defaultExpanded || depth < 1);
25950
+ const children = childrenByParent.get(item.id);
25951
+ const hasChildren = !!children && children.length > 0;
25952
+ const handleClick = React77.useCallback(() => {
25953
+ if (hasChildren) setExpanded((prev) => !prev);
25954
+ onNodeSelect?.(item.id);
25955
+ }, [hasChildren, item.id, onNodeSelect]);
25956
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
25957
+ /* @__PURE__ */ jsxRuntime.jsxs(
25958
+ exports.Box,
25959
+ {
25960
+ className: "flex items-center gap-1.5 py-0.5 px-2 cursor-pointer rounded-sm transition-colors hover:bg-muted",
25961
+ style: { paddingLeft: depth * indent + 8 },
25962
+ onClick: handleClick,
25963
+ role: "treeitem",
25964
+ "aria-expanded": hasChildren ? expanded : void 0,
25965
+ children: [
25966
+ hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
25967
+ exports.Icon,
25968
+ {
25969
+ name: expanded ? "chevron-down" : "chevron-right",
25970
+ size: "xs",
25971
+ className: "text-[var(--color-muted-foreground)] flex-shrink-0"
25972
+ }
25973
+ ) : /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { style: { width: 12, flexShrink: 0 } }),
25974
+ /* @__PURE__ */ jsxRuntime.jsx(
25975
+ exports.Icon,
25976
+ {
25977
+ name: item.icon ?? (hasChildren ? expanded ? "folder-open" : "folder" : "file"),
25978
+ size: "xs",
25979
+ className: hasChildren ? "text-[var(--color-warning)]" : "text-[var(--color-muted-foreground)]"
25980
+ }
25981
+ ),
25982
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "truncate font-mono text-xs", children: item.label })
25983
+ ]
25984
+ }
25985
+ ),
25986
+ hasChildren && expanded && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "group", children: children.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
25987
+ FlatTreeNodeItem,
25988
+ {
25989
+ item: child,
25990
+ depth: depth + 1,
25991
+ indent,
25992
+ childrenByParent,
25993
+ onNodeSelect
25994
+ },
25995
+ child.id
25996
+ )) })
25997
+ ] });
25998
+ };
25878
25999
  exports.FileTree = ({
25879
26000
  tree,
26001
+ items,
25880
26002
  selectedPath,
25881
26003
  onFileSelect,
26004
+ onNodeSelect,
25882
26005
  className,
25883
26006
  indent = 16
25884
26007
  }) => {
25885
- if (tree.length === 0) return null;
26008
+ if (items) {
26009
+ if (items.length === 0) return null;
26010
+ const ids = new Set(items.map((node) => node.id));
26011
+ const childrenByParent = /* @__PURE__ */ new Map();
26012
+ const roots = [];
26013
+ for (const item of items) {
26014
+ if (item.parentId && ids.has(item.parentId)) {
26015
+ const siblings = childrenByParent.get(item.parentId);
26016
+ if (siblings) siblings.push(item);
26017
+ else childrenByParent.set(item.parentId, [item]);
26018
+ } else {
26019
+ roots.push(item);
26020
+ }
26021
+ }
26022
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: roots.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
26023
+ FlatTreeNodeItem,
26024
+ {
26025
+ item,
26026
+ depth: 0,
26027
+ indent,
26028
+ childrenByParent,
26029
+ onNodeSelect,
26030
+ defaultExpanded: true
26031
+ },
26032
+ item.id
26033
+ )) });
26034
+ }
26035
+ if (!tree || tree.length === 0) return null;
25886
26036
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: tree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(
25887
26037
  TreeNodeItem,
25888
26038
  {
@@ -27535,7 +27685,7 @@ var init_debug = __esm({
27535
27685
  logger.createLogger("almadar:ui:debug:game-state");
27536
27686
  }
27537
27687
  });
27538
- var isRelationsDebugEnabled; exports.RelationSelect = void 0;
27688
+ var isRelationsDebugEnabled, MANY_CARDINALITIES; exports.RelationSelect = void 0;
27539
27689
  var init_RelationSelect = __esm({
27540
27690
  "components/core/molecules/RelationSelect.tsx"() {
27541
27691
  "use client";
@@ -27549,6 +27699,11 @@ var init_RelationSelect = __esm({
27549
27699
  init_Typography();
27550
27700
  init_debug();
27551
27701
  isRelationsDebugEnabled = () => isDebugEnabled();
27702
+ MANY_CARDINALITIES = [
27703
+ "many",
27704
+ "one-to-many",
27705
+ "many-to-many"
27706
+ ];
27552
27707
  exports.RelationSelect = ({
27553
27708
  value,
27554
27709
  onChange,
@@ -31605,6 +31760,7 @@ var init_MathCanvas = __esm({
31605
31760
  "components/learning/molecules/MathCanvas.tsx"() {
31606
31761
  "use client";
31607
31762
  init_useEventBus();
31763
+ init_perf();
31608
31764
  init_atoms();
31609
31765
  init_Stack();
31610
31766
  init_LearningCanvas();
@@ -31645,17 +31801,21 @@ var init_MathCanvas = __esm({
31645
31801
  error
31646
31802
  }) => {
31647
31803
  const eventBus = useEventBus();
31804
+ const keyMapKey = keyMap ? JSON.stringify(keyMap) : null;
31805
+ const keyUpMapKey = keyUpMap ? JSON.stringify(keyUpMap) : null;
31806
+ const stableKeyMap = React77.useMemo(() => keyMap, [keyMapKey]);
31807
+ const stableKeyUpMap = React77.useMemo(() => keyUpMap, [keyUpMapKey]);
31648
31808
  React77.useEffect(() => {
31649
- if (!keyMap && !keyUpMap) return;
31809
+ if (!stableKeyMap && !stableKeyUpMap) return;
31650
31810
  const onDown = (e) => {
31651
- const ev = keyMap?.[e.code];
31811
+ const ev = stableKeyMap?.[e.code];
31652
31812
  if (ev) {
31653
31813
  eventBus.emit(`UI:${ev}`, {});
31654
31814
  e.preventDefault();
31655
31815
  }
31656
31816
  };
31657
31817
  const onUp = (e) => {
31658
- const ev = keyUpMap?.[e.code];
31818
+ const ev = stableKeyUpMap?.[e.code];
31659
31819
  if (ev) eventBus.emit(`UI:${ev}`, {});
31660
31820
  };
31661
31821
  window.addEventListener("keydown", onDown);
@@ -31664,8 +31824,9 @@ var init_MathCanvas = __esm({
31664
31824
  window.removeEventListener("keydown", onDown);
31665
31825
  window.removeEventListener("keyup", onUp);
31666
31826
  };
31667
- }, [keyMap, keyUpMap, eventBus]);
31827
+ }, [stableKeyMap, stableKeyUpMap, eventBus]);
31668
31828
  const derivedShapes = React77.useMemo(() => {
31829
+ const _perfT = ui.perfStart("mathcanvas:derive");
31669
31830
  const out = [];
31670
31831
  const margin = 24;
31671
31832
  const plotW = width - margin * 2;
@@ -31909,6 +32070,7 @@ var init_MathCanvas = __esm({
31909
32070
  }
31910
32071
  }
31911
32072
  out.push(...shapes);
32073
+ ui.perfEnd("mathcanvas:derive", _perfT);
31912
32074
  return out;
31913
32075
  }, [
31914
32076
  width,
@@ -38262,7 +38424,7 @@ var init_RichBlockEditor = __esm({
38262
38424
  {
38263
38425
  variant: "bordered",
38264
38426
  padding: "none",
38265
- className: cn("flex flex-col", className),
38427
+ className: cn("flex flex-col text-card-foreground", className),
38266
38428
  children: [
38267
38429
  enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
38268
38430
  exports.Box,
@@ -43715,6 +43877,7 @@ var init_DetailPanel = __esm({
43715
43877
  "use client";
43716
43878
  init_atoms();
43717
43879
  init_Box();
43880
+ init_Input();
43718
43881
  init_Stack();
43719
43882
  init_SimpleGrid();
43720
43883
  init_Menu();
@@ -43730,6 +43893,7 @@ var init_DetailPanel = __esm({
43730
43893
  ReactMarkdown2 = React77.lazy(() => import('react-markdown'));
43731
43894
  exports.DetailPanel = ({
43732
43895
  title: propTitle,
43896
+ onTitleCommit,
43733
43897
  subtitle,
43734
43898
  status,
43735
43899
  avatar,
@@ -43751,6 +43915,7 @@ var init_DetailPanel = __esm({
43751
43915
  }) => {
43752
43916
  const eventBus = useEventBus();
43753
43917
  const { t } = hooks.useTranslate();
43918
+ const [titleDraft, setTitleDraft] = React77__namespace.default.useState(null);
43754
43919
  const isFieldDefArray = (arr) => {
43755
43920
  if (!arr || arr.length === 0) return false;
43756
43921
  const first = arr[0];
@@ -43971,6 +44136,50 @@ var init_DetailPanel = __esm({
43971
44136
  }),
43972
44137
  status && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: status.variant ?? "default", children: status.label })
43973
44138
  ] });
44139
+ const commitTitle = () => {
44140
+ if (!onTitleCommit) return;
44141
+ const next = (titleDraft ?? "").trim();
44142
+ setTitleDraft(null);
44143
+ if (!next || next === title) return;
44144
+ onTitleCommit(next, normalizedData?.id !== void 0 ? String(normalizedData.id) : "");
44145
+ };
44146
+ const titleNode = onTitleCommit && titleDraft !== null ? /* @__PURE__ */ jsxRuntime.jsx(
44147
+ exports.Input,
44148
+ {
44149
+ value: titleDraft,
44150
+ autoFocus: true,
44151
+ "aria-label": t("common.title"),
44152
+ className: "h-auto py-1 text-3xl font-bold tracking-tight",
44153
+ onChange: (e) => setTitleDraft(e.target.value),
44154
+ onBlur: commitTitle,
44155
+ onKeyDown: (e) => {
44156
+ if (e.key === "Enter") {
44157
+ e.preventDefault();
44158
+ commitTitle();
44159
+ } else if (e.key === "Escape") {
44160
+ e.preventDefault();
44161
+ setTitleDraft(null);
44162
+ }
44163
+ },
44164
+ "data-testid": "detail-title-input"
44165
+ }
44166
+ ) : onTitleCommit ? /* @__PURE__ */ jsxRuntime.jsx(
44167
+ exports.Box,
44168
+ {
44169
+ role: "button",
44170
+ tabIndex: 0,
44171
+ className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
44172
+ onClick: () => setTitleDraft(title ?? ""),
44173
+ onKeyDown: (e) => {
44174
+ if (e.key === "Enter" || e.key === " ") {
44175
+ e.preventDefault();
44176
+ setTitleDraft(title ?? "");
44177
+ }
44178
+ },
44179
+ "data-testid": "detail-title-editable",
44180
+ children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h2", weight: "bold", children: title || "Details" })
44181
+ }
44182
+ ) : /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h2", weight: "bold", children: title || "Details" });
43974
44183
  const content = /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "md", className: "p-6", children: [
43975
44184
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "start", gap: "md", children: [
43976
44185
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
@@ -43990,7 +44199,7 @@ var init_DetailPanel = __esm({
43990
44199
  avatar,
43991
44200
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: "min-w-0", children: [
43992
44201
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "center", gap: "sm", wrap: true, children: [
43993
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
44202
+ titleNode,
43994
44203
  statusBadges
43995
44204
  ] }),
43996
44205
  subtitle && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "secondary", children: subtitle })
@@ -44326,6 +44535,9 @@ function determineInputType(field) {
44326
44535
  if (field.type === "relation" || field.relation) {
44327
44536
  return "relation";
44328
44537
  }
44538
+ if (field.type === "array") {
44539
+ return "array";
44540
+ }
44329
44541
  if (field.type === "enum" || field.values || getEnumOptions(field).length > 0) {
44330
44542
  return "select";
44331
44543
  }
@@ -44401,6 +44613,7 @@ var init_Form = __esm({
44401
44613
  init_Typography();
44402
44614
  init_Icon();
44403
44615
  init_RelationSelect();
44616
+ init_TagInput();
44404
44617
  init_UploadDropZone();
44405
44618
  init_Alert();
44406
44619
  init_useEventBus();
@@ -44480,7 +44693,7 @@ var init_Form = __esm({
44480
44693
  values: "values" in f3 ? f3.values : void 0,
44481
44694
  min: f3.min,
44482
44695
  max: f3.max,
44483
- relation: "relation" in f3 ? { entity: f3.relation.entity } : void 0
44696
+ relation: "relation" in f3 ? { entity: f3.relation.entity, cardinality: f3.relation.cardinality } : void 0
44484
44697
  })
44485
44698
  );
44486
44699
  }, [entity, fields]);
@@ -44547,7 +44760,7 @@ var init_Form = __esm({
44547
44760
  });
44548
44761
  debug(
44549
44762
  "forms",
44550
- `Calculation triggered: ${calc.variableName} = ${value}`
44763
+ `Calculation triggered: ${calc.variableName} = ${String(value)}`
44551
44764
  );
44552
44765
  }
44553
44766
  });
@@ -44715,7 +44928,7 @@ var init_Form = __esm({
44715
44928
  values: "values" in entityField ? entityField.values : void 0,
44716
44929
  min: entityField.min,
44717
44930
  max: entityField.max,
44718
- relation: "relation" in entityField ? { entity: entityField.relation.entity } : void 0
44931
+ relation: "relation" in entityField ? { entity: entityField.relation.entity, cardinality: entityField.relation.cardinality } : void 0
44719
44932
  };
44720
44933
  }
44721
44934
  return { name: field, type: "string" };
@@ -44827,6 +45040,22 @@ var init_Form = __esm({
44827
45040
  case "relation": {
44828
45041
  const relationOptions = relationsData[fieldName] || [];
44829
45042
  const relationLoading = relationsLoading[fieldName] || false;
45043
+ if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
45044
+ const selectedValues = Array.isArray(currentValue2) ? currentValue2.map((v) => String(v)) : [];
45045
+ return /* @__PURE__ */ jsxRuntime.jsx(
45046
+ exports.Select,
45047
+ {
45048
+ ...commonProps,
45049
+ multiple: true,
45050
+ searchable: true,
45051
+ clearable: true,
45052
+ options: [...relationOptions],
45053
+ value: selectedValues,
45054
+ onValueChange: (value) => handleChange(fieldName, Array.isArray(value) ? value : [value]),
45055
+ placeholder: field.placeholder || `Select ${label}...`
45056
+ }
45057
+ );
45058
+ }
44830
45059
  return /* @__PURE__ */ jsxRuntime.jsx(
44831
45060
  exports.RelationSelect,
44832
45061
  {
@@ -44841,6 +45070,18 @@ var init_Form = __esm({
44841
45070
  }
44842
45071
  );
44843
45072
  }
45073
+ case "array": {
45074
+ const arrayValue = Array.isArray(currentValue2) ? currentValue2.map((v) => String(v)) : currentValue2 != null && currentValue2 !== "" ? [String(currentValue2)] : [];
45075
+ return /* @__PURE__ */ jsxRuntime.jsx(
45076
+ exports.TagInput,
45077
+ {
45078
+ placeholder: field.placeholder,
45079
+ disabled: isLoading,
45080
+ value: arrayValue,
45081
+ onChange: (next) => handleChange(fieldName, [...next])
45082
+ }
45083
+ );
45084
+ }
44844
45085
  case "number":
44845
45086
  return /* @__PURE__ */ jsxRuntime.jsx(
44846
45087
  exports.Input,
@@ -50390,7 +50631,10 @@ function enrichFormFields(fields, entityDef) {
50390
50631
  enriched.values = entityField.enumValues;
50391
50632
  }
50392
50633
  if (entityField.relation) {
50393
- enriched.relation = entityField.relation.entity;
50634
+ enriched.relation = {
50635
+ entity: entityField.relation.entity,
50636
+ cardinality: entityField.relation.cardinality
50637
+ };
50394
50638
  }
50395
50639
  return enriched;
50396
50640
  }
@@ -50418,7 +50662,10 @@ function enrichFormFields(fields, entityDef) {
50418
50662
  }
50419
50663
  }
50420
50664
  if (!obj.relation && entityField.relation) {
50421
- enriched.relation = entityField.relation.entity;
50665
+ enriched.relation = {
50666
+ entity: entityField.relation.entity,
50667
+ cardinality: entityField.relation.cardinality
50668
+ };
50422
50669
  }
50423
50670
  return enriched;
50424
50671
  }
@@ -50433,7 +50680,12 @@ function enrichDetailFields(fields, entityDef) {
50433
50680
  const meta = { type: entityField.type };
50434
50681
  const values = entityField.values ?? entityField.enumValues;
50435
50682
  if (values && values.length > 0) meta.values = values;
50436
- if (entityField.relation) meta.relation = entityField.relation.entity;
50683
+ if (entityField.relation) {
50684
+ meta.relation = {
50685
+ entity: entityField.relation.entity,
50686
+ cardinality: entityField.relation.cardinality
50687
+ };
50688
+ }
50437
50689
  return meta;
50438
50690
  };
50439
50691
  return fields.map((field) => {
@@ -50987,6 +51239,33 @@ function isPlainConfigObject(value) {
50987
51239
  const proto = Object.getPrototypeOf(value);
50988
51240
  return proto === Object.prototype || proto === null;
50989
51241
  }
51242
+ function subtreeHasTraitRef(value) {
51243
+ if (isEvaluatorResolvedData(value)) return false;
51244
+ const cached = traitRefPresenceCache.get(value);
51245
+ if (cached !== void 0) return cached;
51246
+ let found = false;
51247
+ const children = Array.isArray(value) ? value : Object.values(value);
51248
+ for (const child of children) {
51249
+ if (typeof child === "string" && TRAIT_BINDING_RE.test(child)) {
51250
+ found = true;
51251
+ break;
51252
+ }
51253
+ if (core.isRenderBindingMarker(child)) continue;
51254
+ if (Array.isArray(child)) {
51255
+ if (subtreeHasTraitRef(child)) {
51256
+ found = true;
51257
+ break;
51258
+ }
51259
+ } else if (child !== null && typeof child === "object" && isPlainConfigObject(child)) {
51260
+ if (subtreeHasTraitRef(child)) {
51261
+ found = true;
51262
+ break;
51263
+ }
51264
+ }
51265
+ }
51266
+ traitRefPresenceCache.set(value, found);
51267
+ return found;
51268
+ }
50990
51269
  function substituteTraitRefsDeep(value, pathKey) {
50991
51270
  if (core.isRenderBindingMarker(value)) return value;
50992
51271
  if (typeof value === "string") {
@@ -51001,11 +51280,13 @@ function substituteTraitRefsDeep(value, pathKey) {
51001
51280
  return value;
51002
51281
  }
51003
51282
  if (Array.isArray(value)) {
51283
+ if (!subtreeHasTraitRef(value)) return value;
51004
51284
  return value.map(
51005
51285
  (item, i) => substituteTraitRefsDeep(item, `${pathKey}[${i}]`)
51006
51286
  );
51007
51287
  }
51008
51288
  if (typeof value === "object" && isPlainConfigObject(value)) {
51289
+ if (!subtreeHasTraitRef(value)) return value;
51009
51290
  const out = {};
51010
51291
  for (const [k, v] of Object.entries(value)) {
51011
51292
  out[k] = substituteTraitRefsDeep(v, `${pathKey}.${k}`);
@@ -51032,6 +51313,10 @@ function renderPatternProps(props, onDismiss, propsSchema) {
51032
51313
  };
51033
51314
  rendered[key] = /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content: childContent, onDismiss });
51034
51315
  } else if (Array.isArray(value)) {
51316
+ if (!isEvaluatorResolvedData(value) && value.some((el) => isPatternConfig(el))) ; else if (!subtreeHasTraitRef(value)) {
51317
+ rendered[key] = value;
51318
+ continue;
51319
+ }
51035
51320
  const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
51036
51321
  rendered[key] = value.map((item, i) => {
51037
51322
  const el = item;
@@ -51294,7 +51579,7 @@ function UISlotRenderer({
51294
51579
  }
51295
51580
  return wrapped;
51296
51581
  }
51297
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
51582
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
51298
51583
  var init_UISlotRenderer = __esm({
51299
51584
  "components/core/organisms/UISlotRenderer.tsx"() {
51300
51585
  "use client";
@@ -51368,6 +51653,7 @@ var init_UISlotRenderer = __esm({
51368
51653
  "alert",
51369
51654
  "dialog"
51370
51655
  ]);
51656
+ traitRefPresenceCache = /* @__PURE__ */ new WeakMap();
51371
51657
  UISlotRenderer.displayName = "UISlotRenderer";
51372
51658
  }
51373
51659
  });
@@ -52482,6 +52768,88 @@ function useDeepAgentGeneration() {
52482
52768
 
52483
52769
  // hooks/index.ts
52484
52770
  init_useEventBus();
52771
+ function expressionEqual(a, b) {
52772
+ if (Object.is(a, b)) return true;
52773
+ if (Array.isArray(a) && Array.isArray(b)) {
52774
+ if (a.length !== b.length) return false;
52775
+ for (let i = 0; i < a.length; i++) {
52776
+ if (!expressionEqual(a[i], b[i])) return false;
52777
+ }
52778
+ return true;
52779
+ }
52780
+ if (a !== null && b !== null && typeof a === "object" && typeof b === "object" && !Array.isArray(a) && !Array.isArray(b) && !(a instanceof Date) && !(b instanceof Date)) {
52781
+ const aKeys = Object.keys(a);
52782
+ const bKeys = Object.keys(b);
52783
+ if (aKeys.length !== bKeys.length) return false;
52784
+ for (const key of aKeys) {
52785
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
52786
+ if (!expressionEqual(
52787
+ a[key],
52788
+ b[key]
52789
+ )) return false;
52790
+ }
52791
+ return true;
52792
+ }
52793
+ return false;
52794
+ }
52795
+ function isPlainSlotObject(value) {
52796
+ if (value === null || value === void 0 || typeof value !== "object") return false;
52797
+ if (Array.isArray(value)) return false;
52798
+ if (React77__namespace.default.isValidElement(value)) return false;
52799
+ if (value instanceof Date) return false;
52800
+ if (core.isRenderBindingMarker(value)) return false;
52801
+ return true;
52802
+ }
52803
+ function shareValue(prev, next) {
52804
+ if (Object.is(prev, next)) return { value: prev, equal: true };
52805
+ const prevMarker = core.isRenderBindingMarker(prev) ? prev : void 0;
52806
+ const nextMarker = core.isRenderBindingMarker(next) ? next : void 0;
52807
+ if (prevMarker !== void 0 || nextMarker !== void 0) {
52808
+ if (prevMarker !== void 0 && nextMarker !== void 0 && expressionEqual(prevMarker.expression, nextMarker.expression)) {
52809
+ return { value: prev, equal: true };
52810
+ }
52811
+ return { value: next, equal: false };
52812
+ }
52813
+ if (prev instanceof Date || next instanceof Date) {
52814
+ return prev instanceof Date && next instanceof Date && prev.getTime() === next.getTime() ? { value: prev, equal: true } : { value: next, equal: false };
52815
+ }
52816
+ if (typeof prev === "function" || typeof next === "function") return { value: next, equal: false };
52817
+ if (React77__namespace.default.isValidElement(prev) || React77__namespace.default.isValidElement(next)) return { value: next, equal: false };
52818
+ if (Array.isArray(prev) && Array.isArray(next)) {
52819
+ let equal = prev.length === next.length;
52820
+ const out = new Array(next.length);
52821
+ for (let i = 0; i < next.length; i++) {
52822
+ const child = shareValue(prev[i], next[i]);
52823
+ out[i] = child.value;
52824
+ if (!child.equal) equal = false;
52825
+ }
52826
+ return equal ? { value: prev, equal: true } : { value: out, equal: false };
52827
+ }
52828
+ if (isPlainSlotObject(prev) && isPlainSlotObject(next)) {
52829
+ const prevKeys = Object.keys(prev);
52830
+ const nextKeys = Object.keys(next);
52831
+ let equal = prevKeys.length === nextKeys.length;
52832
+ const out = {};
52833
+ for (const key of nextKeys) {
52834
+ if (!Object.prototype.hasOwnProperty.call(prev, key)) {
52835
+ equal = false;
52836
+ out[key] = next[key];
52837
+ continue;
52838
+ }
52839
+ const child = shareValue(prev[key], next[key]);
52840
+ out[key] = child.value;
52841
+ if (!child.equal) equal = false;
52842
+ }
52843
+ return equal ? { value: prev, equal: true } : { value: out, equal: false };
52844
+ }
52845
+ return { value: next, equal: false };
52846
+ }
52847
+ function reconcileSlotProps(prev, next) {
52848
+ const result = shareValue(prev, next);
52849
+ return { value: result.value, equal: result.equal };
52850
+ }
52851
+
52852
+ // hooks/useUISlots.ts
52485
52853
  var log17 = logger.createLogger("almadar:ui:ui-slots");
52486
52854
  var DEFAULT_SOURCE_KEY = "__default__";
52487
52855
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
@@ -52636,6 +53004,14 @@ function useUISlotManager() {
52636
53004
  });
52637
53005
  return prev;
52638
53006
  }
53007
+ if (existing && existing.priority === content.priority && existing.pattern === content.pattern && existing.animation === content.animation && existing.transitionEvent === content.transitionEvent && existing.fromState === content.fromState && existing.entity === content.entity && existing.nodeId === content.nodeId && existing.autoDismissAt === void 0 && content.autoDismissAt === void 0 && existing.onDismiss === void 0 === (content.onDismiss === void 0)) {
53008
+ const reconciled = reconcileSlotProps(existing.props, content.props);
53009
+ if (reconciled.equal) {
53010
+ log17.debug("slot:flush-bail", { slot: config.target, sourceKey, pattern: content.pattern });
53011
+ return prev;
53012
+ }
53013
+ content.props = reconciled.value;
53014
+ }
52639
53015
  const nextSources = {
52640
53016
  ...slotSources,
52641
53017
  [sourceKey]: content
@@ -52983,6 +53359,7 @@ var en_default = {
52983
53359
  "common.confirm": "Are you sure?",
52984
53360
  "common.create": "Create",
52985
53361
  "common.edit": "Edit",
53362
+ "common.title": "Title",
52986
53363
  "common.view": "View",
52987
53364
  "common.add": "Add",
52988
53365
  "common.remove": "Remove",