@takazudo/zudo-doc 0.2.0 → 0.2.2
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/README.md +53 -2
- package/dist/doclayout/doc-layout.js +25 -19
- package/dist/header/nav-active.d.ts +0 -11
- package/dist/header/nav-active.js +7 -2
- package/dist/html-preview-wrapper/html-preview-wrapper.d.ts +13 -0
- package/dist/html-preview-wrapper/html-preview-wrapper.js +12 -1
- package/dist/html-preview-wrapper/html-preview.d.ts +43 -2
- package/dist/html-preview-wrapper/html-preview.js +8 -2
- package/dist/integrations/claude-resources/generate.js +50 -54
- package/dist/integrations/doc-history/pre-build.d.ts +13 -5
- package/dist/integrations/doc-history/pre-build.js +11 -28
- package/dist/integrations/llms-txt/dev-middleware.d.ts +1 -0
- package/dist/integrations/llms-txt/emit.d.ts +1 -0
- package/dist/integrations/llms-txt/generate.d.ts +1 -0
- package/dist/integrations/llms-txt/index.d.ts +3 -2
- package/dist/integrations/llms-txt/load.d.ts +7 -35
- package/dist/integrations/llms-txt/load.js +21 -54
- package/dist/integrations/llms-txt/strip.d.ts +11 -9
- package/dist/integrations/llms-txt/strip.js +1 -3
- package/dist/integrations/llms-txt/types.d.ts +7 -20
- package/dist/integrations/search-index/collect.js +3 -2
- package/dist/md-utils/index.d.ts +76 -0
- package/dist/{integrations/search-index/content-files.js → md-utils/index.js} +12 -5
- package/dist/safelist.css +1 -1
- package/dist/sidebar-tree/category-meta.js +6 -1
- package/dist/theme/iframe-bridge.js +5 -3
- package/dist/theme/index.d.ts +2 -0
- package/dist/theme/theme-toggle.d.ts +7 -6
- package/dist/theme/theme-toggle.js +5 -81
- package/dist/theme-toggle/color-scheme-sync.d.ts +38 -0
- package/dist/theme-toggle/color-scheme-sync.js +22 -0
- package/dist/theme-toggle/index.d.ts +12 -0
- package/dist/theme-toggle/index.js +83 -0
- package/package.json +15 -6
- package/dist/integrations/search-index/content-files.d.ts +0 -42
|
@@ -1,45 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LlmsTxtLoadOptions, LlmsDocEntry } from './types.js';
|
|
2
|
+
export { collectMdFiles, isExcluded, parseMarkdownFile, slugToUrl } from '../../md-utils/index.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Filesystem loader for llms-txt content. Walks a markdown content root,
|
|
5
6
|
* parses frontmatter via `gray-matter`, and emits sorted
|
|
6
7
|
* {@link LlmsDocEntry} records ready for the generator stage.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* The directory walk / frontmatter / URL helpers live in the shared
|
|
10
|
+
* `md-utils` module (one implementation for search-index and llms-txt,
|
|
11
|
+
* zudo-doc#2024); they are re-exported here so the integration's public
|
|
12
|
+
* surface is unchanged.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
|
-
/**
|
|
15
|
-
* Walk `dir` recursively and return every `*.md` / `*.mdx` file's
|
|
16
|
-
* absolute path together with its docs-relative slug. Files (or whole
|
|
17
|
-
* directories) whose name starts with `_` are skipped, matching the
|
|
18
|
-
* Content Collections convention.
|
|
19
|
-
*/
|
|
20
|
-
declare function collectMdFiles(dir: string): Array<{
|
|
21
|
-
filePath: string;
|
|
22
|
-
slug: string;
|
|
23
|
-
}>;
|
|
24
|
-
/**
|
|
25
|
-
* Compute a docs URL for a given slug + locale. Mirrors the legacy
|
|
26
|
-
* `slugToUrl(slug, locale, true)` call site exactly: when `siteUrl` is
|
|
27
|
-
* empty, the URL is path-only; when `siteUrl` is set, a fully qualified
|
|
28
|
-
* URL is produced.
|
|
29
|
-
*/
|
|
30
|
-
declare function slugToUrl(slug: string, locale: string | null, base: string, siteUrl?: string): string;
|
|
31
|
-
/** Whether a given doc should be excluded from the llms.txt index.
|
|
32
|
-
* `category_no_page` index files are metadata-only with no built route, so
|
|
33
|
-
* they are excluded alongside search_exclude / draft / unlisted. */
|
|
34
|
-
declare function isExcluded(data: LlmsTxtFrontmatter): boolean;
|
|
35
|
-
/**
|
|
36
|
-
* Parse a markdown file into frontmatter + body. Returns `null` when
|
|
37
|
-
* the file is unreadable so callers can simply skip it.
|
|
38
|
-
*/
|
|
39
|
-
declare function parseMarkdownFile(filePath: string): {
|
|
40
|
-
data: LlmsTxtFrontmatter;
|
|
41
|
-
content: string;
|
|
42
|
-
} | null;
|
|
43
15
|
/**
|
|
44
16
|
* Build the sorted {@link LlmsDocEntry} list for a single content root.
|
|
45
17
|
* Excluded pages (draft / unlisted / search_exclude) are dropped.
|
|
@@ -50,4 +22,4 @@ declare function parseMarkdownFile(filePath: string): {
|
|
|
50
22
|
*/
|
|
51
23
|
declare function loadDocEntries(options: LlmsTxtLoadOptions): LlmsDocEntry[];
|
|
52
24
|
|
|
53
|
-
export {
|
|
25
|
+
export { loadDocEntries };
|
|
@@ -1,62 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (entry.isDirectory()) {
|
|
17
|
-
if (entry.name.startsWith("_")) continue;
|
|
18
|
-
walk(fullPath, baseDir);
|
|
19
|
-
} else if (/\.mdx?$/.test(entry.name) && !entry.name.startsWith("_")) {
|
|
20
|
-
const rel = relative(baseDir, fullPath).replace(/\.mdx?$/, "").replace(/\/index$/, "").replace(/^index$/, "");
|
|
21
|
-
results.push({ filePath: fullPath, slug: rel });
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
walk(dir, dir);
|
|
26
|
-
return results;
|
|
27
|
-
}
|
|
28
|
-
function slugToUrl(slug, locale, base, siteUrl) {
|
|
29
|
-
const trimmedBase = base.replace(/\/$/, "");
|
|
30
|
-
const path = locale ? `${trimmedBase}/${locale}/docs/${slug}` : `${trimmedBase}/docs/${slug}`;
|
|
31
|
-
if (siteUrl) {
|
|
32
|
-
return `${siteUrl.replace(/\/$/, "")}${path}`;
|
|
33
|
-
}
|
|
34
|
-
return path;
|
|
35
|
-
}
|
|
36
|
-
function isExcluded(data) {
|
|
37
|
-
return Boolean(
|
|
38
|
-
data.search_exclude || data.draft || data.unlisted || data.category_no_page
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
function parseMarkdownFile(filePath) {
|
|
42
|
-
try {
|
|
43
|
-
const raw = readFileSync(filePath, "utf-8");
|
|
44
|
-
const parsed = matter(raw);
|
|
45
|
-
return { data: parsed.data, content: parsed.content };
|
|
46
|
-
} catch {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
collectMdFiles,
|
|
4
|
+
isExcluded,
|
|
5
|
+
parseMarkdownFile,
|
|
6
|
+
slugToUrl,
|
|
7
|
+
stripMarkdown
|
|
8
|
+
} from "../../md-utils/index.js";
|
|
9
|
+
import { stripImportsAndJsx } from "./strip.js";
|
|
10
|
+
import {
|
|
11
|
+
collectMdFiles as collectMdFiles2,
|
|
12
|
+
isExcluded as isExcluded2,
|
|
13
|
+
parseMarkdownFile as parseMarkdownFile2,
|
|
14
|
+
slugToUrl as slugToUrl2
|
|
15
|
+
} from "../../md-utils/index.js";
|
|
50
16
|
function loadDocEntries(options) {
|
|
51
17
|
const { contentDir, locale, base, siteUrl } = options;
|
|
52
18
|
const absDir = resolve(contentDir);
|
|
53
19
|
const files = collectMdFiles(absDir);
|
|
54
20
|
const entries = [];
|
|
55
|
-
for (const { filePath, slug } of files) {
|
|
21
|
+
for (const { filePath, slug: fileSlug } of files) {
|
|
56
22
|
const parsed = parseMarkdownFile(filePath);
|
|
57
23
|
if (!parsed) continue;
|
|
58
24
|
const { data, content } = parsed;
|
|
59
25
|
if (isExcluded(data)) continue;
|
|
26
|
+
const slug = data.slug ?? fileSlug;
|
|
60
27
|
let description = data.description ?? "";
|
|
61
28
|
if (!description) {
|
|
62
29
|
const stripped = stripMarkdown(content);
|
|
@@ -78,9 +45,9 @@ function loadDocEntries(options) {
|
|
|
78
45
|
return entries;
|
|
79
46
|
}
|
|
80
47
|
export {
|
|
81
|
-
collectMdFiles,
|
|
82
|
-
isExcluded,
|
|
48
|
+
collectMdFiles2 as collectMdFiles,
|
|
49
|
+
isExcluded2 as isExcluded,
|
|
83
50
|
loadDocEntries,
|
|
84
|
-
parseMarkdownFile,
|
|
85
|
-
slugToUrl
|
|
51
|
+
parseMarkdownFile2 as parseMarkdownFile,
|
|
52
|
+
slugToUrl2 as slugToUrl
|
|
86
53
|
};
|
|
@@ -1,22 +1,24 @@
|
|
|
1
|
+
export { stripMarkdown } from '../../md-utils/index.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Markdown / JSX text-stripping helpers.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
6
|
+
* `stripImportsAndJsx` matches the legacy Astro emitter's behaviour
|
|
5
7
|
* byte-for-byte. Any change here is a behaviour change and must be
|
|
6
8
|
* reflected in the byte-equality fixture corpus.
|
|
9
|
+
*
|
|
10
|
+
* `stripMarkdown` (used to derive a fallback `description` from the body
|
|
11
|
+
* when frontmatter doesn't carry one) lives in the shared `md-utils`
|
|
12
|
+
* module — one implementation for search-index and llms-txt
|
|
13
|
+
* (zudo-doc#2024) — and is re-exported here to keep the public surface
|
|
14
|
+
* unchanged.
|
|
7
15
|
*/
|
|
16
|
+
|
|
8
17
|
/**
|
|
9
18
|
* Strip imports, exports, and HTML/JSX tags from MDX/MD content while
|
|
10
19
|
* keeping the prose layout (headings, lists, blockquotes) intact for
|
|
11
20
|
* LLM consumption. Used by `llms-full.txt`.
|
|
12
21
|
*/
|
|
13
22
|
declare function stripImportsAndJsx(content: string): string;
|
|
14
|
-
/**
|
|
15
|
-
* Strip markdown formatting and produce plain prose. Used to derive a
|
|
16
|
-
* fallback `description` from the body when frontmatter doesn't carry
|
|
17
|
-
* one. The regex sequence matches the legacy `stripMarkdown` helper in
|
|
18
|
-
* `src/utils/content-files.ts` — keep them in sync.
|
|
19
|
-
*/
|
|
20
|
-
declare function stripMarkdown(md: string): string;
|
|
21
23
|
|
|
22
|
-
export { stripImportsAndJsx
|
|
24
|
+
export { stripImportsAndJsx };
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
+
import { stripMarkdown } from "../../md-utils/index.js";
|
|
1
2
|
function stripImportsAndJsx(content) {
|
|
2
3
|
return content.replace(/^import\s+.*$/gm, "").replace(/^export\s+.*$/gm, "").replace(/<\/?[a-zA-Z][a-zA-Z0-9]*[^>]*>/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
3
4
|
}
|
|
4
|
-
function stripMarkdown(md) {
|
|
5
|
-
return md.replace(/```[\s\S]*?```/g, "").replace(/`[^`]+`/g, "").replace(/<[^>]+>/g, "").replace(/^#{1,6}\s+/gm, "").replace(/\*{1,3}([^*]+)\*{1,3}/g, "$1").replace(/_{1,3}([^_]+)_{1,3}/g, "$1").replace(/!\[[^\]]*\]\([^)]+\)/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s+/gm, "").replace(/^[-*_]{3,}\s*$/gm, "").replace(/^[\s]*[-*+]\s+/gm, "").replace(/^[\s]*\d+\.\s+/gm, "").replace(/^import\s+.*$/gm, "").replace(/^export\s+.*$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
6
|
-
}
|
|
7
5
|
export {
|
|
8
6
|
stripImportsAndJsx,
|
|
9
7
|
stripMarkdown
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { MdDocFrontmatter } from '../../md-utils/index.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Public types for the framework-agnostic llms-txt build emitter.
|
|
3
5
|
*
|
|
@@ -7,29 +9,14 @@
|
|
|
7
9
|
* markdown content roots — Astro today, zfb tomorrow, plain Node tooling
|
|
8
10
|
* in a unit test, etc.
|
|
9
11
|
*/
|
|
12
|
+
|
|
10
13
|
/**
|
|
11
14
|
* Frontmatter fields the loader reads off of each MDX/MD file. Callers
|
|
12
|
-
* are free to extend this; unrecognised fields are ignored.
|
|
15
|
+
* are free to extend this; unrecognised fields are ignored. Alias of the
|
|
16
|
+
* shared {@link MdDocFrontmatter} shape (`md-utils`, zudo-doc#2024) —
|
|
17
|
+
* search-index and llms-txt read the same fields.
|
|
13
18
|
*/
|
|
14
|
-
|
|
15
|
-
title?: string;
|
|
16
|
-
description?: string;
|
|
17
|
-
sidebar_position?: number;
|
|
18
|
-
draft?: boolean;
|
|
19
|
-
unlisted?: boolean;
|
|
20
|
-
/**
|
|
21
|
-
* `true` when the page is excluded from search and llms.txt indexing.
|
|
22
|
-
* Mirrors the `search_exclude` flag used by the existing project.
|
|
23
|
-
*/
|
|
24
|
-
search_exclude?: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* A `category_no_page` index.mdx carries category metadata only and builds
|
|
27
|
-
* no route — so it must NOT appear in llms.txt (the link would 404). Mirrors
|
|
28
|
-
* the route/sitemap/search exclusion in the host project.
|
|
29
|
-
*/
|
|
30
|
-
category_no_page?: boolean;
|
|
31
|
-
[key: string]: unknown;
|
|
32
|
-
}
|
|
19
|
+
type LlmsTxtFrontmatter = MdDocFrontmatter;
|
|
33
20
|
/**
|
|
34
21
|
* One entry in the generated llms.txt / llms-full.txt files. Pure data,
|
|
35
22
|
* already sorted, already stripped — generators (`generateLlmsTxt`,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
parseMarkdownFile,
|
|
6
6
|
slugToUrl,
|
|
7
7
|
stripMarkdown
|
|
8
|
-
} from "
|
|
8
|
+
} from "../../md-utils/index.js";
|
|
9
9
|
import {
|
|
10
10
|
MAX_BODY_LENGTH
|
|
11
11
|
} from "./types.js";
|
|
@@ -16,11 +16,12 @@ function buildEntries(contentDir, locale, base) {
|
|
|
16
16
|
const absDir = resolve(contentDir);
|
|
17
17
|
const files = collectMdFiles(absDir);
|
|
18
18
|
const entries = [];
|
|
19
|
-
for (const { filePath, slug } of files) {
|
|
19
|
+
for (const { filePath, slug: fileSlug } of files) {
|
|
20
20
|
const parsed = parseMarkdownFile(filePath);
|
|
21
21
|
if (!parsed) continue;
|
|
22
22
|
const { data, content } = parsed;
|
|
23
23
|
if (isExcluded(data)) continue;
|
|
24
|
+
const slug = data.slug ?? fileSlug;
|
|
24
25
|
const id = locale ? `${locale}/${slug}` : slug;
|
|
25
26
|
entries.push({
|
|
26
27
|
id,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontmatter fields the integrations read off each MDX/MD file. Extra
|
|
3
|
+
* keys pass through via the index signature. Mirrors the host project's
|
|
4
|
+
* docs schema (`src/config/docs-schema.ts`) for the fields listed here.
|
|
5
|
+
*/
|
|
6
|
+
interface MdDocFrontmatter {
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Route-slug override (docs-schema `slug:`). The route layer resolves
|
|
11
|
+
* `data.slug ?? toRouteSlug(id)`, so every consumer that derives a URL
|
|
12
|
+
* must apply the same `data.slug ?? filesystemSlug` fallback at its
|
|
13
|
+
* call site — otherwise links to overridden pages 404. Deliberately NOT
|
|
14
|
+
* folded into {@link slugToUrl}: the effective slug is computed where
|
|
15
|
+
* the frontmatter is in scope and passed through.
|
|
16
|
+
*/
|
|
17
|
+
slug?: string;
|
|
18
|
+
sidebar_position?: number;
|
|
19
|
+
draft?: boolean;
|
|
20
|
+
unlisted?: boolean;
|
|
21
|
+
/** `true` when the page is excluded from search and llms.txt indexing. */
|
|
22
|
+
search_exclude?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* A `category_no_page` index.mdx carries category metadata only and builds
|
|
25
|
+
* no route — so it must NOT be indexed or linked (the link would 404).
|
|
26
|
+
* Mirrors the route/sitemap exclusion in the host project.
|
|
27
|
+
*/
|
|
28
|
+
category_no_page?: boolean;
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Strip markdown formatting to plain text. Conservative-by-design — the
|
|
33
|
+
* regex pipeline matches the legacy Astro integrations byte-for-byte so
|
|
34
|
+
* search excerpts and llms-txt descriptions stay byte-equal across the
|
|
35
|
+
* cutover. Do not add new rules without also updating the byte-equality
|
|
36
|
+
* fixtures (topic-plugin-audit).
|
|
37
|
+
*/
|
|
38
|
+
declare function stripMarkdown(md: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Walk `dir` recursively and return every `*.md` / `*.mdx` file's
|
|
41
|
+
* absolute path together with its filesystem-derived slug. Files (or
|
|
42
|
+
* whole directories) whose name starts with `_` are skipped, matching
|
|
43
|
+
* the Content Collections convention (and zfb's routing): docs under
|
|
44
|
+
* them are not built as pages, so emitting them would yield entries
|
|
45
|
+
* whose links 404.
|
|
46
|
+
*/
|
|
47
|
+
declare function collectMdFiles(dir: string): Array<{
|
|
48
|
+
filePath: string;
|
|
49
|
+
slug: string;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Compute a docs URL for a given slug + locale, honouring `base`. When
|
|
53
|
+
* `siteUrl` is empty/omitted the URL is path-only; when set, a fully
|
|
54
|
+
* qualified URL is produced (matching the legacy llms-txt emitter's
|
|
55
|
+
* `slugToUrl(slug, locale, true)` call site exactly).
|
|
56
|
+
*
|
|
57
|
+
* Callers must pass the EFFECTIVE slug (`data.slug ?? filesystem slug`) —
|
|
58
|
+
* the frontmatter override is resolved at the call site, not here.
|
|
59
|
+
*/
|
|
60
|
+
declare function slugToUrl(slug: string, locale: string | null, base: string, siteUrl?: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Parse a markdown file into frontmatter + body. Returns `null` when
|
|
63
|
+
* the file is unreadable so callers can simply skip it.
|
|
64
|
+
*/
|
|
65
|
+
declare function parseMarkdownFile(filePath: string): {
|
|
66
|
+
data: MdDocFrontmatter;
|
|
67
|
+
content: string;
|
|
68
|
+
} | null;
|
|
69
|
+
/**
|
|
70
|
+
* A page is excluded from indexing when explicitly opted out, drafted,
|
|
71
|
+
* unlisted, or a `category_no_page` metadata-only category index (no
|
|
72
|
+
* built route to link to).
|
|
73
|
+
*/
|
|
74
|
+
declare function isExcluded(data: MdDocFrontmatter): boolean;
|
|
75
|
+
|
|
76
|
+
export { type MdDocFrontmatter, collectMdFiles, isExcluded, parseMarkdownFile, slugToUrl, stripMarkdown };
|
|
@@ -27,20 +27,27 @@ function collectMdFiles(dir) {
|
|
|
27
27
|
walk(dir, dir);
|
|
28
28
|
return results;
|
|
29
29
|
}
|
|
30
|
-
function slugToUrl(slug, locale, base) {
|
|
31
|
-
const
|
|
32
|
-
|
|
30
|
+
function slugToUrl(slug, locale, base, siteUrl) {
|
|
31
|
+
const trimmedBase = base.replace(/\/$/, "");
|
|
32
|
+
const path = locale ? `${trimmedBase}/${locale}/docs/${slug}` : `${trimmedBase}/docs/${slug}`;
|
|
33
|
+
if (siteUrl) {
|
|
34
|
+
return `${siteUrl.replace(/\/$/, "")}${path}`;
|
|
35
|
+
}
|
|
36
|
+
return path;
|
|
33
37
|
}
|
|
34
38
|
function parseMarkdownFile(filePath) {
|
|
35
39
|
try {
|
|
36
40
|
const raw = readFileSync(filePath, "utf-8");
|
|
37
|
-
|
|
41
|
+
const parsed = matter(raw);
|
|
42
|
+
return { data: parsed.data, content: parsed.content };
|
|
38
43
|
} catch {
|
|
39
44
|
return null;
|
|
40
45
|
}
|
|
41
46
|
}
|
|
42
47
|
function isExcluded(data) {
|
|
43
|
-
return
|
|
48
|
+
return Boolean(
|
|
49
|
+
data.search_exclude || data.draft || data.unlisted || data.category_no_page
|
|
50
|
+
);
|
|
44
51
|
}
|
|
45
52
|
export {
|
|
46
53
|
collectMdFiles,
|
package/dist/safelist.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/* generated by gen-safelist.mjs — do not edit by hand */
|
|
2
|
-
@source inline("-mb-px [&::-webkit-details-marker]:hidden [&_a]:text-accent [&_a]:underline [&_nav]:mb-0 [doc-history-meta] [doc-layout] a a2 abbr above absolute active actual after after-breadcrumb after-content after-sidebar after-title against agent agents ai-chat ai-chat-trigger align-top all allow-same-origin allow-scripts already-executed an anchor anchored and announce antialiased any application/json applies apply-css-vars are area arg 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 article as asc aside assets assigning assistant async at attach attribute attributes available await away b back backdrop:bg-bg/80 background backtick backticks baked banner bare based be because before below between bg bg-[#fff] bg-accent bg-bg bg-code-bg bg-fg bg-info/10 bg-muted bg-surface bg-transparent bg-warning/10 bg-warning/5 bi bigint bin blob block blockquote blocks blur body body-end-components body-end-scripts boolean bootstrap border border-accent border-b border-b-2 border-b-[5px] border-collapse border-fg border-l border-l-[3px] border-muted border-none border-r border-t border-t-[2px] border-t-[3px] border-transparent border-warning/30 both br breadcrumb:end breadcrumb:start break-words browser browsers btn bundler but button buttons by caller can cancellation cannot canonical caption cases cat-nav- catch category catppuccin-latte checkbox child ci circle cite class class-less claude claude-agents claude-commands claude-md claude-skills cleaned clear clear-css-vars click client clip close code code-block-sr-announce col col-resize colgroup collision color color-scheme color-scheme-changed color-scheme-provider color-tweak colors command commands commit component component:github-link component:language-switcher component:search component:theme-toggle component:version-switcher concrete configuration configure configured consumer container containers content content-type content-wrapper:end content-wrapper:start contents controller converts copy correct covered covers crashes crumb- css cursor cursor-not-allowed cursor-pointer custom dark data-active data-group-id data-header data-header-logo data-header-nav data-header-right data-mermaid-rendered data-nav-item data-nav-item-dropdown data-nav-more data-nav-more-menu data-nav-more-toggle data-sidebar-resizer data-tab-btn data-tab-default data-tab-label data-tab-value data-tabs data-taglist-group data-testid data-theme data-variant data-version-banner data-version-menu data-version-switcher data-version-toggle data-zfb-transition-persist dd decimal declare decoration-muted default defaults del desc description design design-token-panel design-token-trigger desktop desktop-sidebar detached details deterministic dfn diagram diagrams dialog dieser directories directory disc display:none dist div dl doc doc-card- doc-history doc-history-generate does drag draggable dropdown dropdowns dt duration-150 duration-200 during dynamically e2e earlier edge el element elements els else em emit emitting empty en entries entry error escape even eventually every exactly exit expected failed fall fallback falls false fast feed fg fieldset figcaption figure file fill fills finally fire fires first fixed fixtures flex flex-1 flex-col flex-wrap flip flipping flips focus focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2 focus-visible:underline focus:underline font font-bold font-medium font-mono font-semibold footer for form free from frontmatter frontmatter-preview full function further g gap-[clamp(1.5rem,3vw,4rem)] gap-hsp-2xs gap-hsp-sm gap-hsp-xs gap-vsp-lg 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 generate generation geometry get github github-link go got gray-matter grid grid-cols-1 group group-focus-visible:underline group-focus-within:block group-hover:bg-fg group-hover:block group-hover:text-bg group-hover:underline group-open:rotate-90 guard h-[0.5rem] h-[0.875rem] h-[1.575rem] h-[1lh] h-[3.5rem] h-[calc(100vh-3.5rem)] h-icon-sm h-icon-xs h1 h2 h3 h4 h5 h6 hand handle handled handlers has hash-link have head head-links head-scripts header header-call:end header-call:start height here hex hidden 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:border-accent hover:border-accent-hover hover:border-fg hover:text-accent hover:text-accent-hover hover:text-fg hover:underline hr html i i2 i3 i4 identical if iframe img import imports in index inherit initial initialised inline inline-block inline-flex input ins inset-0 inside instanceof instructions intent into inverse invoke is it italic item- items items-center items-start itself javascript justify-between justify-center justify-end kbd keep keeps kept keydown khroma label landing language-switcher last:border-b-0 launch leading-relaxed leading-snug leading-tight leaf- leaving left left-0 left:calc legend lg lg:block lg:flex lg:grid-cols-[repeat(auto-fit,minmax(12rem,1fr))] lg:ml-[var(--zd-sidebar-w)] lg:pt-vsp-2xl lg:px-hsp-2xl lg:py-vsp-2xl li li2 light like line link link- list-disc list-none listener literal lives llms llms-txt load local lostpointercapture luminance m m-0 main make maps mark marks matching max-h-[80vh] max-w-[46rem] max-w-[80rem] max-w-[clamp(50rem,75vw,90rem)] max-w-full max-w-none mb-0 mb-vsp-2xs mb-vsp-lg mb-vsp-md mb-vsp-sm mb-vsp-xl mb-vsp-xs menu merged mermaid message meta metadata min-h-[calc(100vh-3.5rem)] min-h-screen min-w-0 min-w-[10rem] min-w-[8rem] mirror missing ml-auto ml-hsp-2xl ml-hsp-lg ml-hsp-sm ml-hsp-xl mod mode mounted mouseenter mouseleave mt-0 mt-vsp-2xs mt-vsp-3xs mt-vsp-lg mt-vsp-sm mt-vsp-xl must mutates mx-auto my-vsp-lg my-vsp-md name named native nav nav-card- navigating navigation navigations near needs new next no-underline node:fs node:module node:os node:path nodes nofollow noindex non-empty non-string none noopener noreferrer normal not not-object note now null number object observe observer of og:description og:image og:title og:type og:url ol older on once one only opacity-60 option or other others out overflow overflow-auto overflow-hidden overflow-x-auto overflow-y-auto overflow-y:auto own p p-0 p-hsp-lg p-hsp-md p-hsp-sm p-hsp-xl packages padding page page-loading-overlay page-loading-spinner page-navigate-end pages paint panel panels parent parse parsed pass path pb-vsp-xl per pi pick picked picks pins pipelines pl-[1.25rem] pl-hsp-lg pl-hsp-sm pl-hsp-xl place pointer-events-none pointercancel pointerdown pointermove pointerup polite polyline populates port position position:fixed pr-hsp-lg pr-hsp-md pr-hsp-xl pre preact preact/hooks preact/jsx-runtime preload pres produced produces production propagating properties property proxy pt-vsp-3xs pt-vsp-md pt-vsp-sm pt-vsp-xl pt-vsp-xs ptag- public purely px px-hsp-lg px-hsp-md px-hsp-sm px-hsp-xl px-hsp-xs py-0 py-hsp-2xs py-hsp-sm py-hsp-xs py-vsp-2xs py-vsp-3xs py-vsp-md py-vsp-sm py-vsp-xl py-vsp-xs q r raw re-render re-run reach reaches reading ready real references regenerates reinit relative remains render rendered renders reorder replaced repopulate requires reserved resize resize-x resolve resolved resolves return returns right- right-0 ro robots role root rotate-180 rotate-90 round round-trip rounded rounded-full rounded-lg running runs runtime s safer samp schema-mismatch schema-missing script script-evaluation scripts scroll scrollbar scrolled scrollend search section section- see sel-bg sel-fg select select-none self-start separator server server-rendered set shadow-[0_1px_3px_color-mix(in_srgb,var(--color-fg)_8%,transparent)] shadow-lg shared shiki ships should show shown shrink-0 sidebar signal single sitemap- size skill skills slash slug sm:flex-row sm:grid-cols-2 sm:items-center sm:justify-between small so solid some source space-y-vsp-2xs spacing span spans spec specifiers sr-only stale state stay sticky still stored stray string strip stroke-linecap stroke-linejoin stroke-width strong style styles stylesheet sub subagents subsequent success summary sup support survives svg synchronous synchronously syntactically syntect tab tab-panel tabindex table tablist tabpanel tabs tabs-container tabs-content tabs-nav tag- tag-item- tbody td temp temp-element template temporary temporary-element test-results text text-accent text-bg text-body text-caption text-center text-code-fg text-fg text-heading text-info text-left text-micro text-muted text-muted/50 text-small text-title text-warning textarea tfoot th that the thead them theme theme-color theme-toggle then these they this through time title to toggle toggle-ai-chat toggle-design-token-panel token tokens tolerates too top-0 top-[3.5rem] top-full total tr tracking-wider trade-off transition-[background,color,border-color] transition-colors transition-transform tree-child- tree-item- tree-top- trick trigger trigger:ai-chat trigger:design-token-panel triggers true try twitter:card twitter:creator twitter:description twitter:image twitter:site twitter:title two u ul unavailable undefined under underline understand unknown unmaintained unobserve unreadable unreleased unreliable unset unsupported up uppercase use used uses utf-8 utf8 utilities v val value values var variable version- version-menu version-switcher vertical via video viewport visible vitesse-dark w w-[0.5rem] w-[0.875rem] w-[1.575rem] w-[16px] w-[280px] w-[var(--zd-sidebar-w)] w-full w-icon-sm w-icon-xs was watching wbr wbr- we when where whereas which while whitespace-nowrap whitespace-pre whole will with without word worktrees would wrap wrapper wrappers written wrote xl:flex xl:hidden y-scrollbar yet z-10 z-30 z-50 zd-content zd-html-preview-code zfb zfb:after-swap zfb:before-preparation zudo-doc-design-tokens/v1 zudo-doc-sidebar-width zudo-doc-theme zudo-doc-theme-bridge zudo-doc-tweak-state zudo-doc-tweak-state-v2");
|
|
2
|
+
@source inline("-mb-px [&::-webkit-details-marker]:hidden [&_a]:text-accent [&_a]:underline [&_nav]:mb-0 [doc-history-meta] [doc-layout] a a2 abbr above absolute active actual after after-breadcrumb after-content after-sidebar after-title against agent agents ai-chat ai-chat-trigger align-top all allow-same-origin allow-scripts already-executed an anchor anchored and announce antialiased any application/json applies apply-css-vars are area arg 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 article as asc aside assets assigning assistant async at attach attribute attributes available await away b back backdrop:bg-bg/80 background backtick backticks baked banner bare based be because before below between bg bg-[#fff] bg-accent bg-bg bg-code-bg bg-fg bg-info/10 bg-muted bg-surface bg-transparent bg-warning/10 bg-warning/5 bi bigint bin blob block blockquote blocks blur body body-end-components body-end-scripts boolean bootstrap border border-accent border-b border-b-2 border-b-[5px] border-collapse border-fg border-l border-l-[3px] border-muted border-none border-r border-t border-t-[2px] border-t-[3px] border-transparent border-warning/30 both br breadcrumb:end breadcrumb:start break-words browser browsers btn bundler but button buttons by caller can cancellation cannot canonical caption cases cat-nav- catch category catppuccin-latte checkbox child ci circle cite class class-less claude claude-agents claude-commands claude-md claude-skills cleaned clear clear-css-vars click client clip close code code-block-sr-announce col col-resize colgroup collision color color-scheme color-scheme-changed color-scheme-provider color-tweak colors command commands commit component component:github-link component:language-switcher component:search component:theme-toggle component:version-switcher concrete configuration configure configured consumer container containers content content-type content-wrapper:end content-wrapper:start contents controller converts copy correct covered covers crashes crumb- css cursor cursor-not-allowed cursor-pointer custom dark data-active data-group-id data-header data-header-logo data-header-nav data-header-right data-mermaid-rendered data-nav-item data-nav-item-dropdown data-nav-more data-nav-more-menu data-nav-more-toggle data-sidebar-resizer data-tab-btn data-tab-default data-tab-label data-tab-value data-tabs data-taglist-group data-testid data-theme data-variant data-version-banner data-version-menu data-version-switcher data-version-toggle data-zfb-transition-persist dd decimal declare decoration-muted default defaults del desc description design design-token-panel design-token-trigger desktop desktop-sidebar detached details deterministic dfn diagram diagrams dialog dieser directories directory disc display:none dist div dl doc doc-card- doc-history doc-history-generate does drag draggable dropdown dropdowns dt duration-150 duration-200 during dynamically e2e earlier edge el element elements els else em emit emitting empty en entries entry error escape even eventually every exactly exit expected failed fall fallback falls false fast feed fg fieldset figcaption figure file fill fills finally fire fires first fixed fixtures flex flex-1 flex-col flex-wrap flip flipping flips focus focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2 focus-visible:underline focus:underline font font-bold font-medium font-mono font-semibold footer for form free from frontmatter frontmatter-preview full function further g gap-[clamp(1.5rem,3vw,4rem)] gap-hsp-2xs gap-hsp-sm gap-hsp-xs gap-vsp-lg 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 generate generation geometry get github github-link go got gray-matter grid grid-cols-1 group group-focus-visible:underline group-focus-within:block group-hover:bg-fg group-hover:block group-hover:text-bg group-hover:underline group-open:rotate-90 guard h-[0.5rem] h-[0.875rem] h-[1.575rem] h-[1lh] h-[3.5rem] h-[calc(100vh-3.5rem)] h-icon-sm h-icon-xs h1 h2 h3 h4 h5 h6 hand handle handled handlers has hash-link have head head-links head-scripts header header-call:end header-call:start height here hex hidden 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:border-accent hover:border-accent-hover hover:border-fg hover:text-accent hover:text-accent-hover hover:text-fg hover:underline hr html i i2 i3 i4 identical if iframe img import imports in index inherit initial initialised inline inline-block inline-flex input ins inset-0 inside instanceof instructions intent into inverse invoke is it italic item- items items-center items-start itself javascript justify-between justify-center justify-end kbd keep keeps kept keydown khroma label landing language-switcher last:border-b-0 launch leading-relaxed leading-snug leading-tight leaf- leaving left left-0 left:calc legend lg lg:block lg:flex lg:grid-cols-[repeat(auto-fit,minmax(12rem,1fr))] lg:ml-[var(--zd-sidebar-w)] lg:pt-vsp-2xl lg:px-hsp-2xl lg:py-vsp-2xl li li2 light like line link link- list-disc list-none listener literal lives llms llms-txt load local lostpointercapture luminance m m-0 main make maps mark marks matching max-h-[80vh] max-w-[46rem] max-w-[80rem] max-w-[clamp(50rem,75vw,90rem)] max-w-full max-w-none mb-0 mb-vsp-2xs mb-vsp-lg mb-vsp-md mb-vsp-sm mb-vsp-xl mb-vsp-xs menu merged mermaid message meta metadata min-h-[calc(100vh-3.5rem)] min-h-screen min-w-0 min-w-[10rem] min-w-[8rem] mirror missing ml-auto ml-hsp-2xl ml-hsp-lg ml-hsp-sm ml-hsp-xl mod mode mounted mouseenter mouseleave mt-0 mt-vsp-2xs mt-vsp-3xs mt-vsp-lg mt-vsp-sm mt-vsp-xl must mutates mx-auto my-vsp-lg my-vsp-md name named native nav nav-card- navigating navigation navigations near needs new next no-underline node:fs node:module node:path nodes nofollow noindex non-empty non-string none noopener noreferrer normal not not-object note now null number object observe observer of og:description og:image og:title og:type og:url ol older on once one only opacity-60 option or other others out overflow overflow-auto overflow-hidden overflow-x-auto overflow-y-auto overflow-y:auto own p p-0 p-hsp-lg p-hsp-md p-hsp-sm p-hsp-xl packages padding page page-loading-overlay page-loading-spinner page-navigate-end pages paint panel panels parent parse parsed pass path pb-vsp-xl per pi pick picked picks pins pipelines pl-[1.25rem] pl-hsp-lg pl-hsp-sm pl-hsp-xl place pointer-events-none pointercancel pointerdown pointermove pointerup polite polyline populates port position position:fixed pr-hsp-lg pr-hsp-md pr-hsp-xl pre preact preact/hooks preact/jsx-runtime preload pres produced produces production propagating properties property proxy pt-vsp-3xs pt-vsp-md pt-vsp-sm pt-vsp-xl pt-vsp-xs ptag- public purely px px-hsp-lg px-hsp-md px-hsp-sm px-hsp-xl px-hsp-xs py-0 py-hsp-2xs py-hsp-sm py-hsp-xs py-vsp-2xs py-vsp-3xs py-vsp-md py-vsp-sm py-vsp-xl py-vsp-xs q r raw re-render re-run reach reaches reading ready real references regenerates reinit relative remains render rendered renders reorder replaced repopulate requires reserved resize resize-x resolve resolved resolves return returns right- right-0 ro robots role root rotate-180 rotate-90 round round-trip rounded rounded-full rounded-lg running runs runtime s safer samp schema-mismatch schema-missing script script-evaluation scripts scroll scrollbar scrolled scrollend search section section- see sel-bg sel-fg select select-none self-start separator server server-rendered set shadow-[0_1px_3px_color-mix(in_srgb,var(--color-fg)_8%,transparent)] shadow-lg shared shiki ships should show shown shrink-0 sidebar signal single sitemap- size skill skills slash slug sm:flex-row sm:grid-cols-2 sm:items-center sm:justify-between small so solid some source space-y-vsp-2xs spacing span spans spec specifiers sr-only stale state stay sticky still stored stray string strip stroke-linecap stroke-linejoin stroke-width strong style styles stylesheet sub subagents subsequent success summary sup support survives svg synchronous synchronously syntactically syntect tab tab-panel tabindex table tablist tabpanel tabs tabs-container tabs-content tabs-nav tag- tag-item- tbody td temp temp-element template temporary temporary-element test-results text text-accent text-bg text-body text-caption text-center text-code-fg text-fg text-heading text-info text-left text-micro text-muted text-muted/50 text-small text-title text-warning textarea tfoot th that the thead them theme theme-color theme-toggle then these they this through time title to toggle toggle-ai-chat toggle-design-token-panel token tokens tolerates too top-0 top-[3.5rem] top-full total tr tracking-wider trade-off transition-[background,color,border-color] transition-colors transition-transform tree-child- tree-item- tree-top- trick trigger trigger:ai-chat trigger:design-token-panel triggers true try twitter:card twitter:creator twitter:description twitter:image twitter:site twitter:title two u ul unavailable undefined under underline understand unknown unmaintained unobserve unreadable unreleased unreliable unset unsupported up uppercase use used uses utf-8 utf8 utilities v val value values var variable version- version-menu version-switcher vertical via video viewport visible vitesse-dark w w-[0.5rem] w-[0.875rem] w-[1.575rem] w-[16px] w-[280px] w-[var(--zd-sidebar-w)] w-full w-icon-sm w-icon-xs was watching wbr wbr- we when where whereas which while whitespace-nowrap whitespace-pre whole will with without word worktrees would wrap wrapper wrappers written wrote xl:flex xl:hidden y-scrollbar yet z-10 z-30 z-50 zd-content zd-doc-content-band zd-html-preview-code zd-sidebar-content-wrapper zfb zfb:after-swap zfb:before-preparation zudo-doc-design-tokens/v1 zudo-doc-sidebar-width zudo-doc-theme zudo-doc-theme-bridge");
|
|
@@ -2,7 +2,12 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
const cache = /* @__PURE__ */ new Map();
|
|
4
4
|
function loadCategoryMeta(contentDir) {
|
|
5
|
-
|
|
5
|
+
let absolute;
|
|
6
|
+
try {
|
|
7
|
+
absolute = path.resolve(contentDir);
|
|
8
|
+
} catch {
|
|
9
|
+
absolute = contentDir;
|
|
10
|
+
}
|
|
6
11
|
const cached = cache.get(absolute);
|
|
7
12
|
if (cached) return cached;
|
|
8
13
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -12,7 +12,7 @@ function sendApplyCssVars(iframe, vars) {
|
|
|
12
12
|
type: "apply-css-vars",
|
|
13
13
|
vars
|
|
14
14
|
};
|
|
15
|
-
win.postMessage(message,
|
|
15
|
+
win.postMessage(message, window.location.origin);
|
|
16
16
|
}
|
|
17
17
|
function sendClearCssVars(iframe, names) {
|
|
18
18
|
const win = iframe?.contentWindow;
|
|
@@ -22,10 +22,11 @@ function sendClearCssVars(iframe, names) {
|
|
|
22
22
|
type: "clear-css-vars",
|
|
23
23
|
names
|
|
24
24
|
};
|
|
25
|
-
win.postMessage(message,
|
|
25
|
+
win.postMessage(message, window.location.origin);
|
|
26
26
|
}
|
|
27
27
|
function installIframeReceiver(target = window) {
|
|
28
28
|
function handler(event) {
|
|
29
|
+
if (event.origin !== target.location.origin) return;
|
|
29
30
|
const data = event.data;
|
|
30
31
|
if (!isBridgeMessage(data)) return;
|
|
31
32
|
if (data.type === "apply-css-vars") {
|
|
@@ -47,12 +48,13 @@ function installIframeReceiver(target = window) {
|
|
|
47
48
|
const parent = target.parent;
|
|
48
49
|
if (parent && parent !== target) {
|
|
49
50
|
const ready = { source: BRIDGE_SOURCE, type: "ready" };
|
|
50
|
-
parent.postMessage(ready,
|
|
51
|
+
parent.postMessage(ready, target.location.origin);
|
|
51
52
|
}
|
|
52
53
|
return () => target.removeEventListener("message", handler);
|
|
53
54
|
}
|
|
54
55
|
function onIframeReady(expectedSource, callback) {
|
|
55
56
|
function handler(event) {
|
|
57
|
+
if (event.origin !== window.location.origin) return;
|
|
56
58
|
if (expectedSource && event.source !== expectedSource) return;
|
|
57
59
|
if (!isBridgeMessage(event.data)) return;
|
|
58
60
|
if (event.data.type === "ready") callback();
|
package/dist/theme/index.d.ts
CHANGED
|
@@ -5,4 +5,6 @@ export { DESIGN_TOKEN_SCHEMA, DeserializeOptions, DeserializeResult, DesignToken
|
|
|
5
5
|
export { ColorTweakState, TokenOverrides, TweakState, emptyOverrides } from './design-token-types.js';
|
|
6
6
|
export { ApplyCssVarsMessage, BRIDGE_SOURCE, BridgeMessage, ClearCssVarsMessage, CssVarPair, ErrorMessage, ReadyMessage, installIframeReceiver, isBridgeMessage, onIframeReady, sendApplyCssVars, sendClearCssVars } from './iframe-bridge.js';
|
|
7
7
|
import 'preact';
|
|
8
|
+
import '../theme-toggle/index.js';
|
|
9
|
+
import '../theme-toggle/color-scheme-sync.js';
|
|
8
10
|
import '@takazudo/zdtp';
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { VNode } from 'preact';
|
|
2
|
+
import { ThemeToggleProps } from '../theme-toggle/index.js';
|
|
3
|
+
import '../theme-toggle/color-scheme-sync.js';
|
|
2
4
|
|
|
3
5
|
/** @jsxRuntime automatic */
|
|
4
6
|
/** @jsxImportSource preact */
|
|
5
7
|
|
|
6
|
-
interface ThemeToggleProps {
|
|
7
|
-
defaultMode?: "light" | "dark";
|
|
8
|
-
}
|
|
9
8
|
/**
|
|
10
|
-
* Default-export click-toggle for color scheme. Wraps
|
|
11
|
-
* in `<Island when="load">` so the SSG renderer emits a
|
|
9
|
+
* Default-export click-toggle for color scheme. Wraps the bare
|
|
10
|
+
* `<ThemeToggle>` in `<Island when="load">` so the SSG renderer emits a
|
|
12
11
|
* `data-zfb-island="ThemeToggle"` marker the hydration runtime can
|
|
13
|
-
* find at boot time.
|
|
12
|
+
* find at boot time. Call sites that already sit inside an island (or
|
|
13
|
+
* want to control hydration timing themselves) should import the bare
|
|
14
|
+
* component from `@takazudo/zudo-doc/theme-toggle` instead.
|
|
14
15
|
*/
|
|
15
16
|
declare function ThemeToggle(props?: ThemeToggleProps): VNode;
|
|
16
17
|
|
|
@@ -1,88 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
3
|
-
import { useState, useEffect } from "preact/hooks";
|
|
1
|
+
import { jsx } from "preact/jsx-runtime";
|
|
4
2
|
import { Island } from "@takazudo/zfb";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"svg",
|
|
9
|
-
{
|
|
10
|
-
"aria-hidden": "true",
|
|
11
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
12
|
-
width: "20",
|
|
13
|
-
height: "20",
|
|
14
|
-
viewBox: "0 0 24 24",
|
|
15
|
-
fill: "none",
|
|
16
|
-
stroke: "currentColor",
|
|
17
|
-
strokeWidth: "2",
|
|
18
|
-
strokeLinecap: "round",
|
|
19
|
-
strokeLinejoin: "round",
|
|
20
|
-
children: [
|
|
21
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "5" }),
|
|
22
|
-
/* @__PURE__ */ jsx("line", { x1: "12", y1: "1", x2: "12", y2: "3" }),
|
|
23
|
-
/* @__PURE__ */ jsx("line", { x1: "12", y1: "21", x2: "12", y2: "23" }),
|
|
24
|
-
/* @__PURE__ */ jsx("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }),
|
|
25
|
-
/* @__PURE__ */ jsx("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }),
|
|
26
|
-
/* @__PURE__ */ jsx("line", { x1: "1", y1: "12", x2: "3", y2: "12" }),
|
|
27
|
-
/* @__PURE__ */ jsx("line", { x1: "21", y1: "12", x2: "23", y2: "12" }),
|
|
28
|
-
/* @__PURE__ */ jsx("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }),
|
|
29
|
-
/* @__PURE__ */ jsx("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })
|
|
30
|
-
]
|
|
31
|
-
}
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
function MoonIcon() {
|
|
35
|
-
return /* @__PURE__ */ jsx(
|
|
36
|
-
"svg",
|
|
37
|
-
{
|
|
38
|
-
"aria-hidden": "true",
|
|
39
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
40
|
-
width: "20",
|
|
41
|
-
height: "20",
|
|
42
|
-
viewBox: "0 0 24 24",
|
|
43
|
-
fill: "none",
|
|
44
|
-
stroke: "currentColor",
|
|
45
|
-
strokeWidth: "2",
|
|
46
|
-
strokeLinecap: "round",
|
|
47
|
-
strokeLinejoin: "round",
|
|
48
|
-
children: /* @__PURE__ */ jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
|
|
49
|
-
}
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
function ThemeToggleInner({ defaultMode = "dark" }) {
|
|
53
|
-
const [mode, setMode] = useState(defaultMode);
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
const actual = document.documentElement.getAttribute("data-theme") || defaultMode;
|
|
56
|
-
if (actual !== mode) {
|
|
57
|
-
setMode(actual);
|
|
58
|
-
}
|
|
59
|
-
}, []);
|
|
60
|
-
function toggle() {
|
|
61
|
-
const next = mode === "dark" ? "light" : "dark";
|
|
62
|
-
setMode(next);
|
|
63
|
-
document.documentElement.setAttribute("data-theme", next);
|
|
64
|
-
document.documentElement.style.colorScheme = next;
|
|
65
|
-
localStorage.setItem(STORAGE_KEY, next);
|
|
66
|
-
localStorage.removeItem("zudo-doc-tweak-state");
|
|
67
|
-
localStorage.removeItem("zudo-doc-tweak-state-v2");
|
|
68
|
-
window.dispatchEvent(new CustomEvent("color-scheme-changed"));
|
|
69
|
-
}
|
|
70
|
-
const nextMode = mode === "dark" ? "light" : "dark";
|
|
71
|
-
return /* @__PURE__ */ jsx(
|
|
72
|
-
"button",
|
|
73
|
-
{
|
|
74
|
-
onClick: toggle,
|
|
75
|
-
"aria-label": `Switch to ${nextMode} mode`,
|
|
76
|
-
className: "text-muted hover:text-fg transition-colors p-hsp-sm focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2",
|
|
77
|
-
children: mode === "dark" ? /* @__PURE__ */ jsx(SunIcon, {}) : /* @__PURE__ */ jsx(MoonIcon, {})
|
|
78
|
-
}
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
ThemeToggleInner.displayName = "ThemeToggle";
|
|
3
|
+
import {
|
|
4
|
+
ThemeToggle as ThemeToggleBare
|
|
5
|
+
} from "../theme-toggle/index.js";
|
|
82
6
|
function ThemeToggle(props = {}) {
|
|
83
7
|
const rendered = Island({
|
|
84
8
|
when: "load",
|
|
85
|
-
children: /* @__PURE__ */ jsx(
|
|
9
|
+
children: /* @__PURE__ */ jsx(ThemeToggleBare, { ...props })
|
|
86
10
|
});
|
|
87
11
|
return rendered;
|
|
88
12
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
type ColorSchemeMode = "light" | "dark";
|
|
2
|
+
declare const COLOR_SCHEME_CHANGED_EVENT = "color-scheme-changed";
|
|
3
|
+
/**
|
|
4
|
+
* Read the active color scheme from `<html data-theme>`. Falls back to
|
|
5
|
+
* `defaultMode` when the attribute is missing or holds an unexpected
|
|
6
|
+
* value (e.g. before the ColorSchemeProvider bootstrap script ran).
|
|
7
|
+
*/
|
|
8
|
+
declare function readColorSchemeFromDom(defaultMode: ColorSchemeMode): ColorSchemeMode;
|
|
9
|
+
/**
|
|
10
|
+
* Apply `next` as the active color scheme: mutate the DOM, persist the
|
|
11
|
+
* preference, and notify every subscriber (including other mounted
|
|
12
|
+
* ThemeToggle instances and the zdtp design-token panel) via the
|
|
13
|
+
* `color-scheme-changed` window event.
|
|
14
|
+
*
|
|
15
|
+
* Tweak-state reconciliation is intentionally NOT done here (#2037). The zdtp
|
|
16
|
+
* panel owns its own storage lifecycle: it persists the unified tweak envelope
|
|
17
|
+
* under `zudo-doc-tweak-state-v3` (auto-migrating the legacy
|
|
18
|
+
* `zudo-doc-tweak-state-v2` / `zudo-doc-tweak-state` keys into it), and its own
|
|
19
|
+
* `color-scheme-changed` listener clears applied inline styles and re-seeds the
|
|
20
|
+
* color slice from the newly active scheme. An earlier version of this function
|
|
21
|
+
* deleted `zudo-doc-tweak-state` + `-v2` on every toggle, which (a) targeted
|
|
22
|
+
* stale keys after zdtp moved to v3 — so it no longer did anything — and
|
|
23
|
+
* (b) when it did fire, wiped the whole envelope including scheme-independent
|
|
24
|
+
* spacing/typography/size tweaks, contradicting the documented carry-over
|
|
25
|
+
* guarantee. So the host no longer touches zdtp's private storage keys.
|
|
26
|
+
*
|
|
27
|
+
* Whether palette tweaks should instead persist per-scheme (so a light/dark
|
|
28
|
+
* round-trip keeps them) is a zdtp design question tracked upstream at
|
|
29
|
+
* Takazudo/zudo-design-token-panel#343. See zudo-doc#2037.
|
|
30
|
+
*/
|
|
31
|
+
declare function applyColorScheme(next: ColorSchemeMode): void;
|
|
32
|
+
/**
|
|
33
|
+
* Subscribe to color-scheme changes. Returns an unsubscribe function
|
|
34
|
+
* (suitable as a `useEffect` cleanup).
|
|
35
|
+
*/
|
|
36
|
+
declare function subscribeColorSchemeChanged(listener: () => void): () => void;
|
|
37
|
+
|
|
38
|
+
export { COLOR_SCHEME_CHANGED_EVENT, type ColorSchemeMode, applyColorScheme, readColorSchemeFromDom, subscribeColorSchemeChanged };
|