create-zudo-doc 2.5.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/api.js +16 -0
- package/dist/cli.js +1 -6
- package/dist/constants.js +4 -65
- package/dist/index.js +1 -1
- package/dist/prompts.js +4 -43
- package/dist/scaffold.d.ts +1 -1
- package/dist/scaffold.js +3 -3
- package/dist/settings-gen.js +4 -4
- package/package.json +1 -1
- package/templates/base/src/config/color-scheme-utils.ts +14 -5
- package/templates/base/src/config/color-schemes.ts +158 -129
- package/templates/base/src/styles/global.css +5 -31
- package/templates/features/designTokenPanel/files/src/config/design-token-panel-config.ts +296 -149
- package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +12 -5
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ pnpm create zudo-doc my-docs --yes
|
|
|
31
31
|
# Fully specified, non-interactive
|
|
32
32
|
pnpm create zudo-doc my-docs \
|
|
33
33
|
--lang ja \
|
|
34
|
-
--scheme
|
|
34
|
+
--scheme "Default Dark" \
|
|
35
35
|
--no-i18n \
|
|
36
36
|
--search \
|
|
37
37
|
--pm pnpm \
|
|
@@ -109,11 +109,11 @@ pnpm create zudo-doc my-docs \
|
|
|
109
109
|
--install \
|
|
110
110
|
--yes
|
|
111
111
|
|
|
112
|
-
# Light/dark color scheme with
|
|
112
|
+
# Light/dark color scheme with the Default pairing
|
|
113
113
|
pnpm create zudo-doc my-docs \
|
|
114
114
|
--color-scheme-mode light-dark \
|
|
115
|
-
--light-scheme "
|
|
116
|
-
--dark-scheme "
|
|
115
|
+
--light-scheme "Default Light" \
|
|
116
|
+
--dark-scheme "Default Dark" \
|
|
117
117
|
--default-mode dark \
|
|
118
118
|
--yes
|
|
119
119
|
|
|
@@ -130,7 +130,7 @@ await createZudoDoc({
|
|
|
130
130
|
projectName: "my-docs",
|
|
131
131
|
defaultLang: "en",
|
|
132
132
|
colorSchemeMode: "single",
|
|
133
|
-
singleScheme: "
|
|
133
|
+
singleScheme: "Default Dark",
|
|
134
134
|
features: ["search", "sidebarFilter", "tagGovernance"],
|
|
135
135
|
packageManager: "pnpm",
|
|
136
136
|
install: true,
|
package/dist/api.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
+
import { SINGLE_SCHEMES } from "./constants.js";
|
|
2
3
|
import { scaffold } from "./scaffold.js";
|
|
3
4
|
import { initGitRepo, installDependencies, validateProjectName } from "./utils.js";
|
|
4
5
|
export async function createZudoDoc(options) {
|
|
@@ -6,6 +7,21 @@ export async function createZudoDoc(options) {
|
|
|
6
7
|
const nameError = validateProjectName(rest.projectName);
|
|
7
8
|
if (nameError)
|
|
8
9
|
throw new Error(`Invalid projectName: ${nameError}`);
|
|
10
|
+
// Validate scheme names like the CLI (cli.ts) and preset (preset.ts) paths do.
|
|
11
|
+
// Only `Default Light`/`Default Dark` exist post-catalog-drop (#2619); an
|
|
12
|
+
// unvalidated name here would be written verbatim into settings.ts and throw
|
|
13
|
+
// "Unknown color scheme" at the generated site's build — the exact failure
|
|
14
|
+
// this epic set out to eliminate. This is the last scaffolding entry point
|
|
15
|
+
// that lacked the guard.
|
|
16
|
+
for (const [label, value] of [
|
|
17
|
+
["color scheme", rest.singleScheme],
|
|
18
|
+
["light scheme", rest.lightScheme],
|
|
19
|
+
["dark scheme", rest.darkScheme],
|
|
20
|
+
]) {
|
|
21
|
+
if (value && !SINGLE_SCHEMES.includes(value)) {
|
|
22
|
+
throw new Error(`Unknown ${label} "${value}"`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
9
25
|
const choices = { ...rest, defaultLang: rest.defaultLang ?? "en" };
|
|
10
26
|
await scaffold(choices);
|
|
11
27
|
const targetDir = path.resolve(process.cwd(), choices.projectName);
|
package/dist/cli.js
CHANGED
|
@@ -110,12 +110,7 @@ ${pc.bold("Examples:")}
|
|
|
110
110
|
create-zudo-doc my-docs --yes
|
|
111
111
|
|
|
112
112
|
${pc.dim("# Fully specified")}
|
|
113
|
-
create-zudo-doc my-docs --lang ja --scheme
|
|
114
|
-
|
|
115
|
-
${pc.dim("# Light/dark mode with custom schemes")}
|
|
116
|
-
create-zudo-doc my-docs --color-scheme-mode light-dark \\
|
|
117
|
-
--light-scheme "GitHub Light" --dark-scheme "GitHub Dark" \\
|
|
118
|
-
--default-mode dark --yes
|
|
113
|
+
create-zudo-doc my-docs --lang ja --scheme "Default Dark" --no-i18n --pm pnpm --install
|
|
119
114
|
`);
|
|
120
115
|
}
|
|
121
116
|
export function validateArgs(args) {
|
package/dist/constants.js
CHANGED
|
@@ -1,73 +1,12 @@
|
|
|
1
1
|
export const LIGHT_DARK_PAIRINGS = [
|
|
2
2
|
{ light: "Default Light", dark: "Default Dark", label: "Default" },
|
|
3
|
-
{ light: "GitHub Light", dark: "GitHub Dark", label: "GitHub" },
|
|
4
|
-
{ light: "Catppuccin Latte", dark: "Catppuccin Mocha", label: "Catppuccin" },
|
|
5
|
-
{ light: "Solarized Light", dark: "Solarized Dark", label: "Solarized" },
|
|
6
|
-
{ light: "Rose Pine Dawn", dark: "Rose Pine", label: "Rosé Pine" },
|
|
7
|
-
{ light: "Atom One Light", dark: "Atom One Dark", label: "Atom One" },
|
|
8
|
-
{ light: "Everforest Light", dark: "Everforest Dark", label: "Everforest" },
|
|
9
|
-
{ light: "Gruvbox Light", dark: "Gruvbox Dark", label: "Gruvbox" },
|
|
10
|
-
{ light: "Ayu Light", dark: "Ayu Dark", label: "Ayu" },
|
|
11
|
-
];
|
|
12
|
-
// All available single schemes (dark ones most popular first)
|
|
13
|
-
export const SINGLE_SCHEMES = [
|
|
14
|
-
"Default Dark",
|
|
15
|
-
"Dracula",
|
|
16
|
-
"Catppuccin Mocha",
|
|
17
|
-
"GitHub Dark",
|
|
18
|
-
"Nord",
|
|
19
|
-
"TokyoNight",
|
|
20
|
-
"Gruvbox Dark",
|
|
21
|
-
"Atom One Dark",
|
|
22
|
-
"Rose Pine",
|
|
23
|
-
"Solarized Dark",
|
|
24
|
-
"Material Ocean",
|
|
25
|
-
"Monokai Pro",
|
|
26
|
-
"Everforest Dark",
|
|
27
|
-
"Kanagawa Wave",
|
|
28
|
-
"Night Owl",
|
|
29
|
-
"Ayu Dark",
|
|
30
|
-
"VS Code Dark+",
|
|
31
|
-
"Doom One",
|
|
32
|
-
"Challenger Deep",
|
|
33
|
-
"Catppuccin Frappe",
|
|
34
|
-
"Catppuccin Macchiato",
|
|
35
|
-
"Gruvbox Dark Hard",
|
|
36
|
-
"Rose Pine Moon",
|
|
37
|
-
"GitHub Dark Dimmed",
|
|
38
|
-
"Ayu Mirage",
|
|
39
|
-
"Material Darker",
|
|
40
|
-
"Material Dark",
|
|
41
|
-
"Monokai Remastered",
|
|
42
|
-
"Monokai Vivid",
|
|
43
|
-
"Monokai Soda",
|
|
44
|
-
"Solarized Dark Higher Contrast",
|
|
45
|
-
"Gruvbox Material Dark",
|
|
46
|
-
"Kanagawa Dragon",
|
|
47
|
-
// Light schemes
|
|
48
|
-
"Default Light",
|
|
49
|
-
"GitHub Light",
|
|
50
|
-
"Catppuccin Latte",
|
|
51
|
-
"Solarized Light",
|
|
52
|
-
"Rose Pine Dawn",
|
|
53
|
-
"Atom One Light",
|
|
54
|
-
"Everforest Light",
|
|
55
|
-
"Gruvbox Light",
|
|
56
|
-
"Ayu Light",
|
|
57
3
|
];
|
|
4
|
+
// All available single schemes (dark-first ordering — asserted by the host
|
|
5
|
+
// sync test).
|
|
6
|
+
export const SINGLE_SCHEMES = ["Default Dark", "Default Light"];
|
|
58
7
|
// Light-only subset of SINGLE_SCHEMES. Used by the preset generator to populate
|
|
59
8
|
// the "Light scheme" dropdown (dark schemes are derived as SINGLE_SCHEMES minus these).
|
|
60
|
-
export const LIGHT_SCHEMES = [
|
|
61
|
-
"Default Light",
|
|
62
|
-
"GitHub Light",
|
|
63
|
-
"Catppuccin Latte",
|
|
64
|
-
"Solarized Light",
|
|
65
|
-
"Rose Pine Dawn",
|
|
66
|
-
"Atom One Light",
|
|
67
|
-
"Everforest Light",
|
|
68
|
-
"Gruvbox Light",
|
|
69
|
-
"Ayu Light",
|
|
70
|
-
];
|
|
9
|
+
export const LIGHT_SCHEMES = ["Default Light"];
|
|
71
10
|
export const SUPPORTED_LANGS = [
|
|
72
11
|
{ value: "en", label: "English" },
|
|
73
12
|
{ value: "ja", label: "Japanese" },
|
package/dist/index.js
CHANGED
package/dist/prompts.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
|
-
import {
|
|
2
|
+
import { SINGLE_SCHEMES, FEATURES, SUPPORTED_LANGS } from "./constants.js";
|
|
3
3
|
import { validateProjectName } from "./utils.js";
|
|
4
4
|
export async function runPrompts(prefilled = {}) {
|
|
5
5
|
// 1. Project name
|
|
@@ -88,48 +88,9 @@ export async function runPrompts(prefilled = {}) {
|
|
|
88
88
|
darkScheme = prefilled.darkScheme;
|
|
89
89
|
}
|
|
90
90
|
else {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
...LIGHT_DARK_PAIRINGS.map((pair) => ({
|
|
95
|
-
value: pair.label,
|
|
96
|
-
label: `${pair.light} + ${pair.dark}`,
|
|
97
|
-
hint: pair.label,
|
|
98
|
-
})),
|
|
99
|
-
{
|
|
100
|
-
value: "custom",
|
|
101
|
-
label: "Pick individually",
|
|
102
|
-
hint: "Choose light and dark schemes separately",
|
|
103
|
-
},
|
|
104
|
-
],
|
|
105
|
-
});
|
|
106
|
-
if (p.isCancel(pairingChoice))
|
|
107
|
-
process.exit(0);
|
|
108
|
-
if (pairingChoice === "custom") {
|
|
109
|
-
const lightSchemes = SINGLE_SCHEMES.filter((s) => ["Light", "Latte", "Dawn"].some((k) => s.includes(k)));
|
|
110
|
-
const darkSchemes = SINGLE_SCHEMES.filter((s) => !["Light", "Latte", "Dawn"].some((k) => s.includes(k)));
|
|
111
|
-
const light = await p.select({
|
|
112
|
-
message: "Choose light scheme:",
|
|
113
|
-
options: lightSchemes.map((s) => ({ value: s, label: s })),
|
|
114
|
-
});
|
|
115
|
-
if (p.isCancel(light))
|
|
116
|
-
process.exit(0);
|
|
117
|
-
lightScheme = light;
|
|
118
|
-
const dark = await p.select({
|
|
119
|
-
message: "Choose dark scheme:",
|
|
120
|
-
options: darkSchemes.map((s) => ({ value: s, label: s })),
|
|
121
|
-
});
|
|
122
|
-
if (p.isCancel(dark))
|
|
123
|
-
process.exit(0);
|
|
124
|
-
darkScheme = dark;
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
const pairing = LIGHT_DARK_PAIRINGS.find((pair) => pair.label === pairingChoice);
|
|
128
|
-
if (pairing) {
|
|
129
|
-
lightScheme = pairing.light;
|
|
130
|
-
darkScheme = pairing.dark;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
91
|
+
// Only the Default pairing exists — auto-assign, no prompt.
|
|
92
|
+
lightScheme = "Default Light";
|
|
93
|
+
darkScheme = "Default Dark";
|
|
133
94
|
}
|
|
134
95
|
// Default mode
|
|
135
96
|
if (prefilled.defaultMode === undefined) {
|
package/dist/scaffold.d.ts
CHANGED
|
@@ -11,5 +11,5 @@ export { getSecondaryLang };
|
|
|
11
11
|
*
|
|
12
12
|
* Bumped in lockstep by scripts/release-create-zudo-doc.sh.
|
|
13
13
|
*/
|
|
14
|
-
export declare const ZUDO_DOC_PIN = "^
|
|
14
|
+
export declare const ZUDO_DOC_PIN = "^3.0.0";
|
|
15
15
|
export declare function scaffold(choices: UserChoices): Promise<void>;
|
package/dist/scaffold.js
CHANGED
|
@@ -18,7 +18,7 @@ export { getSecondaryLang };
|
|
|
18
18
|
*
|
|
19
19
|
* Bumped in lockstep by scripts/release-create-zudo-doc.sh.
|
|
20
20
|
*/
|
|
21
|
-
export const ZUDO_DOC_PIN = "^
|
|
21
|
+
export const ZUDO_DOC_PIN = "^3.0.0";
|
|
22
22
|
/**
|
|
23
23
|
* Files in `templates/base/**` that must never be copied into a generated
|
|
24
24
|
* project. Each entry is matched against the path relative to `templates/base/`
|
|
@@ -582,7 +582,7 @@ function generatePackageJson(choices) {
|
|
|
582
582
|
// @takazudo/zudo-doc/integrations/doc-history which in turn imports
|
|
583
583
|
// @takazudo/zudo-doc-history-server/git-history. Without this dep the
|
|
584
584
|
// plugin host fails at init with ERR_MODULE_NOT_FOUND — W8A (#1739).
|
|
585
|
-
deps["@takazudo/zudo-doc-history-server"] = "^
|
|
585
|
+
deps["@takazudo/zudo-doc-history-server"] = "^3.0.0";
|
|
586
586
|
// tsx is no longer needed here: the relocated package plugin imports the
|
|
587
587
|
// runner directly (no `tsx -e` spawn) since the package ships compiled
|
|
588
588
|
// dist/ — package-first migration #2321 (#2337).
|
|
@@ -593,7 +593,7 @@ function generatePackageJson(choices) {
|
|
|
593
593
|
if (choices.features.includes("designTokenPanel")) {
|
|
594
594
|
// @takazudo/zdtp requires preact >= 10.29.1 — see the preact floor comment
|
|
595
595
|
// above (~line 382) for why the floor is set there and the coupling this creates.
|
|
596
|
-
deps["@takazudo/zdtp"] = "0.4.
|
|
596
|
+
deps["@takazudo/zdtp"] = "0.4.5";
|
|
597
597
|
}
|
|
598
598
|
if (choices.features.includes("tagGovernance")) {
|
|
599
599
|
// gray-matter is already in `deps` unconditionally (base template uses it),
|
package/dist/settings-gen.js
CHANGED
|
@@ -35,15 +35,15 @@ export function generateSettingsFile(choices) {
|
|
|
35
35
|
lines.push(``);
|
|
36
36
|
lines.push(`export const settings = {`);
|
|
37
37
|
if (choices.colorSchemeMode === "single") {
|
|
38
|
-
lines.push(` colorScheme: ${JSON.stringify(choices.singleScheme ?? "
|
|
38
|
+
lines.push(` colorScheme: ${JSON.stringify(choices.singleScheme ?? "Default Dark")},`);
|
|
39
39
|
lines.push(` colorMode: false as ColorModeConfig | false,`);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
lines.push(` colorScheme: ${JSON.stringify(choices.darkScheme ?? "
|
|
42
|
+
lines.push(` colorScheme: ${JSON.stringify(choices.darkScheme ?? "Default Dark")},`);
|
|
43
43
|
lines.push(` colorMode: {`);
|
|
44
44
|
lines.push(` defaultMode: ${JSON.stringify(choices.defaultMode ?? "dark")},`);
|
|
45
|
-
lines.push(` lightScheme: ${JSON.stringify(choices.lightScheme ?? "
|
|
46
|
-
lines.push(` darkScheme: ${JSON.stringify(choices.darkScheme ?? "
|
|
45
|
+
lines.push(` lightScheme: ${JSON.stringify(choices.lightScheme ?? "Default Light")},`);
|
|
46
|
+
lines.push(` darkScheme: ${JSON.stringify(choices.darkScheme ?? "Default Dark")},`);
|
|
47
47
|
lines.push(` respectPrefersColorScheme: ${choices.respectPrefersColorScheme ?? true},`);
|
|
48
48
|
lines.push(` } satisfies ColorModeConfig as ColorModeConfig | false,`);
|
|
49
49
|
}
|
package/package.json
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Project-side color-scheme helpers.
|
|
3
3
|
*
|
|
4
|
-
* The MECHANISM (
|
|
4
|
+
* The MECHANISM (ramp→CSS injection logic) now lives in the package:
|
|
5
5
|
* `@takazudo/zudo-doc/color-scheme-utils`. This file re-exports those
|
|
6
6
|
* mechanism symbols and adds project-specific thin wrappers that read from
|
|
7
|
-
* this project's `colorSchemes` map and `settings`. The DATA (
|
|
7
|
+
* this project's `colorSchemes` map and `settings`. The DATA (ramp values,
|
|
8
8
|
* scheme names) stays in `./color-schemes` and `./settings`.
|
|
9
9
|
*
|
|
10
10
|
* S9a package-first migration — zudolab/zudo-doc#2333.
|
|
11
|
+
* Ramp-native rewrite — zudolab/zudo-doc#2585/#2586.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
export {
|
|
14
|
-
type
|
|
15
|
+
type OKLCH,
|
|
16
|
+
type StateRole,
|
|
17
|
+
type SemanticKey,
|
|
18
|
+
type RampRef,
|
|
19
|
+
type Ramps,
|
|
20
|
+
type ModeMap,
|
|
15
21
|
type ColorScheme,
|
|
16
|
-
|
|
22
|
+
type CssEmitScope,
|
|
23
|
+
STATE_ROLES,
|
|
24
|
+
SEMANTIC_KEYS,
|
|
25
|
+
SEMANTIC_RAMP_DEFAULTS,
|
|
17
26
|
SEMANTIC_CSS_NAMES,
|
|
18
|
-
|
|
27
|
+
resolveRampRef,
|
|
19
28
|
resolveSemanticColors,
|
|
20
29
|
schemeToCssPairs,
|
|
21
30
|
generateCssCustomProperties as generateCssCustomPropertiesFromScheme,
|
|
@@ -1,136 +1,165 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Ramp-native color schemes (Color Ramp Restructure — zudolab/zudo-doc#2584;
|
|
3
|
+
* minimize pass #2601 / #2602).
|
|
4
|
+
*
|
|
5
|
+
* A `ColorScheme` is `{ ramps, map }` (the MECHANISM types live in the package
|
|
6
|
+
* and are re-exported by `./color-scheme-utils`):
|
|
7
|
+
* - `ramps` — the shared Tier-1 source of truth: a warm-neutral `base` ramp
|
|
8
|
+
* (5 stops, index 0 = lightest), an `accent` ramp (3 stops), and 4 `state`
|
|
9
|
+
* colors. Light and dark modes SHARE these values.
|
|
10
|
+
* - `map` — the per-mode Tier-2 wiring: which ramp stop (or literal OKLCH)
|
|
11
|
+
* each UI role points at.
|
|
12
|
+
*
|
|
13
|
+
* The palette was minimized from base=12 / accent=7 to **base=5 / accent=3**
|
|
14
|
+
* (#2602): the ramp-native engine is length-agnostic, so this is a DATA change.
|
|
15
|
+
* Semantic roles are aggressively merged onto shared stops to keep the number of
|
|
16
|
+
* distinct tones small — most notably `surface`, `codeBg`(light) and
|
|
17
|
+
* `chatAssistantBg` collapse onto `bg`, so header Version boxes and doc cards
|
|
18
|
+
* render as page-bg + border only (no gray fill). 5 stops leave no subtle
|
|
19
|
+
* near-white, so light-mode elevated fills are border-only by design.
|
|
20
|
+
*
|
|
21
|
+
* `ColorScheme` is re-exported here so the many sites that still
|
|
22
|
+
* `import { ColorScheme } from "./color-schemes"` (contrast tooling,
|
|
23
|
+
* zfb.config.ts, …) keep resolving until their own waves port them.
|
|
24
|
+
*/
|
|
3
25
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
cursor: ColorRef;
|
|
8
|
-
selectionBg: ColorRef;
|
|
9
|
-
selectionFg: ColorRef;
|
|
10
|
-
palette: [
|
|
11
|
-
string, string, string, string, string, string, string, string,
|
|
12
|
-
string, string, string, string, string, string, string, string,
|
|
13
|
-
];
|
|
14
|
-
/** Optional, vestigial. Carried only in the optional color-scheme config
|
|
15
|
-
* envelope consumed by the design token panel tooling (falls back to
|
|
16
|
-
* DEFAULT_SHIKI_THEME when omitted), but has no visible effect: that
|
|
17
|
-
* tooling's Shiki integration is a no-op stub, and page code highlighting is
|
|
18
|
-
* done by syntect (dual-theme, configured via `codeHighlight` in
|
|
19
|
-
* zfb.config.ts), not Shiki. */
|
|
20
|
-
shikiTheme?: string;
|
|
21
|
-
/** Optional semantic overrides — when omitted, defaults are used:
|
|
22
|
-
* surface=p0, muted=p8, accent=p5, accentHover=p14
|
|
23
|
-
* codeBg=p10, codeFg=p11, success=p2, danger=p1, warning=p3, info=p4
|
|
24
|
-
* Each field accepts a palette index (number) or a direct color value (string). */
|
|
25
|
-
semantic?: {
|
|
26
|
-
surface?: ColorRef;
|
|
27
|
-
muted?: ColorRef;
|
|
28
|
-
accent?: ColorRef;
|
|
29
|
-
accentHover?: ColorRef;
|
|
30
|
-
codeBg?: ColorRef;
|
|
31
|
-
codeFg?: ColorRef;
|
|
32
|
-
success?: ColorRef;
|
|
33
|
-
danger?: ColorRef;
|
|
34
|
-
warning?: ColorRef;
|
|
35
|
-
info?: ColorRef;
|
|
36
|
-
mermaidNodeBg?: ColorRef;
|
|
37
|
-
mermaidText?: ColorRef;
|
|
38
|
-
mermaidLine?: ColorRef;
|
|
39
|
-
mermaidLabelBg?: ColorRef;
|
|
40
|
-
mermaidNoteBg?: ColorRef;
|
|
41
|
-
chatUserBg?: ColorRef;
|
|
42
|
-
chatUserText?: ColorRef;
|
|
43
|
-
chatAssistantBg?: ColorRef;
|
|
44
|
-
chatAssistantText?: ColorRef;
|
|
45
|
-
/** UI chrome over user images — enlarge/close overlay buttons */
|
|
46
|
-
imageOverlayBg?: ColorRef;
|
|
47
|
-
imageOverlayFg?: ColorRef;
|
|
48
|
-
/** <mark> highlight for matched keywords in search results */
|
|
49
|
-
matchedKeywordBg?: ColorRef;
|
|
50
|
-
matchedKeywordFg?: ColorRef;
|
|
51
|
-
};
|
|
52
|
-
}
|
|
26
|
+
import type { ColorScheme, Ramps, ModeMap } from "./color-scheme-utils";
|
|
27
|
+
|
|
28
|
+
export type { ColorScheme } from "./color-scheme-utils";
|
|
53
29
|
|
|
54
30
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* | p0 | Dark surface | Deepest surface (code blocks, mermaid) |
|
|
60
|
-
* | p1 | Danger | Red family — errors, destructive actions |
|
|
61
|
-
* | p2 | Success | Green family — confirmations, tips |
|
|
62
|
-
* | p3 | Warning | Yellow/amber — caution messages |
|
|
63
|
-
* | p4 | Info | Blue family — informational highlights |
|
|
64
|
-
* | p5 | Accent | Primary interactive color (links, CTA) |
|
|
65
|
-
* | p6 | Neutral | Slate/cyan — borders, secondary elements |
|
|
66
|
-
* | p7 | Secondary neutral | Gray or muted accent |
|
|
67
|
-
* | p8 | Muted | Gray — borders, secondary text, comments |
|
|
68
|
-
* | p9 | Background | Page background |
|
|
69
|
-
* | p10 | Surface | Elevated surface (panels, sidebars) |
|
|
70
|
-
* | p11 | Text primary | Main body text |
|
|
71
|
-
* | p12 | Accent variant | Brighter or alternate accent |
|
|
72
|
-
* | p13 | Decorative | Purple/lavender — non-semantic decoration |
|
|
73
|
-
* | p14 | Accent hover | Hover state for interactive elements |
|
|
74
|
-
* | p15 | Text secondary | Secondary text or muted foreground |
|
|
31
|
+
* Shared Tier-1 ramps — identical across Default Light and Default Dark.
|
|
32
|
+
* Minimized to 5 base / 3 accent (#2602). The warm-neutral spine keeps hue 65;
|
|
33
|
+
* the accent stops are the three tuned orange stops carried over from the 7-stop
|
|
34
|
+
* ramp (old accent2/3/6, re-indexed). Index 0 = lightest.
|
|
75
35
|
*/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
36
|
+
const ramps: Ramps = {
|
|
37
|
+
base: [
|
|
38
|
+
"oklch(.965 .004 65)", // 0 — lightest (light bg / dark fg)
|
|
39
|
+
"oklch(.705 .008 65)", // 1 — dark muted / light selection & mermaid fill
|
|
40
|
+
"oklch(.480 .008 65)", // 2 — light muted / dark selection & mermaid note
|
|
41
|
+
"oklch(.300 .006 65)", // 3 — dark codeBg / mermaid fill
|
|
42
|
+
"oklch(.185 .005 65)", // 4 — darkest (dark bg / light fg)
|
|
43
|
+
],
|
|
44
|
+
accent: [
|
|
45
|
+
"oklch(.755 .130 64)", // 0 — dark hover (was accent2)
|
|
46
|
+
"oklch(.700 .158 62)", // 1 — dark accent (was accent3)
|
|
47
|
+
"oklch(.470 .120 56)", // 2 — light accent (was accent6)
|
|
48
|
+
],
|
|
49
|
+
state: {
|
|
50
|
+
danger: "oklch(.640 .170 25)",
|
|
51
|
+
success: "oklch(.680 .145 145)",
|
|
52
|
+
warning: "oklch(.760 .135 82)",
|
|
53
|
+
info: "oklch(.680 .130 245)",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Default Dark — the authored reference scheme (epic #2584; minimized #2602).
|
|
59
|
+
* bg = darkest base stop, fg = lightest. Roles merged onto shared stops:
|
|
60
|
+
* `surface`, `chatAssistantBg`, `imageOverlayBg` all = `bg` (b4) so elevated
|
|
61
|
+
* panels are bg + border only. Per-mode AA-tuned literals: `danger`,
|
|
62
|
+
* `matchedKeyword*` (carried from #2593). Full WCAG matrix passes at
|
|
63
|
+
* threshold+0.1.
|
|
64
|
+
*/
|
|
65
|
+
const darkMap: ModeMap = {
|
|
66
|
+
bg: { base: 4 },
|
|
67
|
+
fg: { base: 0 },
|
|
68
|
+
selectionBg: { base: 2 },
|
|
69
|
+
selectionFg: { base: 0 },
|
|
70
|
+
semantic: {
|
|
71
|
+
// Merged onto bg (b4) — elevated panels read as page-bg + border, no gray fill.
|
|
72
|
+
surface: { base: 4 },
|
|
73
|
+
muted: { base: 1 },
|
|
74
|
+
accent: { accent: 1 },
|
|
75
|
+
accentHover: { accent: 0 },
|
|
76
|
+
codeBg: { base: 3 },
|
|
77
|
+
codeFg: { base: 0 },
|
|
78
|
+
success: { state: "success" },
|
|
79
|
+
// Per-mode AA-tuned literal — shared state.danger oklch(.640 .170 25) is too
|
|
80
|
+
// dark for the danger-admonition title on its 12%-tint dark bg. L .640→.655
|
|
81
|
+
// (H/C fixed) — admonition-danger at threshold+0.1; carried from #2593.
|
|
82
|
+
danger: "oklch(.655 .170 25)",
|
|
83
|
+
warning: { state: "warning" },
|
|
84
|
+
info: { state: "info" },
|
|
85
|
+
mermaidNodeBg: { base: 3 },
|
|
86
|
+
mermaidText: { base: 0 },
|
|
87
|
+
mermaidLine: { base: 1 },
|
|
88
|
+
mermaidLabelBg: { base: 3 },
|
|
89
|
+
mermaidNoteBg: { base: 2 },
|
|
90
|
+
chatUserBg: { accent: 1 },
|
|
91
|
+
chatUserText: { base: 4 },
|
|
92
|
+
// Merged onto bg (b4) — the chat assistant bubble reads as page-bg + border.
|
|
93
|
+
chatAssistantBg: { base: 4 },
|
|
94
|
+
chatAssistantText: { base: 0 },
|
|
95
|
+
imageOverlayBg: { base: 4 },
|
|
96
|
+
imageOverlayFg: { base: 0 },
|
|
97
|
+
// Search-result <mark> highlight: an amber (accent-hue) fill with dark text —
|
|
98
|
+
// the classic highlighter look, identical in both modes. Kept as literals so
|
|
99
|
+
// the amber fill is scheme-stable; matchedKeywordFg dark so text clears AA on
|
|
100
|
+
// the amber bg — matched-keyword at threshold+0.1; carried from #2593.
|
|
101
|
+
matchedKeywordBg: "oklch(.700 .158 62)",
|
|
102
|
+
matchedKeywordFg: "oklch(.300 .003 65)",
|
|
106
103
|
},
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Default Light — the authored light-mode scheme (epic #2584; minimized #2602).
|
|
108
|
+
* Shares `ramps` with Default Dark; the `map` inverts (light bg = lightest base
|
|
109
|
+
* stop, dark fg = darkest). Roles merged onto shared stops: `surface`, `codeBg`,
|
|
110
|
+
* `chatAssistantBg` all = `bg` (b0) so elevated panels are bg + border only
|
|
111
|
+
* (the intended flat, few-tone look — 5 stops leave no subtle near-white fill).
|
|
112
|
+
* The accent/state colors — authored for a dark bg — need darker per-mode
|
|
113
|
+
* literals to clear AA on a near-white bg. Full WCAG matrix passes at
|
|
114
|
+
* threshold+0.1; ramps untouched so Default Dark is unaffected.
|
|
115
|
+
*/
|
|
116
|
+
const lightMap: ModeMap = {
|
|
117
|
+
bg: { base: 0 },
|
|
118
|
+
fg: { base: 4 },
|
|
119
|
+
selectionBg: { base: 1 },
|
|
120
|
+
selectionFg: { base: 4 },
|
|
121
|
+
semantic: {
|
|
122
|
+
// Merged onto bg (b0) — elevated panels read as page-bg + border, no gray fill.
|
|
123
|
+
surface: { base: 0 },
|
|
124
|
+
muted: { base: 2 },
|
|
125
|
+
accent: { accent: 2 },
|
|
126
|
+
// Light: per-mode literal — the accent ramp has no stop darker than accent2,
|
|
127
|
+
// so the link-hover state darkens further (hover-darkens-on-light
|
|
128
|
+
// convention). L .400, C fitted to the sRGB gamut edge at that L; carried
|
|
129
|
+
// from #2595. accent-hover-vs-bg clears threshold+0.1.
|
|
130
|
+
accentHover: "oklch(.400 .096 56)",
|
|
131
|
+
// Merged onto bg (b0) — inline/code-block fill reads as page-bg + border.
|
|
132
|
+
codeBg: { base: 0 },
|
|
133
|
+
codeFg: { base: 4 },
|
|
134
|
+
// Light: per-mode literals — the shared state colors are authored for a dark
|
|
135
|
+
// bg and are too light on the 12%-tint admonition backgrounds over a light
|
|
136
|
+
// page bg. Each darkened (H fixed; C at gamut max, clipped where noted) to
|
|
137
|
+
// clear its admonition pair at threshold+0.1; carried from #2595.
|
|
138
|
+
success: "oklch(.470 .140 145)",
|
|
139
|
+
danger: "oklch(.505 .170 25)",
|
|
140
|
+
warning: "oklch(.490 .100 82)", // C .135→.100 (gamut-clips at this L)
|
|
141
|
+
info: "oklch(.485 .122 245)",
|
|
142
|
+
mermaidNodeBg: { base: 1 },
|
|
143
|
+
mermaidText: { base: 4 },
|
|
144
|
+
mermaidLine: { base: 2 },
|
|
145
|
+
mermaidLabelBg: { base: 1 },
|
|
146
|
+
mermaidNoteBg: { base: 1 },
|
|
147
|
+
chatUserBg: { accent: 1 },
|
|
148
|
+
// Dark text on the amber user bubble (matches Default Dark).
|
|
149
|
+
chatUserText: { base: 4 },
|
|
150
|
+
// Merged onto bg (b0) — the chat assistant bubble reads as page-bg + border.
|
|
151
|
+
chatAssistantBg: { base: 0 },
|
|
152
|
+
chatAssistantText: { base: 4 },
|
|
153
|
+
imageOverlayBg: { base: 4 },
|
|
154
|
+
imageOverlayFg: { base: 0 },
|
|
155
|
+
// Search-result <mark>: amber highlighter fill with dark text — same look as
|
|
156
|
+
// Default Dark (an amber-on-white highlight reads identically in both modes).
|
|
157
|
+
matchedKeywordBg: "oklch(.700 .158 62)",
|
|
158
|
+
matchedKeywordFg: "oklch(.300 .003 65)",
|
|
135
159
|
},
|
|
136
160
|
};
|
|
161
|
+
|
|
162
|
+
export const colorSchemes: Record<string, ColorScheme> = {
|
|
163
|
+
"Default Light": { ramps, map: lightMap },
|
|
164
|
+
"Default Dark": { ramps, map: darkMap },
|
|
165
|
+
};
|
|
@@ -74,18 +74,10 @@
|
|
|
74
74
|
* Tight token strategy: import preflight + utilities only (no default theme).
|
|
75
75
|
* Only project-specific tokens are available.
|
|
76
76
|
*
|
|
77
|
-
*
|
|
77
|
+
* Base roles: --zd-bg/--zd-fg/--zd-selection-bg/--zd-selection-fg + ramps
|
|
78
|
+
* (--palette-base-0..4 / --palette-accent-0..2 / --palette-state-*) +
|
|
79
|
+
* 23 --zd-{role} semantic tokens, all injected by ColorSchemeProvider.
|
|
78
80
|
* Tailwind tokens: registered below via @theme
|
|
79
|
-
*
|
|
80
|
-
* Palette slots:
|
|
81
|
-
* 0/8: black / bright-black (surfaces, muted text, borders)
|
|
82
|
-
* 1/9: red / bright-red
|
|
83
|
-
* 2/10: green / bright-green
|
|
84
|
-
* 3/11: yellow / bright-yellow
|
|
85
|
-
* 4/12: blue / bright-blue
|
|
86
|
-
* 5/13: magenta / bright-magenta
|
|
87
|
-
* 6/14: cyan / bright-cyan
|
|
88
|
-
* 7/15: white / bright-white
|
|
89
81
|
* ======================================== */
|
|
90
82
|
|
|
91
83
|
@theme {
|
|
@@ -117,26 +109,8 @@
|
|
|
117
109
|
/* ── Base ── */
|
|
118
110
|
--color-bg: var(--zd-bg);
|
|
119
111
|
--color-fg: var(--zd-fg);
|
|
120
|
-
--color-sel-bg: var(--zd-
|
|
121
|
-
--color-sel-fg: var(--zd-
|
|
122
|
-
|
|
123
|
-
/* ── Raw palette (p0–p15) ── */
|
|
124
|
-
--color-p0: var(--zd-0);
|
|
125
|
-
--color-p1: var(--zd-1);
|
|
126
|
-
--color-p2: var(--zd-2);
|
|
127
|
-
--color-p3: var(--zd-3);
|
|
128
|
-
--color-p4: var(--zd-4);
|
|
129
|
-
--color-p5: var(--zd-5);
|
|
130
|
-
--color-p6: var(--zd-6);
|
|
131
|
-
--color-p7: var(--zd-7);
|
|
132
|
-
--color-p8: var(--zd-8);
|
|
133
|
-
--color-p9: var(--zd-9);
|
|
134
|
-
--color-p10: var(--zd-10);
|
|
135
|
-
--color-p11: var(--zd-11);
|
|
136
|
-
--color-p12: var(--zd-12);
|
|
137
|
-
--color-p13: var(--zd-13);
|
|
138
|
-
--color-p14: var(--zd-14);
|
|
139
|
-
--color-p15: var(--zd-15);
|
|
112
|
+
--color-sel-bg: var(--zd-selection-bg);
|
|
113
|
+
--color-sel-fg: var(--zd-selection-fg);
|
|
140
114
|
|
|
141
115
|
/* ── Semantic aliases ── */
|
|
142
116
|
--color-surface: var(--zd-surface);
|
|
@@ -1,17 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* zdtp (zudo-design-token-panel) PanelConfig for this project.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
4
|
+
* Single source of truth passed to `bootstrapDesignTokenPanel(...)` in
|
|
5
|
+
* `src/lib/design-token-panel-bootstrap.ts`.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
7
|
+
* Ramp-native model (mirrors zudo-doc's Color Ramp Restructure —
|
|
8
|
+
* zudolab/zudo-doc#2584 / #2592; mode-scoped Color tab — #2606 / #2610)
|
|
9
|
+
* -------------------------------------------------------------------------
|
|
10
|
+
* The color model is ramp-native (`ColorScheme = { ramps, map }`, see
|
|
11
|
+
* `src/config/color-schemes.ts`). The panel surfaces it through two tabs:
|
|
12
|
+
*
|
|
13
|
+
* - **Palette tab** (reserved id `palette`): three `kind:'color'` OKLCH tiers —
|
|
14
|
+
* `base` (5 stops → `--palette-base-0..4`), `accent` (3 stops →
|
|
15
|
+
* `--palette-accent-0..2`), and `state` (4 roles → `--palette-state-{role}`).
|
|
16
|
+
* zdtp's native L/C/H curve editor renders these. Per zdtp's palette-tab
|
|
17
|
+
* contract, a tab carrying MULTIPLE `kind:'color'` tiers MUST omit
|
|
18
|
+
* `colorExtras` (otherwise `resolveColorClusterFromTab` cannot pick a single
|
|
19
|
+
* palette tier). The ramps are shared across light/dark, so token defaults
|
|
20
|
+
* are read from the active scheme's `ramps` and stay in sync with
|
|
21
|
+
* `color-schemes.ts`. The cssVars match the `--palette-*` custom properties
|
|
22
|
+
* the ColorSchemeProvider emits (`schemeToCssPairs`).
|
|
23
|
+
*
|
|
24
|
+
* - **Color tab** (reserved id `color`): a single `semantic` tier holding the
|
|
25
|
+
* 4 base roles (`--zd-bg`/`--zd-fg`/`--zd-selection-{bg,fg}`) + 23 `--zd-*`
|
|
26
|
+
* semantic roles, each rendered as a grouped ramp dropdown. The tier declares
|
|
27
|
+
* `referencesRamps` pointing at the Palette tab's `base`/`accent`/`state`
|
|
28
|
+
* tiers, so each row's `default` is the encoded `tierId:itemId` ramp
|
|
29
|
+
* reference (`buildSemanticTierItems` / `rampRefToPanelDefault`), the picker
|
|
30
|
+
* renders grouped `<optgroup>` dropdowns, and editing emits live
|
|
31
|
+
* `var(--palette-*)`. The tier carries NO `referencesTier` — that resolves
|
|
32
|
+
* intra-tab only; the cross-tab wiring is `referencesRamps` + `semantic:true`.
|
|
33
|
+
*
|
|
34
|
+
* Mode-scoped defaults
|
|
35
|
+
* --------------------
|
|
36
|
+
* The Color tab's semantic defaults are MODE-SCOPED: `buildDesignTokenPanelConfig(mode)`
|
|
37
|
+
* seeds the tier from the active mode's scheme (light map vs dark map). The
|
|
38
|
+
* bootstrap (`@takazudo/zudo-doc/design-token-panel-bootstrap`) destroys +
|
|
39
|
+
* reconfigures the panel on every `color-scheme-changed` toggle so its defaults
|
|
40
|
+
* follow the live light/dark mode.
|
|
41
|
+
*
|
|
42
|
+
* Caveat: a *saved* color OVERRIDE is still mode-AGNOSTIC here — but this is
|
|
43
|
+
* this project's config-shape choice, not a zdtp limitation. zdtp 0.4.5 ships
|
|
44
|
+
* per-scheme/per-mode keyed color persistence (v4 envelope,
|
|
45
|
+
* Takazudo/zudo-design-token-panel#500 / #509): the color slice is keyed by
|
|
46
|
+
* the cluster's resolved scheme identity (`panelSettings.colorScheme` /
|
|
47
|
+
* `colorMode`). This project's color cluster is scheme-less
|
|
48
|
+
* (`colorExtras.colorSchemes = {}`, no `colorMode`) and switches modes
|
|
49
|
+
* externally via the destroy+reconfigure dance above rather than zdtp's own
|
|
50
|
+
* `colorMode` field, so zdtp always resolves the same single (stub) scheme
|
|
51
|
+
* identity and an override repaints both modes until Reset. Only the
|
|
52
|
+
* DEFAULTS are mode-faithful. Do NOT work around this by reaching into zdtp's
|
|
53
|
+
* private storage keys.
|
|
54
|
+
*
|
|
55
|
+
* The color cluster is **scheme-less**: `colorExtras.colorSchemes = {}` (zdtp's
|
|
56
|
+
* documented scheme-less cluster shape) — the ramps ARE the editable source of
|
|
57
|
+
* truth, surfaced by the Palette tab, so a bundled scheme-preset registry no
|
|
58
|
+
* longer applies.
|
|
15
59
|
*/
|
|
16
60
|
|
|
17
61
|
import type {
|
|
@@ -20,7 +64,6 @@ import type {
|
|
|
20
64
|
TierConfig,
|
|
21
65
|
TierItem,
|
|
22
66
|
ColorClusterExtras,
|
|
23
|
-
ColorScheme as ZdtpColorScheme,
|
|
24
67
|
TokenDef,
|
|
25
68
|
} from "@takazudo/zdtp";
|
|
26
69
|
import {
|
|
@@ -28,66 +71,23 @@ import {
|
|
|
28
71
|
FONT_TOKENS,
|
|
29
72
|
SIZE_TOKENS,
|
|
30
73
|
} from "./design-tokens-manifest";
|
|
74
|
+
import {
|
|
75
|
+
getActiveScheme,
|
|
76
|
+
STATE_ROLES,
|
|
77
|
+
type ColorScheme,
|
|
78
|
+
} from "./color-scheme-utils";
|
|
79
|
+
import { buildSemanticTierItems } from "@takazudo/zudo-doc/color-scheme-utils";
|
|
31
80
|
import { colorSchemes } from "./color-schemes";
|
|
32
|
-
import type { ColorScheme as LocalColorScheme } from "./color-schemes";
|
|
33
|
-
import { SEMANTIC_DEFAULTS, SEMANTIC_CSS_NAMES } from "./color-scheme-utils";
|
|
34
81
|
import { settings } from "./settings";
|
|
35
|
-
import { DESIGN_TOKEN_SCHEMA } from "@takazudo/zudo-doc/theme";
|
|
36
82
|
|
|
37
83
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
background: 0,
|
|
43
|
-
foreground: 15,
|
|
44
|
-
cursor: 6,
|
|
45
|
-
selectionBg: 0,
|
|
46
|
-
selectionFg: 15,
|
|
47
|
-
} as const;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Fallback Shiki theme used when a color scheme's `shikiTheme` field is absent.
|
|
84
|
+
* Inert fallback for the still-REQUIRED `ColorClusterExtras.defaultShikiTheme`.
|
|
85
|
+
* zdtp's Shiki integration is a no-op stub, so this value has no visible
|
|
86
|
+
* effect if you highlight code some other way — but the field is typed
|
|
87
|
+
* `string`, so a value is required.
|
|
51
88
|
*/
|
|
52
89
|
const DEFAULT_SHIKI_THEME = "github-dark";
|
|
53
90
|
|
|
54
|
-
/**
|
|
55
|
-
* Normalize this project's local `ColorScheme` records into zdtp's
|
|
56
|
-
* `ColorScheme` shape. zdtp's type requires `shikiTheme: string`; the local
|
|
57
|
-
* scheme type makes it optional. This helper fills `DEFAULT_SHIKI_THEME` only
|
|
58
|
-
* when a scheme doesn't declare its own, so the result is assignable to
|
|
59
|
-
* `Record<string, ZdtpColorScheme>` via an ordinary type-checked assignment —
|
|
60
|
-
* replacing the previous `as unknown as` double-cast that bypassed every field
|
|
61
|
-
* check. Tracked upstream at Takazudo/zudo-design-token-panel#342 (shikiTheme
|
|
62
|
-
* should be optional in zdtp's `ColorScheme` type); drop this helper once that
|
|
63
|
-
* lands.
|
|
64
|
-
*/
|
|
65
|
-
function toZdtpColorSchemes(
|
|
66
|
-
schemes: Record<string, LocalColorScheme>,
|
|
67
|
-
): Record<string, ZdtpColorScheme> {
|
|
68
|
-
const normalized: Record<string, ZdtpColorScheme> = {};
|
|
69
|
-
for (const [name, scheme] of Object.entries(schemes)) {
|
|
70
|
-
normalized[name] = {
|
|
71
|
-
...scheme,
|
|
72
|
-
shikiTheme: scheme.shikiTheme ?? DEFAULT_SHIKI_THEME,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
return normalized;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Initial palette taken from the configured active scheme.
|
|
80
|
-
*/
|
|
81
|
-
function getInitialPalette(): readonly string[] {
|
|
82
|
-
const scheme = colorSchemes[settings.colorScheme];
|
|
83
|
-
if (!scheme) {
|
|
84
|
-
throw new Error(
|
|
85
|
-
`Unknown color scheme: "${settings.colorScheme}". Available: ${Object.keys(colorSchemes).join(", ")}`,
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
return scheme.palette;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
91
|
// ---------------------------------------------------------------------------
|
|
92
92
|
// Helpers — partition flat manifest arrays into TabConfig.tiers by group.
|
|
93
93
|
// ---------------------------------------------------------------------------
|
|
@@ -129,6 +129,9 @@ function toTierItem(t: TokenDef): TierItem {
|
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
131
|
* Build a tier from the subset of `tokens` whose `group` matches `groupId`.
|
|
132
|
+
* The tier's items share the same kind by construction (all items in a
|
|
133
|
+
* group share the same slider/select/text shape in this project's manifest).
|
|
134
|
+
* zdtp's `assertValidTabs` validator requires this.
|
|
132
135
|
*/
|
|
133
136
|
function tierFromGroup(
|
|
134
137
|
tokens: readonly TokenDef[],
|
|
@@ -145,100 +148,203 @@ function tierFromGroup(
|
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
// ---------------------------------------------------------------------------
|
|
148
|
-
//
|
|
151
|
+
// Palette tab — three ramp tiers (base / accent / state), OKLCH curve editor.
|
|
149
152
|
// ---------------------------------------------------------------------------
|
|
150
153
|
|
|
151
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Build the three ramp tiers from the active scheme's shared `ramps`. Light
|
|
156
|
+
* and dark schemes share the same Tier-1 ramps, so the active scheme's ramps
|
|
157
|
+
* are the single source of truth — read from here rather than hardcoding a
|
|
158
|
+
* second copy of the values.
|
|
159
|
+
*/
|
|
160
|
+
function buildRampTiers(): TierConfig[] {
|
|
161
|
+
const { ramps } = getActiveScheme();
|
|
152
162
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
id:
|
|
167
|
-
label: "
|
|
168
|
-
items,
|
|
163
|
+
const baseTier: TierConfig = {
|
|
164
|
+
id: "base",
|
|
165
|
+
label: "Base",
|
|
166
|
+
items: ramps.base.map((color, i): TierItem => ({
|
|
167
|
+
id: `base-${i}`,
|
|
168
|
+
cssVar: `--palette-base-${i}`,
|
|
169
|
+
label: String(i),
|
|
170
|
+
default: color,
|
|
171
|
+
type: { kind: "color", format: "oklch" },
|
|
172
|
+
})),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const accentTier: TierConfig = {
|
|
176
|
+
id: "accent",
|
|
177
|
+
label: "Accent",
|
|
178
|
+
items: ramps.accent.map((color, i): TierItem => ({
|
|
179
|
+
id: `accent-${i}`,
|
|
180
|
+
cssVar: `--palette-accent-${i}`,
|
|
181
|
+
label: String(i),
|
|
182
|
+
default: color,
|
|
183
|
+
type: { kind: "color", format: "oklch" },
|
|
184
|
+
})),
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const stateTier: TierConfig = {
|
|
188
|
+
id: "state",
|
|
189
|
+
label: "State",
|
|
190
|
+
items: STATE_ROLES.map((role): TierItem => ({
|
|
191
|
+
id: `state-${role}`,
|
|
192
|
+
cssVar: `--palette-state-${role}`,
|
|
193
|
+
label: role,
|
|
194
|
+
default: ramps.state[role],
|
|
195
|
+
type: { kind: "color", format: "oklch" },
|
|
196
|
+
})),
|
|
169
197
|
};
|
|
198
|
+
|
|
199
|
+
return [baseTier, accentTier, stateTier];
|
|
170
200
|
}
|
|
171
201
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
202
|
+
const PALETTE_TAB: TabConfig = {
|
|
203
|
+
id: "palette",
|
|
204
|
+
label: "Palette",
|
|
205
|
+
tiers: buildRampTiers(),
|
|
206
|
+
// No colorExtras: a tab with multiple kind:'color' tiers MUST omit it so
|
|
207
|
+
// resolveColorClusterFromTab is not invoked and zdtp renders the ramps with
|
|
208
|
+
// its native curve editor (zdtp palette-tab contract).
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// Color tab — mode-scoped semantic tier (4 base roles + 23 --zd-* roles) as
|
|
213
|
+
// grouped ramp dropdowns referencing the Palette tab.
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
|
|
216
|
+
type PanelMode = "light" | "dark";
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Resolve the `ColorScheme` whose `map` seeds the semantic tier for `mode`.
|
|
220
|
+
* With a light/dark pair configured, picks `colorMode.{light,dark}Scheme`; on
|
|
221
|
+
* the single-scheme path (`settings.colorMode === false`) there is no pair, so
|
|
222
|
+
* the active `settings.colorScheme` is used for both modes (no toggle wiring).
|
|
223
|
+
*/
|
|
224
|
+
function schemeForMode(mode: PanelMode): ColorScheme {
|
|
225
|
+
const cm = settings.colorMode;
|
|
226
|
+
if (!cm) return getActiveScheme();
|
|
227
|
+
const name = mode === "dark" ? cm.darkScheme : cm.lightScheme;
|
|
228
|
+
return colorSchemes[name] ?? getActiveScheme();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Semantic tier — 4 base roles + 23 `--zd-*` roles, each a grouped ramp
|
|
233
|
+
* dropdown. `referencesRamps` names the Palette tab's ramp tiers this tier's
|
|
234
|
+
* `{ref}` mappings resolve against (cross-tab, resolved at mount by
|
|
235
|
+
* `resolveColorClusterFromTab`); `semantic: true` marks it so zdtp never
|
|
236
|
+
* mistakes it for the palette tier. Items come from `buildSemanticTierItems`:
|
|
237
|
+
* every `default` is an encoded `tierId:itemId` ramp reference, so a scheme
|
|
238
|
+
* with no override round-trips to the exact ramp stop (no snapping to a
|
|
239
|
+
* resolved color). Seeded from `schemeForMode(mode)` — this is the only
|
|
240
|
+
* mode-varying tier.
|
|
241
|
+
*/
|
|
242
|
+
function buildSemanticTier(mode: PanelMode): TierConfig {
|
|
185
243
|
return {
|
|
186
244
|
id: "semantic",
|
|
187
245
|
label: "Semantic",
|
|
188
|
-
|
|
189
|
-
|
|
246
|
+
semantic: true,
|
|
247
|
+
referencesRamps: [
|
|
248
|
+
{ tab: "palette", tier: "base" },
|
|
249
|
+
{ tab: "palette", tier: "accent" },
|
|
250
|
+
{ tab: "palette", tier: "state" },
|
|
251
|
+
],
|
|
252
|
+
items: buildSemanticTierItems(schemeForMode(mode)),
|
|
190
253
|
};
|
|
191
254
|
}
|
|
192
255
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
256
|
+
/**
|
|
257
|
+
* Color cluster extras. Scheme-less (`colorSchemes: {}`) with no base-role
|
|
258
|
+
* editors (`baseRoles`/`baseDefaults` empty) — the Palette tab owns the ramps
|
|
259
|
+
* and the semantic tier owns the roles.
|
|
260
|
+
*
|
|
261
|
+
* `panelSettings.colorMode` is an OBJECT (not `false`) with `defaultMode = mode`.
|
|
262
|
+
* On this scheme-less cluster its only live effect is `getClusterDefaultMode()`,
|
|
263
|
+
* which drives the per-mode literal collapse/preview side — pinning it to the
|
|
264
|
+
* ACTIVE mode keeps that side following the live toggle (a bare `false` would
|
|
265
|
+
* pin it to `light` and mis-collapse per-mode literals for a dark-mode user).
|
|
266
|
+
* `colorScheme`/`lightScheme`/`darkScheme` only need to be non-empty strings
|
|
267
|
+
* (the validator requires that even with `colorSchemes: {}`; the names are not
|
|
268
|
+
* checked against the empty registry).
|
|
269
|
+
*/
|
|
270
|
+
function buildColorExtras(mode: PanelMode): ColorClusterExtras {
|
|
271
|
+
const cm = settings.colorMode;
|
|
272
|
+
const lightScheme = cm ? cm.lightScheme : settings.colorScheme;
|
|
273
|
+
const darkScheme = cm ? cm.darkScheme : settings.colorScheme;
|
|
274
|
+
return {
|
|
275
|
+
// Customize id/label to match your project name to avoid localStorage
|
|
276
|
+
// collisions when multiple zudo-doc-based projects run in the same browser.
|
|
277
|
+
id: "my-doc",
|
|
278
|
+
label: "My Doc",
|
|
279
|
+
baseRoles: {},
|
|
280
|
+
baseDefaults: {},
|
|
281
|
+
defaultShikiTheme: DEFAULT_SHIKI_THEME,
|
|
282
|
+
colorSchemes: {},
|
|
283
|
+
panelSettings: {
|
|
284
|
+
colorScheme: settings.colorScheme,
|
|
285
|
+
colorMode: { defaultMode: mode, lightScheme, darkScheme },
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
222
289
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
290
|
+
function buildColorTab(mode: PanelMode): TabConfig {
|
|
291
|
+
return {
|
|
292
|
+
id: "color",
|
|
293
|
+
label: "Color",
|
|
294
|
+
tiers: [buildSemanticTier(mode)],
|
|
295
|
+
colorExtras: buildColorExtras(mode),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
229
298
|
|
|
230
299
|
// ---------------------------------------------------------------------------
|
|
231
|
-
// Font tab
|
|
300
|
+
// Font tab — five tiers grouped by the manifest's `group` field.
|
|
232
301
|
// ---------------------------------------------------------------------------
|
|
233
302
|
|
|
234
303
|
const FONT_SCALE_TIER_ID = "font-scale";
|
|
235
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Tier 2 semantic role → Tier 1 abstract scale item id. Mirrors the `var(--…)`
|
|
307
|
+
* wiring in `global.css` (`--text-body: var(--text-scale-md)` etc.). The role
|
|
308
|
+
* tier is a *reference* tier: each item's stored value is the id of a
|
|
309
|
+
* `font-scale` item, exactly like the Color tab's semantic→palette tier. zdtp
|
|
310
|
+
* renders these as dropdowns and emits `var(--text-scale-*)`, so editing a
|
|
311
|
+
* scale step propagates to every role live.
|
|
312
|
+
*/
|
|
313
|
+
const FONT_ROLE_TO_SCALE: Readonly<Record<string, string>> = {
|
|
314
|
+
"text-micro": "text-scale-2xs",
|
|
315
|
+
"text-caption": "text-scale-xs",
|
|
316
|
+
"text-small": "text-scale-sm",
|
|
317
|
+
"text-body": "text-scale-md",
|
|
318
|
+
"text-title": "text-scale-lg",
|
|
319
|
+
"text-heading": "text-scale-xl",
|
|
320
|
+
"text-display": "text-scale-2xl",
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Build the semantic font-size tier as a reference tier pointing at the
|
|
325
|
+
* `font-scale` tier. Defaults are overridden to the referenced scale id (the
|
|
326
|
+
* manifest still records resolved rem values for serde / the flat-manifest
|
|
327
|
+
* generator template, so the override happens here rather than in the
|
|
328
|
+
* manifest to keep both consumers correct).
|
|
329
|
+
*/
|
|
330
|
+
function buildFontRoleTier(): TierConfig {
|
|
331
|
+
const base = tierFromGroup(FONT_TOKENS, "font-size", "Font size");
|
|
332
|
+
return {
|
|
333
|
+
...base,
|
|
334
|
+
items: base.items.map((item) => {
|
|
335
|
+
const scaleId = FONT_ROLE_TO_SCALE[item.id];
|
|
336
|
+
return scaleId ? { ...item, default: scaleId } : item;
|
|
337
|
+
}),
|
|
338
|
+
referencesTier: FONT_SCALE_TIER_ID,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
236
342
|
const FONT_TAB: TabConfig = {
|
|
237
343
|
id: "font",
|
|
238
344
|
label: "Font",
|
|
239
345
|
tiers: [
|
|
240
346
|
tierFromGroup(FONT_TOKENS, FONT_SCALE_TIER_ID, "Scale"),
|
|
241
|
-
|
|
347
|
+
buildFontRoleTier(),
|
|
242
348
|
tierFromGroup(FONT_TOKENS, "line-height", "Line height"),
|
|
243
349
|
tierFromGroup(FONT_TOKENS, "font-weight", "Font weight"),
|
|
244
350
|
tierFromGroup(FONT_TOKENS, "font-family", "Font family"),
|
|
@@ -246,7 +352,7 @@ const FONT_TAB: TabConfig = {
|
|
|
246
352
|
};
|
|
247
353
|
|
|
248
354
|
// ---------------------------------------------------------------------------
|
|
249
|
-
// Spacing tab
|
|
355
|
+
// Spacing tab — four tiers grouped by the manifest's `group` field.
|
|
250
356
|
// ---------------------------------------------------------------------------
|
|
251
357
|
|
|
252
358
|
const SPACING_TAB: TabConfig = {
|
|
@@ -261,7 +367,7 @@ const SPACING_TAB: TabConfig = {
|
|
|
261
367
|
};
|
|
262
368
|
|
|
263
369
|
// ---------------------------------------------------------------------------
|
|
264
|
-
// Size tab
|
|
370
|
+
// Size tab — two tiers grouped by the manifest's `group` field.
|
|
265
371
|
// ---------------------------------------------------------------------------
|
|
266
372
|
|
|
267
373
|
const SIZE_TAB: TabConfig = {
|
|
@@ -273,16 +379,57 @@ const SIZE_TAB: TabConfig = {
|
|
|
273
379
|
],
|
|
274
380
|
};
|
|
275
381
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
382
|
+
/**
|
|
383
|
+
* Detect the initial color-scheme mode for the first `configurePanel` call. In
|
|
384
|
+
* the browser, read `<html data-theme>` (set pre-paint by the
|
|
385
|
+
* ColorSchemeProvider bootstrap); otherwise fall back to
|
|
386
|
+
* `settings.colorMode.defaultMode` (or `light` on the single-scheme path).
|
|
387
|
+
* SSR-safe: guards `document`.
|
|
388
|
+
*/
|
|
389
|
+
function detectInitialMode(): PanelMode {
|
|
390
|
+
if (typeof document !== "undefined") {
|
|
391
|
+
const attr = document.documentElement.getAttribute("data-theme");
|
|
392
|
+
if (attr === "light" || attr === "dark") return attr;
|
|
393
|
+
}
|
|
394
|
+
const cm = settings.colorMode;
|
|
395
|
+
return cm ? cm.defaultMode : "light";
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Build the full PanelConfig for a given color-scheme `mode`. Only the Color
|
|
400
|
+
* tab's semantic tier and `panelSettings.colorMode.defaultMode` vary by mode;
|
|
401
|
+
* the Palette/Font/Spacing/Size tabs are mode-independent. The bootstrap calls
|
|
402
|
+
* this per mode on every `color-scheme-changed` toggle (destroy + reconfigure).
|
|
403
|
+
*/
|
|
404
|
+
export function buildDesignTokenPanelConfig(mode: PanelMode): PanelConfig {
|
|
405
|
+
return {
|
|
406
|
+
// Customize these values to match your project name to avoid localStorage
|
|
407
|
+
// collisions when multiple zudo-doc-based projects run in the same browser.
|
|
408
|
+
storagePrefix: "my-doc-tweak",
|
|
409
|
+
consoleNamespace: "myDoc",
|
|
410
|
+
modalClassPrefix: "my-doc-design-token-panel-modal",
|
|
411
|
+
// DISPLAY-ONLY in zdtp 0.4.5: the panel's export hard-codes
|
|
412
|
+
// `zudo-design-tokens/v2` and auto-upgrades to `.../v3` when object leaves
|
|
413
|
+
// ({ref}/{literal}/per-mode) are present — which the semantic tier's ramp
|
|
414
|
+
// refs always are, so real exports carry v3. `schemaId` does NOT gate
|
|
415
|
+
// import; it only labels the Import-modal hint. zdtp DOES export its own
|
|
416
|
+
// `SCHEMA_V1`/`SCHEMA_V2`/`SCHEMA_V3` constants (#498/#505), but those are
|
|
417
|
+
// zdtp's fixed internal schema strings, unrelated to this field. Set to v3
|
|
418
|
+
// (a literal, not one of those constants) so the hint matches what exports
|
|
419
|
+
// actually carry.
|
|
420
|
+
schemaId: "zudo-design-tokens/v3",
|
|
421
|
+
exportFilenameBase: "my-doc-design-tokens",
|
|
422
|
+
tabs: [PALETTE_TAB, buildColorTab(mode), FONT_TAB, SPACING_TAB, SIZE_TAB],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Back-compat / initial-call export: the config for the mode detected at module
|
|
428
|
+
* load. Pass the `buildDesignTokenPanelConfig` BUILDER (not this plain object)
|
|
429
|
+
* to the bootstrap for live mode-scoped rebuilds on light/dark toggle — see
|
|
430
|
+
* `src/lib/design-token-panel-bootstrap.ts`. This plain-config export remains
|
|
431
|
+
* for callers that only need a static config.
|
|
432
|
+
*/
|
|
433
|
+
export const designTokenPanelConfig: PanelConfig = buildDesignTokenPanelConfig(
|
|
434
|
+
detectInitialMode(),
|
|
435
|
+
);
|
|
@@ -5,9 +5,16 @@
|
|
|
5
5
|
* settings.designTokenPanel is truthy. The dynamic import is gated there so
|
|
6
6
|
* this module is only bundled when the feature is enabled.
|
|
7
7
|
*
|
|
8
|
-
* The wiring MECHANISM (configurePanel + setLifecycleAdapter
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The wiring MECHANISM (configurePanel + setLifecycleAdapter + the mode-scoped
|
|
9
|
+
* reconfigure) lives in the package at
|
|
10
|
+
* `@takazudo/zudo-doc/design-token-panel-bootstrap`. This file passes the
|
|
11
|
+
* project's PanelConfig DATA to that mechanism.
|
|
12
|
+
*
|
|
13
|
+
* We pass the `buildDesignTokenPanelConfig` BUILDER (not the plain
|
|
14
|
+
* `designTokenPanelConfig` object) so the bootstrap rebuilds the panel per
|
|
15
|
+
* light/dark mode on every `color-scheme-changed` toggle — keeping the Color
|
|
16
|
+
* tab's semantic defaults mode-faithful. A plain-config caller also works (see
|
|
17
|
+
* `bootstrapDesignTokenPanel`'s JSDoc) but gets no toggle listener.
|
|
11
18
|
*
|
|
12
19
|
* S9a package-first migration — zudolab/zudo-doc#2333.
|
|
13
20
|
*
|
|
@@ -18,6 +25,6 @@
|
|
|
18
25
|
*/
|
|
19
26
|
|
|
20
27
|
import { bootstrapDesignTokenPanel } from "@takazudo/zudo-doc/design-token-panel-bootstrap";
|
|
21
|
-
import {
|
|
28
|
+
import { buildDesignTokenPanelConfig } from "@/config/design-token-panel-config";
|
|
22
29
|
|
|
23
|
-
bootstrapDesignTokenPanel(
|
|
30
|
+
bootstrapDesignTokenPanel(buildDesignTokenPanelConfig);
|