@takazudo/zudo-doc 0.2.21 → 0.2.22
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/bin/gen-z-index.mjs +164 -0
- package/bin/tags-audit-runner.ts +126 -0
- package/bin/tags-audit.mjs +64 -0
- package/dist/ai-chat-modal/index.d.ts +8 -0
- package/dist/ai-chat-modal/index.js +243 -0
- package/dist/body-foot-util/body-foot-util-area.d.ts +4 -8
- package/dist/body-foot-util/edit-link.d.ts +4 -8
- package/dist/body-foot-util/index.d.ts +4 -3
- package/dist/breadcrumb/breadcrumb.d.ts +5 -9
- package/dist/breadcrumb/find-path.d.ts +2 -5
- package/dist/breadcrumb/index.d.ts +4 -4
- package/dist/breadcrumb/types.d.ts +3 -5
- package/dist/category-nav/index.d.ts +86 -0
- package/dist/category-nav/index.js +44 -0
- package/dist/category-tree-nav/index.d.ts +65 -0
- package/dist/category-tree-nav/index.js +24 -0
- package/dist/code-group/index.d.ts +10 -0
- package/dist/code-group/index.js +37 -0
- package/dist/code-syntax/code-block-enhancer-script.d.ts +1 -3
- package/dist/code-syntax/code-block-enhancer.d.ts +3 -6
- package/dist/code-syntax/index.d.ts +9 -8
- package/dist/code-syntax/mermaid-init-script.d.ts +3 -5
- package/dist/code-syntax/mermaid-init.d.ts +4 -7
- package/dist/code-syntax/tabs-init-script.d.ts +1 -3
- package/dist/code-syntax/tabs-init.d.ts +3 -6
- package/dist/code-syntax/tabs.d.ts +4 -7
- package/dist/color-scheme-utils.d.ts +125 -0
- package/dist/color-scheme-utils.js +151 -0
- package/dist/compose-meta-title/index.d.ts +7 -0
- package/dist/compose-meta-title/index.js +10 -0
- package/dist/content/component-map.d.ts +13 -17
- package/dist/content/content-blockquote.d.ts +3 -6
- package/dist/content/content-code.d.ts +3 -6
- package/dist/content/content-code.js +1 -1
- package/dist/content/content-link.d.ts +3 -6
- package/dist/content/content-link.js +1 -1
- package/dist/content/content-ol.d.ts +3 -6
- package/dist/content/content-paragraph.d.ts +3 -6
- package/dist/content/content-strong.d.ts +3 -6
- package/dist/content/content-table.d.ts +3 -6
- package/dist/content/content-ul.d.ts +3 -6
- package/dist/content/heading-h2.d.ts +3 -6
- package/dist/content/heading-h3.d.ts +3 -6
- package/dist/content/heading-h4.d.ts +3 -6
- package/dist/content/index.d.ts +29 -13
- package/dist/content-admonition/index.d.ts +15 -0
- package/dist/content-admonition/index.js +14 -0
- package/dist/design-token-panel-bootstrap.d.ts +38 -0
- package/dist/design-token-panel-bootstrap.js +29 -0
- package/dist/desktop-sidebar-toggle-island/index.d.ts +5 -0
- package/dist/desktop-sidebar-toggle-island/index.js +62 -0
- package/dist/details/details.d.ts +3 -7
- package/dist/details/index.d.ts +2 -2
- package/dist/doc-body-end/index.d.ts +21 -0
- package/dist/doc-body-end/index.js +15 -0
- package/dist/doc-content-header/index.d.ts +69 -0
- package/dist/doc-content-header/index.js +49 -0
- package/dist/doc-history/index.d.ts +10 -0
- package/dist/doc-history/index.js +407 -0
- package/dist/doc-history-area/index.d.ts +84 -0
- package/dist/doc-history-area/index.js +76 -0
- package/dist/doc-metainfo-area/index.d.ts +46 -0
- package/dist/doc-metainfo-area/index.js +41 -0
- package/dist/doc-page-props/index.d.ts +93 -0
- package/dist/doc-page-props/index.js +0 -0
- package/dist/doc-page-renderer/index.d.ts +128 -0
- package/dist/doc-page-renderer/index.js +103 -0
- package/dist/doc-page-shell/index.d.ts +136 -0
- package/dist/doc-page-shell/index.js +127 -0
- package/dist/doc-pager/index.d.ts +25 -0
- package/dist/doc-pager/index.js +41 -0
- package/dist/doc-route-entries/index.d.ts +125 -0
- package/dist/doc-route-entries/index.js +76 -0
- package/dist/doc-route-paths/index.d.ts +54 -0
- package/dist/doc-route-paths/index.js +61 -0
- package/dist/doc-tags-area/index.d.ts +38 -0
- package/dist/doc-tags-area/index.js +30 -0
- package/dist/doclayout/anchors.d.ts +7 -9
- package/dist/doclayout/doc-layout-with-defaults.d.ts +16 -12
- package/dist/doclayout/doc-layout.d.ts +5 -9
- package/dist/doclayout/index.d.ts +6 -6
- package/dist/extract-headings/index.d.ts +43 -0
- package/dist/extract-headings/index.js +111 -0
- package/dist/factory-context/index.d.ts +80 -0
- package/dist/factory-context/index.js +0 -0
- package/dist/features.css +882 -0
- package/dist/footer/footer.d.ts +4 -8
- package/dist/footer/index.d.ts +13 -3
- package/dist/footer/types.d.ts +4 -6
- package/dist/footer-with-defaults/index.d.ts +63 -0
- package/dist/footer-with-defaults/index.js +128 -0
- package/dist/frontmatter-preview-data/index.d.ts +26 -0
- package/dist/frontmatter-preview-data/index.js +23 -0
- package/dist/github-helpers/index.d.ts +17 -0
- package/dist/github-helpers/index.js +16 -0
- package/dist/head/doc-head.d.ts +3 -6
- package/dist/head/index.d.ts +9 -5
- package/dist/head/og-tags.d.ts +4 -7
- package/dist/head/twitter-card.d.ts +3 -6
- package/dist/head/types.d.ts +1 -3
- package/dist/head-with-defaults/index.d.ts +49 -0
- package/dist/head-with-defaults/index.js +62 -0
- package/dist/header/header.d.ts +6 -10
- package/dist/header/index.d.ts +16 -6
- package/dist/header/nav-active.d.ts +5 -7
- package/dist/header/nav-overflow-script.d.ts +1 -3
- package/dist/header/right-items.d.ts +3 -6
- package/dist/header/types.d.ts +10 -12
- package/dist/header-with-defaults/index.d.ts +77 -0
- package/dist/header-with-defaults/index.js +163 -0
- package/dist/html-preview-wrapper/dedent.d.ts +1 -3
- package/dist/html-preview-wrapper/highlighted-code.d.ts +3 -7
- package/dist/html-preview-wrapper/html-preview-wrapper.d.ts +6 -10
- package/dist/html-preview-wrapper/html-preview.d.ts +5 -9
- package/dist/html-preview-wrapper/index.d.ts +8 -5
- package/dist/html-preview-wrapper/preflight.d.ts +1 -3
- package/dist/html-preview-wrapper/preview-base.d.ts +4 -8
- package/dist/i18n-version/index.d.ts +7 -5
- package/dist/i18n-version/language-switcher.d.ts +5 -8
- package/dist/i18n-version/types.d.ts +3 -5
- package/dist/i18n-version/version-banner.d.ts +4 -8
- package/dist/i18n-version/version-switcher.d.ts +7 -10
- package/dist/icons/index.d.ts +9 -13
- package/dist/image-enlarge/index.d.ts +13 -0
- package/dist/image-enlarge/index.js +176 -0
- package/dist/index.d.ts +26 -2
- package/dist/inline-version-switcher/index.d.ts +31 -0
- package/dist/inline-version-switcher/index.js +44 -0
- package/dist/integrations/claude-resources/escape-for-mdx.d.ts +1 -3
- package/dist/integrations/claude-resources/generate.d.ts +2 -4
- package/dist/integrations/claude-resources/index.d.ts +7 -10
- package/dist/integrations/doc-history/index.d.ts +21 -24
- package/dist/integrations/doc-history/pre-build.d.ts +7 -9
- package/dist/integrations/llms-txt/dev-middleware.d.ts +7 -11
- package/dist/integrations/llms-txt/emit.d.ts +2 -7
- package/dist/integrations/llms-txt/generate.d.ts +3 -8
- package/dist/integrations/llms-txt/index.d.ts +33 -8
- package/dist/integrations/llms-txt/load.d.ts +3 -7
- package/dist/integrations/llms-txt/strip.d.ts +2 -6
- package/dist/integrations/llms-txt/types.d.ts +9 -13
- package/dist/integrations/search-index/build-emitter.d.ts +4 -7
- package/dist/integrations/search-index/collect.d.ts +2 -5
- package/dist/integrations/search-index/dev-middleware.d.ts +5 -8
- package/dist/integrations/search-index/index.d.ts +7 -5
- package/dist/integrations/search-index/types.d.ts +5 -7
- package/dist/island-types/index.d.ts +44 -0
- package/dist/island-types/index.js +12 -0
- package/dist/locale-merge/index.d.ts +114 -0
- package/dist/locale-merge/index.js +27 -0
- package/dist/math-block/index.d.ts +18 -0
- package/dist/math-block/index.js +29 -0
- package/dist/md-utils/index.d.ts +6 -8
- package/dist/md-utils/index.js +2 -1
- package/dist/mdx-components/index.d.ts +46 -0
- package/dist/mdx-components/index.js +161 -0
- package/dist/mermaid-enlarge/index.d.ts +11 -0
- package/dist/mermaid-enlarge/index.js +290 -0
- package/dist/metainfo/doc-metainfo.d.ts +5 -9
- package/dist/metainfo/doc-tags.d.ts +7 -11
- package/dist/metainfo/frontmatter-preview.d.ts +8 -12
- package/dist/metainfo/index.d.ts +6 -4
- package/dist/nav-data-prep/index.d.ts +72 -0
- package/dist/nav-data-prep/index.js +34 -0
- package/dist/nav-indexing/category-nav.d.ts +4 -8
- package/dist/nav-indexing/category-tree-nav.d.ts +4 -8
- package/dist/nav-indexing/doc-card-grid.d.ts +4 -8
- package/dist/nav-indexing/docs-sitemap.d.ts +4 -8
- package/dist/nav-indexing/index.d.ts +37 -10
- package/dist/nav-indexing/nav-card-grid.d.ts +4 -8
- package/dist/nav-indexing/site-tree-nav-demo.d.ts +4 -8
- package/dist/nav-indexing/tag-nav.d.ts +6 -10
- package/dist/nav-indexing/types.d.ts +6 -8
- package/dist/nav-indexing/versions-page-content.d.ts +4 -8
- package/dist/nav-scope/index.d.ts +41 -0
- package/dist/nav-scope/index.js +41 -0
- package/dist/nav-source-cache/index.d.ts +13 -0
- package/dist/nav-source-cache/index.js +28 -0
- package/dist/nav-source-docs/index.d.ts +169 -0
- package/dist/nav-source-docs/index.js +123 -0
- package/dist/page-loading/index.d.ts +1 -2
- package/dist/page-loading/page-loading-overlay.d.ts +4 -8
- package/dist/plugins/claude-resources.d.ts +3 -0
- package/dist/plugins/claude-resources.js +27 -0
- package/dist/plugins/connect-adapter.d.ts +31 -0
- package/dist/plugins/connect-adapter.js +78 -0
- package/dist/plugins/doc-history.d.ts +3 -0
- package/dist/plugins/doc-history.js +54 -0
- package/dist/plugins/llms-txt.d.ts +3 -0
- package/dist/plugins/llms-txt.js +48 -0
- package/dist/plugins/plugin-utils.d.ts +17 -0
- package/dist/plugins/plugin-utils.js +11 -0
- package/dist/plugins/search-index.d.ts +3 -0
- package/dist/plugins/search-index.js +24 -0
- package/dist/preset.d.ts +152 -0
- package/dist/preset.js +169 -0
- package/dist/render-markdown/index.d.ts +5 -0
- package/dist/render-markdown/index.js +115 -0
- package/dist/robots.d.ts +36 -0
- package/dist/robots.js +17 -0
- package/dist/route-enumerators/index.d.ts +204 -0
- package/dist/route-enumerators/index.js +156 -0
- package/dist/safelist.css +1 -1
- package/dist/search-widget/index.d.ts +38 -0
- package/dist/search-widget/index.js +169 -0
- package/dist/search-widget-script/index.d.ts +18 -0
- package/dist/search-widget-script/index.js +465 -0
- package/dist/settings.d.ts +255 -0
- package/dist/settings.js +0 -0
- package/dist/sidebar/index.d.ts +21 -3
- package/dist/sidebar/sidebar.d.ts +5 -9
- package/dist/sidebar/types.d.ts +4 -6
- package/dist/sidebar-prepaint/index.d.ts +21 -0
- package/dist/sidebar-prepaint/index.js +23 -0
- package/dist/sidebar-resizer/index.d.ts +2 -6
- package/dist/sidebar-resizer/sidebar-resizer-init.d.ts +6 -10
- package/dist/sidebar-toggle-island/index.d.ts +13 -0
- package/dist/sidebar-toggle-island/index.js +122 -0
- package/dist/sidebar-tree/build-tree.d.ts +4 -8
- package/dist/sidebar-tree/category-meta.d.ts +3 -6
- package/dist/sidebar-tree/index.d.ts +18 -3
- package/dist/sidebar-tree/types.d.ts +6 -8
- package/dist/sidebar-tree-island/index.d.ts +13 -0
- package/dist/sidebar-tree-island/index.js +454 -0
- package/dist/sidebar-utils/index.d.ts +64 -0
- package/dist/sidebar-utils/index.js +88 -0
- package/dist/sidebar-with-defaults/index.d.ts +38 -0
- package/dist/sidebar-with-defaults/index.js +45 -0
- package/dist/site-tree-nav/index.d.ts +71 -0
- package/dist/site-tree-nav/index.js +36 -0
- package/dist/site-tree-nav-island/index.d.ts +11 -0
- package/dist/site-tree-nav-island/index.js +176 -0
- package/dist/slug/index.d.ts +4 -0
- package/dist/slug/index.js +19 -0
- package/dist/smart-break/index.d.ts +55 -0
- package/dist/{toc/smart-break.js → smart-break/index.js} +30 -3
- package/dist/tab-item/index.d.ts +2 -2
- package/dist/tab-item/tab-item.d.ts +6 -7
- package/dist/tag-helpers/index.d.ts +40 -0
- package/dist/tag-helpers/index.js +54 -0
- package/dist/tag-pages/index.d.ts +114 -0
- package/dist/tag-pages/index.js +146 -0
- package/dist/tags-audit.d.ts +107 -0
- package/dist/tags-audit.js +321 -0
- package/dist/theme/color-scheme-provider.d.ts +4 -8
- package/dist/theme/color-tweak-export-modal.d.ts +4 -7
- package/dist/theme/design-token-serde.d.ts +14 -17
- package/dist/theme/design-token-types.d.ts +4 -6
- package/dist/theme/iframe-bridge.d.ts +12 -14
- package/dist/theme/index.d.ts +7 -9
- package/dist/theme/theme-toggle.d.ts +3 -8
- package/dist/theme-toggle/color-scheme-sync.d.ts +5 -7
- package/dist/theme-toggle/index.d.ts +4 -8
- package/dist/toc/cx.d.ts +2 -3
- package/dist/toc/index.d.ts +8 -6
- package/dist/toc/mobile-toc.d.ts +5 -9
- package/dist/toc/mobile-toc.js +1 -1
- package/dist/toc/toc-title.d.ts +1 -3
- package/dist/toc/toc.d.ts +5 -9
- package/dist/toc/toc.js +1 -1
- package/dist/toc/types.d.ts +1 -3
- package/dist/toc/use-active-heading.d.ts +4 -7
- package/dist/transitions/index.d.ts +1 -1
- package/dist/transitions/page-events.d.ts +4 -6
- package/dist/tree-nav-shared/index.d.ts +22 -0
- package/dist/tree-nav-shared/index.js +66 -0
- package/dist/url-helpers/index.d.ts +37 -0
- package/dist/url-helpers/index.js +106 -0
- package/dist/url-normalizer/index.d.ts +39 -1
- package/dist/url-normalizer/normalize.d.ts +3 -5
- package/dist/use-modal-dialog/index.d.ts +70 -0
- package/dist/use-modal-dialog/index.js +73 -0
- package/dist/versions-page/index.d.ts +55 -0
- package/dist/versions-page/index.js +73 -0
- package/package.json +290 -10
- package/dist/toc/smart-break.d.ts +0 -27
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
function makeUrlHelpers(settings, i18n) {
|
|
2
|
+
const { defaultLocale, locales, getLocaleLabel } = i18n;
|
|
3
|
+
const normalizedBase = settings.base.replace(/\/+$/, "");
|
|
4
|
+
function applyTrailingSlash(url) {
|
|
5
|
+
if (!settings.trailingSlash) return url;
|
|
6
|
+
if (url.endsWith("/")) return url;
|
|
7
|
+
const suffixIdx = url.search(/[?#]/);
|
|
8
|
+
const pathPart = suffixIdx >= 0 ? url.slice(0, suffixIdx) : url;
|
|
9
|
+
const suffix = suffixIdx >= 0 ? url.slice(suffixIdx) : "";
|
|
10
|
+
if (pathPart.endsWith("/")) return url;
|
|
11
|
+
const lastSegment = pathPart.split("/").pop() ?? "";
|
|
12
|
+
if (/\.[a-zA-Z]\w*$/.test(lastSegment)) return url;
|
|
13
|
+
return pathPart + "/" + suffix;
|
|
14
|
+
}
|
|
15
|
+
function withBase(path) {
|
|
16
|
+
const raw = normalizedBase === "" ? path : `${normalizedBase}${path.startsWith("/") ? path : `/${path}`}`;
|
|
17
|
+
return applyTrailingSlash(raw);
|
|
18
|
+
}
|
|
19
|
+
function stripBase(path) {
|
|
20
|
+
if (normalizedBase === "") return path;
|
|
21
|
+
if (path === normalizedBase) return "/";
|
|
22
|
+
return path.startsWith(`${normalizedBase}/`) ? path.slice(normalizedBase.length) : path;
|
|
23
|
+
}
|
|
24
|
+
function absoluteUrl(pageUrl) {
|
|
25
|
+
return settings.siteUrl ? settings.siteUrl.replace(/\/$/, "") + pageUrl : void 0;
|
|
26
|
+
}
|
|
27
|
+
function docsUrl(slug, lang = defaultLocale) {
|
|
28
|
+
const path = lang === defaultLocale ? `/docs/${slug}` : `/${lang}/docs/${slug}`;
|
|
29
|
+
return withBase(path);
|
|
30
|
+
}
|
|
31
|
+
function isExternal(href) {
|
|
32
|
+
return href.startsWith("http://") || href.startsWith("https://");
|
|
33
|
+
}
|
|
34
|
+
function resolveHref(href) {
|
|
35
|
+
return isExternal(href) ? href : withBase(href);
|
|
36
|
+
}
|
|
37
|
+
function navHref(path, lang, currentVersion) {
|
|
38
|
+
const isNonDefaultLocale = lang != null && lang !== defaultLocale;
|
|
39
|
+
const versionPrefix = currentVersion ? `/v/${currentVersion}` : "";
|
|
40
|
+
return withBase(
|
|
41
|
+
isNonDefaultLocale ? `${versionPrefix}/${lang}${path}` : `${versionPrefix}${path}`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
function splitVersionPrefix(path) {
|
|
45
|
+
const m = path.match(/^(\/v\/[^/]+)(\/.*|$)/);
|
|
46
|
+
return m ? { versionPrefix: m[1] ?? "", rest: m[2] ?? "/" } : { versionPrefix: "", rest: path };
|
|
47
|
+
}
|
|
48
|
+
function getPathForLocale(path, currentLang, targetLang) {
|
|
49
|
+
const { versionPrefix, rest } = splitVersionPrefix(stripBase(path));
|
|
50
|
+
let relativePath = rest;
|
|
51
|
+
if (currentLang !== defaultLocale) {
|
|
52
|
+
relativePath = relativePath.replace(new RegExp(`^/${currentLang}(?:/|$)`), "/");
|
|
53
|
+
}
|
|
54
|
+
if (targetLang !== defaultLocale) {
|
|
55
|
+
relativePath = `/${targetLang}${relativePath}`;
|
|
56
|
+
}
|
|
57
|
+
return withBase(`${versionPrefix}${relativePath}`);
|
|
58
|
+
}
|
|
59
|
+
function isDefaultLocaleOnlyPath(path) {
|
|
60
|
+
const stripped = stripBase(path);
|
|
61
|
+
const normalized = stripped.endsWith("/") ? stripped : `${stripped}/`;
|
|
62
|
+
return settings.defaultLocaleOnlyPrefixes.some((prefix) => normalized.startsWith(prefix));
|
|
63
|
+
}
|
|
64
|
+
function buildLocaleLinks(currentPath, currentLang) {
|
|
65
|
+
let defaultLocalePath = splitVersionPrefix(stripBase(currentPath)).rest;
|
|
66
|
+
if (currentLang !== defaultLocale) {
|
|
67
|
+
defaultLocalePath = defaultLocalePath.replace(new RegExp(`^/${currentLang}(?:/|$)`), "/");
|
|
68
|
+
}
|
|
69
|
+
if (isDefaultLocaleOnlyPath(defaultLocalePath)) {
|
|
70
|
+
return [{
|
|
71
|
+
code: currentLang,
|
|
72
|
+
label: getLocaleLabel(currentLang),
|
|
73
|
+
href: getPathForLocale(currentPath, currentLang, currentLang),
|
|
74
|
+
active: true
|
|
75
|
+
}];
|
|
76
|
+
}
|
|
77
|
+
return locales.map((code) => ({
|
|
78
|
+
code,
|
|
79
|
+
label: getLocaleLabel(code),
|
|
80
|
+
href: getPathForLocale(currentPath, currentLang, code),
|
|
81
|
+
active: code === currentLang
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
function versionedDocsUrl(slug, versionSlug, lang = defaultLocale) {
|
|
85
|
+
const path = lang === defaultLocale ? `/v/${versionSlug}/docs/${slug}` : `/v/${versionSlug}/${lang}/docs/${slug}`;
|
|
86
|
+
return withBase(path);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
normalizedBase,
|
|
90
|
+
applyTrailingSlash,
|
|
91
|
+
withBase,
|
|
92
|
+
stripBase,
|
|
93
|
+
absoluteUrl,
|
|
94
|
+
docsUrl,
|
|
95
|
+
isExternal,
|
|
96
|
+
resolveHref,
|
|
97
|
+
navHref,
|
|
98
|
+
getPathForLocale,
|
|
99
|
+
buildLocaleLinks,
|
|
100
|
+
isDefaultLocaleOnlyPath,
|
|
101
|
+
versionedDocsUrl
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export {
|
|
105
|
+
makeUrlHelpers
|
|
106
|
+
};
|
|
@@ -1 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Public entry for `@takazudo/zudo-doc/url-normalizer`.
|
|
3
|
+
*
|
|
4
|
+
* Build-time URL normalizer that ensures every emitted route URL ends with
|
|
5
|
+
* a trailing slash, matching the `trailingSlash: "always"` policy of the
|
|
6
|
+
* legacy Astro build.
|
|
7
|
+
*
|
|
8
|
+
* ## Why build-time only?
|
|
9
|
+
*
|
|
10
|
+
* zfb is a purely static site builder — there is no server or dev middleware.
|
|
11
|
+
* The Astro `src/middleware-handler.ts` ran at request time; the equivalent
|
|
12
|
+
* guarantee is now split:
|
|
13
|
+
*
|
|
14
|
+
* - **Build time** (this module): `buildUrl()` constructs URLs that always
|
|
15
|
+
* end with `/`, and `normalizePathname()` corrects any bare pathname passed
|
|
16
|
+
* in from external data.
|
|
17
|
+
* - **Runtime**: the deployment host (e.g. Cloudflare Pages `_redirects`) is
|
|
18
|
+
* responsible for redirecting requests that arrive without a trailing slash.
|
|
19
|
+
*
|
|
20
|
+
* See the "Trailing-Slash Policy" concept doc in the showcase docs for the
|
|
21
|
+
* full design rationale and an example `_redirects` rule.
|
|
22
|
+
*
|
|
23
|
+
* ## Usage
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* import {
|
|
27
|
+
* buildUrl,
|
|
28
|
+
* normalizePathname,
|
|
29
|
+
* shouldSkipNormalization,
|
|
30
|
+
* } from "@takazudo/zudo-doc/url-normalizer";
|
|
31
|
+
*
|
|
32
|
+
* // In a paths() implementation:
|
|
33
|
+
* const url = buildUrl("docs", slug); // "/docs/my-slug/"
|
|
34
|
+
*
|
|
35
|
+
* // In an integration that receives raw pathnames:
|
|
36
|
+
* const canonical = normalizePathname(rawPathname);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export { buildUrl, normalizePathname, shouldSkipNormalization } from "./normalize.js";
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
* buildUrl() // "/"
|
|
59
59
|
* buildUrl("", "docs", "") // "/docs/"
|
|
60
60
|
*/
|
|
61
|
-
declare function buildUrl(...segments: string[]): string;
|
|
61
|
+
export declare function buildUrl(...segments: string[]): string;
|
|
62
62
|
/**
|
|
63
63
|
* Return `true` when `pathname` should NOT receive a trailing slash.
|
|
64
64
|
*
|
|
@@ -78,7 +78,7 @@ declare function buildUrl(...segments: string[]): string;
|
|
|
78
78
|
* shouldSkipNormalization("/docs/guide") // false (needs trailing slash)
|
|
79
79
|
* shouldSkipNormalization("/docs/v2.0") // false (number after dot → not an extension)
|
|
80
80
|
*/
|
|
81
|
-
declare function shouldSkipNormalization(pathname: string): boolean;
|
|
81
|
+
export declare function shouldSkipNormalization(pathname: string): boolean;
|
|
82
82
|
/**
|
|
83
83
|
* Return `pathname` with a trailing slash appended if needed.
|
|
84
84
|
*
|
|
@@ -99,6 +99,4 @@ declare function shouldSkipNormalization(pathname: string): boolean;
|
|
|
99
99
|
* normalizePathname("/docs/guide/") // "/docs/guide/" (no-op)
|
|
100
100
|
* normalizePathname("/assets/app.js") // "/assets/app.js" (skipped)
|
|
101
101
|
*/
|
|
102
|
-
declare function normalizePathname(pathname: string): string;
|
|
103
|
-
|
|
104
|
-
export { buildUrl, normalizePathname, shouldSkipNormalization };
|
|
102
|
+
export declare function normalizePathname(pathname: string): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
interface UseModalDialogOptions {
|
|
2
|
+
/** True when the dialog should be open. */
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
/** Called when the dialog is closed for any reason (Escape, backdrop, navigation). */
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
/**
|
|
7
|
+
* DOM event name that triggers a close-and-reset. Pass the host's SPA
|
|
8
|
+
* after-navigate event name (e.g. `AFTER_NAVIGATE_EVENT`); pass `undefined`
|
|
9
|
+
* to skip navigation-close wiring entirely.
|
|
10
|
+
*/
|
|
11
|
+
navigateEvent?: string;
|
|
12
|
+
/**
|
|
13
|
+
* When `true`, enable backdrop-click-to-close. The caller must forward the
|
|
14
|
+
* click handler returned by this hook to the `<dialog>` element's `onClick`.
|
|
15
|
+
* Defaults to `false` so opt-in only.
|
|
16
|
+
*/
|
|
17
|
+
backdropClickClose?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* When `true`, enable focus management:
|
|
20
|
+
* - On open: capture `document.activeElement` as the return target (unless
|
|
21
|
+
* `returnFocusRef` is provided), then move focus to the first focusable
|
|
22
|
+
* element inside the dialog (or the dialog itself as a fallback).
|
|
23
|
+
* - On close: restore focus to the captured element.
|
|
24
|
+
*
|
|
25
|
+
* When a caller already manages focus internally (e.g. ai-chat-modal focuses
|
|
26
|
+
* its input in its own effect), pass `restoreFocusOnly: true` instead to get
|
|
27
|
+
* trigger-capture + restore without the "move into dialog" step.
|
|
28
|
+
*/
|
|
29
|
+
manageFocus?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Like `manageFocus` but skips the "focus first element in dialog" step —
|
|
32
|
+
* use when the caller has its own focus-on-open logic and only needs
|
|
33
|
+
* trigger-capture + focus restore on close.
|
|
34
|
+
*/
|
|
35
|
+
restoreFocusOnly?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Optional ref holding the element to return focus to on close. When
|
|
38
|
+
* provided and non-null, this takes precedence over the internally captured
|
|
39
|
+
* `document.activeElement`. Useful when the trigger element is conditionally
|
|
40
|
+
* rendered and therefore removed from the DOM before the open effect fires
|
|
41
|
+
* (e.g. the doc-history trigger button, which is unmounted when `isOpen`
|
|
42
|
+
* is true). The caller should set `returnFocusRef.current` to the trigger
|
|
43
|
+
* element in its click handler, before calling `setState`.
|
|
44
|
+
*/
|
|
45
|
+
returnFocusRef?: React.RefObject<HTMLElement | null>;
|
|
46
|
+
}
|
|
47
|
+
interface UseModalDialogResult {
|
|
48
|
+
dialogRef: React.RefObject<HTMLDialogElement>;
|
|
49
|
+
/**
|
|
50
|
+
* Pass this to `<dialog onClick={handleBackdropClick}>`.
|
|
51
|
+
* Only wired when `backdropClickClose` is true; otherwise a no-op.
|
|
52
|
+
*/
|
|
53
|
+
handleBackdropClick: (e: React.MouseEvent<HTMLDialogElement>) => void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Shared hook for `<dialog>`-based modal components.
|
|
57
|
+
*
|
|
58
|
+
* Usage:
|
|
59
|
+
* ```tsx
|
|
60
|
+
* const { dialogRef, handleBackdropClick } = useModalDialog({
|
|
61
|
+
* isOpen,
|
|
62
|
+
* onClose: handleClose,
|
|
63
|
+
* navigateEvent: AFTER_NAVIGATE_EVENT,
|
|
64
|
+
* backdropClickClose: true,
|
|
65
|
+
* });
|
|
66
|
+
* return <dialog ref={dialogRef} onClick={handleBackdropClick} ... />;
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare function useModalDialog({ isOpen, onClose, navigateEvent, backdropClickClose, manageFocus, restoreFocusOnly, returnFocusRef, }: UseModalDialogOptions): UseModalDialogResult;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "preact/compat";
|
|
3
|
+
function useModalDialog({
|
|
4
|
+
isOpen,
|
|
5
|
+
onClose,
|
|
6
|
+
navigateEvent,
|
|
7
|
+
backdropClickClose = false,
|
|
8
|
+
manageFocus = false,
|
|
9
|
+
restoreFocusOnly = false,
|
|
10
|
+
returnFocusRef
|
|
11
|
+
}) {
|
|
12
|
+
const dialogRef = useRef(null);
|
|
13
|
+
const internalTriggerRef = useRef(null);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const dialog = dialogRef.current;
|
|
16
|
+
if (!dialog) return;
|
|
17
|
+
if (isOpen && !dialog.open) {
|
|
18
|
+
if ((manageFocus || restoreFocusOnly) && !returnFocusRef) {
|
|
19
|
+
internalTriggerRef.current = document.activeElement;
|
|
20
|
+
}
|
|
21
|
+
dialog.showModal();
|
|
22
|
+
if (manageFocus && !restoreFocusOnly) {
|
|
23
|
+
const FOCUSABLE = 'button:not([disabled]),a[href],[tabindex]:not([tabindex="-1"]),input:not([disabled]),select:not([disabled]),textarea:not([disabled])';
|
|
24
|
+
const first = dialog.querySelector(FOCUSABLE);
|
|
25
|
+
if (first) {
|
|
26
|
+
first.focus();
|
|
27
|
+
} else {
|
|
28
|
+
dialog.focus();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} else if (!isOpen && dialog.open) {
|
|
32
|
+
dialog.close();
|
|
33
|
+
}
|
|
34
|
+
}, [isOpen, manageFocus, restoreFocusOnly, returnFocusRef]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const dialog = dialogRef.current;
|
|
37
|
+
if (!dialog) return;
|
|
38
|
+
function onDialogClose() {
|
|
39
|
+
if (manageFocus || restoreFocusOnly) {
|
|
40
|
+
const trigger = returnFocusRef ? returnFocusRef.current : internalTriggerRef.current;
|
|
41
|
+
if (trigger instanceof HTMLElement) {
|
|
42
|
+
trigger.focus();
|
|
43
|
+
}
|
|
44
|
+
internalTriggerRef.current = null;
|
|
45
|
+
}
|
|
46
|
+
if (isOpen) onClose();
|
|
47
|
+
}
|
|
48
|
+
dialog.addEventListener("close", onDialogClose);
|
|
49
|
+
return () => dialog.removeEventListener("close", onDialogClose);
|
|
50
|
+
}, [isOpen, onClose, manageFocus, restoreFocusOnly, returnFocusRef]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!navigateEvent) return;
|
|
53
|
+
function handleNavigation() {
|
|
54
|
+
const dialog = dialogRef.current;
|
|
55
|
+
if (dialog?.open) {
|
|
56
|
+
dialog.close();
|
|
57
|
+
onClose();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
document.addEventListener(navigateEvent, handleNavigation);
|
|
61
|
+
return () => document.removeEventListener(navigateEvent, handleNavigation);
|
|
62
|
+
}, [navigateEvent, onClose]);
|
|
63
|
+
function handleBackdropClick(e) {
|
|
64
|
+
if (!backdropClickClose) return;
|
|
65
|
+
const dialog = dialogRef.current;
|
|
66
|
+
if (!dialog) return;
|
|
67
|
+
if (e.target === dialog) dialog.close();
|
|
68
|
+
}
|
|
69
|
+
return { dialogRef, handleBackdropClick };
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
useModalDialog
|
|
73
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
/** @jsxImportSource preact */
|
|
3
|
+
import type { ComponentChildren, JSX } from "preact";
|
|
4
|
+
/** Minimal version config entry needed by the versions page. */
|
|
5
|
+
export interface VersionsPageVersionEntry {
|
|
6
|
+
slug: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
banner?: "unmaintained" | "unreleased" | false;
|
|
9
|
+
}
|
|
10
|
+
/** Settings subset read by the versions page factory. */
|
|
11
|
+
export interface VersionsPageSettings {
|
|
12
|
+
noindex?: boolean;
|
|
13
|
+
dynamicPageTransition?: boolean;
|
|
14
|
+
versions: VersionsPageVersionEntry[] | false;
|
|
15
|
+
base: string;
|
|
16
|
+
}
|
|
17
|
+
/** Host-supplied component bindings injected into the versions page factory. */
|
|
18
|
+
export interface VersionsPageComponents {
|
|
19
|
+
HeadWithDefaults: (props: {
|
|
20
|
+
title: string;
|
|
21
|
+
}) => JSX.Element;
|
|
22
|
+
HeaderWithDefaults: (props: {
|
|
23
|
+
lang?: string;
|
|
24
|
+
currentPath?: string;
|
|
25
|
+
}) => JSX.Element;
|
|
26
|
+
FooterWithDefaults: (props: {
|
|
27
|
+
lang?: string;
|
|
28
|
+
}) => JSX.Element;
|
|
29
|
+
BodyEndIslands: (props: {
|
|
30
|
+
basePath: string;
|
|
31
|
+
}) => JSX.Element;
|
|
32
|
+
}
|
|
33
|
+
/** Injected dependencies for {@link createVersionsPageView}. */
|
|
34
|
+
export interface VersionsPageDeps {
|
|
35
|
+
settings: VersionsPageSettings;
|
|
36
|
+
/** Default locale code (e.g. `"en"`). */
|
|
37
|
+
defaultLocale: string;
|
|
38
|
+
/** Translate a UI string key for a locale. */
|
|
39
|
+
t: (key: string, locale: string) => string;
|
|
40
|
+
/** Prefix a path with the configured base directory. */
|
|
41
|
+
withBase: (path: string) => string;
|
|
42
|
+
/** Compose the meta `<title>` value. */
|
|
43
|
+
composeMetaTitle: (title: string) => string;
|
|
44
|
+
components: VersionsPageComponents;
|
|
45
|
+
}
|
|
46
|
+
/** Props for the VersionsPageView component. */
|
|
47
|
+
export interface VersionsPageViewProps {
|
|
48
|
+
locale: string;
|
|
49
|
+
children?: ComponentChildren;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a `VersionsPageView` component bound to the host's settings and
|
|
53
|
+
* component dependencies.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createVersionsPageView(deps: VersionsPageDeps): (props: VersionsPageViewProps) => JSX.Element;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Fragment, jsx } from "preact/jsx-runtime";
|
|
2
|
+
import { DocLayoutWithDefaults } from "../doclayout/index.js";
|
|
3
|
+
import { VersionsPageContent } from "../nav-indexing/index.js";
|
|
4
|
+
function createVersionsPageView(deps) {
|
|
5
|
+
const {
|
|
6
|
+
settings,
|
|
7
|
+
defaultLocale,
|
|
8
|
+
t,
|
|
9
|
+
withBase,
|
|
10
|
+
composeMetaTitle,
|
|
11
|
+
components: {
|
|
12
|
+
HeadWithDefaults,
|
|
13
|
+
HeaderWithDefaults,
|
|
14
|
+
FooterWithDefaults,
|
|
15
|
+
BodyEndIslands
|
|
16
|
+
}
|
|
17
|
+
} = deps;
|
|
18
|
+
function VersionsPageView({ locale }) {
|
|
19
|
+
const isDefault = locale === defaultLocale;
|
|
20
|
+
const prefix = isDefault ? "" : `/${locale}`;
|
|
21
|
+
const pageTitle = t("version.page.title", locale);
|
|
22
|
+
const labels = {
|
|
23
|
+
pageTitle,
|
|
24
|
+
latestTitle: t("version.page.latest.title", locale),
|
|
25
|
+
latestDescription: t("version.page.latest.description", locale),
|
|
26
|
+
latestLink: t("version.page.latest.link", locale),
|
|
27
|
+
pastTitle: t("version.page.past.title", locale),
|
|
28
|
+
pastDescription: t("version.page.past.description", locale),
|
|
29
|
+
unmaintained: t("version.page.unmaintained", locale),
|
|
30
|
+
unreleased: t("version.page.unreleased", locale),
|
|
31
|
+
versionCol: t("version.switcher.label", locale),
|
|
32
|
+
statusCol: t("version.page.status", locale),
|
|
33
|
+
docsCol: t("version.page.docs", locale)
|
|
34
|
+
};
|
|
35
|
+
const latestHref = withBase(`${prefix}/docs/getting-started`);
|
|
36
|
+
const versions = settings.versions ? settings.versions.map((v) => ({
|
|
37
|
+
slug: v.slug,
|
|
38
|
+
label: v.label ?? v.slug,
|
|
39
|
+
// Version prefix comes BEFORE the locale — the only routed shape is
|
|
40
|
+
// pages/v/[version]/{locale}/docs/...; /{locale}/v/... has no route.
|
|
41
|
+
docsHref: withBase(`/v/${v.slug}${prefix}/docs/getting-started/`),
|
|
42
|
+
banner: v.banner
|
|
43
|
+
})) : [];
|
|
44
|
+
return /* @__PURE__ */ jsx(
|
|
45
|
+
DocLayoutWithDefaults,
|
|
46
|
+
{
|
|
47
|
+
title: composeMetaTitle(pageTitle),
|
|
48
|
+
head: /* @__PURE__ */ jsx(HeadWithDefaults, { title: pageTitle }),
|
|
49
|
+
lang: locale,
|
|
50
|
+
noindex: settings.noindex,
|
|
51
|
+
hideSidebar: true,
|
|
52
|
+
hideToc: true,
|
|
53
|
+
sidebarOverride: /* @__PURE__ */ jsx(Fragment, {}),
|
|
54
|
+
headerOverride: /* @__PURE__ */ jsx(HeaderWithDefaults, { lang: locale, currentPath: withBase(`${prefix}/docs/versions`) }),
|
|
55
|
+
footerOverride: /* @__PURE__ */ jsx(FooterWithDefaults, { lang: locale }),
|
|
56
|
+
bodyEndComponents: /* @__PURE__ */ jsx(BodyEndIslands, { basePath: settings.base ?? "/" }),
|
|
57
|
+
enableClientRouter: settings.dynamicPageTransition,
|
|
58
|
+
children: /* @__PURE__ */ jsx(
|
|
59
|
+
VersionsPageContent,
|
|
60
|
+
{
|
|
61
|
+
latestHref,
|
|
62
|
+
versions,
|
|
63
|
+
labels
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return VersionsPageView;
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
createVersionsPageView
|
|
73
|
+
};
|