@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.
@@ -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,7 +826,16 @@ 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
+ }
813
837
  function subtreeHasMarker(value) {
838
+ if (resolvedMarkerFree.has(value)) return false;
814
839
  const cached = markerPresenceCache.get(value);
815
840
  if (cached !== void 0) return cached;
816
841
  let found = false;
@@ -832,10 +857,23 @@ function subtreeHasMarker(value) {
832
857
  }
833
858
  function walkValue(value, scopeTrait, entity, config, state) {
834
859
  if (isRenderBindingMarker(value)) {
835
- 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 };
836
871
  }
837
872
  if (Array.isArray(value)) {
838
- if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
873
+ if (!subtreeHasMarker(value)) {
874
+ brandResolved(value);
875
+ return { resolved: value, changed: false };
876
+ }
839
877
  const out = [];
840
878
  let changed = false;
841
879
  for (const item of value) {
@@ -850,10 +888,14 @@ function walkValue(value, scopeTrait, entity, config, state) {
850
888
  out.push(resolved);
851
889
  if (itemChanged) changed = true;
852
890
  }
891
+ brandResolved(out);
853
892
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
854
893
  }
855
894
  if (isPlainObject(value)) {
856
- if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
895
+ if (!subtreeHasMarker(value)) {
896
+ brandResolved(value);
897
+ return { resolved: value, changed: false };
898
+ }
857
899
  const sourceTrait = value._sourceTrait;
858
900
  if (typeof sourceTrait === "string" && sourceTrait !== scopeTrait) {
859
901
  return { resolved: value, changed: false };
@@ -865,11 +907,13 @@ function walkValue(value, scopeTrait, entity, config, state) {
865
907
  out[key] = resolved;
866
908
  if (itemChanged) changed = true;
867
909
  }
910
+ brandResolved(out);
868
911
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
869
912
  }
870
913
  return { resolved: value, changed: false };
871
914
  }
872
915
  function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
916
+ if (resolvedMarkerFree.has(props)) return props;
873
917
  const out = {};
874
918
  let changed = false;
875
919
  for (const [key, value] of Object.entries(props)) {
@@ -877,13 +921,17 @@ function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
877
921
  out[key] = resolved;
878
922
  if (propChanged) changed = true;
879
923
  }
924
+ brandResolved(out);
880
925
  return changed ? out : props;
881
926
  }
882
- var markerPresenceCache;
927
+ var markerPresenceCache, markerResolutionCache, resolvedMarkerFree, evaluatorResolvedData;
883
928
  var init_resolve_render_bindings = __esm({
884
929
  "lib/resolve-render-bindings.ts"() {
885
930
  "use client";
886
931
  markerPresenceCache = /* @__PURE__ */ new WeakMap();
932
+ markerResolutionCache = /* @__PURE__ */ new WeakMap();
933
+ resolvedMarkerFree = /* @__PURE__ */ new WeakSet();
934
+ evaluatorResolvedData = /* @__PURE__ */ new WeakSet();
887
935
  }
888
936
  });
889
937
  function cn(...inputs) {
@@ -12208,6 +12256,7 @@ var init_LearningCanvas = __esm({
12208
12256
  "components/learning/atoms/LearningCanvas.tsx"() {
12209
12257
  "use client";
12210
12258
  init_cn();
12259
+ init_perf();
12211
12260
  init_useEventBus();
12212
12261
  DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
12213
12262
  TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
@@ -12251,6 +12300,7 @@ var init_LearningCanvas = __esm({
12251
12300
  return [...shapes, ...traceOut, ...readoutOut];
12252
12301
  }, [shapes, traces, readouts, width, height]);
12253
12302
  const draw = useCallback(() => {
12303
+ const _perfT = perfStart("learningcanvas:paint");
12254
12304
  const canvas = canvasRef.current;
12255
12305
  if (!canvas) return;
12256
12306
  const ctx = canvas.getContext("2d");
@@ -12272,6 +12322,7 @@ var init_LearningCanvas = __esm({
12272
12322
  for (const shape of derivedShapes) {
12273
12323
  if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
12274
12324
  }
12325
+ perfEnd("learningcanvas:paint", _perfT);
12275
12326
  }, [width, height, backgroundColor, derivedShapes]);
12276
12327
  useEffect(() => {
12277
12328
  draw();
@@ -30098,6 +30149,7 @@ var init_MathCanvas = __esm({
30098
30149
  "components/learning/molecules/MathCanvas.tsx"() {
30099
30150
  "use client";
30100
30151
  init_useEventBus();
30152
+ init_perf();
30101
30153
  init_atoms();
30102
30154
  init_Stack();
30103
30155
  init_LearningCanvas();
@@ -30163,6 +30215,7 @@ var init_MathCanvas = __esm({
30163
30215
  };
30164
30216
  }, [stableKeyMap, stableKeyUpMap, eventBus]);
30165
30217
  const derivedShapes = useMemo(() => {
30218
+ const _perfT = perfStart("mathcanvas:derive");
30166
30219
  const out = [];
30167
30220
  const margin = 24;
30168
30221
  const plotW = width - margin * 2;
@@ -30406,6 +30459,7 @@ var init_MathCanvas = __esm({
30406
30459
  }
30407
30460
  }
30408
30461
  out.push(...shapes);
30462
+ perfEnd("mathcanvas:derive", _perfT);
30409
30463
  return out;
30410
30464
  }, [
30411
30465
  width,
@@ -36621,7 +36675,7 @@ var init_RichBlockEditor = __esm({
36621
36675
  {
36622
36676
  variant: "bordered",
36623
36677
  padding: "none",
36624
- className: cn("flex flex-col", className),
36678
+ className: cn("flex flex-col text-card-foreground", className),
36625
36679
  children: [
36626
36680
  enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsx(
36627
36681
  Box,
@@ -41849,6 +41903,7 @@ var init_DetailPanel = __esm({
41849
41903
  "use client";
41850
41904
  init_atoms();
41851
41905
  init_Box();
41906
+ init_Input();
41852
41907
  init_Stack();
41853
41908
  init_SimpleGrid();
41854
41909
  init_Menu();
@@ -41864,6 +41919,7 @@ var init_DetailPanel = __esm({
41864
41919
  ReactMarkdown2 = lazy(() => import('react-markdown'));
41865
41920
  DetailPanel = ({
41866
41921
  title: propTitle,
41922
+ onTitleCommit,
41867
41923
  subtitle,
41868
41924
  status,
41869
41925
  avatar,
@@ -41885,6 +41941,7 @@ var init_DetailPanel = __esm({
41885
41941
  }) => {
41886
41942
  const eventBus = useEventBus();
41887
41943
  const { t } = useTranslate();
41944
+ const [titleDraft, setTitleDraft] = React85__default.useState(null);
41888
41945
  const isFieldDefArray = (arr) => {
41889
41946
  if (!arr || arr.length === 0) return false;
41890
41947
  const first = arr[0];
@@ -42105,6 +42162,50 @@ var init_DetailPanel = __esm({
42105
42162
  }),
42106
42163
  status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42107
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" });
42108
42209
  const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
42109
42210
  /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
42110
42211
  /* @__PURE__ */ jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
@@ -42124,7 +42225,7 @@ var init_DetailPanel = __esm({
42124
42225
  avatar,
42125
42226
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
42126
42227
  /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
42127
- /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42228
+ titleNode,
42128
42229
  statusBadges
42129
42230
  ] }),
42130
42231
  subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
@@ -42685,7 +42786,7 @@ var init_Form = __esm({
42685
42786
  });
42686
42787
  debug(
42687
42788
  "forms",
42688
- `Calculation triggered: ${calc.variableName} = ${value}`
42789
+ `Calculation triggered: ${calc.variableName} = ${String(value)}`
42689
42790
  );
42690
42791
  }
42691
42792
  });
@@ -49184,6 +49285,7 @@ function isPlainConfigObject(value) {
49184
49285
  return proto === Object.prototype || proto === null;
49185
49286
  }
49186
49287
  function subtreeHasTraitRef(value) {
49288
+ if (isEvaluatorResolvedData(value)) return false;
49187
49289
  const cached = traitRefPresenceCache.get(value);
49188
49290
  if (cached !== void 0) return cached;
49189
49291
  let found = false;
@@ -49256,6 +49358,10 @@ function renderPatternProps(props, onDismiss, propsSchema) {
49256
49358
  };
49257
49359
  rendered[key] = /* @__PURE__ */ jsx(SlotContentRenderer, { content: childContent, onDismiss });
49258
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
+ }
49259
49365
  const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
49260
49366
  rendered[key] = value.map((item, i) => {
49261
49367
  const el = item;
@@ -49734,6 +49840,9 @@ function enqueueEvent(queue, entry) {
49734
49840
  }
49735
49841
  queue.push(entry);
49736
49842
  }
49843
+
49844
+ // hooks/useTraitStateMachine.ts
49845
+ init_perf();
49737
49846
  var lambdaLog = createLogger("almadar:ui:fn-form-lambda");
49738
49847
  function isOperatorCall(value) {
49739
49848
  const first = value[0];
@@ -50592,6 +50701,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50592
50701
  }, [eventBus]);
50593
50702
  useEffect(() => {
50594
50703
  const scheduler = createTickScheduler();
50704
+ const timedTick = (key, fn) => () => perfTimeAsync(key, fn);
50595
50705
  const pureWriterTickKeys = /* @__PURE__ */ new Set();
50596
50706
  for (const group of sharedGroups.values()) {
50597
50707
  const ticksByInterval = /* @__PURE__ */ new Map();
@@ -50608,12 +50718,12 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50608
50718
  }
50609
50719
  for (const entries of ticksByInterval.values()) {
50610
50720
  const interval = entries[0].tick.interval;
50611
- const onDue = () => {
50721
+ const onDue = timedTick(`tick:shared:${group.storeKey}@${String(interval)}`, () => {
50612
50722
  const writers = entries.map(
50613
50723
  ({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
50614
50724
  );
50615
50725
  runTickFrame(group.storeKey, writers, sharedEntityStore);
50616
- };
50726
+ });
50617
50727
  if (interval === "frame") {
50618
50728
  scheduler.add(0, onDue);
50619
50729
  } else if (typeof interval === "number") {
@@ -50631,14 +50741,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50631
50741
  if (sharedKey !== void 0 && pureWriterTickKeys.has(`${binding.trait.name}::${tick.name}`)) {
50632
50742
  continue;
50633
50743
  }
50744
+ const tickKey = `tick:${binding.trait.name}::${tick.name}`;
50634
50745
  if (tick.interval === "frame") {
50635
- scheduler.add(0, () => runTickEffects(tick, binding));
50746
+ scheduler.add(0, timedTick(tickKey, () => runTickEffects(tick, binding)));
50636
50747
  } else if (typeof tick.interval === "number") {
50637
- scheduler.add(tick.interval, () => runTickEffects(tick, binding));
50748
+ scheduler.add(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
50638
50749
  } else if (isValidCronExpression(tick.interval)) {
50639
- scheduler.addCron(tick.interval, () => runTickEffects(tick, binding));
50750
+ scheduler.addCron(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
50640
50751
  } else {
50641
- scheduler.add(parseDurationString(tick.interval), () => runTickEffects(tick, binding));
50752
+ scheduler.add(parseDurationString(tick.interval), timedTick(tickKey, () => runTickEffects(tick, binding)));
50642
50753
  }
50643
50754
  }
50644
50755
  }
@@ -50646,6 +50757,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50646
50757
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
50647
50758
  const processEventQueued = useCallback(async (eventKey, payload, targetTrait, tick, sourceTrait) => {
50648
50759
  const normalizedEvent = normalizeEventKey(eventKey);
50760
+ const _perfT0 = perfStart("processEvent:total");
50649
50761
  const bindings = traitBindingsRef.current;
50650
50762
  const currentManager = managerRef.current;
50651
50763
  crossTraitLog.debug("processEvent:enter", () => ({
@@ -50669,6 +50781,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50669
50781
  entityByTrait[name] = { ...sharedEntityStore.getSnapshot(sharedKey) };
50670
50782
  }
50671
50783
  }
50784
+ const _perfT1 = perfStart("processEvent:guardMatch");
50672
50785
  const results = currentManager.sendEvent(
50673
50786
  normalizedEvent,
50674
50787
  payload,
@@ -50677,6 +50790,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50677
50790
  void 0,
50678
50791
  targetTrait
50679
50792
  );
50793
+ perfEnd("processEvent:guardMatch", _perfT1);
50680
50794
  crossTraitLog.debug("processEvent:results", {
50681
50795
  event: normalizedEvent,
50682
50796
  executedCount: results.length,
@@ -50726,6 +50840,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50726
50840
  transition: `${result.previousState} -> ${result.newState}`,
50727
50841
  effects: JSON.stringify(result.effects)
50728
50842
  }));
50843
+ const _perfT2 = perfStart("processEvent:executeAll");
50729
50844
  const emittedDuringExec = await executeTransitionEffects({
50730
50845
  binding,
50731
50846
  // upstream gap: /runtime TransitionResult.effects is unknown[] — they are SExpr at runtime (see Almadar_UI_Gaps.md)
@@ -50737,6 +50852,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50737
50852
  syncOnly: false,
50738
50853
  log: stateLog
50739
50854
  });
50855
+ perfEnd("processEvent:executeAll", _perfT2);
50740
50856
  emittedByTrait.set(traitName, emittedDuringExec);
50741
50857
  for (const emittedKey of emittedDuringExec) {
50742
50858
  bridgeEchoPendingRef.current.set(
@@ -50833,27 +50949,31 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50833
50949
  if (orbital) dispatchedOrbitals.add(orbital);
50834
50950
  }
50835
50951
  const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
50836
- if (tick !== void 0) {
50837
- void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
50838
- } else {
50839
- await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
50840
- }
50952
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
50841
50953
  }
50954
+ perfEnd("processEvent:total", _perfT0);
50955
+ perfEnd(`event:${normalizedEvent}`, _perfT0);
50842
50956
  }, [entities, eventBus, sharedEntityStore]);
50843
50957
  const drainEventQueue = useCallback(async () => {
50844
50958
  if (processingRef.current) return;
50845
50959
  processingRef.current = true;
50960
+ const _perfT = perfStart("drain:pass");
50961
+ let _perfN = 0;
50846
50962
  try {
50847
50963
  while (eventQueueRef.current.length > 0) {
50848
50964
  const entry = eventQueueRef.current.shift();
50965
+ _perfN++;
50849
50966
  await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait, entry.tick, entry.sourceTrait);
50850
50967
  }
50851
50968
  } finally {
50852
50969
  processingRef.current = false;
50970
+ perfEnd("drain:pass", _perfT);
50971
+ perfGauge("drain:passEntries", _perfN);
50853
50972
  }
50854
50973
  }, [processEventQueued]);
50855
50974
  const enqueueAndDrain = useCallback((eventKey, payload, targetTrait, tick, sourceTrait) => {
50856
50975
  enqueueEvent(eventQueueRef.current, { eventKey, payload, targetTrait, tick, sourceTrait });
50976
+ perfGauge("queue:depthAtEnqueue", eventQueueRef.current.length);
50857
50977
  void drainEventQueue();
50858
50978
  }, [drainEventQueue]);
50859
50979
  useCallback((eventKey, payload) => {
@@ -51255,7 +51375,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
51255
51375
  [serverActiveTraits]
51256
51376
  );
51257
51377
  const uiSlots = useUISlots();
51258
- const onEventProcessed = useCallback(async (event, payload, dispatchedOrbitals, tick, sourceTrait) => {
51378
+ const onEventProcessed = useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait) => {
51259
51379
  if (!bridge.connected || !orbitalNames?.length) return;
51260
51380
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
51261
51381
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -51269,9 +51389,10 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
51269
51389
  void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
51270
51390
  continue;
51271
51391
  }
51272
- const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
51273
- recordServerResponse(name, event, meta);
51274
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
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
+ });
51275
51396
  }
51276
51397
  }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits]);
51277
51398
  const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, navigateBack: onNavigateBack, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams } : { navigate: onNavigate, navigateBack: onNavigateBack, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams };
@@ -51589,14 +51710,15 @@ function OrbPreview({
51589
51710
  return pattern.replace(/:([A-Za-z0-9_]+)/g, (whole, key) => routeParams[key] ?? whole);
51590
51711
  }, [currentPagePath, routeParams]);
51591
51712
  const navStackRef = useRef(null);
51592
- const handleNavigate = useCallback((path) => {
51713
+ const handleNavigate = useCallback((path, navState) => {
51593
51714
  const hit = matchPathAmong(pages, path, (entry) => entry.page.path);
51594
51715
  const match = hit?.candidate;
51595
- const params = hit?.params ?? {};
51716
+ const params = { ...hit?.params ?? {}, ...navState ?? {} };
51596
51717
  navLog.debug("handleNavigate", () => ({
51597
51718
  path,
51598
51719
  matched: match?.page.name ?? null,
51599
51720
  params,
51721
+ navState: navState ? JSON.stringify(navState) : void 0,
51600
51722
  availablePaths: pages.map((p) => p.page.path)
51601
51723
  }));
51602
51724
  if (match?.page.name) {
@@ -51611,9 +51733,9 @@ function OrbPreview({
51611
51733
  }
51612
51734
  }, [pages]);
51613
51735
  const handleNavigateEffect = useCallback(
51614
- (path, _params, crumb) => {
51736
+ (path, params, crumb) => {
51615
51737
  navStackRef.current?.beginNavigate(path, crumb);
51616
- handleNavigate(path);
51738
+ handleNavigate(path, params);
51617
51739
  },
51618
51740
  [handleNavigate]
51619
51741
  );
@@ -51768,16 +51890,8 @@ function BrowserPlayground({
51768
51890
  }
51769
51891
  );
51770
51892
  }
51771
- function usePerfBuffer() {
51772
- return useSyncExternalStore(perfStore.subscribe, perfStore.getSnapshot, perfStore.getSnapshot);
51773
- }
51774
- var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
51775
- pushPerfEntry({
51776
- name: `profiler:${id}:${phase}`,
51777
- durationMs: actualDuration,
51778
- ts: commitTime,
51779
- detail: { baseDuration }
51780
- });
51781
- };
51893
+
51894
+ // runtime/index.ts
51895
+ init_perf();
51782
51896
 
51783
51897
  export { BrowserPlayground, OrbPreview, clearSchemaCache, createClientEffectHandlers, profilerOnRender, usePerfBuffer, useResolvedSchema, useTraitStateMachine };
package/locales/ar.json CHANGED
@@ -10,6 +10,7 @@
10
10
  "common.confirm": "هل أنت متأكد؟",
11
11
  "common.create": "إنشاء",
12
12
  "common.edit": "تعديل",
13
+ "common.title": "العنوان",
13
14
  "common.view": "عرض",
14
15
  "common.add": "إضافة",
15
16
  "common.remove": "إزالة",
package/locales/en.json CHANGED
@@ -10,6 +10,7 @@
10
10
  "common.confirm": "Are you sure?",
11
11
  "common.create": "Create",
12
12
  "common.edit": "Edit",
13
+ "common.title": "Title",
13
14
  "common.view": "View",
14
15
  "common.add": "Add",
15
16
  "common.remove": "Remove",
package/locales/sl.json CHANGED
@@ -10,6 +10,7 @@
10
10
  "common.confirm": "Ali ste prepričani?",
11
11
  "common.create": "Ustvari",
12
12
  "common.edit": "Uredi",
13
+ "common.title": "Naslov",
13
14
  "common.view": "Prikaži",
14
15
  "common.add": "Dodaj",
15
16
  "common.remove": "Odstrani",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.158.0",
3
+ "version": "5.159.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -118,11 +118,11 @@
118
118
  "access": "public"
119
119
  },
120
120
  "dependencies": {
121
- "@almadar/core": "^10.68.0",
122
- "@almadar/evaluator": "^2.41.0",
121
+ "@almadar/core": "^10.69.0",
122
+ "@almadar/evaluator": "^2.42.0",
123
123
  "@almadar/logger": "^1.11.0",
124
- "@almadar/runtime": "^6.59.0",
125
- "@almadar/std": "^16.184.0",
124
+ "@almadar/runtime": "^6.60.0",
125
+ "@almadar/std": "^16.185.0",
126
126
  "@almadar/syntax": "^1.15.0",
127
127
  "@dnd-kit/core": "^6.3.1",
128
128
  "@dnd-kit/sortable": "^10.0.0",