@takazudo/zudo-doc 2.4.1 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/desktop-sidebar-toggle-island/index.d.ts +2 -0
- package/dist/desktop-sidebar-toggle-island/index.js +3 -1
- package/dist/doc-page-shell/index.js +15 -3
- package/dist/integrations/claude-resources/generate.js +5 -4
- 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/settings.d.ts +1 -0
- package/dist/sidebar-prepaint/index.d.ts +27 -1
- package/dist/sidebar-prepaint/index.js +33 -13
- package/dist/url-helpers/index.js +5 -3
- package/eject/desktop-sidebar-toggle-island/index.tsx +7 -2
- package/package.json +2 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export declare const SIDEBAR_STORAGE_KEY = "zudo-doc-sidebar-visible";
|
|
2
|
+
export declare function readState(): boolean;
|
|
3
|
+
export declare function setDataAttribute(isVisible: boolean): void;
|
|
2
4
|
export declare function DesktopSidebarToggle(): import("preact").JSX.Element;
|
|
3
5
|
export declare namespace DesktopSidebarToggle {
|
|
4
6
|
var displayName: string;
|
|
@@ -4,7 +4,10 @@ import { DocLayoutWithDefaults } from "../doclayout/index.js";
|
|
|
4
4
|
import { Toc, MobileToc, getTocTitle } from "../toc/index.js";
|
|
5
5
|
import { Breadcrumb } from "../breadcrumb/index.js";
|
|
6
6
|
import { NavCardGrid } from "../nav-indexing/index.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
createSidebarPrepaint,
|
|
9
|
+
createSidebarVisibilityPrepaint
|
|
10
|
+
} from "../sidebar-prepaint/index.js";
|
|
8
11
|
import { createHeadWithDefaults } from "../head-with-defaults/index.js";
|
|
9
12
|
import { createSidebarWithDefaults } from "../sidebar-with-defaults/index.js";
|
|
10
13
|
import { createHeaderWithDefaults } from "../header-with-defaults/index.js";
|
|
@@ -21,8 +24,14 @@ function createDocPageShell(ctx) {
|
|
|
21
24
|
const SidebarWithDefaults = createSidebarWithDefaults(ctx);
|
|
22
25
|
const HeaderWithDefaults = createHeaderWithDefaults(ctx);
|
|
23
26
|
const FooterWithDefaults = createFooterWithDefaults(ctx);
|
|
27
|
+
const sidebarToggleEnabled = Boolean(
|
|
28
|
+
ctx.settings.sidebarToggle
|
|
29
|
+
);
|
|
24
30
|
const SidebarPrepaint = createSidebarPrepaint({
|
|
25
|
-
sidebarToggle:
|
|
31
|
+
sidebarToggle: sidebarToggleEnabled
|
|
32
|
+
});
|
|
33
|
+
const SidebarVisibilityPrepaint = createSidebarVisibilityPrepaint({
|
|
34
|
+
sidebarToggle: sidebarToggleEnabled
|
|
26
35
|
});
|
|
27
36
|
const DocBodyEnd = createDocBodyEnd(ctx);
|
|
28
37
|
const DocPager = createDocPager(ctx);
|
|
@@ -69,7 +78,10 @@ function createDocPageShell(ctx) {
|
|
|
69
78
|
{
|
|
70
79
|
title: composeMetaTitle(title),
|
|
71
80
|
description: settings.metaTags.description ? description : void 0,
|
|
72
|
-
head: /* @__PURE__ */
|
|
81
|
+
head: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
82
|
+
/* @__PURE__ */ jsx(HeadWithDefaults, { title, description, canonical }),
|
|
83
|
+
/* @__PURE__ */ jsx(SidebarVisibilityPrepaint, { hideSidebar })
|
|
84
|
+
] }),
|
|
73
85
|
lang: locale,
|
|
74
86
|
noindex: settings.noindex,
|
|
75
87
|
hideSidebar,
|
|
@@ -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
|
}
|
|
@@ -441,8 +444,6 @@ sidebar_position: 899
|
|
|
441
444
|
generated: true
|
|
442
445
|
---
|
|
443
446
|
|
|
444
|
-
Claude Code configuration reference.
|
|
445
|
-
|
|
446
447
|
## Resources
|
|
447
448
|
|
|
448
449
|
<CategoryNav categories={${categoriesAttr}} />
|
|
@@ -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/settings.d.ts
CHANGED
|
@@ -8,10 +8,36 @@ export interface SidebarPrepaintProps {
|
|
|
8
8
|
*/
|
|
9
9
|
hideSidebar?: boolean;
|
|
10
10
|
}
|
|
11
|
-
/** Settings subset read by
|
|
11
|
+
/** Settings subset read by the sidebar-prepaint factories. */
|
|
12
12
|
export interface SidebarPrepaintSettings {
|
|
13
13
|
sidebarToggle?: boolean;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Pre-paint inline script body: restore persisted sidebar visibility to
|
|
17
|
+
* `<html data-sidebar-hidden>` before first paint to avoid a hard-reload flash.
|
|
18
|
+
*
|
|
19
|
+
* A tiny synchronous IIFE that reads `localStorage[SIDEBAR_STORAGE_KEY]` and
|
|
20
|
+
* sets `data-sidebar-hidden` only when the stored value is exactly "false"
|
|
21
|
+
* (the collapsed preference) — the default (visible) needs no attribute and
|
|
22
|
+
* causes no layout shift. Silently no-ops in privacy / disabled-storage modes.
|
|
23
|
+
*
|
|
24
|
+
* The storage key is interpolated from `SIDEBAR_STORAGE_KEY` so it stays in
|
|
25
|
+
* sync with the island's reader; the `data-sidebar-hidden` attribute name is a
|
|
26
|
+
* literal here (the island's `setDataAttribute` hardcodes the same literal —
|
|
27
|
+
* keep the two in sync). Intended for `<head>` placement so it executes before
|
|
28
|
+
* the `<aside>` sidebar is painted.
|
|
29
|
+
*/
|
|
30
|
+
export declare const SIDEBAR_VISIBILITY_PREPAINT_SCRIPT: string;
|
|
31
|
+
/**
|
|
32
|
+
* Create a `SidebarVisibilityPrepaint` component bound to the host's settings.
|
|
33
|
+
*
|
|
34
|
+
* Returns the pre-paint `<script>` (for the page `<head>`) when
|
|
35
|
+
* `settings.sidebarToggle` is enabled AND the page actually shows a sidebar;
|
|
36
|
+
* returns `undefined` when the toggle is disabled or the page hides the sidebar
|
|
37
|
+
* — the SAME gating as {@link createSidebarPrepaint}, so the head script and
|
|
38
|
+
* the afterSidebar toggle button always appear together.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createSidebarVisibilityPrepaint(settings: SidebarPrepaintSettings): (props: SidebarPrepaintProps) => JSX.Element | undefined;
|
|
15
41
|
/**
|
|
16
42
|
* Create a `SidebarPrepaint` component bound to the host's settings.
|
|
17
43
|
*
|
|
@@ -1,23 +1,43 @@
|
|
|
1
|
-
import { Fragment, jsx
|
|
1
|
+
import { Fragment, jsx } from "preact/jsx-runtime";
|
|
2
2
|
import { Island } from "@takazudo/zfb";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
DesktopSidebarToggle,
|
|
5
|
+
SIDEBAR_STORAGE_KEY
|
|
6
|
+
} from "../desktop-sidebar-toggle-island/index.js";
|
|
7
|
+
function sidebarPrepaintActive(settings, hideSidebar) {
|
|
8
|
+
return Boolean(settings.sidebarToggle) && !hideSidebar;
|
|
9
|
+
}
|
|
10
|
+
const SIDEBAR_VISIBILITY_PREPAINT_SCRIPT = `(function(){try{if(localStorage.getItem(${JSON.stringify(
|
|
11
|
+
SIDEBAR_STORAGE_KEY
|
|
12
|
+
)})==='false'){document.documentElement.setAttribute('data-sidebar-hidden','');}}catch(e){}})();`;
|
|
13
|
+
function createSidebarVisibilityPrepaint(settings) {
|
|
14
|
+
function SidebarVisibilityPrepaint({
|
|
15
|
+
hideSidebar
|
|
16
|
+
}) {
|
|
17
|
+
if (!sidebarPrepaintActive(settings, hideSidebar)) return void 0;
|
|
18
|
+
return /* @__PURE__ */ jsx(
|
|
19
|
+
"script",
|
|
20
|
+
{
|
|
21
|
+
dangerouslySetInnerHTML: { __html: SIDEBAR_VISIBILITY_PREPAINT_SCRIPT }
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return SidebarVisibilityPrepaint;
|
|
26
|
+
}
|
|
4
27
|
function createSidebarPrepaint(settings) {
|
|
5
28
|
function SidebarPrepaint({
|
|
6
29
|
hideSidebar
|
|
7
30
|
}) {
|
|
8
|
-
if (!settings
|
|
9
|
-
return /* @__PURE__ */
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Island({
|
|
14
|
-
when: "load",
|
|
15
|
-
children: /* @__PURE__ */ jsx(DesktopSidebarToggle, {})
|
|
16
|
-
})
|
|
17
|
-
] });
|
|
31
|
+
if (!sidebarPrepaintActive(settings, hideSidebar)) return void 0;
|
|
32
|
+
return /* @__PURE__ */ jsx(Fragment, { children: Island({
|
|
33
|
+
when: "load",
|
|
34
|
+
children: /* @__PURE__ */ jsx(DesktopSidebarToggle, {})
|
|
35
|
+
}) });
|
|
18
36
|
}
|
|
19
37
|
return SidebarPrepaint;
|
|
20
38
|
}
|
|
21
39
|
export {
|
|
22
|
-
|
|
40
|
+
SIDEBAR_VISIBILITY_PREPAINT_SCRIPT,
|
|
41
|
+
createSidebarPrepaint,
|
|
42
|
+
createSidebarVisibilityPrepaint
|
|
23
43
|
};
|
|
@@ -25,7 +25,8 @@ function makeUrlHelpers(settings, i18n) {
|
|
|
25
25
|
return settings.siteUrl ? settings.siteUrl.replace(/\/$/, "") + pageUrl : void 0;
|
|
26
26
|
}
|
|
27
27
|
function docsUrl(slug, lang = defaultLocale) {
|
|
28
|
-
const
|
|
28
|
+
const defaultPath = `/docs/${slug}`;
|
|
29
|
+
const path = lang === defaultLocale || isDefaultLocaleOnlyPath(defaultPath) ? defaultPath : `/${lang}/docs/${slug}`;
|
|
29
30
|
return withBase(path);
|
|
30
31
|
}
|
|
31
32
|
function isExternal(href) {
|
|
@@ -35,7 +36,7 @@ function makeUrlHelpers(settings, i18n) {
|
|
|
35
36
|
return isExternal(href) ? href : withBase(href);
|
|
36
37
|
}
|
|
37
38
|
function navHref(path, lang, currentVersion) {
|
|
38
|
-
const isNonDefaultLocale = lang != null && lang !== defaultLocale;
|
|
39
|
+
const isNonDefaultLocale = lang != null && lang !== defaultLocale && !isDefaultLocaleOnlyPath(path);
|
|
39
40
|
const versionPrefix = currentVersion ? `/v/${currentVersion}` : "";
|
|
40
41
|
return withBase(
|
|
41
42
|
isNonDefaultLocale ? `${versionPrefix}/${lang}${path}` : `${versionPrefix}${path}`
|
|
@@ -82,7 +83,8 @@ function makeUrlHelpers(settings, i18n) {
|
|
|
82
83
|
}));
|
|
83
84
|
}
|
|
84
85
|
function versionedDocsUrl(slug, versionSlug, lang = defaultLocale) {
|
|
85
|
-
const
|
|
86
|
+
const localePrefixed = lang !== defaultLocale && !isDefaultLocaleOnlyPath(`/docs/${slug}`);
|
|
87
|
+
const path = localePrefixed ? `/v/${versionSlug}/${lang}/docs/${slug}` : `/v/${versionSlug}/docs/${slug}`;
|
|
86
88
|
return withBase(path);
|
|
87
89
|
}
|
|
88
90
|
return {
|
|
@@ -8,7 +8,12 @@ import { AFTER_NAVIGATE_EVENT } from "../transitions/index.js";
|
|
|
8
8
|
|
|
9
9
|
export const SIDEBAR_STORAGE_KEY = "zudo-doc-sidebar-visible";
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// Exported for unit testing the mount-reconcile logic in a plain Node env
|
|
12
|
+
// (no jsdom in the package vitest config) — the same convention the sibling
|
|
13
|
+
// ThemeToggle uses via color-scheme-sync.ts. `readState` is the localStorage
|
|
14
|
+
// reader the mount effect uses to reconcile `visible` on initial load;
|
|
15
|
+
// `setDataAttribute` is the `<html data-sidebar-hidden>` writer.
|
|
16
|
+
export function readState(): boolean {
|
|
12
17
|
if (typeof window === "undefined") return true;
|
|
13
18
|
try {
|
|
14
19
|
return localStorage.getItem(SIDEBAR_STORAGE_KEY) !== "false";
|
|
@@ -17,7 +22,7 @@ function readState(): boolean {
|
|
|
17
22
|
}
|
|
18
23
|
}
|
|
19
24
|
|
|
20
|
-
function setDataAttribute(isVisible: boolean) {
|
|
25
|
+
export function setDataAttribute(isVisible: boolean) {
|
|
21
26
|
if (isVisible) {
|
|
22
27
|
document.documentElement.removeAttribute("data-sidebar-hidden");
|
|
23
28
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takazudo/zudo-doc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "zudo-doc framework primitives layer that sits on top of zfb's engine — sidebar, theme, TOC, breadcrumb, layouts, head injection, View Transitions, SSR-skip wrappers (per ADR-003).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -586,7 +586,7 @@
|
|
|
586
586
|
"zod": "^4.3.6",
|
|
587
587
|
"@takazudo/zfb": "0.1.0-next.76",
|
|
588
588
|
"@takazudo/zfb-runtime": "0.1.0-next.76",
|
|
589
|
-
"@takazudo/zudo-doc-history-server": "2.
|
|
589
|
+
"@takazudo/zudo-doc-history-server": "2.5.1"
|
|
590
590
|
},
|
|
591
591
|
"scripts": {
|
|
592
592
|
"build": "tsup && tsc -p tsconfig.build.json",
|