@almadar/ui 5.158.0 → 5.160.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,7 +6361,16 @@ 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
+ }
6364
6372
  function subtreeHasMarker(value) {
6373
+ if (resolvedMarkerFree.has(value)) return false;
6365
6374
  const cached = markerPresenceCache.get(value);
6366
6375
  if (cached !== void 0) return cached;
6367
6376
  let found = false;
@@ -6383,10 +6392,23 @@ function subtreeHasMarker(value) {
6383
6392
  }
6384
6393
  function walkValue(value, scopeTrait, entity, config, state) {
6385
6394
  if (core.isRenderBindingMarker(value)) {
6386
- 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 };
6387
6406
  }
6388
6407
  if (Array.isArray(value)) {
6389
- if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
6408
+ if (!subtreeHasMarker(value)) {
6409
+ brandResolved(value);
6410
+ return { resolved: value, changed: false };
6411
+ }
6390
6412
  const out = [];
6391
6413
  let changed = false;
6392
6414
  for (const item of value) {
@@ -6401,10 +6423,14 @@ function walkValue(value, scopeTrait, entity, config, state) {
6401
6423
  out.push(resolved);
6402
6424
  if (itemChanged) changed = true;
6403
6425
  }
6426
+ brandResolved(out);
6404
6427
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
6405
6428
  }
6406
6429
  if (isPlainObject(value)) {
6407
- if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
6430
+ if (!subtreeHasMarker(value)) {
6431
+ brandResolved(value);
6432
+ return { resolved: value, changed: false };
6433
+ }
6408
6434
  const sourceTrait = value._sourceTrait;
6409
6435
  if (typeof sourceTrait === "string" && sourceTrait !== scopeTrait) {
6410
6436
  return { resolved: value, changed: false };
@@ -6416,11 +6442,13 @@ function walkValue(value, scopeTrait, entity, config, state) {
6416
6442
  out[key] = resolved;
6417
6443
  if (itemChanged) changed = true;
6418
6444
  }
6445
+ brandResolved(out);
6419
6446
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
6420
6447
  }
6421
6448
  return { resolved: value, changed: false };
6422
6449
  }
6423
6450
  function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
6451
+ if (resolvedMarkerFree.has(props)) return props;
6424
6452
  const out = {};
6425
6453
  let changed = false;
6426
6454
  for (const [key, value] of Object.entries(props)) {
@@ -6428,13 +6456,17 @@ function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
6428
6456
  out[key] = resolved;
6429
6457
  if (propChanged) changed = true;
6430
6458
  }
6459
+ brandResolved(out);
6431
6460
  return changed ? out : props;
6432
6461
  }
6433
- var markerPresenceCache;
6462
+ var markerPresenceCache, markerResolutionCache, resolvedMarkerFree, evaluatorResolvedData;
6434
6463
  var init_resolve_render_bindings = __esm({
6435
6464
  "lib/resolve-render-bindings.ts"() {
6436
6465
  "use client";
6437
6466
  markerPresenceCache = /* @__PURE__ */ new WeakMap();
6467
+ markerResolutionCache = /* @__PURE__ */ new WeakMap();
6468
+ resolvedMarkerFree = /* @__PURE__ */ new WeakSet();
6469
+ evaluatorResolvedData = /* @__PURE__ */ new WeakSet();
6438
6470
  }
6439
6471
  });
6440
6472
  var sizeClasses6, minWidthClasses, lookStyles2; exports.Modal = void 0;
@@ -8520,6 +8552,10 @@ var init_ComponentPatterns = __esm({
8520
8552
  AlertPattern.displayName = "AlertPattern";
8521
8553
  }
8522
8554
  });
8555
+ var init_perf = __esm({
8556
+ "lib/perf.ts"() {
8557
+ }
8558
+ });
8523
8559
  function themeBodyFont(el) {
8524
8560
  if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
8525
8561
  const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
@@ -8868,6 +8904,7 @@ var init_LearningCanvas = __esm({
8868
8904
  "components/learning/atoms/LearningCanvas.tsx"() {
8869
8905
  "use client";
8870
8906
  init_cn();
8907
+ init_perf();
8871
8908
  init_useEventBus();
8872
8909
  DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
8873
8910
  TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
@@ -8911,6 +8948,7 @@ var init_LearningCanvas = __esm({
8911
8948
  return [...shapes, ...traceOut, ...readoutOut];
8912
8949
  }, [shapes, traces, readouts, width, height]);
8913
8950
  const draw = React77.useCallback(() => {
8951
+ const _perfT = ui.perfStart("learningcanvas:paint");
8914
8952
  const canvas = canvasRef.current;
8915
8953
  if (!canvas) return;
8916
8954
  const ctx = canvas.getContext("2d");
@@ -8932,6 +8970,7 @@ var init_LearningCanvas = __esm({
8932
8970
  for (const shape of derivedShapes) {
8933
8971
  if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
8934
8972
  }
8973
+ ui.perfEnd("learningcanvas:paint", _perfT);
8935
8974
  }, [width, height, backgroundColor, derivedShapes]);
8936
8975
  React77.useEffect(() => {
8937
8976
  draw();
@@ -24262,8 +24301,8 @@ function DataGrid({
24262
24301
  }
24263
24302
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center", children: [
24264
24303
  field.icon && renderIconInput(field.icon, { size: "xs", className: "text-muted-foreground" }),
24265
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
24266
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", children: formatValue(value, field.format) })
24304
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", className: "sr-only", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
24305
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", children: formatValue(value, field.format) })
24267
24306
  ] }, field.name);
24268
24307
  }) })
24269
24308
  ] }) })
@@ -24691,7 +24730,11 @@ function DataList({
24691
24730
  exports.Box,
24692
24731
  {
24693
24732
  className: cn(
24694
- "group flex items-center gap-4 transition-all duration-fast",
24733
+ // items-start, not items-center: a multi-line row (title + meta +
24734
+ // progress) centred its action cluster in the vertical middle, so
24735
+ // the buttons floated between the meta fields instead of anchoring
24736
+ // to the title they act on (U-DATALIST-ACTIONS-FLOAT-MID-ROW).
24737
+ "group flex items-start gap-4 transition-all duration-fast",
24695
24738
  isCompact ? "px-4 py-2" : "px-6 py-4",
24696
24739
  "hover:bg-muted/80",
24697
24740
  !isCard && !isCompact && "rounded-lg border border-transparent hover:border-border"
@@ -24722,11 +24765,19 @@ function DataList({
24722
24765
  if (value === void 0 || value === null || value === "") return null;
24723
24766
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center", children: [
24724
24767
  field.icon && renderIconInput2(field.icon, { size: "xs", className: "text-muted-foreground" }),
24725
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", color: "secondary", children: [
24726
- field.label ?? fieldLabel3(field.name),
24727
- ":"
24728
- ] }),
24729
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
24768
+ /* @__PURE__ */ jsxRuntime.jsxs(
24769
+ exports.Typography,
24770
+ {
24771
+ variant: "caption",
24772
+ color: "secondary",
24773
+ className: cn(field.format !== "boolean" && "sr-only"),
24774
+ children: [
24775
+ field.label ?? fieldLabel3(field.name),
24776
+ ":"
24777
+ ]
24778
+ }
24779
+ ),
24780
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
24730
24781
  ] }, field.name);
24731
24782
  }) }),
24732
24783
  progressFields.map((field) => {
@@ -24745,7 +24796,7 @@ function DataList({
24745
24796
  ]
24746
24797
  }
24747
24798
  ),
24748
- isCard && !isLast && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "mx-6 border-b border-border/40" })
24799
+ (isCard || isCompact) && !isLast && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("border-b border-border/40", isCompact ? "mx-4" : "mx-6") })
24749
24800
  ] }, id)
24750
24801
  );
24751
24802
  };
@@ -24755,7 +24806,13 @@ function DataList({
24755
24806
  {
24756
24807
  className: cn(
24757
24808
  isCard && "bg-card rounded-xl border border-border shadow-elevation-dialog overflow-hidden",
24758
- !isCard && gapClass,
24809
+ // `gap-*` is inert on a block container, and Box only emits a display
24810
+ // class when its `display` prop is set — so every non-card list had
24811
+ // been asking for a gap that CSS silently dropped. flex-col makes it
24812
+ // real. `compact` keeps gap-0 on purpose: it separates with the row
24813
+ // divider above instead, never both (Almadar_UI_Beauty.md 6).
24814
+ !isCard && "flex flex-col",
24815
+ !isCard && !isCompact && gapClass,
24759
24816
  listLookStyles[look],
24760
24817
  className
24761
24818
  ),
@@ -31721,6 +31778,7 @@ var init_MathCanvas = __esm({
31721
31778
  "components/learning/molecules/MathCanvas.tsx"() {
31722
31779
  "use client";
31723
31780
  init_useEventBus();
31781
+ init_perf();
31724
31782
  init_atoms();
31725
31783
  init_Stack();
31726
31784
  init_LearningCanvas();
@@ -31786,6 +31844,7 @@ var init_MathCanvas = __esm({
31786
31844
  };
31787
31845
  }, [stableKeyMap, stableKeyUpMap, eventBus]);
31788
31846
  const derivedShapes = React77.useMemo(() => {
31847
+ const _perfT = ui.perfStart("mathcanvas:derive");
31789
31848
  const out = [];
31790
31849
  const margin = 24;
31791
31850
  const plotW = width - margin * 2;
@@ -32029,6 +32088,7 @@ var init_MathCanvas = __esm({
32029
32088
  }
32030
32089
  }
32031
32090
  out.push(...shapes);
32091
+ ui.perfEnd("mathcanvas:derive", _perfT);
32032
32092
  return out;
32033
32093
  }, [
32034
32094
  width,
@@ -38382,7 +38442,7 @@ var init_RichBlockEditor = __esm({
38382
38442
  {
38383
38443
  variant: "bordered",
38384
38444
  padding: "none",
38385
- className: cn("flex flex-col", className),
38445
+ className: cn("flex flex-col text-card-foreground", className),
38386
38446
  children: [
38387
38447
  enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
38388
38448
  exports.Box,
@@ -43835,6 +43895,7 @@ var init_DetailPanel = __esm({
43835
43895
  "use client";
43836
43896
  init_atoms();
43837
43897
  init_Box();
43898
+ init_Input();
43838
43899
  init_Stack();
43839
43900
  init_SimpleGrid();
43840
43901
  init_Menu();
@@ -43850,6 +43911,7 @@ var init_DetailPanel = __esm({
43850
43911
  ReactMarkdown2 = React77.lazy(() => import('react-markdown'));
43851
43912
  exports.DetailPanel = ({
43852
43913
  title: propTitle,
43914
+ onTitleCommit,
43853
43915
  subtitle,
43854
43916
  status,
43855
43917
  avatar,
@@ -43871,6 +43933,7 @@ var init_DetailPanel = __esm({
43871
43933
  }) => {
43872
43934
  const eventBus = useEventBus();
43873
43935
  const { t } = hooks.useTranslate();
43936
+ const [titleDraft, setTitleDraft] = React77__namespace.default.useState(null);
43874
43937
  const isFieldDefArray = (arr) => {
43875
43938
  if (!arr || arr.length === 0) return false;
43876
43939
  const first = arr[0];
@@ -44091,6 +44154,50 @@ var init_DetailPanel = __esm({
44091
44154
  }),
44092
44155
  status && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: status.variant ?? "default", children: status.label })
44093
44156
  ] });
44157
+ const commitTitle = () => {
44158
+ if (!onTitleCommit) return;
44159
+ const next = (titleDraft ?? "").trim();
44160
+ setTitleDraft(null);
44161
+ if (!next || next === title) return;
44162
+ onTitleCommit(next, normalizedData?.id !== void 0 ? String(normalizedData.id) : "");
44163
+ };
44164
+ const titleNode = onTitleCommit && titleDraft !== null ? /* @__PURE__ */ jsxRuntime.jsx(
44165
+ exports.Input,
44166
+ {
44167
+ value: titleDraft,
44168
+ autoFocus: true,
44169
+ "aria-label": t("common.title"),
44170
+ className: "h-auto py-1 text-3xl font-bold tracking-tight",
44171
+ onChange: (e) => setTitleDraft(e.target.value),
44172
+ onBlur: commitTitle,
44173
+ onKeyDown: (e) => {
44174
+ if (e.key === "Enter") {
44175
+ e.preventDefault();
44176
+ commitTitle();
44177
+ } else if (e.key === "Escape") {
44178
+ e.preventDefault();
44179
+ setTitleDraft(null);
44180
+ }
44181
+ },
44182
+ "data-testid": "detail-title-input"
44183
+ }
44184
+ ) : onTitleCommit ? /* @__PURE__ */ jsxRuntime.jsx(
44185
+ exports.Box,
44186
+ {
44187
+ role: "button",
44188
+ tabIndex: 0,
44189
+ className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
44190
+ onClick: () => setTitleDraft(title ?? ""),
44191
+ onKeyDown: (e) => {
44192
+ if (e.key === "Enter" || e.key === " ") {
44193
+ e.preventDefault();
44194
+ setTitleDraft(title ?? "");
44195
+ }
44196
+ },
44197
+ "data-testid": "detail-title-editable",
44198
+ children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h2", weight: "bold", children: title || "Details" })
44199
+ }
44200
+ ) : /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h2", weight: "bold", children: title || "Details" });
44094
44201
  const content = /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "md", className: "p-6", children: [
44095
44202
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "start", gap: "md", children: [
44096
44203
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
@@ -44110,7 +44217,7 @@ var init_DetailPanel = __esm({
44110
44217
  avatar,
44111
44218
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: "min-w-0", children: [
44112
44219
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "center", gap: "sm", wrap: true, children: [
44113
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
44220
+ titleNode,
44114
44221
  statusBadges
44115
44222
  ] }),
44116
44223
  subtitle && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "secondary", children: subtitle })
@@ -44210,16 +44317,11 @@ var init_DetailPanel = __esm({
44210
44317
  footer
44211
44318
  ] })
44212
44319
  ] }) });
44213
- return /* @__PURE__ */ jsxRuntime.jsx(
44214
- exports.Box,
44215
- {
44216
- className: cn(
44217
- slideOver && "fixed inset-y-0 right-0 w-full max-w-2xl bg-card shadow-lg z-50 overflow-y-auto p-6",
44218
- className
44219
- ),
44220
- children: content
44221
- }
44222
- );
44320
+ if (!slideOver) {
44321
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className, children: content });
44322
+ }
44323
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("fixed inset-y-0 right-0 w-full max-w-2xl bg-card shadow-lg z-50 overflow-y-auto p-6", className), children: content });
44324
+ return typeof document === "undefined" ? panel : reactDom.createPortal(panel, document.body);
44223
44325
  };
44224
44326
  exports.DetailPanel.displayName = "DetailPanel";
44225
44327
  }
@@ -44671,7 +44773,7 @@ var init_Form = __esm({
44671
44773
  });
44672
44774
  debug(
44673
44775
  "forms",
44674
- `Calculation triggered: ${calc.variableName} = ${value}`
44776
+ `Calculation triggered: ${calc.variableName} = ${String(value)}`
44675
44777
  );
44676
44778
  }
44677
44779
  });
@@ -51151,6 +51253,7 @@ function isPlainConfigObject(value) {
51151
51253
  return proto === Object.prototype || proto === null;
51152
51254
  }
51153
51255
  function subtreeHasTraitRef(value) {
51256
+ if (isEvaluatorResolvedData(value)) return false;
51154
51257
  const cached = traitRefPresenceCache.get(value);
51155
51258
  if (cached !== void 0) return cached;
51156
51259
  let found = false;
@@ -51223,6 +51326,10 @@ function renderPatternProps(props, onDismiss, propsSchema) {
51223
51326
  };
51224
51327
  rendered[key] = /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content: childContent, onDismiss });
51225
51328
  } else if (Array.isArray(value)) {
51329
+ if (!isEvaluatorResolvedData(value) && value.some((el) => isPatternConfig(el))) ; else if (!subtreeHasTraitRef(value)) {
51330
+ rendered[key] = value;
51331
+ continue;
51332
+ }
51226
51333
  const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
51227
51334
  rendered[key] = value.map((item, i) => {
51228
51335
  const el = item;
@@ -52674,6 +52781,88 @@ function useDeepAgentGeneration() {
52674
52781
 
52675
52782
  // hooks/index.ts
52676
52783
  init_useEventBus();
52784
+ function expressionEqual(a, b) {
52785
+ if (Object.is(a, b)) return true;
52786
+ if (Array.isArray(a) && Array.isArray(b)) {
52787
+ if (a.length !== b.length) return false;
52788
+ for (let i = 0; i < a.length; i++) {
52789
+ if (!expressionEqual(a[i], b[i])) return false;
52790
+ }
52791
+ return true;
52792
+ }
52793
+ if (a !== null && b !== null && typeof a === "object" && typeof b === "object" && !Array.isArray(a) && !Array.isArray(b) && !(a instanceof Date) && !(b instanceof Date)) {
52794
+ const aKeys = Object.keys(a);
52795
+ const bKeys = Object.keys(b);
52796
+ if (aKeys.length !== bKeys.length) return false;
52797
+ for (const key of aKeys) {
52798
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
52799
+ if (!expressionEqual(
52800
+ a[key],
52801
+ b[key]
52802
+ )) return false;
52803
+ }
52804
+ return true;
52805
+ }
52806
+ return false;
52807
+ }
52808
+ function isPlainSlotObject(value) {
52809
+ if (value === null || value === void 0 || typeof value !== "object") return false;
52810
+ if (Array.isArray(value)) return false;
52811
+ if (React77__namespace.default.isValidElement(value)) return false;
52812
+ if (value instanceof Date) return false;
52813
+ if (core.isRenderBindingMarker(value)) return false;
52814
+ return true;
52815
+ }
52816
+ function shareValue(prev, next) {
52817
+ if (Object.is(prev, next)) return { value: prev, equal: true };
52818
+ const prevMarker = core.isRenderBindingMarker(prev) ? prev : void 0;
52819
+ const nextMarker = core.isRenderBindingMarker(next) ? next : void 0;
52820
+ if (prevMarker !== void 0 || nextMarker !== void 0) {
52821
+ if (prevMarker !== void 0 && nextMarker !== void 0 && expressionEqual(prevMarker.expression, nextMarker.expression)) {
52822
+ return { value: prev, equal: true };
52823
+ }
52824
+ return { value: next, equal: false };
52825
+ }
52826
+ if (prev instanceof Date || next instanceof Date) {
52827
+ return prev instanceof Date && next instanceof Date && prev.getTime() === next.getTime() ? { value: prev, equal: true } : { value: next, equal: false };
52828
+ }
52829
+ if (typeof prev === "function" || typeof next === "function") return { value: next, equal: false };
52830
+ if (React77__namespace.default.isValidElement(prev) || React77__namespace.default.isValidElement(next)) return { value: next, equal: false };
52831
+ if (Array.isArray(prev) && Array.isArray(next)) {
52832
+ let equal = prev.length === next.length;
52833
+ const out = new Array(next.length);
52834
+ for (let i = 0; i < next.length; i++) {
52835
+ const child = shareValue(prev[i], next[i]);
52836
+ out[i] = child.value;
52837
+ if (!child.equal) equal = false;
52838
+ }
52839
+ return equal ? { value: prev, equal: true } : { value: out, equal: false };
52840
+ }
52841
+ if (isPlainSlotObject(prev) && isPlainSlotObject(next)) {
52842
+ const prevKeys = Object.keys(prev);
52843
+ const nextKeys = Object.keys(next);
52844
+ let equal = prevKeys.length === nextKeys.length;
52845
+ const out = {};
52846
+ for (const key of nextKeys) {
52847
+ if (!Object.prototype.hasOwnProperty.call(prev, key)) {
52848
+ equal = false;
52849
+ out[key] = next[key];
52850
+ continue;
52851
+ }
52852
+ const child = shareValue(prev[key], next[key]);
52853
+ out[key] = child.value;
52854
+ if (!child.equal) equal = false;
52855
+ }
52856
+ return equal ? { value: prev, equal: true } : { value: out, equal: false };
52857
+ }
52858
+ return { value: next, equal: false };
52859
+ }
52860
+ function reconcileSlotProps(prev, next) {
52861
+ const result = shareValue(prev, next);
52862
+ return { value: result.value, equal: result.equal };
52863
+ }
52864
+
52865
+ // hooks/useUISlots.ts
52677
52866
  var log17 = logger.createLogger("almadar:ui:ui-slots");
52678
52867
  var DEFAULT_SOURCE_KEY = "__default__";
52679
52868
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
@@ -52828,6 +53017,14 @@ function useUISlotManager() {
52828
53017
  });
52829
53018
  return prev;
52830
53019
  }
53020
+ 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)) {
53021
+ const reconciled = reconcileSlotProps(existing.props, content.props);
53022
+ if (reconciled.equal) {
53023
+ log17.debug("slot:flush-bail", { slot: config.target, sourceKey, pattern: content.pattern });
53024
+ return prev;
53025
+ }
53026
+ content.props = reconciled.value;
53027
+ }
52831
53028
  const nextSources = {
52832
53029
  ...slotSources,
52833
53030
  [sourceKey]: content
@@ -53175,6 +53372,7 @@ var en_default = {
53175
53372
  "common.confirm": "Are you sure?",
53176
53373
  "common.create": "Create",
53177
53374
  "common.edit": "Edit",
53375
+ "common.title": "Title",
53178
53376
  "common.view": "View",
53179
53377
  "common.add": "Add",
53180
53378
  "common.remove": "Remove",
@@ -10814,6 +10814,14 @@ interface DetailPanelProps extends DisplayStateProps {
10814
10814
  /** RECORD-cardinality: renders ONE record (see body collapse below). */
10815
10815
  entity?: EntityRow;
10816
10816
  title?: string;
10817
+ /**
10818
+ * When bound, the title becomes editable in place: clicking it swaps the
10819
+ * heading for an input, Enter or blur commits, Escape reverts. Receives the
10820
+ * committed title and the record's id so the host can persist a partial
10821
+ * update. Unbound, the title stays a plain heading.
10822
+ * @offByDefault
10823
+ */
10824
+ onTitleCommit?: (title: string, id: string) => void;
10817
10825
  subtitle?: string;
10818
10826
  status?: DetailPanelStatus;
10819
10827
  avatar?: React__default.ReactNode;
@@ -10814,6 +10814,14 @@ interface DetailPanelProps extends DisplayStateProps {
10814
10814
  /** RECORD-cardinality: renders ONE record (see body collapse below). */
10815
10815
  entity?: EntityRow;
10816
10816
  title?: string;
10817
+ /**
10818
+ * When bound, the title becomes editable in place: clicking it swaps the
10819
+ * heading for an input, Enter or blur commits, Escape reverts. Receives the
10820
+ * committed title and the record's id so the host can persist a partial
10821
+ * update. Unbound, the title stays a plain heading.
10822
+ * @offByDefault
10823
+ */
10824
+ onTitleCommit?: (title: string, id: string) => void;
10817
10825
  subtitle?: string;
10818
10826
  status?: DetailPanelStatus;
10819
10827
  avatar?: React__default.ReactNode;