@jsenv/navi 0.29.236 → 0.29.298

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.
@@ -13,7 +13,7 @@ const installImportMetaCssBuild = (importMeta) => {
13
13
  const stylesheet = new CSSStyleSheet({ baseUrl: importMeta.url });
14
14
  stylesheet.replaceSync(value);
15
15
  stylesheetMap.set(url, stylesheet);
16
- document.adoptedStyleSheets = [...document.adoptedStyleSheets, stylesheet];
16
+ document.adoptedStyleSheets.push(stylesheet);
17
17
  };
18
18
  const update = (url, value) => {
19
19
  stylesheetMap.get(url).replaceSync(value);
@@ -9,12 +9,12 @@
9
9
  "../../src/navi_css_vars.js"
10
10
  ],
11
11
  "sourcesContent": [
12
- "const installImportMetaCssBuild = (importMeta) => {\n const IMPORT_META_CSS_BUILD = \"jsenv_import_meta_css_build\";\n\n if (importMeta.css === IMPORT_META_CSS_BUILD) {\n return;\n }\n\n const stylesheetMap = new Map();\n const adopt = (url, value) => {\n const stylesheet = new CSSStyleSheet({ baseUrl: importMeta.url });\n stylesheet.replaceSync(value);\n stylesheetMap.set(url, stylesheet);\n document.adoptedStyleSheets = [...document.adoptedStyleSheets, stylesheet];\n };\n const update = (url, value) => {\n stylesheetMap.get(url).replaceSync(value);\n };\n const remove = (url) => {\n const stylesheet = stylesheetMap.get(url);\n document.adoptedStyleSheets = document.adoptedStyleSheets.filter(\n (s) => s !== stylesheet,\n );\n stylesheetMap.delete(url);\n };\n\n const currentCssSourceMap = new Map();\n Object.defineProperty(importMeta, \"css\", {\n configurable: true,\n get() {\n return IMPORT_META_CSS_BUILD;\n },\n set([value, url]) {\n if (value === undefined) {\n if (stylesheetMap.has(url)) {\n remove(url);\n currentCssSourceMap.delete(url);\n }\n return;\n }\n if (!stylesheetMap.has(url)) {\n adopt(url, value);\n currentCssSourceMap.set(url, value);\n } else if (currentCssSourceMap.get(url) !== value) {\n update(url, value);\n currentCssSourceMap.set(url, value);\n }\n },\n });\n};\n\nexport { installImportMetaCssBuild };\n",
12
+ "const installImportMetaCssBuild = (importMeta) => {\n const IMPORT_META_CSS_BUILD = \"jsenv_import_meta_css_build\";\n\n if (importMeta.css === IMPORT_META_CSS_BUILD) {\n return;\n }\n\n const stylesheetMap = new Map();\n const adopt = (url, value) => {\n const stylesheet = new CSSStyleSheet({ baseUrl: importMeta.url });\n stylesheet.replaceSync(value);\n stylesheetMap.set(url, stylesheet);\n document.adoptedStyleSheets.push(stylesheet);\n };\n const update = (url, value) => {\n stylesheetMap.get(url).replaceSync(value);\n };\n const remove = (url) => {\n const stylesheet = stylesheetMap.get(url);\n document.adoptedStyleSheets = document.adoptedStyleSheets.filter(\n (s) => s !== stylesheet,\n );\n stylesheetMap.delete(url);\n };\n\n const currentCssSourceMap = new Map();\n Object.defineProperty(importMeta, \"css\", {\n configurable: true,\n get() {\n return IMPORT_META_CSS_BUILD;\n },\n set([value, url]) {\n if (value === undefined) {\n if (stylesheetMap.has(url)) {\n remove(url);\n currentCssSourceMap.delete(url);\n }\n return;\n }\n if (!stylesheetMap.has(url)) {\n adopt(url, value);\n currentCssSourceMap.set(url, value);\n } else if (currentCssSourceMap.get(url) !== value) {\n update(url, value);\n currentCssSourceMap.set(url, value);\n }\n },\n });\n};\n\nexport { installImportMetaCssBuild };\n",
13
13
  "import {\n getVirtualKeyboardOverlayHeight,\n subscribeVisualViewportResizeSettled,\n subscribeWindowResizeSettled,\n} from \"@jsenv/dom\";\nimport { computed, signal } from \"@preact/signals\";\n\nexport const windowWidthSignal = signal(window.innerWidth);\nexport const windowHeightSignal = signal(window.innerHeight);\n\n// Debounced (not a raw \"resize\" listener) — see window_size.js's own\n// module comment: mobile fires a transient \"resize\" when the browser's own\n// UI chrome (address bar, etc.) briefly shows/hides, and this needs to settle\n// on the same tick as Popover/Dialog's own repositioning, or one flickers a\n// moment out of sync with the others.\nsubscribeWindowResizeSettled(() => {\n windowWidthSignal.value = window.innerWidth;\n windowHeightSignal.value = window.innerHeight;\n});\n\n// Visual viewport dimensions — update when the virtual keyboard opens/closes or\n// when the browser UI (address bar) shows/hides.\n// When visualViewport is not available, derived from window signals so they\n// stay live without any extra listeners.\nconst vv = window.visualViewport;\nexport const visualViewportWidthSignal = vv\n ? signal(vv.width)\n : computed(() => windowWidthSignal.value);\nexport const visualViewportHeightSignal = vv\n ? signal(vv.height)\n : computed(() => windowHeightSignal.value);\n\nif (vv) {\n const update = () => {\n visualViewportWidthSignal.value = vv.width;\n visualViewportHeightSignal.value = vv.height;\n };\n // The two directions are not equally trustworthy, and treating them alike is\n // what makes one of the two bugs unavoidable.\n //\n // SMALLER is believed at once. Something now covers the screen, and what is\n // sized against these numbers — the dialog/popover ceilings, through\n // --navi-vvh (navi_css_vars.js) — has to answer the smaller screen in the\n // same frame the placement does. The placement reads the viewport live (see\n // getVisibleViewportRect in @jsenv/dom), so a ceiling arriving a debounce\n // later means a box sized for a screen that is gone, placed in the one that\n // replaced it.\n //\n // BIGGER waits for the resize to settle, because growing back is the reading\n // a mobile browser lies about: going straight from one field to the next\n // fires a blur/focus pair that briefly reports the full height again, with\n // the keyboard never having left. Believed, it flicks every popup back to\n // full height and down again between two taps — the \"two inputs\" case in\n // Dialog's own demo. Nothing is lost by waiting: a keyboard that really left\n // stays gone, and the settled event lands 100ms later.\n vv.addEventListener(\"resize\", () => {\n if (\n vv.width < visualViewportWidthSignal.peek() ||\n vv.height < visualViewportHeightSignal.peek()\n ) {\n update();\n }\n });\n subscribeVisualViewportResizeSettled(update);\n vv.addEventListener(\"scroll\", update);\n}\n\n// The app's own screen — the visual viewport, unless the app declared a\n// narrower one with --navi-app-max-width/--navi-app-height (see\n// navi_css_vars.js, which derives --navi-app-width/--navi-app-height from\n// these in CSS). Anything escaping normal flow is sized against this rather\n// than the viewport: an app that simulates a handheld screen keeps that width\n// even for what paints on top of it.\n//\n// The declaration stays in CSS and is read back from there rather than handed\n// to navi a second time in JS — a JS copy would be the one that goes stale.\n// Read on the spot rather than cached in a signal: the only caller is a popup\n// resolving its own margin as it places itself, which already reads far more\n// of the DOM than this, and nothing then has to be invalidated when the value\n// changes.\nconst unresolvableWarned = new Set();\nconst readAppMax = (propertyName) => {\n const declared = getComputedStyle(document.documentElement)\n .getPropertyValue(propertyName)\n .trim();\n if (!declared) {\n return Infinity;\n }\n // A custom property computes to a token stream, not to a length: \"40rem\"\n // arrives here as the string \"40rem\", and parseFloat would read it as 40\n // pixels. Only px is accepted — an app declaring the screen it simulates has\n // a pixel number to give, and resolving arbitrary lengths would mean laying\n // out a probe element on every read.\n const inPixels = /^([0-9.]+)px$/.exec(declared);\n if (!inPixels) {\n if (!unresolvableWarned.has(propertyName)) {\n unresolvableWarned.add(propertyName);\n // Not silently wrong, just partially applied: CSS still caps the popup's\n // size with the declared length, only the margin it keeps with the edges\n // falls back to a share of the viewport (the pre-token behavior).\n console.warn(\n `${propertyName}=\"${declared}\" must be a length in pixels (\"600px\"). Until then popups keep viewport-sized margins.`,\n );\n }\n return Infinity;\n }\n return parseFloat(inPixels[1]);\n};\nexport const getAppWidth = () =>\n Math.min(visualViewportWidthSignal.value, readAppMax(\"--navi-app-max-width\"));\n// The JS reading of --navi-app-inset-* (see safe_area.js): the centered bands\n// between the window's edges and the app's own rectangle. Handed to\n// @jsenv/dom (setPlacementViewportInsets, wired in navi_css_vars.js) so\n// placement keeps to the same rectangle the CSS size caps describe. The\n// keyboard is deliberately absent, unlike in the CSS twin: the placement\n// viewport already subtracts the keyboard overlay itself (see\n// getVisibleViewportRect in @jsenv/dom's visible_rect.js), so carrying it\n// here too would count it twice.\nexport const getAppInsets = () => {\n const vvWidth = visualViewportWidthSignal.value;\n const vvHeight = visualViewportHeightSignal.value;\n const appMaxWidth = readAppMax(\"--navi-app-max-width\");\n const appMaxHeight = readAppMax(\"--navi-app-max-height\");\n const bandX = appMaxWidth < vvWidth ? (vvWidth - appMaxWidth) / 2 : 0;\n const bandY = appMaxHeight < vvHeight ? (vvHeight - appMaxHeight) / 2 : 0;\n return { left: bandX, top: bandY, right: bandX, bottom: bandY };\n};\n// Minus what the keyboard covers, so this stays the JS reading of the very\n// same rectangle --navi-app-height describes in CSS (see safe_area.js's own\n// --navi-keyboard-inset-bottom). Zero unless the app opted into the keyboard\n// overlaying its content — otherwise the shrinking visual viewport above has\n// already accounted for it, and subtracting again would count it twice.\nexport const getAppHeight = () =>\n Math.max(\n 0,\n Math.min(\n visualViewportHeightSignal.value,\n readAppMax(\"--navi-app-max-height\"),\n ) - getVirtualKeyboardOverlayHeight(),\n );\n\n// Whether the primary input is a finger rather than a mouse. A pointer type is\n// not a size: a narrow desktop window is still a mouse, and a large tablet is\n// still a finger — so anything sized for the on-screen keyboard must key off\n// this, never off windowWidthSignal. Thumb reach needs both, which is what\n// smallTouchScreenSignal below answers.\nconst coarsePointerQuery = window.matchMedia\n ? window.matchMedia(\"(pointer: coarse)\")\n : null;\nexport const coarsePointerSignal = signal(\n coarsePointerQuery ? coarsePointerQuery.matches : false,\n);\nif (coarsePointerQuery) {\n coarsePointerQuery.addEventListener(\"change\", () => {\n coarsePointerSignal.value = coarsePointerQuery.matches;\n });\n}\n\n// Whether the screen is one a bottom sheet actually suits: a finger *and* a\n// screen whose bottom edge stays where the thumb already is. Touch alone is not\n// enough — a tall touch screen (a tablet, a kiosk panel) docks a sheet a whole\n// screen away from where the finger just tapped, which is worse than the\n// centered box it replaced.\n//\n// A phone is recognized by its SHAPE, not by a box of maximum dimensions: what\n// makes the bottom edge reachable is holding a narrow slab, and phones keep\n// growing along their long side (20:9, 21:9) while staying just as narrow. So\n// each orientation is answered on the short side plus the elongation:\n// - upright: narrow enough to be held in one hand, and taller than it is wide.\n// Its height is deliberately unbounded — a very tall narrow screen is the\n// case a bottom sheet is most for, not the case to exclude.\n// - on its side: short enough that the bottom edge is a thumb away whatever the\n// width, and wider than it is tall.\n// A tablet fails both: it is too wide upright, and too tall on its side — the\n// smallest one already starts around 740 CSS px on its short side, and the\n// bound below leaves that gap deliberately wide rather than cutting close to\n// the largest phone.\n//\n// Read off window, not visualViewport: the virtual keyboard shrinks the visual\n// viewport while the user types, and a dialog must not undock mid-interaction\n// because a keyboard opened under it.\nconst HANDHELD_MAX_SHORT_SIDE = 600;\n// Enough elongation to tell a slab from a square-ish panel; a phone is well\n// past it (1.7 and up) in either orientation.\nconst HANDHELD_MIN_RATIO = 1.2;\nexport const smallTouchScreenSignal = computed(() => {\n if (!coarsePointerSignal.value) {\n return false;\n }\n const width = windowWidthSignal.value;\n const height = windowHeightSignal.value;\n if (width <= HANDHELD_MAX_SHORT_SIDE) {\n return height >= width * HANDHELD_MIN_RATIO;\n }\n if (height <= HANDHELD_MAX_SHORT_SIDE) {\n return width >= height * HANDHELD_MIN_RATIO;\n }\n return false;\n});\n",
14
14
  "/**\n * The part of the window an app actually has, in two levels.\n *\n * Two, and not one, for a reason worth stating up front: a fixed bar is one of\n * the things that reduce the free region, so it cannot ALSO be placed against\n * that region — it would push itself off the edge it is pinned to. What is\n * anchored and what is anchored-inside are two different rectangles.\n *\n * 1. `--navi-app-inset-{top,right,bottom,left}` — from the window's edges to\n * the app's own rectangle. Whatever is pinned to an edge (a fixed bar, a\n * side panel, a popup aimed at a corner) is pinned to THAT, so an app\n * pretending to be a 600px handheld inside a 1500px window stays one\n * rectangle instead of a column with its furniture spread across the glass.\n *\n * 2. `--navi-safe-area-inset-{top,right,bottom,left}` — from the window's edges\n * to the band left free INSIDE that rectangle. Whatever flows, scrolls, or\n * gets painted keeps to it.\n *\n * Level 2 is a sum, and the contract for taking part in it is only \"publish\n * what you take on one edge\": the device's own notch (`env(safe-area-inset-*)`,\n * which is the browser's version of this very idea), the fixed bars\n * (fixed_bar_space.js), and anything an app adds. That is the point of naming\n * it at all — a component that must stay clear of what covers the screen reads\n * ONE set of numbers, and never has to learn what is covering it.\n *\n * `max()` between the notch and the bars rather than a sum: a bar pinned to an\n * edge already reaches under the notch and counts it in its own size (see\n * fixed_bar.jsx), so adding both would reserve it twice.\n *\n * JS placement answers to the level-1 rectangle too: getAppInsets\n * (layout/responsive.js) is its reading of these same bands, handed to\n * pickPositionRelativeTo via setPlacementViewportInsets (see\n * navi_css_vars.js).\n */\n\nconst SAFE_AREA_CSS = /* css */ `\n /* Declared as lengths so that they COMPUTE to one: an unregistered custom\n property keeps the calc() it was written as, and the sum below is then a\n string no one can read back. Reading it off the computed style — which a\n route transition does, to keep the band the page being left had (see\n nav/transition_window.js) — only works for a registered property. */\n @property --navi-safe-area-inset-top {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-safe-area-inset-right {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-safe-area-inset-bottom {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-safe-area-inset-left {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n\n @layer navi {\n /* Layered whole, rules included: the two rules below are offers, not\n structure. [data-navi-safe-area] is an attribute the app puts on its own\n scroller, so the app's own padding on that element has to win over what\n navi suggests for it. */\n :root {\n /* The room each kind of furniture takes, declared here at zero and\n written by whoever takes it. A slot rather than a value: the sum below\n has to be readable whether or not the app ever mounts a fixed bar. */\n --navi-fixed-bar-space-top: 0px;\n --navi-fixed-bar-space-right: 0px;\n --navi-fixed-bar-space-bottom: 0px;\n --navi-fixed-bar-space-left: 0px;\n\n /* What the on-screen keyboard covers — and ONLY where it overlays the\n content rather than shrinking the viewport, which is navi's default\n wherever the browser has the VirtualKeyboard API (see\n layout/virtual_keyboard.js). Zero on Firefox/Safari, which have no\n such API, and zero for an app that called\n disableVirtualKeyboardOverlay(): both get a keyboard that shrinks the\n visual viewport instead, which --navi-vvh already tracks. Reading\n env() rather than a JS-written value keeps it live: the keyboard\n slides in over several frames and this follows it without a\n listener. */\n --navi-keyboard-inset-bottom: env(keyboard-inset-height, 0px);\n\n /* Level 1. Centered bands, so that declaring one ceiling\n (--navi-app-max-width) is all an app has to do to be a narrow screen in\n a wide window; an app that wants them uneven writes these directly. */\n --navi-app-inset-top: max(\n 0px,\n (var(--navi-vvh) - var(--navi-app-max-height, var(--navi-vvh))) / 2\n );\n /* The keyboard on top of the band, and on this edge only: it eats into\n the app's own rectangle exactly like a viewport that shrank, which is\n what makes both paths end up at the same --navi-app-height (and so at\n the same dialog/popover ceiling). Not part of the centering, hence\n added here rather than folded into --navi-app-inset-top: a keyboard\n takes the bottom, it doesn't re-center anything. */\n --navi-app-inset-bottom: calc(\n var(--navi-app-inset-top) + var(--navi-keyboard-inset-bottom)\n );\n --navi-app-inset-left: max(\n 0px,\n (var(--navi-vvw) - var(--navi-app-max-width, var(--navi-vvw))) / 2\n );\n --navi-app-inset-right: var(--navi-app-inset-left);\n\n /* Level 2. */\n --navi-safe-area-inset-top: calc(\n var(--navi-app-inset-top) +\n max(env(safe-area-inset-top), var(--navi-fixed-bar-space-top))\n );\n --navi-safe-area-inset-right: calc(\n var(--navi-app-inset-right) +\n max(env(safe-area-inset-right), var(--navi-fixed-bar-space-right))\n );\n --navi-safe-area-inset-bottom: calc(\n var(--navi-app-inset-bottom) +\n max(env(safe-area-inset-bottom), var(--navi-fixed-bar-space-bottom))\n );\n --navi-safe-area-inset-left: calc(\n var(--navi-app-inset-left) +\n max(env(safe-area-inset-left), var(--navi-fixed-bar-space-left))\n );\n\n /* The document is the scrollport in the common case, and something the\n browser scrolls to — an anchor, a focused field, a restored position —\n landing under a bar is never what anyone wants. */\n scroll-padding-top: var(--navi-safe-area-inset-top);\n scroll-padding-right: var(--navi-safe-area-inset-right);\n scroll-padding-bottom: var(--navi-safe-area-inset-bottom);\n scroll-padding-left: var(--navi-safe-area-inset-left);\n }\n\n /* Put this on whatever scrolls under the furniture.\n\n There are TWO rooms to give back, and forgetting the second one is the\n classic bug:\n\n - padding, so the end of the content can be scrolled out from under it.\n Without it the last screenful stays covered, unreachable.\n - scroll-padding, so anything the browser scrolls TO lands in front of it\n rather than under. The padding above does not help here: it moves the\n content, not the place the browser scrolls the target to.\n\n Marked by the app rather than picked by navi: which element scrolls is\n the app's business, and an app with more than one would have to fight a\n component that chose for it. An app is free to read the variables itself\n instead. */\n [data-navi-safe-area] {\n padding-top: var(--navi-safe-area-inset-top);\n padding-right: var(--navi-safe-area-inset-right);\n padding-bottom: var(--navi-safe-area-inset-bottom);\n padding-left: var(--navi-safe-area-inset-left);\n\n scroll-padding-top: var(--navi-safe-area-inset-top);\n scroll-padding-right: var(--navi-safe-area-inset-right);\n scroll-padding-bottom: var(--navi-safe-area-inset-bottom);\n scroll-padding-left: var(--navi-safe-area-inset-left);\n }\n }\n`;\nimport.meta.css = SAFE_AREA_CSS;\n",
15
15
  "/**\n * navi's stance on the on-screen keyboard: it overlays the app rather than\n * resizing the viewport, wherever the browser can be told so (the\n * VirtualKeyboard API — Chromium only). See virtual_keyboard.js in @jsenv/dom\n * for what that trades away and what it gives back.\n *\n * Turned on rather than offered, because navi already sizes everything that\n * escapes normal flow against the app's own rectangle rather than against the\n * window (--navi-app-width/height, see navi_css_vars.js), and\n * --navi-keyboard-inset-bottom (safe_area.js) puts the keyboard into exactly\n * that rectangle. So the two mechanisms reach the same numbers here, and the\n * overlay reaches them without reflowing the page underneath — a resizing\n * viewport is a resize of everything, fired transiently every time focus goes\n * from one input to the next.\n *\n * An app that built its own layout around the viewport shrinking can say so\n * with disableVirtualKeyboardOverlay(), and gets the behavior Firefox and\n * Safari give it anyway.\n *\n * The one thing it hands back to the app: scrolling the focused field into\n * view. A viewport that shrinks makes the browser do it; a keyboard that\n * merely paints over the page leaves whatever is under it under it. navi\n * answers that for what it places itself — a popup is sized and positioned\n * against the app rectangle the keyboard was just subtracted from — and for\n * anything marked [data-navi-safe-area], whose scroll-padding-bottom counts\n * the keyboard in (safe_area.js). A field in a scroller the app never marked\n * is the app's own to handle.\n */\n\nimport { setVirtualKeyboardOverlaysContent } from \"@jsenv/dom\";\n\nsetVirtualKeyboardOverlaysContent(true);\n\nexport const disableVirtualKeyboardOverlay = () => {\n setVirtualKeyboardOverlaysContent(false);\n};\n",
16
16
  "/**\n * Regroup CSS vars that makes sense to share across all navi components.\n */\nimport { setPlacementViewportInsets } from \"@jsenv/dom\";\nimport { effect } from \"@preact/signals\";\n\nimport {\n getAppInsets,\n visualViewportHeightSignal,\n visualViewportWidthSignal,\n} from \"./layout/responsive.js\";\nimport \"./layout/safe_area.js\";\n// Side-effect import: turns the on-screen keyboard into something that\n// overlays the app instead of resizing the viewport, which is navi's default\n// (see that module for why, and safe_area.js's own\n// --navi-keyboard-inset-bottom for what then reads the geometry). Here rather\n// than in each component: it is one decision about the whole window, and this\n// is the module that already makes those.\nimport \"./layout/virtual_keyboard.js\";\n\nconst button = document.createElement(\"button\");\nbutton.style.display = \"none\";\ndocument.body.appendChild(button);\nconst computedStyle = getComputedStyle(button);\nconst controlDefaultFontFamily = computedStyle.fontFamily;\nconst controlDefaultFontSize = computedStyle.fontSize;\ndocument.body.removeChild(button);\n\nconst css = /* css */ `\n @layer navi {\n :root {\n /* Overridden at runtime with precise VisualViewport pixel values so that dvw/dvh \n (which don't track the virtual keyboard dimensions) are never used in practice on supported browsers. */\n --navi-vvw: 100dvw;\n --navi-vvh: 100dvh;\n\n /* What navi treats as \"the screen\" when it sizes something that escapes\n normal flow (a dialog in the top layer, a popover, anything built on\n them): the app's own rectangle, which is the visual viewport minus the\n bands an app asks for. An app that never spans the whole window says so\n ONCE, without ever naming a component:\n\n :root {\n --navi-app-max-width: 600px;\n }\n\n In pixels — see readAppMax in layout/responsive.js for why.\n\n Typically an app simulating a handheld screen: a column centered in a\n wide window with bands on the sides. A dialog is in the top layer, so\n it answers to the viewport, not to that column — left alone it would\n paint 1500px of modal over a 600px app. Declaring the ceiling here is\n what makes the app's own width apply everywhere, including on top.\n\n It stays a ceiling and nothing else: on a screen narrower than the app\n it never binds, so popups keep shrinking with the phone, and the gap\n each popup keeps with the edges (marginWithContainer) is subtracted\n from it as before. A single popup that genuinely needs more can still\n raise its own maxWidth/maxHeight prop.\n\n Read from the insets rather than as a min() of its own so that the\n width and the placement come from ONE description of where the app is\n (see layout/safe_area.js). Placement follows the same rectangle: see\n setPlacementViewportInsets at the bottom of this file. */\n --navi-app-width: calc(\n var(--navi-vvw) - var(--navi-app-inset-left) - var(\n --navi-app-inset-right\n )\n );\n --navi-app-height: calc(\n var(--navi-vvh) - var(--navi-app-inset-top) - var(\n --navi-app-inset-bottom\n )\n );\n\n --navi-focus-outline-width: 2px;\n --navi-focus-outline-color: light-dark(#4476ff, #3b82f6);\n --navi-loader-color: light-dark(#355fcc, #3b82f6);\n /* The grey rectangle a mobile browser flashes under a finger: we draw\n what a press does ourselves (background, ring, chevron), and the\n browser's rectangle is a square painted over rounded corners.\n Declared here alone because the property is inherited: :root covers\n every element, in and out of navi, so nothing has to remember it.\n Set the token to a color to get the flash back, on the whole app or\n on one subtree. */\n -webkit-tap-highlight-color: var(--navi-tap-highlight-color);\n --navi-tap-highlight-color: transparent;\n\n /* A face of their own for the controls, and nothing else on the page.\n Undeclared here on purpose: controls write in inherit, so what a\n container says reaches the button, the field and the picker inside it,\n and a value on :root would resolve here and wall that off again. Set\n it to give the controls their own face, on the whole app or on one\n subtree:\n\n :root {\n --navi-control-font-family: system-ui;\n }\n */\n --navi-control-font-size: ${controlDefaultFontSize};\n --navi-control-border-radius: 2px;\n /* A checkbox is only ~1em wide: applying a control radius meant for\n buttons/inputs (say 8px) would turn it into a circle and make it read\n as a radio. So it follows the control radius but capped at a quarter\n of its own size. Override this token alone to opt out of the cap. */\n --navi-checkbox-border-radius: min(\n var(--navi-control-border-radius),\n 0.25em\n );\n /* The color a control uses to say \"this one is on\": a checked checkbox,\n a checked radio, an enabled switch. Kept apart from --navi-accent-color\n (the brand color for CTA and selection) because a control that is on\n must stay readable as a control, which usually means a brighter, more\n saturated color than a brand accent.\n The light value matches the browser's own accent so a navi control and\n a native one can sit side by side without reading as two blues. */\n --navi-control-accent-color: light-dark(rgb(24, 117, 255), #3b82f6);\n --navi-control-border-width: 1px;\n --navi-control-border-color: light-dark(#767676, #8e8e93);\n --navi-control-padding-x-default: 2px;\n --navi-control-padding-y-default: 1px;\n /* Global padding defaults — override these to change all button paddings. */\n /* Use --button-padding, --button-padding-x, --button-padding-y for per-button overrides. */\n --navi-button-padding-x-default: 6px;\n --navi-button-padding-y-default: 1px;\n /* Read by Button, and by whatever is drawn as one (a picker under\n variant=\"button\"), so the two keep the same surface. */\n --navi-button-background-color: light-dark(#f3f4f6, #2d3748);\n\n /* What a callout says about its message, as a color — and what draws\n as one outside the callout too (the icon a picker in callout mode\n presses, see CalloutStatusIcon). */\n --navi-callout-success-color: #4caf50;\n --navi-callout-info-color: #2196f3;\n --navi-callout-warning-color: #ff9800;\n --navi-callout-error-color: #f44336;\n /* No status: the message is an aside, said without an icon and framed\n like a control rather than like a warning. */\n --navi-callout-neutral-color: var(--navi-control-border-color);\n /* For list item we need slightly more padding to be able to see radio/checkbox outline */\n --navi-list-item-padding-x-default: 4px;\n --navi-list-item-padding-y-default: 1px;\n /* default */\n --navi-picker-padding-x-default: var(--navi-control-padding-x-default);\n --navi-picker-padding-y-default: var(--navi-control-padding-y-default);\n\n --navi-popup-border-radius: 8px;\n --navi-popup-border-color: light-dark(#d0d0d0, #3b3b3b);\n --navi-popup-box-shadow:\n 0 4px 8px rgba(0, 0, 0, 0.08), 0 12px 40px rgba(0, 0, 0, 0.22);\n /* A popup's paper is the same paper as everything else's: derived from\n the surface token so one override themes fields, layouts and popups\n together. Still its own token, so popups alone can be re-papered. */\n --navi-popup-background-color: var(--navi-surface-color);\n /* The ink written on that paper. A popup declares both on itself (see\n dialog.jsx / popover.jsx): it is a new surface, so it does not write\n in whatever the container it was declared in writes in. */\n --navi-popup-color: var(--navi-surface-text-color);\n /* A backdrop's paint is a colour AND a filter, one pair per kind of\n backdrop: how much the page behind must stop competing for the eye is\n a different question from what an outside click does, so a backdrop\n that closes on that click can blur too. Only \"capture\" blurs by\n default. */\n --navi-backdrop-close-background: rgba(0, 0, 0, 0.08);\n --navi-backdrop-close-backdrop-filter: none;\n /* backdropVariant=\"discrete\": the popup still catches every outside\n click, it just stops announcing that it did. For an affordance one\n reaches past rather than through — the dim is there to mark the layer,\n not to push the page behind it away. */\n --navi-backdrop-discrete-background: rgba(0, 0, 0, 0.02);\n --navi-backdrop-discrete-backdrop-filter: none;\n /* \"capture\" means the rest of the page is fully non-interactive —\n blurred, not just dimmed, so it reads as clearly secondary and\n pulls visual focus onto the popover's own content. */\n --navi-backdrop-capture-background: rgb(255 255 255 / 0.08);\n --navi-backdrop-capture-backdrop-filter: blur(30px) saturate(180%);\n\n /* Link colors. They live here rather than only on .navi_link because a\n var declared on the element itself always beats the same var inherited\n from an ancestor: a page setting --link-color-pressed on :root would\n never reach a link. These :root tokens are the theme-level surface;\n --link-color-* stays the per-link (or per-subtree) override. */\n --navi-link-color: rgb(0, 0, 238);\n /* --navi-link-color-visited is intentionally left undefined: by default a\n visited link is derived from whatever --link-color ended up being on\n the link itself (see link.jsx), which a :root token cannot see. Set it\n here from an app to pin one visited color for every link. */\n --navi-link-color-pressed: red;\n --navi-link-current-indicator-color: rgb(205, 52, 37);\n\n --navi-selection-border-color: #0078d4;\n --navi-selection-background-color: #eaf1fd;\n /* Accent color — used for call-to-action buttons and selected list items.\n Override this single variable to apply a consistent brand color across\n all components that need to stand out. */\n --navi-accent-color: rgb(3, 30, 60);\n /* The surface a control's content sits on — the \"paper\" behind the text.\n Fields use it whenever they need a solid background (their default\n background, a transparent field being edited, …). */\n --navi-surface-color: light-dark(#ffffff, #1c1c1e);\n /* What is written on that paper. The browser's own text color rather\n than a literal, so it follows color-scheme the way the paper does. */\n --navi-surface-text-color: CanvasText;\n /* The plane the app's own frame is painted on — a top bar, a side menu, a\n toolbar: everything that surrounds a screen rather than being in it.\n A step away from the paper, because a bar painted in the paper's own\n color on a page of that color is not a bar, it is a line. Derived from\n the paper rather than written as a literal so it follows the theme by\n construction: a step toward the dark on a light page, toward the light\n on a dark one. Reached as background=\"chrome\" on a Box. */\n --navi-chrome-color: color-mix(\n in srgb,\n var(--navi-surface-color) 94%,\n var(--navi-color-dark)\n );\n /* The line that separates two regions of one surface — a scrolling area's\n header from what scrolls under it, for instance. Not a border: the\n separation belongs to the layout, not to the box that draws it. */\n --navi-separator-color-default: #d1d9e0;\n --navi-color-white: white;\n --navi-color-dark: rgb(55, 60, 69);\n\n --navi-info-color-light: #eaf6fc;\n --navi-info-color: #376cc2;\n --navi-success-color-light: #ecf9ef;\n --navi-success-color: #50c464;\n --navi-warning-color-light: #fdf6e3;\n --navi-warning-color: #f19c05;\n --navi-error-color-light: #fcebed;\n --navi-error-color: #eb364b;\n\n --navi-xxs: 0.125em; /* = 2px at 16px base */\n --navi-xs: 0.25em; /* = 4px at 16px base */\n --navi-s: 0.5em; /* = 8px at 16px base */\n --navi-m: 1em; /* = 16px at 16px base (base font size) */\n --navi-l: 1.5em; /* = 24px at 16px base */\n --navi-xl: 2em; /* = 32px at 16px base */\n --navi-xxl: 3em; /* 48px at 16px base */\n\n --navi-typo-xxs: 0.625rem; /* 10px at 16px base */\n --navi-typo-xs: 0.75rem; /* 12px at 16px base */\n --navi-typo-s: 0.875rem; /* 14px at 16px base */\n --navi-typo-m: 1rem; /* 16px at 16px base (base font size) */\n --navi-typo-l: 1.125rem; /* 18px at 16px base */\n --navi-typo-xl: 1.25rem; /* 20px at 16px base */\n --navi-typo-xxl: 1.5rem; /* 24px at 16px base */\n\n /* The line every text is written on, controls included. A number\n rather than \"normal\", because of the emoji: its box is taller than a\n letter's, so under \"normal\" the one line carrying one stands taller\n than the lines around it, and under a tighter line its top is cut.\n 1.25 is where neither happens — 1 clips the glyph, 1.5 spaces the rows\n out more than reading them asks for — which also makes it the floor\n for an app that displays what people typed.\n The document is written on it; the components that come with a line of\n their own from the browser (Button, Input, Textarea, Select) are handed\n it by name. One number everywhere is what keeps a value on the same\n line whether it is typed in a field or drawn in the page.\n See docs/typography.md. */\n --navi-line-height: 1.25;\n /* The face everything is written in, controls included — they inherit\n it rather than carrying one of their own, so an app names its typeface\n once, wherever it wants it to start: on the document, on a section, on\n a popup opened with font=\"…\". The browser's own control face rather\n than the document's default, so that a page saying nothing gets its\n text and its buttons in one face instead of two.\n Layered like the line above, so the app's own rule wins. */\n font-family: ${controlDefaultFontFamily};\n /* Layered: the document's line is what navi writes on when the app says\n nothing, so the app's own body rule wins. */\n line-height: var(--navi-line-height);\n /* The same line for a control, snapped to the pixel. The browser lays a\n line out at its exact height but paints the glyph on a pixel row: at\n the default control size (13.333px) the line is 16.666px, and the\n two-thirds that do not fit go entirely under the glyph, which then\n sits a pixel above the middle of its field. A whole number of pixels\n has no remainder to put anywhere. Written with em so it resolves on\n the control that uses it, at that control's own size. */\n --navi-control-line-height: round(\n calc(var(--navi-line-height) * 1em),\n 1px\n );\n\n /* Color keywords.\n primary: the ink of the paper, at full strength. An absolute\n rather than currentColor: it is what brings a run back\n to plain text inside a muted one (a value in a secondary\n label). Themed through --navi-surface-text-color.\n secondary: supporting text, captions, less important labels\n emphasis: reinforce meaning, make content stand out more\n discrete: unobtrusive elements that shouldn't compete for attention\n hint: barely-there color, watermarks, ghost placeholders\n The last four mix currentColor toward transparent or black, so they\n follow whatever ink a container writes in: a dark card sets color and\n nothing else. The share of ink in each is the theme's knob, a -mix\n token read wherever the formulas are declared — here and on each\n surface that re-declares them (rules below) — so a ratio set on :root\n reaches the page and its popups alike. Set on a container it reaches\n none of that container's own text, a var() resolving where its custom\n property is declared; a container that wants one keyword otherwise\n for ITS paper (a white at 88% where 80% of white reads too faint on a\n colored resin) pins the keyword itself, and that value stops at the\n next surface. */\n --navi-color-secondary-mix: 80%;\n --navi-color-emphasis-mix: 50%;\n --navi-color-discrete-mix: 60%;\n --navi-color-hint-mix: 25%;\n --navi-color-primary: var(--navi-surface-text-color);\n\n /* What a control shows while it holds nothing: the ::placeholder of an\n input, the empty value slot of a picker. One place for the whole app:\n\n :root {\n --navi-placeholder-color: #8a94a6;\n --navi-placeholder-font-style: italic;\n }\n */\n --navi-placeholder-color: var(--navi-color-discrete);\n --navi-placeholder-font-style: normal;\n }\n\n /* A surface is a new paper: it writes in an ink of its own, and the\n keywords below are computed against that ink rather than the\n container's. A popup writes in --navi-popup-color. */\n .navi_popover,\n .navi_dialog {\n --navi-color-primary: var(--navi-popup-color);\n }\n /* A callout writes in the UA's own ink: callout.js sets color: revert on\n a [popover] element, which the UA styles CanvasText. */\n .navi_callout {\n --navi-color-primary: CanvasText;\n }\n\n /* The keywords derived from the ink, declared on every paper — :root, and\n each surface that is a new one — so what a container pinned for its own\n paper stops at the surface. The -mix ratios (see :root) are read where\n this is declared, and each paper resolves currentColor as its own. On\n the element itself, so a surface beats the container's value whatever\n the layer; an app that wants a surface to keep its container's ink says\n so on the surface, unlayered, and wins in turn. */\n :root,\n .navi_popover,\n .navi_dialog,\n .navi_callout {\n --navi-color-secondary: color-mix(\n in srgb,\n currentColor var(--navi-color-secondary-mix),\n transparent\n );\n --navi-color-emphasis: color-mix(\n in srgb,\n currentColor var(--navi-color-emphasis-mix),\n black\n );\n --navi-color-discrete: color-mix(\n in srgb,\n currentColor var(--navi-color-discrete-mix),\n transparent\n );\n --navi-color-hint: color-mix(\n in srgb,\n currentColor var(--navi-color-hint-mix),\n transparent\n );\n }\n }\n\n /* Hidden appearance */\n input[navi-visually-hidden],\n button[navi-visually-hidden],\n div[navi-visually-hidden] {\n position: absolute;\n top: 0;\n left: 0;\n z-index: -1;\n /* Important to take full size so that scrollIntoView work as expected */\n width: 100%;\n height: 100%;\n margin: 0;\n padding: 0;\n white-space: nowrap;\n border: 0;\n clip-path: inset(50%);\n appearance: none;\n overflow: hidden;\n\n &[navi-debug] {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n clip-path: none;\n appearance: auto;\n }\n }\n`;\nimport.meta.css = css;\n\neffect(() => {\n document.documentElement.style.setProperty(\n \"--navi-vvw\",\n `${visualViewportWidthSignal.value}px`,\n );\n document.documentElement.style.setProperty(\n \"--navi-vvh\",\n `${visualViewportHeightSignal.value}px`,\n );\n});\n\n// Placement follows the same rectangle the size caps above describe: the JS\n// that positions popups (pickPositionRelativeTo in @jsenv/dom) computes\n// against the visual viewport narrowed by the app's own bands, so a dialog\n// centers on the app column and a side panel docks flush against the app's\n// edge rather than the window's. Here rather than in each component, for the\n// same reason as virtual_keyboard.js above: one decision about the whole\n// window.\nsetPlacementViewportInsets(getAppInsets);\n"
17
17
  ],
18
18
  "names": [],
19
- "mappings": "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"
19
+ "mappings": ";;;AAAK,MAAC,yBAAyB,GAAG,CAAC,UAAU,KAAK;AAClD,EAAE,MAAM,qBAAqB,GAAG,6BAA6B;;AAE7D,EAAE,IAAI,UAAU,CAAC,GAAG,KAAK,qBAAqB,EAAE;AAChD,IAAI;AACJ,EAAE;;AAEF,EAAE,MAAM,aAAa,GAAG,IAAI,GAAG,EAAE;AACjC,EAAE,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,KAAK,KAAK;AAChC,IAAI,MAAM,UAAU,GAAG,IAAI,aAAa,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,GAAG,EAAE,CAAC;AACrE,IAAI,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC;AACjC,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC;AACtC,IAAI,QAAQ,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAChD,EAAE,CAAC;AACH,EAAE,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,KAAK,KAAK;AACjC,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC;AAC7C,EAAE,CAAC;AACH,EAAE,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK;AAC1B,IAAI,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;AAC7C,IAAI,QAAQ,CAAC,kBAAkB,GAAG,QAAQ,CAAC,kBAAkB,CAAC,MAAM;AACpE,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,UAAU;AAC7B,KAAK;AACL,IAAI,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC;AAC7B,EAAE,CAAC;;AAEH,EAAE,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAE;AACvC,EAAE,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,EAAE;AAC3C,IAAI,YAAY,EAAE,IAAI;AACtB,IAAI,GAAG,GAAG;AACV,MAAM,OAAO,qBAAqB;AAClC,IAAI,CAAC;AACL,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE;AACtB,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE;AAC/B,QAAQ,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACpC,UAAU,MAAM,CAAC,GAAG,CAAC;AACrB,UAAU,mBAAmB,CAAC,MAAM,CAAC,GAAG,CAAC;AACzC,QAAQ;AACR,QAAQ;AACR,MAAM;AACN,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACnC,QAAQ,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC;AACzB,QAAQ,mBAAmB,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;AAC3C,MAAM,CAAC,MAAM,IAAI,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE;AACzD,QAAQ,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC;AAC1B,QAAQ,mBAAmB,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;AAC3C,MAAM;AACN,IAAI,CAAC;AACL,GAAG,CAAC;AACJ;;ACzCY,MAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU;AAC7C,MAAC,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW;;AAE3D;AACA;AACA;AACA;AACA;AACA,4BAA4B,CAAC,MAAM;AACnC,EAAE,iBAAiB,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU;AAC7C,EAAE,kBAAkB,CAAC,KAAK,GAAG,MAAM,CAAC,WAAW;AAC/C,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA,MAAM,EAAE,GAAG,MAAM,CAAC,cAAc;AACpB,MAAC,yBAAyB,GAAG;AACzC,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK;AACnB,IAAI,QAAQ,CAAC,MAAM,iBAAiB,CAAC,KAAK;AAC9B,MAAC,0BAA0B,GAAG;AAC1C,IAAI,MAAM,CAAC,EAAE,CAAC,MAAM;AACpB,IAAI,QAAQ,CAAC,MAAM,kBAAkB,CAAC,KAAK;;AAE3C,IAAI,EAAE,EAAE;AACR,EAAE,MAAM,MAAM,GAAG,MAAM;AACvB,IAAI,yBAAyB,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK;AAC9C,IAAI,0BAA0B,CAAC,KAAK,GAAG,EAAE,CAAC,MAAM;AAChD,EAAE,CAAC;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM;AACtC,IAAI;AACJ,MAAM,EAAE,CAAC,KAAK,GAAG,yBAAyB,CAAC,IAAI,EAAE;AACjD,MAAM,EAAE,CAAC,MAAM,GAAG,0BAA0B,CAAC,IAAI;AACjD,MAAM;AACN,MAAM,MAAM,EAAE;AACd,IAAI;AACJ,EAAE,CAAC,CAAC;AACJ,EAAE,oCAAoC,CAAC,MAAM,CAAC;AAC9C,EAAE,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC;AACvC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAE;AACpC,MAAM,UAAU,GAAG,CAAC,YAAY,KAAK;AACrC,EAAE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,CAAC,eAAe;AAC5D,KAAK,gBAAgB,CAAC,YAAY;AAClC,KAAK,IAAI,EAAE;AACX,EAAE,IAAI,CAAC,QAAQ,EAAE;AACjB,IAAI,OAAO,QAAQ;AACnB,EAAE;AACF;AACA;AACA;AACA;AACA;AACA,EAAE,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC;AACjD,EAAE,IAAI,CAAC,QAAQ,EAAE;AACjB,IAAI,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE;AAC/C,MAAM,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC;AAC1C;AACA;AACA;AACA,MAAM,OAAO,CAAC,IAAI;AAClB,QAAQ,CAAC,EAAE,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,sFAAsF,CAAC;AAC5H,OAAO;AACP,IAAI;AACJ,IAAI,OAAO,QAAQ;AACnB,EAAE;AACF,EAAE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAChC,CAAC;AACW,MAAC,WAAW,GAAG;AAC3B,EAAE,IAAI,CAAC,GAAG,CAAC,yBAAyB,CAAC,KAAK,EAAE,UAAU,CAAC,sBAAsB,CAAC;AAC9E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,YAAY,GAAG,MAAM;AAClC,EAAE,MAAM,OAAO,GAAG,yBAAyB,CAAC,KAAK;AACjD,EAAE,MAAM,QAAQ,GAAG,0BAA0B,CAAC,KAAK;AACnD,EAAE,MAAM,WAAW,GAAG,UAAU,CAAC,sBAAsB,CAAC;AACxD,EAAE,MAAM,YAAY,GAAG,UAAU,CAAC,uBAAuB,CAAC;AAC1D,EAAE,MAAM,KAAK,GAAG,WAAW,GAAG,OAAO,GAAG,CAAC,OAAO,GAAG,WAAW,IAAI,CAAC,GAAG,CAAC;AACvE,EAAE,MAAM,KAAK,GAAG,YAAY,GAAG,QAAQ,GAAG,CAAC,QAAQ,GAAG,YAAY,IAAI,CAAC,GAAG,CAAC;AAC3E,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;AACjE,CAAC;AACD;AACA;AACA;AACA;AACA;AACY,MAAC,YAAY,GAAG;AAC5B,EAAE,IAAI,CAAC,GAAG;AACV,IAAI,CAAC;AACL,IAAI,IAAI,CAAC,GAAG;AACZ,MAAM,0BAA0B,CAAC,KAAK;AACtC,MAAM,UAAU,CAAC,uBAAuB,CAAC;AACzC,KAAK,GAAG,+BAA+B,EAAE;AACzC;;AAEA;AACA;AACA;AACA;AACA;AACA,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,IAAI,MAAM,CAAC,UAAU,CAAC,mBAAmB;AACzC,IAAI,IAAI;AACI,MAAC,mBAAmB,GAAG,MAAM;AACzC,EAAE,kBAAkB,GAAG,kBAAkB,CAAC,OAAO,GAAG,KAAK;AACzD;AACA,IAAI,kBAAkB,EAAE;AACxB,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM;AACtD,IAAI,mBAAmB,CAAC,KAAK,GAAG,kBAAkB,CAAC,OAAO;AAC1D,EAAE,CAAC,CAAC;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,uBAAuB,GAAG,GAAG;AACnC;AACA;AACA,MAAM,kBAAkB,GAAG,GAAG;AAClB,MAAC,sBAAsB,GAAG,QAAQ,CAAC,MAAM;AACrD,EAAE,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE;AAClC,IAAI,OAAO,KAAK;AAChB,EAAE;AACF,EAAE,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK;AACvC,EAAE,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK;AACzC,EAAE,IAAI,KAAK,IAAI,uBAAuB,EAAE;AACxC,IAAI,OAAO,MAAM,IAAI,KAAK,GAAG,kBAAkB;AAC/C,EAAE;AACF,EAAE,IAAI,MAAM,IAAI,uBAAuB,EAAE;AACzC,IAAI,OAAO,KAAK,IAAI,MAAM,GAAG,kBAAkB;AAC/C,EAAE;AACF,EAAE,OAAO,KAAK;AACd,CAAC;;;;ACtMD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,MAAM,aAAa,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiI/B;AACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAG,aAAA,EAAA,qCAAA,CAAa;;ACrK/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAIA,iCAAiC,CAAC,IAAI,CAAC;;AAE3B,MAAC,6BAA6B,GAAG,MAAM;AACnD,EAAE,iCAAiC,CAAC,KAAK,CAAC;AAC1C;;;;ACfA,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC;AAC/C,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM;AAC7B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;AACjC,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,CAAC;AAC9C,MAAM,wBAAwB,GAAG,aAAa,CAAC,UAAU;AACzD,MAAM,sBAAsB,GAAG,aAAa,CAAC,QAAQ;AACrD,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;;AAEjC,MAAM,GAAG,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgXrB;AACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAG,GAAA,EAAA,kCAAA,CAAG;;AAErB,MAAM,CAAC,MAAM;AACb,EAAE,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW;AAC5C,IAAI,YAAY;AAChB,IAAI,CAAC,EAAE,yBAAyB,CAAC,KAAK,CAAC,EAAE,CAAC;AAC1C,GAAG;AACH,EAAE,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW;AAC5C,IAAI,YAAY;AAChB,IAAI,CAAC,EAAE,0BAA0B,CAAC,KAAK,CAAC,EAAE,CAAC;AAC3C,GAAG;AACH,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0B,CAAC,YAAY,CAAC"
20
20
  }
@@ -18654,6 +18654,20 @@ const resolveSpacingSize = (size, element, property = "padding") => {
18654
18654
  }
18655
18655
  return normalizeStyle(SIZE_MAP[size] || size, property, "js", element);
18656
18656
  };
18657
+ // Whether a spacing is nothing at all, answered without resolving it. A
18658
+ // viewport or container length resolves through a computed-style read, and
18659
+ // asked during a render that read forces the browser to compute the style of
18660
+ // a tree still being built, stylesheets still being adopted. Zero is a fact
18661
+ // about the number written, whatever the unit; a scale keyword is never zero.
18662
+ const isZeroSpacingSize = (size) => {
18663
+ if (typeof size === "number") {
18664
+ return size === 0;
18665
+ }
18666
+ if (typeof size !== "string") {
18667
+ return false;
18668
+ }
18669
+ return parseFloat(size) === 0;
18670
+ };
18657
18671
 
18658
18672
  const COLOR_KEYWORD_MAP = {
18659
18673
  primary: "var(--navi-color-primary)",
@@ -20281,16 +20295,16 @@ const useComposeElementRef = (syncElement, externalRef) => {
20281
20295
  const cleanupRef = useRef(null);
20282
20296
  const elRef = useRef(null);
20283
20297
  const prevSyncElementRef = useRef(undefined);
20284
- const stableRef = useRef(null);
20298
+ const holderRef = useRef(null);
20285
20299
  const externalRefRef = useRef(externalRef);
20286
20300
  const syncElementRef = useRef(syncElement);
20287
20301
  syncElementRef.current = syncElement;
20288
- // Detect external ref identity change between renders. The refCallback is
20289
- // stable across renders, so when the parent passes a new ref object (or
20290
- // switches from null to a ref), Preact does NOT re-fire the callback while
20291
- // the DOM element is unchanged. We must manually clear the old ref and
20292
- // populate the new one with the current element to avoid leaving the new
20293
- // ref's `.current` stuck at `null`.
20302
+ // Detect external ref identity change between renders. The refCallback keeps
20303
+ // its identity once it holds the element, so when the parent passes a new
20304
+ // ref object (or switches from null to a ref), Preact does NOT re-fire the
20305
+ // callback while the DOM element is unchanged. We must manually clear the
20306
+ // old ref and populate the new one with the current element to avoid
20307
+ // leaving the new ref's `.current` stuck at `null`.
20294
20308
  const prevExternalRefRef = useRef(externalRef);
20295
20309
  if (prevExternalRefRef.current !== externalRef) {
20296
20310
  const previous = prevExternalRefRef.current;
@@ -20308,12 +20322,12 @@ const useComposeElementRef = (syncElement, externalRef) => {
20308
20322
  }
20309
20323
  externalRefRef.current = externalRef;
20310
20324
 
20311
- if (!stableRef.current) {
20312
- // Created once, like the ref callback that calls it, and reading the sync
20313
- // function through a ref for that reason: the element can be replaced long
20314
- // after the first render — a tag that changes, a box hidden then shown —
20315
- // and what the new element gets must be the current render's sync, not
20316
- // the one the first render closed over.
20325
+ if (!holderRef.current) {
20326
+ // Created once, and reading the sync function through a ref for that
20327
+ // reason: the element can be replaced long after the first render — a tag
20328
+ // that changes, a box hidden then shown — and what the new element gets
20329
+ // must be the current render's sync, not the one the first render closed
20330
+ // over.
20317
20331
  const runSync = (el) => {
20318
20332
  if (cleanupRef.current) {
20319
20333
  cleanupRef.current();
@@ -20326,6 +20340,18 @@ const useComposeElementRef = (syncElement, externalRef) => {
20326
20340
  cleanupRef.current = cleanup;
20327
20341
  }
20328
20342
  };
20343
+ holderRef.current = { runSync, refCallback: null };
20344
+ }
20345
+ const { runSync } = holderRef.current;
20346
+
20347
+ // A new callback on every render until Preact has handed it the element,
20348
+ // the same one afterwards. Preact re-applies a ref only when its identity
20349
+ // changes, and a diff that throws with no error boundary above skips
20350
+ // commitRoot: its refs are dropped while its DOM stays and is adopted as
20351
+ // mounted by the next render. A callback that never fired would then never
20352
+ // fire, and a Box would stay raw HTML for the life of the document. Free on
20353
+ // a healthy mount, where the first callback is the only one ever created.
20354
+ if (!elRef.current) {
20329
20355
  const refCallback = (el) => {
20330
20356
  elRef.current = el;
20331
20357
  // Keep .current in sync immediately so useEffect callbacks that read
@@ -20349,9 +20375,9 @@ const useComposeElementRef = (syncElement, externalRef) => {
20349
20375
  prevSyncElementRef.current = undefined;
20350
20376
  }
20351
20377
  };
20352
- stableRef.current = { refCallback, runSync };
20378
+ holderRef.current.refCallback = refCallback;
20353
20379
  }
20354
- const { refCallback, runSync } = stableRef.current;
20380
+ const { refCallback } = holderRef.current;
20355
20381
 
20356
20382
  // If element already mounted, re-sync when syncElement reference changed.
20357
20383
  if (elRef.current && syncElement !== prevSyncElementRef.current) {
@@ -31708,6 +31734,17 @@ registerNaviCommand("--navi-send", (source, event, { requester }) => {
31708
31734
  return sent;
31709
31735
  }
31710
31736
  if (isRunning) {
31737
+ // An optimistic control takes its send as done on its own word: what
31738
+ // follows it runs at once and the run goes on detached — still
31739
+ // watched, still able to fail, its error callout then drawn on what
31740
+ // surrounds the closed surface it was typed in (see openCallout's
31741
+ // anchor resolution in callout.js).
31742
+ const sendController = (findControlHost(target) || target)
31743
+ .__uiStateController__;
31744
+ if (sendController?.optimistic) {
31745
+ runAfterSend();
31746
+ return sent;
31747
+ }
31711
31748
  // The send is committing but has not finished: leaving now would take
31712
31749
  // the form off the screen mid-submission (a popup closing over its own
31713
31750
  // running action, a slide moving on before it is answered). What
@@ -36212,6 +36249,16 @@ const useControlProps = (props, {
36212
36249
  if (!deferredCommand) {
36213
36250
  return;
36214
36251
  }
36252
+ if (uiStateController.optimistic) {
36253
+ // An optimistic button takes its action as done on its own word:
36254
+ // the command runs at once and the run goes on detached, the same
36255
+ // release the form counterpart gives the send (see --navi-send in
36256
+ // commands.js). Only a refused press has nothing to follow.
36257
+ if (completion.result !== false) {
36258
+ deferredCommand();
36259
+ }
36260
+ return;
36261
+ }
36215
36262
  runWhenActionSucceeded(completion, deferredCommand);
36216
36263
  };
36217
36264
  return {
@@ -39403,47 +39450,35 @@ const LoadingIndicatorFluid = ({
39403
39450
  const [containerWidth, setContainerWidth] = useState(0);
39404
39451
  const [containerHeight, setContainerHeight] = useState(0);
39405
39452
  const [containerRadius, setContainerRadius] = useState(0);
39453
+
39454
+ // Sized by the observer alone, first frame included: an element gets an
39455
+ // observation for the size it has when first observed, delivered after the
39456
+ // frame's own layout and before its paint. Measuring here instead would
39457
+ // force a layout in the middle of the commit, on a tree the commit is still
39458
+ // writing to, and the frame would lay it out again. Inside the callback style
39459
+ // and layout are clean, so the radius read there is a lookup, not a recalc.
39406
39460
  useLayoutEffect(() => {
39407
39461
  const indicatorEl = ref.current;
39408
39462
  if (!indicatorEl) {
39409
39463
  return null;
39410
39464
  }
39411
- const {
39412
- width,
39413
- height
39414
- } = indicatorEl.getBoundingClientRect();
39415
- setContainerWidth(width);
39416
- setContainerHeight(height);
39417
- if (radius === undefined || radius === "inherit") {
39418
- const parentEl = indicatorEl.parentElement;
39419
- // Prefer the inline style (always available, even before layout is computed).
39420
- // Fall back to computed longhands — getComputedStyle shorthand may return ""
39421
- // if the browser hasn't resolved the layout yet.
39422
- const radius = parentEl.style.borderRadius || parentEl.style.borderTopLeftRadius || getComputedStyle(parentEl).borderTopLeftRadius || "0px";
39423
- setContainerRadius(radius);
39424
- }
39425
- let animationFrameId = null;
39426
- // Create a resize observer to detect changes in the container's dimensions
39427
- const resizeObserver = new ResizeObserver(entries => {
39428
- // Use requestAnimationFrame to debounce updates
39429
- if (animationFrameId) {
39430
- cancelAnimationFrame(animationFrameId);
39431
- }
39432
- animationFrameId = requestAnimationFrame(() => {
39433
- const [containerEntry] = entries;
39434
- const {
39435
- width,
39436
- height
39437
- } = containerEntry.contentRect;
39438
- setContainerWidth(width);
39439
- setContainerHeight(height);
39440
- });
39465
+ const resizeObserver = new ResizeObserver(([containerEntry]) => {
39466
+ const {
39467
+ width,
39468
+ height
39469
+ } = containerEntry.contentRect;
39470
+ setContainerWidth(width);
39471
+ setContainerHeight(height);
39472
+ if (radius === undefined || radius === "inherit") {
39473
+ const parentEl = indicatorEl.parentElement;
39474
+ // The inline style says the radius as the caller wrote it; the
39475
+ // computed longhand otherwise, the shorthand computing to "" when the
39476
+ // four corners differ.
39477
+ setContainerRadius(parentEl.style.borderRadius || parentEl.style.borderTopLeftRadius || getComputedStyle(parentEl).borderTopLeftRadius || "0px");
39478
+ }
39441
39479
  });
39442
39480
  resizeObserver.observe(indicatorEl);
39443
39481
  return () => {
39444
- if (animationFrameId) {
39445
- cancelAnimationFrame(animationFrameId);
39446
- }
39447
39482
  resizeObserver.disconnect();
39448
39483
  };
39449
39484
  }, []);
@@ -43776,6 +43811,13 @@ const measureDrawing = ({
43776
43811
  * Measured rather than declared: a bar's size comes from a prop, a theme
43777
43812
  * variable, its own content or the device's notch, and only the used value
43778
43813
  * knows all four.
43814
+ *
43815
+ * Written once per task, after every bar has measured. The variable resizes
43816
+ * an ancestor of the bars — the scroll container takes its padding from it —
43817
+ * so a write landing between two measurements turns the second one into a
43818
+ * style recalc and a layout of the whole page. Sizes are queued, and the queue
43819
+ * lands in a microtask: still before the browser paints, so the content is
43820
+ * never seen under a bar, and after every read of the commit that queued them.
43779
43821
  */
43780
43822
 
43781
43823
  // Several bars can share an edge — during a page transition the outgoing and
@@ -43789,10 +43831,14 @@ const sizeMapByArea = new Map();
43789
43831
  // size again — the ones for the smaller bars, and the second half of a page
43790
43832
  // transition.
43791
43833
  const writtenValueByArea = new Map();
43834
+ // Sizes said and not yet written. One queue for both callers below: a bar
43835
+ // saying its size twice before the write keeps the last word, whoever said it.
43836
+ const pendingSizeMapByArea = new Map();
43837
+ let flushMicrotaskQueued = false;
43838
+ let flushFrame = null;
43792
43839
 
43793
- // A size that must land before the next paint: a render changed the bar, so
43794
- // the room it takes is given back in that same commit and the content is never
43795
- // painted under it.
43840
+ // A size a render produced: a prop, the children or a theme variable changed
43841
+ // the bar, and the room it takes is given back before that commit is painted.
43796
43842
  /**
43797
43843
  * @param {"top"|"bottom"|"left"|"right"} area
43798
43844
  * @param {Element} barElement - Which bar this size belongs to.
@@ -43800,41 +43846,47 @@ const writtenValueByArea = new Map();
43800
43846
  * content.
43801
43847
  */
43802
43848
  const setFixedBarSpace = (area, barElement, size) => {
43803
- dropPendingSize(area, barElement);
43804
- storeSize(area, barElement, size);
43805
- writeSpace(area);
43806
- };
43807
-
43808
- // A size nothing asked for, coming from a ResizeObserver. Writing the variable
43809
- // resizes an ANCESTOR of the bars — the scroll container takes its padding
43810
- // from it — and mutating layout from inside a resize callback is what makes
43811
- // the browser report "ResizeObserver loop completed with undelivered
43812
- // notifications". So the write waits for the frame that resize produced.
43813
- // Queued here rather than deferred by each bar on its own, so the bars sharing
43814
- // an edge resolve to a single write instead of one per bar.
43849
+ queueSize(area, barElement, size);
43850
+ if (flushMicrotaskQueued) {
43851
+ return;
43852
+ }
43853
+ flushMicrotaskQueued = true;
43854
+ queueMicrotask(() => {
43855
+ flushMicrotaskQueued = false;
43856
+ flushPendingSizes();
43857
+ });
43858
+ };
43859
+
43860
+ // A size nothing asked for, coming from a ResizeObserver. Mutating layout from
43861
+ // inside a resize callback is what makes the browser report "ResizeObserver
43862
+ // loop completed with undelivered notifications", so this write waits for the
43863
+ // frame that resize produced.
43815
43864
  /**
43816
43865
  * @param {"top"|"bottom"|"left"|"right"} area
43817
43866
  * @param {Element} barElement - Which bar this size belongs to.
43818
43867
  * @param {number} size - In px.
43819
43868
  */
43820
43869
  const requestFixedBarSpace = (area, barElement, size) => {
43870
+ queueSize(area, barElement, size);
43871
+ if (flushFrame !== null) {
43872
+ return;
43873
+ }
43874
+ flushFrame = requestAnimationFrame(() => {
43875
+ flushFrame = null;
43876
+ flushPendingSizes();
43877
+ });
43878
+ };
43879
+
43880
+ const queueSize = (area, barElement, size) => {
43821
43881
  let pendingSizeMap = pendingSizeMapByArea.get(area);
43822
43882
  if (!pendingSizeMap) {
43823
43883
  pendingSizeMap = new Map();
43824
43884
  pendingSizeMapByArea.set(area, pendingSizeMap);
43825
43885
  }
43826
43886
  pendingSizeMap.set(barElement, size);
43827
- if (flushFrame !== null) {
43828
- return;
43829
- }
43830
- flushFrame = requestAnimationFrame(flushPendingSizes);
43831
43887
  };
43832
43888
 
43833
- const pendingSizeMapByArea = new Map();
43834
- let flushFrame = null;
43835
-
43836
43889
  const flushPendingSizes = () => {
43837
- flushFrame = null;
43838
43890
  for (const [area, pendingSizeMap] of pendingSizeMapByArea) {
43839
43891
  for (const [barElement, size] of pendingSizeMap) {
43840
43892
  storeSize(area, barElement, size);
@@ -43844,20 +43896,6 @@ const flushPendingSizes = () => {
43844
43896
  pendingSizeMapByArea.clear();
43845
43897
  };
43846
43898
 
43847
- // What the bar itself just said wins over what its observer had queued about
43848
- // it: a bar unmounting gives its room back, and a size queued for it before
43849
- // that must not put it back.
43850
- const dropPendingSize = (area, barElement) => {
43851
- const pendingSizeMap = pendingSizeMapByArea.get(area);
43852
- if (!pendingSizeMap) {
43853
- return;
43854
- }
43855
- pendingSizeMap.delete(barElement);
43856
- if (pendingSizeMap.size === 0) {
43857
- pendingSizeMapByArea.delete(area);
43858
- }
43859
- };
43860
-
43861
43899
  const storeSize = (area, barElement, size) => {
43862
43900
  let sizeMap = sizeMapByArea.get(area);
43863
43901
  if (!sizeMap) {
@@ -48078,6 +48116,7 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
48078
48116
  * replace?: boolean,
48079
48117
  * actionStandalone?: boolean,
48080
48118
  * actionAbortable?: boolean,
48119
+ * optimistic?: boolean,
48081
48120
  * [key: string]: any,
48082
48121
  * }>}
48083
48122
  * @param {boolean} [replace] Go where the press leads — an `href`, a
@@ -48121,6 +48160,12 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
48121
48160
  * running, which the app watches from somewhere else; never for one holding
48122
48161
  * an answer the screen is the only place to read (see
48123
48162
  * docs/interactions.md#the-fourth-question-whose-wait-is-it).
48163
+ * @param {boolean} [optimistic] The action is taken as done on the button's own
48164
+ * word: no busy state, a second press queues behind the run, and its
48165
+ * `command` runs at once — the popup it sits in closes over the run, which
48166
+ * goes on behind. A failure then has no popup to be read in: `resetOnError`
48167
+ * puts the button back and the error callout is drawn on what surrounds the
48168
+ * closed popup (see docs/popup_open.md#the-popup-owns-its-open-state).
48124
48169
  * @param {boolean} [actionAbortable] The person waiting may give up on this
48125
48170
  * button's action: closing the popup the run holds calls it off and goes
48126
48171
  * through, instead of being refused. For a run whose answer may never come —
@@ -58955,7 +59000,7 @@ const useDialogProps = props => {
58955
59000
  bottom: false,
58956
59001
  left: false
58957
59002
  };
58958
- if (resolveSpacingSize(marginWithContainer) === 0) {
59003
+ if (isZeroSpacingSize(marginWithContainer)) {
58959
59004
  const {
58960
59005
  y,
58961
59006
  x