@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.
@@ -1,8 +1,10 @@
1
1
  import * as React85 from 'react';
2
- import React85__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId } from 'react';
2
+ import React85__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, useSyncExternalStore, Suspense, useState, useLayoutEffect, lazy, useId } from 'react';
3
3
  import { EventBusContext, useTraitScopeChain, RenderSlotProvider, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, useEntitySchema, getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, TraitScopeProvider, useNavStack, ServerBridgeProvider, useCurrentPagePath, useRenderSlot, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext } from '@almadar/ui/providers';
4
4
  export { EntitySchemaProvider, ServerBridgeProvider, TraitContext, TraitProvider, useEntitySchema, useEntitySchemaOptional, useServerBridge, useTrait, useTraitContext } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
+ import { perfStore, pushPerfEntry, wrapCallbackForEvent, perfStart, perfEnd, perfGauge, prepareSchemaForPreview, perfTimeAsync, collectTraitRefsFromResolvedTrait, buildOrbitalsByTrait, collectEmbeddedTraits } from '@almadar/runtime/ui';
7
+ export { PERF_NAMESPACE, adjustSchemaForMockData, buildMockData, clearPerf, perfEnd, perfStart, perfTime, prepareSchemaForPreview, wrapCallbackForEvent } from '@almadar/runtime/ui';
6
8
  import { StateMachineManager, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, createServerEffectHandlers, EffectExecutor, createContextFromBindings, createTickScheduler, isValidCronExpression, parseDurationString, InMemoryPersistence, normalizeCallSiteConfigToValues, interpolateValue } from '@almadar/runtime';
7
9
  import { mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, walkSExpr, buildResolvedTraitConfigs, isRenderBindingMarker, isFileValue, isInlineTrait, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER, ANIMATION_NAMES } from '@almadar/core';
8
10
  import { clsx } from 'clsx';
@@ -14,8 +16,6 @@ import { createPortal } from 'react-dom';
14
16
  import { useTranslate } from '@almadar/ui/hooks';
15
17
  import { useUISlots, UISlotProvider, useTheme } from '@almadar/ui/context';
16
18
  import { evaluateGuard, evaluateListenPayloadExpr, evaluate, createMinimalContext, executeEffects } from '@almadar/evaluator';
17
- import { wrapCallbackForEvent, prepareSchemaForPreview, perfStore, pushPerfEntry, collectTraitRefsFromResolvedTrait, buildOrbitalsByTrait, collectEmbeddedTraits } from '@almadar/runtime/ui';
18
- export { PERF_NAMESPACE, adjustSchemaForMockData, buildMockData, clearPerf, perfEnd, perfStart, perfTime, prepareSchemaForPreview, wrapCallbackForEvent } from '@almadar/runtime/ui';
19
19
  import { Link, Outlet, useLocation } from 'react-router-dom';
20
20
  import ELK from 'elkjs/lib/elk.bundled.js';
21
21
  import SyntaxHighlighter from 'react-syntax-highlighter/dist/esm/prism-light.js';
@@ -793,6 +793,22 @@ var init_useTapReveal = __esm({
793
793
  "hooks/useTapReveal.ts"() {
794
794
  }
795
795
  });
796
+ function usePerfBuffer() {
797
+ return useSyncExternalStore(perfStore.subscribe, perfStore.getSnapshot, perfStore.getSnapshot);
798
+ }
799
+ var profilerOnRender;
800
+ var init_perf = __esm({
801
+ "lib/perf.ts"() {
802
+ profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
803
+ pushPerfEntry({
804
+ name: `profiler:${id}:${phase}`,
805
+ durationMs: actualDuration,
806
+ ts: commitTime,
807
+ detail: { baseDuration }
808
+ });
809
+ };
810
+ }
811
+ });
796
812
  function resolveMarkerExpression(expression, entity, config, state) {
797
813
  const ctx = createContextFromBindings({
798
814
  entity,
@@ -810,11 +826,54 @@ function isPlainObject(value) {
810
826
  if (typeof value === "function") return false;
811
827
  return true;
812
828
  }
829
+ function isEvaluatorResolvedData(value) {
830
+ return evaluatorResolvedData.has(value);
831
+ }
832
+ function brandResolved(value) {
833
+ if (value !== null && typeof value === "object" && !React85__default.isValidElement(value) && !(value instanceof Date)) {
834
+ resolvedMarkerFree.add(value);
835
+ }
836
+ }
837
+ function subtreeHasMarker(value) {
838
+ if (resolvedMarkerFree.has(value)) return false;
839
+ const cached = markerPresenceCache.get(value);
840
+ if (cached !== void 0) return cached;
841
+ let found = false;
842
+ const children = Array.isArray(value) ? value : Object.values(value);
843
+ for (const child of children) {
844
+ if (isRenderBindingMarker(child)) {
845
+ found = true;
846
+ break;
847
+ }
848
+ if (Array.isArray(child) || isPlainObject(child)) {
849
+ if (subtreeHasMarker(child)) {
850
+ found = true;
851
+ break;
852
+ }
853
+ }
854
+ }
855
+ markerPresenceCache.set(value, found);
856
+ return found;
857
+ }
813
858
  function walkValue(value, scopeTrait, entity, config, state) {
814
859
  if (isRenderBindingMarker(value)) {
815
- return { resolved: resolveMarkerExpression(value.expression, entity, config, state), changed: true };
860
+ const cached = markerResolutionCache.get(value);
861
+ if (cached !== void 0 && cached.entity === entity && cached.config === config && cached.state === state) {
862
+ return { resolved: cached.resolved, changed: false };
863
+ }
864
+ const resolved = resolveMarkerExpression(value.expression, entity, config, state);
865
+ markerResolutionCache.set(value, { entity, config, state, resolved });
866
+ if (resolved !== null && typeof resolved === "object" && !React85__default.isValidElement(resolved) && !(resolved instanceof Date)) {
867
+ resolvedMarkerFree.add(resolved);
868
+ evaluatorResolvedData.add(resolved);
869
+ }
870
+ return { resolved, changed: true };
816
871
  }
817
872
  if (Array.isArray(value)) {
873
+ if (!subtreeHasMarker(value)) {
874
+ brandResolved(value);
875
+ return { resolved: value, changed: false };
876
+ }
818
877
  const out = [];
819
878
  let changed = false;
820
879
  for (const item of value) {
@@ -829,9 +888,14 @@ function walkValue(value, scopeTrait, entity, config, state) {
829
888
  out.push(resolved);
830
889
  if (itemChanged) changed = true;
831
890
  }
891
+ brandResolved(out);
832
892
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
833
893
  }
834
894
  if (isPlainObject(value)) {
895
+ if (!subtreeHasMarker(value)) {
896
+ brandResolved(value);
897
+ return { resolved: value, changed: false };
898
+ }
835
899
  const sourceTrait = value._sourceTrait;
836
900
  if (typeof sourceTrait === "string" && sourceTrait !== scopeTrait) {
837
901
  return { resolved: value, changed: false };
@@ -843,11 +907,13 @@ function walkValue(value, scopeTrait, entity, config, state) {
843
907
  out[key] = resolved;
844
908
  if (itemChanged) changed = true;
845
909
  }
910
+ brandResolved(out);
846
911
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
847
912
  }
848
913
  return { resolved: value, changed: false };
849
914
  }
850
915
  function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
916
+ if (resolvedMarkerFree.has(props)) return props;
851
917
  const out = {};
852
918
  let changed = false;
853
919
  for (const [key, value] of Object.entries(props)) {
@@ -855,11 +921,17 @@ function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
855
921
  out[key] = resolved;
856
922
  if (propChanged) changed = true;
857
923
  }
924
+ brandResolved(out);
858
925
  return changed ? out : props;
859
926
  }
927
+ var markerPresenceCache, markerResolutionCache, resolvedMarkerFree, evaluatorResolvedData;
860
928
  var init_resolve_render_bindings = __esm({
861
929
  "lib/resolve-render-bindings.ts"() {
862
930
  "use client";
931
+ markerPresenceCache = /* @__PURE__ */ new WeakMap();
932
+ markerResolutionCache = /* @__PURE__ */ new WeakMap();
933
+ resolvedMarkerFree = /* @__PURE__ */ new WeakSet();
934
+ evaluatorResolvedData = /* @__PURE__ */ new WeakSet();
863
935
  }
864
936
  });
865
937
  function cn(...inputs) {
@@ -12184,6 +12256,7 @@ var init_LearningCanvas = __esm({
12184
12256
  "components/learning/atoms/LearningCanvas.tsx"() {
12185
12257
  "use client";
12186
12258
  init_cn();
12259
+ init_perf();
12187
12260
  init_useEventBus();
12188
12261
  DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
12189
12262
  TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
@@ -12227,6 +12300,7 @@ var init_LearningCanvas = __esm({
12227
12300
  return [...shapes, ...traceOut, ...readoutOut];
12228
12301
  }, [shapes, traces, readouts, width, height]);
12229
12302
  const draw = useCallback(() => {
12303
+ const _perfT = perfStart("learningcanvas:paint");
12230
12304
  const canvas = canvasRef.current;
12231
12305
  if (!canvas) return;
12232
12306
  const ctx = canvas.getContext("2d");
@@ -12248,6 +12322,7 @@ var init_LearningCanvas = __esm({
12248
12322
  for (const shape of derivedShapes) {
12249
12323
  if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
12250
12324
  }
12325
+ perfEnd("learningcanvas:paint", _perfT);
12251
12326
  }, [width, height, backgroundColor, derivedShapes]);
12252
12327
  useEffect(() => {
12253
12328
  draw();
@@ -26921,7 +26996,7 @@ function fileIcon(name) {
26921
26996
  return "file";
26922
26997
  }
26923
26998
  }
26924
- var TreeNodeItem, FileTree;
26999
+ var TreeNodeItem, FlatTreeNodeItem, FileTree;
26925
27000
  var init_FileTree = __esm({
26926
27001
  "components/core/molecules/FileTree.tsx"() {
26927
27002
  "use client";
@@ -27006,14 +27081,101 @@ var init_FileTree = __esm({
27006
27081
  )) })
27007
27082
  ] });
27008
27083
  };
27084
+ FlatTreeNodeItem = ({
27085
+ item,
27086
+ depth,
27087
+ indent,
27088
+ childrenByParent,
27089
+ onNodeSelect,
27090
+ defaultExpanded = false
27091
+ }) => {
27092
+ const [expanded, setExpanded] = useState(defaultExpanded || depth < 1);
27093
+ const children = childrenByParent.get(item.id);
27094
+ const hasChildren = !!children && children.length > 0;
27095
+ const handleClick = useCallback(() => {
27096
+ if (hasChildren) setExpanded((prev) => !prev);
27097
+ onNodeSelect?.(item.id);
27098
+ }, [hasChildren, item.id, onNodeSelect]);
27099
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
27100
+ /* @__PURE__ */ jsxs(
27101
+ Box,
27102
+ {
27103
+ className: "flex items-center gap-1.5 py-0.5 px-2 cursor-pointer rounded-sm transition-colors hover:bg-muted",
27104
+ style: { paddingLeft: depth * indent + 8 },
27105
+ onClick: handleClick,
27106
+ role: "treeitem",
27107
+ "aria-expanded": hasChildren ? expanded : void 0,
27108
+ children: [
27109
+ hasChildren ? /* @__PURE__ */ jsx(
27110
+ Icon,
27111
+ {
27112
+ name: expanded ? "chevron-down" : "chevron-right",
27113
+ size: "xs",
27114
+ className: "text-[var(--color-muted-foreground)] flex-shrink-0"
27115
+ }
27116
+ ) : /* @__PURE__ */ jsx(Box, { style: { width: 12, flexShrink: 0 } }),
27117
+ /* @__PURE__ */ jsx(
27118
+ Icon,
27119
+ {
27120
+ name: item.icon ?? (hasChildren ? expanded ? "folder-open" : "folder" : "file"),
27121
+ size: "xs",
27122
+ className: hasChildren ? "text-[var(--color-warning)]" : "text-[var(--color-muted-foreground)]"
27123
+ }
27124
+ ),
27125
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "truncate font-mono text-xs", children: item.label })
27126
+ ]
27127
+ }
27128
+ ),
27129
+ hasChildren && expanded && /* @__PURE__ */ jsx(Box, { role: "group", children: children.map((child) => /* @__PURE__ */ jsx(
27130
+ FlatTreeNodeItem,
27131
+ {
27132
+ item: child,
27133
+ depth: depth + 1,
27134
+ indent,
27135
+ childrenByParent,
27136
+ onNodeSelect
27137
+ },
27138
+ child.id
27139
+ )) })
27140
+ ] });
27141
+ };
27009
27142
  FileTree = ({
27010
27143
  tree,
27144
+ items,
27011
27145
  selectedPath,
27012
27146
  onFileSelect,
27147
+ onNodeSelect,
27013
27148
  className,
27014
27149
  indent = 16
27015
27150
  }) => {
27016
- if (tree.length === 0) return null;
27151
+ if (items) {
27152
+ if (items.length === 0) return null;
27153
+ const ids = new Set(items.map((node) => node.id));
27154
+ const childrenByParent = /* @__PURE__ */ new Map();
27155
+ const roots = [];
27156
+ for (const item of items) {
27157
+ if (item.parentId && ids.has(item.parentId)) {
27158
+ const siblings = childrenByParent.get(item.parentId);
27159
+ if (siblings) siblings.push(item);
27160
+ else childrenByParent.set(item.parentId, [item]);
27161
+ } else {
27162
+ roots.push(item);
27163
+ }
27164
+ }
27165
+ return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: roots.map((item) => /* @__PURE__ */ jsx(
27166
+ FlatTreeNodeItem,
27167
+ {
27168
+ item,
27169
+ depth: 0,
27170
+ indent,
27171
+ childrenByParent,
27172
+ onNodeSelect,
27173
+ defaultExpanded: true
27174
+ },
27175
+ item.id
27176
+ )) });
27177
+ }
27178
+ if (!tree || tree.length === 0) return null;
27017
27179
  return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: tree.map((node) => /* @__PURE__ */ jsx(
27018
27180
  TreeNodeItem,
27019
27181
  {
@@ -28344,7 +28506,7 @@ var init_debug = __esm({
28344
28506
  createLogger("almadar:ui:debug:game-state");
28345
28507
  }
28346
28508
  });
28347
- var isRelationsDebugEnabled, RelationSelect;
28509
+ var isRelationsDebugEnabled, MANY_CARDINALITIES, RelationSelect;
28348
28510
  var init_RelationSelect = __esm({
28349
28511
  "components/core/molecules/RelationSelect.tsx"() {
28350
28512
  "use client";
@@ -28358,6 +28520,11 @@ var init_RelationSelect = __esm({
28358
28520
  init_Typography();
28359
28521
  init_debug();
28360
28522
  isRelationsDebugEnabled = () => isDebugEnabled();
28523
+ MANY_CARDINALITIES = [
28524
+ "many",
28525
+ "one-to-many",
28526
+ "many-to-many"
28527
+ ];
28361
28528
  RelationSelect = ({
28362
28529
  value,
28363
28530
  onChange,
@@ -29982,6 +30149,7 @@ var init_MathCanvas = __esm({
29982
30149
  "components/learning/molecules/MathCanvas.tsx"() {
29983
30150
  "use client";
29984
30151
  init_useEventBus();
30152
+ init_perf();
29985
30153
  init_atoms();
29986
30154
  init_Stack();
29987
30155
  init_LearningCanvas();
@@ -30022,17 +30190,21 @@ var init_MathCanvas = __esm({
30022
30190
  error
30023
30191
  }) => {
30024
30192
  const eventBus = useEventBus();
30193
+ const keyMapKey = keyMap ? JSON.stringify(keyMap) : null;
30194
+ const keyUpMapKey = keyUpMap ? JSON.stringify(keyUpMap) : null;
30195
+ const stableKeyMap = useMemo(() => keyMap, [keyMapKey]);
30196
+ const stableKeyUpMap = useMemo(() => keyUpMap, [keyUpMapKey]);
30025
30197
  useEffect(() => {
30026
- if (!keyMap && !keyUpMap) return;
30198
+ if (!stableKeyMap && !stableKeyUpMap) return;
30027
30199
  const onDown = (e) => {
30028
- const ev = keyMap?.[e.code];
30200
+ const ev = stableKeyMap?.[e.code];
30029
30201
  if (ev) {
30030
30202
  eventBus.emit(`UI:${ev}`, {});
30031
30203
  e.preventDefault();
30032
30204
  }
30033
30205
  };
30034
30206
  const onUp = (e) => {
30035
- const ev = keyUpMap?.[e.code];
30207
+ const ev = stableKeyUpMap?.[e.code];
30036
30208
  if (ev) eventBus.emit(`UI:${ev}`, {});
30037
30209
  };
30038
30210
  window.addEventListener("keydown", onDown);
@@ -30041,8 +30213,9 @@ var init_MathCanvas = __esm({
30041
30213
  window.removeEventListener("keydown", onDown);
30042
30214
  window.removeEventListener("keyup", onUp);
30043
30215
  };
30044
- }, [keyMap, keyUpMap, eventBus]);
30216
+ }, [stableKeyMap, stableKeyUpMap, eventBus]);
30045
30217
  const derivedShapes = useMemo(() => {
30218
+ const _perfT = perfStart("mathcanvas:derive");
30046
30219
  const out = [];
30047
30220
  const margin = 24;
30048
30221
  const plotW = width - margin * 2;
@@ -30286,6 +30459,7 @@ var init_MathCanvas = __esm({
30286
30459
  }
30287
30460
  }
30288
30461
  out.push(...shapes);
30462
+ perfEnd("mathcanvas:derive", _perfT);
30289
30463
  return out;
30290
30464
  }, [
30291
30465
  width,
@@ -36501,7 +36675,7 @@ var init_RichBlockEditor = __esm({
36501
36675
  {
36502
36676
  variant: "bordered",
36503
36677
  padding: "none",
36504
- className: cn("flex flex-col", className),
36678
+ className: cn("flex flex-col text-card-foreground", className),
36505
36679
  children: [
36506
36680
  enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsx(
36507
36681
  Box,
@@ -41729,6 +41903,7 @@ var init_DetailPanel = __esm({
41729
41903
  "use client";
41730
41904
  init_atoms();
41731
41905
  init_Box();
41906
+ init_Input();
41732
41907
  init_Stack();
41733
41908
  init_SimpleGrid();
41734
41909
  init_Menu();
@@ -41744,6 +41919,7 @@ var init_DetailPanel = __esm({
41744
41919
  ReactMarkdown2 = lazy(() => import('react-markdown'));
41745
41920
  DetailPanel = ({
41746
41921
  title: propTitle,
41922
+ onTitleCommit,
41747
41923
  subtitle,
41748
41924
  status,
41749
41925
  avatar,
@@ -41765,6 +41941,7 @@ var init_DetailPanel = __esm({
41765
41941
  }) => {
41766
41942
  const eventBus = useEventBus();
41767
41943
  const { t } = useTranslate();
41944
+ const [titleDraft, setTitleDraft] = React85__default.useState(null);
41768
41945
  const isFieldDefArray = (arr) => {
41769
41946
  if (!arr || arr.length === 0) return false;
41770
41947
  const first = arr[0];
@@ -41985,6 +42162,50 @@ var init_DetailPanel = __esm({
41985
42162
  }),
41986
42163
  status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
41987
42164
  ] });
42165
+ const commitTitle = () => {
42166
+ if (!onTitleCommit) return;
42167
+ const next = (titleDraft ?? "").trim();
42168
+ setTitleDraft(null);
42169
+ if (!next || next === title) return;
42170
+ onTitleCommit(next, normalizedData?.id !== void 0 ? String(normalizedData.id) : "");
42171
+ };
42172
+ const titleNode = onTitleCommit && titleDraft !== null ? /* @__PURE__ */ jsx(
42173
+ Input,
42174
+ {
42175
+ value: titleDraft,
42176
+ autoFocus: true,
42177
+ "aria-label": t("common.title"),
42178
+ className: "h-auto py-1 text-3xl font-bold tracking-tight",
42179
+ onChange: (e) => setTitleDraft(e.target.value),
42180
+ onBlur: commitTitle,
42181
+ onKeyDown: (e) => {
42182
+ if (e.key === "Enter") {
42183
+ e.preventDefault();
42184
+ commitTitle();
42185
+ } else if (e.key === "Escape") {
42186
+ e.preventDefault();
42187
+ setTitleDraft(null);
42188
+ }
42189
+ },
42190
+ "data-testid": "detail-title-input"
42191
+ }
42192
+ ) : onTitleCommit ? /* @__PURE__ */ jsx(
42193
+ Box,
42194
+ {
42195
+ role: "button",
42196
+ tabIndex: 0,
42197
+ className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
42198
+ onClick: () => setTitleDraft(title ?? ""),
42199
+ onKeyDown: (e) => {
42200
+ if (e.key === "Enter" || e.key === " ") {
42201
+ e.preventDefault();
42202
+ setTitleDraft(title ?? "");
42203
+ }
42204
+ },
42205
+ "data-testid": "detail-title-editable",
42206
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" })
42207
+ }
42208
+ ) : /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" });
41988
42209
  const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
41989
42210
  /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
41990
42211
  /* @__PURE__ */ jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
@@ -42004,7 +42225,7 @@ var init_DetailPanel = __esm({
42004
42225
  avatar,
42005
42226
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
42006
42227
  /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
42007
- /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42228
+ titleNode,
42008
42229
  statusBadges
42009
42230
  ] }),
42010
42231
  subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
@@ -42340,6 +42561,9 @@ function determineInputType(field) {
42340
42561
  if (field.type === "relation" || field.relation) {
42341
42562
  return "relation";
42342
42563
  }
42564
+ if (field.type === "array") {
42565
+ return "array";
42566
+ }
42343
42567
  if (field.type === "enum" || field.values || getEnumOptions(field).length > 0) {
42344
42568
  return "select";
42345
42569
  }
@@ -42415,6 +42639,7 @@ var init_Form = __esm({
42415
42639
  init_Typography();
42416
42640
  init_Icon();
42417
42641
  init_RelationSelect();
42642
+ init_TagInput();
42418
42643
  init_UploadDropZone();
42419
42644
  init_Alert();
42420
42645
  init_useEventBus();
@@ -42494,7 +42719,7 @@ var init_Form = __esm({
42494
42719
  values: "values" in f3 ? f3.values : void 0,
42495
42720
  min: f3.min,
42496
42721
  max: f3.max,
42497
- relation: "relation" in f3 ? { entity: f3.relation.entity } : void 0
42722
+ relation: "relation" in f3 ? { entity: f3.relation.entity, cardinality: f3.relation.cardinality } : void 0
42498
42723
  })
42499
42724
  );
42500
42725
  }, [entity, fields]);
@@ -42561,7 +42786,7 @@ var init_Form = __esm({
42561
42786
  });
42562
42787
  debug(
42563
42788
  "forms",
42564
- `Calculation triggered: ${calc.variableName} = ${value}`
42789
+ `Calculation triggered: ${calc.variableName} = ${String(value)}`
42565
42790
  );
42566
42791
  }
42567
42792
  });
@@ -42729,7 +42954,7 @@ var init_Form = __esm({
42729
42954
  values: "values" in entityField ? entityField.values : void 0,
42730
42955
  min: entityField.min,
42731
42956
  max: entityField.max,
42732
- relation: "relation" in entityField ? { entity: entityField.relation.entity } : void 0
42957
+ relation: "relation" in entityField ? { entity: entityField.relation.entity, cardinality: entityField.relation.cardinality } : void 0
42733
42958
  };
42734
42959
  }
42735
42960
  return { name: field, type: "string" };
@@ -42841,6 +43066,22 @@ var init_Form = __esm({
42841
43066
  case "relation": {
42842
43067
  const relationOptions = relationsData[fieldName] || [];
42843
43068
  const relationLoading = relationsLoading[fieldName] || false;
43069
+ if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
43070
+ const selectedValues = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : [];
43071
+ return /* @__PURE__ */ jsx(
43072
+ Select,
43073
+ {
43074
+ ...commonProps,
43075
+ multiple: true,
43076
+ searchable: true,
43077
+ clearable: true,
43078
+ options: [...relationOptions],
43079
+ value: selectedValues,
43080
+ onValueChange: (value) => handleChange(fieldName, Array.isArray(value) ? value : [value]),
43081
+ placeholder: field.placeholder || `Select ${label}...`
43082
+ }
43083
+ );
43084
+ }
42844
43085
  return /* @__PURE__ */ jsx(
42845
43086
  RelationSelect,
42846
43087
  {
@@ -42855,6 +43096,18 @@ var init_Form = __esm({
42855
43096
  }
42856
43097
  );
42857
43098
  }
43099
+ case "array": {
43100
+ const arrayValue = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : currentValue != null && currentValue !== "" ? [String(currentValue)] : [];
43101
+ return /* @__PURE__ */ jsx(
43102
+ TagInput,
43103
+ {
43104
+ placeholder: field.placeholder,
43105
+ disabled: isLoading,
43106
+ value: arrayValue,
43107
+ onChange: (next) => handleChange(fieldName, [...next])
43108
+ }
43109
+ );
43110
+ }
42858
43111
  case "number":
42859
43112
  return /* @__PURE__ */ jsx(
42860
43113
  Input,
@@ -48423,7 +48676,10 @@ function enrichFormFields(fields, entityDef) {
48423
48676
  enriched.values = entityField.enumValues;
48424
48677
  }
48425
48678
  if (entityField.relation) {
48426
- enriched.relation = entityField.relation.entity;
48679
+ enriched.relation = {
48680
+ entity: entityField.relation.entity,
48681
+ cardinality: entityField.relation.cardinality
48682
+ };
48427
48683
  }
48428
48684
  return enriched;
48429
48685
  }
@@ -48451,7 +48707,10 @@ function enrichFormFields(fields, entityDef) {
48451
48707
  }
48452
48708
  }
48453
48709
  if (!obj.relation && entityField.relation) {
48454
- enriched.relation = entityField.relation.entity;
48710
+ enriched.relation = {
48711
+ entity: entityField.relation.entity,
48712
+ cardinality: entityField.relation.cardinality
48713
+ };
48455
48714
  }
48456
48715
  return enriched;
48457
48716
  }
@@ -48466,7 +48725,12 @@ function enrichDetailFields(fields, entityDef) {
48466
48725
  const meta = { type: entityField.type };
48467
48726
  const values = entityField.values ?? entityField.enumValues;
48468
48727
  if (values && values.length > 0) meta.values = values;
48469
- if (entityField.relation) meta.relation = entityField.relation.entity;
48728
+ if (entityField.relation) {
48729
+ meta.relation = {
48730
+ entity: entityField.relation.entity,
48731
+ cardinality: entityField.relation.cardinality
48732
+ };
48733
+ }
48470
48734
  return meta;
48471
48735
  };
48472
48736
  return fields.map((field) => {
@@ -49020,6 +49284,33 @@ function isPlainConfigObject(value) {
49020
49284
  const proto = Object.getPrototypeOf(value);
49021
49285
  return proto === Object.prototype || proto === null;
49022
49286
  }
49287
+ function subtreeHasTraitRef(value) {
49288
+ if (isEvaluatorResolvedData(value)) return false;
49289
+ const cached = traitRefPresenceCache.get(value);
49290
+ if (cached !== void 0) return cached;
49291
+ let found = false;
49292
+ const children = Array.isArray(value) ? value : Object.values(value);
49293
+ for (const child of children) {
49294
+ if (typeof child === "string" && TRAIT_BINDING_RE.test(child)) {
49295
+ found = true;
49296
+ break;
49297
+ }
49298
+ if (isRenderBindingMarker(child)) continue;
49299
+ if (Array.isArray(child)) {
49300
+ if (subtreeHasTraitRef(child)) {
49301
+ found = true;
49302
+ break;
49303
+ }
49304
+ } else if (child !== null && typeof child === "object" && isPlainConfigObject(child)) {
49305
+ if (subtreeHasTraitRef(child)) {
49306
+ found = true;
49307
+ break;
49308
+ }
49309
+ }
49310
+ }
49311
+ traitRefPresenceCache.set(value, found);
49312
+ return found;
49313
+ }
49023
49314
  function substituteTraitRefsDeep(value, pathKey) {
49024
49315
  if (isRenderBindingMarker(value)) return value;
49025
49316
  if (typeof value === "string") {
@@ -49034,11 +49325,13 @@ function substituteTraitRefsDeep(value, pathKey) {
49034
49325
  return value;
49035
49326
  }
49036
49327
  if (Array.isArray(value)) {
49328
+ if (!subtreeHasTraitRef(value)) return value;
49037
49329
  return value.map(
49038
49330
  (item, i) => substituteTraitRefsDeep(item, `${pathKey}[${i}]`)
49039
49331
  );
49040
49332
  }
49041
49333
  if (typeof value === "object" && isPlainConfigObject(value)) {
49334
+ if (!subtreeHasTraitRef(value)) return value;
49042
49335
  const out = {};
49043
49336
  for (const [k, v] of Object.entries(value)) {
49044
49337
  out[k] = substituteTraitRefsDeep(v, `${pathKey}.${k}`);
@@ -49065,6 +49358,10 @@ function renderPatternProps(props, onDismiss, propsSchema) {
49065
49358
  };
49066
49359
  rendered[key] = /* @__PURE__ */ jsx(SlotContentRenderer, { content: childContent, onDismiss });
49067
49360
  } else if (Array.isArray(value)) {
49361
+ if (!isEvaluatorResolvedData(value) && value.some((el) => isPatternConfig(el))) ; else if (!subtreeHasTraitRef(value)) {
49362
+ rendered[key] = value;
49363
+ continue;
49364
+ }
49068
49365
  const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
49069
49366
  rendered[key] = value.map((item, i) => {
49070
49367
  const el = item;
@@ -49327,7 +49624,7 @@ function UISlotRenderer({
49327
49624
  }
49328
49625
  return wrapped;
49329
49626
  }
49330
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49627
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
49331
49628
  var init_UISlotRenderer = __esm({
49332
49629
  "components/core/organisms/UISlotRenderer.tsx"() {
49333
49630
  "use client";
@@ -49401,6 +49698,7 @@ var init_UISlotRenderer = __esm({
49401
49698
  "alert",
49402
49699
  "dialog"
49403
49700
  ]);
49701
+ traitRefPresenceCache = /* @__PURE__ */ new WeakMap();
49404
49702
  UISlotRenderer.displayName = "UISlotRenderer";
49405
49703
  }
49406
49704
  });
@@ -49528,6 +49826,23 @@ function createClientEffectHandlers(options) {
49528
49826
  })
49529
49827
  };
49530
49828
  }
49829
+
49830
+ // lib/event-queue-coalesce.ts
49831
+ function enqueueEvent(queue, entry) {
49832
+ if (entry.tick !== void 0) {
49833
+ const pending = queue.find(
49834
+ (e) => e.tick !== void 0 && e.eventKey === entry.eventKey && e.targetTrait === entry.targetTrait
49835
+ );
49836
+ if (pending !== void 0) {
49837
+ pending.payload = entry.payload;
49838
+ return;
49839
+ }
49840
+ }
49841
+ queue.push(entry);
49842
+ }
49843
+
49844
+ // hooks/useTraitStateMachine.ts
49845
+ init_perf();
49531
49846
  var lambdaLog = createLogger("almadar:ui:fn-form-lambda");
49532
49847
  function isOperatorCall(value) {
49533
49848
  const first = value[0];
@@ -50273,12 +50588,13 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50273
50588
  entityId
50274
50589
  };
50275
50590
  const emittedDuringExec = [];
50591
+ const tickName = flushEvent.startsWith("tick:") ? flushEvent.slice(5) : void 0;
50276
50592
  const baseEmit = handlers.emit;
50277
50593
  const trackingHandlers = {
50278
50594
  ...handlers,
50279
50595
  emit: (event, eventPayload, source) => {
50280
50596
  emittedDuringExec.push(event);
50281
- baseEmit(event, eventPayload, source);
50597
+ baseEmit(event, eventPayload, tickName !== void 0 ? { ...source, tick: tickName } : source);
50282
50598
  }
50283
50599
  };
50284
50600
  if (traitName === "Hero") {
@@ -50385,6 +50701,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50385
50701
  }, [eventBus]);
50386
50702
  useEffect(() => {
50387
50703
  const scheduler = createTickScheduler();
50704
+ const timedTick = (key, fn) => () => perfTimeAsync(key, fn);
50388
50705
  const pureWriterTickKeys = /* @__PURE__ */ new Set();
50389
50706
  for (const group of sharedGroups.values()) {
50390
50707
  const ticksByInterval = /* @__PURE__ */ new Map();
@@ -50401,12 +50718,12 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50401
50718
  }
50402
50719
  for (const entries of ticksByInterval.values()) {
50403
50720
  const interval = entries[0].tick.interval;
50404
- const onDue = () => {
50721
+ const onDue = timedTick(`tick:shared:${group.storeKey}@${String(interval)}`, () => {
50405
50722
  const writers = entries.map(
50406
50723
  ({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
50407
50724
  );
50408
50725
  runTickFrame(group.storeKey, writers, sharedEntityStore);
50409
- };
50726
+ });
50410
50727
  if (interval === "frame") {
50411
50728
  scheduler.add(0, onDue);
50412
50729
  } else if (typeof interval === "number") {
@@ -50424,21 +50741,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50424
50741
  if (sharedKey !== void 0 && pureWriterTickKeys.has(`${binding.trait.name}::${tick.name}`)) {
50425
50742
  continue;
50426
50743
  }
50744
+ const tickKey = `tick:${binding.trait.name}::${tick.name}`;
50427
50745
  if (tick.interval === "frame") {
50428
- scheduler.add(0, () => runTickEffects(tick, binding));
50746
+ scheduler.add(0, timedTick(tickKey, () => runTickEffects(tick, binding)));
50429
50747
  } else if (typeof tick.interval === "number") {
50430
- scheduler.add(tick.interval, () => runTickEffects(tick, binding));
50748
+ scheduler.add(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
50431
50749
  } else if (isValidCronExpression(tick.interval)) {
50432
- scheduler.addCron(tick.interval, () => runTickEffects(tick, binding));
50750
+ scheduler.addCron(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
50433
50751
  } else {
50434
- scheduler.add(parseDurationString(tick.interval), () => runTickEffects(tick, binding));
50752
+ scheduler.add(parseDurationString(tick.interval), timedTick(tickKey, () => runTickEffects(tick, binding)));
50435
50753
  }
50436
50754
  }
50437
50755
  }
50438
50756
  return () => scheduler.stopAll();
50439
50757
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
50440
- const processEventQueued = useCallback(async (eventKey, payload, targetTrait) => {
50758
+ const processEventQueued = useCallback(async (eventKey, payload, targetTrait, tick, sourceTrait) => {
50441
50759
  const normalizedEvent = normalizeEventKey(eventKey);
50760
+ const _perfT0 = perfStart("processEvent:total");
50442
50761
  const bindings = traitBindingsRef.current;
50443
50762
  const currentManager = managerRef.current;
50444
50763
  crossTraitLog.debug("processEvent:enter", () => ({
@@ -50462,6 +50781,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50462
50781
  entityByTrait[name] = { ...sharedEntityStore.getSnapshot(sharedKey) };
50463
50782
  }
50464
50783
  }
50784
+ const _perfT1 = perfStart("processEvent:guardMatch");
50465
50785
  const results = currentManager.sendEvent(
50466
50786
  normalizedEvent,
50467
50787
  payload,
@@ -50470,6 +50790,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50470
50790
  void 0,
50471
50791
  targetTrait
50472
50792
  );
50793
+ perfEnd("processEvent:guardMatch", _perfT1);
50473
50794
  crossTraitLog.debug("processEvent:results", {
50474
50795
  event: normalizedEvent,
50475
50796
  executedCount: results.length,
@@ -50519,6 +50840,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50519
50840
  transition: `${result.previousState} -> ${result.newState}`,
50520
50841
  effects: JSON.stringify(result.effects)
50521
50842
  }));
50843
+ const _perfT2 = perfStart("processEvent:executeAll");
50522
50844
  const emittedDuringExec = await executeTransitionEffects({
50523
50845
  binding,
50524
50846
  // upstream gap: /runtime TransitionResult.effects is unknown[] — they are SExpr at runtime (see Almadar_UI_Gaps.md)
@@ -50530,6 +50852,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50530
50852
  syncOnly: false,
50531
50853
  log: stateLog
50532
50854
  });
50855
+ perfEnd("processEvent:executeAll", _perfT2);
50533
50856
  emittedByTrait.set(traitName, emittedDuringExec);
50534
50857
  for (const emittedKey of emittedDuringExec) {
50535
50858
  bridgeEchoPendingRef.current.set(
@@ -50626,23 +50949,31 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50626
50949
  if (orbital) dispatchedOrbitals.add(orbital);
50627
50950
  }
50628
50951
  const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
50629
- await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
50952
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
50630
50953
  }
50954
+ perfEnd("processEvent:total", _perfT0);
50955
+ perfEnd(`event:${normalizedEvent}`, _perfT0);
50631
50956
  }, [entities, eventBus, sharedEntityStore]);
50632
50957
  const drainEventQueue = useCallback(async () => {
50633
50958
  if (processingRef.current) return;
50634
50959
  processingRef.current = true;
50960
+ const _perfT = perfStart("drain:pass");
50961
+ let _perfN = 0;
50635
50962
  try {
50636
50963
  while (eventQueueRef.current.length > 0) {
50637
50964
  const entry = eventQueueRef.current.shift();
50638
- await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait);
50965
+ _perfN++;
50966
+ await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait, entry.tick, entry.sourceTrait);
50639
50967
  }
50640
50968
  } finally {
50641
50969
  processingRef.current = false;
50970
+ perfEnd("drain:pass", _perfT);
50971
+ perfGauge("drain:passEntries", _perfN);
50642
50972
  }
50643
50973
  }, [processEventQueued]);
50644
- const enqueueAndDrain = useCallback((eventKey, payload, targetTrait) => {
50645
- eventQueueRef.current.push({ eventKey, payload, targetTrait });
50974
+ const enqueueAndDrain = useCallback((eventKey, payload, targetTrait, tick, sourceTrait) => {
50975
+ enqueueEvent(eventQueueRef.current, { eventKey, payload, targetTrait, tick, sourceTrait });
50976
+ perfGauge("queue:depthAtEnqueue", eventQueueRef.current.length);
50646
50977
  void drainEventQueue();
50647
50978
  }, [drainEventQueue]);
50648
50979
  useCallback((eventKey, payload) => {
@@ -50695,7 +51026,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50695
51026
  crossTraitLog.debug("self:fire-server-cascade", { traitName, busKey: selfBusKey, eventKey });
50696
51027
  }
50697
51028
  crossTraitLog.debug("self:fire", { traitName, busKey: selfBusKey, eventKey });
50698
- enqueueAndDrain(eventKey, event.payload, traitName);
51029
+ enqueueAndDrain(eventKey, event.payload, traitName, event.source?.tick, event.source?.trait);
50699
51030
  });
50700
51031
  unsubscribes.push(() => {
50701
51032
  crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
@@ -50713,7 +51044,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50713
51044
  const bareKey = `UI:${eventKey}`;
50714
51045
  const unsub = eventBus.on(bareKey, (event) => {
50715
51046
  crossTraitLog.debug("bare-cascade:fire", { bareKey, eventKey });
50716
- enqueueAndDrain(eventKey, event.payload);
51047
+ enqueueAndDrain(eventKey, event.payload, void 0, event.source?.tick, event.source?.trait);
50717
51048
  });
50718
51049
  unsubscribes.push(() => {
50719
51050
  crossTraitLog.debug("bare-cascade:unsubscribe", { bareKey, eventKey });
@@ -50747,7 +51078,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50747
51078
  enqueueAndDrain(
50748
51079
  listen.triggers,
50749
51080
  applyListenPayloadMapping(listen.payloadMapping, event.payload, evaluateListenPayloadExpr),
50750
- binding.trait.name
51081
+ binding.trait.name,
51082
+ event.source?.tick,
51083
+ event.source?.trait
50751
51084
  );
50752
51085
  });
50753
51086
  unsubscribes.push(() => {
@@ -51042,7 +51375,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
51042
51375
  [serverActiveTraits]
51043
51376
  );
51044
51377
  const uiSlots = useUISlots();
51045
- const onEventProcessed = useCallback(async (event, payload, dispatchedOrbitals) => {
51378
+ const onEventProcessed = useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait) => {
51046
51379
  if (!bridge.connected || !orbitalNames?.length) return;
51047
51380
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
51048
51381
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -51052,9 +51385,14 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
51052
51385
  dispatchedOrbitalsSize: dispatchedOrbitals?.size ?? 0
51053
51386
  }));
51054
51387
  for (const name of targets) {
51055
- const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
51056
- recordServerResponse(name, event, meta);
51057
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
51388
+ if (tick !== void 0) {
51389
+ void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
51390
+ continue;
51391
+ }
51392
+ void bridge.sendEvent(name, event, withActiveTraits(payload)).then(({ effects, meta }) => {
51393
+ recordServerResponse(name, event, meta);
51394
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
51395
+ });
51058
51396
  }
51059
51397
  }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits]);
51060
51398
  const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, navigateBack: onNavigateBack, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams } : { navigate: onNavigate, navigateBack: onNavigateBack, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams };
@@ -51372,14 +51710,15 @@ function OrbPreview({
51372
51710
  return pattern.replace(/:([A-Za-z0-9_]+)/g, (whole, key) => routeParams[key] ?? whole);
51373
51711
  }, [currentPagePath, routeParams]);
51374
51712
  const navStackRef = useRef(null);
51375
- const handleNavigate = useCallback((path) => {
51713
+ const handleNavigate = useCallback((path, navState) => {
51376
51714
  const hit = matchPathAmong(pages, path, (entry) => entry.page.path);
51377
51715
  const match = hit?.candidate;
51378
- const params = hit?.params ?? {};
51716
+ const params = { ...hit?.params ?? {}, ...navState ?? {} };
51379
51717
  navLog.debug("handleNavigate", () => ({
51380
51718
  path,
51381
51719
  matched: match?.page.name ?? null,
51382
51720
  params,
51721
+ navState: navState ? JSON.stringify(navState) : void 0,
51383
51722
  availablePaths: pages.map((p) => p.page.path)
51384
51723
  }));
51385
51724
  if (match?.page.name) {
@@ -51394,9 +51733,9 @@ function OrbPreview({
51394
51733
  }
51395
51734
  }, [pages]);
51396
51735
  const handleNavigateEffect = useCallback(
51397
- (path, _params, crumb) => {
51736
+ (path, params, crumb) => {
51398
51737
  navStackRef.current?.beginNavigate(path, crumb);
51399
- handleNavigate(path);
51738
+ handleNavigate(path, params);
51400
51739
  },
51401
51740
  [handleNavigate]
51402
51741
  );
@@ -51527,11 +51866,13 @@ function BrowserPlayground({
51527
51866
  unregister: async () => {
51528
51867
  runtime.unregisterAll();
51529
51868
  },
51530
- sendEvent: async (orbitalName, event, payload) => {
51869
+ sendEvent: async (orbitalName, event, payload, _clientId, tick, sourceTrait) => {
51531
51870
  await registrationReady;
51532
51871
  return runtime.processOrbitalEvent(orbitalName, {
51533
51872
  event,
51534
- payload
51873
+ payload,
51874
+ tick,
51875
+ sourceTrait
51535
51876
  // @almadar/runtime OrbitalEventResponse.clientEffects uses a wider ClientEffectTuple than
51536
51877
  // ServerBridge's local definition — cast at this boundary (upstream fix queued).
51537
51878
  });
@@ -51549,16 +51890,8 @@ function BrowserPlayground({
51549
51890
  }
51550
51891
  );
51551
51892
  }
51552
- function usePerfBuffer() {
51553
- return useSyncExternalStore(perfStore.subscribe, perfStore.getSnapshot, perfStore.getSnapshot);
51554
- }
51555
- var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
51556
- pushPerfEntry({
51557
- name: `profiler:${id}:${phase}`,
51558
- durationMs: actualDuration,
51559
- ts: commitTime,
51560
- detail: { baseDuration }
51561
- });
51562
- };
51893
+
51894
+ // runtime/index.ts
51895
+ init_perf();
51563
51896
 
51564
51897
  export { BrowserPlayground, OrbPreview, clearSchemaCache, createClientEffectHandlers, profilerOnRender, usePerfBuffer, useResolvedSchema, useTraitStateMachine };