@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.
@@ -5,11 +5,11 @@ import * as _almadar_runtime from '@almadar/runtime';
5
5
  import { TraitState, EffectHandlers } from '@almadar/runtime';
6
6
  import '../useEventBus-CQWyAWpK.js';
7
7
  import { c as useUISlots } from '../UISlotContext-CB89mv7N.js';
8
- import { a as EntityBindingSource, h as ServerBridgeTransport } from '../EntityBindingContext-Bn3ePJQC.js';
9
- export { b as EntitySchemaContextValue, c as EntitySchemaProvider, d as EntitySchemaProviderProps, e as ServerBridgeContextValue, f as ServerBridgeProvider, i as ServerClientEffect, T as TraitContext, k as TraitContextValue, l as TraitInstance, m as TraitProvider, n as TraitProviderProps, o as useEntitySchema, p as useEntitySchemaOptional, q as useServerBridge, r as useTrait, s as useTraitContext } from '../EntityBindingContext-Bn3ePJQC.js';
10
- import { PerfEntry } from '@almadar/runtime/ui';
8
+ import { E as EntityBindingSource, S as ServerBridgeTransport } from '../EntityBindingContext-0Evn_LcT.js';
9
+ export { a as EntitySchemaContextValue, b as EntitySchemaProvider, c as EntitySchemaProviderProps, d as ServerBridgeContextValue, e as ServerBridgeProvider, f as ServerClientEffect, T as TraitContext, g as TraitContextValue, h as TraitInstance, i as TraitProvider, j as TraitProviderProps, u as useEntitySchema, k as useEntitySchemaOptional, l as useServerBridge, m as useTrait, n as useTraitContext } from '../EntityBindingContext-0Evn_LcT.js';
11
10
  export { PERF_NAMESPACE, PerfEntry, PreparedPreviewSchema, adjustSchemaForMockData, buildMockData, clearPerf, perfEnd, perfStart, perfTime, prepareSchemaForPreview, wrapCallbackForEvent } from '@almadar/runtime/ui';
12
- import React__default, { ProfilerOnRenderCallback } from 'react';
11
+ import React__default from 'react';
12
+ export { p as profilerOnRender, u as usePerfBuffer } from '../perf-DaVdsbU0.js';
13
13
  import '../event-bus-types-Bl78kokd.js';
14
14
  import '../useUISlots-GNwGLlW2.js';
15
15
  import '@almadar/core/patterns';
@@ -312,21 +312,4 @@ interface BrowserPlaygroundProps {
312
312
  }
313
313
  declare function BrowserPlayground({ schema, mode, initialPagePath, height, className, }: BrowserPlaygroundProps): React__default.ReactElement;
314
314
 
315
- /**
316
- * @almadar/ui/runtime — perf instrumentation
317
- *
318
- * React-specific consumption layer on top of the renderer-agnostic perf ring
319
- * now living in `@almadar/runtime/ui/perf`. Re-exports the framework-free
320
- * timing primitives and adds `useSyncExternalStore` / `React.Profiler` hooks.
321
- */
322
-
323
- /**
324
- * React hook: returns the current perf ring snapshot, re-renders on push.
325
- */
326
- declare function usePerfBuffer(): readonly PerfEntry[];
327
- /**
328
- * React.Profiler `onRender` callback. Records `actualDuration` per commit.
329
- */
330
- declare const profilerOnRender: ProfilerOnRenderCallback;
331
-
332
- export { BrowserPlayground, type BrowserPlaygroundProps, type ClientEventBus, type CreateClientEffectHandlersOptions, OrbPreview, type OrbPreviewProps, type ResolvedSchemaResult, ServerBridgeTransport, type SlotPatternEntry, type SlotSetter, type SlotSource, type TraitStateMachineResult, type UseTraitStateMachineOptions, clearSchemaCache, createClientEffectHandlers, profilerOnRender, usePerfBuffer, useResolvedSchema, useTraitStateMachine };
315
+ export { BrowserPlayground, type BrowserPlaygroundProps, type ClientEventBus, type CreateClientEffectHandlersOptions, OrbPreview, type OrbPreviewProps, type ResolvedSchemaResult, ServerBridgeTransport, type SlotPatternEntry, type SlotSetter, type SlotSource, type TraitStateMachineResult, type UseTraitStateMachineOptions, clearSchemaCache, createClientEffectHandlers, useResolvedSchema, useTraitStateMachine };
@@ -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();
@@ -26054,8 +26105,8 @@ function DataGrid({
26054
26105
  }
26055
26106
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
26056
26107
  field.icon && renderIconInput(field.icon, { size: "xs", className: "text-muted-foreground" }),
26057
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
26058
- /* @__PURE__ */ jsx(Typography, { variant: "small", children: formatValue(value, field.format) })
26108
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "sr-only", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
26109
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: formatValue(value, field.format) })
26059
26110
  ] }, field.name);
26060
26111
  }) })
26061
26112
  ] }) })
@@ -26483,7 +26534,11 @@ function DataList({
26483
26534
  Box,
26484
26535
  {
26485
26536
  className: cn(
26486
- "group flex items-center gap-4 transition-all duration-fast",
26537
+ // items-start, not items-center: a multi-line row (title + meta +
26538
+ // progress) centred its action cluster in the vertical middle, so
26539
+ // the buttons floated between the meta fields instead of anchoring
26540
+ // to the title they act on (U-DATALIST-ACTIONS-FLOAT-MID-ROW).
26541
+ "group flex items-start gap-4 transition-all duration-fast",
26487
26542
  isCompact ? "px-4 py-2" : "px-6 py-4",
26488
26543
  "hover:bg-muted/80",
26489
26544
  !isCard && !isCompact && "rounded-lg border border-transparent hover:border-border"
@@ -26514,11 +26569,19 @@ function DataList({
26514
26569
  if (value === void 0 || value === null || value === "") return null;
26515
26570
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
26516
26571
  field.icon && renderIconInput2(field.icon, { size: "xs", className: "text-muted-foreground" }),
26517
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
26518
- field.label ?? fieldLabel3(field.name),
26519
- ":"
26520
- ] }),
26521
- /* @__PURE__ */ jsx(Typography, { variant: "small", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
26572
+ /* @__PURE__ */ jsxs(
26573
+ Typography,
26574
+ {
26575
+ variant: "caption",
26576
+ color: "secondary",
26577
+ className: cn(field.format !== "boolean" && "sr-only"),
26578
+ children: [
26579
+ field.label ?? fieldLabel3(field.name),
26580
+ ":"
26581
+ ]
26582
+ }
26583
+ ),
26584
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
26522
26585
  ] }, field.name);
26523
26586
  }) }),
26524
26587
  progressFields.map((field) => {
@@ -26537,7 +26600,7 @@ function DataList({
26537
26600
  ]
26538
26601
  }
26539
26602
  ),
26540
- isCard && !isLast && /* @__PURE__ */ jsx(Box, { className: "mx-6 border-b border-border/40" })
26603
+ (isCard || isCompact) && !isLast && /* @__PURE__ */ jsx(Box, { className: cn("border-b border-border/40", isCompact ? "mx-4" : "mx-6") })
26541
26604
  ] }, id)
26542
26605
  );
26543
26606
  };
@@ -26547,7 +26610,13 @@ function DataList({
26547
26610
  {
26548
26611
  className: cn(
26549
26612
  isCard && "bg-card rounded-xl border border-border shadow-elevation-dialog overflow-hidden",
26550
- !isCard && gapClass,
26613
+ // `gap-*` is inert on a block container, and Box only emits a display
26614
+ // class when its `display` prop is set — so every non-card list had
26615
+ // been asking for a gap that CSS silently dropped. flex-col makes it
26616
+ // real. `compact` keeps gap-0 on purpose: it separates with the row
26617
+ // divider above instead, never both (Almadar_UI_Beauty.md 6).
26618
+ !isCard && "flex flex-col",
26619
+ !isCard && !isCompact && gapClass,
26551
26620
  listLookStyles[look],
26552
26621
  className
26553
26622
  ),
@@ -30098,6 +30167,7 @@ var init_MathCanvas = __esm({
30098
30167
  "components/learning/molecules/MathCanvas.tsx"() {
30099
30168
  "use client";
30100
30169
  init_useEventBus();
30170
+ init_perf();
30101
30171
  init_atoms();
30102
30172
  init_Stack();
30103
30173
  init_LearningCanvas();
@@ -30163,6 +30233,7 @@ var init_MathCanvas = __esm({
30163
30233
  };
30164
30234
  }, [stableKeyMap, stableKeyUpMap, eventBus]);
30165
30235
  const derivedShapes = useMemo(() => {
30236
+ const _perfT = perfStart("mathcanvas:derive");
30166
30237
  const out = [];
30167
30238
  const margin = 24;
30168
30239
  const plotW = width - margin * 2;
@@ -30406,6 +30477,7 @@ var init_MathCanvas = __esm({
30406
30477
  }
30407
30478
  }
30408
30479
  out.push(...shapes);
30480
+ perfEnd("mathcanvas:derive", _perfT);
30409
30481
  return out;
30410
30482
  }, [
30411
30483
  width,
@@ -36621,7 +36693,7 @@ var init_RichBlockEditor = __esm({
36621
36693
  {
36622
36694
  variant: "bordered",
36623
36695
  padding: "none",
36624
- className: cn("flex flex-col", className),
36696
+ className: cn("flex flex-col text-card-foreground", className),
36625
36697
  children: [
36626
36698
  enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsx(
36627
36699
  Box,
@@ -41849,6 +41921,7 @@ var init_DetailPanel = __esm({
41849
41921
  "use client";
41850
41922
  init_atoms();
41851
41923
  init_Box();
41924
+ init_Input();
41852
41925
  init_Stack();
41853
41926
  init_SimpleGrid();
41854
41927
  init_Menu();
@@ -41864,6 +41937,7 @@ var init_DetailPanel = __esm({
41864
41937
  ReactMarkdown2 = lazy(() => import('react-markdown'));
41865
41938
  DetailPanel = ({
41866
41939
  title: propTitle,
41940
+ onTitleCommit,
41867
41941
  subtitle,
41868
41942
  status,
41869
41943
  avatar,
@@ -41885,6 +41959,7 @@ var init_DetailPanel = __esm({
41885
41959
  }) => {
41886
41960
  const eventBus = useEventBus();
41887
41961
  const { t } = useTranslate();
41962
+ const [titleDraft, setTitleDraft] = React85__default.useState(null);
41888
41963
  const isFieldDefArray = (arr) => {
41889
41964
  if (!arr || arr.length === 0) return false;
41890
41965
  const first = arr[0];
@@ -42105,6 +42180,50 @@ var init_DetailPanel = __esm({
42105
42180
  }),
42106
42181
  status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42107
42182
  ] });
42183
+ const commitTitle = () => {
42184
+ if (!onTitleCommit) return;
42185
+ const next = (titleDraft ?? "").trim();
42186
+ setTitleDraft(null);
42187
+ if (!next || next === title) return;
42188
+ onTitleCommit(next, normalizedData?.id !== void 0 ? String(normalizedData.id) : "");
42189
+ };
42190
+ const titleNode = onTitleCommit && titleDraft !== null ? /* @__PURE__ */ jsx(
42191
+ Input,
42192
+ {
42193
+ value: titleDraft,
42194
+ autoFocus: true,
42195
+ "aria-label": t("common.title"),
42196
+ className: "h-auto py-1 text-3xl font-bold tracking-tight",
42197
+ onChange: (e) => setTitleDraft(e.target.value),
42198
+ onBlur: commitTitle,
42199
+ onKeyDown: (e) => {
42200
+ if (e.key === "Enter") {
42201
+ e.preventDefault();
42202
+ commitTitle();
42203
+ } else if (e.key === "Escape") {
42204
+ e.preventDefault();
42205
+ setTitleDraft(null);
42206
+ }
42207
+ },
42208
+ "data-testid": "detail-title-input"
42209
+ }
42210
+ ) : onTitleCommit ? /* @__PURE__ */ jsx(
42211
+ Box,
42212
+ {
42213
+ role: "button",
42214
+ tabIndex: 0,
42215
+ className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
42216
+ onClick: () => setTitleDraft(title ?? ""),
42217
+ onKeyDown: (e) => {
42218
+ if (e.key === "Enter" || e.key === " ") {
42219
+ e.preventDefault();
42220
+ setTitleDraft(title ?? "");
42221
+ }
42222
+ },
42223
+ "data-testid": "detail-title-editable",
42224
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" })
42225
+ }
42226
+ ) : /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" });
42108
42227
  const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
42109
42228
  /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
42110
42229
  /* @__PURE__ */ jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
@@ -42124,7 +42243,7 @@ var init_DetailPanel = __esm({
42124
42243
  avatar,
42125
42244
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
42126
42245
  /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
42127
- /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42246
+ titleNode,
42128
42247
  statusBadges
42129
42248
  ] }),
42130
42249
  subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
@@ -42224,16 +42343,11 @@ var init_DetailPanel = __esm({
42224
42343
  footer
42225
42344
  ] })
42226
42345
  ] }) });
42227
- return /* @__PURE__ */ jsx(
42228
- Box,
42229
- {
42230
- className: cn(
42231
- slideOver && "fixed inset-y-0 right-0 w-full max-w-2xl bg-card shadow-lg z-50 overflow-y-auto p-6",
42232
- className
42233
- ),
42234
- children: content
42235
- }
42236
- );
42346
+ if (!slideOver) {
42347
+ return /* @__PURE__ */ jsx(Box, { className, children: content });
42348
+ }
42349
+ const panel = /* @__PURE__ */ 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 });
42350
+ return typeof document === "undefined" ? panel : createPortal(panel, document.body);
42237
42351
  };
42238
42352
  DetailPanel.displayName = "DetailPanel";
42239
42353
  }
@@ -42685,7 +42799,7 @@ var init_Form = __esm({
42685
42799
  });
42686
42800
  debug(
42687
42801
  "forms",
42688
- `Calculation triggered: ${calc.variableName} = ${value}`
42802
+ `Calculation triggered: ${calc.variableName} = ${String(value)}`
42689
42803
  );
42690
42804
  }
42691
42805
  });
@@ -49184,6 +49298,7 @@ function isPlainConfigObject(value) {
49184
49298
  return proto === Object.prototype || proto === null;
49185
49299
  }
49186
49300
  function subtreeHasTraitRef(value) {
49301
+ if (isEvaluatorResolvedData(value)) return false;
49187
49302
  const cached = traitRefPresenceCache.get(value);
49188
49303
  if (cached !== void 0) return cached;
49189
49304
  let found = false;
@@ -49256,6 +49371,10 @@ function renderPatternProps(props, onDismiss, propsSchema) {
49256
49371
  };
49257
49372
  rendered[key] = /* @__PURE__ */ jsx(SlotContentRenderer, { content: childContent, onDismiss });
49258
49373
  } else if (Array.isArray(value)) {
49374
+ if (!isEvaluatorResolvedData(value) && value.some((el) => isPatternConfig(el))) ; else if (!subtreeHasTraitRef(value)) {
49375
+ rendered[key] = value;
49376
+ continue;
49377
+ }
49259
49378
  const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
49260
49379
  rendered[key] = value.map((item, i) => {
49261
49380
  const el = item;
@@ -49734,6 +49853,9 @@ function enqueueEvent(queue, entry) {
49734
49853
  }
49735
49854
  queue.push(entry);
49736
49855
  }
49856
+
49857
+ // hooks/useTraitStateMachine.ts
49858
+ init_perf();
49737
49859
  var lambdaLog = createLogger("almadar:ui:fn-form-lambda");
49738
49860
  function isOperatorCall(value) {
49739
49861
  const first = value[0];
@@ -50592,6 +50714,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50592
50714
  }, [eventBus]);
50593
50715
  useEffect(() => {
50594
50716
  const scheduler = createTickScheduler();
50717
+ const timedTick = (key, fn) => () => perfTimeAsync(key, fn);
50595
50718
  const pureWriterTickKeys = /* @__PURE__ */ new Set();
50596
50719
  for (const group of sharedGroups.values()) {
50597
50720
  const ticksByInterval = /* @__PURE__ */ new Map();
@@ -50608,12 +50731,12 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50608
50731
  }
50609
50732
  for (const entries of ticksByInterval.values()) {
50610
50733
  const interval = entries[0].tick.interval;
50611
- const onDue = () => {
50734
+ const onDue = timedTick(`tick:shared:${group.storeKey}@${String(interval)}`, () => {
50612
50735
  const writers = entries.map(
50613
50736
  ({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
50614
50737
  );
50615
50738
  runTickFrame(group.storeKey, writers, sharedEntityStore);
50616
- };
50739
+ });
50617
50740
  if (interval === "frame") {
50618
50741
  scheduler.add(0, onDue);
50619
50742
  } else if (typeof interval === "number") {
@@ -50631,14 +50754,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50631
50754
  if (sharedKey !== void 0 && pureWriterTickKeys.has(`${binding.trait.name}::${tick.name}`)) {
50632
50755
  continue;
50633
50756
  }
50757
+ const tickKey = `tick:${binding.trait.name}::${tick.name}`;
50634
50758
  if (tick.interval === "frame") {
50635
- scheduler.add(0, () => runTickEffects(tick, binding));
50759
+ scheduler.add(0, timedTick(tickKey, () => runTickEffects(tick, binding)));
50636
50760
  } else if (typeof tick.interval === "number") {
50637
- scheduler.add(tick.interval, () => runTickEffects(tick, binding));
50761
+ scheduler.add(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
50638
50762
  } else if (isValidCronExpression(tick.interval)) {
50639
- scheduler.addCron(tick.interval, () => runTickEffects(tick, binding));
50763
+ scheduler.addCron(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
50640
50764
  } else {
50641
- scheduler.add(parseDurationString(tick.interval), () => runTickEffects(tick, binding));
50765
+ scheduler.add(parseDurationString(tick.interval), timedTick(tickKey, () => runTickEffects(tick, binding)));
50642
50766
  }
50643
50767
  }
50644
50768
  }
@@ -50646,6 +50770,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50646
50770
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
50647
50771
  const processEventQueued = useCallback(async (eventKey, payload, targetTrait, tick, sourceTrait) => {
50648
50772
  const normalizedEvent = normalizeEventKey(eventKey);
50773
+ const _perfT0 = perfStart("processEvent:total");
50649
50774
  const bindings = traitBindingsRef.current;
50650
50775
  const currentManager = managerRef.current;
50651
50776
  crossTraitLog.debug("processEvent:enter", () => ({
@@ -50669,6 +50794,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50669
50794
  entityByTrait[name] = { ...sharedEntityStore.getSnapshot(sharedKey) };
50670
50795
  }
50671
50796
  }
50797
+ const _perfT1 = perfStart("processEvent:guardMatch");
50672
50798
  const results = currentManager.sendEvent(
50673
50799
  normalizedEvent,
50674
50800
  payload,
@@ -50677,6 +50803,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50677
50803
  void 0,
50678
50804
  targetTrait
50679
50805
  );
50806
+ perfEnd("processEvent:guardMatch", _perfT1);
50680
50807
  crossTraitLog.debug("processEvent:results", {
50681
50808
  event: normalizedEvent,
50682
50809
  executedCount: results.length,
@@ -50726,6 +50853,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50726
50853
  transition: `${result.previousState} -> ${result.newState}`,
50727
50854
  effects: JSON.stringify(result.effects)
50728
50855
  }));
50856
+ const _perfT2 = perfStart("processEvent:executeAll");
50729
50857
  const emittedDuringExec = await executeTransitionEffects({
50730
50858
  binding,
50731
50859
  // upstream gap: /runtime TransitionResult.effects is unknown[] — they are SExpr at runtime (see Almadar_UI_Gaps.md)
@@ -50737,6 +50865,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50737
50865
  syncOnly: false,
50738
50866
  log: stateLog
50739
50867
  });
50868
+ perfEnd("processEvent:executeAll", _perfT2);
50740
50869
  emittedByTrait.set(traitName, emittedDuringExec);
50741
50870
  for (const emittedKey of emittedDuringExec) {
50742
50871
  bridgeEchoPendingRef.current.set(
@@ -50833,27 +50962,31 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50833
50962
  if (orbital) dispatchedOrbitals.add(orbital);
50834
50963
  }
50835
50964
  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
- }
50965
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
50841
50966
  }
50967
+ perfEnd("processEvent:total", _perfT0);
50968
+ perfEnd(`event:${normalizedEvent}`, _perfT0);
50842
50969
  }, [entities, eventBus, sharedEntityStore]);
50843
50970
  const drainEventQueue = useCallback(async () => {
50844
50971
  if (processingRef.current) return;
50845
50972
  processingRef.current = true;
50973
+ const _perfT = perfStart("drain:pass");
50974
+ let _perfN = 0;
50846
50975
  try {
50847
50976
  while (eventQueueRef.current.length > 0) {
50848
50977
  const entry = eventQueueRef.current.shift();
50978
+ _perfN++;
50849
50979
  await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait, entry.tick, entry.sourceTrait);
50850
50980
  }
50851
50981
  } finally {
50852
50982
  processingRef.current = false;
50983
+ perfEnd("drain:pass", _perfT);
50984
+ perfGauge("drain:passEntries", _perfN);
50853
50985
  }
50854
50986
  }, [processEventQueued]);
50855
50987
  const enqueueAndDrain = useCallback((eventKey, payload, targetTrait, tick, sourceTrait) => {
50856
50988
  enqueueEvent(eventQueueRef.current, { eventKey, payload, targetTrait, tick, sourceTrait });
50989
+ perfGauge("queue:depthAtEnqueue", eventQueueRef.current.length);
50857
50990
  void drainEventQueue();
50858
50991
  }, [drainEventQueue]);
50859
50992
  useCallback((eventKey, payload) => {
@@ -51255,7 +51388,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
51255
51388
  [serverActiveTraits]
51256
51389
  );
51257
51390
  const uiSlots = useUISlots();
51258
- const onEventProcessed = useCallback(async (event, payload, dispatchedOrbitals, tick, sourceTrait) => {
51391
+ const onEventProcessed = useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait) => {
51259
51392
  if (!bridge.connected || !orbitalNames?.length) return;
51260
51393
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
51261
51394
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -51269,9 +51402,10 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
51269
51402
  void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
51270
51403
  continue;
51271
51404
  }
51272
- const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
51273
- recordServerResponse(name, event, meta);
51274
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
51405
+ void bridge.sendEvent(name, event, withActiveTraits(payload)).then(({ effects, meta }) => {
51406
+ recordServerResponse(name, event, meta);
51407
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
51408
+ });
51275
51409
  }
51276
51410
  }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits]);
51277
51411
  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 +51723,15 @@ function OrbPreview({
51589
51723
  return pattern.replace(/:([A-Za-z0-9_]+)/g, (whole, key) => routeParams[key] ?? whole);
51590
51724
  }, [currentPagePath, routeParams]);
51591
51725
  const navStackRef = useRef(null);
51592
- const handleNavigate = useCallback((path) => {
51726
+ const handleNavigate = useCallback((path, navState) => {
51593
51727
  const hit = matchPathAmong(pages, path, (entry) => entry.page.path);
51594
51728
  const match = hit?.candidate;
51595
- const params = hit?.params ?? {};
51729
+ const params = { ...hit?.params ?? {}, ...navState ?? {} };
51596
51730
  navLog.debug("handleNavigate", () => ({
51597
51731
  path,
51598
51732
  matched: match?.page.name ?? null,
51599
51733
  params,
51734
+ navState: navState ? JSON.stringify(navState) : void 0,
51600
51735
  availablePaths: pages.map((p) => p.page.path)
51601
51736
  }));
51602
51737
  if (match?.page.name) {
@@ -51611,9 +51746,9 @@ function OrbPreview({
51611
51746
  }
51612
51747
  }, [pages]);
51613
51748
  const handleNavigateEffect = useCallback(
51614
- (path, _params, crumb) => {
51749
+ (path, params, crumb) => {
51615
51750
  navStackRef.current?.beginNavigate(path, crumb);
51616
- handleNavigate(path);
51751
+ handleNavigate(path, params);
51617
51752
  },
51618
51753
  [handleNavigate]
51619
51754
  );
@@ -51768,16 +51903,8 @@ function BrowserPlayground({
51768
51903
  }
51769
51904
  );
51770
51905
  }
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
- };
51906
+
51907
+ // runtime/index.ts
51908
+ init_perf();
51782
51909
 
51783
51910
  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",