@takazudo/zudo-doc 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/header/header.js +28 -1
- package/dist/header-with-defaults/index.d.ts +4 -0
- package/dist/header-with-defaults/index.js +14 -2
- package/dist/i18n-version/index.d.ts +4 -4
- package/dist/i18n-version/index.js +13 -3
- package/dist/i18n-version/language-switcher.d.ts +54 -1
- package/dist/i18n-version/language-switcher.js +80 -3
- package/dist/i18n-version/version-switcher.d.ts +76 -0
- package/dist/i18n-version/version-switcher.js +137 -4
- package/dist/integrations/claude-resources/generate.js +5 -2
- package/dist/integrations/claude-resources/index.d.ts +19 -2
- package/dist/integrations/claude-resources/index.js +2 -1
- package/dist/plugins/claude-resources.js +2 -0
- package/dist/preset.d.ts +5 -0
- package/dist/preset.js +1 -0
- package/dist/safelist.css +1 -1
- package/dist/settings.d.ts +1 -0
- package/eject/header/header.tsx +33 -5
- package/package.json +6 -6
package/dist/header/header.js
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
pathForMatch
|
|
7
7
|
} from "./nav-active.js";
|
|
8
8
|
import { NAV_OVERFLOW_SCRIPT } from "./nav-overflow-script.js";
|
|
9
|
+
import { LANGUAGE_SWITCHER_INIT_SCRIPT } from "../i18n-version/language-switcher.js";
|
|
10
|
+
import { VERSION_SWITCHER_REWIRE_SCRIPT } from "../i18n-version/version-switcher.js";
|
|
9
11
|
import { GitHub as GitHubIcon } from "../icons/index.js";
|
|
10
12
|
function Header(props) {
|
|
11
13
|
const {
|
|
@@ -25,6 +27,7 @@ function Header(props) {
|
|
|
25
27
|
headerRightItems,
|
|
26
28
|
colorModeEnabled,
|
|
27
29
|
hasLocales,
|
|
30
|
+
hasVersions,
|
|
28
31
|
githubRepoUrl,
|
|
29
32
|
githubLabel,
|
|
30
33
|
urlHelpers,
|
|
@@ -115,7 +118,31 @@ function Header(props) {
|
|
|
115
118
|
))
|
|
116
119
|
}
|
|
117
120
|
),
|
|
118
|
-
/* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: NAV_OVERFLOW_SCRIPT } })
|
|
121
|
+
/* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: NAV_OVERFLOW_SCRIPT } }),
|
|
122
|
+
hasLocales ? (
|
|
123
|
+
// Keeps the persisted header's language-switcher hrefs pointing at the
|
|
124
|
+
// current page's equivalent in each other locale across same-locale SPA
|
|
125
|
+
// navigation (#2551). Registers a document-level AFTER_NAVIGATE_EVENT
|
|
126
|
+
// listener once; idempotent across re-execution.
|
|
127
|
+
/* @__PURE__ */ jsx(
|
|
128
|
+
"script",
|
|
129
|
+
{
|
|
130
|
+
dangerouslySetInnerHTML: { __html: LANGUAGE_SWITCHER_INIT_SCRIPT }
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
) : null,
|
|
134
|
+
hasVersions ? (
|
|
135
|
+
// Keeps the persisted header's version-switcher menu hrefs / active row /
|
|
136
|
+
// trigger label tracking the current page across same-locale SPA
|
|
137
|
+
// navigation (#2553). Registers a document-level AFTER_NAVIGATE_EVENT
|
|
138
|
+
// listener once; idempotent across re-execution.
|
|
139
|
+
/* @__PURE__ */ jsx(
|
|
140
|
+
"script",
|
|
141
|
+
{
|
|
142
|
+
dangerouslySetInnerHTML: { __html: VERSION_SWITCHER_REWIRE_SCRIPT }
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
) : null
|
|
119
146
|
]
|
|
120
147
|
}
|
|
121
148
|
);
|
|
@@ -23,6 +23,10 @@ export interface HeaderVersionEntry {
|
|
|
23
23
|
/** Settings subset read by {@link createHeaderWithDefaults}. */
|
|
24
24
|
export interface HeaderWithDefaultsSettings {
|
|
25
25
|
siteName: string;
|
|
26
|
+
/** Site base path — fed to the language-switcher SPA re-wire config. */
|
|
27
|
+
base: string;
|
|
28
|
+
/** Trailing-slash policy — fed to the language-switcher SPA re-wire config. */
|
|
29
|
+
trailingSlash: boolean;
|
|
26
30
|
headerNav: Array<{
|
|
27
31
|
label: string;
|
|
28
32
|
labelKey?: string;
|
|
@@ -97,14 +97,26 @@ function createHeaderWithDefaults(ctx) {
|
|
|
97
97
|
versionsPageUrl,
|
|
98
98
|
versionUrls,
|
|
99
99
|
labels,
|
|
100
|
-
idSuffix: "header"
|
|
100
|
+
idSuffix: "header",
|
|
101
|
+
rewireConfig: {
|
|
102
|
+
base: settings.base.replace(/\/+$/, ""),
|
|
103
|
+
defaultLocale,
|
|
104
|
+
trailingSlash: settings.trailingSlash,
|
|
105
|
+
currentLocale: lang
|
|
106
|
+
}
|
|
101
107
|
}
|
|
102
108
|
);
|
|
103
109
|
}
|
|
104
110
|
const languageSwitcher = localeLinks != null ? /* @__PURE__ */ jsx(
|
|
105
111
|
LanguageSwitcher,
|
|
106
112
|
{
|
|
107
|
-
links: localeLinks
|
|
113
|
+
links: localeLinks,
|
|
114
|
+
config: {
|
|
115
|
+
base: settings.base.replace(/\/+$/, ""),
|
|
116
|
+
defaultLocale,
|
|
117
|
+
trailingSlash: settings.trailingSlash
|
|
118
|
+
},
|
|
119
|
+
currentLocale: lang
|
|
108
120
|
}
|
|
109
121
|
) : void 0;
|
|
110
122
|
const persistKey = `header-${lang}`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { LanguageSwitcher } from "./language-switcher.js";
|
|
2
|
-
export type { LanguageSwitcherProps } from "./language-switcher.js";
|
|
3
|
-
export { VersionSwitcher, VERSION_SWITCHER_INIT_SCRIPT, VERSION_SWITCHER_VISIBILITY_STYLE, } from "./version-switcher.js";
|
|
4
|
-
export type { VersionSwitcherProps } from "./version-switcher.js";
|
|
1
|
+
export { LanguageSwitcher, LANGUAGE_SWITCHER_INIT_SCRIPT, switchLocaleHref, } from "./language-switcher.js";
|
|
2
|
+
export type { LanguageSwitcherProps, LanguageSwitcherConfig, } from "./language-switcher.js";
|
|
3
|
+
export { VersionSwitcher, VERSION_SWITCHER_INIT_SCRIPT, VERSION_SWITCHER_REWIRE_SCRIPT, VERSION_SWITCHER_VISIBILITY_STYLE, computeVersionSwitcherState, } from "./version-switcher.js";
|
|
4
|
+
export type { VersionSwitcherProps, VersionSwitcherRewireConfig, VersionSwitcherState, } from "./version-switcher.js";
|
|
5
5
|
export { VersionBanner } from "./version-banner.js";
|
|
6
6
|
export type { VersionBannerProps, VersionBannerLabels } from "./version-banner.js";
|
|
7
7
|
export type { LocaleLink, VersionEntry, VersionSwitcherLabels, } from "./types.js";
|
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
LanguageSwitcher,
|
|
3
|
+
LANGUAGE_SWITCHER_INIT_SCRIPT,
|
|
4
|
+
switchLocaleHref
|
|
5
|
+
} from "./language-switcher.js";
|
|
2
6
|
import {
|
|
3
7
|
VersionSwitcher,
|
|
4
8
|
VERSION_SWITCHER_INIT_SCRIPT,
|
|
5
|
-
|
|
9
|
+
VERSION_SWITCHER_REWIRE_SCRIPT,
|
|
10
|
+
VERSION_SWITCHER_VISIBILITY_STYLE,
|
|
11
|
+
computeVersionSwitcherState
|
|
6
12
|
} from "./version-switcher.js";
|
|
7
13
|
import { VersionBanner } from "./version-banner.js";
|
|
8
14
|
export {
|
|
15
|
+
LANGUAGE_SWITCHER_INIT_SCRIPT,
|
|
9
16
|
LanguageSwitcher,
|
|
10
17
|
VERSION_SWITCHER_INIT_SCRIPT,
|
|
18
|
+
VERSION_SWITCHER_REWIRE_SCRIPT,
|
|
11
19
|
VERSION_SWITCHER_VISIBILITY_STYLE,
|
|
12
20
|
VersionBanner,
|
|
13
|
-
VersionSwitcher
|
|
21
|
+
VersionSwitcher,
|
|
22
|
+
computeVersionSwitcherState,
|
|
23
|
+
switchLocaleHref
|
|
14
24
|
};
|
|
@@ -2,6 +2,46 @@
|
|
|
2
2
|
/** @jsxImportSource preact */
|
|
3
3
|
import type { VNode } from "preact";
|
|
4
4
|
import type { LocaleLink } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* The minimal project config the client re-wire needs to reproduce
|
|
7
|
+
* `getPathForLocale`'s output from the live pathname. Emitted as `data-*`
|
|
8
|
+
* attributes on the switcher container so {@link LANGUAGE_SWITCHER_INIT_SCRIPT}
|
|
9
|
+
* can read it without any serialized props.
|
|
10
|
+
*/
|
|
11
|
+
export interface LanguageSwitcherConfig {
|
|
12
|
+
/** Normalized site base with no trailing slash (`""` for a root site). */
|
|
13
|
+
base: string;
|
|
14
|
+
/** The project's default locale (rendered without a locale prefix). */
|
|
15
|
+
defaultLocale: string;
|
|
16
|
+
/** Whether the project appends trailing slashes to page URLs. */
|
|
17
|
+
trailingSlash: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Client-side re-computation of a locale-switched href from the *current*
|
|
21
|
+
* pathname. This is a self-contained port of `getPathForLocale` (+ its
|
|
22
|
+
* `stripBase` / version-prefix split / `withBase` / `applyTrailingSlash`
|
|
23
|
+
* dependencies) from `url-helpers`. It MUST stay behaviourally identical to
|
|
24
|
+
* that function — pinned by the drift-guard test in
|
|
25
|
+
* `__tests__/language-switcher.test.tsx`, which asserts equality against the
|
|
26
|
+
* real `buildLocaleLinks` output across a case table.
|
|
27
|
+
*
|
|
28
|
+
* It is deliberately self-contained (no references to module-scope helpers)
|
|
29
|
+
* because {@link LANGUAGE_SWITCHER_INIT_SCRIPT} embeds it verbatim via
|
|
30
|
+
* `.toString()`, so it must be valid as a standalone function in the browser.
|
|
31
|
+
*/
|
|
32
|
+
export declare function switchLocaleHref(pathname: string, config: LanguageSwitcherConfig, currentLang: string, targetLang: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Inline init script that keeps the switcher's per-page hrefs correct inside a
|
|
35
|
+
* persisted header. Mounted once wherever a header is rendered (see
|
|
36
|
+
* `header.tsx`), it registers a document-level `zfb:after-swap` listener on
|
|
37
|
+
* first load and re-runs {@link switchLocaleHref} against the live pathname —
|
|
38
|
+
* the same rebind-on-navigate contract `VERSION_SWITCHER_INIT_SCRIPT` uses.
|
|
39
|
+
*
|
|
40
|
+
* `window[FLAG]` makes it idempotent: the tag may re-execute on a hard reload
|
|
41
|
+
* or a cross-locale header repaint, but the listener is registered exactly
|
|
42
|
+
* once per page lifetime.
|
|
43
|
+
*/
|
|
44
|
+
export declare const LANGUAGE_SWITCHER_INIT_SCRIPT: string;
|
|
5
45
|
export interface LanguageSwitcherProps {
|
|
6
46
|
/**
|
|
7
47
|
* Pre-built locale links, ordered as they should appear in the bar.
|
|
@@ -9,6 +49,19 @@ export interface LanguageSwitcherProps {
|
|
|
9
49
|
* `buildLocaleLinks(currentPath, currentLang)` helper before passing.
|
|
10
50
|
*/
|
|
11
51
|
links: LocaleLink[];
|
|
52
|
+
/**
|
|
53
|
+
* When provided, the switcher container emits `data-*` config so
|
|
54
|
+
* {@link LANGUAGE_SWITCHER_INIT_SCRIPT} can re-compute each anchor's href
|
|
55
|
+
* from the live pathname after a same-locale SPA navigation (the header is
|
|
56
|
+
* persisted, so SSR hrefs would otherwise go stale). Omit for a purely
|
|
57
|
+
* static / no-JS render.
|
|
58
|
+
*/
|
|
59
|
+
config?: LanguageSwitcherConfig;
|
|
60
|
+
/**
|
|
61
|
+
* The active locale code, recorded as `data-current-locale` so the re-wire
|
|
62
|
+
* script knows which locale segment to strip. Only read when `config` is set.
|
|
63
|
+
*/
|
|
64
|
+
currentLocale?: string;
|
|
12
65
|
}
|
|
13
66
|
/**
|
|
14
67
|
* Inline locale switcher rendered in the header / sidebar footer.
|
|
@@ -17,5 +70,5 @@ export interface LanguageSwitcherProps {
|
|
|
17
70
|
* template's `localeLinks.length > 1 &&` guard so call-sites can mount
|
|
18
71
|
* the component unconditionally).
|
|
19
72
|
*/
|
|
20
|
-
export declare function LanguageSwitcher({ links }: LanguageSwitcherProps): VNode | null;
|
|
73
|
+
export declare function LanguageSwitcher({ links, config, currentLocale, }: LanguageSwitcherProps): VNode | null;
|
|
21
74
|
export type { LocaleLink };
|
|
@@ -1,8 +1,83 @@
|
|
|
1
1
|
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { Fragment } from "preact";
|
|
3
|
-
|
|
3
|
+
import { AFTER_NAVIGATE_EVENT } from "../transitions/index.js";
|
|
4
|
+
function switchLocaleHref(pathname, config, currentLang, targetLang) {
|
|
5
|
+
const normalizedBase = config.base;
|
|
6
|
+
const defaultLocale = config.defaultLocale;
|
|
7
|
+
const trailingSlash = config.trailingSlash;
|
|
8
|
+
const stripBase = (path) => {
|
|
9
|
+
if (normalizedBase === "") return path;
|
|
10
|
+
if (path === normalizedBase) return "/";
|
|
11
|
+
return path.indexOf(normalizedBase + "/") === 0 ? path.slice(normalizedBase.length) : path;
|
|
12
|
+
};
|
|
13
|
+
const applyTrailingSlash = (url) => {
|
|
14
|
+
if (!trailingSlash) return url;
|
|
15
|
+
if (url.charAt(url.length - 1) === "/") return url;
|
|
16
|
+
const suffixIdx = url.search(/[?#]/);
|
|
17
|
+
const pathPart = suffixIdx >= 0 ? url.slice(0, suffixIdx) : url;
|
|
18
|
+
const suffix = suffixIdx >= 0 ? url.slice(suffixIdx) : "";
|
|
19
|
+
if (pathPart.charAt(pathPart.length - 1) === "/") return url;
|
|
20
|
+
const segments = pathPart.split("/");
|
|
21
|
+
const lastSegment = segments[segments.length - 1] || "";
|
|
22
|
+
if (/\.[a-zA-Z]\w*$/.test(lastSegment)) return url;
|
|
23
|
+
return pathPart + "/" + suffix;
|
|
24
|
+
};
|
|
25
|
+
const withBase = (path) => {
|
|
26
|
+
const raw = normalizedBase === "" ? path : normalizedBase + (path.charAt(0) === "/" ? path : "/" + path);
|
|
27
|
+
return applyTrailingSlash(raw);
|
|
28
|
+
};
|
|
29
|
+
const stripped = stripBase(pathname);
|
|
30
|
+
const versionMatch = stripped.match(/^(\/v\/[^/]+)(\/.*|$)/);
|
|
31
|
+
const versionPrefix = versionMatch ? versionMatch[1] || "" : "";
|
|
32
|
+
let relativePath = versionMatch ? versionMatch[2] || "/" : stripped;
|
|
33
|
+
if (currentLang !== defaultLocale) {
|
|
34
|
+
relativePath = relativePath.replace(
|
|
35
|
+
new RegExp("^/" + currentLang + "(?:/|$)"),
|
|
36
|
+
"/"
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
if (targetLang !== defaultLocale) {
|
|
40
|
+
relativePath = "/" + targetLang + relativePath;
|
|
41
|
+
}
|
|
42
|
+
return withBase(versionPrefix + relativePath);
|
|
43
|
+
}
|
|
44
|
+
const LANGUAGE_SWITCHER_INIT_SCRIPT = `(function(){
|
|
45
|
+
var FLAG="__zdLanguageSwitcherInit";
|
|
46
|
+
if(window[FLAG])return;
|
|
47
|
+
window[FLAG]=true;
|
|
48
|
+
var switchLocaleHref=${switchLocaleHref.toString()};
|
|
49
|
+
function rewire(){
|
|
50
|
+
var containers=document.querySelectorAll("[data-language-switcher]");
|
|
51
|
+
for(var i=0;i<containers.length;i++){
|
|
52
|
+
var c=containers[i];
|
|
53
|
+
var config={base:c.getAttribute("data-base")||"",defaultLocale:c.getAttribute("data-default-locale")||"",trailingSlash:c.getAttribute("data-trailing-slash")==="true"};
|
|
54
|
+
var currentLang=c.getAttribute("data-current-locale")||config.defaultLocale;
|
|
55
|
+
var anchors=c.querySelectorAll("a[lang]");
|
|
56
|
+
for(var j=0;j<anchors.length;j++){
|
|
57
|
+
var a=anchors[j];
|
|
58
|
+
var target=a.getAttribute("lang");
|
|
59
|
+
if(!target)continue;
|
|
60
|
+
a.setAttribute("href",switchLocaleHref(window.location.pathname,config,currentLang,target));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
rewire();
|
|
65
|
+
document.addEventListener(${JSON.stringify(AFTER_NAVIGATE_EVENT)},rewire);
|
|
66
|
+
})();`;
|
|
67
|
+
function LanguageSwitcher({
|
|
68
|
+
links,
|
|
69
|
+
config,
|
|
70
|
+
currentLocale
|
|
71
|
+
}) {
|
|
4
72
|
if (links.length <= 1) return null;
|
|
5
|
-
|
|
73
|
+
const rewireAttrs = config ? {
|
|
74
|
+
"data-language-switcher": true,
|
|
75
|
+
"data-base": config.base,
|
|
76
|
+
"data-default-locale": config.defaultLocale,
|
|
77
|
+
"data-trailing-slash": String(config.trailingSlash),
|
|
78
|
+
"data-current-locale": currentLocale ?? config.defaultLocale
|
|
79
|
+
} : {};
|
|
80
|
+
return /* @__PURE__ */ jsx("div", { class: "flex items-center gap-x-hsp-xs text-small", ...rewireAttrs, children: links.map((link, i) => (
|
|
6
81
|
// Fragment is keyed via the locale code so the reconciler keeps
|
|
7
82
|
// the active/inactive nodes paired correctly across re-renders
|
|
8
83
|
// (e.g. when the active locale flips after navigation).
|
|
@@ -21,5 +96,7 @@ function LanguageSwitcher({ links }) {
|
|
|
21
96
|
)) });
|
|
22
97
|
}
|
|
23
98
|
export {
|
|
24
|
-
|
|
99
|
+
LANGUAGE_SWITCHER_INIT_SCRIPT,
|
|
100
|
+
LanguageSwitcher,
|
|
101
|
+
switchLocaleHref
|
|
25
102
|
};
|
|
@@ -38,7 +38,66 @@ export interface VersionSwitcherProps {
|
|
|
38
38
|
* unique.
|
|
39
39
|
*/
|
|
40
40
|
idSuffix?: string;
|
|
41
|
+
/**
|
|
42
|
+
* When provided, the switcher container emits `data-version-rewire` plus its
|
|
43
|
+
* config as `data-*`, and the menu anchors / trigger label carry the marker
|
|
44
|
+
* attributes {@link VERSION_SWITCHER_REWIRE_SCRIPT} needs. This lets the
|
|
45
|
+
* script recompute the per-page menu hrefs + active state + trigger label
|
|
46
|
+
* from the live pathname after a same-locale SPA navigation — the header is
|
|
47
|
+
* persisted (`data-zfb-transition-persist="header-${lang}"`), so its SSR menu
|
|
48
|
+
* values would otherwise go stale (zudolab/zudo-doc#2553).
|
|
49
|
+
*
|
|
50
|
+
* Omit for a static / no-JS render, e.g. the inline breadcrumb switcher,
|
|
51
|
+
* which lives in swapped content and is re-rendered fresh on every swap.
|
|
52
|
+
*/
|
|
53
|
+
rewireConfig?: VersionSwitcherRewireConfig;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The minimal project config the client re-wire needs to reproduce the header
|
|
57
|
+
* factory's per-page menu URLs from the live pathname. Emitted as `data-*`
|
|
58
|
+
* attributes on the switcher container so {@link VERSION_SWITCHER_REWIRE_SCRIPT}
|
|
59
|
+
* can read it without any serialized props.
|
|
60
|
+
*/
|
|
61
|
+
export interface VersionSwitcherRewireConfig {
|
|
62
|
+
/** Normalized site base with no trailing slash (`""` for a root site). */
|
|
63
|
+
base: string;
|
|
64
|
+
/** The project's default locale (rendered without a locale prefix). */
|
|
65
|
+
defaultLocale: string;
|
|
66
|
+
/** Whether the project appends trailing slashes to page URLs. */
|
|
67
|
+
trailingSlash: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* The active locale code — constant within a `header-${lang}` persist window,
|
|
70
|
+
* so it can be read once from the SSR container rather than re-derived.
|
|
71
|
+
*/
|
|
72
|
+
currentLocale: string;
|
|
73
|
+
}
|
|
74
|
+
/** The re-computed per-page menu state {@link computeVersionSwitcherState} returns. */
|
|
75
|
+
export interface VersionSwitcherState {
|
|
76
|
+
/** Href for the "Latest" entry, matching the SSR `latestUrl`. */
|
|
77
|
+
latestHref: string;
|
|
78
|
+
/** version slug → href for that version of the current page (SSR `versionUrls`). */
|
|
79
|
+
versionHrefs: Record<string, string>;
|
|
80
|
+
/** Active version slug, or `null` when the current page is on "latest". */
|
|
81
|
+
activeVersion: string | null;
|
|
41
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Client-side re-computation of the version-switcher's per-page menu state from
|
|
85
|
+
* the *current* pathname. This is a self-contained port of the header factory's
|
|
86
|
+
* URL logic (`createHeaderWithDefaults`'s `latestUrl` / `versionUrls` block, in
|
|
87
|
+
* turn `docsUrl` / `versionedDocsUrl` / `withBase` from `url-helpers`). It MUST
|
|
88
|
+
* stay behaviourally identical to that SSR path — pinned by the drift-guard test
|
|
89
|
+
* in `__tests__/version-switcher.test.tsx`, which asserts equality against the
|
|
90
|
+
* real `makeUrlHelpers` output across a case table.
|
|
91
|
+
*
|
|
92
|
+
* It is deliberately self-contained (no references to module-scope helpers)
|
|
93
|
+
* because {@link VERSION_SWITCHER_REWIRE_SCRIPT} embeds it verbatim via
|
|
94
|
+
* `.toString()`, so it must be valid as a standalone function in the browser.
|
|
95
|
+
*
|
|
96
|
+
* The `/docs/versions` listing is a standalone package route (`currentSlug`
|
|
97
|
+
* `undefined` on the SSR side), so — like the SSR factory — every menu href
|
|
98
|
+
* there collapses to `versionsPageUrl`; the exact-path check below mirrors that.
|
|
99
|
+
*/
|
|
100
|
+
export declare function computeVersionSwitcherState(pathname: string, config: VersionSwitcherRewireConfig, versionSlugs: string[]): VersionSwitcherState;
|
|
42
101
|
/**
|
|
43
102
|
* Canonical CSS rule that backs the responsive visibility of the
|
|
44
103
|
* version-switcher's host wrapper. **The component itself does NOT
|
|
@@ -98,4 +157,21 @@ export declare function VersionSwitcher(props: VersionSwitcherProps): VNode;
|
|
|
98
157
|
* vocabulary swap.
|
|
99
158
|
*/
|
|
100
159
|
export declare const VERSION_SWITCHER_INIT_SCRIPT: string;
|
|
160
|
+
/**
|
|
161
|
+
* Inline script that keeps the switcher's per-page menu correct inside a
|
|
162
|
+
* persisted header. Mounted once wherever a header renders a re-wireable
|
|
163
|
+
* VersionSwitcher (see `header.tsx`, gated on `hasVersions`), it recomputes each
|
|
164
|
+
* menu anchor's href, the active row, and the trigger label from the live
|
|
165
|
+
* pathname on first load and on `zfb:after-swap` — the same rebind-on-navigate
|
|
166
|
+
* contract `LANGUAGE_SWITCHER_INIT_SCRIPT` uses (zudolab/zudo-doc#2553).
|
|
167
|
+
*
|
|
168
|
+
* It only touches switchers carrying `data-version-rewire` (emitted when a
|
|
169
|
+
* {@link VersionSwitcherRewireConfig} is passed), so the inline breadcrumb
|
|
170
|
+
* switcher — re-rendered fresh on every swap — is left alone.
|
|
171
|
+
*
|
|
172
|
+
* `window[FLAG]` makes it idempotent: the tag may re-execute on a hard reload or
|
|
173
|
+
* a cross-locale header repaint, but the listener registers exactly once per
|
|
174
|
+
* page lifetime.
|
|
175
|
+
*/
|
|
176
|
+
export declare const VERSION_SWITCHER_REWIRE_SCRIPT: string;
|
|
101
177
|
export type { VersionEntry, VersionSwitcherLabels };
|
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { AFTER_NAVIGATE_EVENT } from "../transitions/page-events.js";
|
|
3
|
+
function computeVersionSwitcherState(pathname, config, versionSlugs) {
|
|
4
|
+
const normalizedBase = config.base;
|
|
5
|
+
const defaultLocale = config.defaultLocale;
|
|
6
|
+
const currentLocale = config.currentLocale;
|
|
7
|
+
const trailingSlash = config.trailingSlash;
|
|
8
|
+
const stripBase = (path) => {
|
|
9
|
+
if (normalizedBase === "") return path;
|
|
10
|
+
if (path === normalizedBase) return "/";
|
|
11
|
+
return path.indexOf(normalizedBase + "/") === 0 ? path.slice(normalizedBase.length) : path;
|
|
12
|
+
};
|
|
13
|
+
const applyTrailingSlash = (url) => {
|
|
14
|
+
if (!trailingSlash) return url;
|
|
15
|
+
if (url.charAt(url.length - 1) === "/") return url;
|
|
16
|
+
const suffixIdx = url.search(/[?#]/);
|
|
17
|
+
const pathPart = suffixIdx >= 0 ? url.slice(0, suffixIdx) : url;
|
|
18
|
+
const suffix = suffixIdx >= 0 ? url.slice(suffixIdx) : "";
|
|
19
|
+
if (pathPart.charAt(pathPart.length - 1) === "/") return url;
|
|
20
|
+
const segments = pathPart.split("/");
|
|
21
|
+
const lastSegment = segments[segments.length - 1] || "";
|
|
22
|
+
if (/\.[a-zA-Z]\w*$/.test(lastSegment)) return url;
|
|
23
|
+
return pathPart + "/" + suffix;
|
|
24
|
+
};
|
|
25
|
+
const withBase = (path) => {
|
|
26
|
+
const raw = normalizedBase === "" ? path : normalizedBase + (path.charAt(0) === "/" ? path : "/" + path);
|
|
27
|
+
return applyTrailingSlash(raw);
|
|
28
|
+
};
|
|
29
|
+
const versionsPageUrl = withBase(
|
|
30
|
+
currentLocale === defaultLocale ? "/docs/versions" : "/" + currentLocale + "/docs/versions"
|
|
31
|
+
);
|
|
32
|
+
const stripped = stripBase(pathname);
|
|
33
|
+
const versionMatch = stripped.match(/^\/v\/([^/]+)(\/.*|$)/);
|
|
34
|
+
const rest = versionMatch ? versionMatch[2] || "/" : stripped;
|
|
35
|
+
let localeStripped = rest;
|
|
36
|
+
if (currentLocale !== defaultLocale) {
|
|
37
|
+
localeStripped = rest.replace(
|
|
38
|
+
new RegExp("^/" + currentLocale + "(?:/|$)"),
|
|
39
|
+
"/"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
const isVersionsIndex = localeStripped === "/docs/versions" || localeStripped === "/docs/versions/";
|
|
43
|
+
const isDocPage = /^\/docs(\/|$)/.test(localeStripped) && !isVersionsIndex;
|
|
44
|
+
const activeVersion = isDocPage && versionMatch ? versionMatch[1] || null : null;
|
|
45
|
+
const versionHrefs = {};
|
|
46
|
+
let latestHref;
|
|
47
|
+
if (isDocPage) {
|
|
48
|
+
latestHref = withBase(rest);
|
|
49
|
+
for (let i = 0; i < versionSlugs.length; i++) {
|
|
50
|
+
const slug = versionSlugs[i];
|
|
51
|
+
if (slug) versionHrefs[slug] = withBase("/v/" + slug + rest);
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
latestHref = versionsPageUrl;
|
|
55
|
+
for (let i = 0; i < versionSlugs.length; i++) {
|
|
56
|
+
const slug = versionSlugs[i];
|
|
57
|
+
if (slug) versionHrefs[slug] = versionsPageUrl;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { latestHref, versionHrefs, activeVersion };
|
|
61
|
+
}
|
|
3
62
|
function cls(...parts) {
|
|
4
63
|
return parts.filter(Boolean).join(" ");
|
|
5
64
|
}
|
|
@@ -34,12 +93,21 @@ function VersionSwitcher(props) {
|
|
|
34
93
|
versionUrls,
|
|
35
94
|
unavailableVersions,
|
|
36
95
|
labels,
|
|
37
|
-
idSuffix = ""
|
|
96
|
+
idSuffix = "",
|
|
97
|
+
rewireConfig
|
|
38
98
|
} = props;
|
|
39
99
|
const menuId = `version-menu${idSuffix ? `-${idSuffix}` : ""}`;
|
|
40
100
|
const isLatest = !currentVersion;
|
|
41
101
|
const triggerLabel = isLatest ? labels.latest : versions.find((v) => v.slug === currentVersion)?.label ?? currentVersion;
|
|
42
|
-
|
|
102
|
+
const rewire = rewireConfig != null;
|
|
103
|
+
const rewireAttrs = rewireConfig ? {
|
|
104
|
+
"data-version-rewire": true,
|
|
105
|
+
"data-base": rewireConfig.base,
|
|
106
|
+
"data-default-locale": rewireConfig.defaultLocale,
|
|
107
|
+
"data-trailing-slash": String(rewireConfig.trailingSlash),
|
|
108
|
+
"data-current-locale": rewireConfig.currentLocale
|
|
109
|
+
} : {};
|
|
110
|
+
return /* @__PURE__ */ jsxs("div", { class: "version-switcher relative", "data-version-switcher": true, ...rewireAttrs, children: [
|
|
43
111
|
/* @__PURE__ */ jsxs(
|
|
44
112
|
"button",
|
|
45
113
|
{
|
|
@@ -53,7 +121,14 @@ function VersionSwitcher(props) {
|
|
|
53
121
|
labels.switcher,
|
|
54
122
|
":"
|
|
55
123
|
] }),
|
|
56
|
-
/* @__PURE__ */ jsx(
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
"span",
|
|
126
|
+
{
|
|
127
|
+
class: "font-medium",
|
|
128
|
+
"data-version-trigger-label": rewire ? true : void 0,
|
|
129
|
+
children: triggerLabel
|
|
130
|
+
}
|
|
131
|
+
),
|
|
57
132
|
/* @__PURE__ */ jsx(ChevronDownIcon, {})
|
|
58
133
|
]
|
|
59
134
|
}
|
|
@@ -74,6 +149,7 @@ function VersionSwitcher(props) {
|
|
|
74
149
|
"block px-hsp-md py-vsp-2xs text-small hover:bg-accent/10 hover:underline focus-visible:underline",
|
|
75
150
|
isLatest ? "font-bold text-accent" : "text-fg"
|
|
76
151
|
),
|
|
152
|
+
"data-version-latest": rewire ? true : void 0,
|
|
77
153
|
children: labels.latest
|
|
78
154
|
}
|
|
79
155
|
) }),
|
|
@@ -90,6 +166,7 @@ function VersionSwitcher(props) {
|
|
|
90
166
|
"block px-hsp-md py-vsp-2xs text-small hover:bg-accent/10 hover:underline focus-visible:underline",
|
|
91
167
|
isActive ? "font-bold text-accent" : "text-fg"
|
|
92
168
|
),
|
|
169
|
+
"data-version-slug": rewire ? v.slug : void 0,
|
|
93
170
|
children: v.label
|
|
94
171
|
}
|
|
95
172
|
) : /* @__PURE__ */ jsx(
|
|
@@ -100,6 +177,7 @@ function VersionSwitcher(props) {
|
|
|
100
177
|
tabindex: -1,
|
|
101
178
|
class: "block px-hsp-md py-vsp-2xs text-small text-muted/50 cursor-not-allowed pointer-events-none",
|
|
102
179
|
title: labels.unavailable,
|
|
180
|
+
"data-version-slug": rewire ? v.slug : void 0,
|
|
103
181
|
children: v.label
|
|
104
182
|
}
|
|
105
183
|
) }, v.slug);
|
|
@@ -150,8 +228,63 @@ toggle.focus();
|
|
|
150
228
|
initVersionSwitcher();
|
|
151
229
|
document.addEventListener(${JSON.stringify(AFTER_NAVIGATE_EVENT)},initVersionSwitcher);
|
|
152
230
|
})();`;
|
|
231
|
+
const VERSION_SWITCHER_REWIRE_SCRIPT = `(function(){
|
|
232
|
+
var FLAG="__zdVersionSwitcherRewire";
|
|
233
|
+
if(window[FLAG])return;
|
|
234
|
+
window[FLAG]=true;
|
|
235
|
+
var computeVersionSwitcherState=${computeVersionSwitcherState.toString()};
|
|
236
|
+
function setActive(a,active){
|
|
237
|
+
a.classList.toggle("font-bold",active);
|
|
238
|
+
a.classList.toggle("text-accent",active);
|
|
239
|
+
a.classList.toggle("text-fg",!active);
|
|
240
|
+
if(active){a.setAttribute("aria-current","page");}else{a.removeAttribute("aria-current");}
|
|
241
|
+
}
|
|
242
|
+
function rewire(){
|
|
243
|
+
var containers=document.querySelectorAll("[data-version-rewire]");
|
|
244
|
+
for(var i=0;i<containers.length;i++){
|
|
245
|
+
var c=containers[i];
|
|
246
|
+
var config={base:c.getAttribute("data-base")||"",defaultLocale:c.getAttribute("data-default-locale")||"",trailingSlash:c.getAttribute("data-trailing-slash")==="true",currentLocale:c.getAttribute("data-current-locale")||""};
|
|
247
|
+
var versionAnchors=c.querySelectorAll("[data-version-slug]");
|
|
248
|
+
var slugs=[];
|
|
249
|
+
for(var j=0;j<versionAnchors.length;j++){
|
|
250
|
+
var s=versionAnchors[j].getAttribute("data-version-slug");
|
|
251
|
+
if(s)slugs.push(s);
|
|
252
|
+
}
|
|
253
|
+
var state=computeVersionSwitcherState(window.location.pathname,config,slugs);
|
|
254
|
+
var latest=c.querySelector("[data-version-latest]");
|
|
255
|
+
if(latest){
|
|
256
|
+
latest.setAttribute("href",state.latestHref);
|
|
257
|
+
setActive(latest,state.activeVersion===null);
|
|
258
|
+
}
|
|
259
|
+
for(var k=0;k<versionAnchors.length;k++){
|
|
260
|
+
var a=versionAnchors[k];
|
|
261
|
+
var slug=a.getAttribute("data-version-slug");
|
|
262
|
+
if(!slug)continue;
|
|
263
|
+
var href=state.versionHrefs[slug];
|
|
264
|
+
if(href!=null)a.setAttribute("href",href);
|
|
265
|
+
if(!a.hasAttribute("aria-disabled"))setActive(a,state.activeVersion===slug);
|
|
266
|
+
}
|
|
267
|
+
var label=c.querySelector("[data-version-trigger-label]");
|
|
268
|
+
if(label){
|
|
269
|
+
if(state.activeVersion===null){
|
|
270
|
+
if(latest)label.textContent=latest.textContent;
|
|
271
|
+
}else{
|
|
272
|
+
var activeLabel=null;
|
|
273
|
+
for(var m=0;m<versionAnchors.length;m++){
|
|
274
|
+
if(versionAnchors[m].getAttribute("data-version-slug")===state.activeVersion){activeLabel=versionAnchors[m].textContent;break;}
|
|
275
|
+
}
|
|
276
|
+
label.textContent=activeLabel!=null?activeLabel:state.activeVersion;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
rewire();
|
|
282
|
+
document.addEventListener(${JSON.stringify(AFTER_NAVIGATE_EVENT)},rewire);
|
|
283
|
+
})();`;
|
|
153
284
|
export {
|
|
154
285
|
VERSION_SWITCHER_INIT_SCRIPT,
|
|
286
|
+
VERSION_SWITCHER_REWIRE_SCRIPT,
|
|
155
287
|
VERSION_SWITCHER_VISIBILITY_STYLE,
|
|
156
|
-
VersionSwitcher
|
|
288
|
+
VersionSwitcher,
|
|
289
|
+
computeVersionSwitcherState
|
|
157
290
|
};
|
|
@@ -97,11 +97,14 @@ function downgradeRepoRelativeLinks(content) {
|
|
|
97
97
|
function findClaudeMdFiles(dir, excludeDirs) {
|
|
98
98
|
const results = [];
|
|
99
99
|
if (!fs.existsSync(dir)) return results;
|
|
100
|
+
const excludes = excludeDirs.map(
|
|
101
|
+
(d) => d.endsWith(path.sep) ? d.slice(0, -path.sep.length) : d
|
|
102
|
+
);
|
|
100
103
|
for (const item of fs.readdirSync(dir)) {
|
|
101
104
|
if (item === "node_modules") continue;
|
|
102
105
|
if (item.startsWith(".")) continue;
|
|
103
106
|
const itemPath = path.join(dir, item);
|
|
104
|
-
if (
|
|
107
|
+
if (excludes.some((d) => itemPath === d || itemPath.startsWith(d + path.sep))) continue;
|
|
105
108
|
let stat;
|
|
106
109
|
try {
|
|
107
110
|
stat = fs.lstatSync(itemPath);
|
|
@@ -109,7 +112,7 @@ function findClaudeMdFiles(dir, excludeDirs) {
|
|
|
109
112
|
continue;
|
|
110
113
|
}
|
|
111
114
|
if (stat.isDirectory()) {
|
|
112
|
-
results.push(...findClaudeMdFiles(itemPath,
|
|
115
|
+
results.push(...findClaudeMdFiles(itemPath, excludes));
|
|
113
116
|
} else if (stat.isFile() && item === "CLAUDE.md") {
|
|
114
117
|
results.push(itemPath);
|
|
115
118
|
}
|
|
@@ -12,10 +12,27 @@ export interface ClaudeResourcesPluginOptions {
|
|
|
12
12
|
*/
|
|
13
13
|
claudeDir: string;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
* and
|
|
15
|
+
* Anchor for resolving the relative `claudeDir`, `docsDir`, and `scanRoot`
|
|
16
|
+
* paths, and the default value of `scanRoot`. Defaults to `process.cwd()`.
|
|
17
|
+
*
|
|
18
|
+
* Note: this does NOT itself decide where `CLAUDE.md` discovery walks — that
|
|
19
|
+
* is `scanRoot` (which defaults to this). Set `scanRoot` to widen discovery
|
|
20
|
+
* (e.g. a subdirectory doc site scanning its repo root) without moving the
|
|
21
|
+
* output base, which stays anchored here.
|
|
17
22
|
*/
|
|
18
23
|
projectRoot?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Root for `CLAUDE.md` discovery and the base for the generated pages'
|
|
26
|
+
* relative-path titles/slugs. Defaults to `projectRoot`. Resolved against
|
|
27
|
+
* `projectRoot` when relative (absolute allowed).
|
|
28
|
+
*
|
|
29
|
+
* Scope: governs `CLAUDE.md` discovery ONLY. Commands, skills, and agents
|
|
30
|
+
* always come from `claudeDir` and are unaffected by `scanRoot`. Decoupling
|
|
31
|
+
* this from `projectRoot` lets a doc site in a repo subdirectory scan
|
|
32
|
+
* repo-wide `CLAUDE.md` files while still writing generated pages into its
|
|
33
|
+
* own content collection (see #2558).
|
|
34
|
+
*/
|
|
35
|
+
scanRoot?: string;
|
|
19
36
|
/**
|
|
20
37
|
* Output directory for generated MDX pages, resolved against
|
|
21
38
|
* `projectRoot` when relative. Defaults to `src/content/docs` to
|
|
@@ -12,9 +12,10 @@ function claudeResourcesPlugin(options) {
|
|
|
12
12
|
function runClaudeResourcesPreStep(options) {
|
|
13
13
|
const projectRoot = path.resolve(options.projectRoot ?? process.cwd());
|
|
14
14
|
const claudeDir = path.isAbsolute(options.claudeDir) ? options.claudeDir : path.resolve(projectRoot, options.claudeDir);
|
|
15
|
+
const scanRoot = options.scanRoot === void 0 ? projectRoot : path.isAbsolute(options.scanRoot) ? options.scanRoot : path.resolve(projectRoot, options.scanRoot);
|
|
15
16
|
const docsDirInput = options.docsDir ?? "src/content/docs";
|
|
16
17
|
const docsDir = path.isAbsolute(docsDirInput) ? docsDirInput : path.resolve(projectRoot, docsDirInput);
|
|
17
|
-
return generateClaudeResourcesDocs({ claudeDir, projectRoot, docsDir });
|
|
18
|
+
return generateClaudeResourcesDocs({ claudeDir, projectRoot: scanRoot, docsDir });
|
|
18
19
|
}
|
|
19
20
|
export {
|
|
20
21
|
CLAUDE_RESOURCES_PLUGIN_NAME,
|
|
@@ -10,10 +10,12 @@ const plugin = {
|
|
|
10
10
|
);
|
|
11
11
|
}
|
|
12
12
|
const projectRootOpt = ctx.options["projectRoot"];
|
|
13
|
+
const scanRootOpt = ctx.options["scanRoot"];
|
|
13
14
|
const docsDirOpt = ctx.options["docsDir"];
|
|
14
15
|
const result = await runClaudeResourcesPreStep({
|
|
15
16
|
claudeDir,
|
|
16
17
|
projectRoot: typeof projectRootOpt === "string" ? projectRootOpt : ctx.projectRoot,
|
|
18
|
+
scanRoot: typeof scanRootOpt === "string" ? scanRootOpt : void 0,
|
|
17
19
|
docsDir: typeof docsDirOpt === "string" ? docsDirOpt : "src/content/docs"
|
|
18
20
|
});
|
|
19
21
|
ctx.logger.info(
|
package/dist/preset.d.ts
CHANGED
|
@@ -50,6 +50,11 @@ export interface PresetVersionConfig {
|
|
|
50
50
|
export interface PresetClaudeResourcesConfig {
|
|
51
51
|
claudeDir: string;
|
|
52
52
|
projectRoot?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Root for `CLAUDE.md` discovery; defaults to `projectRoot`. Decouples
|
|
55
|
+
* repo-wide scanning from the output base for subdirectory doc sites (#2558).
|
|
56
|
+
*/
|
|
57
|
+
scanRoot?: string;
|
|
53
58
|
}
|
|
54
59
|
/**
|
|
55
60
|
* The subset of `settings` the preset reads. Any concrete `typeof settings`
|
package/dist/preset.js
CHANGED
|
@@ -171,6 +171,7 @@ function buildPlugins(settings, routeContext) {
|
|
|
171
171
|
options: {
|
|
172
172
|
claudeDir: settings.claudeResources.claudeDir,
|
|
173
173
|
projectRoot: settings.claudeResources.projectRoot,
|
|
174
|
+
scanRoot: settings.claudeResources.scanRoot,
|
|
174
175
|
docsDir: settings.docsDir
|
|
175
176
|
}
|
|
176
177
|
}
|
package/dist/safelist.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/* generated by gen-safelist.mjs — do not edit by hand */
|
|
2
|
-
@source inline("-link -mb-px -ml-hsp-sm -noscript -translate-x-full 2xl:w-[24px] [&::-webkit-details-marker]:hidden [&_a]:text-accent [&_a]:underline [&_nav]:mb-0 [data-admonition] [data-kbd-shortcut] [doc-history-meta] [doc-history] [doc-layout] [llms-txt] a a2 abbr about above absent absolute accent across activated active actual 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-same-origin allow-scripts alone already already-executed already-picked an anchor anchored and and/or animate-spin announce antialiased any application/json application/xml applies apply-css-vars approach are area arg aria-atomic aria-busy aria-controls aria-current aria-disabled aria-expanded aria-haspopup aria-hidden aria-label aria-live aria-orientation aria-pressed aria-selected aria-valuemax aria-valuemin aria-valuenow arm arms arrows article as asc aside aspect-[1200/630] aspect-square asset- assets assistant async at attach attribute attributes auto autogenerated available 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 base64 based batch be because before below best 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-transparent bg-warning/10 bg-warning/5 bi bigint bin blank blanks blob block blockquote blocks blur 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-info/30 border-l border-l-0 border-l-[3px] border-left-width border-muted border-none border-r border-radius border-solid border-t border-t-[2px] border-t-[3px] border-transparent border-warning/30 border-width border-y both bottom-vsp-xl box-border br brand breadcrumb:end breadcrumb:start break-words browser browsers btn bug build bundler but button buttons by bypassed byte-identical bytes cached call caller calls can cancellation cannot canonical canvas caption card card-grid carry cases cat-nav- catch category catppuccin-latte caught caution center center/contain ch chains change changed changes checkbox child chrome ci circle cite class class-less claude claude-agents claude-commands claude-md claude-skills cleaned clear clear-css-vars clearing click client client-router client-side clip clobbering close closed closing code code-block-sr-announce code-group code-group-panel col col-resize colgroup collision color color-scheme color-scheme-changed color-scheme-provider color-tweak colorization colors column comma command commands comment commit compare component component:github-link component:language-switcher component:search component:theme-toggle component:version-switcher compute computed concrete configuration configure configured confuse connect const construction consumer consumes container containers containing content content-admonition content-link content-type content-wrapper:end content-wrapper:start contents context controller controls converts copy corners correct correctly corrupt count covered covers cp crashes created cross-component crumb- cs css ctx cur current cursor cursor-not-allowed cursor-pointer custom danger dark data data-active data-admonition data-base data-close-search data-group-id data-header data-header-logo data-header-nav data-header-right data-kbd-shortcut 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-open-search data-pan-active data-processed 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-tab-btn data-tab-default data-tab-label data-tab-value data-tabs data-taglist-group data-testid data-theme data-theme/style data-variant data-version-banner data-version-menu data-version-switcher data-version-toggle data-zd-nosidebar data-zd-toc data-zfb-transition-persist dd decimal declare decoration decoration-muted default defaults del delegated dependency depth desc description design design-token-panel design-token-trigger desktop desktop-sidebar desktop-sidebar-toggle desktop-sidebar-toggle-island destructive detach detached details deterministic develop dfn diagram diagrams dialog dieser diff diff-line-added diff-line-content diff-line-empty diff-line-num diff-line-removed diff-row dir directly directories directory disabled disabled:opacity-50 disc display:none dist 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 docs docs- docs-v- document document-level documented does double-registration drag draggable drop dropdown dropdown-child dropdown-parent dropdowns dt duration-150 duration-200 during dynamically e2e each ease-in-out edge eject ejected el element elements els else em embedded emit emitting empty empty/undefined en enable end enlarged entire entities entries entry error escape escaped even eventually every exactly excerpt excludes existing exists exit expected export extends factories failed fall fallback fallbacks falls false family fast feature feed fg fields fieldset figcaption figure file fill fills finally find fire fires first fixed fixed-width fixtures flag flash flat flex flex-1 flex-col flex-wrap flip flipping flips flush-left focus focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2 focus-visible:underline focus:border-accent focus:outline-none focus:underline font font-bold font-family font-medium font-mono font-semibold font-weight footer footer- for form found free fresh from frontmatter frontmatter-preview frozen fs-extra full fully function further g gap-[0.3em] gap-[clamp(1.5rem,3vw,4rem)] gap-hsp-2xs gap-hsp-md gap-hsp-sm gap-hsp-xl gap-hsp-xs gap-vsp-2xs 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-lg gap-y-vsp-md gap-y-vsp-xs gaps gate generate generation genuine geometry get github github-link go got graph gray-matter grid grid-cols-1 grid-cols-2 group group-focus-visible:text-accent group-focus-visible:underline group-focus-within:block group-hover:bg-fg group-hover:block group-hover:text-accent group-hover:text-bg group-hover:underline group-open:rotate-90 guard h-[0.5rem] h-[0.625rem] h-[0.875rem] h-[1.125rem] h-[1.25rem] h-[1.575rem] h-[14px] h-[1em] h-[1lh] h-[2rem] h-[3.5rem] h-[3rem] 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 h2s h3 h4 h5 h6 half hand hand-copied handle handled handler handlers has hash-link have head head-links head-scripts header header- header-call:end header-call:start heading heading-h2 heading-h3 heading-h4 headings height here hex hidden highlight highlighter history hit 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-surface hover:border-accent hover:border-accent-hover hover:border-fg hover:text-accent hover:text-accent-hover hover:text-fg hover:underline hr href hrefs html i i18n/theme. i2 i3 i4 icon identical idle idx if iframe image image-enlarge image/png img implementation import important imports in inactive inbox includes index info inherit inherited initial initialised injected inline inline-block inline-flex inner input input-clear ins inset-0 inside install instance instanceof instead instructions intended intent into invalid inverse inversion invoke is issues it italic item item- items items-baseline items-center items-start its itself javascript justify-between justify-center justify-end justify-start katex kbd keep keeps kept keyboard keyboard-shortcut keydown keystroke keywords khroma label landing language-switcher last:border-b-0 later launch layout leading-relaxed leading-snug leading-tight leaf- leak leaves leaving left left-0 left:calc legend legitimate 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-[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: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 light like likely line linger link link- links list-disc list-none listener literal literals lives llms llms-txt load local locale locales log longest-match look lostpointercapture lower luminance m m-0 m21 m6 main make malformed malicious maps mark marks matches matching math math-display math-inline max max-h-[80vh] max-h-[85vh] max-h-[90vh] max-h-full max-h-none max-w-[46rem] max-w-[85%] max-w-[85vw] max-w-[90vw] max-w-[clamp(50rem,75vw,90rem)] max-w-full max-w-none max-width may mb-0 mb-vsp-2xs mb-vsp-lg mb-vsp-md mb-vsp-sm mb-vsp-xl mb-vsp-xs measures measuring mechanism menu merged mermaid message messages meta meta-knob metadata migration min-h-[60vh] min-h-[calc(100vh-3.5rem)] min-h-screen min-w-0 min-w-[10rem] min-w-[8rem] mirror mirrors missing ml-auto ml-hsp-2xl ml-hsp-lg ml-hsp-sm ml-hsp-xl mod mode module mounted mouseenter mouseleave mr-hsp-sm mt-0 mt-vsp-2xl mt-vsp-2xs mt-vsp-3xs mt-vsp-lg mt-vsp-md mt-vsp-sm mt-vsp-xl must mutates mutation mutations mx-auto my-vsp-lg my-vsp-md name named native nav nav-active nav-card- nav/doc navigating navigation navigations near needs nested new newly-swapped next no no-enlarge no-op no-repeat no-underline node node:buffer node:fs node:fs/promises node:module node:path nodes nofollow noindex non-empty non-light-dark non-persisted non-string none noopener noreferrer normal noscript not not-object note now null number numeric object object-contain observe observer occurred of off og:description og:image og:image:alt og:image:height og:image:width og:title og:type og:url ol old older on once one only onto opacity-60 open open/close option or original other others out outline-none over overflow overflow-auto overflow-hidden overflow-x-auto overflow-y-auto overflow-y:auto overwrite own p p-0 p-hsp-lg p-hsp-md p-hsp-sm p-hsp-xl package package-owned packages padding page page-loading page-loading-overlay page-loading-spinner page-navigate-end page-title pages pages/. paint paint-and-read pan panel panels paren-balance-aware parent parse parsed pass passed passes path paths pattern pb-[50vh] pb-vsp-md pb-vsp-xl pb-vsp-xs per per-link permanently persisted pi pick picked picks picocolors pins pipelines pl-[1.25rem] pl-hsp-lg pl-hsp-sm pl-hsp-xl place placeholder placeholder:text-muted plain plural plus pnpm pointer-events-none pointercancel pointerdown pointermove pointerup polite polygon polyline populates port position position:fixed pr-[4px] pr-hsp-lg pr-hsp-md pr-hsp-sm pr-hsp-xl pre pre-lowercased preact preact/compat preact/hooks preact/jsx-runtime preconnect prefix preload pres preserving print produce produced produces production project 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 px px-hsp-2xl px-hsp-2xs px-hsp-lg px-hsp-md px-hsp-sm px-hsp-xl px-hsp-xs py-0 py-[2px] py-[calc(var(--spacing-vsp-xs)+0.15rem)] py-hsp-2xs 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 q query question r radius raw re-encode/decode re-querying re-render re-renders re-run re-running re-selects reach reached reaches read reading ready real real-value received receives recorded recovers redefine ref- references refetch refreshes regenerate regenerates regex reinit reinits relative reload relying remove removed render rendered renders reorder repaint repeated repeating replaced replaces repopulate requires reserved resize resize-x resolve resolved resolves response restore restores result result-click results results-area retry return returns revision revisions rewrite right right- right-0 ro robots role root rotate-180 rotate-90 round round-trip rounded rounded-[0.75rem] rounded-bl-[0.25rem] rounded-bl-[1rem] rounded-br-[0.25rem] rounded-br-[1rem] rounded-full rounded-lg rounded-t-[1rem] rounds route router routes routes-src running runs runtime s safe safer same same-locale samp scale scanned schema-mismatch schema-missing scheme scored script script- script-eval script-evaluation script-injection scripts scroll scrollbar scrolled scrollend search search-index section section- see sel-bg sel-fg select select-none self self-start semibold sentinel separator serialised serialize server server-rendered set sets setting setup shadow-[0_1px_3px_color-mix(in_srgb,var(--color-fg)_8%,transparent)] shadow-lg shape share shared sharing shiki ship shipped ships short shortcut should show shown shrink-0 sidebar sidebar- sidebar-toggle-island sidebar-tree-island signal similarity single singular site-search site-tree-nav-island sitemap- sites size skill skills skipping skips slash slug sm:border sm:border-muted sm:flex-row sm:grid-cols-2 sm:h-auto sm:items-center sm:justify-between sm:max-h-[80vh] sm:max-w-[52rem] sm:mx-auto sm:my-[10vh] sm:rounded-lg small smooth snapshot snapshots so soft soft-nav solid some somehow source sources space-y-vsp-2xs spacing span spans spec specifier specifiers splitter square sr-only src stale start state status stay staying sticky still stop stored stray string strings strip stripe stroke-linecap stroke-linejoin stroke-width strong stronger style style-attribute styled styles stylesheet sub subagents subsequent success successful summary sup surfaces survives svg swap swapped swaps synchronous synchronously syntactically syntect t tab tab-item tab-panel tabindex table tablist tabpanel tabs tabs-container tabs-content tabs-nav tag tag- tag-item- tags tags:audit take 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-heading text-info text-left text-micro text-muted text-muted/50 text-right text-small text-title text-warning text/plain textarea tfoot th that the thead their them theme theme-color theme-toggle theme/token then there these they this through throw tighten time tip title to toc toggle toggle-ai-chat toggle-design-token-panel toggles token tokens tolerates too toolbar top-0 top-[3.5rem] top-full top-level total touches tp tr tracked tracking-wider trade-off transition transition-[background,color,border-color] transition-[left,color] transition-colors transition-transform translate-x-0 translations transparent treats tree tree-child- tree-item- tree-top- trigger trigger:ai-chat trigger:design-token-panel triggers true truncate truncated try turn twitter:card twitter:creator twitter:description twitter:image twitter:site twitter:title two type typography u ul unavailable unchanged undefined under underline underlines understand unit-tested unknown unmaintained unobserve unreadable unrelated unreleased unset unsupported up uppercase usage use used user uses utf-8 utf8 utilities utility v v2 val value value-reader values var variable variant verbatim version version- version-menu version-switcher vertical via video viewport virtual:zudo-doc-chrome-bindings virtual:zudo-doc-route-context visible vitesse-dark vocabulary 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-[14px] w-[16px] w-[16rem] w-[18px] w-[1em] w-[280px] w-[2rem] w-[320px] w-[90vw] w-[var(--zd-sidebar-w)] w-dvw w-full w-icon-lg w-icon-md w-icon-sm w-icon-xs want warning was watching wbr wbr- we website weight went were what when where whereas whether which while whitespace-nowrap whitespace-pre whole wide-gamut width will with without word working worktrees would wrap wrapper wrappers written wrong wrote xl:flex xl:hidden y-scrollbar yet you your z-dropdown z-local-1 z-modal z-modal-backdrop z-sidebar z-toolbar zd-content zd-desktop-sidebar-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 zfb zfb:after-swap zfb:before-preparation zod zoom zudo-doc-design-tokens/v1 zudo-doc-sidebar-visible zudo-doc-sidebar-width zudo-doc-theme zudo-doc-theme-bridge");
|
|
2
|
+
@source inline("-link -mb-px -ml-hsp-sm -noscript -translate-x-full 2xl:w-[24px] [&::-webkit-details-marker]:hidden [&_a]:text-accent [&_a]:underline [&_nav]:mb-0 [data-admonition] [data-kbd-shortcut] [doc-history-meta] [doc-history] [doc-layout] [llms-txt] a a2 abbr about above absent absolute accent across activated active actual 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-same-origin allow-scripts alone already already-executed already-picked an anchor anchored and and/or animate-spin announce antialiased any application/json application/xml applies apply-css-vars approach are area arg aria-atomic aria-busy aria-controls aria-current aria-disabled aria-expanded aria-haspopup aria-hidden aria-label aria-live aria-orientation aria-pressed aria-selected aria-valuemax aria-valuemin aria-valuenow arm arms arrows article as asc aside aspect-[1200/630] aspect-square asset- assets assistant async at attach attribute attributes auto autogenerated available 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 base64 based batch be because before below best 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-transparent bg-warning/10 bg-warning/5 bi bigint bin blank blanks blob block blockquote blocks blur 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-info/30 border-l border-l-0 border-l-[3px] border-left-width border-muted border-none border-r border-radius border-solid border-t border-t-[2px] border-t-[3px] border-transparent border-warning/30 border-width border-y both bottom-vsp-xl box-border br brand breadcrumb:end breadcrumb:start break-words browser browsers btn bug build bundler but button buttons by bypassed byte-identical bytes cached call caller calls can cancellation cannot canonical canvas caption card card-grid carry cases cat-nav- catch category catppuccin-latte caught caution center center/contain ch chains change changed changes checkbox child chrome ci circle cite class class-less claude claude-agents claude-commands claude-md claude-skills cleaned clear clear-css-vars clearing click client client-router client-side clip clobbering close closed closing code code-block-sr-announce code-group code-group-panel col col-resize colgroup collision color color-scheme color-scheme-changed color-scheme-provider color-tweak colorization colors column comma command commands comment commit compare component component:github-link component:language-switcher component:search component:theme-toggle component:version-switcher compute computed concrete configuration configure configured confuse connect const construction consumer consumes container containers containing content content-admonition content-link content-type content-wrapper:end content-wrapper:start contents context controller controls converts copy corners correct correctly corrupt count covered covers cp crashes created cross-component crumb- cs css ctx cur current cursor cursor-not-allowed cursor-pointer custom danger dark data data-active data-admonition data-base data-close-search data-current-locale data-default-locale data-group-id data-header data-header-logo data-header-nav data-header-right data-kbd-shortcut data-language-switcher 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-open-search data-pan-active data-processed 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-tab-btn data-tab-default data-tab-label data-tab-value data-tabs data-taglist-group data-testid data-theme data-theme/style data-trailing-slash 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-nosidebar data-zd-toc data-zfb-transition-persist dd decimal declare decoration decoration-muted default defaults del delegated dependency depth desc description design design-token-panel design-token-trigger desktop desktop-sidebar desktop-sidebar-toggle desktop-sidebar-toggle-island destructive detach detached details deterministic develop dfn diagram diagrams dialog dieser diff diff-line-added diff-line-content diff-line-empty diff-line-num diff-line-removed diff-row dir directly directories directory disabled disabled:opacity-50 disc display:none dist 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 docs docs- docs-v- document document-level documented does double-registration drag draggable drop dropdown dropdown-child dropdown-parent dropdowns dt duration-150 duration-200 during dynamically e2e each ease-in-out edge eject ejected el element elements els else em embedded emit emitting empty empty/undefined en enable end enlarged entire entities entries entry error escape escaped even eventually every exactly excerpt excludes existing exists exit expected export extends factories failed fall fallback fallbacks falls false family fast feature feed fg fields fieldset figcaption figure file fill fills finally find fire fires first fixed fixed-width fixtures flag flash flat flex flex-1 flex-col flex-wrap flip flipping flips flush-left focus focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2 focus-visible:underline focus:border-accent focus:outline-none focus:underline font font-bold font-family font-medium font-mono font-semibold font-weight footer footer- for form found free fresh from frontmatter frontmatter-preview frozen fs-extra full fully function further g gap-[0.3em] gap-[clamp(1.5rem,3vw,4rem)] gap-hsp-2xs gap-hsp-md gap-hsp-sm gap-hsp-xl gap-hsp-xs gap-vsp-2xs 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-lg gap-y-vsp-md gap-y-vsp-xs gaps gate generate generation genuine geometry get github github-link go got graph gray-matter grid grid-cols-1 grid-cols-2 group group-focus-visible:text-accent group-focus-visible:underline group-focus-within:block group-hover:bg-fg group-hover:block group-hover:text-accent group-hover:text-bg group-hover:underline group-open:rotate-90 guard h-[0.5rem] h-[0.625rem] h-[0.875rem] h-[1.125rem] h-[1.25rem] h-[1.575rem] h-[14px] h-[1em] h-[1lh] h-[2rem] h-[3.5rem] h-[3rem] 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 h2s h3 h4 h5 h6 half hand hand-copied handle handled handler handlers has hash-link have head head-links head-scripts header header- header-call:end header-call:start heading heading-h2 heading-h3 heading-h4 headings height here hex hidden highlight highlighter history hit 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-surface hover:border-accent hover:border-accent-hover hover:border-fg hover:text-accent hover:text-accent-hover hover:text-fg hover:underline hr href hrefs html i i18n/theme. i2 i3 i4 icon identical idle idx if iframe image image-enlarge image/png img implementation import important imports in inactive inbox includes index info inherit inherited initial initialised injected inline inline-block inline-flex inner input input-clear ins inset-0 inside install instance instanceof instead instructions intended intent into invalid inverse inversion invoke is issues it italic item item- items items-baseline items-center items-start its itself javascript justify-between justify-center justify-end justify-start katex kbd keep keeps kept keyboard keyboard-shortcut keydown keystroke keywords khroma label landing language-switcher last:border-b-0 later launch layout leading-relaxed leading-snug leading-tight leaf- leak leaves leaving left left-0 left:calc legend legitimate 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-[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: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 light like likely line linger link link- links list-disc list-none listener literal literals lives llms llms-txt load local locale locales log longest-match look lostpointercapture lower luminance m m-0 m21 m6 main make malformed malicious maps mark marks matches matching math math-display math-inline max max-h-[80vh] max-h-[85vh] max-h-[90vh] max-h-full max-h-none max-w-[46rem] max-w-[85%] max-w-[85vw] max-w-[90vw] max-w-[clamp(50rem,75vw,90rem)] max-w-full max-w-none max-width may mb-0 mb-vsp-2xs mb-vsp-lg mb-vsp-md mb-vsp-sm mb-vsp-xl mb-vsp-xs measures measuring mechanism menu merged mermaid message messages meta meta-knob metadata migration min-h-[60vh] min-h-[calc(100vh-3.5rem)] min-h-screen min-w-0 min-w-[10rem] min-w-[8rem] mirror mirrors missing ml-auto ml-hsp-2xl ml-hsp-lg ml-hsp-sm ml-hsp-xl mod mode module mounted mouseenter mouseleave mr-hsp-sm mt-0 mt-vsp-2xl mt-vsp-2xs mt-vsp-3xs mt-vsp-lg mt-vsp-md mt-vsp-sm mt-vsp-xl must mutates mutation mutations mx-auto my-vsp-lg my-vsp-md name named native nav nav-active nav-card- nav/doc navigating navigation navigations near needs nested new newly-swapped next no no-enlarge no-op no-repeat no-underline node node:buffer node:fs node:fs/promises node:module node:path nodes nofollow noindex non-empty non-light-dark non-persisted non-string none noopener noreferrer normal noscript not not-object note now null number numeric object object-contain observe observer occurred of off og:description og:image og:image:alt og:image:height og:image:width og:title og:type og:url ol old older on once one only onto opacity-60 open open/close option or original other others out outline-none over overflow overflow-auto overflow-hidden overflow-x-auto overflow-y-auto overflow-y:auto overwrite own p p-0 p-hsp-lg p-hsp-md p-hsp-sm p-hsp-xl package package-owned packages padding page page-loading page-loading-overlay page-loading-spinner page-navigate-end page-title pages pages/. paint paint-and-read pan panel panels paren-balance-aware parent parse parsed pass passed passes path paths pattern pb-[50vh] pb-vsp-md pb-vsp-xl pb-vsp-xs per per-link permanently persisted pi pick picked picks picocolors pins pipelines pl-[1.25rem] pl-hsp-lg pl-hsp-sm pl-hsp-xl place placeholder placeholder:text-muted plain plural plus pnpm pointer-events-none pointercancel pointerdown pointermove pointerup polite polygon polyline populates port position position:fixed pr-[4px] pr-hsp-lg pr-hsp-md pr-hsp-sm pr-hsp-xl pre pre-lowercased preact preact/compat preact/hooks preact/jsx-runtime preconnect prefix preload pres preserving print produce produced produces production project 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 px px-hsp-2xl px-hsp-2xs px-hsp-lg px-hsp-md px-hsp-sm px-hsp-xl px-hsp-xs py-0 py-[2px] py-[calc(var(--spacing-vsp-xs)+0.15rem)] py-hsp-2xs 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 q query question r radius raw re-encode/decode re-querying re-render re-renders re-run re-running re-selects reach reached reaches read reading ready real real-value received receives recorded recovers redefine ref- references refetch refreshes regenerate regenerates regex reinit reinits relative reload relying remove removed render rendered renders reorder repaint repeated repeating replaced replaces repopulate requires reserved resize resize-x resolve resolved resolves response restore restores result result-click results results-area retry return returns revision revisions rewrite right right- right-0 ro robots role root rotate-180 rotate-90 round round-trip rounded rounded-[0.75rem] rounded-bl-[0.25rem] rounded-bl-[1rem] rounded-br-[0.25rem] rounded-br-[1rem] rounded-full rounded-lg rounded-t-[1rem] rounds route router routes routes-src running runs runtime s safe safer same same-locale samp scale scanned schema-mismatch schema-missing scheme scored script script- script-eval script-evaluation script-injection scripts scroll scrollbar scrolled scrollend search search-index section section- see sel-bg sel-fg select select-none self self-start semibold sentinel separator serialised serialize server server-rendered set sets setting setup shadow-[0_1px_3px_color-mix(in_srgb,var(--color-fg)_8%,transparent)] shadow-lg shape share shared sharing shiki ship shipped ships short shortcut should show shown shrink-0 sidebar sidebar- sidebar-toggle-island sidebar-tree-island signal similarity single singular site-search site-tree-nav-island sitemap- sites size skill skills skipping skips slash slug sm:border sm:border-muted sm:flex-row sm:grid-cols-2 sm:h-auto sm:items-center sm:justify-between sm:max-h-[80vh] sm:max-w-[52rem] sm:mx-auto sm:my-[10vh] sm:rounded-lg small smooth snapshot snapshots so soft soft-nav solid some somehow source sources space-y-vsp-2xs spacing span spans spec specifier specifiers splitter square sr-only src stale start state status stay staying sticky still stop stored stray string strings strip stripe stroke-linecap stroke-linejoin stroke-width strong stronger style style-attribute styled styles stylesheet sub subagents subsequent success successful summary sup surfaces survives svg swap swapped swaps synchronous synchronously syntactically syntect t tab tab-item tab-panel tabindex table tablist tabpanel tabs tabs-container tabs-content tabs-nav tag tag- tag-item- tags tags:audit take 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-heading text-info text-left text-micro text-muted text-muted/50 text-right text-small text-title text-warning text/plain textarea tfoot th that the thead their them theme theme-color theme-toggle theme/token then there these they this through throw tighten time tip title to toc toggle toggle-ai-chat toggle-design-token-panel toggles token tokens tolerates too toolbar top-0 top-[3.5rem] top-full top-level total touches tp tr tracked tracking-wider trade-off transition transition-[background,color,border-color] transition-[left,color] transition-colors transition-transform translate-x-0 translations transparent treats tree tree-child- tree-item- tree-top- trigger trigger:ai-chat trigger:design-token-panel triggers true truncate truncated try turn twitter:card twitter:creator twitter:description twitter:image twitter:site twitter:title two type typography u ul unavailable unchanged undefined under underline underlines understand unit-tested unknown unmaintained unobserve unreadable unrelated unreleased unset unsupported up uppercase usage use used user uses utf-8 utf8 utilities utility v v2 val value value-reader values var variable variant verbatim version version- version-menu version-switcher vertical via video viewport virtual:zudo-doc-chrome-bindings virtual:zudo-doc-route-context visible vitesse-dark vocabulary 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-[14px] w-[16px] w-[16rem] w-[18px] w-[1em] w-[280px] w-[2rem] w-[320px] w-[90vw] w-[var(--zd-sidebar-w)] w-dvw w-full w-icon-lg w-icon-md w-icon-sm w-icon-xs want warning was watching wbr wbr- we website weight went were what when where whereas whether which while whitespace-nowrap whitespace-pre whole wide-gamut width will with without word working worktrees would wrap wrapper wrappers written wrong wrote xl:flex xl:hidden y-scrollbar yet you your z-dropdown z-local-1 z-modal z-modal-backdrop z-sidebar z-toolbar zd-content zd-desktop-sidebar-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 zfb zfb:after-swap zfb:before-preparation zod zoom zudo-doc-design-tokens/v1 zudo-doc-sidebar-visible zudo-doc-sidebar-width zudo-doc-theme zudo-doc-theme-bridge");
|
package/dist/settings.d.ts
CHANGED
package/eject/header/header.tsx
CHANGED
|
@@ -45,6 +45,8 @@ import {
|
|
|
45
45
|
pathForMatch,
|
|
46
46
|
} from "./nav-active.js";
|
|
47
47
|
import { NAV_OVERFLOW_SCRIPT } from "./nav-overflow-script.js";
|
|
48
|
+
import { LANGUAGE_SWITCHER_INIT_SCRIPT } from "../i18n-version/language-switcher.js";
|
|
49
|
+
import { VERSION_SWITCHER_REWIRE_SCRIPT } from "../i18n-version/version-switcher.js";
|
|
48
50
|
import type {
|
|
49
51
|
HeaderNavItem,
|
|
50
52
|
HeaderRightItem,
|
|
@@ -253,6 +255,7 @@ export function Header(props: HeaderProps): JSX.Element {
|
|
|
253
255
|
headerRightItems,
|
|
254
256
|
colorModeEnabled,
|
|
255
257
|
hasLocales,
|
|
258
|
+
hasVersions,
|
|
256
259
|
githubRepoUrl,
|
|
257
260
|
githubLabel,
|
|
258
261
|
urlHelpers,
|
|
@@ -279,8 +282,12 @@ export function Header(props: HeaderProps): JSX.Element {
|
|
|
279
282
|
// AFTER_NAVIGATE_EVENT or URL derivation:
|
|
280
283
|
// - ThemeToggle: re-applies from localStorage on AFTER_NAVIGATE_EVENT
|
|
281
284
|
// (color-scheme-provider.tsx bootstrap script, #1546 verified (a))
|
|
282
|
-
// - VersionSwitcher:
|
|
283
|
-
//
|
|
285
|
+
// - VersionSwitcher: its menu hrefs, active row, and trigger label ARE
|
|
286
|
+
// per-page (derived from currentSlug/currentVersion), so within a
|
|
287
|
+
// same-locale persist window they WOULD go stale. VERSION_SWITCHER_INIT_SCRIPT
|
|
288
|
+
// re-binds the dropdown toggle on AFTER_NAVIGATE_EVENT, and
|
|
289
|
+
// VERSION_SWITCHER_REWIRE_SCRIPT recomputes the menu from
|
|
290
|
+
// window.location on the same event (zudolab/zudo-doc#2553).
|
|
284
291
|
// - Search: <site-search> custom element re-registers on
|
|
285
292
|
// AFTER_NAVIGATE_EVENT (_search-widget-script.ts:184, verified (a))
|
|
286
293
|
// - SidebarToggle (mobile): closes on AFTER_NAVIGATE_EVENT
|
|
@@ -290,9 +297,12 @@ export function Header(props: HeaderProps): JSX.Element {
|
|
|
290
297
|
// Island re-hydrates with correct nodes on mount)
|
|
291
298
|
// - Header nav + aria-current: NAV_OVERFLOW_SCRIPT re-runs on
|
|
292
299
|
// AFTER_NAVIGATE_EVENT (nav-overflow-script.ts:198, verified (a))
|
|
293
|
-
// - LanguageSwitcher:
|
|
294
|
-
// a same-locale persist window
|
|
295
|
-
//
|
|
300
|
+
// - LanguageSwitcher: its target hrefs ARE per-page (the equivalent
|
|
301
|
+
// page in each other locale), so within a same-locale persist window
|
|
302
|
+
// they WOULD go stale — the locale is constant but the path is not.
|
|
303
|
+
// LANGUAGE_SWITCHER_INIT_SCRIPT recomputes each anchor's href from
|
|
304
|
+
// window.location on AFTER_NAVIGATE_EVENT, same as the controls above
|
|
305
|
+
// (zudolab/zudo-doc#2551).
|
|
296
306
|
// Omit persistKey to fall back to the old repaint-on-every-swap path.
|
|
297
307
|
data-zfb-transition-persist={persistKey}
|
|
298
308
|
>
|
|
@@ -365,6 +375,24 @@ export function Header(props: HeaderProps): JSX.Element {
|
|
|
365
375
|
</div>
|
|
366
376
|
|
|
367
377
|
<script dangerouslySetInnerHTML={{ __html: NAV_OVERFLOW_SCRIPT }} />
|
|
378
|
+
{hasLocales ? (
|
|
379
|
+
// Keeps the persisted header's language-switcher hrefs pointing at the
|
|
380
|
+
// current page's equivalent in each other locale across same-locale SPA
|
|
381
|
+
// navigation (#2551). Registers a document-level AFTER_NAVIGATE_EVENT
|
|
382
|
+
// listener once; idempotent across re-execution.
|
|
383
|
+
<script
|
|
384
|
+
dangerouslySetInnerHTML={{ __html: LANGUAGE_SWITCHER_INIT_SCRIPT }}
|
|
385
|
+
/>
|
|
386
|
+
) : null}
|
|
387
|
+
{hasVersions ? (
|
|
388
|
+
// Keeps the persisted header's version-switcher menu hrefs / active row /
|
|
389
|
+
// trigger label tracking the current page across same-locale SPA
|
|
390
|
+
// navigation (#2553). Registers a document-level AFTER_NAVIGATE_EVENT
|
|
391
|
+
// listener once; idempotent across re-execution.
|
|
392
|
+
<script
|
|
393
|
+
dangerouslySetInnerHTML={{ __html: VERSION_SWITCHER_REWIRE_SCRIPT }}
|
|
394
|
+
/>
|
|
395
|
+
) : null}
|
|
368
396
|
</header>
|
|
369
397
|
);
|
|
370
398
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takazudo/zudo-doc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
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",
|
|
@@ -540,8 +540,8 @@
|
|
|
540
540
|
],
|
|
541
541
|
"peerDependencies": {
|
|
542
542
|
"preact": "^10.29.1",
|
|
543
|
-
"@takazudo/zfb": "^0.1.0-next.
|
|
544
|
-
"@takazudo/zfb-runtime": "^0.1.0-next.
|
|
543
|
+
"@takazudo/zfb": "^0.1.0-next.76",
|
|
544
|
+
"@takazudo/zfb-runtime": "^0.1.0-next.76",
|
|
545
545
|
"@takazudo/zudo-doc-history-server": "^2.2.1",
|
|
546
546
|
"@takazudo/zdtp": "^0.4.2",
|
|
547
547
|
"shiki": "^4.0.2",
|
|
@@ -584,9 +584,9 @@
|
|
|
584
584
|
"typescript": "^5.0.0",
|
|
585
585
|
"vitest": "^4.1.0",
|
|
586
586
|
"zod": "^4.3.6",
|
|
587
|
-
"@takazudo/zfb": "0.1.0-next.
|
|
588
|
-
"@takazudo/zfb-runtime": "0.1.0-next.
|
|
589
|
-
"@takazudo/zudo-doc-history-server": "2.
|
|
587
|
+
"@takazudo/zfb": "0.1.0-next.76",
|
|
588
|
+
"@takazudo/zfb-runtime": "0.1.0-next.76",
|
|
589
|
+
"@takazudo/zudo-doc-history-server": "2.5.0"
|
|
590
590
|
},
|
|
591
591
|
"scripts": {
|
|
592
592
|
"build": "tsup && tsc -p tsconfig.build.json",
|