@takazudo/zudo-doc 4.3.0 → 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/bin/zudo-doc.mjs +54 -13
- package/dist/auto-logo/index.d.ts +18 -0
- package/dist/auto-logo/index.js +33 -0
- package/dist/auto-logo/shapes.d.ts +32 -0
- package/dist/auto-logo/shapes.js +183 -0
- package/dist/auto-logo/standalone.d.ts +7 -0
- package/dist/auto-logo/standalone.js +35 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +1 -0
- package/dist/content.css +4 -3
- package/dist/doc-content-header/index.js +1 -1
- package/dist/doc-history/index.js +1 -1
- package/dist/doc-page-shell/index.js +1 -1
- package/dist/doc-pager/index.js +2 -2
- package/dist/eject-logo/config-parse.d.ts +25 -0
- package/dist/eject-logo/config-parse.js +68 -0
- package/dist/eject-logo/config-rewriter.d.ts +22 -0
- package/dist/eject-logo/config-rewriter.js +68 -0
- package/dist/eject-logo/eject.d.ts +18 -0
- package/dist/eject-logo/eject.js +66 -0
- package/dist/eject-logo/index.d.ts +4 -0
- package/dist/eject-logo/index.js +16 -0
- package/dist/eject-logo/site-name.d.ts +21 -0
- package/dist/eject-logo/site-name.js +31 -0
- package/dist/header/header.js +14 -6
- package/dist/header/nav-class-tokens.d.ts +12 -0
- package/dist/header/nav-class-tokens.js +80 -0
- package/dist/header/nav-overflow-script.js +33 -16
- package/dist/home-page/index.d.ts +34 -5
- package/dist/home-page/index.js +95 -38
- package/dist/home-page/prepare-home-data.d.ts +9 -0
- package/dist/home-page/prepare-home-data.js +10 -3
- package/dist/i18n-defaults/index.js +3 -0
- package/dist/metainfo/doc-metainfo.js +35 -22
- package/dist/routes/index.js +2 -1
- package/dist/routes/locale-index.js +2 -1
- package/dist/safelist.css +1 -1
- package/dist/settings.d.ts +7 -0
- package/dist/sidebar-tree-island/index.js +3 -3
- package/dist/theme-cli/config-scanner.js +47 -0
- package/dist/theme-packs/academia/fonts/CourierPrime-400-latin.woff2 +0 -0
- package/dist/theme-packs/academia/fonts/CourierPrime-700-latin.woff2 +0 -0
- package/dist/theme-packs/academia/fonts/OFL.txt +94 -0
- package/dist/theme-packs/academia/fonts/SourceSerif4-italic-latin.woff2 +0 -0
- package/dist/theme-packs/academia/fonts/SourceSerif4-latin.woff2 +0 -0
- package/dist/theme-packs/academia/meta.json +37 -0
- package/dist/theme-packs/academia/pack.css +505 -0
- package/dist/theme-packs/bauhaus/fonts/ArchivoBlack-latin.woff2 +0 -0
- package/dist/theme-packs/bauhaus/fonts/Jost-latin.woff2 +0 -0
- package/dist/theme-packs/bauhaus/fonts/OFL.txt +95 -0
- package/dist/theme-packs/bauhaus/fonts/SpaceMono-400-latin.woff2 +0 -0
- package/dist/theme-packs/bauhaus/fonts/SpaceMono-700-latin.woff2 +0 -0
- package/dist/theme-packs/bauhaus/meta.json +38 -0
- package/dist/theme-packs/bauhaus/pack.css +614 -0
- package/dist/theme-packs/blueprint/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
- package/dist/theme-packs/blueprint/fonts/IBMPlexMono-400-latin.woff2 +0 -0
- package/dist/theme-packs/blueprint/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
- package/dist/theme-packs/blueprint/fonts/IBMPlexMono-500-latin.woff2 +0 -0
- package/dist/theme-packs/blueprint/fonts/Inter-latin-ext.woff2 +0 -0
- package/dist/theme-packs/blueprint/fonts/Inter-latin.woff2 +0 -0
- package/dist/theme-packs/blueprint/fonts/JosefinSans-latin.woff2 +0 -0
- package/dist/theme-packs/blueprint/fonts/OFL.txt +95 -0
- package/dist/theme-packs/blueprint/meta.json +38 -0
- package/dist/theme-packs/blueprint/pack.css +577 -0
- package/dist/theme-packs/botanica/fonts/CormorantGaramond-latin.woff2 +0 -0
- package/dist/theme-packs/botanica/fonts/CourierPrime-400-latin-ext.woff2 +0 -0
- package/dist/theme-packs/botanica/fonts/CourierPrime-400-latin.woff2 +0 -0
- package/dist/theme-packs/botanica/fonts/CourierPrime-700-latin-ext.woff2 +0 -0
- package/dist/theme-packs/botanica/fonts/CourierPrime-700-latin.woff2 +0 -0
- package/dist/theme-packs/botanica/fonts/CrimsonPro-latin-italic.woff2 +0 -0
- package/dist/theme-packs/botanica/fonts/CrimsonPro-latin.woff2 +0 -0
- package/dist/theme-packs/botanica/fonts/OFL.txt +95 -0
- package/dist/theme-packs/botanica/meta.json +38 -0
- package/dist/theme-packs/botanica/pack.css +546 -0
- package/dist/theme-packs/brutalist/meta.json +1 -1
- package/dist/theme-packs/brutalist/pack.css +10 -9
- package/dist/theme-packs/eink/fonts/CourierPrime-400-latin-ext.woff2 +0 -0
- package/dist/theme-packs/eink/fonts/CourierPrime-400-latin.woff2 +0 -0
- package/dist/theme-packs/eink/fonts/CourierPrime-700-latin-ext.woff2 +0 -0
- package/dist/theme-packs/eink/fonts/CourierPrime-700-latin.woff2 +0 -0
- package/dist/theme-packs/eink/fonts/EBGaramond-latin-italic.woff2 +0 -0
- package/dist/theme-packs/eink/fonts/EBGaramond-latin.woff2 +0 -0
- package/dist/theme-packs/eink/fonts/OFL.txt +94 -0
- package/dist/theme-packs/eink/meta.json +37 -0
- package/dist/theme-packs/eink/pack.css +490 -0
- package/dist/theme-packs/foundry/meta.json +1 -1
- package/dist/theme-packs/foundry/pack.css +2 -2
- package/dist/theme-packs/hearth/meta.json +1 -1
- package/dist/theme-packs/hearth/pack.css +1 -1
- package/dist/theme-packs/nocturne/pack.css +4 -0
- package/dist/theme-packs/observatory/meta.json +1 -1
- package/dist/theme-packs/observatory/pack.css +4 -1
- package/dist/theme-packs/onyx/pack.css +6 -0
- package/dist/theme-packs/phosphor/pack.css +4 -0
- package/dist/theme-packs/riso/fonts/ArchivoBlack-latin.woff2 +0 -0
- package/dist/theme-packs/riso/fonts/OFL.txt +95 -0
- package/dist/theme-packs/riso/fonts/SpaceGrotesk-latin.woff2 +0 -0
- package/dist/theme-packs/riso/fonts/SpaceMono-400-italic-latin.woff2 +0 -0
- package/dist/theme-packs/riso/fonts/SpaceMono-400-latin.woff2 +0 -0
- package/dist/theme-packs/riso/fonts/SpaceMono-700-latin.woff2 +0 -0
- package/dist/theme-packs/riso/meta.json +38 -0
- package/dist/theme-packs/riso/pack.css +602 -0
- package/dist/theme-packs/sakura/fonts/MPlus1Code-400-latin.woff2 +0 -0
- package/dist/theme-packs/sakura/fonts/MPlus1Code-500-latin.woff2 +0 -0
- package/dist/theme-packs/sakura/fonts/Nunito-latin.woff2 +0 -0
- package/dist/theme-packs/sakura/fonts/OFL.txt +95 -0
- package/dist/theme-packs/sakura/fonts/ShipporiMincho-600-latin.woff2 +0 -0
- package/dist/theme-packs/sakura/fonts/ShipporiMincho-700-latin.woff2 +0 -0
- package/dist/theme-packs/sakura/meta.json +38 -0
- package/dist/theme-packs/sakura/pack.css +473 -0
- package/dist/theme-packs/scandi/fonts/Manrope-latin.woff2 +0 -0
- package/dist/theme-packs/scandi/fonts/OFL.txt +94 -0
- package/dist/theme-packs/scandi/fonts/SplineSansMono-latin.woff2 +0 -0
- package/dist/theme-packs/scandi/meta.json +37 -0
- package/dist/theme-packs/scandi/pack.css +370 -0
- package/dist/theme-packs/sumi/meta.json +1 -1
- package/dist/theme-packs/sumi/pack.css +4 -1
- package/dist/theme-packs/swissgrid/pack.css +4 -0
- package/dist/theme-packs/tidepool/fonts/JetBrainsMono-latin-ext.woff2 +0 -0
- package/dist/theme-packs/tidepool/fonts/JetBrainsMono-latin.woff2 +0 -0
- package/dist/theme-packs/tidepool/fonts/OFL.txt +94 -0
- package/dist/theme-packs/tidepool/fonts/SpaceGrotesk-latin.woff2 +0 -0
- package/dist/theme-packs/tidepool/meta.json +37 -0
- package/dist/theme-packs/tidepool/pack.css +425 -0
- package/dist/theme-packs/timberline/fonts/Fraunces-italic-latin.woff2 +0 -0
- package/dist/theme-packs/timberline/fonts/Fraunces-latin.woff2 +0 -0
- package/dist/theme-packs/timberline/fonts/JetBrainsMono-latin.woff2 +0 -0
- package/dist/theme-packs/timberline/fonts/OFL.txt +95 -0
- package/dist/theme-packs/timberline/fonts/SourceSans3-italic-latin.woff2 +0 -0
- package/dist/theme-packs/timberline/fonts/SourceSans3-latin.woff2 +0 -0
- package/dist/theme-packs/timberline/meta.json +38 -0
- package/dist/theme-packs/timberline/pack.css +509 -0
- package/dist/theme-packs/washi/meta.json +1 -1
- package/dist/theme-packs/washi/pack.css +6 -26
- package/eject/doc-history/index.tsx +1 -1
- package/eject/doc-pager/index.tsx +2 -2
- package/eject/header/header.tsx +16 -8
- package/eject/header/nav-class-tokens.ts +115 -0
- package/eject/header/nav-overflow-script.ts +49 -16
- package/eject/sidebar-tree-island/index.tsx +4 -4
- package/package.json +17 -9
- package/routes-src/index.tsx +2 -1
- package/routes-src/locale-index.tsx +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,39 @@ All notable changes to `@takazudo/zudo-doc` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
|
|
6
6
|
|
|
7
|
+
## [4.4.1] - 2026-07-22
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- create-zudo-doc: removed the unused `@takazudo/zfb-adapter-cloudflare` dependency from the generated scaffold — fresh projects no longer install an adapter they never reference (eabda6fc2)
|
|
12
|
+
|
|
13
|
+
### Other Changes
|
|
14
|
+
|
|
15
|
+
- Bumped first-party `^4.0.0` dependency floors to `^4.4.0`: the `@takazudo/zudo-doc-history-server` peer floor in `@takazudo/zudo-doc`, and the target-manifest test fixture's `@takazudo/zudo-doc` pin (84d592cea)
|
|
16
|
+
|
|
17
|
+
## [4.4.0] - 2026-07-22
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
- Theme Batch 5 adds 10 new theme packs — academia, bauhaus, blueprint, botanica, eink, riso, sakura, scandi, tidepool, and timberline — bringing the catalog to 31 packs, each with full admonition/code/sidebar coverage and a rendered WCAG contrast audit (29bb3497f, 12665cdf1)
|
|
22
|
+
- A new `logo` setting renders a generated "auto" home-hero logo by default, alongside a `zudo-doc eject logo` CLI subcommand for taking full ownership of the logo markup (a2ba5188a, 3cedbee04)
|
|
23
|
+
- `scripts/theme-a11y-audit.ts` adds a rendered per-pack contrast audit tool, now wired into the nightly exam and documented for theme authors (4c969cd4a, 8ed913f9d)
|
|
24
|
+
- The top-page Tags section now renders tag chips, and the home hero gained a `heroLink` prop with an inline extras row (55be502c2, fc865e6be)
|
|
25
|
+
- Header, sidebar, overflow-dropdown, and doc-pager non-active nav elements now consistently turn accent color on hover/focus, with a shared class-token source of truth for the header nav (fafecc154, 274a8641f)
|
|
26
|
+
|
|
27
|
+
### Bug Fixes
|
|
28
|
+
|
|
29
|
+
- Resolved 10 rendered-a11y audit findings across 5 existing theme packs, plus follow-up contrast fixes for blueprint/sakura/tidepool and five additional deep-review findings (b694708dc, 2c2dcc858, 12f7964f9)
|
|
30
|
+
- Theme-CLI config scanning now correctly decodes `\uXXXX`, `\u{...}`, and `\xXX` escapes (2e58e3dc0)
|
|
31
|
+
- The default title-rule border is reset only where a theme pack owns its own title styling, keeping custom pack titles from double-bordering (9043b9439)
|
|
32
|
+
- Doc-history anchors now point at the actual generated heading slugs (9e3e62655)
|
|
33
|
+
|
|
34
|
+
### Other Changes
|
|
35
|
+
|
|
36
|
+
- Re-baselined the route-injection parity test's normalized-HTML sha256 snapshots twice this cycle, attributing every changed byte to its originating merged PR (5e46831d4, 4fd642c3b)
|
|
37
|
+
- Bumped the `@takazudo/zfb` family to `0.1.0-next.90`
|
|
38
|
+
- Expanded English and Japanese documentation for the logo/eject-logo feature, the Theme Gallery (31 packs), and the hover-accent interactive color rule
|
|
39
|
+
|
|
7
40
|
## [4.3.0] - 2026-07-19
|
|
8
41
|
|
|
9
42
|
### Features
|
package/bin/zudo-doc.mjs
CHANGED
|
@@ -3,21 +3,28 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Package bin: `zudo-doc eject <component>` swizzle CLI, plus
|
|
5
5
|
// `zudo-doc theme list|apply <slug>` (issue #2824; ADR
|
|
6
|
-
// docs/adr/theme-packs.md)
|
|
6
|
+
// docs/adr/theme-packs.md), plus `zudo-doc eject logo` (issue #3050; epic
|
|
7
|
+
// #3047).
|
|
7
8
|
//
|
|
8
|
-
// Self-contained ESM — runs on plain `node` with NO `tsx` requirement.
|
|
9
|
-
//
|
|
10
|
-
// `../dist/eject/index.js` / `../dist/theme-cli/index.js
|
|
11
|
-
// runtime deps are `minimist` +
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
9
|
+
// Self-contained ESM — runs on plain `node` with NO `tsx` requirement. The
|
|
10
|
+
// eject, theme-cli, and eject-logo logic are imported from the package's
|
|
11
|
+
// COMPILED `../dist/eject/index.js` / `../dist/theme-cli/index.js` /
|
|
12
|
+
// `../dist/eject-logo/index.js`, and the only runtime deps are `minimist` +
|
|
13
|
+
// `picocolors` (declared deps of this package, so they are present
|
|
14
|
+
// transitively in any consumer's node_modules). This is the key difference
|
|
15
|
+
// from the tsx-runner pattern used by `bin/tags-audit.mjs`: tags-audit must
|
|
16
|
+
// load the *project's* TypeScript config files at runtime (hence tsx),
|
|
17
|
+
// whereas eject/theme-cli/eject-logo only copy files / rewrite text — no TS
|
|
18
|
+
// eval needed — so all three work in a default generated project that never
|
|
19
|
+
// installed tsx (#2367).
|
|
20
|
+
//
|
|
21
|
+
// `eject logo` is special-cased below, BEFORE the EJECTABLE lookup — it is
|
|
22
|
+
// NOT a source-copy swizzle (EJECTABLE's contract), it renders a fresh SVG
|
|
23
|
+
// and rewrites a config field, so it does not belong in that map.
|
|
18
24
|
//
|
|
19
25
|
// Usage:
|
|
20
26
|
// zudo-doc eject <component> # eject a component's TS source into the project
|
|
27
|
+
// zudo-doc eject logo # render public/img/logo.svg + rewrite the logo field
|
|
21
28
|
// zudo-doc theme list # list installed theme packs + the active one
|
|
22
29
|
// zudo-doc theme apply <slug> # rewrite zfb.config.ts's themePack field
|
|
23
30
|
// zudo-doc --help # show help
|
|
@@ -25,10 +32,12 @@
|
|
|
25
32
|
import minimist from "minimist";
|
|
26
33
|
import pc from "picocolors";
|
|
27
34
|
import { eject, EJECTABLE } from "../dist/eject/index.js";
|
|
35
|
+
import { ejectLogo } from "../dist/eject-logo/index.js";
|
|
28
36
|
import { applyThemePack, formatThemeList, listThemePacks } from "../dist/theme-cli/index.js";
|
|
29
37
|
|
|
30
38
|
const argv = minimist(process.argv.slice(2), {
|
|
31
|
-
|
|
39
|
+
string: ["seed"],
|
|
40
|
+
boolean: ["help", "force"],
|
|
32
41
|
alias: { h: "help" },
|
|
33
42
|
});
|
|
34
43
|
|
|
@@ -40,6 +49,9 @@ ${pc.bold("Usage:")} zudo-doc <subcommand> [options]
|
|
|
40
49
|
${pc.bold("Subcommands:")}
|
|
41
50
|
eject <component> Copy a component's TS source into your project and
|
|
42
51
|
rewrite imports so it resolves locally.
|
|
52
|
+
eject logo [--seed <name>] [--force]
|
|
53
|
+
Render public/img/logo.svg and rewrite zfb.config.ts's
|
|
54
|
+
logo field to point at it.
|
|
43
55
|
theme list List the installed theme packs and which one is active.
|
|
44
56
|
theme apply <slug> Rewrite zfb.config.ts's themePack field to <slug>.
|
|
45
57
|
|
|
@@ -47,7 +59,9 @@ ${pc.bold("Ejectable components:")}
|
|
|
47
59
|
${validNames}
|
|
48
60
|
|
|
49
61
|
${pc.bold("Options:")}
|
|
50
|
-
-h, --help
|
|
62
|
+
-h, --help Show this help message
|
|
63
|
+
--seed <name> (eject logo) Seed for the generated logo, overriding siteName
|
|
64
|
+
--force (eject logo) Overwrite an existing public/img/logo.svg
|
|
51
65
|
|
|
52
66
|
${pc.bold("Examples:")}
|
|
53
67
|
${pc.dim("# Eject the header component")}
|
|
@@ -56,6 +70,9 @@ ${pc.bold("Examples:")}
|
|
|
56
70
|
${pc.dim("# Eject the theme-toggle component")}
|
|
57
71
|
zudo-doc eject theme-toggle
|
|
58
72
|
|
|
73
|
+
${pc.dim("# Eject the logo as a standalone SVG")}
|
|
74
|
+
zudo-doc eject logo
|
|
75
|
+
|
|
59
76
|
${pc.dim("# List installed theme packs")}
|
|
60
77
|
zudo-doc theme list
|
|
61
78
|
|
|
@@ -77,6 +94,29 @@ async function runEject(componentArg) {
|
|
|
77
94
|
await eject(componentArg, { cwd: process.cwd() });
|
|
78
95
|
}
|
|
79
96
|
|
|
97
|
+
async function runEjectLogo() {
|
|
98
|
+
const hasSeedFlag = Object.prototype.hasOwnProperty.call(argv, "seed");
|
|
99
|
+
if (hasSeedFlag && argv.seed.length === 0) {
|
|
100
|
+
console.error(
|
|
101
|
+
pc.red(`Missing value for --seed.`) +
|
|
102
|
+
`\nUsage: zudo-doc eject logo [--seed <name>] [--force]`,
|
|
103
|
+
);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const result = await ejectLogo({
|
|
108
|
+
cwd: process.cwd(),
|
|
109
|
+
seed: hasSeedFlag ? argv.seed : undefined,
|
|
110
|
+
force: argv.force,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!result.ok) {
|
|
114
|
+
console.error(pc.red(result.message));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
console.log(pc.green(result.message));
|
|
118
|
+
}
|
|
119
|
+
|
|
80
120
|
async function runThemeList() {
|
|
81
121
|
const result = await listThemePacks({ cwd: process.cwd() });
|
|
82
122
|
console.log(formatThemeList(result));
|
|
@@ -120,6 +160,7 @@ async function main() {
|
|
|
120
160
|
|
|
121
161
|
const [subcommand, ...rest] = argv._;
|
|
122
162
|
|
|
163
|
+
if (subcommand === "eject" && rest[0] === "logo") return runEjectLogo();
|
|
123
164
|
if (subcommand === "eject") return runEject(rest[0]);
|
|
124
165
|
if (subcommand === "theme") return runTheme(rest);
|
|
125
166
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
/** @jsxImportSource preact */
|
|
3
|
+
import type { JSX } from "preact";
|
|
4
|
+
import { pickGlyphName } from "./shapes.js";
|
|
5
|
+
export { pickGlyphName };
|
|
6
|
+
export { renderAutoLogoStandaloneSvg } from "./standalone.js";
|
|
7
|
+
/** Props for {@link AutoLogo}. */
|
|
8
|
+
export interface AutoLogoProps {
|
|
9
|
+
/** Deterministic seed — pass `settings.siteName`. */
|
|
10
|
+
seed: string;
|
|
11
|
+
/** Class list for the root `<svg>` (sizing/color, e.g. `w-[320px] text-fg`). */
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generated "decorated plate" logo — plate + frame + corner rays + disc +
|
|
16
|
+
* seeded line-art glyph. Server-rendered, deterministic per seed.
|
|
17
|
+
*/
|
|
18
|
+
export declare function AutoLogo({ seed, class: className }: AutoLogoProps): JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
2
|
+
import { h } from "preact";
|
|
3
|
+
import {
|
|
4
|
+
W,
|
|
5
|
+
H,
|
|
6
|
+
PLATE_SHAPE,
|
|
7
|
+
INNER_FRAME_SHAPE,
|
|
8
|
+
RAY_SHAPES,
|
|
9
|
+
DISC_SHAPE,
|
|
10
|
+
GLYPH_SHAPES,
|
|
11
|
+
GLYPH_TRANSFORM,
|
|
12
|
+
pickGlyphName
|
|
13
|
+
} from "./shapes.js";
|
|
14
|
+
import { renderAutoLogoStandaloneSvg } from "./standalone.js";
|
|
15
|
+
function renderShape({ el, attrs }, key) {
|
|
16
|
+
return h(el, key !== void 0 ? { ...attrs, key } : attrs);
|
|
17
|
+
}
|
|
18
|
+
function AutoLogo({ seed, class: className }) {
|
|
19
|
+
const glyphName = pickGlyphName(seed);
|
|
20
|
+
const glyphShapes = GLYPH_SHAPES[glyphName];
|
|
21
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${W} ${H}`, fill: "currentColor", class: className, "data-auto-logo": glyphName, "aria-hidden": "true", children: [
|
|
22
|
+
renderShape(PLATE_SHAPE),
|
|
23
|
+
renderShape(INNER_FRAME_SHAPE),
|
|
24
|
+
RAY_SHAPES.map((ray, i) => renderShape(ray, i)),
|
|
25
|
+
renderShape(DISC_SHAPE),
|
|
26
|
+
/* @__PURE__ */ jsx("g", { transform: GLYPH_TRANSFORM, children: glyphShapes.map((shape, i) => renderShape(shape, i)) })
|
|
27
|
+
] });
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
AutoLogo,
|
|
31
|
+
pickGlyphName,
|
|
32
|
+
renderAutoLogoStandaloneSvg
|
|
33
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** One SVG primitive as plain data: element name + its attributes. */
|
|
2
|
+
export interface ShapePrimitive {
|
|
3
|
+
el: "path" | "line" | "circle" | "rect";
|
|
4
|
+
attrs: Record<string, string | number>;
|
|
5
|
+
}
|
|
6
|
+
export declare const W = 200;
|
|
7
|
+
export declare const H = 105;
|
|
8
|
+
export declare const INSET = 6;
|
|
9
|
+
export declare const CX: number;
|
|
10
|
+
export declare const CY: number;
|
|
11
|
+
export declare const DISC_R = 34;
|
|
12
|
+
/** Full-bleed plate rect. */
|
|
13
|
+
export declare const PLATE_SHAPE: ShapePrimitive;
|
|
14
|
+
/** Thin inner frame stroke, knocked out against the page background. */
|
|
15
|
+
export declare const INNER_FRAME_SHAPE: ShapePrimitive;
|
|
16
|
+
/** 4 diagonal corner rays, each stopping just short of the disc edge. */
|
|
17
|
+
export declare const RAY_SHAPES: ShapePrimitive[];
|
|
18
|
+
/** Centered disc, knocked out against the page background. */
|
|
19
|
+
export declare const DISC_SHAPE: ShapePrimitive;
|
|
20
|
+
/** Scale + translate that centers a glyph's 100×100 box onto the disc
|
|
21
|
+
* (slight overscan reads better). */
|
|
22
|
+
export declare const GLYPH_SCALE: number;
|
|
23
|
+
export declare const GLYPH_TRANSFORM = "translate(64.3, 16.799999999999997) scale(0.7140000000000001)";
|
|
24
|
+
/** Line-art glyphs drawn in a 100×100 box centered at (50,50), as plain
|
|
25
|
+
* shape data. The original JSX wrapped each glyph (and some repeated
|
|
26
|
+
* groups) in a `<g>` that carried no attributes of its own — only React
|
|
27
|
+
* `key`s for list rendering — so flattening into a single primitive array
|
|
28
|
+
* per glyph loses no visual information. */
|
|
29
|
+
export declare const GLYPH_SHAPES: Record<string, ShapePrimitive[]>;
|
|
30
|
+
export declare const GLYPH_NAMES: string[];
|
|
31
|
+
/** Deterministic glyph name for a seed. */
|
|
32
|
+
export declare function pickGlyphName(seed: string): string;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
const W = 200;
|
|
2
|
+
const H = 105;
|
|
3
|
+
const INSET = 6;
|
|
4
|
+
const CX = W / 2;
|
|
5
|
+
const CY = H / 2;
|
|
6
|
+
const DISC_R = 34;
|
|
7
|
+
const KO = "var(--color-bg)";
|
|
8
|
+
const PLATE_SHAPE = {
|
|
9
|
+
el: "rect",
|
|
10
|
+
attrs: { x: 0, y: 0, width: W, height: H, fill: "currentColor" }
|
|
11
|
+
};
|
|
12
|
+
const INNER_FRAME_SHAPE = {
|
|
13
|
+
el: "rect",
|
|
14
|
+
attrs: {
|
|
15
|
+
x: INSET,
|
|
16
|
+
y: INSET,
|
|
17
|
+
width: W - INSET * 2,
|
|
18
|
+
height: H - INSET * 2,
|
|
19
|
+
fill: "none",
|
|
20
|
+
stroke: KO,
|
|
21
|
+
"stroke-width": 1.6
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const RAY_SHAPES = [
|
|
25
|
+
[INSET, INSET],
|
|
26
|
+
[W - INSET, INSET],
|
|
27
|
+
[INSET, H - INSET],
|
|
28
|
+
[W - INSET, H - INSET]
|
|
29
|
+
].map(([x, y]) => {
|
|
30
|
+
const dx = CX - x;
|
|
31
|
+
const dy = CY - y;
|
|
32
|
+
const len = Math.hypot(dx, dy);
|
|
33
|
+
const stop = (len - DISC_R - 7) / len;
|
|
34
|
+
return {
|
|
35
|
+
el: "line",
|
|
36
|
+
attrs: { x1: x, y1: y, x2: x + dx * stop, y2: y + dy * stop, stroke: KO, "stroke-width": 1.4 }
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
const DISC_SHAPE = {
|
|
40
|
+
el: "circle",
|
|
41
|
+
attrs: { cx: CX, cy: CY, r: DISC_R, fill: KO }
|
|
42
|
+
};
|
|
43
|
+
const GLYPH_SCALE = DISC_R * 2 / 100 * 1.05;
|
|
44
|
+
const GLYPH_TRANSFORM = `translate(${CX - 50 * GLYPH_SCALE}, ${CY - 50 * GLYPH_SCALE}) scale(${GLYPH_SCALE})`;
|
|
45
|
+
const GLYPH_SHAPES = {
|
|
46
|
+
book: [
|
|
47
|
+
{
|
|
48
|
+
el: "path",
|
|
49
|
+
attrs: {
|
|
50
|
+
d: "M50 30 C42 24, 30 23, 22 26 L22 68 C30 65, 42 66, 50 72 C58 66, 70 65, 78 68 L78 26 C70 23, 58 24, 50 30 Z",
|
|
51
|
+
fill: "none",
|
|
52
|
+
stroke: "currentColor",
|
|
53
|
+
"stroke-width": 6,
|
|
54
|
+
"stroke-linejoin": "round"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ el: "line", attrs: { x1: 50, y1: 30, x2: 50, y2: 72, stroke: "currentColor", "stroke-width": 5 } },
|
|
58
|
+
...[0, 1, 2].flatMap(
|
|
59
|
+
(i) => [
|
|
60
|
+
{
|
|
61
|
+
el: "line",
|
|
62
|
+
attrs: {
|
|
63
|
+
x1: 29,
|
|
64
|
+
y1: 38 + i * 10,
|
|
65
|
+
x2: 43,
|
|
66
|
+
y2: 40 + i * 10,
|
|
67
|
+
stroke: "currentColor",
|
|
68
|
+
"stroke-width": 4,
|
|
69
|
+
"stroke-linecap": "round"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
el: "line",
|
|
74
|
+
attrs: {
|
|
75
|
+
x1: 57,
|
|
76
|
+
y1: 40 + i * 10,
|
|
77
|
+
x2: 71,
|
|
78
|
+
y2: 38 + i * 10,
|
|
79
|
+
stroke: "currentColor",
|
|
80
|
+
"stroke-width": 4,
|
|
81
|
+
"stroke-linecap": "round"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
)
|
|
86
|
+
],
|
|
87
|
+
doc: [
|
|
88
|
+
{
|
|
89
|
+
el: "path",
|
|
90
|
+
attrs: { d: "M32 22 L60 22 L70 32 L70 78 L32 78 Z", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linejoin": "round" }
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
el: "path",
|
|
94
|
+
attrs: { d: "M60 22 L60 32 L70 32", fill: "none", stroke: "currentColor", "stroke-width": 5, "stroke-linejoin": "round" }
|
|
95
|
+
},
|
|
96
|
+
...[0, 1, 2].map(
|
|
97
|
+
(i) => ({
|
|
98
|
+
el: "line",
|
|
99
|
+
attrs: { x1: 40, y1: 44 + i * 11, x2: 62, y2: 44 + i * 11, stroke: "currentColor", "stroke-width": 4, "stroke-linecap": "round" }
|
|
100
|
+
})
|
|
101
|
+
)
|
|
102
|
+
],
|
|
103
|
+
bookmark: [
|
|
104
|
+
{
|
|
105
|
+
el: "path",
|
|
106
|
+
attrs: { d: "M34 22 L66 22 L66 78 L50 64 L34 78 Z", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linejoin": "round" }
|
|
107
|
+
},
|
|
108
|
+
{ el: "line", attrs: { x1: 42, y1: 36, x2: 58, y2: 36, stroke: "currentColor", "stroke-width": 4, "stroke-linecap": "round" } }
|
|
109
|
+
],
|
|
110
|
+
compass: [
|
|
111
|
+
{ el: "circle", attrs: { cx: 50, cy: 50, r: 26, fill: "none", stroke: "currentColor", "stroke-width": 6 } },
|
|
112
|
+
{ el: "path", attrs: { d: "M60 40 L54 56 L40 60 L46 44 Z", fill: "currentColor" } }
|
|
113
|
+
],
|
|
114
|
+
terminal: [
|
|
115
|
+
{ el: "rect", attrs: { x: 24, y: 30, width: 52, height: 40, rx: 5, fill: "none", stroke: "currentColor", "stroke-width": 6 } },
|
|
116
|
+
{
|
|
117
|
+
el: "path",
|
|
118
|
+
attrs: { d: "M33 42 L41 50 L33 58", fill: "none", stroke: "currentColor", "stroke-width": 5, "stroke-linecap": "round", "stroke-linejoin": "round" }
|
|
119
|
+
},
|
|
120
|
+
{ el: "line", attrs: { x1: 47, y1: 58, x2: 60, y2: 58, stroke: "currentColor", "stroke-width": 5, "stroke-linecap": "round" } }
|
|
121
|
+
],
|
|
122
|
+
bulb: [
|
|
123
|
+
{
|
|
124
|
+
el: "path",
|
|
125
|
+
attrs: {
|
|
126
|
+
d: "M50 22 C37 22, 30 32, 30 41 C30 49, 35 53, 39 58 L39 64 L61 64 L61 58 C65 53, 70 49, 70 41 C70 32, 63 22, 50 22 Z",
|
|
127
|
+
fill: "none",
|
|
128
|
+
stroke: "currentColor",
|
|
129
|
+
"stroke-width": 6,
|
|
130
|
+
"stroke-linejoin": "round"
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{ el: "line", attrs: { x1: 42, y1: 71, x2: 58, y2: 71, stroke: "currentColor", "stroke-width": 5, "stroke-linecap": "round" } },
|
|
134
|
+
{ el: "line", attrs: { x1: 45, y1: 78, x2: 55, y2: 78, stroke: "currentColor", "stroke-width": 5, "stroke-linecap": "round" } }
|
|
135
|
+
],
|
|
136
|
+
chat: [
|
|
137
|
+
{
|
|
138
|
+
el: "path",
|
|
139
|
+
attrs: { d: "M26 28 L74 28 L74 62 L48 62 L36 74 L36 62 L26 62 Z", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linejoin": "round" }
|
|
140
|
+
},
|
|
141
|
+
...[0, 1, 2].map((i) => ({ el: "circle", attrs: { cx: 38 + i * 12, cy: 45, r: 3, fill: "currentColor" } }))
|
|
142
|
+
],
|
|
143
|
+
layers: [
|
|
144
|
+
{ el: "path", attrs: { d: "M50 22 L78 36 L50 50 L22 36 Z", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linejoin": "round" } },
|
|
145
|
+
{
|
|
146
|
+
el: "path",
|
|
147
|
+
attrs: { d: "M25 47 L50 60 L75 47", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linecap": "round", "stroke-linejoin": "round" }
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
el: "path",
|
|
151
|
+
attrs: { d: "M25 58 L50 71 L75 58", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linecap": "round", "stroke-linejoin": "round" }
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
};
|
|
155
|
+
const GLYPH_NAMES = Object.keys(GLYPH_SHAPES);
|
|
156
|
+
function hashString(str) {
|
|
157
|
+
let h = 2166136261 >>> 0;
|
|
158
|
+
for (let i = 0; i < str.length; i++) {
|
|
159
|
+
h ^= str.charCodeAt(i);
|
|
160
|
+
h = Math.imul(h, 16777619);
|
|
161
|
+
}
|
|
162
|
+
return h >>> 0;
|
|
163
|
+
}
|
|
164
|
+
function pickGlyphName(seed) {
|
|
165
|
+
return GLYPH_NAMES[hashString(seed) % GLYPH_NAMES.length];
|
|
166
|
+
}
|
|
167
|
+
export {
|
|
168
|
+
CX,
|
|
169
|
+
CY,
|
|
170
|
+
DISC_R,
|
|
171
|
+
DISC_SHAPE,
|
|
172
|
+
GLYPH_NAMES,
|
|
173
|
+
GLYPH_SCALE,
|
|
174
|
+
GLYPH_SHAPES,
|
|
175
|
+
GLYPH_TRANSFORM,
|
|
176
|
+
H,
|
|
177
|
+
INNER_FRAME_SHAPE,
|
|
178
|
+
INSET,
|
|
179
|
+
PLATE_SHAPE,
|
|
180
|
+
RAY_SHAPES,
|
|
181
|
+
W,
|
|
182
|
+
pickGlyphName
|
|
183
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the deterministic AutoLogo design as a standalone, mask-friendly
|
|
3
|
+
* SVG string. Same seed always produces byte-identical output; glyph
|
|
4
|
+
* selection reuses {@link pickGlyphName} so an ejected file shows the same
|
|
5
|
+
* glyph as `logo: "auto"` mode.
|
|
6
|
+
*/
|
|
7
|
+
export declare function renderAutoLogoStandaloneSvg(seed: string): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
W,
|
|
3
|
+
H,
|
|
4
|
+
PLATE_SHAPE,
|
|
5
|
+
INNER_FRAME_SHAPE,
|
|
6
|
+
RAY_SHAPES,
|
|
7
|
+
DISC_SHAPE,
|
|
8
|
+
GLYPH_SHAPES,
|
|
9
|
+
GLYPH_TRANSFORM,
|
|
10
|
+
pickGlyphName
|
|
11
|
+
} from "./shapes.js";
|
|
12
|
+
const MASK_ID = "auto-logo-mask";
|
|
13
|
+
const MASK_COLORS = {
|
|
14
|
+
currentColor: "#fff",
|
|
15
|
+
"var(--color-bg)": "#000"
|
|
16
|
+
};
|
|
17
|
+
function escapeAttr(value) {
|
|
18
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
19
|
+
}
|
|
20
|
+
function renderShape(shape) {
|
|
21
|
+
const attrs = Object.entries(shape.attrs).map(([key, value]) => {
|
|
22
|
+
const resolved = typeof value === "string" && value in MASK_COLORS ? MASK_COLORS[value] : value;
|
|
23
|
+
return `${key}="${escapeAttr(String(resolved))}"`;
|
|
24
|
+
}).join(" ");
|
|
25
|
+
return `<${shape.el} ${attrs} />`;
|
|
26
|
+
}
|
|
27
|
+
function renderAutoLogoStandaloneSvg(seed) {
|
|
28
|
+
const glyphName = pickGlyphName(seed);
|
|
29
|
+
const glyphShapes = GLYPH_SHAPES[glyphName];
|
|
30
|
+
const maskContent = renderShape(PLATE_SHAPE) + renderShape(INNER_FRAME_SHAPE) + RAY_SHAPES.map(renderShape).join("") + renderShape(DISC_SHAPE) + `<g transform="${escapeAttr(GLYPH_TRANSFORM)}">${glyphShapes.map(renderShape).join("")}</g>`;
|
|
31
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" data-auto-logo="${glyphName}"><mask id="${MASK_ID}" mask-type="luminance" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" x="0" y="0" width="${W}" height="${H}">` + maskContent + `</mask><rect x="0" y="0" width="${W}" height="${H}" fill="#000" mask="url(#${MASK_ID})" /></svg>`;
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
renderAutoLogoStandaloneSvg
|
|
35
|
+
};
|
package/dist/config.d.ts
CHANGED
|
@@ -92,6 +92,14 @@ export interface ZudoDocConfig {
|
|
|
92
92
|
* @default ""
|
|
93
93
|
*/
|
|
94
94
|
siteDescription?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Home-hero logo. `"auto"` renders a generated deterministic SVG seeded by
|
|
97
|
+
* `siteName` (no asset needed); a path string (e.g. `"/img/logo.svg"`)
|
|
98
|
+
* renders that asset as a theme-adaptive CSS mask; `false` hides the logo
|
|
99
|
+
* block entirely.
|
|
100
|
+
* @default "auto"
|
|
101
|
+
*/
|
|
102
|
+
logo?: string | false;
|
|
95
103
|
/**
|
|
96
104
|
* Public URL sub-path prefix mounted in front of every absolute asset URL
|
|
97
105
|
* (e.g. `"/pj/my-site/"`). `"/"` = root-mounted.
|
package/dist/config.js
CHANGED
package/dist/content.css
CHANGED
|
@@ -144,12 +144,13 @@
|
|
|
144
144
|
/* ── Links ── */
|
|
145
145
|
|
|
146
146
|
/* Link reset inside site-nav (component can't detect ancestor context) */
|
|
147
|
-
|
|
147
|
+
/* Reset must stay specificity-0 (:where()) so island utility classes like hover:text-accent control color */
|
|
148
|
+
:where(.zd-content [data-site-nav] a) {
|
|
148
149
|
color: inherit;
|
|
149
150
|
text-decoration: none;
|
|
150
151
|
}
|
|
151
|
-
.zd-content [data-site-nav] a:hover,
|
|
152
|
-
.zd-content [data-site-nav] a:focus-visible {
|
|
152
|
+
:where(.zd-content [data-site-nav] a):hover,
|
|
153
|
+
:where(.zd-content [data-site-nav] a):focus-visible {
|
|
153
154
|
text-decoration: underline;
|
|
154
155
|
}
|
|
155
156
|
|
|
@@ -19,7 +19,7 @@ function createDocContentHeader(ctx) {
|
|
|
19
19
|
version
|
|
20
20
|
}) {
|
|
21
21
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22
|
-
/* @__PURE__ */ jsx("h1", { class: "text-heading font-bold mb-vsp-xs", children: entry.data.title }),
|
|
22
|
+
/* @__PURE__ */ jsx("h1", { class: "text-heading font-bold border-b border-fg pb-vsp-xs mb-vsp-xs", children: entry.data.title }),
|
|
23
23
|
docContentHeaderExtras?.({
|
|
24
24
|
entry,
|
|
25
25
|
slug,
|
|
@@ -339,7 +339,7 @@ function DocHistory({ slug, locale, basePath = "/" }) {
|
|
|
339
339
|
{
|
|
340
340
|
type: "button",
|
|
341
341
|
onClick: handleOpen,
|
|
342
|
-
className: "doc-history-trigger flex items-center gap-hsp-xs px-hsp-md py-vsp-xs rounded-lg bg-surface border border-muted text-muted hover:text-
|
|
342
|
+
className: "doc-history-trigger flex items-center gap-hsp-xs px-hsp-md py-vsp-xs rounded-lg bg-surface border border-muted text-muted hover:text-accent hover:border-accent focus-visible:text-accent focus-visible:border-accent transition-colors",
|
|
343
343
|
"aria-label": "View document history",
|
|
344
344
|
children: [
|
|
345
345
|
/* @__PURE__ */ jsx(History, { className: "h-icon-md w-icon-md" }),
|
|
@@ -133,7 +133,7 @@ function createDocPageShell(ctx) {
|
|
|
133
133
|
Fragment (not <div>) so children become direct children of
|
|
134
134
|
<article class="zd-content">, picking up the flow-space rule. */
|
|
135
135
|
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
136
|
-
/* @__PURE__ */ jsx("h1", { class: "text-heading font-bold mb-vsp-xs", children: autoIndexLabel }),
|
|
136
|
+
/* @__PURE__ */ jsx("h1", { class: "text-heading font-bold border-b border-fg pb-vsp-xs mb-vsp-xs", children: autoIndexLabel }),
|
|
137
137
|
metainfoSlot,
|
|
138
138
|
description && /* @__PURE__ */ jsx("p", { class: "mb-vsp-lg text-title text-muted", "data-doc-description": true, children: description }),
|
|
139
139
|
/* @__PURE__ */ jsx(NavCardGrid, { children: autoIndexChildren ?? [] })
|
package/dist/doc-pager/index.js
CHANGED
|
@@ -12,7 +12,7 @@ function createDocPager(ctx) {
|
|
|
12
12
|
href: prev.href,
|
|
13
13
|
class: "group border border-muted rounded-lg p-hsp-lg hover:border-accent",
|
|
14
14
|
children: [
|
|
15
|
-
/* @__PURE__ */ jsxs("div", { class: "flex items-center gap-hsp-xs text-caption text-muted mb-vsp-2xs", children: [
|
|
15
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center gap-hsp-xs text-caption text-muted mb-vsp-2xs group-hover:text-accent group-focus-visible:text-accent", children: [
|
|
16
16
|
/* @__PURE__ */ jsx(ChevronLeft, { className: "h-[1.125rem] w-[1.125rem]" }),
|
|
17
17
|
/* @__PURE__ */ jsx("span", { class: "no-underline", children: t("nav.previous", locale) })
|
|
18
18
|
] }),
|
|
@@ -26,7 +26,7 @@ function createDocPager(ctx) {
|
|
|
26
26
|
href: next.href,
|
|
27
27
|
class: "group border border-muted rounded-lg p-hsp-lg hover:border-accent text-right",
|
|
28
28
|
children: [
|
|
29
|
-
/* @__PURE__ */ jsxs("div", { class: "flex items-center justify-end gap-hsp-xs text-caption text-muted mb-vsp-2xs", children: [
|
|
29
|
+
/* @__PURE__ */ jsxs("div", { class: "flex items-center justify-end gap-hsp-xs text-caption text-muted mb-vsp-2xs group-hover:text-accent group-focus-visible:text-accent", children: [
|
|
30
30
|
/* @__PURE__ */ jsx("span", { class: "no-underline", children: t("nav.next", locale) }),
|
|
31
31
|
/* @__PURE__ */ jsx(ChevronRight, { className: "h-[1.125rem] w-[1.125rem]" })
|
|
32
32
|
] }),
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type TopLevelMember } from "../theme-cli/config-scanner.js";
|
|
2
|
+
export interface ParsedZudoDocConfig {
|
|
3
|
+
ok: true;
|
|
4
|
+
members: TopLevelMember[];
|
|
5
|
+
/** Index of the zudoDoc({ ... }) call's opening `{`. */
|
|
6
|
+
braceOpenIdx: number;
|
|
7
|
+
/** Index of the matching closing `}`. */
|
|
8
|
+
braceCloseIdx: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ParsedZudoDocConfigRefusal {
|
|
11
|
+
ok: false;
|
|
12
|
+
/** Human-readable WHY, with no field-specific hint appended — callers own
|
|
13
|
+
* wording their own remediation (a read-only lookup vs. a write refusal
|
|
14
|
+
* need different follow-up text). */
|
|
15
|
+
reason: string;
|
|
16
|
+
}
|
|
17
|
+
export type ParseZudoDocConfigResult = ParsedZudoDocConfig | ParsedZudoDocConfigRefusal;
|
|
18
|
+
/**
|
|
19
|
+
* Locate and validate the project's `zfb.config.ts` canonical shape — a
|
|
20
|
+
* single `zudoDoc({ ... })` call whose sole argument is a literal,
|
|
21
|
+
* non-spread object — and return its top-level members. Mirrors
|
|
22
|
+
* `theme-cli/config-rewriter.ts#applyThemePackToConfigSource`'s validation
|
|
23
|
+
* exactly, generalized to any caller field.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseZudoDocConfigMembers(source: string): ParseZudoDocConfigResult;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConfigSyntaxError,
|
|
3
|
+
findMatchingBrace,
|
|
4
|
+
findZudoDocCallParens,
|
|
5
|
+
skipWhitespaceAndComments,
|
|
6
|
+
splitTopLevelMembers
|
|
7
|
+
} from "../theme-cli/config-scanner.js";
|
|
8
|
+
function parseZudoDocConfigMembers(source) {
|
|
9
|
+
let calls;
|
|
10
|
+
try {
|
|
11
|
+
calls = findZudoDocCallParens(source);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
return { ok: false, reason: `could not parse zfb.config.ts (${err.message}).` };
|
|
14
|
+
}
|
|
15
|
+
if (calls.length === 0) {
|
|
16
|
+
return {
|
|
17
|
+
ok: false,
|
|
18
|
+
reason: "no zudoDoc(...) call found in zfb.config.ts. This CLI only reads/rewrites the canonical generated shape: export default defineConfig(zudoDoc({ ... }))."
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (calls.length > 1) {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
reason: `found ${calls.length} zudoDoc(...) call sites in zfb.config.ts \u2014 cannot safely determine which one to use.`
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const openParenIdx = calls[0];
|
|
28
|
+
let braceOpenIdx;
|
|
29
|
+
let braceCloseIdx;
|
|
30
|
+
let afterBrace;
|
|
31
|
+
try {
|
|
32
|
+
braceOpenIdx = skipWhitespaceAndComments(source, openParenIdx + 1);
|
|
33
|
+
if (source[braceOpenIdx] !== "{") {
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
reason: "zudoDoc(...) is not called with a plain object literal (found a computed or non-literal argument instead of zudoDoc({ ... })). This CLI only reads/rewrites the canonical generated shape."
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
braceCloseIdx = findMatchingBrace(source, braceOpenIdx);
|
|
40
|
+
afterBrace = skipWhitespaceAndComments(source, braceCloseIdx + 1);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
return { ok: false, reason: `could not parse zfb.config.ts (${err.message}).` };
|
|
43
|
+
}
|
|
44
|
+
const afterOptionalComma = source[afterBrace] === "," ? skipWhitespaceAndComments(source, afterBrace + 1) : afterBrace;
|
|
45
|
+
if (source[afterOptionalComma] !== ")") {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
reason: "zudoDoc(...) is called with more than one argument \u2014 not the canonical single-object-literal shape."
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
let members;
|
|
52
|
+
try {
|
|
53
|
+
members = splitTopLevelMembers(source, braceOpenIdx + 1, braceCloseIdx);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
return { ok: false, reason: `could not parse zfb.config.ts (${err.message}).` };
|
|
56
|
+
}
|
|
57
|
+
const spread = members.find((m) => m.isSpread);
|
|
58
|
+
if (spread) {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
reason: "zudoDoc({ ...spread }) uses a spread argument \u2014 not the canonical generated shape. This CLI only reads/rewrites a literal, hand-editable field list."
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return { ok: true, members, braceOpenIdx, braceCloseIdx };
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
parseZudoDocConfigMembers
|
|
68
|
+
};
|