@almadar/ui 6.53.1 → 6.55.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.
Files changed (120) hide show
  1. package/dist/{AwaitingSkeletonContext-Bh399p1x.d.ts → AwaitingSkeletonContext-DPgW__WV.d.ts} +3 -1
  2. package/dist/{Canvas3DHost-B-y2TNij.d.ts → Canvas3DHost-DfAhg6EH.d.ts} +2 -0
  3. package/dist/Typography-7PDT62PG.js +5 -0
  4. package/dist/{UISlotRenderer-P7A4SZSK.js → UISlotRenderer-QTHFT6Z4.js} +8 -8
  5. package/dist/avl/index.cjs +3031 -1790
  6. package/dist/avl/index.js +57 -745
  7. package/dist/{avl-3d-context-MAt_NuU9.d.ts → avl-3d-context-BpBKZIbc.d.ts} +1 -0
  8. package/dist/{chunk-ERXIHZW3.js → chunk-D2E44ODB.js} +29 -15
  9. package/dist/{chunk-UOTYAPV5.js → chunk-F4ZTPKZL.js} +3 -3
  10. package/dist/{chunk-53VTY5CC.js → chunk-FQM37C56.js} +352 -162
  11. package/dist/{chunk-HB4C27KU.js → chunk-HCSJHRC4.js} +1 -1
  12. package/dist/{chunk-7KSLPY56.js → chunk-HRCR53OW.js} +11 -1
  13. package/dist/{chunk-O7OLVVBR.js → chunk-Q47ZN2DW.js} +24 -22
  14. package/dist/{chunk-DQTV6QVS.js → chunk-QGH4NAQO.js} +53 -2
  15. package/dist/{chunk-QVXLKFUQ.js → chunk-RNY5LJXJ.js} +1 -1
  16. package/dist/{chunk-FUQASYEW.js → chunk-SGJHVPHC.js} +2 -2
  17. package/dist/{chunk-6O37FCX4.js → chunk-V3FGTQUE.js} +37 -2
  18. package/dist/{chunk-HUG25TT3.js → chunk-XZKGRSIO.js} +2745 -1644
  19. package/dist/{chunk-7LM6I3NA.js → chunk-Y53Q55H5.js} +522 -24
  20. package/dist/components/index.cjs +4614 -2858
  21. package/dist/components/index.d.ts +122 -53
  22. package/dist/components/index.js +16 -17
  23. package/dist/context/index.cjs +38 -1
  24. package/dist/context/index.d.ts +1 -1
  25. package/dist/context/index.js +3 -1
  26. package/dist/design-classes/index.cjs +29 -15
  27. package/dist/design-classes/index.d.cts +1 -0
  28. package/dist/design-classes/index.d.ts +1 -0
  29. package/dist/design-classes/index.js +29 -15
  30. package/dist/hooks/index.cjs +16 -1
  31. package/dist/hooks/index.d.ts +10 -1
  32. package/dist/hooks/index.js +3 -4
  33. package/dist/lib/drawable/three/index.cjs +444 -82
  34. package/dist/lib/drawable/three/index.d.ts +4 -4
  35. package/dist/lib/drawable/three/index.js +28 -24
  36. package/dist/lib/index.cjs +53 -3
  37. package/dist/lib/index.d.ts +1 -1
  38. package/dist/lib/index.js +4 -4
  39. package/dist/locales/index.cjs +33 -3
  40. package/dist/locales/index.js +23 -3
  41. package/dist/providers/index.cjs +4987 -3183
  42. package/dist/providers/index.d.ts +2 -2
  43. package/dist/providers/index.js +14 -11
  44. package/dist/runtime/index.cjs +8973 -7000
  45. package/dist/runtime/index.d.ts +28 -6
  46. package/dist/runtime/index.js +16 -13
  47. package/dist/ssr/index.cjs +26 -9
  48. package/dist/ssr/index.js +26 -9
  49. package/dist/{themeTokens-Ccu7EmGY.d.ts → themeTokens-D6MFwKrC.d.ts} +6 -2
  50. package/locales/ar.json +11 -1
  51. package/locales/en.json +11 -1
  52. package/locales/sl.json +11 -1
  53. package/package.json +8 -7
  54. package/scripts/audit-tailwind-safelist.ts +21 -1
  55. package/scripts/theme-contrast-audit.mjs +20 -1
  56. package/tailwind-preset.cjs +5 -6
  57. package/themes/_base.css +61 -0
  58. package/themes/_contract.md +37 -0
  59. package/themes/_fonts.css +6 -4
  60. package/themes/_presets.css +2 -0
  61. package/themes/almadar-heritage.css +455 -0
  62. package/themes/almadar-website.css +82 -0
  63. package/themes/almadar.css +82 -0
  64. package/themes/aqua.css +82 -0
  65. package/themes/arabesque.css +82 -0
  66. package/themes/art-deco.css +82 -0
  67. package/themes/art-nouveau.css +82 -0
  68. package/themes/atelier.css +82 -0
  69. package/themes/bauhaus.css +82 -0
  70. package/themes/bloomberg-dense.css +83 -1
  71. package/themes/blueprint.css +84 -0
  72. package/themes/clay.css +82 -0
  73. package/themes/comic.css +82 -0
  74. package/themes/constructivist.css +82 -0
  75. package/themes/corporate.css +82 -0
  76. package/themes/cyberpunk.css +82 -0
  77. package/themes/frutiger-aero.css +83 -0
  78. package/themes/game-adventure.css +15 -0
  79. package/themes/game-rpg.css +16 -0
  80. package/themes/game-sci-fi.css +15 -0
  81. package/themes/game-ui-pack.css +15 -0
  82. package/themes/gazette.css +82 -0
  83. package/themes/glass.css +82 -0
  84. package/themes/gothic.css +82 -0
  85. package/themes/ink-wash.css +82 -0
  86. package/themes/kawaii.css +82 -0
  87. package/themes/kiosk.css +82 -0
  88. package/themes/linear-clean.css +82 -0
  89. package/themes/luxury.css +82 -0
  90. package/themes/mac-classic.css +90 -8
  91. package/themes/material.css +82 -0
  92. package/themes/memphis.css +82 -0
  93. package/themes/mid-century.css +82 -0
  94. package/themes/minimalist.css +82 -0
  95. package/themes/mudcloth.css +82 -0
  96. package/themes/neon.css +83 -0
  97. package/themes/neumorphic.css +82 -0
  98. package/themes/newsprint.css +82 -0
  99. package/themes/notion-editorial.css +82 -0
  100. package/themes/orb.css +455 -0
  101. package/themes/pixel.css +82 -0
  102. package/themes/prism.css +82 -0
  103. package/themes/retro.css +83 -0
  104. package/themes/risograph.css +82 -0
  105. package/themes/scandi.css +82 -0
  106. package/themes/sketch.css +82 -0
  107. package/themes/solarpunk.css +82 -0
  108. package/themes/steampunk.css +82 -0
  109. package/themes/swiss.css +82 -0
  110. package/themes/terminal.css +84 -1
  111. package/themes/trait-wars.css +82 -0
  112. package/themes/ukiyo-e.css +82 -0
  113. package/themes/vaporwave.css +82 -0
  114. package/themes/victorian.css +82 -0
  115. package/themes/wabi-sabi.css +82 -0
  116. package/themes/win95.css +84 -0
  117. package/themes/wireframe.css +82 -0
  118. package/themes/zine.css +82 -0
  119. package/dist/Typography-7QEWUTW3.js +0 -5
  120. package/dist/chunk-E37BYSXY.js +0 -51
@@ -1,18 +1,18 @@
1
- import { runTickFrame } from './chunk-DQTV6QVS.js';
1
+ import { useTranslate, useRenderI18n, runTickFrame } from './chunk-QGH4NAQO.js';
2
2
  import { ALL_SLOTS } from './chunk-VHTJRS2N.js';
3
- import { UISlotRenderer } from './chunk-HUG25TT3.js';
4
- import { Box } from './chunk-7LM6I3NA.js';
5
- import { Typography } from './chunk-HB4C27KU.js';
3
+ import { LoadingState, ErrorState, UISlotRenderer } from './chunk-XZKGRSIO.js';
4
+ import { Box } from './chunk-Y53Q55H5.js';
5
+ import { Typography } from './chunk-HCSJHRC4.js';
6
6
  import { beginPending, endPending } from './chunk-JRW33U35.js';
7
7
  import { useEventBus } from './chunk-6EV3OEBQ.js';
8
- import { isNotificationSlot } from './chunk-PEST66PN.js';
9
8
  import { registerTrait, unregisterTrait, perfTimeAsync } from './chunk-KQXXO42S.js';
10
- import { bindTraitStateGetter, registerTraitSnapshot, recordTransition, recordingTransport, recordServerResponse } from './chunk-O7OLVVBR.js';
9
+ import { bindTraitStateGetter, registerTraitSnapshot, recordTransition, recordingTransport, recordServerResponse } from './chunk-Q47ZN2DW.js';
10
+ import { isNotificationSlot } from './chunk-PEST66PN.js';
11
11
  import React, { useState, useMemo, useEffect, useRef, useCallback, useSyncExternalStore } from 'react';
12
- import { schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, applyListenPayloadMapping, buildResolvedTraitConfigs, collectCallsiteCaptureChildren, walkSExpr, isRenderBindingMarker, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER } from '@almadar/core';
12
+ import { schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, traitsEmbeddedByEvent, traitReferencesCallsitePayload, configReferencesCallsitePayload, TRAIT_MOUNT_ERROR_TESTID, traitsEmbeddedByEffects, applyListenPayloadMapping, buildResolvedTraitConfigs, collectCallsiteCaptureChildren, orbitalInlineEntities, storesRowsInBrowser, walkSExpr, isRenderBindingMarker, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER } from '@almadar/core';
13
13
  import { createLogger } from '@almadar/logger';
14
- import { collectListenerTargets, LIFECYCLE_EVENTS, InMemoryPersistence, createInProcessTransport, buildTraitIndex, createMemoryCircuitStore, createIndexStageRunner, evaluateOrbitalEvent, createClientKernel, createTickScheduler, isValidCronExpression, parseDurationString, UNMOUNT_EVENT, createClientEffectHandlers, EffectExecutor, isSExpression, collectDeclaredConfigDefaults, createContextFromBindings, normalizeCallSiteConfigToValues } from '@almadar/runtime';
15
- import { useUser, useArbitraryClassStyles, getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, useNavStack, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, ServerBridgeProvider, useServerBridge, EntityBindingContext, AwaitingSkeletonContext } from '@almadar/ui/providers';
14
+ import { collectListenerTargets, LIFECYCLE_EVENTS, TraitMountError, InMemoryPersistence, loadLazyPage, createInProcessTransport, buildTraitIndex, createMemoryCircuitStore, createLocalStoreTransport, createResidenceTransport, createClientKernel, createTickScheduler, isValidCronExpression, parseDurationString, UNMOUNT_EVENT, createClientEffectHandlers, EffectExecutor, isSExpression, collectDeclaredConfigDefaults, createContextFromBindings, openBrowserStore, browserStoreName, normalizeCallSiteConfigToValues } from '@almadar/runtime';
15
+ import { useUser, useHasServerBridge, useArbitraryClassStyles, getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, ServerBridgeProvider, useNavStack, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext, AwaitingSkeletonContext } from '@almadar/ui/providers';
16
16
  import { evaluateListenPayloadExpr, evaluate, createMinimalContext, evaluateGuard, executeEffects } from '@almadar/evaluator';
17
17
  import { getPatternDefinition } from '@almadar/core/patterns';
18
18
  import { isKnownStdOperator } from '@almadar/std/registry';
@@ -20,9 +20,10 @@ import { prepareSchemaForPreview, collectTraitRefsFromResolvedTrait, buildOrbita
20
20
  export { adjustSchemaForMockData, buildMockData, collectEmbeddedTraits, prepareSchemaForPreview } from '@almadar/runtime/ui';
21
21
  import { useInRouterContext, useHref } from 'react-router-dom';
22
22
  import { UISlotProvider, useUISlots } from '@almadar/ui/context';
23
- import { useTranslate } from '@almadar/ui/hooks';
23
+ import { useTranslate as useTranslate$1, createTranslate, I18nProvider } from '@almadar/ui/hooks';
24
24
  import { jsx, jsxs } from 'react/jsx-runtime';
25
25
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
26
+ import { localeDirection } from '@almadar/core/i18n';
26
27
 
27
28
  var resolvedSchemaLog = createLogger("almadar:ui:resolved-schema");
28
29
  function useResolvedSchema(schema, pageName) {
@@ -172,9 +173,9 @@ function getTabClientId() {
172
173
  return tabClientId;
173
174
  }
174
175
  var log = createLogger("almadar:ui:circuit-kernel");
175
- function postUnmounts(transport, traits) {
176
+ function postUnmounts(transport, traits, locale) {
176
177
  for (const [traitName, orbitalName] of traits) {
177
- transport.send(orbitalName, { event: UNMOUNT_EVENT, targetTrait: traitName, clientId: getTabClientId() }).catch((err) => log.warn("unmount-post-failed", { trait: traitName, orbital: orbitalName, error: String(err) }));
178
+ transport.send(orbitalName, { event: UNMOUNT_EVENT, targetTrait: traitName, clientId: getTabClientId(), locale }).catch((err) => log.warn("unmount-post-failed", { trait: traitName, orbital: orbitalName, error: String(err) }));
178
179
  }
179
180
  }
180
181
  function restrictTraitIndex(full, activeNames) {
@@ -185,16 +186,22 @@ function restrictTraitIndex(full, activeNames) {
185
186
  return { byName, allEntities: full.allEntities, orbitals: full.orbitals };
186
187
  }
187
188
  function useCircuitKernel(traitBindings, options) {
189
+ const hostRunsProgram = options.transport?.hostsBrowserStore === true;
188
190
  const fullTraitIndex = useMemo(
189
- () => buildTraitIndex(options.orbitals, options.traitConfigsByName),
190
- [options.orbitals, options.traitConfigsByName]
191
+ () => buildTraitIndex(options.orbitals, options.traitConfigsByName, { hostRunsProgram }),
192
+ [options.orbitals, options.traitConfigsByName, hostRunsProgram]
191
193
  );
194
+ const appTraitNames = useMemo(() => new Set(fullTraitIndex.byName.keys()), [fullTraitIndex]);
192
195
  const activeTraitKey = traitBindings.map((b) => b.trait.name).filter((n) => !!n).sort().join("\0");
193
196
  const traitIndex = useMemo(
194
197
  () => restrictTraitIndex(fullTraitIndex, new Set(activeTraitKey === "" ? [] : activeTraitKey.split("\0"))),
195
198
  [activeTraitKey, fullTraitIndex]
196
199
  );
197
200
  const mountedRef = useRef(/* @__PURE__ */ new Map());
201
+ const { locale } = useTranslate();
202
+ const i18n = useRenderI18n();
203
+ const localeRef = useRef(locale);
204
+ localeRef.current = locale;
198
205
  const transport = options.transport;
199
206
  const topologyPending = options.awaitTopology === true;
200
207
  useEffect(() => {
@@ -203,12 +210,12 @@ function useCircuitKernel(traitBindings, options) {
203
210
  const dropped = [...mounted].filter(([name]) => !traitIndex.byName.has(name));
204
211
  mounted.clear();
205
212
  for (const [name, entry] of traitIndex.byName) mounted.set(name, entry.orbitalName);
206
- postUnmounts(transport, dropped);
213
+ postUnmounts(transport, dropped, localeRef.current);
207
214
  }, [traitIndex, transport, topologyPending]);
208
215
  useEffect(() => {
209
216
  if (transport === void 0) return;
210
217
  const mounted = mountedRef.current;
211
- return () => postUnmounts(transport, [...mounted]);
218
+ return () => postUnmounts(transport, [...mounted], localeRef.current);
212
219
  }, [transport]);
213
220
  const store = useMemo(() => {
214
221
  const traitDefs = Array.from(traitIndex.byName.values(), (entry) => entry.traitDef);
@@ -219,42 +226,43 @@ function useCircuitKernel(traitBindings, options) {
219
226
  };
220
227
  return createMemoryCircuitStore(traitDefs, config, options.observer);
221
228
  }, [traitIndex, options.guardMode, options.strictBindings, options.contextExtensions, options.observer]);
222
- const offlineTransport = useMemo(() => {
223
- if (options.transport !== void 0 || options.persistence === void 0) return void 0;
224
- const persistence = options.persistence;
225
- return createInProcessTransport(
226
- async (_orbitalName, request) => {
227
- const runEffects = createIndexStageRunner({
228
- traitIndex,
229
- persistence,
230
- frames: store.frames,
231
- manager: store.manager,
232
- ...options.callService !== void 0 ? { extraEffectHandlers: { callService: options.callService } } : {},
233
- ...options.debug !== void 0 ? { debug: options.debug } : {}
234
- });
235
- return evaluateOrbitalEvent(
236
- {
237
- traitIndex,
238
- manager: store.manager,
239
- persistence,
240
- frames: store.frames,
241
- runEffects,
242
- ...options.user !== void 0 ? { user: options.user } : {},
243
- ...options.guardMode !== void 0 ? { guardMode: options.guardMode } : {},
244
- ...options.strictBindings !== void 0 ? { strictBindings: options.strictBindings } : {},
245
- ...options.contextExtensions !== void 0 ? { contextExtensions: options.contextExtensions } : {},
246
- ...options.debug !== void 0 ? { debug: options.debug } : {},
247
- ...options.logContext !== void 0 ? { logContext: options.logContext } : {}
248
- },
249
- request
250
- );
251
- },
252
- { carriesCircuitState: false }
253
- );
229
+ const localTransportOptions = {
230
+ traitIndex,
231
+ store,
232
+ ...options.callService !== void 0 ? { callService: options.callService } : {},
233
+ ...options.user !== void 0 ? { user: options.user } : {},
234
+ ...options.guardMode !== void 0 ? { guardMode: options.guardMode } : {},
235
+ ...options.strictBindings !== void 0 ? { strictBindings: options.strictBindings } : {},
236
+ ...options.contextExtensions !== void 0 ? { contextExtensions: options.contextExtensions } : {},
237
+ ...options.debug !== void 0 ? { debug: options.debug } : {},
238
+ ...options.logContext !== void 0 ? { logContext: options.logContext } : {}
239
+ };
240
+ const offlineTransport = useMemo(
241
+ () => options.transport !== void 0 || options.persistence === void 0 ? void 0 : createLocalStoreTransport({ ...localTransportOptions, persistence: options.persistence }),
242
+ [
243
+ options.transport,
244
+ options.persistence,
245
+ traitIndex,
246
+ store,
247
+ options.callService,
248
+ options.user,
249
+ options.guardMode,
250
+ options.strictBindings,
251
+ options.contextExtensions,
252
+ options.debug,
253
+ options.logContext
254
+ ]
255
+ );
256
+ const routedTransport = useMemo(() => {
257
+ if (options.browserStore === void 0) return void 0;
258
+ const local = createLocalStoreTransport({ ...localTransportOptions, traitIndex: fullTraitIndex, persistence: options.browserStore, clientRelays: true });
259
+ const remote = options.transport ?? offlineTransport;
260
+ return createResidenceTransport({ local, ...remote !== void 0 ? { remote } : {}, traitIndex: fullTraitIndex });
254
261
  }, [
262
+ options.browserStore,
255
263
  options.transport,
256
- options.persistence,
257
- traitIndex,
264
+ offlineTransport,
265
+ fullTraitIndex,
258
266
  store,
259
267
  options.callService,
260
268
  options.user,
@@ -264,7 +272,7 @@ function useCircuitKernel(traitBindings, options) {
264
272
  options.debug,
265
273
  options.logContext
266
274
  ]);
267
- const baseTransport = options.transport ?? offlineTransport;
275
+ const baseTransport = routedTransport ?? options.transport ?? offlineTransport;
268
276
  const effectiveTransport = useMemo(
269
277
  () => baseTransport === void 0 ? void 0 : recordingTransport(baseTransport),
270
278
  [baseTransport]
@@ -289,6 +297,8 @@ function useCircuitKernel(traitBindings, options) {
289
297
  store,
290
298
  ...options.persistence !== void 0 ? { persistence: options.persistence } : {},
291
299
  ...options.user !== void 0 ? { user: options.user } : {},
300
+ locale,
301
+ ...i18n !== void 0 ? { messages: { [i18n.locale]: i18n.messages } } : {},
292
302
  ...options.guardMode !== void 0 ? { guardMode: options.guardMode } : {},
293
303
  ...options.strictBindings !== void 0 ? { strictBindings: options.strictBindings } : {},
294
304
  ...options.contextExtensions !== void 0 ? { contextExtensions: options.contextExtensions } : {},
@@ -304,6 +314,8 @@ function useCircuitKernel(traitBindings, options) {
304
314
  store,
305
315
  options.persistence,
306
316
  options.user,
317
+ locale,
318
+ i18n,
307
319
  options.guardMode,
308
320
  options.strictBindings,
309
321
  options.contextExtensions,
@@ -312,21 +324,29 @@ function useCircuitKernel(traitBindings, options) {
312
324
  topology,
313
325
  effectiveTransport
314
326
  ]);
315
- const kernel = useMemo(() => ({
316
- store: rawKernel.store,
317
- dispatch: (request, hooks) => rawKernel.dispatch(request.clientId !== void 0 ? request : { ...request, clientId: getTabClientId() }, hooks),
318
- dispatchMount: (seeds, hooks, base = {}) => rawKernel.dispatchMount(seeds, hooks, base.clientId !== void 0 ? base : { ...base, clientId: getTabClientId() }),
319
- dispatchProgress: (request) => rawKernel.dispatchProgress(request.clientId !== void 0 ? request : { ...request, clientId: getTabClientId() })
320
- }), [rawKernel]);
321
- return { kernel, store, traitIndex, transport: effectiveTransport };
327
+ const kernel = useMemo(() => {
328
+ const stamp = (request) => ({
329
+ ...request,
330
+ clientId: request.clientId ?? getTabClientId(),
331
+ locale: request.locale ?? locale
332
+ });
333
+ return {
334
+ store: rawKernel.store,
335
+ dispatch: (request, hooks) => rawKernel.dispatch(stamp(request), hooks),
336
+ dispatchMount: (seeds, hooks, base = {}) => rawKernel.dispatchMount(seeds, hooks, stamp(base)),
337
+ dispatchProgress: (request) => rawKernel.dispatchProgress(stamp(request)),
338
+ foldHostDispatch: (request, response) => rawKernel.foldHostDispatch(request, response)
339
+ };
340
+ }, [rawKernel, locale]);
341
+ return { kernel, store, traitIndex, appTraitNames, transport: effectiveTransport };
322
342
  }
323
343
  var log2 = createLogger("almadar:ui:circuit:bus-ingress");
324
- function useBusIngress(traitBindings, traitIndex, settle, eventBus) {
344
+ function useBusIngress(traitBindings, traitIndex, settle, eventBus, appTraitNames) {
325
345
  useEffect(() => {
326
346
  const unsubscribes = [];
327
347
  const subscribedQualified = /* @__PURE__ */ new Set();
328
348
  const subscribedBare = /* @__PURE__ */ new Set();
329
- const deliveredHere = (source) => source?.dispatched === true && source.trait !== void 0 && traitIndex.byName.has(source.trait);
349
+ const deliveredHere = (source) => source?.dispatched === true && source.trait !== void 0 && (traitIndex.byName.has(source.trait) || appTraitNames.has(source.trait));
330
350
  const claims = /* @__PURE__ */ new WeakMap();
331
351
  const claim = (source, traitName, eventKey) => {
332
352
  if (source === void 0) return true;
@@ -423,7 +443,7 @@ function useBusIngress(traitBindings, traitIndex, settle, eventBus) {
423
443
  return () => {
424
444
  for (const unsub of unsubscribes) unsub();
425
445
  };
426
- }, [traitBindings, traitIndex, settle, eventBus]);
446
+ }, [traitBindings, traitIndex, settle, eventBus, appTraitNames]);
427
447
  }
428
448
  var lambdaLog = createLogger("almadar:ui:fn-form-lambda");
429
449
  function patternPropsSchema(patternType) {
@@ -498,7 +518,7 @@ function resolveLambdaBindings(body, params, item, index) {
498
518
  return body;
499
519
  }
500
520
  var LazySlotContentRenderer = React.lazy(
501
- () => import('./UISlotRenderer-P7A4SZSK.js').then((m) => ({ default: m.SlotContentRenderer }))
521
+ () => import('./UISlotRenderer-QTHFT6Z4.js').then((m) => ({ default: m.SlotContentRenderer }))
502
522
  );
503
523
  function deferLambdaEntityExprs(value) {
504
524
  if (typeof value === "string") {
@@ -795,11 +815,12 @@ async function runCircuitEffects(params) {
795
815
  // hooks/circuit/useCallsiteCapture.ts
796
816
  var log4 = createLogger("almadar:ui:circuit:callsite-capture");
797
817
  function useCallsiteCapture(traitBindings, store, traitIndex, slotFlush, options) {
798
- return useCallback(async function reRender(traitName, callsitePayload, entityByTrait, visited = /* @__PURE__ */ new Set(), serverComposed = /* @__PURE__ */ new Set(), keepComposed = false) {
818
+ return useCallback(async function reRender(traitName, callsitePayload, entityByTrait, visited = /* @__PURE__ */ new Set(), serverComposed = /* @__PURE__ */ new Set(), keepComposed = false, composedBy) {
799
819
  const children = options.callsiteCaptureChildrenByTrait?.get(traitName);
800
820
  if (!children || children.size === 0) return;
801
821
  const bindingMap = new Map(traitBindings.map((b) => [b.trait.name, b]));
802
822
  for (const childName of children) {
823
+ if (composedBy !== void 0 && !composedBy.has(childName)) continue;
803
824
  if (visited.has(childName)) continue;
804
825
  visited.add(childName);
805
826
  const childBinding = bindingMap.get(childName);
@@ -853,7 +874,7 @@ function useCallsiteCapture(traitBindings, store, traitIndex, slotFlush, options
853
874
  { event: lifecycleEvent, state: entry.result.previousState, entity: childBinding.linkedEntity }
854
875
  );
855
876
  }
856
- await reRender(childName, composedWith, entityByTrait, visited, serverComposed, keepComposed);
877
+ await reRender(childName, composedWith, entityByTrait, visited, serverComposed, keepComposed, traitsEmbeddedByEffects(indexed.irTrait, entry.result.effects));
857
878
  }
858
879
  }, [traitBindings, store, traitIndex, slotFlush, options.callsiteCaptureChildrenByTrait, options.eventBus, options.navigate, options.navigateBack, options.user]);
859
880
  }
@@ -1131,6 +1152,7 @@ function useAwaitingSkeletonSource(store, traitIndex, embeddedTraits) {
1131
1152
  }
1132
1153
 
1133
1154
  // hooks/useTraitStateMachine.ts
1155
+ var MOUNT_ERROR_SOURCE = "$mount";
1134
1156
  var stateLog = createLogger("almadar:ui:state-transitions");
1135
1157
  function diffDroppedTraitNames(prev, next) {
1136
1158
  const dropped = [];
@@ -1145,15 +1167,17 @@ function normalizeEventKey(eventKey) {
1145
1167
  function useTraitStateMachine(traitBindings, uiSlots, options) {
1146
1168
  const eventBus = useEventBus();
1147
1169
  const { user: contextViewer } = useUser();
1170
+ const { t } = useTranslate();
1148
1171
  const userContext = options.user ?? contextViewer ?? void 0;
1149
1172
  const observer = useMemo(() => createCircuitVerificationObserver(), []);
1150
- const { kernel, store, traitIndex, transport } = useCircuitKernel(traitBindings, {
1173
+ const { kernel, store, traitIndex, appTraitNames, transport } = useCircuitKernel(traitBindings, {
1151
1174
  orbitals: options.orbitals,
1152
1175
  ...options.traitConfigsByName !== void 0 ? { traitConfigsByName: options.traitConfigsByName } : {},
1153
1176
  ...options.transport !== void 0 ? { transport: options.transport } : {},
1154
1177
  ...options.carriesCircuitState !== void 0 ? { carriesCircuitState: options.carriesCircuitState } : {},
1155
1178
  ...options.awaitTopology !== void 0 ? { awaitTopology: options.awaitTopology } : {},
1156
1179
  ...options.persistence !== void 0 ? { persistence: options.persistence } : {},
1180
+ ...options.browserStore !== void 0 ? { browserStore: options.browserStore } : {},
1157
1181
  ...options.callService !== void 0 ? { callService: options.callService } : {},
1158
1182
  ...userContext !== void 0 ? { user: userContext } : {},
1159
1183
  ...options.debug !== void 0 ? { debug: options.debug } : {},
@@ -1192,11 +1216,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
1192
1216
  name: trait.name,
1193
1217
  currentState: state?.currentState ?? trait.states[0]?.name ?? "unknown",
1194
1218
  states: trait.states.map((s) => s.name),
1195
- transitions: trait.transitions.flatMap((t) => {
1196
- const froms = Array.isArray(t.from) ? t.from : [t.from];
1197
- return froms.map((f) => ({ from: f, to: t.to, event: t.event, guard: t.guard ? String(t.guard) : void 0 }));
1219
+ transitions: trait.transitions.flatMap((t2) => {
1220
+ const froms = Array.isArray(t2.from) ? t2.from : [t2.from];
1221
+ return froms.map((f) => ({ from: f, to: t2.to, event: t2.event, guard: t2.guard ? String(t2.guard) : void 0 }));
1198
1222
  }),
1199
- guards: trait.transitions.filter((t) => t.guard).map((t) => ({ name: String(t.guard) })),
1223
+ guards: trait.transitions.filter((t2) => t2.guard).map((t2) => ({ name: String(t2.guard) })),
1200
1224
  transitionCount: 0
1201
1225
  };
1202
1226
  registerTrait(info);
@@ -1234,7 +1258,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
1234
1258
  }
1235
1259
  const repainted = new Set(repaints.map((e) => e.traitName));
1236
1260
  const serverComposed = new Set([...store.callsitePayloads].filter(([child, composed]) => composedBefore.get(child) !== composed).map(([child]) => child));
1237
- await reRenderCallsiteCaptureChildren(traitName, payload ?? {}, entityByTrait, repainted, serverComposed);
1261
+ const irTrait = traitIndex.byName.get(traitName)?.irTrait;
1262
+ const composedBy = irTrait !== void 0 ? traitsEmbeddedByEvent(irTrait, eventKey) : void 0;
1263
+ await reRenderCallsiteCaptureChildren(traitName, payload ?? {}, entityByTrait, repainted, serverComposed, false, composedBy);
1238
1264
  }
1239
1265
  options.onEventProcessed?.(eventKey, payload);
1240
1266
  }, [traitIndex, store, reRenderCallsiteCaptureChildren, options.onEventProcessed]);
@@ -1303,11 +1329,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
1303
1329
  useEffect(() => {
1304
1330
  if (!transport?.subscribe) return;
1305
1331
  return transport.subscribe((emitted, target) => {
1306
- for (const t of collectListenerTargets(traitIndex, emitted.source, emitted.event, emitted.payload)) {
1307
- void (target === "origin" ? progressAndSettle(t.listenerTrait, t.triggers, t.payload) : dispatchAndSettle(t.listenerTrait, t.triggers, t.payload));
1332
+ for (const t2 of collectListenerTargets(traitIndex, emitted.source, emitted.event, emitted.payload)) {
1333
+ void (target === "origin" ? progressAndSettle(t2.listenerTrait, t2.triggers, t2.payload) : dispatchAndSettle(t2.listenerTrait, t2.triggers, t2.payload));
1308
1334
  }
1309
1335
  }, { clientId: getTabClientId() });
1310
1336
  }, [transport, traitIndex, progressAndSettle, dispatchAndSettle]);
1337
+ useEffect(() => {
1338
+ if (!transport?.subscribeHostDispatches) return;
1339
+ return transport.subscribeHostDispatches((_orbitalName, request, response) => {
1340
+ const traitName = request.targetTrait;
1341
+ if (traitName === void 0 || !traitIndex.byName.has(traitName)) return;
1342
+ const composedBefore = new Map(store.callsitePayloads);
1343
+ void kernel.foldHostDispatch(request, response).then((outcome) => settleOutcome(traitName, request.event, request.payload, outcome, composedBefore));
1344
+ });
1345
+ }, [transport, traitIndex, kernel, store, settleOutcome]);
1311
1346
  const mountAndSettle = useCallback(async (seeds, payload) => {
1312
1347
  const composedBefore = new Map(store.callsitePayloads);
1313
1348
  let beforeLocalPaint;
@@ -1341,7 +1376,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
1341
1376
  await afterSettle(seed.trait, seed.event, payload, seed.local.transitioned, [...seed.local.clientEffectsByTrait ?? [], ...serverRepaints], composedBefore);
1342
1377
  }
1343
1378
  }, [kernel, slotFlush, traitIndex, store, eventBus, options.navigate, options.navigateBack, afterSettle]);
1344
- useBusIngress(traitBindings, traitIndex, dispatchAndSettle, eventBus);
1379
+ useBusIngress(traitBindings, traitIndex, dispatchAndSettle, eventBus, appTraitNames);
1345
1380
  const sendEvent = useCallback((eventKey, payload) => {
1346
1381
  const normalized = normalizeEventKey(eventKey);
1347
1382
  for (const [traitName] of traitIndex.byName) {
@@ -1376,25 +1411,70 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
1376
1411
  for (const traitName of left) mount.initialized.delete(traitName);
1377
1412
  store.mount.unmounted(left);
1378
1413
  const entering = [];
1414
+ const embeddedChildren = new Set([...options.callsiteCaptureChildrenByTrait?.values() ?? []].flatMap((c) => [...c]));
1379
1415
  for (const binding of traitBindings) {
1380
1416
  const traitName = binding.trait.name;
1381
1417
  if (mount.initialized.has(traitName)) continue;
1382
1418
  mount.initialized.add(traitName);
1419
+ const indexed = traitIndex.byName.get(traitName);
1420
+ if (embeddedChildren.has(traitName) && indexed !== void 0 && (traitReferencesCallsitePayload(indexed.irTrait) || configReferencesCallsitePayload(indexed.config)) && store.manager.repaintLifecycleEvent(traitName) !== void 0 && !store.callsitePayloads.has(traitName)) {
1421
+ continue;
1422
+ }
1383
1423
  const lifecycleEvent = LIFECYCLE_EVENTS.find((evt) => store.manager.canHandleEvent(traitName, evt));
1384
1424
  if (lifecycleEvent !== void 0) entering.push({ trait: traitName, event: lifecycleEvent });
1385
1425
  }
1386
1426
  store.mount.mounting(entering.map((e) => e.trait));
1387
1427
  if (entering.length > 0) {
1428
+ uiSlots.clearBySource("main", MOUNT_ERROR_SOURCE);
1388
1429
  void mountAndSettle(entering, { ...options.initPayload ?? {} }).catch((err) => {
1389
- stateLog.warn("mount:failed", { traits: entering.map((e) => e.trait), error: String(err) });
1430
+ const traits = entering.map((e) => e.trait);
1431
+ const message = err instanceof Error ? err.message : String(err);
1432
+ const failed = err instanceof TraitMountError ? err.trait : traits.join(", ");
1433
+ const cause = err instanceof TraitMountError ? err.original : err;
1434
+ stateLog.warn("mount:failed", { traits, failed, error: message, stack: cause instanceof Error ? cause.stack : void 0 });
1435
+ uiSlots.render({
1436
+ target: "main",
1437
+ pattern: "error-state",
1438
+ props: { title: t("error.traitMountFailed", { trait: failed }), message, "data-testid": TRAIT_MOUNT_ERROR_TESTID },
1439
+ sourceTrait: MOUNT_ERROR_SOURCE
1440
+ });
1390
1441
  });
1391
1442
  }
1392
1443
  mountedRef.current = mount;
1393
- }, [traitBindings, store, mountAndSettle, mountKey]);
1444
+ }, [traitBindings, store, mountAndSettle, mountKey, uiSlots, traitIndex, options.callsiteCaptureChildrenByTrait, t]);
1394
1445
  useSyncExternalStore(store.subscribe, store.getVersion, store.getVersion);
1395
1446
  const traitStates = useMemo(() => store.manager.getAllStates(), [store, store.getVersion()]);
1396
1447
  return { traitStates, sendEvent, getTraitState, canHandleEvent, entityBindingSource, awaitingSkeletonSource };
1397
1448
  }
1449
+ function useBrowserStore(appName, orbitals, locale) {
1450
+ const entities = useMemo(() => orbitals.flatMap(orbitalInlineEntities).filter(storesRowsInBrowser), [orbitals]);
1451
+ const [state, setState] = useState(() => entities.length === 0 ? { status: "none" } : { status: "opening" });
1452
+ useEffect(() => {
1453
+ if (entities.length === 0) {
1454
+ setState({ status: "none" });
1455
+ return;
1456
+ }
1457
+ let cancelled = false;
1458
+ let opened;
1459
+ setState({ status: "opening" });
1460
+ const open = async () => {
1461
+ try {
1462
+ const store = await openBrowserStore(browserStoreName(appName, locale), orbitals, void 0, locale);
1463
+ if (store === null) return;
1464
+ opened = store;
1465
+ if (!cancelled) setState({ status: "ready", store });
1466
+ } catch (err) {
1467
+ if (!cancelled) setState({ status: "failed", error: err instanceof Error ? err : new Error(String(err)) });
1468
+ }
1469
+ };
1470
+ void open();
1471
+ return () => {
1472
+ cancelled = true;
1473
+ opened?.close();
1474
+ };
1475
+ }, [appName, locale, entities, orbitals]);
1476
+ return state;
1477
+ }
1398
1478
 
1399
1479
  // runtime/fitContent.ts
1400
1480
  var IDENTITY = { scale: 1, x: 0, y: 0 };
@@ -1454,7 +1534,7 @@ function appPathFromHref(href, hostHrefBase) {
1454
1534
  if (!href.startsWith(`${root}/`)) return null;
1455
1535
  return href.slice(root.length);
1456
1536
  }
1457
- function TraitInitializer({ traits, routeParams, mountKey, orbitals, onNavigate, onNavigateBack, onLocalFallback, localFallbackTimeoutMs, persistence, traitConfigsByName, embeddedTraits, callsiteCaptureChildrenByTrait, hasBridge, children }) {
1537
+ function TraitInitializer({ traits, routeParams, mountKey, orbitals, onNavigate, onNavigateBack, onLocalFallback, localFallbackTimeoutMs, persistence, browserStore, traitConfigsByName, embeddedTraits, callsiteCaptureChildrenByTrait, hasBridge, children }) {
1458
1538
  const bridge = useServerBridge();
1459
1539
  const uiSlots = useUISlots();
1460
1540
  useEffect(() => {
@@ -1473,11 +1553,13 @@ function TraitInitializer({ traits, routeParams, mountKey, orbitals, onNavigate,
1473
1553
  ...callsiteCaptureChildrenByTrait !== void 0 ? { callsiteCaptureChildrenByTrait } : {},
1474
1554
  initPayload: routeParams,
1475
1555
  ...mountKey !== void 0 ? { mountKey } : {},
1476
- ...hasBridge ? { transport: bridge.transport, carriesCircuitState: bridge.carriesCircuitState, awaitTopology: !bridge.topologyKnown } : { persistence }
1556
+ ...hasBridge ? { transport: bridge.transport, carriesCircuitState: bridge.carriesCircuitState, awaitTopology: !bridge.topologyKnown } : { persistence },
1557
+ ...browserStore !== void 0 ? { browserStore } : {}
1477
1558
  });
1478
1559
  return /* @__PURE__ */ jsx(EntityBindingContext.Provider, { value: entityBindingSource, children: /* @__PURE__ */ jsx(AwaitingSkeletonContext.Provider, { value: awaitingSkeletonSource, children }) });
1479
1560
  }
1480
1561
  var CONTENT_STAGE_WIDTH = 480;
1562
+ var NO_ORBITALS = [];
1481
1563
  function FitToBox({ children, mode = "box" }) {
1482
1564
  const outerRef = useRef(null);
1483
1565
  const innerRef = useRef(null);
@@ -1659,6 +1741,8 @@ function SchemaRunner({ schema, serverUrl, transport, getAccessToken, mockData,
1659
1741
  return set;
1660
1742
  }, [schema, pageName]);
1661
1743
  const activeOrbitalTheme = useMemo(() => resolvePreviewTheme(schema, pageName), [schema, pageName]);
1744
+ const { locale } = useTranslate$1();
1745
+ const browserStore = useBrowserStore(schema.name ?? "app", transport?.hostsBrowserStore === true ? NO_ORBITALS : schema.orbitals, locale);
1662
1746
  const inner = /* @__PURE__ */ jsx(VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsx(
1663
1747
  EntitySchemaProvider,
1664
1748
  {
@@ -1681,13 +1765,15 @@ function SchemaRunner({ schema, serverUrl, transport, getAccessToken, mockData,
1681
1765
  onLocalFallback,
1682
1766
  localFallbackTimeoutMs,
1683
1767
  persistence,
1768
+ ...browserStore.status === "ready" ? { browserStore: browserStore.store } : {},
1684
1769
  children: /* @__PURE__ */ jsx(OrbitalThemeProvider, { theme: activeOrbitalTheme, override: themeOverride, children: /* @__PURE__ */ jsx(Box, { className: "h-full min-h-full overflow-auto p-4", children: /* @__PURE__ */ jsx(UISlotRenderer, { includeHud: true, hudMode: "inline", includeFloating: true, pageKey: pageName }) }) })
1685
1770
  }
1686
1771
  )
1687
1772
  }
1688
1773
  ) });
1689
- if (serverUrl || transport) {
1690
- return /* @__PURE__ */ jsx(ServerBridgeProvider, { schema, serverUrl, transport, getAccessToken, children: inner });
1774
+ if (browserStore.status === "opening") return /* @__PURE__ */ jsx(LoadingState, {});
1775
+ if (browserStore.status === "failed") {
1776
+ return /* @__PURE__ */ jsx(ErrorState, { "data-testid": "browser-store-error", message: browserStore.error.message });
1691
1777
  }
1692
1778
  return inner;
1693
1779
  }
@@ -1718,12 +1804,15 @@ function OrbPreview({
1718
1804
  onPageChange,
1719
1805
  user = null,
1720
1806
  localFallbackTimeoutMs,
1721
- themeOverride
1807
+ themeOverride,
1808
+ lazyLoader,
1809
+ schemaPath,
1810
+ onUnmatchedNavigate
1722
1811
  }) {
1723
1812
  if (serverUrl && transport) {
1724
1813
  throw new Error("OrbPreview accepts serverUrl OR transport, not both");
1725
1814
  }
1726
- const { t } = useTranslate();
1815
+ const { t } = useTranslate$1();
1727
1816
  const [localFallback, setLocalFallback] = useState(false);
1728
1817
  const eventBus = useEventBus();
1729
1818
  const inHostRouter = useInRouterContext();
@@ -1732,6 +1821,7 @@ function OrbPreview({
1732
1821
  if (localFallback) return;
1733
1822
  setLocalFallback(true);
1734
1823
  }, [localFallback]);
1824
+ const hasOuterBridge = useHasServerBridge();
1735
1825
  const parseResult = useMemo(() => {
1736
1826
  let parsed;
1737
1827
  if (typeof schema === "string") {
@@ -1767,12 +1857,44 @@ function OrbPreview({
1767
1857
  return [];
1768
1858
  }
1769
1859
  }, [parsedSchema]);
1860
+ const lazyPages = useMemo(() => parsedSchema?.lazyPages ?? [], [parsedSchema]);
1861
+ const matchRoute = useCallback((path) => {
1862
+ const candidates = [
1863
+ ...pages.map((entry) => ({ kind: "page", entry, path: entry.page.path })),
1864
+ ...lazyPages.map((page) => ({ kind: "lazy", page, path: page.path }))
1865
+ ];
1866
+ const hit = matchPathAmong(candidates, path, (c) => c.path);
1867
+ if (!hit) return null;
1868
+ return hit.candidate.kind === "page" ? { kind: "page", entry: hit.candidate.entry, params: hit.params } : { kind: "lazy", page: hit.candidate.page, params: hit.params };
1869
+ }, [pages, lazyPages]);
1870
+ const initialRoute = useMemo(
1871
+ () => initialPagePath ? matchRoute(initialPagePath) : null,
1872
+ [matchRoute, initialPagePath]
1873
+ );
1770
1874
  const initialPageMatch = useMemo(() => {
1771
- if (!initialPagePath) return void 0;
1772
- const hit = matchPathAmong(pages, initialPagePath, (entry) => entry.page.path);
1773
- if (!hit) return void 0;
1774
- return { name: hit.candidate.page.name, params: hit.params };
1775
- }, [pages, initialPagePath]);
1875
+ if (initialRoute?.kind !== "page" || !initialRoute.entry.page.name) return void 0;
1876
+ return { name: initialRoute.entry.page.name, params: initialRoute.params };
1877
+ }, [initialRoute]);
1878
+ const [lazyRoute, setLazyRoute] = useState(
1879
+ initialRoute?.kind === "lazy" && initialPagePath ? { page: initialRoute.page, path: initialPagePath } : null
1880
+ );
1881
+ const [lazyLoad, setLazyLoad] = useState({ status: "loading" });
1882
+ useEffect(() => {
1883
+ if (!lazyRoute) return;
1884
+ if (!lazyLoader) {
1885
+ setLazyLoad({ status: "error", error: `No loader for lazy page ${lazyRoute.page.path} (${lazyRoute.page.orbRef})` });
1886
+ return;
1887
+ }
1888
+ let cancelled = false;
1889
+ setLazyLoad({ status: "loading" });
1890
+ void loadLazyPage(lazyLoader, lazyRoute.page, schemaPath).then((result) => {
1891
+ if (cancelled) return;
1892
+ setLazyLoad(result.success ? { status: "ready", schema: result.data } : { status: "error", error: result.error });
1893
+ });
1894
+ return () => {
1895
+ cancelled = true;
1896
+ };
1897
+ }, [lazyRoute?.page, lazyLoader, schemaPath]);
1776
1898
  const initialPageName = initialPageMatch?.name;
1777
1899
  const [currentPage, setCurrentPage] = useState(initialPageName);
1778
1900
  const [routeParams, setRouteParams] = useState(initialPageMatch?.params ?? {});
@@ -1782,8 +1904,9 @@ function OrbPreview({
1782
1904
  prevInitialPagePathRef.current = initialPagePath;
1783
1905
  setCurrentPage(initialPageName);
1784
1906
  setRouteParams(initialPageMatch?.params ?? {});
1907
+ setLazyRoute(initialRoute?.kind === "lazy" && initialPagePath ? { page: initialRoute.page, path: initialPagePath } : null);
1785
1908
  }
1786
- }, [initialPagePath, initialPageName, initialPageMatch]);
1909
+ }, [initialPagePath, initialPageName, initialPageMatch, initialRoute]);
1787
1910
  const currentPagePath = useMemo(() => {
1788
1911
  const activePageName = currentPage ?? pages[0]?.page.name;
1789
1912
  const resolved = pages.find((p) => p.page.name === activePageName)?.page.path;
@@ -1798,10 +1921,30 @@ function OrbPreview({
1798
1921
  return pattern.replace(/:([A-Za-z0-9_]+)/g, (whole, key) => routeParams[key] ?? whole);
1799
1922
  }, [currentPagePath, routeParams]);
1800
1923
  const navStackRef = useRef(null);
1924
+ const announcePage = useCallback((path) => {
1925
+ if (onPageChange) {
1926
+ onPageChange(path);
1927
+ } else if (!isolated && typeof window !== "undefined") {
1928
+ const url = new URL(window.location.href);
1929
+ url.searchParams.set("page", path);
1930
+ window.history.pushState({}, "", url.toString());
1931
+ window.dispatchEvent(new PopStateEvent("popstate"));
1932
+ }
1933
+ }, [isolated, onPageChange]);
1801
1934
  const handleNavigate = useCallback((path, navState) => {
1802
- const hit = matchPathAmong(pages, path, (entry) => entry.page.path);
1803
- const match = hit?.candidate;
1804
- const params = { ...hit?.params ?? {}, ...navState ?? {} };
1935
+ const route = matchRoute(path);
1936
+ if (!route) {
1937
+ onUnmatchedNavigate?.(path);
1938
+ return;
1939
+ }
1940
+ if (route.kind === "lazy") {
1941
+ setLazyRoute({ page: route.page, path });
1942
+ announcePage(path);
1943
+ return;
1944
+ }
1945
+ setLazyRoute(null);
1946
+ const match = route.entry;
1947
+ const params = { ...route.params, ...navState ?? {} };
1805
1948
  navLog.debug("handleNavigate", () => ({
1806
1949
  path,
1807
1950
  matched: match?.page.name ?? null,
@@ -1809,19 +1952,12 @@ function OrbPreview({
1809
1952
  navState: navState ? JSON.stringify(navState) : void 0,
1810
1953
  availablePaths: pages.map((p) => p.page.path)
1811
1954
  }));
1812
- if (match?.page.name) {
1955
+ if (match.page.name) {
1813
1956
  setRouteParams(params);
1814
1957
  setCurrentPage(match.page.name);
1815
- if (onPageChange) {
1816
- onPageChange(path);
1817
- } else if (!isolated && typeof window !== "undefined") {
1818
- const url = new URL(window.location.href);
1819
- url.searchParams.set("page", path);
1820
- window.history.pushState({}, "", url.toString());
1821
- window.dispatchEvent(new PopStateEvent("popstate"));
1822
- }
1958
+ announcePage(path);
1823
1959
  }
1824
- }, [pages, isolated, onPageChange]);
1960
+ }, [pages, matchRoute, announcePage, onUnmatchedNavigate]);
1825
1961
  const handleNavigateEffect = useCallback(
1826
1962
  (path, params, crumb) => {
1827
1963
  navStackRef.current?.beginNavigate(path, crumb);
@@ -1886,64 +2022,96 @@ function OrbPreview({
1886
2022
  navLog.info("interceptor:installed", { pageCount: pages.length, paths: pages.map((p) => p.page.path) });
1887
2023
  return () => el.removeEventListener("click", handler, true);
1888
2024
  }, [pages, handleNavigate, isolated, hostHrefBase]);
1889
- return /* @__PURE__ */ jsxs(
1890
- Box,
1891
- {
1892
- ref: containerRef,
1893
- className: `${fit ? "overflow-hidden" : "overflow-auto"} border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
1894
- style: { height },
1895
- children: [
1896
- localFallback && /* @__PURE__ */ jsx(Box, { className: "px-3 py-2 bg-[var(--color-warning)] bg-opacity-10 border-b border-[var(--color-warning)] flex items-center gap-2", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-warning-foreground)] flex-1", children: t("orbPreview.serverUnreachable") }) }),
1897
- inHostRouter ? /* @__PURE__ */ jsx(HostHrefBaseProbe, { onBase: setHostHrefBase }) : null,
1898
- /* @__PURE__ */ jsx(CurrentPagePathProvider, { value: currentPagePath, children: /* @__PURE__ */ jsxs(
1899
- NavStackProvider,
2025
+ const withBridge = (node) => (serverUrl || transport) && !hasOuterBridge ? /* @__PURE__ */ jsx(ServerBridgeProvider, { schema: parseResult.schema, serverUrl, transport, getAccessToken, children: node }) : node;
2026
+ if (lazyRoute) {
2027
+ return withBridge(
2028
+ /* @__PURE__ */ jsxs(Box, { ref: containerRef, className: `overflow-auto ${className ?? ""}`, style: { height }, children: [
2029
+ lazyLoad.status === "loading" && /* @__PURE__ */ jsx(LoadingState, {}),
2030
+ lazyLoad.status === "error" && /* @__PURE__ */ jsx(ErrorState, { "data-testid": "lazy-page-error", message: lazyLoad.error }),
2031
+ lazyLoad.status === "ready" && /* @__PURE__ */ jsx(
2032
+ OrbPreview,
1900
2033
  {
1901
- pages: navPages,
1902
- currentPath: concreteCurrentPath,
1903
- navigate: handleNavigate,
1904
- storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
1905
- children: [
1906
- /* @__PURE__ */ jsx(NavStackRefBridge, { apiRef: navStackRef }),
1907
- /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsx(UISlotProvider, { children: fit ? /* @__PURE__ */ jsx(FitToBox, { mode: fit === "content" ? "content" : "box", children: /* @__PURE__ */ jsx(
1908
- SchemaRunner,
1909
- {
1910
- schema: parseResult.schema,
1911
- serverUrl,
1912
- transport,
1913
- getAccessToken,
1914
- mockData: effectiveMockData,
1915
- pageName: currentPage,
1916
- routeParams,
1917
- onNavigate: handleNavigateEffect,
1918
- onNavigateBack: handleNavigateBack,
1919
- onLocalFallback: handleLocalFallback,
1920
- localFallbackTimeoutMs,
1921
- persistence,
1922
- themeOverride
1923
- }
1924
- ) }) : /* @__PURE__ */ jsx(
1925
- SchemaRunner,
1926
- {
1927
- schema: parseResult.schema,
1928
- serverUrl,
1929
- transport,
1930
- getAccessToken,
1931
- mockData: effectiveMockData,
1932
- pageName: currentPage,
1933
- routeParams,
1934
- onNavigate: handleNavigateEffect,
1935
- onNavigateBack: handleNavigateBack,
1936
- onLocalFallback: handleLocalFallback,
1937
- localFallbackTimeoutMs,
1938
- persistence,
1939
- themeOverride
1940
- }
1941
- ) }) })
1942
- ]
1943
- }
1944
- ) })
1945
- ]
1946
- }
2034
+ schema: lazyLoad.schema,
2035
+ autoMock,
2036
+ height: "100%",
2037
+ className: "border-0",
2038
+ serverUrl,
2039
+ transport,
2040
+ getAccessToken,
2041
+ initialPagePath: lazyRoute.path,
2042
+ user,
2043
+ isolated,
2044
+ fit,
2045
+ onPageChange: announcePage,
2046
+ themeOverride,
2047
+ lazyLoader,
2048
+ onUnmatchedNavigate: handleNavigate
2049
+ },
2050
+ lazyRoute.page.orbRef
2051
+ )
2052
+ ] })
2053
+ );
2054
+ }
2055
+ return withBridge(
2056
+ /* @__PURE__ */ jsxs(
2057
+ Box,
2058
+ {
2059
+ ref: containerRef,
2060
+ className: `${fit ? "overflow-hidden" : "overflow-auto"} border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
2061
+ style: { height },
2062
+ children: [
2063
+ localFallback && /* @__PURE__ */ jsx(Box, { className: "px-3 py-2 bg-[var(--color-warning)] bg-opacity-10 border-b border-[var(--color-warning)] flex items-center gap-2", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-warning-foreground)] flex-1", children: t("orbPreview.serverUnreachable") }) }),
2064
+ inHostRouter ? /* @__PURE__ */ jsx(HostHrefBaseProbe, { onBase: setHostHrefBase }) : null,
2065
+ /* @__PURE__ */ jsx(CurrentPagePathProvider, { value: currentPagePath, children: /* @__PURE__ */ jsxs(
2066
+ NavStackProvider,
2067
+ {
2068
+ pages: navPages,
2069
+ currentPath: concreteCurrentPath,
2070
+ navigate: handleNavigate,
2071
+ storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
2072
+ children: [
2073
+ /* @__PURE__ */ jsx(NavStackRefBridge, { apiRef: navStackRef }),
2074
+ /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsx(UISlotProvider, { children: fit ? /* @__PURE__ */ jsx(FitToBox, { mode: fit === "content" ? "content" : "box", children: /* @__PURE__ */ jsx(
2075
+ SchemaRunner,
2076
+ {
2077
+ schema: parseResult.schema,
2078
+ serverUrl,
2079
+ transport,
2080
+ getAccessToken,
2081
+ mockData: effectiveMockData,
2082
+ pageName: currentPage,
2083
+ routeParams,
2084
+ onNavigate: handleNavigateEffect,
2085
+ onNavigateBack: handleNavigateBack,
2086
+ onLocalFallback: handleLocalFallback,
2087
+ localFallbackTimeoutMs,
2088
+ persistence,
2089
+ themeOverride
2090
+ }
2091
+ ) }) : /* @__PURE__ */ jsx(
2092
+ SchemaRunner,
2093
+ {
2094
+ schema: parseResult.schema,
2095
+ serverUrl,
2096
+ transport,
2097
+ getAccessToken,
2098
+ mockData: effectiveMockData,
2099
+ pageName: currentPage,
2100
+ routeParams,
2101
+ onNavigate: handleNavigateEffect,
2102
+ onNavigateBack: handleNavigateBack,
2103
+ onLocalFallback: handleLocalFallback,
2104
+ localFallbackTimeoutMs,
2105
+ persistence,
2106
+ themeOverride
2107
+ }
2108
+ ) }) })
2109
+ ]
2110
+ }
2111
+ ) })
2112
+ ]
2113
+ }
2114
+ )
1947
2115
  );
1948
2116
  }
1949
2117
  OrbPreview.displayName = "OrbPreview";
@@ -1957,7 +2125,11 @@ function BrowserPlayground({
1957
2125
  paused,
1958
2126
  fit,
1959
2127
  onPageChange,
1960
- viewer
2128
+ viewer,
2129
+ messages,
2130
+ locale,
2131
+ lazyLoader,
2132
+ schemaPath
1961
2133
  }) {
1962
2134
  const [runtime] = useState(
1963
2135
  () => new OrbitalServerRuntime({ mode, debug: false, ...viewer !== void 0 ? { defaultUser: viewer } : {} })
@@ -1973,10 +2145,18 @@ function BrowserPlayground({
1973
2145
  const registrationReady = useMemo(() => {
1974
2146
  const orbitalNames = schema.orbitals.map((o) => o.name);
1975
2147
  playgroundLog.debug("register:start", { schema: schema.name, orbitalNames });
1976
- return runtime.register(schema).then(() => {
2148
+ const lazyPages = schema.lazyPages ?? [];
2149
+ const lazy = lazyPages.length > 0 && lazyLoader !== void 0 ? Promise.all(lazyPages.map(async (page) => {
2150
+ const loaded = await loadLazyPage(lazyLoader, page, schemaPath);
2151
+ if (!loaded.success) throw new Error(loaded.error);
2152
+ return loaded.data;
2153
+ })) : Promise.resolve([]);
2154
+ return lazy.then(
2155
+ (lazySchemas) => runtime.register(schema, { ...messages !== void 0 ? { messages } : {}, lazy: lazySchemas })
2156
+ ).then(() => {
1977
2157
  playgroundLog.debug("register:done", { schema: schema.name, orbitalNames });
1978
2158
  });
1979
- }, [runtime, schema]);
2159
+ }, [runtime, schema, messages, lazyLoader, schemaPath]);
1980
2160
  const teardownTimerRef = useRef(null);
1981
2161
  useEffect(() => {
1982
2162
  if (teardownTimerRef.current !== null) {
@@ -1998,7 +2178,8 @@ function BrowserPlayground({
1998
2178
  return runtime.processOrbitalEvent(orbitalName, request);
1999
2179
  },
2000
2180
  {
2001
- onRegister: (s) => runtime.register(s),
2181
+ // The program (lazy behaviors included) is registered above; the bridge only waits for it.
2182
+ onRegister: () => registrationReady,
2002
2183
  onUnregister: () => {
2003
2184
  runtime.unregisterAll();
2004
2185
  }
@@ -2006,7 +2187,13 @@ function BrowserPlayground({
2006
2187
  ),
2007
2188
  [runtime, registrationReady]
2008
2189
  );
2009
- return /* @__PURE__ */ jsx(
2190
+ const viewerLocale = locale ?? schema.locales?.[0];
2191
+ const i18n = useMemo(() => {
2192
+ if (viewerLocale === void 0 || messages === void 0) return void 0;
2193
+ const catalog = messages[viewerLocale] ?? {};
2194
+ return { locale: viewerLocale, direction: localeDirection(viewerLocale), t: createTranslate(catalog), messages: catalog };
2195
+ }, [viewerLocale, messages]);
2196
+ const preview = /* @__PURE__ */ jsx(
2010
2197
  OrbPreview,
2011
2198
  {
2012
2199
  schema,
@@ -2017,9 +2204,12 @@ function BrowserPlayground({
2017
2204
  fit,
2018
2205
  onPageChange,
2019
2206
  user: viewer ?? null,
2207
+ lazyLoader,
2208
+ schemaPath,
2020
2209
  isolated: true
2021
2210
  }
2022
2211
  );
2212
+ return i18n !== void 0 ? /* @__PURE__ */ jsx(I18nProvider, { value: i18n, children: preview }) : preview;
2023
2213
  }
2024
2214
 
2025
2215
  export { BrowserPlayground, OrbPreview, clearSchemaCache, useResolvedSchema, useTraitStateMachine };