@almadar/ui 5.158.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,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();
@@ -31721,6 +31760,7 @@ var init_MathCanvas = __esm({
31721
31760
  "components/learning/molecules/MathCanvas.tsx"() {
31722
31761
  "use client";
31723
31762
  init_useEventBus();
31763
+ init_perf();
31724
31764
  init_atoms();
31725
31765
  init_Stack();
31726
31766
  init_LearningCanvas();
@@ -31786,6 +31826,7 @@ var init_MathCanvas = __esm({
31786
31826
  };
31787
31827
  }, [stableKeyMap, stableKeyUpMap, eventBus]);
31788
31828
  const derivedShapes = React77.useMemo(() => {
31829
+ const _perfT = ui.perfStart("mathcanvas:derive");
31789
31830
  const out = [];
31790
31831
  const margin = 24;
31791
31832
  const plotW = width - margin * 2;
@@ -32029,6 +32070,7 @@ var init_MathCanvas = __esm({
32029
32070
  }
32030
32071
  }
32031
32072
  out.push(...shapes);
32073
+ ui.perfEnd("mathcanvas:derive", _perfT);
32032
32074
  return out;
32033
32075
  }, [
32034
32076
  width,
@@ -38382,7 +38424,7 @@ var init_RichBlockEditor = __esm({
38382
38424
  {
38383
38425
  variant: "bordered",
38384
38426
  padding: "none",
38385
- className: cn("flex flex-col", className),
38427
+ className: cn("flex flex-col text-card-foreground", className),
38386
38428
  children: [
38387
38429
  enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
38388
38430
  exports.Box,
@@ -43835,6 +43877,7 @@ var init_DetailPanel = __esm({
43835
43877
  "use client";
43836
43878
  init_atoms();
43837
43879
  init_Box();
43880
+ init_Input();
43838
43881
  init_Stack();
43839
43882
  init_SimpleGrid();
43840
43883
  init_Menu();
@@ -43850,6 +43893,7 @@ var init_DetailPanel = __esm({
43850
43893
  ReactMarkdown2 = React77.lazy(() => import('react-markdown'));
43851
43894
  exports.DetailPanel = ({
43852
43895
  title: propTitle,
43896
+ onTitleCommit,
43853
43897
  subtitle,
43854
43898
  status,
43855
43899
  avatar,
@@ -43871,6 +43915,7 @@ var init_DetailPanel = __esm({
43871
43915
  }) => {
43872
43916
  const eventBus = useEventBus();
43873
43917
  const { t } = hooks.useTranslate();
43918
+ const [titleDraft, setTitleDraft] = React77__namespace.default.useState(null);
43874
43919
  const isFieldDefArray = (arr) => {
43875
43920
  if (!arr || arr.length === 0) return false;
43876
43921
  const first = arr[0];
@@ -44091,6 +44136,50 @@ var init_DetailPanel = __esm({
44091
44136
  }),
44092
44137
  status && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: status.variant ?? "default", children: status.label })
44093
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" });
44094
44183
  const content = /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "md", className: "p-6", children: [
44095
44184
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "start", gap: "md", children: [
44096
44185
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
@@ -44110,7 +44199,7 @@ var init_DetailPanel = __esm({
44110
44199
  avatar,
44111
44200
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: "min-w-0", children: [
44112
44201
  /* @__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" }),
44202
+ titleNode,
44114
44203
  statusBadges
44115
44204
  ] }),
44116
44205
  subtitle && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "secondary", children: subtitle })
@@ -44671,7 +44760,7 @@ var init_Form = __esm({
44671
44760
  });
44672
44761
  debug(
44673
44762
  "forms",
44674
- `Calculation triggered: ${calc.variableName} = ${value}`
44763
+ `Calculation triggered: ${calc.variableName} = ${String(value)}`
44675
44764
  );
44676
44765
  }
44677
44766
  });
@@ -51151,6 +51240,7 @@ function isPlainConfigObject(value) {
51151
51240
  return proto === Object.prototype || proto === null;
51152
51241
  }
51153
51242
  function subtreeHasTraitRef(value) {
51243
+ if (isEvaluatorResolvedData(value)) return false;
51154
51244
  const cached = traitRefPresenceCache.get(value);
51155
51245
  if (cached !== void 0) return cached;
51156
51246
  let found = false;
@@ -51223,6 +51313,10 @@ function renderPatternProps(props, onDismiss, propsSchema) {
51223
51313
  };
51224
51314
  rendered[key] = /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content: childContent, onDismiss });
51225
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
+ }
51226
51320
  const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
51227
51321
  rendered[key] = value.map((item, i) => {
51228
51322
  const el = item;
@@ -52674,6 +52768,88 @@ function useDeepAgentGeneration() {
52674
52768
 
52675
52769
  // hooks/index.ts
52676
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
52677
52853
  var log17 = logger.createLogger("almadar:ui:ui-slots");
52678
52854
  var DEFAULT_SOURCE_KEY = "__default__";
52679
52855
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
@@ -52828,6 +53004,14 @@ function useUISlotManager() {
52828
53004
  });
52829
53005
  return prev;
52830
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
+ }
52831
53015
  const nextSources = {
52832
53016
  ...slotSources,
52833
53017
  [sourceKey]: content
@@ -53175,6 +53359,7 @@ var en_default = {
53175
53359
  "common.confirm": "Are you sure?",
53176
53360
  "common.create": "Create",
53177
53361
  "common.edit": "Edit",
53362
+ "common.title": "Title",
53178
53363
  "common.view": "View",
53179
53364
  "common.add": "Add",
53180
53365
  "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;