@takazudo/zudo-doc 5.17.0 → 5.17.1

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ All notable changes to `@takazudo/zudo-doc` are documented in this file.
4
4
 
5
5
  The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
6
6
 
7
+ ## [5.17.1] - 2026-09-04
8
+
9
+ ### Bug Fixes
10
+
11
+ - A cross-origin `headerNav` entry no longer steals the active-nav highlight. The header's inline script rebuilt its entries from the live DOM via `new URL(a.href, location.href).pathname`, which drops the origin, so an external entry pointing at another site's root contributed `"/"` and exact-matched this site's own root route (`384cfe1d3`, `440e366fb`).
12
+ - The client nav now reuses the active item SSR already resolved instead of re-deriving it from `location.pathname` alone. SSR prefers category matching and falls back to path matching; the client knew only the path half, so any page whose category picked an item that URL-prefix matching does not lost its highlight on load (`90b0f2904`).
13
+ - The nav repaint now waits until the content band is parsed. The script is inlined inside `<header>`, so at its top-level run the element carrying the section attribute does not exist yet — the repaint fell back to path-only matching and cleared SSR's highlight, and no navigation event fires on initial load to put it back (`76baaf3b1`).
14
+ - `navHref` passes an absolute cross-origin URL through untouched instead of concatenating the locale, version, and base prefixes onto it. Beyond producing a dead same-origin href, the mangled result defeated the cross-origin active-state guard above in exactly the locale and base configurations that need it most (`c6fe1bb08`).
15
+
16
+ ### Other Changes
17
+
18
+ - The nav script's explanatory comments moved out of the emitted bytes into the generator source. The rationale for the fixes above had shipped inline in the `<head>` of every page, growing the script by 1273 bytes of prose; the net cost of the fix set is now +173 bytes (`4f4b73ce5`).
19
+ - Update the zfb peer and development dependency family to 2.15.1 (`e54438e96`). This is a documentation-only upstream release — the shipped wasm artifacts are byte-size identical to 2.15.0 and no public API, export, config default, or engine requirement moves.
20
+
7
21
  ## [5.17.0] - 2026-09-04
8
22
 
9
23
  ### Features
@@ -134,6 +134,7 @@ function createDocPageShell(ctx) {
134
134
  hideSidebar,
135
135
  hideToc,
136
136
  contentWide,
137
+ navSection,
137
138
  headings,
138
139
  canonical,
139
140
  sidebarPersistKey,
@@ -116,6 +116,21 @@ export interface DocLayoutProps extends DocLayoutHtmlAttrs {
116
116
  * (home page, etc.) that want a full-width grid. Defaults to `false`.
117
117
  */
118
118
  contentWide?: boolean;
119
+ /**
120
+ * The page's resolved big category (`getNavSectionForSlug`). Emitted as
121
+ * `data-zd-nav-section` on `.zd-doc-content-band` so the header's inline
122
+ * nav script can reuse SSR's own active-state decision instead of
123
+ * re-deriving one from the URL (zudolab/zudo-doc#3953).
124
+ *
125
+ * It belongs on the content band specifically because that element is
126
+ * INSIDE the client router's swapped region, while the header is persisted
127
+ * across swaps (`data-zfb-transition-persist`). The value therefore stays
128
+ * correct after a body swap, whereas anything written into the header
129
+ * itself would go stale. Omitted when the page has no section (home, 404,
130
+ * tag, version pages), in which case the script falls back to path
131
+ * matching exactly as before.
132
+ */
133
+ navSection?: string;
119
134
  /** Optional footer rendered below the content. */
120
135
  footer?: ComponentChildren;
121
136
  /**
@@ -25,6 +25,7 @@ function DocLayout(props) {
25
25
  toc,
26
26
  hideToc = false,
27
27
  contentWide = false,
28
+ navSection,
28
29
  footer,
29
30
  bodyEndComponents,
30
31
  bodyEndScripts,
@@ -86,6 +87,7 @@ function DocLayout(props) {
86
87
  class: "zd-doc-content-band flex w-full gap-[clamp(1.5rem,3vw,4rem)]",
87
88
  ...!showSidebar ? { "data-zd-nosidebar": "" } : {},
88
89
  ...contentWide ? { "data-zd-wide": "" } : {},
90
+ ...navSection !== void 0 ? { "data-zd-nav-section": navSection } : {},
89
91
  children: [
90
92
  /* @__PURE__ */ jsxs("main", { class: "flex-1 min-w-0 px-hsp-xl py-vsp-xl lg:px-hsp-2xl lg:py-vsp-2xl", children: [
91
93
  breadcrumb,
@@ -192,6 +192,7 @@ function renderNavItem(item, activeNavPath, activeCategory, lang, currentVersion
192
192
  {
193
193
  href,
194
194
  "aria-current": isActive ? "page" : void 0,
195
+ "data-nav-category": item.categoryMatch,
195
196
  "aria-haspopup": "true",
196
197
  "aria-expanded": "false",
197
198
  class: [
@@ -233,6 +234,7 @@ function renderNavItem(item, activeNavPath, activeCategory, lang, currentVersion
233
234
  "a",
234
235
  {
235
236
  href: childHref,
237
+ "data-nav-category": child.categoryMatch,
236
238
  "data-active": childActive ? "" : void 0,
237
239
  class: [
238
240
  "block px-hsp-md py-vsp-2xs text-small hover:bg-accent/10 hover:underline focus-visible:underline",
@@ -251,6 +253,7 @@ function renderNavItem(item, activeNavPath, activeCategory, lang, currentVersion
251
253
  {
252
254
  href,
253
255
  "aria-current": isActive ? "page" : void 0,
256
+ "data-nav-category": item.categoryMatch,
254
257
  "data-nav-item": true,
255
258
  class: [
256
259
  "px-hsp-md py-vsp-2xs text-small font-medium transition-colors shrink-0",
@@ -7,9 +7,13 @@ function buildNavOverflowScript() {
7
7
  return p || "/";
8
8
  }
9
9
 
10
+ // "" for cross-origin: unmatchable sentinel, matching SSR (#3950).
10
11
  function navPathname(a) {
11
- try { return trimSlashes(new URL(a.href, location.href).pathname); }
12
- catch (e) { return ""; }
12
+ try {
13
+ var u = new URL(a.href, location.href);
14
+ if (u.origin !== location.origin) return "";
15
+ return trimSlashes(u.pathname);
16
+ } catch (e) { return ""; }
13
17
  }
14
18
 
15
19
  // Explicit current-route override, embedded from current-path/index.ts so
@@ -57,13 +61,17 @@ function buildNavOverflowScript() {
57
61
 
58
62
  var cur = trimSlashes(readCurrentPath(CURRENT_PATH_DATASET_KEY));
59
63
 
64
+ // SSR's own resolved big category, republished per page (#3953).
65
+ var sectionEl = document.querySelector("[data-zd-nav-section]");
66
+ var navSection = (sectionEl && sectionEl.getAttribute("data-zd-nav-section")) || "";
67
+
60
68
  // Build NavItemLike-shaped entries from the live DOM so the shared
61
69
  // computeActiveNavPath can do the deepest-match walk \u2014 the same call
62
70
  // shape the SSR header uses (matches computeActiveNavPath). A dropdown
63
- // missing its own top-level anchor is skipped entirely (path "" would
64
- // otherwise match every current path \u2014 pathMatchesNavPath treats "" as
65
- // the root "/"), mirroring the parentLink guard used below for the same
66
- // malformed-markup case.
71
+ // missing its own top-level anchor is skipped entirely, mirroring the
72
+ // parentLink guard used below for the same malformed-markup case.
73
+ // A "" path (cross-origin) is unmatchable ONLY because of the length sort
74
+ // plus the \`activePath !== ""\` guards below \u2014 keep both.
67
75
  var navItems = [];
68
76
  topItems.forEach(function (it) {
69
77
  var isDropdown = it.hasAttribute("data-nav-item-dropdown");
@@ -80,6 +88,13 @@ function buildNavOverflowScript() {
80
88
 
81
89
  var activePath = computeActiveNavPath(navItems, cur) || "";
82
90
 
91
+ // Mirrors SSR: category match OR path match, per item (see nav-active.ts).
92
+ function isAnchorActive(a) {
93
+ if (!a) return false;
94
+ if (navSection !== "" && a.getAttribute("data-nav-category") === navSection) return true;
95
+ return activePath !== "" && navPathname(a) === activePath;
96
+ }
97
+
83
98
  function setTopActive(a, active) {
84
99
  if (!a) return;
85
100
  if (active) {
@@ -99,10 +114,10 @@ function buildNavOverflowScript() {
99
114
  var topActive = false;
100
115
 
101
116
  if (isDropdown) {
102
- var parentMatch = !!topA && navPathname(topA) === activePath && activePath !== "";
117
+ var parentMatch = isAnchorActive(topA);
103
118
  var anyChild = false;
104
119
  it.querySelectorAll(":scope > div a").forEach(function (c) {
105
- var childActive = navPathname(c) === activePath && activePath !== "";
120
+ var childActive = isAnchorActive(c);
106
121
  if (childActive) {
107
122
  anyChild = true;
108
123
  c.setAttribute("data-active", "");
@@ -121,7 +136,7 @@ function buildNavOverflowScript() {
121
136
  else { svg.classList.add("text-muted"); svg.classList.remove("text-bg"); }
122
137
  }
123
138
  } else {
124
- topActive = activePath !== "" && navPathname(topA) === activePath;
139
+ topActive = isAnchorActive(topA);
125
140
  }
126
141
 
127
142
  setTopActive(topA, topActive);
@@ -133,7 +148,8 @@ function buildNavOverflowScript() {
133
148
 
134
149
  // Repaint the active highlight for the current URL before measuring /
135
150
  // cloning, so the overflow "\xB7\xB7\xB7" menu mirrors the correct active state.
136
- applyActiveNav();
151
+ // Skipped mid-parse: the content band is not in the DOM yet (#3953).
152
+ if (document.readyState !== "loading") applyActiveNav();
137
153
 
138
154
  var nav = document.querySelector("[data-header-nav]");
139
155
  var moreContainer = document.querySelector("[data-nav-more]");
@@ -337,6 +353,11 @@ function buildNavOverflowScript() {
337
353
  }
338
354
 
339
355
  initNavOverflow();
356
+ // First-paint re-init once the body is parsed, so applyActiveNav can read
357
+ // the content band's data-zd-nav-section (#3953).
358
+ if (document.readyState === "loading") {
359
+ document.addEventListener("DOMContentLoaded", initNavOverflow, { once: true });
360
+ }
340
361
  document.addEventListener("zfb:after-swap", initNavOverflow);
341
362
  })();`;
342
363
  }
package/dist/safelist.css CHANGED
@@ -1,2 +1,2 @@
1
1
  /* generated by gen-safelist.mjs — do not edit by hand */
2
- @source inline("-domtweaker-enabled -elpath-enabled -left-[calc(var(--spacing-icon-lg)/2)] -link -mb-px -ml-hsp-sm -mt-px -noscript -open -state -translate-x-full 2xl:w-[24px] [&::-webkit-details-marker]:hidden [&_a]:pointer-events-auto [&_a]:text-accent [&_a]:underline [&_li]:mb-0 [&_nav]:mb-0 [asset-viewer] [data-admonition] [data-kbd-shortcut] [data-switcher-launcher] [doc-history-meta] [doc-history] [doc-layout] [llms-txt] [zudo-doc] a a2 abbr about above absent absolute accent accent- accent:accent- access across activated active actual actually added admonition admonition- admonition-body admonition-title admonition/callout after after-breadcrumb after-content after-navigate after-sidebar after-title against agent agents ai-chat ai-chat-md ai-chat-trigger alert align-top all allow allow-same-origin allow-scripts allowed alone already already-executed already-multiline already-picked also always an anchor and and/or animate-pulse animate-spin announce ansehen antialiased any anywhere anzeigen app appear application/json application/octet-stream application/pdf application/sql application/toml application/x-httpd-php application/xml application/yaml applied applies apply applying approach approval are area arg argument aria-atomic aria-busy aria-controls aria-current aria-disabled aria-expanded aria-haspopup aria-hidden aria-label aria-labelledby aria-live aria-orientation aria-pressed aria-selected aria-valuemax aria-valuemin aria-valuenow arm arms around arrows article as asc ascii aside aspect-[1200/630] aspect-square asset asset- asset-components assets assets/client assistant async at at-rule attach attribute attributes auf authored auto auto-logo-mask autogenerated availability available avc1 avif avis avoid await away b back backdrop:bg-bg/30 backdrop:bg-bg/80 backdrop:bg-overlay/60 backdrop:z-modal-backdrop background background-color backtick backticks baked banner bare base base- base64 base:base- based bash batch be bearbeiten because becomes been before below best best-effort between bg bg-[#fff] bg-accent bg-bg bg-chat-assistant-bg bg-chat-user-bg bg-code-bg bg-fg bg-info/10 bg-info/5 bg-muted bg-overlay/30 bg-surface bg-surface/50 bg-transparent bg-warning/10 bg-warning/5 bi bigint bin binaries bind binding blank blanks block blockquote blocks blur bodies body body-end-components body-end-scripts bold boolean bootstrap border border-accent border-b border-b-2 border-b-[5px] border-bg/30 border-collapse border-danger border-dashed border-fg border-image border-info/30 border-l border-l-0 border-l-[3px] border-left-width border-muted border-none border-r border-r-0 border-radius border-solid border-t border-t-[2px] border-t-[3px] border-transparent border-warning/30 border-width border-y both bottom-hsp-lg bottom-vsp-xl boundaries box box-border br brackets brand breadcrumb:end breadcrumb:start break-words brief brown browser browser-tab browsers browses btn budget bug build builder built built-in bundler but button buttons by bypassed byte-identical bytes c cache cached calendar-valid call callable called caller calls can cancellation candidate cannot canonical canvas caption captured captures card card-grid cards carry case-insensitive cases cat-nav- catalog catch categories category caught caution center center/contain ch chains change changed changelog changelogs changes characters check checker child children choose chrome chrome-font ci circle cite cjs class class-less class-mode claude claude-agents claude-commands claude-md claude-resources claude-skills cleaned cleanly clear clearing click client client-router client-side clip clobber clobbering close closed closes closing closure code code-block-sr-announce code-group code-group-panel codex codex-agents codex-agents-md codex-config codex-hooks codex-resources codex-rules codex-skills col col-resize col-span-full col-start-1 colgroup collapse collapses collapsible collision color color-scheme color-scheme-changed color-scheme-provider color-tweak colorization colors column comma command commands commas comment commercial commercial-font-denylist commit compare complete component component:github-link component:language-switcher component:search component:theme-toggle component:version-switcher composes composition compute computed concrete conf config configuration configurations configure configured conflicting conflicts confuse connect const construction consumer consumes contain container containers containing contains content content-admonition content-layer content-link content-type content-wrapper:end content-wrapper:start contents context contract control controller controls converts cookie-blocking copied copy copy-url core corners correct correctly corrupt could count covered covers cpp crashes created cross-component crumb- cs csharp css css-presence csv ctx cur current current-path/index.ts current-route currently cursor cursor-not-allowed cursor-pointer custom cycle d danger dark dash data data-active data-admonition data-asset-details-hidden data-auto-logo data-base data-close-search data-current-locale data-default-locale data-doc-date data-doc-description data-doc-metainfo data-doc-pager data-doc-unavailable-versions data-find-active data-find-match data-footer data-group-id data-header data-header-logo data-header-nav data-header-right data-kbd-shortcut data-lang data-language-menu data-language-switcher data-language-toggle data-loading-index data-mermaid-enlarge-ready data-mermaid-rendered data-mermaid-src data-nav-active data-nav-item data-nav-item-dropdown data-nav-more data-nav-more-menu data-nav-more-toggle data-no-results data-note-tray-group data-note-tray-row data-open-search data-pan-active data-processed data-props data-result-count-template data-search-count data-search-count-narrow data-search-dialog data-search-input data-search-placeholder data-search-results data-search-unavailable data-sidebar-hidden data-sidebar-resizer data-site-nav data-switcher-card data-switcher-launcher data-tab-btn data-tab-default data-tab-label data-tab-value data-tabs data-taglist-group data-testid data-theme data-theme-pack data-theme-pack-switcher data-theme/style data-toc-hidden data-trailing-slash data-unavailable-label data-variant data-version-banner data-version-latest data-version-menu data-version-rewire data-version-slug data-version-switcher data-version-toggle data-version-trigger-label data-zd-asset-action data-zd-asset-actions data-zd-asset-details data-zd-asset-details-chevron data-zd-asset-details-list data-zd-asset-details-toggle data-zd-asset-index-action data-zd-asset-index-empty data-zd-asset-index-page data-zd-asset-page data-zd-asset-tree data-zd-copy-url data-zd-html-preview-reservation data-zd-label-collapse data-zd-label-expand data-zd-mobile-sidebar data-zd-mobile-toc data-zd-nosidebar data-zd-pending data-zd-props-preserve data-zd-sidebar-open-key data-zd-theme-pack-css data-zd-theme-pack-css-loading data-zd-theme-pack-loading data-zd-toc data-zd-wide data-zfb-island data-zfb-island-remount data-zfb-reload data-zfb-transition-persist date dated dd decimal decision declaration declare declared declares decoration decoration-muted deepest deepest-match default default-transition-duration defaults deferral deferred del delegated delete deliberately delimiter dependency depends depth der desc description design design-token design-token-panel design-token-trigger desktop desktop-sidebar desktop-sidebar-toggle desktop-sidebar-toggle-island desktop-toc-toggle destroys destructive detach detached details determine deterministic dev dfn diagram diagrams dialog did die dieser diff diff-line-added diff-line-content diff-line-empty diff-line-num diff-line-removed diff-row differ different dir directly directories directory disabled disabled:cursor-default disabled:opacity-50 disabled:pointer-events-none disc display display:none dist distance distinct div dl do doc doc-card- doc-content-band doc-history doc-history-generate doc-history-panel doc-history-trigger doc-page doc-pager doc-prose doc-title docblock docs docs- docs-v- document document-level documentation documented documents does dog dot double-registration download draft drag drawer drift drifts drop dropdown dropdown-parent dropdowns dt duplicate duration-150 duration-200 during dynamically e e2e each eager earlier early ease-in-out edge editing einer either eject ejectable ejectables ejected el element elements els else em embedded emit emitting empty empty/undefined en enable enabled end enhanced enhancement enlarged entire entirely entities entries entry entrypoint env equal error escape escaped escapes even event eventually every everything-enabled exactly example exceeds excerpt excludes exclusively existing exists exit expand expected explicit export extends extra f factories failed fall fallback fallbacks falling falls false family fast favicon feature fg field fields fieldset figcaption figure file files fill fills finally find find-match find-match-active fire fires first first-paint first:mt-0 fit fix fixed fixed-width fixtures flag flash flat flex flex-1 flex-col flex-wrap flip flipping flips flow flush-left focus focus-visible:bg-accent/10 focus-visible:border-accent focus-visible:decoration-accent focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2 focus-visible:text-accent focus-visible:underline focus-within:border-accent focus-within:z-local-1 focus:border-accent focus:outline-none focus:text-accent focus:underline folder folders follows font font-bold font-face-parity font-family font-file-missing font-medium font-mono font-sans font-scale font-semibold font-size font-weight font-weight-bold font-weight-medium font-weight-normal font-weight-semibold font/woff2 fonts footer footer- for form format former found four-link fox fragment frame free freeze fresh from frontmatter frontmatter-preview frozen frozen-script fs-extra ftyp full fully function further g gains gap-[0.3em] gap-[clamp(1.5rem,3vw,4rem)] gap-hsp-2xs gap-hsp-lg gap-hsp-md gap-hsp-sm gap-hsp-xl gap-hsp-xs gap-vsp-2xs gap-vsp-3xs gap-vsp-lg gap-vsp-md gap-vsp-xs gap-x-hsp-2xs gap-x-hsp-lg gap-x-hsp-md gap-x-hsp-sm gap-x-hsp-xs gap-y-vsp-2xs gap-y-vsp-3xs gap-y-vsp-lg gap-y-vsp-md gap-y-vsp-xs gaps gate geladen2026 generate generated generation genuine geometry get getting-started gif git github github-dark github-link give go got grab gradient granular graph grid grid-cols-1 grid-cols-2 grid-cols-[auto_1fr] grid-rows-[auto_auto] grid-rows-subgrid group group-focus-visible:decoration-accent group-focus-visible:text-accent group-focus-visible:text-accent-hover group-focus-visible:text-fg group-focus-visible:underline group-focus-within:block group-hover:bg-fg group-hover:block group-hover:decoration-accent group-hover:text-accent group-hover:text-accent-hover group-hover:text-bg group-hover:text-fg group-hover:underline group-open:rotate-90 grouped grouping guard gz h h-[0.5rem] h-[0.625rem] h-[0.875rem] h-[1.125rem] h-[1.25rem] h-[1.575rem] h-[10rem] h-[14px] h-[1em] h-[1lh] h-[2.5rem] h-[2rem] h-[3.5rem] h-[3rem] h-[70vh] h-[90vh] h-[calc(100%-3rem)] h-[calc(100vh-3.5rem)] h-dvh h-full h-icon-lg h-icon-md h-icon-sm h-icon-xs h1 h1s h2 h22013h4 h2s h3 h4 h5 h6 half hand-copied hand-editable handle handled handler handlers happens hard-loaded hardcoded has hash-link have head head-links head-scripts header header- header-call:end header-call:start header-right heading heading-h2 heading-h3 heading-h4 heading-rule headings height here hex hi-root hidden hide hierarchical highlight highlighting history home hook hooks hooks-json horizontal host hover:bg-[color-mix(in_srgb,var(--color-surface)_80%,var(--color-fg)_20%)] hover:bg-accent-hover hover:bg-accent/10 hover:bg-danger/10 hover:bg-surface hover:border-accent hover:border-accent-hover hover:border-fg hover:decoration-accent hover:text-accent hover:text-accent-hover hover:text-fg hover:underline hover:z-local-1 hpp hr href hrefs hsp hsp-2xl hsp-2xs hsp-lg hsp-md hsp-sm hsp-xl hsp-xs html i i18n/theme. i2 i3 i4 ico icon icon-lg icon-md icon-sm icon-xs identical idle idx if iframe ignoring image image-enlarge image-overlay-inset image/avif image/gif image/jpeg image/png image/webp image/x-icon img implementation import important important-allowlist imports in inactive includes including incomplete independently index index2026 indirectly info inherit inherited ini initial initialised injected inline inline-block inline-flex inner input input-clear ins inserted-after-color-mode inserted-after-color-scheme inserted-after-site-name inserted-first insertion inset-0 inside inside-only inspect install installation installed instance instanceof instead instructions intended intent intentionally intercept interface internal interpolation into invalid invalidated inverse inversion invocation invoke is is-checker island island-root iso2 iso3 iso4 iso5 iso6 isom ispe issues it italic item item- items items-baseline items-center items-end items-start iteration its itself ja java javascript jpeg jpg js json jsx jumps just justification justify-between justify-center justify-end justify-start katex kbd keep keeping keeps kept key keyboard keyboard-shortcut keydown keys keystroke keyword keywords khroma known-token-names kopieren kotlin kt label landing lands language-menu language-switcher language-toggle larger last:border-b-0 last:pb-0 later latest launch layout lazy leading leading-none leading-normal leading-relaxed leading-snug leading-tight leaf leaf- leak leaves leaving left left-0 left:calc legend legitimate length lets letter-spacing lg lg:block lg:border lg:border-fg lg:border-solid lg:flex lg:flex-col lg:flex-row lg:gap-hsp-xl lg:grid-cols-3 lg:grid-cols-[repeat(auto-fit,minmax(12rem,1fr))] lg:h-[90vh] lg:hidden lg:justify-start lg:m-auto lg:max-h-[90vh] lg:max-w-[52.5rem] lg:ml-[var(--zd-sidebar-w)] lg:pr-hsp-sm lg:pt-vsp-2xl lg:px-hsp-2xl lg:py-vsp-2xl lg:text-left lg:w-[90vw] lg:w-[clamp(16rem,25%,22rem)] li li2 library license lifecycle light light/dark like likely line line-height line/statement lines linger link link- links list list-disc list-none listener lists literal literally literals live lives llms llms-txt load loaded loader loading local local-1 local-2 local-3 locale locales log logo long longer longest-match look loses lostpointercapture lower luminance m m-0 m-auto m10 m14 m16 m21 m6 machinery main major make malformed malformed-markup malicious managed manifest manual manually maps mark markdown marks match matches matching math math-display math-inline max max-h-[85vh] max-h-[90vh] max-h-full max-h-none max-w-[16rem] max-w-[64rem] max-w-[85%] max-w-[85vw] max-w-[90vw] max-w-[calc(100vw-2rem)] max-w-[calc(100vw-var(--spacing-hsp-xl))] max-w-[clamp(50rem,75vw,90rem)] max-w-full max-w-none max-w-sm max-width maximum may mb-0 mb-vsp-2xs mb-vsp-lg mb-vsp-md mb-vsp-sm mb-vsp-xl mb-vsp-xs md mdx means measured measurement measures measuring mechanism menu mermaid message messages meta meta-knob meta-schema metadata migration min-h-0 min-h-[20rem] min-h-[44px] min-h-[60vh] min-h-[calc(100vh-3.5rem)] min-h-screen min-w-0 min-w-[10rem] min-w-[3rem] min-w-[44px] min-w-[8rem] minifier minor mirror mirroring mirrors missing mit mjs ml-[calc(var(--spacing-hsp-xl)+1px)] ml-auto ml-hsp-2xl ml-hsp-lg ml-hsp-md ml-hsp-sm ml-hsp-xl mobile mod modal modal-backdrop mode model modify module moment monospace month more most mount mounted mouseenter mouseleave mov move mp4 mp41 mp42 mr-[calc(var(--spacing-hsp-xl)+1px)] mr-hsp-sm ms mt-0 mt-vsp-2xl mt-vsp-2xs mt-vsp-3xs mt-vsp-lg mt-vsp-md mt-vsp-sm mt-vsp-xl mt-vsp-xs multi-changelog multiple must mutates mutation mutations muted mvhd mx-auto my-vsp-lg my-vsp-md n name named names native natural nav nav-active nav-card- nav/doc navigating navigation navigations near needed needs neither nested neutral never new newly-swapped next nicht no no-color-scheme no-data-theme-selector no-enlarge no-op no-repeat no-underline noch node node:buffer node:fs node:fs/promises node:module node:path node:url node:util nodes nofollow noindex non-draggable non-empty non-index non-light-dark non-literal non-null non-persisted none noopener noreferrer normal noscript not notable note note-tray notes now null number numeric object object-contain observe observer occurred of off offered offsets ofl-required og:description og:image og:image:alt og:image:height og:image:width og:title og:type og:url oklch ol old older omit omitting on once one only onto opacity-60 open open/close option or order original other others otherwise out outgoing outline-none over overflow overflow-auto overflow-hidden overflow-x-auto overflow-y-auto overflow-y:auto overlaps override overrides overscroll-contain overwrite own owned p p-0 p-hsp-2xs p-hsp-lg p-hsp-md p-hsp-sm p-hsp-xl pack pack-scoped package package-default package-injected package-owned packages packs padding page page-loading page-loading-overlay page-loading-spinner page-navigate-end page-title page-wide pages pages/. paint paint-and-read palette pan panel panels paren-balance-aware parent parse parsed parser parses part pass passed passes patch path paths pattern payload payload-budget pb-[50vh] pb-vsp-2xs pb-vsp-lg pb-vsp-md pb-vsp-xl pb-vsp-xs pdf peer peer-focus-visible:border-accent peer-focus-visible:text-accent peer-hover:border-accent peer-hover:text-accent pending per per-block per-link per-package per-release permanently persisted persistence php pi pick picked picks picocolors pins pipelines pl-[1.25rem] pl-hsp-lg pl-hsp-md pl-hsp-sm pl-hsp-xl place place-items-center placeholder placeholder:text-muted plain plural plus png png16 png32 pnpm point pointer pointer-events-none pointercancel pointerdown pointermove pointerup policy polite polygon polyline popover populates port position position:fixed pr-hsp-lg pr-hsp-md pr-hsp-sm pr-hsp-xl pr-hsp-xs pre pre-lowercased preact preact/compat preact/hooks preact/jsx-runtime preconnect preference prefix preload pres present preserving preview preview-swatch-color previews2026 previously primary print prior private produce produced produces producing production profiles project project-owned project-root-relative properties property props prose provided proxy pt-[0.15rem] pt-[2px] pt-vsp-3xs pt-vsp-md pt-vsp-sm pt-vsp-xl pt-vsp-xs ptag- public purely puts px px-hsp-2xl px-hsp-2xs px-hsp-lg px-hsp-md px-hsp-sm px-hsp-xl px-hsp-xs py py-0 py-[2px] py-[4px] py-[calc(var(--spacing-vsp-xs)+0.15rem)] py-hsp-2xs py-hsp-3xs py-hsp-sm py-hsp-xs py-vsp-2xs py-vsp-3xs py-vsp-lg py-vsp-md py-vsp-sm py-vsp-xl py-vsp-xs python q qt query question quick r radius radius-full radius-lg rail ramp range rar rather raw rb re-encode/decode re-exports re-initialized re-querying re-render re-renders re-run re-running re-runs re-selects re-syncs reach reached reaches read reader reader-facing reading readings reads/rewrites real real-value received receives recorded recovers rect redefine redistribution ref- reference referenced references refetch refresh refreshes refusing regardless regenerate regenerates regex registry reinit reinits rejected rel relative release released releases reload relying rem remapped remembered remove remove/rename removed removing rename render rendered renderer renderers renders reorder repaint repair repeated repeating replace replaced replacement replaces repopulate report repository requested require required requires reserved resize resize-x resolve resolved resolves responded response restore restores restyle result result-click results results-area retry return returns rev-parse reveal revision revisions rewire rewrite rewrites right right- right-0 right-hsp-lg ring-2 ring-accent risking ro robots role roles root rotate-180 rotate-90 round round-trip rounded rounded-[0.75rem] rounded-bl-[0.25rem] rounded-bl-[1rem] rounded-bl-lg rounded-br-[0.25rem] rounded-br-[1rem] rounded-full rounded-lg rounded-md rounded-t-[1rem] rounds route routed router routes routes-src routes/sitemap.xml row row-span-2 row-start-1 row-start-2 rs ruby rule rules run running runs runtime rust s safe safely safer same same-locale samp sans sans-serif scale scanned scanning scheme scoped scoping score scored script script- script-eval script-evaluation script-injection scripts scroll scrollbar scrolled scrollend scrolling scss seam search search-index section section- see seed segment segments sehen select select-none selection-bg selection-fg selector self self-contained self-hosted self-start self-stretch semantic semibold semver sentinel separator serialised serialize server server-rendered session set sets setting settles setup sh shadow shadow-[0_1px_3px_color-mix(in_srgb,var(--color-fg)_8%,transparent)] shadow-lg shadow-md shadowed shape share shared sharing shell ship shipped ships short shortcut should show shown shrink-0 sidebar sidebar- sidebar-toggle-island sidebar-tree-island sidebar-w sidecar signal silently similarity simple since single single-line single-object-literal singular site site-search site-tree-nav-island sitemap- sites size size-icon-lg skill skills skipped skipping skips slash slot slug slug-dir-parity slugs sm:block sm:border sm:border-muted sm:col-start-2 sm:flex sm:flex-row sm:gap-x-hsp-xl sm:grid sm:grid-cols-2 sm:grid-cols-[minmax(0,1fr)_auto] sm:grid-cols-subgrid sm:h-auto sm:hidden sm:items-center sm:justify-between sm:max-h-[80vh] sm:max-w-[52rem] sm:mr-0 sm:mx-auto sm:my-[10vh] sm:rounded-lg sm:row-span-2 sm:row-start-1 small smol-toml smooth snapping snapshot snapshots so soft soft-nav solid some somehow source sources space-y-vsp-2xs space-y-vsp-lg space-y-vsp-sm spacing spacing-0 spacing-px span spans spec specifiers specify spelling splitter spread spurious sql square sr-only src stable stack stale standalone start state state- state:state- statement status stay staying sticky still stock stop stops stored straddles stray strict string strings strip stripe strips stroke-linecap stroke-linejoin stroke-width strong stronger stub-rendered style style-attribute styled styles stylesheet sub subagents subsequent substitute substitution subtracting success successful summary sup supply supported surface surfaces survives svg swap swapped swaps swift switcher switching symlink synchronous synchronously syntactically syntax t tab tab-item tab-panel tabindex table tablist tabpanel tabs tabs-container tabs-content tabs-nav tabular-nums tag tag- tag-item- tagged tags tags:audit take tar tbody td temp-element template temporary temporary-element terminal terms test-results tested text text-accent text-bg text-body text-caption text-center text-chat-assistant-text text-chat-user-text text-code-fg text-danger text-decoration text-display text-fg text-fg/60 text-heading text-info text-left text-micro text-muted text-muted/50 text-right text-scale-2xl text-scale-2xs text-scale-lg text-scale-md text-scale-sm text-scale-xl text-scale-xs text-small text-title text-warning text/css text/csv text/html text/javascript text/jsx text/markdown text/mdx text/plain text/tab-separated-values text/tsx text/typescript text/x-c text/x-csharp text/x-go text/x-java-source text/x-kotlin text/x-python text/x-ruby text/x-rust text/x-scss text/x-shellscript text/x-swift textarea tfoot tgz th than that the thead their them theme theme-color theme-pack theme-pack-changed theme-packs theme-packs/index.json theme-toggle theme/token then there these they this those though three threw through throw throws tighten time timeline tip title tkhd to toast toc toggle toggle- toggle-ai-chat toggle-design-token-panel toggles toggling token tokens tolerates toml too toolbar tooltip top top-0 top-[3.5rem] top-full top-hsp-2xs top-level total touches tr tracked tracking-wide tracking-wider trade-off trailing transferred transition transition-[background,color,border-color] transition-[left,color] transition-[right,color] transition-colors transition-transform translate-x-0 translated translations transparent tray treats tree tree-child- tree-item- tree-top- trigger trigger:ai-chat trigger:design-token-panel triggers true truncate truncated try ts tsv tsx turn twitter:card twitter:creator twitter:description twitter:image twitter:site twitter:title two txt type typeface typeof typescript typography u ul umschalten unable unavailable unbalanced unchanged und undefined under underline underlines understand unit-tested unknown unlike unlisted unmaintained unobserve unreadable unrelated unreleased unresolvable unresolved unset unsupported unterminated until unusable unwrapped up up-to-date update updated uppercase use used useful user uses using usual utf-8 utf8 utilities utility v v2 val value value-reader values var variable variant verbatim version version- version-menu version-switcher versions vertical via video video/mp4 video/quicktime video/webm viewer viewing viewport viewports virtual:zudo-doc-asset-bodies virtual:zudo-doc-chrome-bindings virtual:zudo-doc-design-token-panel-config virtual:zudo-doc-route-context visibility visible vocabulary void von vsp vsp-2xl vsp-2xs vsp-3xs vsp-lg vsp-md vsp-sm vsp-xl vsp-xs w w-1/2 w-[0.5rem] w-[0.625rem] w-[0.875rem] w-[1.125rem] w-[1.575rem] w-[1.5rem] w-[1.75rem] w-[12rem] w-[14px] w-[16px] w-[16rem] w-[18px] w-[1em] w-[2.5rem] w-[280px] w-[2rem] w-[320px] w-[360px] w-[6.5rem] w-[90vw] w-[calc(100vw-2rem)] w-[var(--zd-sidebar-w)] w-dvw w-full w-icon-lg w-icon-md w-icon-sm w-icon-xs walk walks want warn warning was watching way wbr wbr- we webm webp website weight went were what when where whereas whether which while whitespace-nowrap whitespace-pre whole whose wide wide-gamut wider-than-scrollbar width will window wins wird wired with without word wordmark working works worktrees would wrap wrapped wrapper wrappers wrapping wraps writing written wrong wrote wurde x xl:flex xl:hidden xml y-scrollbar yaml year yet yielded yields yml you your z-dropdown z-local-1 z-modal z-modal-backdrop z-popover z-sidebar z-toolbar zd-asset-code zd-asset-details-rail zd-asset-details-toggle zd-asset-filebar zd-asset-media-grid zd-asset-media-rail zd-asset-page zd-asset-pdf zd-asset-stage zd-content zd-desktop-sidebar-toggle zd-desktop-toc-toggle zd-doc-content-band zd-enlarge-btn zd-enlarge-dialog zd-enlarge-dialog-close zd-enlargeable zd-html-preview-code zd-mermaid-dialog zd-mermaid-enlargeable zd-mermaid-tool-btn zd-mermaid-toolbar zd-mermaid-transform zd-mermaid-viewport zd-sidebar-content-wrapper zd-sidebar-open zd-theme-pack-dialog-title zd-toc-col zdtp zfb zfb:after-swap zfb:before-preparation zfb:before-swap zip zod zoom zudo-design-token-panel zudo-design-tokens/v3 zudo-doc zudo-doc-asset-details-visible zudo-doc-code-wrap zudo-doc-design-token-panel-modal zudo-doc-design-tokens zudo-doc-sidebar-visible zudo-doc-sidebar-width zudo-doc-theme zudo-doc-theme-pack zudo-doc-toc-visible zudo-doc-tweak zum");
2
+ @source inline("-domtweaker-enabled -elpath-enabled -left-[calc(var(--spacing-icon-lg)/2)] -link -mb-px -ml-hsp-sm -mt-px -noscript -open -state -translate-x-full 2xl:w-[24px] [&::-webkit-details-marker]:hidden [&_a]:pointer-events-auto [&_a]:text-accent [&_a]:underline [&_li]:mb-0 [&_nav]:mb-0 [asset-viewer] [data-admonition] [data-kbd-shortcut] [data-switcher-launcher] [doc-history-meta] [doc-history] [doc-layout] [llms-txt] [zudo-doc] a a2 abbr about above absent absolute accent accent- accent:accent- access across activated active actual actually added admonition admonition- admonition-body admonition-title admonition/callout after after-breadcrumb after-content after-navigate after-sidebar after-title against agent agents ai-chat ai-chat-md ai-chat-trigger alert align-top all allow allow-same-origin allow-scripts allowed alone already already-executed already-multiline already-picked also always an anchor and and/or animate-pulse animate-spin announce ansehen antialiased any anywhere anzeigen app appear application/json application/octet-stream application/pdf application/sql application/toml application/x-httpd-php application/xml application/yaml applied applies apply applying approach approval are area arg argument aria-atomic aria-busy aria-controls aria-current aria-disabled aria-expanded aria-haspopup aria-hidden aria-label aria-labelledby aria-live aria-orientation aria-pressed aria-selected aria-valuemax aria-valuemin aria-valuenow arm arms around arrows article as asc ascii aside aspect-[1200/630] aspect-square asset asset- asset-components assets assets/client assistant async at at-rule attach attribute attributes auf authored auto auto-logo-mask autogenerated availability available avc1 avif avis avoid await away b back backdrop:bg-bg/30 backdrop:bg-bg/80 backdrop:bg-overlay/60 backdrop:z-modal-backdrop background background-color backtick backticks baked band banner bare base base- base64 base:base- based bash batch be bearbeiten because becomes been before below best best-effort between bg bg-[#fff] bg-accent bg-bg bg-chat-assistant-bg bg-chat-user-bg bg-code-bg bg-fg bg-info/10 bg-info/5 bg-muted bg-overlay/30 bg-surface bg-surface/50 bg-transparent bg-warning/10 bg-warning/5 bi big bigint bin binaries bind binding blank blanks block blockquote blocks blur bodies body body-end-components body-end-scripts bold boolean bootstrap border border-accent border-b border-b-2 border-b-[5px] border-bg/30 border-collapse border-danger border-dashed border-fg border-image border-info/30 border-l border-l-0 border-l-[3px] border-left-width border-muted border-none border-r border-r-0 border-radius border-solid border-t border-t-[2px] border-t-[3px] border-transparent border-warning/30 border-width border-y both bottom-hsp-lg bottom-vsp-xl boundaries box box-border br brackets brand breadcrumb:end breadcrumb:start break-words brief brown browser browser-tab browsers browses btn budget bug build builder built built-in bundler but button buttons by bypassed byte-identical bytes c cache cached calendar-valid call callable called caller calls can cancellation candidate cannot canonical canvas caption captured captures card card-grid cards carry case-insensitive cases cat-nav- catalog catch categories category caught caution center center/contain ch chains change changed changelog changelogs changes characters check checker child children choose chrome chrome-font ci circle cite cjs class class-less class-mode claude claude-agents claude-commands claude-md claude-resources claude-skills cleaned cleanly clear clearing click client client-router client-side clip clobber clobbering close closed closes closing closure code code-block-sr-announce code-group code-group-panel codex codex-agents codex-agents-md codex-config codex-hooks codex-resources codex-rules codex-skills col col-resize col-span-full col-start-1 colgroup collapse collapses collapsible collision color color-scheme color-scheme-changed color-scheme-provider color-tweak colorization colors column comma command commands commas comment commercial commercial-font-denylist commit compare complete component component:github-link component:language-switcher component:search component:theme-toggle component:version-switcher composes composition compute computed concrete conf config configuration configurations configure configured conflicting conflicts confuse connect const construction consumer consumes contain container containers containing contains content content-admonition content-layer content-link content-type content-wrapper:end content-wrapper:start contents context contract control controller controls converts cookie-blocking copied copy copy-url core corners correct correctly corrupt could count covered covers cpp crashes created cross-component crumb- cs csharp css css-presence csv ctx cur current current-path/index.ts current-route currently cursor cursor-not-allowed cursor-pointer custom cycle d danger dark dash data data-active data-admonition data-asset-details-hidden data-auto-logo data-base data-close-search data-current-locale data-default-locale data-doc-date data-doc-description data-doc-metainfo data-doc-pager data-doc-unavailable-versions data-find-active data-find-match data-footer data-group-id data-header data-header-logo data-header-nav data-header-right data-kbd-shortcut data-lang data-language-menu data-language-switcher data-language-toggle data-loading-index data-mermaid-enlarge-ready data-mermaid-rendered data-mermaid-src data-nav-active data-nav-category data-nav-item data-nav-item-dropdown data-nav-more data-nav-more-menu data-nav-more-toggle data-no-results data-note-tray-group data-note-tray-row data-open-search data-pan-active data-processed data-props data-result-count-template data-search-count data-search-count-narrow data-search-dialog data-search-input data-search-placeholder data-search-results data-search-unavailable data-sidebar-hidden data-sidebar-resizer data-site-nav data-switcher-card data-switcher-launcher data-tab-btn data-tab-default data-tab-label data-tab-value data-tabs data-taglist-group data-testid data-theme data-theme-pack data-theme-pack-switcher data-theme/style data-toc-hidden data-trailing-slash data-unavailable-label data-variant data-version-banner data-version-latest data-version-menu data-version-rewire data-version-slug data-version-switcher data-version-toggle data-version-trigger-label data-zd-asset-action data-zd-asset-actions data-zd-asset-details data-zd-asset-details-chevron data-zd-asset-details-list data-zd-asset-details-toggle data-zd-asset-index-action data-zd-asset-index-empty data-zd-asset-index-page data-zd-asset-page data-zd-asset-tree data-zd-copy-url data-zd-html-preview-reservation data-zd-label-collapse data-zd-label-expand data-zd-mobile-sidebar data-zd-mobile-toc data-zd-nav-section data-zd-nosidebar data-zd-pending data-zd-props-preserve data-zd-sidebar-open-key data-zd-theme-pack-css data-zd-theme-pack-css-loading data-zd-theme-pack-loading data-zd-toc data-zd-wide data-zfb-island data-zfb-island-remount data-zfb-reload data-zfb-transition-persist date dated dd decimal decision declaration declare declared declares decoration decoration-muted deepest deepest-match default default-transition-duration defaults deferral deferred del delegated delete deliberately delimiter dependency depends depth der desc description design design-token design-token-panel design-token-trigger desktop desktop-sidebar desktop-sidebar-toggle desktop-sidebar-toggle-island desktop-toc-toggle destroys destructive detach detached details determine deterministic dev dfn diagram diagrams dialog did die dieser diff diff-line-added diff-line-content diff-line-empty diff-line-num diff-line-removed diff-row differ different dir directly directories directory disabled disabled:cursor-default disabled:opacity-50 disabled:pointer-events-none disc display display:none dist distance distinct div dl do doc doc-card- doc-content-band doc-history doc-history-generate doc-history-panel doc-history-trigger doc-page doc-pager doc-prose doc-title docblock docs docs- docs-v- document document-level documentation documented documents does dog dot double-registration download draft drag drawer drift drifts drop dropdown dropdown-parent dropdowns dt duplicate duration-150 duration-200 during dynamically e e2e each eager earlier early ease-in-out edge editing einer either eject ejectable ejectables ejected el element elements els else em embedded emit emitting empty empty/undefined en enable enabled end enhanced enhancement enlarged entire entities entries entry entrypoint env equal error escape escaped escapes even event eventually every everything-enabled exactly example exceeds excerpt excludes exclusively existing exists exit expand expected explicit export extends extra f factories failed fall fallback fallbacks falling falls false family fast favicon feature fg field fields fieldset figcaption figure file files fill fills finally find find-match find-match-active fire fires first first-paint first:mt-0 fit fix fixed fixed-width fixtures flag flash flat flex flex-1 flex-col flex-wrap flip flipping flips flow flush-left focus focus-visible:bg-accent/10 focus-visible:border-accent focus-visible:decoration-accent focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2 focus-visible:text-accent focus-visible:underline focus-within:border-accent focus-within:z-local-1 focus:border-accent focus:outline-none focus:text-accent focus:underline folder folders follows font font-bold font-face-parity font-family font-file-missing font-medium font-mono font-sans font-scale font-semibold font-size font-weight font-weight-bold font-weight-medium font-weight-normal font-weight-semibold font/woff2 fonts footer footer- for form format former found four-link fox fragment frame free freeze fresh from frontmatter frontmatter-preview frozen frozen-script fs-extra ftyp full fully function further g gains gap-[0.3em] gap-[clamp(1.5rem,3vw,4rem)] gap-hsp-2xs gap-hsp-lg gap-hsp-md gap-hsp-sm gap-hsp-xl gap-hsp-xs gap-vsp-2xs gap-vsp-3xs gap-vsp-lg gap-vsp-md gap-vsp-xs gap-x-hsp-2xs gap-x-hsp-lg gap-x-hsp-md gap-x-hsp-sm gap-x-hsp-xs gap-y-vsp-2xs gap-y-vsp-3xs gap-y-vsp-lg gap-y-vsp-md gap-y-vsp-xs gaps gate geladen2026 generate generated generation genuine geometry get getting-started gif git github github-dark github-link give go got grab gradient granular graph grid grid-cols-1 grid-cols-2 grid-cols-[auto_1fr] grid-rows-[auto_auto] grid-rows-subgrid group group-focus-visible:decoration-accent group-focus-visible:text-accent group-focus-visible:text-accent-hover group-focus-visible:text-fg group-focus-visible:underline group-focus-within:block group-hover:bg-fg group-hover:block group-hover:decoration-accent group-hover:text-accent group-hover:text-accent-hover group-hover:text-bg group-hover:text-fg group-hover:underline group-open:rotate-90 grouped grouping guard guards gz h h-[0.5rem] h-[0.625rem] h-[0.875rem] h-[1.125rem] h-[1.25rem] h-[1.575rem] h-[10rem] h-[14px] h-[1em] h-[1lh] h-[2.5rem] h-[2rem] h-[3.5rem] h-[3rem] h-[70vh] h-[90vh] h-[calc(100%-3rem)] h-[calc(100vh-3.5rem)] h-dvh h-full h-icon-lg h-icon-md h-icon-sm h-icon-xs h1 h1s h2 h22013h4 h2s h3 h4 h5 h6 half hand-copied hand-editable handle handled handler handlers happens hard-loaded hardcoded has hash-link have head head-links head-scripts header header- header-call:end header-call:start header-right heading heading-h2 heading-h3 heading-h4 heading-rule headings height here hex hi-root hidden hide hierarchical highlight highlighting history home hook hooks hooks-json horizontal host hover:bg-[color-mix(in_srgb,var(--color-surface)_80%,var(--color-fg)_20%)] hover:bg-accent-hover hover:bg-accent/10 hover:bg-danger/10 hover:bg-surface hover:border-accent hover:border-accent-hover hover:border-fg hover:decoration-accent hover:text-accent hover:text-accent-hover hover:text-fg hover:underline hover:z-local-1 hpp hr href hrefs hsp hsp-2xl hsp-2xs hsp-lg hsp-md hsp-sm hsp-xl hsp-xs html i i18n/theme. i2 i3 i4 ico icon icon-lg icon-md icon-sm icon-xs identical idle idx if iframe ignoring image image-enlarge image-overlay-inset image/avif image/gif image/jpeg image/png image/webp image/x-icon img implementation import important important-allowlist imports in inactive includes including incomplete independently index index2026 indirectly info inherit inherited ini initial initialised injected inline inline-block inline-flex inner input input-clear ins inserted-after-color-mode inserted-after-color-scheme inserted-after-site-name inserted-first insertion inset-0 inside inside-only inspect install installation installed instance instanceof instead instructions intended intent intentionally intercept interface internal interpolation into invalid invalidated inverse inversion invocation invoke is is-checker island island-root iso2 iso3 iso4 iso5 iso6 isom ispe issues it italic item item- items items-baseline items-center items-end items-start iteration its itself ja java javascript jpeg jpg js json jsx jumps just justification justify-between justify-center justify-end justify-start katex kbd keep keeping keeps kept key keyboard keyboard-shortcut keydown keys keystroke keyword keywords khroma known-token-names kopieren kotlin kt label landing lands language-menu language-switcher language-toggle larger last:border-b-0 last:pb-0 later latest launch layout lazy leading leading-none leading-normal leading-relaxed leading-snug leading-tight leaf leaf- leak leaves leaving left left-0 left:calc legend legitimate length lets letter-spacing lg lg:block lg:border lg:border-fg lg:border-solid lg:flex lg:flex-col lg:flex-row lg:gap-hsp-xl lg:grid-cols-3 lg:grid-cols-[repeat(auto-fit,minmax(12rem,1fr))] lg:h-[90vh] lg:hidden lg:justify-start lg:m-auto lg:max-h-[90vh] lg:max-w-[52.5rem] lg:ml-[var(--zd-sidebar-w)] lg:pr-hsp-sm lg:pt-vsp-2xl lg:px-hsp-2xl lg:py-vsp-2xl lg:text-left lg:w-[90vw] lg:w-[clamp(16rem,25%,22rem)] li li2 library license lifecycle light light/dark like likely line line-height line/statement lines linger link link- links list list-disc list-none listener lists literal literally literals live lives llms llms-txt load loaded loader loading local local-1 local-2 local-3 locale locales log logo long longer longest-match look loses lostpointercapture lower luminance m m-0 m-auto m10 m14 m16 m21 m6 machinery main major make malformed malformed-markup malicious managed manifest manual manually maps mark markdown marks match matches matching math math-display math-inline max max-h-[85vh] max-h-[90vh] max-h-full max-h-none max-w-[16rem] max-w-[64rem] max-w-[85%] max-w-[85vw] max-w-[90vw] max-w-[calc(100vw-2rem)] max-w-[calc(100vw-var(--spacing-hsp-xl))] max-w-[clamp(50rem,75vw,90rem)] max-w-full max-w-none max-w-sm max-width maximum may mb-0 mb-vsp-2xs mb-vsp-lg mb-vsp-md mb-vsp-sm mb-vsp-xl mb-vsp-xs md mdx means measured measurement measures measuring mechanism menu mermaid message messages meta meta-knob meta-schema metadata migration min-h-0 min-h-[20rem] min-h-[44px] min-h-[60vh] min-h-[calc(100vh-3.5rem)] min-h-screen min-w-0 min-w-[10rem] min-w-[3rem] min-w-[44px] min-w-[8rem] minifier minor mirror mirroring mirrors missing mit mjs ml-[calc(var(--spacing-hsp-xl)+1px)] ml-auto ml-hsp-2xl ml-hsp-lg ml-hsp-md ml-hsp-sm ml-hsp-xl mobile mod modal modal-backdrop mode model modify module moment monospace month more most mount mounted mouseenter mouseleave mov move mp4 mp41 mp42 mr-[calc(var(--spacing-hsp-xl)+1px)] mr-hsp-sm ms mt-0 mt-vsp-2xl mt-vsp-2xs mt-vsp-3xs mt-vsp-lg mt-vsp-md mt-vsp-sm mt-vsp-xl mt-vsp-xs multi-changelog multiple must mutates mutation mutations muted mvhd mx-auto my-vsp-lg my-vsp-md n name named names native natural nav nav-active nav-card- nav/doc navigating navigation navigations near needed needs neither nested neutral never new newly-swapped next nicht no no-color-scheme no-data-theme-selector no-enlarge no-op no-repeat no-underline noch node node:buffer node:fs node:fs/promises node:module node:path node:url node:util nodes nofollow noindex non-draggable non-empty non-index non-light-dark non-literal non-null non-persisted none noopener noreferrer normal noscript not notable note note-tray notes now null number numeric object object-contain observe observer occurred of off offered offsets ofl-required og:description og:image og:image:alt og:image:height og:image:width og:title og:type og:url oklch ol old older omit omitting on once one only onto opacity-60 open open/close option or order original other others otherwise out outgoing outline-none over overflow overflow-auto overflow-hidden overflow-x-auto overflow-y-auto overflow-y:auto overlaps override overrides overscroll-contain overwrite own owned p p-0 p-hsp-2xs p-hsp-lg p-hsp-md p-hsp-sm p-hsp-xl pack pack-scoped package package-default package-injected package-owned packages packs padding page page-loading page-loading-overlay page-loading-spinner page-navigate-end page-title page-wide pages pages/. paint paint-and-read palette pan panel panels paren-balance-aware parent parse parsed parser parses part pass passed passes patch path paths pattern payload payload-budget pb-[50vh] pb-vsp-2xs pb-vsp-lg pb-vsp-md pb-vsp-xl pb-vsp-xs pdf peer peer-focus-visible:border-accent peer-focus-visible:text-accent peer-hover:border-accent peer-hover:text-accent pending per per-block per-link per-package per-release permanently persisted persistence php pi pick picked picks picocolors pins pipelines pl-[1.25rem] pl-hsp-lg pl-hsp-md pl-hsp-sm pl-hsp-xl place place-items-center placeholder placeholder:text-muted plain plural plus png png16 png32 pnpm point pointer pointer-events-none pointercancel pointerdown pointermove pointerup policy polite polygon polyline popover populates port position position:fixed pr-hsp-lg pr-hsp-md pr-hsp-sm pr-hsp-xl pr-hsp-xs pre pre-lowercased preact preact/compat preact/hooks preact/jsx-runtime preconnect preference prefix preload pres present preserving preview preview-swatch-color previews2026 previously primary print prior private produce produced produces producing production profiles project project-owned project-root-relative properties property props prose provided proxy pt-[0.15rem] pt-[2px] pt-vsp-3xs pt-vsp-md pt-vsp-sm pt-vsp-xl pt-vsp-xs ptag- public purely puts px px-hsp-2xl px-hsp-2xs px-hsp-lg px-hsp-md px-hsp-sm px-hsp-xl px-hsp-xs py py-0 py-[2px] py-[4px] py-[calc(var(--spacing-vsp-xs)+0.15rem)] py-hsp-2xs py-hsp-3xs py-hsp-sm py-hsp-xs py-vsp-2xs py-vsp-3xs py-vsp-lg py-vsp-md py-vsp-sm py-vsp-xl py-vsp-xs python q qt query question quick r radius radius-full radius-lg rail ramp range rar rather raw rb re-encode/decode re-exports re-init re-initialized re-querying re-render re-renders re-run re-running re-runs re-selects re-syncs reach reached reaches read reader reader-facing reading readings reads/rewrites real real-value received receives recorded recovers rect redefine redistribution ref- reference referenced references refetch refresh refreshes refusing regardless regenerate regenerates regex registry reinit reinits rejected rel relative release released releases reload relying rem remapped remembered remove remove/rename removed removing rename render rendered renderer renderers renders reorder repaint repair repeated repeating replace replaced replacement replaces repopulate report repository republished requested require required requires reserved resize resize-x resolve resolved resolves responded response restore restores restyle result result-click results results-area retry return returns rev-parse reveal revision revisions rewire rewrite rewrites right right- right-0 right-hsp-lg ring-2 ring-accent risking ro robots role roles root rotate-180 rotate-90 round round-trip rounded rounded-[0.75rem] rounded-bl-[0.25rem] rounded-bl-[1rem] rounded-bl-lg rounded-br-[0.25rem] rounded-br-[1rem] rounded-full rounded-lg rounded-md rounded-t-[1rem] rounds route routed router routes routes-src routes/sitemap.xml row row-span-2 row-start-1 row-start-2 rs ruby rule rules run running runs runtime rust s safe safely safer same same-locale samp sans sans-serif scale scanned scanning scheme scoped scoping score scored script script- script-eval script-evaluation script-injection scripts scroll scrollbar scrolled scrollend scrolling scss seam search search-index section section- see seed segment segments sehen select select-none selection-bg selection-fg selector self self-contained self-hosted self-start self-stretch semantic semibold semver sentinel separator serialised serialize server server-rendered session set sets setting settles setup sh shadow shadow-[0_1px_3px_color-mix(in_srgb,var(--color-fg)_8%,transparent)] shadow-lg shadow-md shadowed shape share shared sharing shell ship shipped ships short shortcut should show shown shrink-0 sidebar sidebar- sidebar-toggle-island sidebar-tree-island sidebar-w sidecar signal silently similarity simple since single single-line single-object-literal singular site site-search site-tree-nav-island sitemap- sites size size-icon-lg skill skills skipped skipping skips slash slot slug slug-dir-parity slugs sm:block sm:border sm:border-muted sm:col-start-2 sm:flex sm:flex-row sm:gap-x-hsp-xl sm:grid sm:grid-cols-2 sm:grid-cols-[minmax(0,1fr)_auto] sm:grid-cols-subgrid sm:h-auto sm:hidden sm:items-center sm:justify-between sm:max-h-[80vh] sm:max-w-[52rem] sm:mr-0 sm:mx-auto sm:my-[10vh] sm:rounded-lg sm:row-span-2 sm:row-start-1 small smol-toml smooth snapping snapshot snapshots so soft soft-nav solid some somehow sort source sources space-y-vsp-2xs space-y-vsp-lg space-y-vsp-sm spacing spacing-0 spacing-px span spans spec specifiers specify spelling splitter spread spurious sql square sr-only src stable stack stale standalone start state state- state:state- statement status stay staying sticky still stock stop stops stored straddles stray strict string strings strip stripe strips stroke-linecap stroke-linejoin stroke-width strong stronger stub-rendered style style-attribute styled styles stylesheet sub subagents subsequent substitute substitution subtracting success successful summary sup supply supported surface surfaces survives svg swap swapped swaps swift switcher switching symlink synchronous synchronously syntactically syntax t tab tab-item tab-panel tabindex table tablist tabpanel tabs tabs-container tabs-content tabs-nav tabular-nums tag tag- tag-item- tagged tags tags:audit take tar tbody td temp-element template temporary temporary-element terminal terms test-results tested text text-accent text-bg text-body text-caption text-center text-chat-assistant-text text-chat-user-text text-code-fg text-danger text-decoration text-display text-fg text-fg/60 text-heading text-info text-left text-micro text-muted text-muted/50 text-right text-scale-2xl text-scale-2xs text-scale-lg text-scale-md text-scale-sm text-scale-xl text-scale-xs text-small text-title text-warning text/css text/csv text/html text/javascript text/jsx text/markdown text/mdx text/plain text/tab-separated-values text/tsx text/typescript text/x-c text/x-csharp text/x-go text/x-java-source text/x-kotlin text/x-python text/x-ruby text/x-rust text/x-scss text/x-shellscript text/x-swift textarea tfoot tgz th than that the thead their them theme theme-color theme-pack theme-pack-changed theme-packs theme-packs/index.json theme-toggle theme/token then there these they this those though three threw through throw throws tighten time timeline tip title tkhd to toast toc toggle toggle- toggle-ai-chat toggle-design-token-panel toggles toggling token tokens tolerates toml too toolbar tooltip top top-0 top-[3.5rem] top-full top-hsp-2xs top-level total touches tr tracked tracking-wide tracking-wider trade-off trailing transferred transition transition-[background,color,border-color] transition-[left,color] transition-[right,color] transition-colors transition-transform translate-x-0 translated translations transparent tray treats tree tree-child- tree-item- tree-top- trigger trigger:ai-chat trigger:design-token-panel triggers true truncate truncated try ts tsv tsx turn twitter:card twitter:creator twitter:description twitter:image twitter:site twitter:title two txt type typeface typeof typescript typography u ul umschalten unable unavailable unbalanced unchanged und undefined under underline underlines understand unit-tested unknown unlike unlisted unmaintained unmatchable unobserve unreadable unrelated unreleased unresolvable unresolved unset unsupported unterminated until unusable unwrapped up up-to-date update updated uppercase use used useful user uses using usual utf-8 utf8 utilities utility v v2 val value value-reader values var variable variant verbatim version version- version-menu version-switcher versions vertical via video video/mp4 video/quicktime video/webm viewer viewing viewport viewports virtual:zudo-doc-asset-bodies virtual:zudo-doc-chrome-bindings virtual:zudo-doc-design-token-panel-config virtual:zudo-doc-route-context visibility visible vocabulary void von vsp vsp-2xl vsp-2xs vsp-3xs vsp-lg vsp-md vsp-sm vsp-xl vsp-xs w w-1/2 w-[0.5rem] w-[0.625rem] w-[0.875rem] w-[1.125rem] w-[1.575rem] w-[1.5rem] w-[1.75rem] w-[12rem] w-[14px] w-[16px] w-[16rem] w-[18px] w-[1em] w-[2.5rem] w-[280px] w-[2rem] w-[320px] w-[360px] w-[6.5rem] w-[90vw] w-[calc(100vw-2rem)] w-[var(--zd-sidebar-w)] w-dvw w-full w-icon-lg w-icon-md w-icon-sm w-icon-xs walk walks want warn warning was watching way wbr wbr- we webm webp website weight went were what when where whereas whether which while whitespace-nowrap whitespace-pre whole whose wide wide-gamut wider-than-scrollbar width will window wins wird wired with without word wordmark working works worktrees would wrap wrapped wrapper wrappers wrapping wraps writing written wrong wrote wurde x xl:flex xl:hidden xml y-scrollbar yaml year yet yielded yields yml you your z-dropdown z-local-1 z-modal z-modal-backdrop z-popover z-sidebar z-toolbar zd-asset-code zd-asset-details-rail zd-asset-details-toggle zd-asset-filebar zd-asset-media-grid zd-asset-media-rail zd-asset-page zd-asset-pdf zd-asset-stage zd-content zd-desktop-sidebar-toggle zd-desktop-toc-toggle zd-doc-content-band zd-enlarge-btn zd-enlarge-dialog zd-enlarge-dialog-close zd-enlargeable zd-html-preview-code zd-mermaid-dialog zd-mermaid-enlargeable zd-mermaid-tool-btn zd-mermaid-toolbar zd-mermaid-transform zd-mermaid-viewport zd-sidebar-content-wrapper zd-sidebar-open zd-theme-pack-dialog-title zd-toc-col zdtp zfb zfb:after-swap zfb:before-preparation zfb:before-swap zip zod zoom zudo-design-token-panel zudo-design-tokens/v3 zudo-doc zudo-doc-asset-details-visible zudo-doc-code-wrap zudo-doc-design-token-panel-modal zudo-doc-design-tokens zudo-doc-sidebar-visible zudo-doc-sidebar-width zudo-doc-theme zudo-doc-theme-pack zudo-doc-toc-visible zudo-doc-tweak zum");
@@ -36,6 +36,7 @@ function makeUrlHelpers(settings, i18n) {
36
36
  return isExternal(href) ? href : withBase(href);
37
37
  }
38
38
  function navHref(path, lang, currentVersion, versioned = true) {
39
+ if (isExternal(path)) return path;
39
40
  const isNonDefaultLocale = lang != null && lang !== defaultLocale && !isDefaultLocaleOnlyPath(path);
40
41
  const versionPrefix = versioned && currentVersion ? `/v/${currentVersion}` : "";
41
42
  return withBase(
@@ -399,6 +399,72 @@ export function buildNavOverflowScript(context = resolveGenerationContext()) {
399
399
  extractAfterNavigateEvent(context, transformSync),
400
400
  );
401
401
 
402
+ // ---------------------------------------------------------------------
403
+ // NOTE ON COMMENTS IN THE TEMPLATE BELOW: every byte inside the returned
404
+ // literal ships inline in the <head> of EVERY page, so rationale lives out
405
+ // here (generator source, not shipped) and the template keeps only short
406
+ // pointers back to these paragraphs.
407
+ //
408
+ // navPathname / cross-origin (zudolab/zudo-doc#3950)
409
+ // -------------------------------------------------
410
+ // Keeping only `.pathname` collapses an external `headerNav` entry like
411
+ // "https://other.example/" to "/", which then exact-matches this site's own
412
+ // root route and steals the highlight from whatever SSR marked active. The
413
+ // SSR matcher (nav-active.ts) compares the raw configured `path` string, so
414
+ // "https://other.example/" never equals or prefixes "/". Returning "" for a
415
+ // cross-origin href restores that agreement instead of re-deriving it.
416
+ //
417
+ // The "" sentinel, and why it cannot win
418
+ // --------------------------------------
419
+ // "" is produced by navPathname for a cross-origin or unparseable href.
420
+ // pathMatchesNavPath LETS IT PASS for every absolute current path — with
421
+ // navPath "" the prefix test degenerates to `currentPath.startsWith("/")`.
422
+ // What makes it safe is computeActiveNavPath's length-descending sort: ""
423
+ // is the strict minimum, so it is only ever picked when nothing else
424
+ // matched, and all three paint sites then guard on `activePath !== ""` and
425
+ // paint nothing. Those guards are load-bearing — do not drop them as
426
+ // "redundant". A same-origin href can never yield "" (trimSlashes floors at
427
+ // "/"), so "" is exclusively the sentinel.
428
+ //
429
+ // Reusing SSR's active-state decision (zudolab/zudo-doc#3953)
430
+ // -----------------------------------------------------------
431
+ // SSR resolves the active item as `isNavItemActiveByCategory(item,
432
+ // activeCategory) || isNavItemActive(item, activeNavPath)` — category
433
+ // first, path as a per-item fallback, ORed, not either/or. This script used
434
+ // to know only the path half, so it repainted from the URL alone and
435
+ // DISCARDED SSR's category decision on every first paint: any page whose
436
+ // big category picks an item that URL-prefix matching does not lost its
437
+ // highlight on load (measured: a root route rendering a docs/<section>
438
+ // entry went from `aria-current="page"` to nothing).
439
+ //
440
+ // Rather than re-deriving the category client-side — a third matcher to
441
+ // keep in sync, which is the drift the embedded-verbatim core above exists
442
+ // to prevent — SSR republishes what it already resolved:
443
+ // * `data-zd-nav-section` on `.zd-doc-content-band` (doc-layout.tsx) —
444
+ // the page's `navSection`. That element is INSIDE the client router's
445
+ // swapped region, so the value is fresh after every swap. The header is
446
+ // persisted across swaps, so nothing written into it could be.
447
+ // * `data-nav-category` on each nav anchor (header.tsx) — the item's
448
+ // configured `categoryMatch`. Config-derived and identical on every
449
+ // page, so it is safe on the persisted header.
450
+ // `isAnchorActive` below then mirrors SSR's precedence exactly. When the
451
+ // page has no section (home, 404, tag, version), the attribute is absent
452
+ // and the predicate collapses to the previous path-only behaviour.
453
+ //
454
+ // PARSE ORDER — why the first paint is deferred
455
+ // ---------------------------------------------
456
+ // This script is emitted INSIDE <header> (header.tsx), which the HTML
457
+ // parser reaches BEFORE `.zd-doc-content-band` further down the body. At
458
+ // the top-level `initNavOverflow()` call the band therefore does not exist
459
+ // yet, `document.querySelector("[data-zd-nav-section]")` returns null, and
460
+ // a repaint at that moment would fall back to path-only matching and CLEAR
461
+ // exactly the SSR category highlight this section exists to preserve.
462
+ // AFTER_NAVIGATE_EVENT does not fire on initial load, so nothing would put
463
+ // it back. SSR's paint is already correct for the page being parsed, so
464
+ // `initNavOverflow` skips `applyActiveNav` while `document.readyState ===
465
+ // "loading"` and re-inits once on DOMContentLoaded, when the band is in the
466
+ // DOM. Do not "simplify" either half away.
467
+ // ---------------------------------------------------------------------
402
468
  return /* javascript */ `(function () {
403
469
  var cleanupNavOverflow = null;
404
470
 
@@ -407,9 +473,13 @@ export function buildNavOverflowScript(context = resolveGenerationContext()) {
407
473
  return p || "/";
408
474
  }
409
475
 
476
+ // "" for cross-origin: unmatchable sentinel, matching SSR (#3950).
410
477
  function navPathname(a) {
411
- try { return trimSlashes(new URL(a.href, location.href).pathname); }
412
- catch (e) { return ""; }
478
+ try {
479
+ var u = new URL(a.href, location.href);
480
+ if (u.origin !== location.origin) return "";
481
+ return trimSlashes(u.pathname);
482
+ } catch (e) { return ""; }
413
483
  }
414
484
 
415
485
  // Explicit current-route override, embedded from current-path/index.ts so
@@ -441,13 +511,17 @@ export function buildNavOverflowScript(context = resolveGenerationContext()) {
441
511
 
442
512
  var cur = trimSlashes(readCurrentPath(CURRENT_PATH_DATASET_KEY));
443
513
 
514
+ // SSR's own resolved big category, republished per page (#3953).
515
+ var sectionEl = document.querySelector("[data-zd-nav-section]");
516
+ var navSection = (sectionEl && sectionEl.getAttribute("data-zd-nav-section")) || "";
517
+
444
518
  // Build NavItemLike-shaped entries from the live DOM so the shared
445
519
  // computeActiveNavPath can do the deepest-match walk — the same call
446
520
  // shape the SSR header uses (matches computeActiveNavPath). A dropdown
447
- // missing its own top-level anchor is skipped entirely (path "" would
448
- // otherwise match every current path pathMatchesNavPath treats "" as
449
- // the root "/"), mirroring the parentLink guard used below for the same
450
- // malformed-markup case.
521
+ // missing its own top-level anchor is skipped entirely, mirroring the
522
+ // parentLink guard used below for the same malformed-markup case.
523
+ // A "" path (cross-origin) is unmatchable ONLY because of the length sort
524
+ // plus the \`activePath !== ""\` guards below — keep both.
451
525
  var navItems = [];
452
526
  topItems.forEach(function (it) {
453
527
  var isDropdown = it.hasAttribute("data-nav-item-dropdown");
@@ -464,6 +538,13 @@ export function buildNavOverflowScript(context = resolveGenerationContext()) {
464
538
 
465
539
  var activePath = computeActiveNavPath(navItems, cur) || "";
466
540
 
541
+ // Mirrors SSR: category match OR path match, per item (see nav-active.ts).
542
+ function isAnchorActive(a) {
543
+ if (!a) return false;
544
+ if (navSection !== "" && a.getAttribute("data-nav-category") === navSection) return true;
545
+ return activePath !== "" && navPathname(a) === activePath;
546
+ }
547
+
467
548
  function setTopActive(a, active) {
468
549
  if (!a) return;
469
550
  if (active) {
@@ -483,10 +564,10 @@ export function buildNavOverflowScript(context = resolveGenerationContext()) {
483
564
  var topActive = false;
484
565
 
485
566
  if (isDropdown) {
486
- var parentMatch = !!topA && navPathname(topA) === activePath && activePath !== "";
567
+ var parentMatch = isAnchorActive(topA);
487
568
  var anyChild = false;
488
569
  it.querySelectorAll(":scope > div a").forEach(function (c) {
489
- var childActive = navPathname(c) === activePath && activePath !== "";
570
+ var childActive = isAnchorActive(c);
490
571
  if (childActive) {
491
572
  anyChild = true;
492
573
  c.setAttribute("data-active", "");
@@ -505,7 +586,7 @@ export function buildNavOverflowScript(context = resolveGenerationContext()) {
505
586
  else { svg.classList.add(${clsArgs(NAV_CHEVRON_INACTIVE)}); svg.classList.remove(${clsArgs(NAV_CHEVRON_ACTIVE)}); }
506
587
  }
507
588
  } else {
508
- topActive = activePath !== "" && navPathname(topA) === activePath;
589
+ topActive = isAnchorActive(topA);
509
590
  }
510
591
 
511
592
  setTopActive(topA, topActive);
@@ -517,7 +598,8 @@ export function buildNavOverflowScript(context = resolveGenerationContext()) {
517
598
 
518
599
  // Repaint the active highlight for the current URL before measuring /
519
600
  // cloning, so the overflow "···" menu mirrors the correct active state.
520
- applyActiveNav();
601
+ // Skipped mid-parse: the content band is not in the DOM yet (#3953).
602
+ if (document.readyState !== "loading") applyActiveNav();
521
603
 
522
604
  var nav = document.querySelector("[data-header-nav]");
523
605
  var moreContainer = document.querySelector("[data-nav-more]");
@@ -721,6 +803,11 @@ export function buildNavOverflowScript(context = resolveGenerationContext()) {
721
803
  }
722
804
 
723
805
  initNavOverflow();
806
+ // First-paint re-init once the body is parsed, so applyActiveNav can read
807
+ // the content band's data-zd-nav-section (#3953).
808
+ if (document.readyState === "loading") {
809
+ document.addEventListener("DOMContentLoaded", initNavOverflow, { once: true });
810
+ }
724
811
  document.addEventListener(${afterNavigateEventLiteral}, initNavOverflow);
725
812
  })();`;
726
813
  }
@@ -485,6 +485,7 @@ function renderNavItem(
485
485
  <a
486
486
  href={href}
487
487
  aria-current={isActive ? "page" : undefined}
488
+ data-nav-category={item.categoryMatch}
488
489
  aria-haspopup="true"
489
490
  aria-expanded="false"
490
491
  class={[
@@ -526,6 +527,7 @@ function renderNavItem(
526
527
  return (
527
528
  <a
528
529
  href={childHref}
530
+ data-nav-category={child.categoryMatch}
529
531
  data-active={childActive ? "" : undefined}
530
532
  class={[
531
533
  "block px-hsp-md py-vsp-2xs text-small hover:bg-accent/10 hover:underline focus-visible:underline",
@@ -546,6 +548,7 @@ function renderNavItem(
546
548
  <a
547
549
  href={href}
548
550
  aria-current={isActive ? "page" : undefined}
551
+ data-nav-category={item.categoryMatch}
549
552
  data-nav-item
550
553
  class={[
551
554
  "px-hsp-md py-vsp-2xs text-small font-medium transition-colors shrink-0",
@@ -19,7 +19,7 @@
19
19
  * this module: comparing NAV_OVERFLOW_SCRIPT below against this same file's
20
20
  * function would be a vacuous self-comparison. */
21
21
  export function buildNavOverflowScript(): string {
22
- return "(function () {\n var cleanupNavOverflow = null;\n\n function trimSlashes(p) {\n while (p.length > 1 && p.charAt(p.length - 1) === \"/\") p = p.slice(0, -1);\n return p || \"/\";\n }\n\n function navPathname(a) {\n try { return trimSlashes(new URL(a.href, location.href).pathname); }\n catch (e) { return \"\"; }\n }\n\n // Explicit current-route override, embedded from current-path/index.ts so\n // this script cannot drift from the three other read sites\n // (zudolab/zudo-doc#3398, #3408).\n var CURRENT_PATH_DATASET_KEY=\"zdCurrentPath\";var readCurrentPath=function readCurrentPath(datasetKey, explicit) {\n const override = typeof document === \"undefined\" ? void 0 : document.documentElement.dataset[datasetKey];\n return explicit || override || (typeof window === \"undefined\" ? void 0 : window.location.pathname);\n};\n\n // Shared matching core (zudolab/zudo-doc#3398): embedded verbatim from\n // nav-active.ts so this script's longest-match walk cannot drift from the\n // SSR header's own computeActiveNavPath call (header.tsx). computeActiveNavPath\n // closes over pathMatchesNavPath, so both are embedded together.\n var pathMatchesNavPath = function pathMatchesNavPath(currentPath, navPath) {\n if (currentPath === navPath) return true;\n const prefix = navPath.endsWith(\"/\") ? navPath : `${navPath}/`;\n return currentPath.startsWith(prefix);\n};\n var computeActiveNavPath = function computeActiveNavPath(navItems, pathForMatchValue) {\n const allNavPaths = navItems.flatMap((item) => {\n const paths = [item.path];\n if (item.children) {\n paths.push(...item.children.map((child) => child.path));\n }\n return paths;\n });\n return allNavPaths.filter((p) => pathMatchesNavPath(pathForMatchValue, p)).sort((a, b) => b.length - a.length)[0];\n};\n\n // Recompute which header nav item is \"active\" from the CURRENT URL and\n // repaint the highlight. SSR sets the active item on first paint, but the\n // header is persisted across same-locale client-router swaps\n // (data-zfb-transition-persist), so without this the highlight would stay\n // frozen on the page where the header was first rendered. Mirrors the\n // sidebar island's client-side approach (match the current path against\n // each entry's href) and the SSR longest-match + dropdown-parent rules.\n // URL-based: hrefs and the current path both carry the base + locale\n // prefix, so they compare directly without stripping.\n function applyActiveNav() {\n var nav = document.querySelector(\"[data-header-nav]\");\n if (!nav) return;\n var topItems = Array.from(nav.querySelectorAll(\":scope > [data-nav-item]\"));\n if (topItems.length === 0) return;\n\n var cur = trimSlashes(readCurrentPath(CURRENT_PATH_DATASET_KEY));\n\n // Build NavItemLike-shaped entries from the live DOM so the shared\n // computeActiveNavPath can do the deepest-match walk — the same call\n // shape the SSR header uses (matches computeActiveNavPath). A dropdown\n // missing its own top-level anchor is skipped entirely (path \"\" would\n // otherwise match every current path — pathMatchesNavPath treats \"\" as\n // the root \"/\"), mirroring the parentLink guard used below for the same\n // malformed-markup case.\n var navItems = [];\n topItems.forEach(function (it) {\n var isDropdown = it.hasAttribute(\"data-nav-item-dropdown\");\n var topA = isDropdown ? it.querySelector(\":scope > a\") : it;\n if (!topA) return;\n var children = [];\n if (isDropdown) {\n it.querySelectorAll(\":scope > div a\").forEach(function (c) {\n children.push({ path: navPathname(c) });\n });\n }\n navItems.push({ path: navPathname(topA), children: children });\n });\n\n var activePath = computeActiveNavPath(navItems, cur) || \"\";\n\n function setTopActive(a, active) {\n if (!a) return;\n if (active) {\n a.classList.add(\"bg-fg\", \"text-bg\");\n a.classList.remove(\"text-muted\", \"hover:text-accent\", \"hover:underline\", \"focus:underline\", \"focus:text-accent\");\n a.setAttribute(\"aria-current\", \"page\");\n } else {\n a.classList.remove(\"bg-fg\", \"text-bg\");\n a.classList.add(\"text-muted\", \"hover:text-accent\", \"hover:underline\", \"focus:underline\", \"focus:text-accent\");\n a.removeAttribute(\"aria-current\");\n }\n }\n\n topItems.forEach(function (it) {\n var isDropdown = it.hasAttribute(\"data-nav-item-dropdown\");\n var topA = isDropdown ? it.querySelector(\":scope > a\") : it;\n var topActive = false;\n\n if (isDropdown) {\n var parentMatch = !!topA && navPathname(topA) === activePath && activePath !== \"\";\n var anyChild = false;\n it.querySelectorAll(\":scope > div a\").forEach(function (c) {\n var childActive = navPathname(c) === activePath && activePath !== \"\";\n if (childActive) {\n anyChild = true;\n c.setAttribute(\"data-active\", \"\");\n c.classList.add(\"font-bold\", \"text-accent\");\n c.classList.remove(\"text-fg\", \"hover:text-accent\", \"focus-visible:text-accent\");\n } else {\n c.removeAttribute(\"data-active\");\n c.classList.remove(\"font-bold\", \"text-accent\");\n c.classList.add(\"text-fg\", \"hover:text-accent\", \"focus-visible:text-accent\");\n }\n });\n topActive = parentMatch || anyChild;\n var svg = topA ? topA.querySelector(\"svg\") : null;\n if (svg) {\n if (topActive) { svg.classList.add(\"text-bg\"); svg.classList.remove(\"text-muted\"); }\n else { svg.classList.add(\"text-muted\"); svg.classList.remove(\"text-bg\"); }\n }\n } else {\n topActive = activePath !== \"\" && navPathname(topA) === activePath;\n }\n\n setTopActive(topA, topActive);\n });\n }\n\n function initNavOverflow() {\n if (cleanupNavOverflow) cleanupNavOverflow();\n\n // Repaint the active highlight for the current URL before measuring /\n // cloning, so the overflow \"···\" menu mirrors the correct active state.\n applyActiveNav();\n\n var nav = document.querySelector(\"[data-header-nav]\");\n var moreContainer = document.querySelector(\"[data-nav-more]\");\n var moreMenu = document.querySelector(\"[data-nav-more-menu]\");\n var moreToggle = document.querySelector(\"[data-nav-more-toggle]\");\n if (!nav || !moreContainer || !moreMenu || !moreToggle) return;\n\n function setMoreActive(active) {\n if (active) {\n moreToggle.classList.add(\"bg-fg\", \"text-bg\");\n moreToggle.classList.remove(\"text-muted\", \"hover:text-accent\", \"focus-visible:text-accent\");\n } else {\n moreToggle.classList.add(\"text-muted\", \"hover:text-accent\", \"focus-visible:text-accent\");\n moreToggle.classList.remove(\"bg-fg\", \"text-bg\");\n }\n }\n\n // The persisted header can be re-initialized with a different nav shape.\n // Clear a prior page's transferred active state even when there are no\n // items and update() will not be installed (#3758).\n setMoreActive(false);\n\n var items = Array.from(nav.querySelectorAll(\":scope > [data-nav-item]\"));\n if (items.length === 0) {\n moreContainer.style.display = \"none\";\n return;\n }\n\n var controller = new AbortController();\n\n function update() {\n items.forEach(function (el) { el.style.display = \"\"; });\n moreContainer.style.display = \"\";\n moreMenu.innerHTML = \"\";\n moreMenu.classList.add(\"hidden\");\n moreToggle.setAttribute(\"aria-expanded\", \"false\");\n setMoreActive(false);\n\n var itemWidths = items.map(function (el) { return el.offsetWidth; });\n var moreWidth = moreContainer.offsetWidth;\n var navGap = parseFloat(getComputedStyle(nav).columnGap) || 0;\n var available = nav.clientWidth;\n\n if (available <= 0) {\n moreContainer.style.display = \"none\";\n return;\n }\n\n var total = 0;\n for (var i = 0; i < itemWidths.length; i++) {\n total += itemWidths[i] + (i > 0 ? navGap : 0);\n }\n\n if (total <= available) {\n moreContainer.style.display = \"none\";\n return;\n }\n\n var used = 0;\n var cutoffIndex = 0;\n\n for (var i2 = 0; i2 < items.length; i2++) {\n var w = itemWidths[i2] + (i2 > 0 ? navGap : 0);\n if (used + w > available - moreWidth - navGap) break;\n used += w;\n cutoffIndex = i2 + 1;\n }\n\n var hiddenHasActiveItem = false;\n for (var i3 = cutoffIndex; i3 < items.length; i3++) {\n var hiddenItem = items[i3];\n hiddenItem.style.display = \"none\";\n var hiddenTopLink = hiddenItem.hasAttribute(\"data-nav-item-dropdown\")\n ? hiddenItem.querySelector(\":scope > a\")\n : hiddenItem;\n var hiddenActiveChild = hiddenItem.querySelector(\":scope > div a[data-active]\");\n if ((hiddenTopLink && hiddenTopLink.getAttribute(\"aria-current\") === \"page\") || hiddenActiveChild) {\n hiddenHasActiveItem = true;\n }\n }\n setMoreActive(hiddenHasActiveItem);\n\n var currentCloneAssigned = false;\n for (var i4 = cutoffIndex; i4 < items.length; i4++) {\n var el = items[i4];\n var isDropdown = el.hasAttribute(\"data-nav-item-dropdown\");\n\n if (isDropdown) {\n var parentLink = el.querySelector(\":scope > a\");\n var childLinks = el.querySelectorAll(\":scope > div a\");\n var hasActiveChild = Array.from(childLinks).some(function (child) {\n return child.hasAttribute(\"data-active\");\n });\n if (parentLink) {\n var li = document.createElement(\"li\");\n var a = document.createElement(\"a\");\n a.href = parentLink.href;\n var parentText = parentLink.textContent ? parentLink.textContent.trim().replace(/\\s+/g, \" \") : \"\";\n a.textContent = parentText;\n a.className = \"block px-hsp-md py-vsp-2xs text-small font-bold hover:bg-accent/10 hover:underline focus-visible:underline focus-visible:text-accent text-fg hover:text-accent\";\n if (parentLink.getAttribute(\"aria-current\") === \"page\") {\n a.className += \" text-accent\";\n if (!hasActiveChild && !currentCloneAssigned) {\n a.setAttribute(\"aria-current\", \"page\");\n currentCloneAssigned = true;\n }\n }\n li.appendChild(a);\n moreMenu.appendChild(li);\n }\n childLinks.forEach(function (child) {\n var li = document.createElement(\"li\");\n var a = document.createElement(\"a\");\n a.href = child.href;\n a.textContent = child.textContent ? child.textContent.trim() : \"\";\n var isChildActive = child.hasAttribute(\"data-active\");\n a.className = isChildActive\n ? \"block pl-hsp-xl pr-hsp-md py-vsp-2xs text-small font-bold text-accent hover:bg-accent/10 hover:underline focus-visible:underline\"\n : \"block pl-hsp-xl pr-hsp-md py-vsp-2xs text-small text-fg hover:bg-accent/10 hover:text-accent hover:underline focus-visible:underline focus-visible:text-accent\";\n if (isChildActive && !currentCloneAssigned) {\n a.setAttribute(\"aria-current\", \"page\");\n currentCloneAssigned = true;\n }\n li.appendChild(a);\n moreMenu.appendChild(li);\n });\n } else {\n var anchor = el;\n var li2 = document.createElement(\"li\");\n var a2 = document.createElement(\"a\");\n a2.href = anchor.href;\n a2.textContent = anchor.textContent ? anchor.textContent.trim() : \"\";\n a2.className = \"block px-hsp-md py-vsp-2xs text-small hover:bg-accent/10 hover:underline focus-visible:underline focus-visible:text-accent text-fg hover:text-accent\";\n if (anchor.getAttribute(\"aria-current\") === \"page\") {\n a2.className += \" font-bold text-accent\";\n if (!currentCloneAssigned) {\n a2.setAttribute(\"aria-current\", \"page\");\n currentCloneAssigned = true;\n }\n }\n li2.appendChild(a2);\n moreMenu.appendChild(li2);\n }\n }\n }\n\n moreToggle.addEventListener(\"click\", function () {\n var isOpen = !moreMenu.classList.contains(\"hidden\");\n moreMenu.classList.toggle(\"hidden\", isOpen);\n moreToggle.setAttribute(\"aria-expanded\", String(!isOpen));\n }, { signal: controller.signal });\n\n document.addEventListener(\"click\", function (e) {\n if (!moreContainer.contains(e.target)) {\n moreMenu.classList.add(\"hidden\");\n moreToggle.setAttribute(\"aria-expanded\", \"false\");\n }\n }, { signal: controller.signal });\n\n document.addEventListener(\"keydown\", function (e) {\n if (e.key !== \"Escape\") return;\n if (!moreMenu.classList.contains(\"hidden\")) {\n moreMenu.classList.add(\"hidden\");\n moreToggle.setAttribute(\"aria-expanded\", \"false\");\n moreToggle.focus();\n return;\n }\n var active = document.activeElement;\n var dropdown = active && active.closest ? active.closest(\"[data-nav-item-dropdown]\") : null;\n if (dropdown && active && active.blur) {\n active.blur();\n }\n }, { signal: controller.signal });\n\n var dropdowns = nav.querySelectorAll(\"[data-nav-item-dropdown]\");\n dropdowns.forEach(function (dd) {\n var trigger = dd.querySelector(\":scope > a\");\n if (!trigger) return;\n function setExpanded(v) {\n trigger.setAttribute(\"aria-expanded\", String(v));\n }\n dd.addEventListener(\"mouseenter\", function () { setExpanded(true); }, { signal: controller.signal });\n dd.addEventListener(\"mouseleave\", function () { setExpanded(false); }, { signal: controller.signal });\n dd.addEventListener(\"focusin\", function () { setExpanded(true); }, { signal: controller.signal });\n dd.addEventListener(\"focusout\", function (e) {\n if (!dd.contains(e.relatedTarget)) {\n setExpanded(false);\n }\n }, { signal: controller.signal });\n });\n\n var ro = new ResizeObserver(update);\n ro.observe(nav);\n controller.signal.addEventListener(\"abort\", function () { ro.disconnect(); });\n\n document.fonts.ready.then(update);\n\n update();\n\n cleanupNavOverflow = function () { controller.abort(); };\n }\n\n initNavOverflow();\n document.addEventListener(\"zfb:after-swap\", initNavOverflow);\n})();";
22
+ return "(function () {\n var cleanupNavOverflow = null;\n\n function trimSlashes(p) {\n while (p.length > 1 && p.charAt(p.length - 1) === \"/\") p = p.slice(0, -1);\n return p || \"/\";\n }\n\n // \"\" for cross-origin: unmatchable sentinel, matching SSR (#3950).\n function navPathname(a) {\n try {\n var u = new URL(a.href, location.href);\n if (u.origin !== location.origin) return \"\";\n return trimSlashes(u.pathname);\n } catch (e) { return \"\"; }\n }\n\n // Explicit current-route override, embedded from current-path/index.ts so\n // this script cannot drift from the three other read sites\n // (zudolab/zudo-doc#3398, #3408).\n var CURRENT_PATH_DATASET_KEY=\"zdCurrentPath\";var readCurrentPath=function readCurrentPath(datasetKey, explicit) {\n const override = typeof document === \"undefined\" ? void 0 : document.documentElement.dataset[datasetKey];\n return explicit || override || (typeof window === \"undefined\" ? void 0 : window.location.pathname);\n};\n\n // Shared matching core (zudolab/zudo-doc#3398): embedded verbatim from\n // nav-active.ts so this script's longest-match walk cannot drift from the\n // SSR header's own computeActiveNavPath call (header.tsx). computeActiveNavPath\n // closes over pathMatchesNavPath, so both are embedded together.\n var pathMatchesNavPath = function pathMatchesNavPath(currentPath, navPath) {\n if (currentPath === navPath) return true;\n const prefix = navPath.endsWith(\"/\") ? navPath : `${navPath}/`;\n return currentPath.startsWith(prefix);\n};\n var computeActiveNavPath = function computeActiveNavPath(navItems, pathForMatchValue) {\n const allNavPaths = navItems.flatMap((item) => {\n const paths = [item.path];\n if (item.children) {\n paths.push(...item.children.map((child) => child.path));\n }\n return paths;\n });\n return allNavPaths.filter((p) => pathMatchesNavPath(pathForMatchValue, p)).sort((a, b) => b.length - a.length)[0];\n};\n\n // Recompute which header nav item is \"active\" from the CURRENT URL and\n // repaint the highlight. SSR sets the active item on first paint, but the\n // header is persisted across same-locale client-router swaps\n // (data-zfb-transition-persist), so without this the highlight would stay\n // frozen on the page where the header was first rendered. Mirrors the\n // sidebar island's client-side approach (match the current path against\n // each entry's href) and the SSR longest-match + dropdown-parent rules.\n // URL-based: hrefs and the current path both carry the base + locale\n // prefix, so they compare directly without stripping.\n function applyActiveNav() {\n var nav = document.querySelector(\"[data-header-nav]\");\n if (!nav) return;\n var topItems = Array.from(nav.querySelectorAll(\":scope > [data-nav-item]\"));\n if (topItems.length === 0) return;\n\n var cur = trimSlashes(readCurrentPath(CURRENT_PATH_DATASET_KEY));\n\n // SSR's own resolved big category, republished per page (#3953).\n var sectionEl = document.querySelector(\"[data-zd-nav-section]\");\n var navSection = (sectionEl && sectionEl.getAttribute(\"data-zd-nav-section\")) || \"\";\n\n // Build NavItemLike-shaped entries from the live DOM so the shared\n // computeActiveNavPath can do the deepest-match walk — the same call\n // shape the SSR header uses (matches computeActiveNavPath). A dropdown\n // missing its own top-level anchor is skipped entirely, mirroring the\n // parentLink guard used below for the same malformed-markup case.\n // A \"\" path (cross-origin) is unmatchable ONLY because of the length sort\n // plus the `activePath !== \"\"` guards below — keep both.\n var navItems = [];\n topItems.forEach(function (it) {\n var isDropdown = it.hasAttribute(\"data-nav-item-dropdown\");\n var topA = isDropdown ? it.querySelector(\":scope > a\") : it;\n if (!topA) return;\n var children = [];\n if (isDropdown) {\n it.querySelectorAll(\":scope > div a\").forEach(function (c) {\n children.push({ path: navPathname(c) });\n });\n }\n navItems.push({ path: navPathname(topA), children: children });\n });\n\n var activePath = computeActiveNavPath(navItems, cur) || \"\";\n\n // Mirrors SSR: category match OR path match, per item (see nav-active.ts).\n function isAnchorActive(a) {\n if (!a) return false;\n if (navSection !== \"\" && a.getAttribute(\"data-nav-category\") === navSection) return true;\n return activePath !== \"\" && navPathname(a) === activePath;\n }\n\n function setTopActive(a, active) {\n if (!a) return;\n if (active) {\n a.classList.add(\"bg-fg\", \"text-bg\");\n a.classList.remove(\"text-muted\", \"hover:text-accent\", \"hover:underline\", \"focus:underline\", \"focus:text-accent\");\n a.setAttribute(\"aria-current\", \"page\");\n } else {\n a.classList.remove(\"bg-fg\", \"text-bg\");\n a.classList.add(\"text-muted\", \"hover:text-accent\", \"hover:underline\", \"focus:underline\", \"focus:text-accent\");\n a.removeAttribute(\"aria-current\");\n }\n }\n\n topItems.forEach(function (it) {\n var isDropdown = it.hasAttribute(\"data-nav-item-dropdown\");\n var topA = isDropdown ? it.querySelector(\":scope > a\") : it;\n var topActive = false;\n\n if (isDropdown) {\n var parentMatch = isAnchorActive(topA);\n var anyChild = false;\n it.querySelectorAll(\":scope > div a\").forEach(function (c) {\n var childActive = isAnchorActive(c);\n if (childActive) {\n anyChild = true;\n c.setAttribute(\"data-active\", \"\");\n c.classList.add(\"font-bold\", \"text-accent\");\n c.classList.remove(\"text-fg\", \"hover:text-accent\", \"focus-visible:text-accent\");\n } else {\n c.removeAttribute(\"data-active\");\n c.classList.remove(\"font-bold\", \"text-accent\");\n c.classList.add(\"text-fg\", \"hover:text-accent\", \"focus-visible:text-accent\");\n }\n });\n topActive = parentMatch || anyChild;\n var svg = topA ? topA.querySelector(\"svg\") : null;\n if (svg) {\n if (topActive) { svg.classList.add(\"text-bg\"); svg.classList.remove(\"text-muted\"); }\n else { svg.classList.add(\"text-muted\"); svg.classList.remove(\"text-bg\"); }\n }\n } else {\n topActive = isAnchorActive(topA);\n }\n\n setTopActive(topA, topActive);\n });\n }\n\n function initNavOverflow() {\n if (cleanupNavOverflow) cleanupNavOverflow();\n\n // Repaint the active highlight for the current URL before measuring /\n // cloning, so the overflow \"···\" menu mirrors the correct active state.\n // Skipped mid-parse: the content band is not in the DOM yet (#3953).\n if (document.readyState !== \"loading\") applyActiveNav();\n\n var nav = document.querySelector(\"[data-header-nav]\");\n var moreContainer = document.querySelector(\"[data-nav-more]\");\n var moreMenu = document.querySelector(\"[data-nav-more-menu]\");\n var moreToggle = document.querySelector(\"[data-nav-more-toggle]\");\n if (!nav || !moreContainer || !moreMenu || !moreToggle) return;\n\n function setMoreActive(active) {\n if (active) {\n moreToggle.classList.add(\"bg-fg\", \"text-bg\");\n moreToggle.classList.remove(\"text-muted\", \"hover:text-accent\", \"focus-visible:text-accent\");\n } else {\n moreToggle.classList.add(\"text-muted\", \"hover:text-accent\", \"focus-visible:text-accent\");\n moreToggle.classList.remove(\"bg-fg\", \"text-bg\");\n }\n }\n\n // The persisted header can be re-initialized with a different nav shape.\n // Clear a prior page's transferred active state even when there are no\n // items and update() will not be installed (#3758).\n setMoreActive(false);\n\n var items = Array.from(nav.querySelectorAll(\":scope > [data-nav-item]\"));\n if (items.length === 0) {\n moreContainer.style.display = \"none\";\n return;\n }\n\n var controller = new AbortController();\n\n function update() {\n items.forEach(function (el) { el.style.display = \"\"; });\n moreContainer.style.display = \"\";\n moreMenu.innerHTML = \"\";\n moreMenu.classList.add(\"hidden\");\n moreToggle.setAttribute(\"aria-expanded\", \"false\");\n setMoreActive(false);\n\n var itemWidths = items.map(function (el) { return el.offsetWidth; });\n var moreWidth = moreContainer.offsetWidth;\n var navGap = parseFloat(getComputedStyle(nav).columnGap) || 0;\n var available = nav.clientWidth;\n\n if (available <= 0) {\n moreContainer.style.display = \"none\";\n return;\n }\n\n var total = 0;\n for (var i = 0; i < itemWidths.length; i++) {\n total += itemWidths[i] + (i > 0 ? navGap : 0);\n }\n\n if (total <= available) {\n moreContainer.style.display = \"none\";\n return;\n }\n\n var used = 0;\n var cutoffIndex = 0;\n\n for (var i2 = 0; i2 < items.length; i2++) {\n var w = itemWidths[i2] + (i2 > 0 ? navGap : 0);\n if (used + w > available - moreWidth - navGap) break;\n used += w;\n cutoffIndex = i2 + 1;\n }\n\n var hiddenHasActiveItem = false;\n for (var i3 = cutoffIndex; i3 < items.length; i3++) {\n var hiddenItem = items[i3];\n hiddenItem.style.display = \"none\";\n var hiddenTopLink = hiddenItem.hasAttribute(\"data-nav-item-dropdown\")\n ? hiddenItem.querySelector(\":scope > a\")\n : hiddenItem;\n var hiddenActiveChild = hiddenItem.querySelector(\":scope > div a[data-active]\");\n if ((hiddenTopLink && hiddenTopLink.getAttribute(\"aria-current\") === \"page\") || hiddenActiveChild) {\n hiddenHasActiveItem = true;\n }\n }\n setMoreActive(hiddenHasActiveItem);\n\n var currentCloneAssigned = false;\n for (var i4 = cutoffIndex; i4 < items.length; i4++) {\n var el = items[i4];\n var isDropdown = el.hasAttribute(\"data-nav-item-dropdown\");\n\n if (isDropdown) {\n var parentLink = el.querySelector(\":scope > a\");\n var childLinks = el.querySelectorAll(\":scope > div a\");\n var hasActiveChild = Array.from(childLinks).some(function (child) {\n return child.hasAttribute(\"data-active\");\n });\n if (parentLink) {\n var li = document.createElement(\"li\");\n var a = document.createElement(\"a\");\n a.href = parentLink.href;\n var parentText = parentLink.textContent ? parentLink.textContent.trim().replace(/\\s+/g, \" \") : \"\";\n a.textContent = parentText;\n a.className = \"block px-hsp-md py-vsp-2xs text-small font-bold hover:bg-accent/10 hover:underline focus-visible:underline focus-visible:text-accent text-fg hover:text-accent\";\n if (parentLink.getAttribute(\"aria-current\") === \"page\") {\n a.className += \" text-accent\";\n if (!hasActiveChild && !currentCloneAssigned) {\n a.setAttribute(\"aria-current\", \"page\");\n currentCloneAssigned = true;\n }\n }\n li.appendChild(a);\n moreMenu.appendChild(li);\n }\n childLinks.forEach(function (child) {\n var li = document.createElement(\"li\");\n var a = document.createElement(\"a\");\n a.href = child.href;\n a.textContent = child.textContent ? child.textContent.trim() : \"\";\n var isChildActive = child.hasAttribute(\"data-active\");\n a.className = isChildActive\n ? \"block pl-hsp-xl pr-hsp-md py-vsp-2xs text-small font-bold text-accent hover:bg-accent/10 hover:underline focus-visible:underline\"\n : \"block pl-hsp-xl pr-hsp-md py-vsp-2xs text-small text-fg hover:bg-accent/10 hover:text-accent hover:underline focus-visible:underline focus-visible:text-accent\";\n if (isChildActive && !currentCloneAssigned) {\n a.setAttribute(\"aria-current\", \"page\");\n currentCloneAssigned = true;\n }\n li.appendChild(a);\n moreMenu.appendChild(li);\n });\n } else {\n var anchor = el;\n var li2 = document.createElement(\"li\");\n var a2 = document.createElement(\"a\");\n a2.href = anchor.href;\n a2.textContent = anchor.textContent ? anchor.textContent.trim() : \"\";\n a2.className = \"block px-hsp-md py-vsp-2xs text-small hover:bg-accent/10 hover:underline focus-visible:underline focus-visible:text-accent text-fg hover:text-accent\";\n if (anchor.getAttribute(\"aria-current\") === \"page\") {\n a2.className += \" font-bold text-accent\";\n if (!currentCloneAssigned) {\n a2.setAttribute(\"aria-current\", \"page\");\n currentCloneAssigned = true;\n }\n }\n li2.appendChild(a2);\n moreMenu.appendChild(li2);\n }\n }\n }\n\n moreToggle.addEventListener(\"click\", function () {\n var isOpen = !moreMenu.classList.contains(\"hidden\");\n moreMenu.classList.toggle(\"hidden\", isOpen);\n moreToggle.setAttribute(\"aria-expanded\", String(!isOpen));\n }, { signal: controller.signal });\n\n document.addEventListener(\"click\", function (e) {\n if (!moreContainer.contains(e.target)) {\n moreMenu.classList.add(\"hidden\");\n moreToggle.setAttribute(\"aria-expanded\", \"false\");\n }\n }, { signal: controller.signal });\n\n document.addEventListener(\"keydown\", function (e) {\n if (e.key !== \"Escape\") return;\n if (!moreMenu.classList.contains(\"hidden\")) {\n moreMenu.classList.add(\"hidden\");\n moreToggle.setAttribute(\"aria-expanded\", \"false\");\n moreToggle.focus();\n return;\n }\n var active = document.activeElement;\n var dropdown = active && active.closest ? active.closest(\"[data-nav-item-dropdown]\") : null;\n if (dropdown && active && active.blur) {\n active.blur();\n }\n }, { signal: controller.signal });\n\n var dropdowns = nav.querySelectorAll(\"[data-nav-item-dropdown]\");\n dropdowns.forEach(function (dd) {\n var trigger = dd.querySelector(\":scope > a\");\n if (!trigger) return;\n function setExpanded(v) {\n trigger.setAttribute(\"aria-expanded\", String(v));\n }\n dd.addEventListener(\"mouseenter\", function () { setExpanded(true); }, { signal: controller.signal });\n dd.addEventListener(\"mouseleave\", function () { setExpanded(false); }, { signal: controller.signal });\n dd.addEventListener(\"focusin\", function () { setExpanded(true); }, { signal: controller.signal });\n dd.addEventListener(\"focusout\", function (e) {\n if (!dd.contains(e.relatedTarget)) {\n setExpanded(false);\n }\n }, { signal: controller.signal });\n });\n\n var ro = new ResizeObserver(update);\n ro.observe(nav);\n controller.signal.addEventListener(\"abort\", function () { ro.disconnect(); });\n\n document.fonts.ready.then(update);\n\n update();\n\n cleanupNavOverflow = function () { controller.abort(); };\n }\n\n initNavOverflow();\n // First-paint re-init once the body is parsed, so applyActiveNav can read\n // the content band's data-zd-nav-section (#3953).\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", initNavOverflow, { once: true });\n }\n document.addEventListener(\"zfb:after-swap\", initNavOverflow);\n})();";
23
23
  }
24
24
 
25
25
  /** Client-side script string for the desktop header nav overflow controller.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takazudo/zudo-doc",
3
- "version": "5.17.0",
3
+ "version": "5.17.1",
4
4
  "type": "module",
5
5
  "description": "zudo-doc framework primitives layer that sits on top of zfb's engine — sidebar, theme, TOC, breadcrumb, layouts, head injection, View Transitions, SSR-skip wrappers (per ADR-003).",
6
6
  "license": "MIT",
@@ -668,9 +668,9 @@
668
668
  ],
669
669
  "peerDependencies": {
670
670
  "@takazudo/zdtp": "^0.4.14",
671
- "@takazudo/zfb": "^2.15.0",
672
- "@takazudo/zfb-md-wasm": "^2.15.0",
673
- "@takazudo/zfb-runtime": "^2.15.0",
671
+ "@takazudo/zfb": "^2.15.1",
672
+ "@takazudo/zfb-md-wasm": "^2.15.1",
673
+ "@takazudo/zfb-runtime": "^2.15.1",
674
674
  "@takazudo/zudo-doc-history-server": "^5.15.0",
675
675
  "diff": "^8.0.0",
676
676
  "katex": "^0.16.0",
@@ -706,9 +706,9 @@
706
706
  "yaml": "^2.9.0"
707
707
  },
708
708
  "devDependencies": {
709
- "@takazudo/zfb": "2.15.0",
710
- "@takazudo/zfb-md-wasm": "2.15.0",
711
- "@takazudo/zfb-runtime": "2.15.0",
709
+ "@takazudo/zfb": "2.15.1",
710
+ "@takazudo/zfb-md-wasm": "2.15.1",
711
+ "@takazudo/zfb-runtime": "2.15.1",
712
712
  "@types/fs-extra": "^11.0.4",
713
713
  "@types/minimist": "^1.2.5",
714
714
  "@types/node": "^25.3.5",
@@ -720,7 +720,7 @@
720
720
  "typescript": "^5.0.0",
721
721
  "vitest": "^4.1.0",
722
722
  "zod": "^4.3.6",
723
- "@takazudo/zudo-doc-history-server": "5.17.0"
723
+ "@takazudo/zudo-doc-history-server": "5.17.1"
724
724
  },
725
725
  "scripts": {
726
726
  "gen:search-widget-script": "node scripts/gen-search-widget-script.mjs",