@jsenv/navi 0.29.52 → 0.29.54

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.
@@ -13083,6 +13083,117 @@ const setActionPrivateProperties = (action, properties) => {
13083
13083
  actionPrivatePropertiesWeakMap.set(action, properties);
13084
13084
  };
13085
13085
 
13086
+ /**
13087
+ * Where an action error goes when nothing displays it.
13088
+ *
13089
+ * An action that fails writes the error into its `errorSignal` and stops there.
13090
+ * It cannot know whether a screen is going to show it: at the instant it fails,
13091
+ * the screen that will is often not even mounted — a route action runs before
13092
+ * its page renders, which is precisely the case a guess made at failure time
13093
+ * gets wrong. So nothing is guessed. The error is let go, and whoever displays
13094
+ * it SAYS so by marking it; what nobody ever took is reported as unhandled.
13095
+ *
13096
+ * The mark is `__handled_by__`, the same one the jsenv supervisor reads to stay
13097
+ * out of the way of an error the app is already showing — one mark, one meaning:
13098
+ * "this is on screen somewhere".
13099
+ *
13100
+ * The whole picture, control errors and validation included: docs/error_handling.md
13101
+ */
13102
+
13103
+ const markErrorAsDisplayedBy = (error, by) => {
13104
+ if (error && typeof error === "object") {
13105
+ error.__handled_by__ = by;
13106
+ }
13107
+ };
13108
+
13109
+ const errorIsDisplayed = (error) => {
13110
+ return Boolean(error && error.__handled_by__);
13111
+ };
13112
+
13113
+ /**
13114
+ * A render has read this error — it is now the render tree's business, not this
13115
+ * module's, and there is nothing left to report.
13116
+ *
13117
+ * Whatever the reader does with it is already covered without any deadline: it
13118
+ * displays it (and marks it), or it throws it, and a thrown error either finds a
13119
+ * boundary that displays it or reaches window on its own — `preact/debug`
13120
+ * re-throws every error a boundary caught, and an unbounded one aborts the
13121
+ * render loudly. Reporting it here as well would be a second voice saying the
13122
+ * same thing, always the wrong one, since this module cannot see which of those
13123
+ * happened.
13124
+ */
13125
+ const errorTakenByRenderSet = new WeakSet();
13126
+ const markErrorAsTakenByRender = (error) => {
13127
+ if (error && typeof error === "object") {
13128
+ errorTakenByRenderSet.add(error);
13129
+ }
13130
+ };
13131
+
13132
+ /**
13133
+ * When the answer "nobody took it" is final.
13134
+ *
13135
+ * The floor is one macrotask: every render that could take the error — Preact's
13136
+ * queue, a Suspense boundary settling on the failure, the boundary above it —
13137
+ * happens in microtasks.
13138
+ *
13139
+ * That floor is enough for an action failing under a page that is already on
13140
+ * screen, and far too early for a route action: it fails ON the url change,
13141
+ * before its page exists, and that page cannot render until the routing that
13142
+ * asked for the data is over. Measured on an offline navigation, the screen
13143
+ * displaying the error arrived ~12ms after this deadline — so the app was told
13144
+ * it had displayed nothing while it was displaying it.
13145
+ *
13146
+ * The browser integration knows when the document has stopped moving and hands
13147
+ * that over here (see installReportDeadlineExtension); nothing else does, and
13148
+ * this module stays free of the DOM. Waiting longer costs nothing now that a
13149
+ * read is enough to call this off: what still reaches the report was read by no
13150
+ * render at all, and a late report about that is as good as a prompt one.
13151
+ */
13152
+ let waitForDocumentSettled = null;
13153
+ const installReportDeadlineExtension = (fn) => {
13154
+ waitForDocumentSettled = fn;
13155
+ };
13156
+
13157
+ /**
13158
+ * Rethrown rather than logged: an error nobody took is an unhandled error, and
13159
+ * the runtime already knows what to do with those (window "error" event, jsenv
13160
+ * overlay in dev). Same trick preact/debug uses for the same reason.
13161
+ */
13162
+ const errorReportedSet = new WeakSet();
13163
+ const reportErrorIfNobodyDisplaysIt = (error, { action } = {}) => {
13164
+ const decide = () => {
13165
+ if (errorIsDisplayed(error) || errorTakenByRenderSet.has(error)) {
13166
+ return;
13167
+ }
13168
+ if (error && typeof error === "object") {
13169
+ // The same error can reach here from more than one direction (the run
13170
+ // that produced it, the routing promise carrying it): it is one error and
13171
+ // it is reported once.
13172
+ if (errorReportedSet.has(error)) {
13173
+ return;
13174
+ }
13175
+ errorReportedSet.add(error);
13176
+ }
13177
+ if (action && error && typeof error === "object" && !error.action) {
13178
+ error.action = action;
13179
+ }
13180
+ throw error;
13181
+ };
13182
+
13183
+ setTimeout(() => {
13184
+ if (errorIsDisplayed(error) || errorTakenByRenderSet.has(error)) {
13185
+ // Already taken within the microtasks that followed the failure: the
13186
+ // common case, and there is nothing to wait for.
13187
+ return;
13188
+ }
13189
+ if (waitForDocumentSettled) {
13190
+ waitForDocumentSettled(decide);
13191
+ return;
13192
+ }
13193
+ decide();
13194
+ });
13195
+ };
13196
+
13086
13197
  const SYMBOL_OBJECT_SIGNAL = Symbol.for("navi_object_signal");
13087
13198
 
13088
13199
  let DEBUG$1 = false;
@@ -13916,7 +14027,6 @@ const createAction = (callback, rootOptions = {}) => {
13916
14027
  const ui = {
13917
14028
  renderLoaded: null,
13918
14029
  renderLoadedAsync,
13919
- hasRenderers: false, // Flag to track if action is bound to UI components
13920
14030
  };
13921
14031
  let sideEffectCleanup;
13922
14032
  let completeSideEffectCleanup;
@@ -14058,28 +14168,27 @@ const createAction = (callback, rootOptions = {}) => {
14058
14168
  return error;
14059
14169
  }
14060
14170
  if (DEBUG$1) {
14061
- console.log(
14062
- `"${action}": failed (error: ${error}, handled by ui: ${ui.hasRenderers})`,
14063
- );
14171
+ console.log(`"${action}": failed (error: ${error})`);
14064
14172
  }
14173
+ error.action = action;
14065
14174
  batch(() => {
14066
14175
  errorSignal.value = error;
14067
14176
  runningStateSignal.value = FAILED;
14068
14177
  onError?.(error, { event, action, args });
14069
14178
  });
14070
14179
 
14071
- if (ui.hasRenderers || onError) {
14072
- // When inside suspense this console.error is redundant with the error thrown by preact debug at
14073
- // https://github.com/preactjs/preact/blob/21dd6d04c1a9a43e5b60976bb5eb7d856253195b/debug/src/debug.js#L109
14074
- console.error(error);
14075
- // For UI-bound actions: error is properly handled by logging + UI display
14076
- // Return error instead of throwing to signal it's handled and prevent:
14077
- // - jsenv error overlay from appearing
14078
- // - error being treated as unhandled by runtime
14079
- return error;
14180
+ // The error is in errorSignal; from here it is the UI's, and running
14181
+ // the action is not the place to decide whether the UI wants it
14182
+ // that answer does not exist yet at this instant (see
14183
+ // action_error_report.js). So the run never throws: it settles with
14184
+ // the error as its value, and being displayed or not is constated
14185
+ // afterwards, in one place, by one rule.
14186
+ if (onError) {
14187
+ // Asking for the error IS taking it.
14188
+ markErrorAsDisplayedBy(error, "onError");
14080
14189
  }
14081
- error.action = action;
14082
- throw error;
14190
+ reportErrorIfNobodyDisplaysIt(error, { action });
14191
+ return error;
14083
14192
  };
14084
14193
 
14085
14194
  try {
@@ -14440,15 +14549,8 @@ const createActionProxyFromSignal = (
14440
14549
  performReset: proxyPrivateMethod("performReset"),
14441
14550
  ui: currentActionPrivateProperties.ui,
14442
14551
  };
14443
- onActionTargetChange((actionTarget, previousTarget) => {
14552
+ onActionTargetChange(() => {
14444
14553
  proxyPrivateProperties.ui = currentActionPrivateProperties.ui;
14445
- if (previousTarget && actionTarget) {
14446
- const previousPrivateProps = getActionPrivateProperties(previousTarget);
14447
- if (previousPrivateProps.ui.hasRenderers) {
14448
- const newPrivateProps = getActionPrivateProperties(actionTarget);
14449
- newPrivateProps.ui.hasRenderers = true;
14450
- }
14451
- }
14452
14554
  proxyPrivateProperties.childActionWeakSet =
14453
14555
  currentActionPrivateProperties.childActionWeakSet;
14454
14556
  });
@@ -21701,7 +21803,18 @@ const setupBrowserIntegrationViaHistory = ({
21701
21803
  // something at the first announcement has a definite place to give it back.
21702
21804
  publishBeforeRouting({ url, ...options });
21703
21805
  try {
21704
- return applyRoutingTask(url, options);
21806
+ const routingResult = applyRoutingTask(url, options);
21807
+ if (routingResult && typeof routingResult.then === "function") {
21808
+ // Every caller below drops this value — a click handler has nothing to
21809
+ // do with what the routing returns — so a rejection here would become
21810
+ // an anonymous unhandled one, pointing at the navigation rather than at
21811
+ // what failed. It goes to the single place that knows what to do with
21812
+ // an error nobody displays (see action_error_report.js).
21813
+ routingResult.catch((e) => {
21814
+ reportErrorIfNobodyDisplaysIt(e);
21815
+ });
21816
+ }
21817
+ return routingResult;
21705
21818
  } finally {
21706
21819
  publishAfterRouting({ url, ...options });
21707
21820
  }
@@ -21777,6 +21890,9 @@ const setupBrowserIntegrationViaHistory = ({
21777
21890
  isVisited,
21778
21891
  state,
21779
21892
  });
21893
+ if (navigationType === "push") {
21894
+ startAtTop(url);
21895
+ }
21780
21896
  executeWithCleanup(
21781
21897
  () => allResult,
21782
21898
  () => {
@@ -21925,6 +22041,36 @@ const setupBrowserIntegrationViaHistory = ({
21925
22041
  };
21926
22042
  };
21927
22043
 
22044
+ // A page one arrives at for the first time starts at its top. Only a document
22045
+ // navigation does that on its own: a pushState creates its entry with whatever
22046
+ // scroll happened to be there, so without this the new page opens at the offset
22047
+ // of the one before it — and worse, that borrowed offset is what the browser
22048
+ // then remembers FOR that entry, and hands back on the way forward.
22049
+ //
22050
+ // Push only. A traverse is the browser's business and it is already right: it
22051
+ // keeps a position per entry and restores it. A replace is not an arrival —
22052
+ // it is the same place, said differently (a tab row travelling, see
22053
+ // route_travel.jsx), and resetting there would throw the reader out of a page
22054
+ // they never left.
22055
+ //
22056
+ // After the routes have been told, and that ordering is the whole subtlety:
22057
+ // the routes changing is what sets a travel off, and a travel measures the box
22058
+ // it is leaving as it stands. Reset before that and the picture of the page
22059
+ // being left is taken at the top of a page the reader was not at the top of —
22060
+ // it is then watched jumping back to its first line before it even begins to
22061
+ // leave (see holdTravelGeometry in route_travel.jsx). After pushState too, so
22062
+ // the entry being left keeps the offset it is at.
22063
+ //
22064
+ // The document, because the document is the scrollport in the common case. An
22065
+ // app that scrolls an element of its own scrolls it itself.
22066
+ const startAtTop = (url) => {
22067
+ // A fragment names where to land, and the browser is the one that finds it.
22068
+ if (new URL(url, window.location.href).hash) {
22069
+ return;
22070
+ }
22071
+ window.scrollTo({ top: 0, left: 0, behavior: "instant" });
22072
+ };
22073
+
21928
22074
  let updateRoutes;
21929
22075
 
21930
22076
  const applyActions = (params) => {
@@ -22004,6 +22150,39 @@ const browserIntegration = setupBrowserIntegrationViaHistory({
22004
22150
  isRouting: () => Boolean(updateRoutes),
22005
22151
  });
22006
22152
 
22153
+ /**
22154
+ * How long an error that nothing displayed is given before it is called
22155
+ * unhandled (see action_error_report.js, which knows the rule but not the DOM).
22156
+ *
22157
+ * A route action fails ON the url change, before its page exists: it is the
22158
+ * routing itself that will bring what displays the error, so the answer is only
22159
+ * final once the document has stopped moving AND the frame that paints what it
22160
+ * brought has been through. Anything faster tells an app displaying "you are
22161
+ * offline" that it displayed nothing.
22162
+ */
22163
+ installReportDeadlineExtension((decide) => {
22164
+ const whenPainted = () => {
22165
+ // The frame that paints what routing brought, then the microtasks after it:
22166
+ // a render claiming the error is on either side of that paint, never later.
22167
+ requestAnimationFrame(() => {
22168
+ setTimeout(decide);
22169
+ });
22170
+ };
22171
+ if (!documentIsBusySignal.peek()) {
22172
+ whenPainted();
22173
+ return;
22174
+ }
22175
+ // Busy right now, so the synchronous first callback of subscribe() says
22176
+ // "busy" and is skipped; what unsubscribes below is a later one.
22177
+ const unsubscribe = documentIsBusySignal.subscribe((documentIsBusy) => {
22178
+ if (documentIsBusy) {
22179
+ return;
22180
+ }
22181
+ unsubscribe();
22182
+ whenPainted();
22183
+ });
22184
+ });
22185
+
22007
22186
  setOnAllRouteReady((v) => {
22008
22187
  updateRoutes = v;
22009
22188
  browserIntegration.init();
@@ -32227,18 +32406,6 @@ const ActionRenderer = ({
32227
32406
  } = useActionStatus(action);
32228
32407
  const UIRenderedPromise = useUIRenderedPromise(action);
32229
32408
  const [errorBoundary, resetErrorBoundary] = useErrorBoundary();
32230
-
32231
- // Mark this action as bound to UI components (has renderers)
32232
- // This tells the action system that errors should be caught and stored
32233
- // in the action's error state rather than bubbling up
32234
- useLayoutEffect(() => {
32235
- if (action) {
32236
- const {
32237
- ui
32238
- } = getActionPrivateProperties(action);
32239
- ui.hasRenderers = true;
32240
- }
32241
- }, [action]);
32242
32409
  useLayoutEffect(() => {
32243
32410
  resetErrorBoundary();
32244
32411
  }, [action, loading, idle, resetErrorBoundary]);
@@ -32266,6 +32433,8 @@ const ActionRenderer = ({
32266
32433
  return renderIdle(action);
32267
32434
  }
32268
32435
  if (errorBoundary) {
32436
+ // Displaying it is what makes it handled (see action_error_report.js)
32437
+ markErrorAsDisplayedBy(errorBoundary, "<ActionRenderer>");
32269
32438
  return renderError(errorBoundary, "ui_error", action);
32270
32439
  }
32271
32440
  if (aborted) {
@@ -32291,6 +32460,7 @@ const ActionRenderer = ({
32291
32460
  return renderLoading(action);
32292
32461
  }
32293
32462
  if (error) {
32463
+ markErrorAsDisplayedBy(error, "<ActionRenderer>");
32294
32464
  return renderError(error, "action_error", action);
32295
32465
  }
32296
32466
  return renderCompletedSafe(data, action);
@@ -35772,14 +35942,22 @@ const useActionAsyncData = (action, {
35772
35942
  throw dismissedPromise;
35773
35943
  }
35774
35944
  const actionError = action.errorSignal.peek();
35945
+ // A render has it now, whichever way it goes from here (see
35946
+ // action_error_report.js): displayed below, or thrown to a boundary that
35947
+ // displays it — and if none does, the throw reaches window on its own.
35948
+ markErrorAsTakenByRender(actionError);
35775
35949
  if (errorEffect === "use") {
35950
+ // Handed to the component, which is what displays it from here on
35951
+ // (see action_error_report.js)
35952
+ markErrorAsDisplayedBy(actionError, "useAsyncData({ error: true })");
35776
35953
  const dismissError = () => {
35777
35954
  dismissedActionWeakSet.add(action);
35778
35955
  setTick(n => n + 1);
35779
35956
  };
35780
35957
  return [undefined, false, actionError, dismissError];
35781
35958
  }
35782
- actionError.action = action;
35959
+ // Not marked: nothing is displayed yet — the boundary that catches this is
35960
+ // what says so, and only if it has something to show.
35783
35961
  throw actionError;
35784
35962
  }
35785
35963
 
@@ -35917,8 +36095,34 @@ const LoadingFallback = ({
35917
36095
  };
35918
36096
 
35919
36097
  // ─── ErrorBoundary ────────────────────────────────────────────────────────────
35920
- // Catches errors thrown by useAction. Subscribes to error.action so it
35921
- // auto-resets when the action runs again.
36098
+ /**
36099
+ * Displays what its subtree throws — an action failure delegated by
36100
+ * `useAsyncData`, or any render error under it.
36101
+ *
36102
+ * Two things it gets right that a hand-written boundary rarely does, both
36103
+ * explained in docs/error_handling.md:
36104
+ *
36105
+ * - It marks the error as displayed ONLY when it actually displays it.
36106
+ * `preact/debug` rethrows every error a boundary caught in a `setTimeout`, on
36107
+ * purpose (React devtools compatibility), so a handled error still reaches
36108
+ * window and the jsenv overlay covers the app unless `__handled_by__` is set.
36109
+ * Setting it before knowing whether anything is rendered turns a boundary into
36110
+ * a bug swallower: a TypeError in a component becomes a blank page AND a
36111
+ * silent one. Without a `fallback` there is nothing to display, so the error is
36112
+ * left alone and continues up.
36113
+ *
36114
+ * - It resets on navigation, not only on rerun. Rerunning the failed action is
36115
+ * one way out; going somewhere else is the common one. Without a reset on the
36116
+ * document URL, the error stays in place of every page after it, including the
36117
+ * ones that would render fine.
36118
+ *
36119
+ * @param {object} props
36120
+ * @param {Function|import("ignore:preact").VNode} [props.fallback] - what is displayed
36121
+ * instead of the children: an element, or a component receiving
36122
+ * `{ error, resetError }`. Without it the boundary is transparent.
36123
+ * @param {() => void} [props.onReset] - called when the fallback dismisses the
36124
+ * error via its `resetError`.
36125
+ */
35922
36126
  const ErrorBoundary = ({
35923
36127
  children,
35924
36128
  fallback,
@@ -35932,9 +36136,30 @@ const ErrorBoundary = ({
35932
36136
  cleanupRef.current?.();
35933
36137
  };
35934
36138
  }, []);
35935
- if (error) {
35936
- error.__handled_by__ = "<ErrorBoundary>"; // prevent jsenv from displaying it
35937
36139
 
36140
+ // The error belongs to the page that failed: leaving it means leaving it
36141
+ // behind.
36142
+ useEffect(() => {
36143
+ if (!error) {
36144
+ return undefined;
36145
+ }
36146
+ const documentUrlWhenCaught = documentUrlSignal.peek();
36147
+ return documentUrlSignal.subscribe(documentUrl => {
36148
+ // subscribe() calls back synchronously with the current value
36149
+ if (documentUrl === documentUrlWhenCaught) {
36150
+ return;
36151
+ }
36152
+ setDismissed(false);
36153
+ resetError();
36154
+ });
36155
+ }, [error]);
36156
+ if (error) {
36157
+ if (!fallback) {
36158
+ // Nothing to display means nothing handled: rethrow untouched so the
36159
+ // error reaches whoever can do something with it (an outer boundary, or
36160
+ // the dev overlay).
36161
+ throw error;
36162
+ }
35938
36163
  const action = error.action;
35939
36164
  if (action) {
35940
36165
  cleanupRef.current?.();
@@ -35964,9 +36189,7 @@ const ErrorBoundary = ({
35964
36189
  setDismissed(true);
35965
36190
  resetError();
35966
36191
  };
35967
- if (!fallback) {
35968
- return null;
35969
- }
36192
+ markErrorAsDisplayedBy(error, "<ErrorBoundary>"); // displayed here, so nothing else has to
35970
36193
  if (typeof fallback === "function") {
35971
36194
  return h(fallback, {
35972
36195
  error,
@@ -37368,10 +37591,24 @@ const debug$1 = (...args) => {
37368
37591
  }
37369
37592
  };
37370
37593
 
37371
- // <Route> dispatches based on props:
37372
- // - children RouteContainer (traverses children statically, renders active branch)
37373
- // - route RouteLeafRoute (rendered by parent container when URL matches)
37374
- // - fallback RouteActive (rendered by parent container when no sibling matches)
37594
+ /**
37595
+ * Dispatches on its props:
37596
+ * - children RouteContainer (traverses children statically, renders active branch)
37597
+ * - route RouteLeafRoute (rendered by parent container when URL matches)
37598
+ * - fallback → RouteActive (rendered by parent container when no sibling matches)
37599
+ *
37600
+ * @param {object} props
37601
+ * @param {object} [props.route] - the route this branch is for, from `route()`
37602
+ * @param {object} [props.routeParams] - selects a branch on a param of that route
37603
+ * @param {boolean} [props.fallback] - the branch taken when no sibling matches
37604
+ * @param {Function|import("ignore:preact").VNode} [props.element] - what the branch renders
37605
+ * @param {object} [props.elementProps] - props given to `element`
37606
+ *
37607
+ * A branch says what it renders, not what happens when it cannot: loading and
37608
+ * error states are delegated to `<Loading>` and `<ErrorBoundary>` ancestors,
37609
+ * which may be written between routes (a container reads through them, see
37610
+ * collectBranches).
37611
+ */
37375
37612
  const Route = props => {
37376
37613
  if (props.children) {
37377
37614
  return jsx(RouteContainer, {
@@ -37413,6 +37650,9 @@ const collectRoutePages = children => {
37413
37650
  return;
37414
37651
  }
37415
37652
  if (child.type !== Route) {
37653
+ // Something written between routes — <Loading>, <ErrorBoundary>, a box of
37654
+ // the app's own. The pages are inside it (see collectBranches).
37655
+ visit(child.props && child.props.children);
37416
37656
  return;
37417
37657
  }
37418
37658
  const {
@@ -37475,8 +37715,8 @@ const RouteContainer = ({
37475
37715
  return content;
37476
37716
  };
37477
37717
  // Walk JSX children vnodes (without rendering) to build a branch list and
37478
- // find the active one in the same pass.
37479
- // All children must be <Route> throws in dev otherwise.
37718
+ // find the active one in the same pass. Anything that is not a <Route> is read
37719
+ // through and kept around the branch it holds (see below).
37480
37720
  // Returns { matchingBranch, fallbackBranch, activeBranch }.
37481
37721
  const collectBranches = children => {
37482
37722
  let matchingBranch = null;
@@ -37492,7 +37732,31 @@ const collectBranches = children => {
37492
37732
  return;
37493
37733
  }
37494
37734
  if (child.type !== Route) {
37495
- throw new Error(`All <Route> children must be <Route> nodes, got: ${String(child.type?.name ?? child.type)}`);
37735
+ // Anything else is a wrapper around branches, and the two that matter are
37736
+ // navi's own: a page says what it renders and delegates what it cannot —
37737
+ // loading to <Loading>, failing to <ErrorBoundary> — so those are written
37738
+ // BETWEEN the container and its routes. Reading through them is what lets
37739
+ // a subtree of pages share one, instead of the router demanding that its
37740
+ // children be routes and pushing every boundary outside of it.
37741
+ //
37742
+ // The wrapper is kept around whatever it holds: the container renders the
37743
+ // active branch alone, so the branch has to carry the wrapper with it, or
37744
+ // being selected would mean losing what was written around it.
37745
+ const wrapperChildren = child.props && child.props.children;
37746
+ if (!wrapperChildren) {
37747
+ throw new Error(`A <Route> child must be a <Route>, or hold some: ${String(child.type?.name ?? child.type)} holds nothing.`);
37748
+ }
37749
+ const {
37750
+ matchingBranch: matchingInside,
37751
+ fallbackBranch: fallbackInside
37752
+ } = collectBranches(wrapperChildren);
37753
+ if (matchingInside && !matchingBranch) {
37754
+ matchingBranch = wrapBranch(matchingInside, child);
37755
+ }
37756
+ if (fallbackInside && !fallbackBranch) {
37757
+ fallbackBranch = wrapBranch(fallbackInside, child);
37758
+ }
37759
+ return;
37496
37760
  }
37497
37761
  const {
37498
37762
  children: nodeChildren,
@@ -37549,6 +37813,12 @@ const collectBranches = children => {
37549
37813
  activeBranch
37550
37814
  };
37551
37815
  };
37816
+ const wrapBranch = (branch, wrapper) => {
37817
+ return {
37818
+ ...branch,
37819
+ node: cloneElement(wrapper, null, branch.node)
37820
+ };
37821
+ };
37552
37822
  const RouteLeaf = props => {
37553
37823
  if (props.route) {
37554
37824
  return jsx(RouteLeafRoute, {
@@ -37644,6 +37914,19 @@ const DRAGGED_ATTRIBUTE = "data-navi-route-travel-dragged";
37644
37914
  const TURNED_ATTRIBUTE = "data-navi-route-travel-turned";
37645
37915
  // The name the box wears while it travels, and only then (see nameForTravel).
37646
37916
  const TRAVEL_NAME = "navi-route-travel";
37917
+ // Where the two boxes of a travel stand in the window, published for the
37918
+ // length of it. Measurements only: what is DERIVED from them — where a picture
37919
+ // goes, what a bar covers — is derived in the CSS below, so the app's own
37920
+ // numbers (the room its fixed bars take) can take part in it. Only the
37921
+ // measuring needs JS, and only for the one moment both boxes exist (see
37922
+ // holdTravelGeometry).
37923
+ const TRAVEL_TOP_PROPERTY = "--navi-route-travel-top";
37924
+ const TRAVEL_LEFT_PROPERTY = "--navi-route-travel-left";
37925
+ const TRAVEL_WIDTH_PROPERTY = "--navi-route-travel-width";
37926
+ const TRAVEL_HEIGHT_PROPERTY = "--navi-route-travel-height";
37927
+ const TRAVEL_OLD_TOP_PROPERTY = "--navi-route-travel-old-top";
37928
+ const TRAVEL_OLD_LEFT_PROPERTY = "--navi-route-travel-old-left";
37929
+ const TRAVEL_GEOMETRY_PROPERTIES = [TRAVEL_TOP_PROPERTY, TRAVEL_LEFT_PROPERTY, TRAVEL_WIDTH_PROPERTY, TRAVEL_HEIGHT_PROPERTY, TRAVEL_OLD_TOP_PROPERTY, TRAVEL_OLD_LEFT_PROPERTY];
37647
37930
  const css$R = /* css */`
37648
37931
  /* The name that makes the page inside this box a picture of its own during a
37649
37932
  transition — rather than part of the one big picture the document takes, so
@@ -37716,6 +37999,23 @@ const css$R = /* css */`
37716
37999
  same page changing its mind. */
37717
38000
  mix-blend-mode: normal;
37718
38001
  }
38002
+ &::view-transition-old(navi-route-travel) {
38003
+ /* Where the page being left WAS on screen, which is not where the group
38004
+ stands: the group is at the arriving box (its position animation is
38005
+ dropped along with its height one, below), and the two boxes are at the
38006
+ same place in the layout without being at the same place in the window
38007
+ — one page is scrolled and the other is not, so the box being left
38008
+ starts higher up. Left at the group's own corner the page being left
38009
+ would be seen jumping back to its top before it even begins to leave.
38010
+ Offset here rather than by \`translate\`, which the movement itself uses,
38011
+ and at its own size rather than the group's so that nothing is cut off
38012
+ the far side of the shift (see holdTravelGeometry). */
38013
+ top: calc(var(${TRAVEL_OLD_TOP_PROPERTY}) - var(${TRAVEL_TOP_PROPERTY}));
38014
+ left: calc(
38015
+ var(${TRAVEL_OLD_LEFT_PROPERTY}) - var(${TRAVEL_LEFT_PROPERTY})
38016
+ );
38017
+ width: auto;
38018
+ }
37719
38019
  /* The pages are cut at the edge of the box they travel in. Said HERE and
37720
38020
  nowhere else: these pictures are drawn in the top layer, so no overflow
37721
38021
  on any element of the document — not the box's own, not a frame around
@@ -37727,7 +38027,7 @@ const css$R = /* css */`
37727
38027
  }
37728
38028
  &::view-transition-group(navi-route-travel) {
37729
38029
  /* The window the two pictures are seen through, held still for the whole
37730
- travel at the taller of the two boxes (see holdTravelHeight): the group
38030
+ travel at the taller of the two boxes (see holdTravelGeometry): the group
37731
38031
  is what CLIPS, and the browser animates its height from the box being
37732
38032
  left to the box arriving — so the window shrinks under the pictures and
37733
38033
  cuts the page leaving from the bottom, progressively. The box does end
@@ -37738,7 +38038,44 @@ const css$R = /* css */`
37738
38038
  winning against it with !important — which also drops its position
37739
38039
  animation, fine while a travel box stands in the same place from one
37740
38040
  route to the next. */
37741
- height: var(--navi-route-travel-height);
38041
+ height: var(${TRAVEL_HEIGHT_PROPERTY});
38042
+
38043
+ /* Cut at the safe area, on top of being cut at the box. The pictures are
38044
+ drawn in the top layer, so they cover a fixed bar as easily as anything
38045
+ else — and the box they travel in runs UNDER the bars by design: that
38046
+ is what a fixed bar is for, and what the room it gives back is for. A
38047
+ box scrolled by so much as a pixel therefore starts above the top bar
38048
+ and ends below the bottom one, and the travel would be watched painting
38049
+ over both for its whole length.
38050
+
38051
+ The band left free is the app's own safe area (see layout/safe_area.js)
38052
+ — every kind of furniture at once, not the bars alone, and read rather
38053
+ than asked for, so one that grows, shrinks or unmounts mid-travel is
38054
+ followed without anything being told. What the group cannot know is
38055
+ only where it itself stands, and that is the measured half. */
38056
+ --navi-route-travel-clip-top: max(
38057
+ 0px,
38058
+ var(--navi-safe-area-inset-top) - var(${TRAVEL_TOP_PROPERTY})
38059
+ );
38060
+ --navi-route-travel-clip-left: max(
38061
+ 0px,
38062
+ var(--navi-safe-area-inset-left) - var(${TRAVEL_LEFT_PROPERTY})
38063
+ );
38064
+ --navi-route-travel-clip-bottom: max(
38065
+ 0px,
38066
+ var(${TRAVEL_TOP_PROPERTY}) + var(${TRAVEL_HEIGHT_PROPERTY}) +
38067
+ var(--navi-safe-area-inset-bottom) - 100dvh
38068
+ );
38069
+ --navi-route-travel-clip-right: max(
38070
+ 0px,
38071
+ var(${TRAVEL_LEFT_PROPERTY}) + var(${TRAVEL_WIDTH_PROPERTY}) +
38072
+ var(--navi-safe-area-inset-right) - 100dvw
38073
+ );
38074
+ clip-path: inset(
38075
+ var(--navi-route-travel-clip-top) var(--navi-route-travel-clip-right)
38076
+ var(--navi-route-travel-clip-bottom)
38077
+ var(--navi-route-travel-clip-left)
38078
+ );
37742
38079
  animation-duration: var(--navi-route-travel-duration, 300ms);
37743
38080
  animation-name: none;
37744
38081
  }
@@ -37947,9 +38284,10 @@ const css$R = /* css */`
37947
38284
  * sections inside the box the whole application travels in — and the class they
37948
38285
  * share plus their axis are not enough to tell them apart from the outside.
37949
38286
  *
37950
- * The pages are cut at the edge of this box while they travel, which is written
37951
- * on the transition's own pseudo-elements no overflow of the document reaches
37952
- * pictures drawn in the top layer. It needs nothing of the browser beyond view
38287
+ * The pages are cut at the edge of this box while they travel, and at the app's
38288
+ * safe area the box runs under, which is written on the transition's own
38289
+ * pseudo-elements no overflow of the document reaches pictures drawn in the
38290
+ * top layer. It needs nothing of the browser beyond view
37953
38291
  * transitions themselves: a browser without them (Firefox) navigates without the
37954
38292
  * movement, and the gesture applies its change on release instead of dragging a
37955
38293
  * picture that does not exist.
@@ -38052,8 +38390,8 @@ const RouteTravel = ({
38052
38390
  }
38053
38391
  pageAskedForRef.current = page;
38054
38392
  // The box as it stands before anything moves: rendering is held, so this is
38055
- // still the page being left (see holdTravelHeight).
38056
- const heightBefore = elementRef.current.getBoundingClientRect().height;
38393
+ // still the page being left (see holdTravelGeometry).
38394
+ const rectBefore = elementRef.current.getBoundingClientRect();
38057
38395
  // The hold a navigation already took, if this travel is the answer to one:
38058
38396
  // taking another would be taking a hold on a page that is holding still.
38059
38397
  const releaseRendering = renderingHeldForRouting || holdRendering();
@@ -38068,6 +38406,11 @@ const RouteTravel = ({
38068
38406
  // screen and an error nobody asked for.
38069
38407
  const renderWait = armRouteRenderWait();
38070
38408
  const viewTransition = startViewTransition(async () => {
38409
+ // Whatever is awaited here must be able to resolve without the page being
38410
+ // rendered: the document is frozen for the whole of this callback, and a
38411
+ // frame never comes — waiting for one waits until the browser gives up on
38412
+ // the transition. And it stays frozen exactly this long, so this is also
38413
+ // the shortest thing there is to keep short.
38071
38414
  await whilePageRenders(page, async () => {
38072
38415
  releaseRendering();
38073
38416
  if (change) {
@@ -38076,7 +38419,7 @@ const RouteTravel = ({
38076
38419
  }, renderWait);
38077
38420
  // The page arriving is in the DOM and the transition has not started
38078
38421
  // playing: the one moment both boxes can be known.
38079
- holdTravelHeight(elementRef.current, heightBefore);
38422
+ holdTravelGeometry(elementRef.current, rectBefore);
38080
38423
  });
38081
38424
  travel.viewTransition = viewTransition;
38082
38425
  if (scrub) {
@@ -38405,7 +38748,7 @@ const RouteTravel = ({
38405
38748
  document.documentElement.removeAttribute(TRAVEL_AXIS_ATTRIBUTE);
38406
38749
  document.documentElement.removeAttribute(DRAGGED_ATTRIBUTE);
38407
38750
  document.documentElement.removeAttribute(TURNED_ATTRIBUTE);
38408
- releaseTravelHeight();
38751
+ releaseTravelGeometry();
38409
38752
  }
38410
38753
  };
38411
38754
 
@@ -38787,23 +39130,43 @@ const releaseHold = travel => {
38787
39130
  travelHoldingPictures = null;
38788
39131
  document.documentElement.removeAttribute(HOLD_ATTRIBUTE);
38789
39132
  };
38790
- const TRAVEL_HEIGHT_PROPERTY = "--navi-route-travel-height";
38791
- // The height the group is held at for the whole travel: the taller of the two
38792
- // boxes, so neither picture is ever cut. It cannot be said in CSS neither box
38793
- // is knowable there — and it cannot be measured from one side alone: a page
38794
- // arriving shorter than the one it replaces would cut the one leaving, a page
38795
- // arriving taller would be cut itself.
38796
- const holdTravelHeight = (element, heightBefore) => {
38797
- const heightAfter = element.getBoundingClientRect().height;
38798
- const height = heightBefore > heightAfter ? heightBefore : heightAfter;
38799
- document.documentElement.style.setProperty(TRAVEL_HEIGHT_PROPERTY, `${height}px`);
39133
+
39134
+ // The two boxes of a travel, measured at the one moment both exist: the
39135
+ // arriving page is in the DOM and the transition has not started playing.
39136
+ //
39137
+ // The group stands at the ARRIVING box its own animation is dropped, so it
39138
+ // takes the geometry the browser declared for it and holds it for the whole
39139
+ // travel. That is why both rectangles have to be published: a group that does
39140
+ // not move says nothing about where the page being left was, and its rectangle
39141
+ // in the window is the only thing CSS cannot work out on its own.
39142
+ const holdTravelGeometry = (element, rectBefore) => {
39143
+ const rectAfter = element.getBoundingClientRect();
39144
+ // The height it is held at is the taller of the two boxes, so neither picture
39145
+ // is ever cut. It cannot be measured from one side alone: a page arriving
39146
+ // shorter than the one it replaces would cut the one leaving, a page arriving
39147
+ // taller would be cut itself.
39148
+ const height = rectBefore.height > rectAfter.height ? rectBefore.height : rectAfter.height;
39149
+ const {
39150
+ style
39151
+ } = document.documentElement;
39152
+ style.setProperty(TRAVEL_TOP_PROPERTY, `${rectAfter.top}px`);
39153
+ style.setProperty(TRAVEL_LEFT_PROPERTY, `${rectAfter.left}px`);
39154
+ style.setProperty(TRAVEL_WIDTH_PROPERTY, `${rectAfter.width}px`);
39155
+ style.setProperty(TRAVEL_HEIGHT_PROPERTY, `${height}px`);
39156
+ style.setProperty(TRAVEL_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
39157
+ style.setProperty(TRAVEL_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
38800
39158
  };
38801
39159
  // The live layout takes the box back. A discontinuity by construction — the
38802
39160
  // group stands at the held height, the box is at the new one — and an invisible
38803
39161
  // one: the page arriving is fully in place, and the strip below it that the
38804
39162
  // group still covers shows the page leaving only while it is still on screen.
38805
- const releaseTravelHeight = () => {
38806
- document.documentElement.style.removeProperty(TRAVEL_HEIGHT_PROPERTY);
39163
+ const releaseTravelGeometry = () => {
39164
+ const {
39165
+ style
39166
+ } = document.documentElement;
39167
+ for (const property of TRAVEL_GEOMETRY_PROPERTIES) {
39168
+ style.removeProperty(property);
39169
+ }
38807
39170
  };
38808
39171
 
38809
39172
  // The browser does not take the picture of the page being left when a
@@ -43278,60 +43641,15 @@ const withPixelUnit = value => {
43278
43641
  };
43279
43642
 
43280
43643
  /**
43281
- * The room a fixed bar takes from the content, published so whatever scrolls
43282
- * under it can give that room back.
43283
- *
43284
- * There are TWO rooms to give back, and forgetting the second one is the
43285
- * classic bug:
43286
- *
43287
- * - **padding**, so the end of the content can be scrolled out from under the
43288
- * bar. Without it the last screenful stays covered, unreachable.
43289
- * - **scroll-padding**, so anything the browser scrolls TO lands in front of
43290
- * the bar rather than under it. An anchor link, `scrollIntoView()`, a focused
43291
- * field brought into view, restoring a scroll position — all of them align
43292
- * the target with the edge of the scrollport, which is behind the bar. The
43293
- * padding above does not help here: it moves the content, not the place the
43294
- * browser scrolls the target to.
43295
- *
43296
- * Published on <html> as CSS variables rather than applied to some element:
43297
- * which element scrolls is the app's business, and an app with more than one
43298
- * would have to fight a component that picked for it. The app either marks its
43299
- * scrolling area with `data-navi-fixed-bar-space` (the rules below) or reads
43300
- * the variables itself. `:root` gets the scroll-padding unconditionally,
43301
- * because the document is the scrollport in the common case and an anchor
43302
- * landing under a bar is never what anyone wants.
43303
- *
43304
- * The variables hold the measured size of the bars on that edge — see the
43305
- * comment where FixedBar sets them.
43644
+ * How much room the fixed bars take on each edge, published for the safe area
43645
+ * to add up (see layout/safe_area.js — it declares the four variables written
43646
+ * here, and what reads them reads the sum, never these).
43647
+ *
43648
+ * Measured rather than declared: a bar's size comes from a prop, a theme
43649
+ * variable, its own content or the device's notch, and only the used value
43650
+ * knows all four.
43306
43651
  */
43307
43652
 
43308
- const FIXED_BAR_SPACE_CSS = /* css */ `
43309
- :root {
43310
- --navi-fixed-bar-space-top: 0px;
43311
- --navi-fixed-bar-space-bottom: 0px;
43312
- --navi-fixed-bar-space-left: 0px;
43313
- --navi-fixed-bar-space-right: 0px;
43314
-
43315
- scroll-padding-top: var(--navi-fixed-bar-space-top);
43316
- scroll-padding-right: var(--navi-fixed-bar-space-right);
43317
- scroll-padding-bottom: var(--navi-fixed-bar-space-bottom);
43318
- scroll-padding-left: var(--navi-fixed-bar-space-left);
43319
- }
43320
-
43321
- /* Put this on whatever scrolls under the bars. */
43322
- [data-navi-fixed-bar-space] {
43323
- padding-top: var(--navi-fixed-bar-space-top);
43324
- padding-right: var(--navi-fixed-bar-space-right);
43325
- padding-bottom: var(--navi-fixed-bar-space-bottom);
43326
- padding-left: var(--navi-fixed-bar-space-left);
43327
-
43328
- scroll-padding-top: var(--navi-fixed-bar-space-top);
43329
- scroll-padding-right: var(--navi-fixed-bar-space-right);
43330
- scroll-padding-bottom: var(--navi-fixed-bar-space-bottom);
43331
- scroll-padding-left: var(--navi-fixed-bar-space-left);
43332
- }
43333
- `;
43334
-
43335
43653
  // Several bars can share an edge — during a page transition the outgoing and
43336
43654
  // the incoming one are both mounted. They are all pinned to that same edge, so
43337
43655
  // they overlap: the room to give back is the largest of them, not their sum,
@@ -43461,7 +43779,10 @@ installImportMetaCssBuild(import.meta);/**
43461
43779
  * nearest scrolling ancestor, and an app shell almost always has one (an
43462
43780
  * `overflow` somewhere) — the bar would then stick inside that box and
43463
43781
  * never to the window. Fixed, centered and bounded by `maxWidth`, it also
43464
- * stays lined up with the content on a wide screen.
43782
+ * stays lined up with the content on a wide screen. It is pinned to the
43783
+ * app's rectangle rather than to the glass (`--navi-app-inset-*`, see
43784
+ * layout/safe_area.js): an app that declares itself narrower than the window
43785
+ * keeps its bars against its own edges.
43465
43786
  * 2. **It gives its space back.** Being fixed it covers the content: without a
43466
43787
  * reserve the end of a long page stays under it, unreachable. It publishes
43467
43788
  * what it takes on <html> — see fixed_bar_space.js.
@@ -43493,8 +43814,6 @@ const css$L = /* css */`
43493
43814
  }
43494
43815
  }
43495
43816
 
43496
- ${FIXED_BAR_SPACE_CSS}
43497
-
43498
43817
  .navi_fixed_bar {
43499
43818
  position: fixed;
43500
43819
  z-index: var(--navi-z-index-bar);
@@ -43509,8 +43828,8 @@ const css$L = /* css */`
43509
43828
  whose padding ignored it would put its first item under it. */
43510
43829
  &[data-area="top"],
43511
43830
  &[data-area="bottom"] {
43512
- right: 0;
43513
- left: 0;
43831
+ right: var(--navi-app-inset-right);
43832
+ left: var(--navi-app-inset-left);
43514
43833
  /* No width of its own: pinned to both edges, the used width absorbs the
43515
43834
  padding instead of being inflated by it. max-width then narrows it and
43516
43835
  the auto margins re-center it. */
@@ -43524,8 +43843,8 @@ const css$L = /* css */`
43524
43843
  }
43525
43844
  &[data-area="left"],
43526
43845
  &[data-area="right"] {
43527
- top: 0;
43528
- bottom: 0;
43846
+ top: var(--navi-app-inset-top);
43847
+ bottom: var(--navi-app-inset-bottom);
43529
43848
  padding-top: calc(
43530
43849
  var(--navi-fixed-bar-padding) + env(safe-area-inset-top)
43531
43850
  );
@@ -43539,28 +43858,28 @@ const css$L = /* css */`
43539
43858
  added to the size: the background then runs under the notch while the
43540
43859
  content keeps the whole width/height asked for. */
43541
43860
  &[data-area="top"] {
43542
- top: 0;
43861
+ top: var(--navi-app-inset-top);
43543
43862
  height: calc(var(--navi-fixed-bar-height) + env(safe-area-inset-top));
43544
43863
  padding-top: env(safe-area-inset-top);
43545
43864
  box-shadow: 0 var(--navi-fixed-bar-border-width) 0
43546
43865
  var(--navi-fixed-bar-border-color);
43547
43866
  }
43548
43867
  &[data-area="bottom"] {
43549
- bottom: 0;
43868
+ bottom: var(--navi-app-inset-bottom);
43550
43869
  height: calc(var(--navi-fixed-bar-height) + env(safe-area-inset-bottom));
43551
43870
  padding-bottom: env(safe-area-inset-bottom);
43552
43871
  box-shadow: 0 calc(-1 * var(--navi-fixed-bar-border-width)) 0
43553
43872
  var(--navi-fixed-bar-border-color);
43554
43873
  }
43555
43874
  &[data-area="left"] {
43556
- left: 0;
43875
+ left: var(--navi-app-inset-left);
43557
43876
  width: calc(var(--navi-fixed-bar-width) + env(safe-area-inset-left));
43558
43877
  padding-left: env(safe-area-inset-left);
43559
43878
  box-shadow: var(--navi-fixed-bar-border-width) 0 0
43560
43879
  var(--navi-fixed-bar-border-color);
43561
43880
  }
43562
43881
  &[data-area="right"] {
43563
- right: 0;
43882
+ right: var(--navi-app-inset-right);
43564
43883
  width: calc(var(--navi-fixed-bar-width) + env(safe-area-inset-right));
43565
43884
  padding-right: env(safe-area-inset-right);
43566
43885
  box-shadow: calc(-1 * var(--navi-fixed-bar-border-width)) 0 0
@@ -54760,12 +55079,13 @@ const css$v = /* css */`
54760
55079
  pointer-events: none;
54761
55080
  }
54762
55081
 
54763
- /* Scrolling with the page means sticking to the viewport, and a FixedBar
54764
- is in front of that viewport: without the offset a sticky label lands
54765
- behind the bar. The bar publishes the room it takes (see
54766
- fixed_bar_space.js) and it is 0px when there is no bar. */
55082
+ /* Scrolling with the page means sticking to the viewport, and whatever the
55083
+ app puts in front of that viewport a FixedBar, a band of its own — is
55084
+ in front of the label too: without the offset a sticky label lands behind
55085
+ it. The safe area is what that adds up to (see layout/safe_area.js) and
55086
+ it is 0px when nothing covers the top. */
54767
55087
  &[data-scroller="document"] {
54768
- --x-list-group-label-top: var(--navi-fixed-bar-space-top, 0px);
55088
+ --x-list-group-label-top: var(--navi-safe-area-inset-top);
54769
55089
  }
54770
55090
 
54771
55091
  &[data-expand-x] {
@@ -71604,5 +71924,5 @@ const UserSvg = () => jsx("svg", {
71604
71924
  })
71605
71925
  });
71606
71926
 
71607
- export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeSpin, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
71927
+ export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeSpin, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
71608
71928
  //# sourceMappingURL=jsenv_navi.js.map