@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.
@@ -272,4 +272,4 @@ declare function useEntityBindingSnapshot(traitName: string | undefined): {
272
272
  state: string;
273
273
  };
274
274
 
275
- export { EntityBindingContext as E, type SendEventResult as S, TraitContext as T, type EntityBindingSource as a, type EntitySchemaContextValue as b, EntitySchemaProvider as c, type EntitySchemaProviderProps as d, type ServerBridgeContextValue as e, ServerBridgeProvider as f, type ServerBridgeProviderProps as g, type ServerBridgeTransport as h, type ServerClientEffect as i, type ServerResponseMeta as j, type TraitContextValue as k, type TraitInstance as l, TraitProvider as m, type TraitProviderProps as n, useEntitySchema as o, useEntitySchemaOptional as p, useServerBridge as q, useTrait as r, useTraitContext as s, useEntityBindingSnapshot as u };
275
+ export { type EntityBindingSource as E, type ServerBridgeTransport as S, TraitContext as T, type EntitySchemaContextValue as a, EntitySchemaProvider as b, type EntitySchemaProviderProps as c, type ServerBridgeContextValue as d, ServerBridgeProvider as e, type ServerClientEffect as f, type TraitContextValue as g, type TraitInstance as h, TraitProvider as i, type TraitProviderProps as j, useEntitySchemaOptional as k, useServerBridge as l, useTrait as m, useTraitContext as n, EntityBindingContext as o, type SendEventResult as p, type ServerBridgeProviderProps as q, type ServerResponseMeta as r, useEntityBindingSnapshot as s, useEntitySchema as u };
@@ -272,4 +272,4 @@ declare function useEntityBindingSnapshot(traitName: string | undefined): {
272
272
  state: string;
273
273
  };
274
274
 
275
- export { EntityBindingContext as E, type SendEventResult as S, TraitContext as T, type EntityBindingSource as a, type EntitySchemaContextValue as b, EntitySchemaProvider as c, type EntitySchemaProviderProps as d, type ServerBridgeContextValue as e, ServerBridgeProvider as f, type ServerBridgeProviderProps as g, type ServerBridgeTransport as h, type ServerClientEffect as i, type ServerResponseMeta as j, type TraitContextValue as k, type TraitInstance as l, TraitProvider as m, type TraitProviderProps as n, useEntitySchema as o, useEntitySchemaOptional as p, useServerBridge as q, useTrait as r, useTraitContext as s, useEntityBindingSnapshot as u };
275
+ export { type EntityBindingSource as E, type ServerBridgeTransport as S, TraitContext as T, type EntitySchemaContextValue as a, EntitySchemaProvider as b, type EntitySchemaProviderProps as c, type ServerBridgeContextValue as d, ServerBridgeProvider as e, type ServerClientEffect as f, type TraitContextValue as g, type TraitInstance as h, TraitProvider as i, type TraitProviderProps as j, useEntitySchemaOptional as k, useServerBridge as l, useTrait as m, useTraitContext as n, EntityBindingContext as o, type SendEventResult as p, type ServerBridgeProviderProps as q, type ServerResponseMeta as r, useEntityBindingSnapshot as s, useEntitySchema as u };
@@ -4664,7 +4664,16 @@ function isPlainObject(value) {
4664
4664
  if (typeof value === "function") return false;
4665
4665
  return true;
4666
4666
  }
4667
+ function isEvaluatorResolvedData(value) {
4668
+ return evaluatorResolvedData.has(value);
4669
+ }
4670
+ function brandResolved(value) {
4671
+ if (value !== null && typeof value === "object" && !React94__namespace.default.isValidElement(value) && !(value instanceof Date)) {
4672
+ resolvedMarkerFree.add(value);
4673
+ }
4674
+ }
4667
4675
  function subtreeHasMarker(value) {
4676
+ if (resolvedMarkerFree.has(value)) return false;
4668
4677
  const cached = markerPresenceCache.get(value);
4669
4678
  if (cached !== void 0) return cached;
4670
4679
  let found = false;
@@ -4686,10 +4695,23 @@ function subtreeHasMarker(value) {
4686
4695
  }
4687
4696
  function walkValue(value, scopeTrait, entity, config, state) {
4688
4697
  if (core.isRenderBindingMarker(value)) {
4689
- return { resolved: resolveMarkerExpression(value.expression, entity, config, state), changed: true };
4698
+ const cached = markerResolutionCache.get(value);
4699
+ if (cached !== void 0 && cached.entity === entity && cached.config === config && cached.state === state) {
4700
+ return { resolved: cached.resolved, changed: false };
4701
+ }
4702
+ const resolved = resolveMarkerExpression(value.expression, entity, config, state);
4703
+ markerResolutionCache.set(value, { entity, config, state, resolved });
4704
+ if (resolved !== null && typeof resolved === "object" && !React94__namespace.default.isValidElement(resolved) && !(resolved instanceof Date)) {
4705
+ resolvedMarkerFree.add(resolved);
4706
+ evaluatorResolvedData.add(resolved);
4707
+ }
4708
+ return { resolved, changed: true };
4690
4709
  }
4691
4710
  if (Array.isArray(value)) {
4692
- if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
4711
+ if (!subtreeHasMarker(value)) {
4712
+ brandResolved(value);
4713
+ return { resolved: value, changed: false };
4714
+ }
4693
4715
  const out = [];
4694
4716
  let changed = false;
4695
4717
  for (const item of value) {
@@ -4704,10 +4726,14 @@ function walkValue(value, scopeTrait, entity, config, state) {
4704
4726
  out.push(resolved);
4705
4727
  if (itemChanged) changed = true;
4706
4728
  }
4729
+ brandResolved(out);
4707
4730
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
4708
4731
  }
4709
4732
  if (isPlainObject(value)) {
4710
- if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
4733
+ if (!subtreeHasMarker(value)) {
4734
+ brandResolved(value);
4735
+ return { resolved: value, changed: false };
4736
+ }
4711
4737
  const sourceTrait = value._sourceTrait;
4712
4738
  if (typeof sourceTrait === "string" && sourceTrait !== scopeTrait) {
4713
4739
  return { resolved: value, changed: false };
@@ -4719,11 +4745,13 @@ function walkValue(value, scopeTrait, entity, config, state) {
4719
4745
  out[key] = resolved;
4720
4746
  if (itemChanged) changed = true;
4721
4747
  }
4748
+ brandResolved(out);
4722
4749
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
4723
4750
  }
4724
4751
  return { resolved: value, changed: false };
4725
4752
  }
4726
4753
  function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
4754
+ if (resolvedMarkerFree.has(props)) return props;
4727
4755
  const out = {};
4728
4756
  let changed = false;
4729
4757
  for (const [key, value] of Object.entries(props)) {
@@ -4731,13 +4759,17 @@ function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
4731
4759
  out[key] = resolved;
4732
4760
  if (propChanged) changed = true;
4733
4761
  }
4762
+ brandResolved(out);
4734
4763
  return changed ? out : props;
4735
4764
  }
4736
- var markerPresenceCache;
4765
+ var markerPresenceCache, markerResolutionCache, resolvedMarkerFree, evaluatorResolvedData;
4737
4766
  var init_resolve_render_bindings = __esm({
4738
4767
  "lib/resolve-render-bindings.ts"() {
4739
4768
  "use client";
4740
4769
  markerPresenceCache = /* @__PURE__ */ new WeakMap();
4770
+ markerResolutionCache = /* @__PURE__ */ new WeakMap();
4771
+ resolvedMarkerFree = /* @__PURE__ */ new WeakSet();
4772
+ evaluatorResolvedData = /* @__PURE__ */ new WeakSet();
4741
4773
  }
4742
4774
  });
4743
4775
  function getCurrentIconFamily() {
@@ -15342,6 +15374,19 @@ var init_molecules = __esm({
15342
15374
  init_puzzleObject();
15343
15375
  }
15344
15376
  });
15377
+ var profilerOnRender;
15378
+ var init_perf = __esm({
15379
+ "lib/perf.ts"() {
15380
+ profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
15381
+ ui.pushPerfEntry({
15382
+ name: `profiler:${id}:${phase}`,
15383
+ durationMs: actualDuration,
15384
+ ts: commitTime,
15385
+ detail: { baseDuration }
15386
+ });
15387
+ };
15388
+ }
15389
+ });
15345
15390
  function themeBodyFont(el) {
15346
15391
  if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
15347
15392
  const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
@@ -15690,6 +15735,7 @@ var init_LearningCanvas = __esm({
15690
15735
  "components/learning/atoms/LearningCanvas.tsx"() {
15691
15736
  "use client";
15692
15737
  init_cn();
15738
+ init_perf();
15693
15739
  init_useEventBus();
15694
15740
  DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
15695
15741
  TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
@@ -15733,6 +15779,7 @@ var init_LearningCanvas = __esm({
15733
15779
  return [...shapes, ...traceOut, ...readoutOut];
15734
15780
  }, [shapes, traces, readouts, width, height]);
15735
15781
  const draw = React94.useCallback(() => {
15782
+ const _perfT = ui.perfStart("learningcanvas:paint");
15736
15783
  const canvas = canvasRef.current;
15737
15784
  if (!canvas) return;
15738
15785
  const ctx = canvas.getContext("2d");
@@ -15754,6 +15801,7 @@ var init_LearningCanvas = __esm({
15754
15801
  for (const shape of derivedShapes) {
15755
15802
  if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
15756
15803
  }
15804
+ ui.perfEnd("learningcanvas:paint", _perfT);
15757
15805
  }, [width, height, backgroundColor, derivedShapes]);
15758
15806
  React94.useEffect(() => {
15759
15807
  draw();
@@ -28840,8 +28888,8 @@ function DataGrid({
28840
28888
  }
28841
28889
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
28842
28890
  field.icon && renderIconInput(field.icon, { size: "xs", className: "text-muted-foreground" }),
28843
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
28844
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", children: formatValue(value, field.format) })
28891
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "sr-only", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
28892
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: formatValue(value, field.format) })
28845
28893
  ] }, field.name);
28846
28894
  }) })
28847
28895
  ] }) })
@@ -29269,7 +29317,11 @@ function DataList({
29269
29317
  Box,
29270
29318
  {
29271
29319
  className: cn(
29272
- "group flex items-center gap-4 transition-all duration-fast",
29320
+ // items-start, not items-center: a multi-line row (title + meta +
29321
+ // progress) centred its action cluster in the vertical middle, so
29322
+ // the buttons floated between the meta fields instead of anchoring
29323
+ // to the title they act on (U-DATALIST-ACTIONS-FLOAT-MID-ROW).
29324
+ "group flex items-start gap-4 transition-all duration-fast",
29273
29325
  isCompact ? "px-4 py-2" : "px-6 py-4",
29274
29326
  "hover:bg-muted/80",
29275
29327
  !isCard && !isCompact && "rounded-lg border border-transparent hover:border-border"
@@ -29300,11 +29352,19 @@ function DataList({
29300
29352
  if (value === void 0 || value === null || value === "") return null;
29301
29353
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
29302
29354
  field.icon && renderIconInput2(field.icon, { size: "xs", className: "text-muted-foreground" }),
29303
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "secondary", children: [
29304
- field.label ?? fieldLabel3(field.name),
29305
- ":"
29306
- ] }),
29307
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
29355
+ /* @__PURE__ */ jsxRuntime.jsxs(
29356
+ Typography,
29357
+ {
29358
+ variant: "caption",
29359
+ color: "secondary",
29360
+ className: cn(field.format !== "boolean" && "sr-only"),
29361
+ children: [
29362
+ field.label ?? fieldLabel3(field.name),
29363
+ ":"
29364
+ ]
29365
+ }
29366
+ ),
29367
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
29308
29368
  ] }, field.name);
29309
29369
  }) }),
29310
29370
  progressFields.map((field) => {
@@ -29323,7 +29383,7 @@ function DataList({
29323
29383
  ]
29324
29384
  }
29325
29385
  ),
29326
- isCard && !isLast && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-6 border-b border-border/40" })
29386
+ (isCard || isCompact) && !isLast && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("border-b border-border/40", isCompact ? "mx-4" : "mx-6") })
29327
29387
  ] }, id)
29328
29388
  );
29329
29389
  };
@@ -29333,7 +29393,13 @@ function DataList({
29333
29393
  {
29334
29394
  className: cn(
29335
29395
  isCard && "bg-card rounded-xl border border-border shadow-elevation-dialog overflow-hidden",
29336
- !isCard && gapClass,
29396
+ // `gap-*` is inert on a block container, and Box only emits a display
29397
+ // class when its `display` prop is set — so every non-card list had
29398
+ // been asking for a gap that CSS silently dropped. flex-col makes it
29399
+ // real. `compact` keeps gap-0 on purpose: it separates with the row
29400
+ // divider above instead, never both (Almadar_UI_Beauty.md 6).
29401
+ !isCard && "flex flex-col",
29402
+ !isCard && !isCompact && gapClass,
29337
29403
  listLookStyles[look],
29338
29404
  className
29339
29405
  ),
@@ -32948,6 +33014,7 @@ var init_MathCanvas = __esm({
32948
33014
  "components/learning/molecules/MathCanvas.tsx"() {
32949
33015
  "use client";
32950
33016
  init_useEventBus();
33017
+ init_perf();
32951
33018
  init_atoms();
32952
33019
  init_Stack();
32953
33020
  init_LearningCanvas();
@@ -33013,6 +33080,7 @@ var init_MathCanvas = __esm({
33013
33080
  };
33014
33081
  }, [stableKeyMap, stableKeyUpMap, eventBus]);
33015
33082
  const derivedShapes = React94.useMemo(() => {
33083
+ const _perfT = ui.perfStart("mathcanvas:derive");
33016
33084
  const out = [];
33017
33085
  const margin = 24;
33018
33086
  const plotW = width - margin * 2;
@@ -33256,6 +33324,7 @@ var init_MathCanvas = __esm({
33256
33324
  }
33257
33325
  }
33258
33326
  out.push(...shapes);
33327
+ ui.perfEnd("mathcanvas:derive", _perfT);
33259
33328
  return out;
33260
33329
  }, [
33261
33330
  width,
@@ -39609,7 +39678,7 @@ var init_RichBlockEditor = __esm({
39609
39678
  {
39610
39679
  variant: "bordered",
39611
39680
  padding: "none",
39612
- className: cn("flex flex-col", className),
39681
+ className: cn("flex flex-col text-card-foreground", className),
39613
39682
  children: [
39614
39683
  enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
39615
39684
  Box,
@@ -44428,6 +44497,7 @@ var init_DetailPanel = __esm({
44428
44497
  "use client";
44429
44498
  init_atoms();
44430
44499
  init_Box();
44500
+ init_Input();
44431
44501
  init_Stack();
44432
44502
  init_SimpleGrid();
44433
44503
  init_Menu();
@@ -44443,6 +44513,7 @@ var init_DetailPanel = __esm({
44443
44513
  ReactMarkdown2 = React94.lazy(() => import('react-markdown'));
44444
44514
  DetailPanel = ({
44445
44515
  title: propTitle,
44516
+ onTitleCommit,
44446
44517
  subtitle,
44447
44518
  status,
44448
44519
  avatar,
@@ -44464,6 +44535,7 @@ var init_DetailPanel = __esm({
44464
44535
  }) => {
44465
44536
  const eventBus = useEventBus();
44466
44537
  const { t } = hooks.useTranslate();
44538
+ const [titleDraft, setTitleDraft] = React94__namespace.default.useState(null);
44467
44539
  const isFieldDefArray = (arr) => {
44468
44540
  if (!arr || arr.length === 0) return false;
44469
44541
  const first = arr[0];
@@ -44684,6 +44756,50 @@ var init_DetailPanel = __esm({
44684
44756
  }),
44685
44757
  status && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: status.variant ?? "default", children: status.label })
44686
44758
  ] });
44759
+ const commitTitle = () => {
44760
+ if (!onTitleCommit) return;
44761
+ const next = (titleDraft ?? "").trim();
44762
+ setTitleDraft(null);
44763
+ if (!next || next === title) return;
44764
+ onTitleCommit(next, normalizedData?.id !== void 0 ? String(normalizedData.id) : "");
44765
+ };
44766
+ const titleNode = onTitleCommit && titleDraft !== null ? /* @__PURE__ */ jsxRuntime.jsx(
44767
+ Input,
44768
+ {
44769
+ value: titleDraft,
44770
+ autoFocus: true,
44771
+ "aria-label": t("common.title"),
44772
+ className: "h-auto py-1 text-3xl font-bold tracking-tight",
44773
+ onChange: (e) => setTitleDraft(e.target.value),
44774
+ onBlur: commitTitle,
44775
+ onKeyDown: (e) => {
44776
+ if (e.key === "Enter") {
44777
+ e.preventDefault();
44778
+ commitTitle();
44779
+ } else if (e.key === "Escape") {
44780
+ e.preventDefault();
44781
+ setTitleDraft(null);
44782
+ }
44783
+ },
44784
+ "data-testid": "detail-title-input"
44785
+ }
44786
+ ) : onTitleCommit ? /* @__PURE__ */ jsxRuntime.jsx(
44787
+ Box,
44788
+ {
44789
+ role: "button",
44790
+ tabIndex: 0,
44791
+ className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
44792
+ onClick: () => setTitleDraft(title ?? ""),
44793
+ onKeyDown: (e) => {
44794
+ if (e.key === "Enter" || e.key === " ") {
44795
+ e.preventDefault();
44796
+ setTitleDraft(title ?? "");
44797
+ }
44798
+ },
44799
+ "data-testid": "detail-title-editable",
44800
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" })
44801
+ }
44802
+ ) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" });
44687
44803
  const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
44688
44804
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
44689
44805
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
@@ -44703,7 +44819,7 @@ var init_DetailPanel = __esm({
44703
44819
  avatar,
44704
44820
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
44705
44821
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
44706
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
44822
+ titleNode,
44707
44823
  statusBadges
44708
44824
  ] }),
44709
44825
  subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
@@ -44803,16 +44919,11 @@ var init_DetailPanel = __esm({
44803
44919
  footer
44804
44920
  ] })
44805
44921
  ] }) });
44806
- return /* @__PURE__ */ jsxRuntime.jsx(
44807
- Box,
44808
- {
44809
- className: cn(
44810
- slideOver && "fixed inset-y-0 right-0 w-full max-w-2xl bg-card shadow-lg z-50 overflow-y-auto p-6",
44811
- className
44812
- ),
44813
- children: content
44814
- }
44815
- );
44922
+ if (!slideOver) {
44923
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className, children: content });
44924
+ }
44925
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(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 });
44926
+ return typeof document === "undefined" ? panel : reactDom.createPortal(panel, document.body);
44816
44927
  };
44817
44928
  DetailPanel.displayName = "DetailPanel";
44818
44929
  }
@@ -45264,7 +45375,7 @@ var init_Form = __esm({
45264
45375
  });
45265
45376
  debug(
45266
45377
  "forms",
45267
- `Calculation triggered: ${calc.variableName} = ${value}`
45378
+ `Calculation triggered: ${calc.variableName} = ${String(value)}`
45268
45379
  );
45269
45380
  }
45270
45381
  });
@@ -51763,6 +51874,7 @@ function isPlainConfigObject(value) {
51763
51874
  return proto === Object.prototype || proto === null;
51764
51875
  }
51765
51876
  function subtreeHasTraitRef(value) {
51877
+ if (isEvaluatorResolvedData(value)) return false;
51766
51878
  const cached = traitRefPresenceCache.get(value);
51767
51879
  if (cached !== void 0) return cached;
51768
51880
  let found = false;
@@ -51835,6 +51947,10 @@ function renderPatternProps(props, onDismiss, propsSchema) {
51835
51947
  };
51836
51948
  rendered[key] = /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content: childContent, onDismiss });
51837
51949
  } else if (Array.isArray(value)) {
51950
+ if (!isEvaluatorResolvedData(value) && value.some((el) => isPatternConfig(el))) ; else if (!subtreeHasTraitRef(value)) {
51951
+ rendered[key] = value;
51952
+ continue;
51953
+ }
51838
51954
  const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
51839
51955
  rendered[key] = value.map((item, i) => {
51840
51956
  const el = item;
@@ -55861,6 +55977,7 @@ function enqueueEvent(queue, entry) {
55861
55977
  }
55862
55978
 
55863
55979
  // hooks/useTraitStateMachine.ts
55980
+ init_perf();
55864
55981
  init_traitRegistry();
55865
55982
  init_verificationRegistry();
55866
55983
  var crossTraitLog = logger.createLogger("almadar:ui:cross-trait");
@@ -56547,6 +56664,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56547
56664
  }, [eventBus]);
56548
56665
  React94.useEffect(() => {
56549
56666
  const scheduler = runtime.createTickScheduler();
56667
+ const timedTick = (key, fn) => () => ui.perfTimeAsync(key, fn);
56550
56668
  const pureWriterTickKeys = /* @__PURE__ */ new Set();
56551
56669
  for (const group of sharedGroups.values()) {
56552
56670
  const ticksByInterval = /* @__PURE__ */ new Map();
@@ -56563,12 +56681,12 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56563
56681
  }
56564
56682
  for (const entries of ticksByInterval.values()) {
56565
56683
  const interval = entries[0].tick.interval;
56566
- const onDue = () => {
56684
+ const onDue = timedTick(`tick:shared:${group.storeKey}@${String(interval)}`, () => {
56567
56685
  const writers = entries.map(
56568
56686
  ({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
56569
56687
  );
56570
56688
  runTickFrame(group.storeKey, writers, sharedEntityStore);
56571
- };
56689
+ });
56572
56690
  if (interval === "frame") {
56573
56691
  scheduler.add(0, onDue);
56574
56692
  } else if (typeof interval === "number") {
@@ -56586,14 +56704,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56586
56704
  if (sharedKey !== void 0 && pureWriterTickKeys.has(`${binding.trait.name}::${tick.name}`)) {
56587
56705
  continue;
56588
56706
  }
56707
+ const tickKey = `tick:${binding.trait.name}::${tick.name}`;
56589
56708
  if (tick.interval === "frame") {
56590
- scheduler.add(0, () => runTickEffects(tick, binding));
56709
+ scheduler.add(0, timedTick(tickKey, () => runTickEffects(tick, binding)));
56591
56710
  } else if (typeof tick.interval === "number") {
56592
- scheduler.add(tick.interval, () => runTickEffects(tick, binding));
56711
+ scheduler.add(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
56593
56712
  } else if (runtime.isValidCronExpression(tick.interval)) {
56594
- scheduler.addCron(tick.interval, () => runTickEffects(tick, binding));
56713
+ scheduler.addCron(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
56595
56714
  } else {
56596
- scheduler.add(runtime.parseDurationString(tick.interval), () => runTickEffects(tick, binding));
56715
+ scheduler.add(runtime.parseDurationString(tick.interval), timedTick(tickKey, () => runTickEffects(tick, binding)));
56597
56716
  }
56598
56717
  }
56599
56718
  }
@@ -56601,6 +56720,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56601
56720
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
56602
56721
  const processEventQueued = React94.useCallback(async (eventKey, payload, targetTrait, tick, sourceTrait) => {
56603
56722
  const normalizedEvent = normalizeEventKey(eventKey);
56723
+ const _perfT0 = ui.perfStart("processEvent:total");
56604
56724
  const bindings = traitBindingsRef.current;
56605
56725
  const currentManager = managerRef.current;
56606
56726
  crossTraitLog.debug("processEvent:enter", () => ({
@@ -56624,6 +56744,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56624
56744
  entityByTrait[name] = { ...sharedEntityStore.getSnapshot(sharedKey) };
56625
56745
  }
56626
56746
  }
56747
+ const _perfT1 = ui.perfStart("processEvent:guardMatch");
56627
56748
  const results = currentManager.sendEvent(
56628
56749
  normalizedEvent,
56629
56750
  payload,
@@ -56632,6 +56753,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56632
56753
  void 0,
56633
56754
  targetTrait
56634
56755
  );
56756
+ ui.perfEnd("processEvent:guardMatch", _perfT1);
56635
56757
  crossTraitLog.debug("processEvent:results", {
56636
56758
  event: normalizedEvent,
56637
56759
  executedCount: results.length,
@@ -56681,6 +56803,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56681
56803
  transition: `${result.previousState} -> ${result.newState}`,
56682
56804
  effects: JSON.stringify(result.effects)
56683
56805
  }));
56806
+ const _perfT2 = ui.perfStart("processEvent:executeAll");
56684
56807
  const emittedDuringExec = await executeTransitionEffects({
56685
56808
  binding,
56686
56809
  // upstream gap: /runtime TransitionResult.effects is unknown[] — they are SExpr at runtime (see Almadar_UI_Gaps.md)
@@ -56692,6 +56815,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56692
56815
  syncOnly: false,
56693
56816
  log: stateLog
56694
56817
  });
56818
+ ui.perfEnd("processEvent:executeAll", _perfT2);
56695
56819
  emittedByTrait.set(traitName, emittedDuringExec);
56696
56820
  for (const emittedKey of emittedDuringExec) {
56697
56821
  bridgeEchoPendingRef.current.set(
@@ -56788,27 +56912,31 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56788
56912
  if (orbital) dispatchedOrbitals.add(orbital);
56789
56913
  }
56790
56914
  const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
56791
- if (tick !== void 0) {
56792
- void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
56793
- } else {
56794
- await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
56795
- }
56915
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
56796
56916
  }
56917
+ ui.perfEnd("processEvent:total", _perfT0);
56918
+ ui.perfEnd(`event:${normalizedEvent}`, _perfT0);
56797
56919
  }, [entities, eventBus, sharedEntityStore]);
56798
56920
  const drainEventQueue = React94.useCallback(async () => {
56799
56921
  if (processingRef.current) return;
56800
56922
  processingRef.current = true;
56923
+ const _perfT = ui.perfStart("drain:pass");
56924
+ let _perfN = 0;
56801
56925
  try {
56802
56926
  while (eventQueueRef.current.length > 0) {
56803
56927
  const entry = eventQueueRef.current.shift();
56928
+ _perfN++;
56804
56929
  await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait, entry.tick, entry.sourceTrait);
56805
56930
  }
56806
56931
  } finally {
56807
56932
  processingRef.current = false;
56933
+ ui.perfEnd("drain:pass", _perfT);
56934
+ ui.perfGauge("drain:passEntries", _perfN);
56808
56935
  }
56809
56936
  }, [processEventQueued]);
56810
56937
  const enqueueAndDrain = React94.useCallback((eventKey, payload, targetTrait, tick, sourceTrait) => {
56811
56938
  enqueueEvent(eventQueueRef.current, { eventKey, payload, targetTrait, tick, sourceTrait });
56939
+ ui.perfGauge("queue:depthAtEnqueue", eventQueueRef.current.length);
56812
56940
  void drainEventQueue();
56813
56941
  }, [drainEventQueue]);
56814
56942
  React94.useCallback((eventKey, payload) => {
@@ -57107,7 +57235,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
57107
57235
  [serverActiveTraits]
57108
57236
  );
57109
57237
  const uiSlots = context.useUISlots();
57110
- const onEventProcessed = React94.useCallback(async (event, payload, dispatchedOrbitals, tick, sourceTrait) => {
57238
+ const onEventProcessed = React94.useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait) => {
57111
57239
  if (!bridge.connected || !orbitalNames?.length) return;
57112
57240
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
57113
57241
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -57121,9 +57249,10 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
57121
57249
  void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
57122
57250
  continue;
57123
57251
  }
57124
- const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
57125
- recordServerResponse(name, event, meta);
57126
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
57252
+ void bridge.sendEvent(name, event, withActiveTraits(payload)).then(({ effects, meta }) => {
57253
+ recordServerResponse(name, event, meta);
57254
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
57255
+ });
57127
57256
  }
57128
57257
  }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits]);
57129
57258
  const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, navigateBack: onNavigateBack, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams } : { navigate: onNavigate, navigateBack: onNavigateBack, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams };
@@ -57441,14 +57570,15 @@ function OrbPreview({
57441
57570
  return pattern.replace(/:([A-Za-z0-9_]+)/g, (whole, key) => routeParams[key] ?? whole);
57442
57571
  }, [currentPagePath, routeParams]);
57443
57572
  const navStackRef = React94.useRef(null);
57444
- const handleNavigate = React94.useCallback((path) => {
57573
+ const handleNavigate = React94.useCallback((path, navState) => {
57445
57574
  const hit = providers.matchPathAmong(pages, path, (entry) => entry.page.path);
57446
57575
  const match = hit?.candidate;
57447
- const params = hit?.params ?? {};
57576
+ const params = { ...hit?.params ?? {}, ...navState ?? {} };
57448
57577
  navLog.debug("handleNavigate", () => ({
57449
57578
  path,
57450
57579
  matched: match?.page.name ?? null,
57451
57580
  params,
57581
+ navState: navState ? JSON.stringify(navState) : void 0,
57452
57582
  availablePaths: pages.map((p) => p.page.path)
57453
57583
  }));
57454
57584
  if (match?.page.name) {
@@ -57463,9 +57593,9 @@ function OrbPreview({
57463
57593
  }
57464
57594
  }, [pages]);
57465
57595
  const handleNavigateEffect = React94.useCallback(
57466
- (path, _params, crumb) => {
57596
+ (path, params, crumb) => {
57467
57597
  navStackRef.current?.beginNavigate(path, crumb);
57468
- handleNavigate(path);
57598
+ handleNavigate(path, params);
57469
57599
  },
57470
57600
  [handleNavigate]
57471
57601
  );
@@ -59782,14 +59912,7 @@ TraitCardNode.displayName = "TraitCardNode";
59782
59912
 
59783
59913
  // components/avl/organisms/FlowCanvas.tsx
59784
59914
  init_useEventBus();
59785
- var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
59786
- ui.pushPerfEntry({
59787
- name: `profiler:${id}:${phase}`,
59788
- durationMs: actualDuration,
59789
- ts: commitTime,
59790
- detail: { baseDuration }
59791
- });
59792
- };
59915
+ init_perf();
59793
59916
  var flowCanvasLog = logger.createLogger("almadar:ui:flow-canvas");
59794
59917
  var NODE_TYPES = {
59795
59918
  preview: OrbPreviewNode,