@bytechain.cn/colamd-themes 0.3.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.
Files changed (108) hide show
  1. package/.claude/skills/colamd-themes-preview.skill.md +333 -0
  2. package/.claude/skills/colamd-themes.skill.md +155 -0
  3. package/.hermes/skills/tools/colamd-themes/SKILL.md +159 -0
  4. package/.openclaw/skills/colamd-themes/SKILL.md +166 -0
  5. package/.opencode/skills/colamd-themes/SKILL.md +162 -0
  6. package/.trae/rules/colamd-themes.md +150 -0
  7. package/CHANGELOG.md +102 -0
  8. package/README.md +373 -0
  9. package/README_CN.md +373 -0
  10. package/RELEASENOTE.md +177 -0
  11. package/dist/cli.d.ts +2 -0
  12. package/dist/cli.js +208 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/color-systems.d.ts +17 -0
  15. package/dist/color-systems.js +251 -0
  16. package/dist/color-systems.js.map +1 -0
  17. package/dist/color-utils.d.ts +9 -0
  18. package/dist/color-utils.js +112 -0
  19. package/dist/color-utils.js.map +1 -0
  20. package/dist/contrast.d.ts +15 -0
  21. package/dist/contrast.js +63 -0
  22. package/dist/contrast.js.map +1 -0
  23. package/dist/docx-style-paradigm.d.ts +153 -0
  24. package/dist/docx-style-paradigm.js +640 -0
  25. package/dist/docx-style-paradigm.js.map +1 -0
  26. package/dist/export-cli.d.ts +10 -0
  27. package/dist/export-cli.js +266 -0
  28. package/dist/export-cli.js.map +1 -0
  29. package/dist/extractors/base.d.ts +6 -0
  30. package/dist/extractors/base.js +3 -0
  31. package/dist/extractors/base.js.map +1 -0
  32. package/dist/extractors/docx-extractor.d.ts +52 -0
  33. package/dist/extractors/docx-extractor.js +370 -0
  34. package/dist/extractors/docx-extractor.js.map +1 -0
  35. package/dist/extractors/pdf-extractor.d.ts +66 -0
  36. package/dist/extractors/pdf-extractor.js +430 -0
  37. package/dist/extractors/pdf-extractor.js.map +1 -0
  38. package/dist/extractors/url-extractor.d.ts +104 -0
  39. package/dist/extractors/url-extractor.js +761 -0
  40. package/dist/extractors/url-extractor.js.map +1 -0
  41. package/dist/generator.d.ts +4 -0
  42. package/dist/generator.js +71 -0
  43. package/dist/generator.js.map +1 -0
  44. package/dist/index.d.ts +10 -0
  45. package/dist/index.js +9 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/mermaid-presets.d.ts +14 -0
  48. package/dist/mermaid-presets.js +122 -0
  49. package/dist/mermaid-presets.js.map +1 -0
  50. package/dist/models.d.ts +106 -0
  51. package/dist/models.js +92 -0
  52. package/dist/models.js.map +1 -0
  53. package/dist/renderer.d.ts +70 -0
  54. package/dist/renderer.js +204 -0
  55. package/dist/renderer.js.map +1 -0
  56. package/dist/templates/template.css +498 -0
  57. package/dist/templates/theme-paradigm.md +658 -0
  58. package/dist/templates/theme-template.hbs +370 -0
  59. package/dist/theme-store.d.ts +37 -0
  60. package/dist/theme-store.js +90 -0
  61. package/dist/theme-store.js.map +1 -0
  62. package/dist/validators.d.ts +26 -0
  63. package/dist/validators.js +144 -0
  64. package/dist/validators.js.map +1 -0
  65. package/package.json +36 -0
  66. package/skills/build-skills.sh +166 -0
  67. package/skills/colamd-themes/SKILL.md +148 -0
  68. package/src/cli.ts +200 -0
  69. package/src/color-systems.ts +289 -0
  70. package/src/color-utils.ts +120 -0
  71. package/src/contrast.ts +66 -0
  72. package/src/docx-style-paradigm.ts +811 -0
  73. package/src/export-cli.ts +301 -0
  74. package/src/extractors/base.ts +8 -0
  75. package/src/extractors/docx-extractor.ts +417 -0
  76. package/src/extractors/pdf-extractor.ts +527 -0
  77. package/src/extractors/url-extractor.ts +927 -0
  78. package/src/generator.ts +88 -0
  79. package/src/index.ts +29 -0
  80. package/src/mermaid-presets.ts +142 -0
  81. package/src/models.ts +212 -0
  82. package/src/renderer.ts +277 -0
  83. package/src/templates/template.css +498 -0
  84. package/src/templates/theme-paradigm.md +658 -0
  85. package/src/templates/theme-template.hbs +370 -0
  86. package/src/theme-store.ts +112 -0
  87. package/src/types/pdf-parse.d.ts +12 -0
  88. package/src/validators.ts +176 -0
  89. package/themes/.gitkeep +0 -0
  90. package/themes/academic-paper-pt.css +1805 -0
  91. package/themes/academic-paper.css +1099 -0
  92. package/themes/academic-style.css +2430 -0
  93. package/themes/colly.css +370 -0
  94. package/themes/elegant.css +671 -0
  95. package/themes/forest-ink.css +664 -0
  96. package/themes/guizang.css +732 -0
  97. package/themes/meeting-minutes.css +370 -0
  98. package/themes/pixso-design.css +1261 -0
  99. package/themes/salvator-blog.css +370 -0
  100. package/themes/swiss-design copy.css +746 -0
  101. package/themes/swiss-design.css +746 -0
  102. package/themes/template.css +498 -0
  103. package/themes/trae-blue.css +737 -0
  104. package/themes-preview/colly-preview.html +623 -0
  105. package/themes-preview/demo-content.md +187 -0
  106. package/themes-preview/meeting-minutes-preview.html +625 -0
  107. package/themes-preview/salvator-blog-preview.html +623 -0
  108. package/tsconfig.json +18 -0
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@bytechain.cn/colamd-themes",
3
+ "version": "0.3.0",
4
+ "description": "Theme development & export tool for ColaMD — extract formatting, generate themes, and export Markdown to HTML/PDF",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "colamd-themes": "./dist/cli.js",
9
+ "cthemes": "./dist/cli.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc && node --input-type=commonjs -e \"require('fs').cpSync('src/templates','dist/templates',{recursive:true})\"",
13
+ "build:skills": "bash skills/build-skills.sh",
14
+ "dev": "tsx src/cli.ts",
15
+ "start": "node dist/cli.js"
16
+ },
17
+ "dependencies": {
18
+ "@bytechain.cn/colamd": "^1.5.2",
19
+ "axios": "^1.7",
20
+ "chalk": "^5.3",
21
+ "cheerio": "^1.0",
22
+ "commander": "^12.0",
23
+ "css-tree": "^3.0",
24
+ "handlebars": "^4.7",
25
+ "mammoth": "^1.8",
26
+ "ora": "^8.0",
27
+ "pdf-parse": "^1.1",
28
+ "puppeteer": "^24.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/css-tree": "^2.3",
32
+ "@types/node": "^22.0",
33
+ "tsx": "^4.0",
34
+ "typescript": "^5.5"
35
+ }
36
+ }
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Build agent-specific skill files from the canonical source.
4
+ #
5
+ # Usage:
6
+ # bash skills/build-skills.sh # generate all
7
+ # bash skills/build-skills.sh claude # generate for one agent
8
+ #
9
+ # Source: skills/colamd-themes/SKILL.md
10
+ # Output: .{agent}/skills/colamd-themes/SKILL.md (or equivalent)
11
+
12
+ set -euo pipefail
13
+ cd "$(dirname "$0")/.."
14
+
15
+ SOURCE="skills/colamd-themes/SKILL.md"
16
+ if [[ ! -f "$SOURCE" ]]; then
17
+ echo "Error: Source skill not found: $SOURCE" >&2
18
+ exit 1
19
+ fi
20
+
21
+ # Read the body (everything after the first blank line following the heading)
22
+ BODY=$(sed -n '/^$/,$ { /^# ColaMD/!p; }' "$SOURCE" | sed '1{/^$/d;}')
23
+
24
+ # ── Frontmatter templates ──
25
+
26
+ FRONT_CLAUDE='---
27
+ name: colamd-themes
28
+ description: >
29
+ Generate, validate, export, and manage ColaMD v3.0 paradigm CSS themes.
30
+ Extract formatting from URLs, DOCX, or PDF sources; export Markdown to
31
+ standalone HTML/PDF with themes applied; batch export; manage theme
32
+ configuration.
33
+ ---'
34
+
35
+ FRONT_OPENCODE='---
36
+ name: colamd-themes
37
+ version: "1.0.0"
38
+ description: >
39
+ Generate, validate, export, and manage ColaMD v3.0 paradigm CSS themes.
40
+ Extract from URLs/DOCX/PDF; export Markdown to HTML/PDF; batch export.
41
+ user-invocable: true
42
+ allowed-tools:
43
+ - Read
44
+ - Write
45
+ - Edit
46
+ - Bash
47
+ - Glob
48
+ - Grep
49
+ ---'
50
+
51
+ FRONT_OPENCLAW='---
52
+ id: colamd-themes
53
+ name: ColaMD Themes
54
+ description: >
55
+ Generate, validate, export, and manage ColaMD v3.0 paradigm CSS themes.
56
+ Extract from URLs/DOCX/PDF; export Markdown to HTML/PDF; batch export.
57
+ version: "1.0.0"
58
+ icon: "\U0001F3A8"
59
+ author: bytechain
60
+ homepage: https://github.com/byteuser1977/ColaMD-themes
61
+ user-invocable: true
62
+ metadata:
63
+ openclaw:
64
+ os: [darwin, linux, win32]
65
+ clawdbot:
66
+ emoji: "\U0001F3A8"
67
+ requires:
68
+ bins: [node, npm]
69
+ ---'
70
+
71
+ FRONT_HERMES='---
72
+ name: colamd-themes
73
+ description: "Generate, validate, export, and manage ColaMD v3.0 paradigm CSS themes."
74
+ version: 1.0.0
75
+ author: bytechain
76
+ license: MIT
77
+ metadata:
78
+ hermes:
79
+ tags: [colamd, themes, css, markdown, export, pdf, html]
80
+ prerequisites:
81
+ commands: [node, npm]
82
+ ---'
83
+
84
+ # ── Generator functions ──
85
+
86
+ generate_claude() {
87
+ local dir=".claude/skills"
88
+ mkdir -p "$dir"
89
+ local file="$dir/colamd-themes.skill.md"
90
+ echo "$FRONT_CLAUDE" > "$file"
91
+ echo "" >> "$file"
92
+ echo "$BODY" >> "$file"
93
+ echo " → $file"
94
+ }
95
+
96
+ generate_opencode() {
97
+ local dir=".opencode/skills/colamd-themes"
98
+ mkdir -p "$dir"
99
+ local file="$dir/SKILL.md"
100
+ echo "$FRONT_OPENCODE" > "$file"
101
+ echo "" >> "$file"
102
+ echo "$BODY" >> "$file"
103
+ echo " → $file"
104
+ }
105
+
106
+ generate_openclaw() {
107
+ local dir=".openclaw/skills/colamd-themes"
108
+ mkdir -p "$dir"
109
+ local file="$dir/SKILL.md"
110
+ echo "$FRONT_OPENCLAW" > "$file"
111
+ echo "" >> "$file"
112
+ echo "$BODY" >> "$file"
113
+ echo " → $file"
114
+ }
115
+
116
+ generate_hermes() {
117
+ local dir=".hermes/skills/tools/colamd-themes"
118
+ mkdir -p "$dir"
119
+ local file="$dir/SKILL.md"
120
+ echo "$FRONT_HERMES" > "$file"
121
+ echo "" >> "$file"
122
+ echo "$BODY" >> "$file"
123
+ echo " → $file"
124
+ }
125
+
126
+ generate_trae() {
127
+ local dir=".trae/rules"
128
+ mkdir -p "$dir"
129
+ local file="$dir/colamd-themes.md"
130
+ # Trae uses plain Markdown, no YAML frontmatter
131
+ echo "# ColaMD Themes Skill Instructions" > "$file"
132
+ echo "" >> "$file"
133
+ echo "When the user asks to create, validate, export, or manage ColaMD themes, use the following CLI tool:" >> "$file"
134
+ echo "" >> "$file"
135
+ echo "$BODY" >> "$file"
136
+ echo " → $file"
137
+ }
138
+
139
+ # ── Main ──
140
+
141
+ TARGET="${1:-all}"
142
+ echo "Building skill files from: $SOURCE"
143
+ echo ""
144
+
145
+ case "$TARGET" in
146
+ claude) generate_claude ;;
147
+ opencode) generate_opencode ;;
148
+ openclaw) generate_openclaw ;;
149
+ hermes) generate_hermes ;;
150
+ trae) generate_trae ;;
151
+ all)
152
+ generate_claude
153
+ generate_opencode
154
+ generate_openclaw
155
+ generate_hermes
156
+ generate_trae
157
+ ;;
158
+ *)
159
+ echo "Unknown agent: $TARGET" >&2
160
+ echo "Usage: $0 [claude|opencode|openclaw|hermes|trae|all]" >&2
161
+ exit 1
162
+ ;;
163
+ esac
164
+
165
+ echo ""
166
+ echo "Done. Skill files generated."
@@ -0,0 +1,148 @@
1
+ # ColaMD Themes — Universal AI Agent Skill
2
+
3
+ Generate, validate, export, and manage ColaMD v3.0 paradigm CSS themes. Extract formatting from URLs, DOCX, or PDF sources; export Markdown to standalone HTML/PDF with themes applied; batch export; manage theme configuration.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js ≥ 18
8
+ - Project root: run `npm install && npm run build` before first use
9
+ - Or run directly: `npx tsx src/cli.ts <command>`
10
+
11
+ ## Commands
12
+
13
+ ### Theme extraction
14
+
15
+ ```bash
16
+ colamd-themes from-url <URL> [options]
17
+ colamd-themes from-docx <file> [options]
18
+ colamd-themes from-pdf <file> [options]
19
+ colamd-themes extract <source> [options]
20
+ ```
21
+
22
+ ### Export to HTML / PDF
23
+
24
+ ```bash
25
+ colamd-themes export-html <input.md> -t <theme> -o <output.html>
26
+ colamd-themes export-pdf <input.md> -t <theme> -o <output.pdf>
27
+ colamd-themes export <inputs...> --format html|pdf -t <theme> -d <dir>
28
+ ```
29
+
30
+ ### Theme management
31
+
32
+ ```bash
33
+ colamd-themes set-theme <name> --default
34
+ colamd-themes set-theme <name> --css <file>
35
+ colamd-themes set-theme <name> --css <file> --default
36
+ colamd-themes set-theme <name> --remove
37
+ ```
38
+
39
+ ### Inspection
40
+
41
+ ```bash
42
+ colamd-themes color-systems [-j]
43
+ colamd-themes mermaid-presets [-j]
44
+ colamd-themes list [-d <dir>]
45
+ colamd-themes validate <file.css>
46
+ ```
47
+
48
+ ## Options
49
+
50
+ ### Extract options
51
+
52
+ | Flag | Description |
53
+ |------|-------------|
54
+ | `-n, --name` | Theme name |
55
+ | `-o, --output` | Output CSS file path |
56
+ | `-d, --themes-dir` | Themes directory (default: `themes/`) |
57
+ | `-c, --color-system` | Force a color system |
58
+ | `-m, --mermaid-preset` | Force a Mermaid preset (light/dark/elegant) |
59
+ | `-A, --auto-match` | Auto-match color system and Mermaid preset |
60
+
61
+ ### Export options
62
+
63
+ | Flag | Description |
64
+ |------|-------------|
65
+ | `-t, --theme` | Theme: built-in name, custom name, or `.css` file path |
66
+ | `-o, --output` | Output file path |
67
+ | `-d, --output-dir` | Output directory for batch export |
68
+ | `--format` | Batch: `html` or `pdf`; PDF: `A4`, `Letter`, `A3` |
69
+
70
+ ## Theme Resolution
71
+
72
+ `-t, --theme` accepts:
73
+ 1. **Built-in**: `light`, `dark`, `elegant`, `newsprint`
74
+ 2. **Custom registered**: name set via `set-theme --css`
75
+ 3. **File path**: any `.css` file on disk
76
+
77
+ Config: `~/.colamd-themes/config.json`
78
+
79
+ ## Color Systems (15 schemes)
80
+
81
+ | Family | Schemes |
82
+ |--------|---------|
83
+ | 莫兰迪 (Morandi) | misty-rose, sage-green, dusky-blue |
84
+ | 马卡龙 (Macaron) | strawberry, mint, lavender |
85
+ | 北欧 (Nordic) | fjord-blue, pine-green, cloud-grey |
86
+ | 复古 (Vintage) | amber, burgundy, forest |
87
+ | 薄荷绿 (Mint) | matcha, spearmint, seafoam |
88
+
89
+ ## Mermaid Presets
90
+
91
+ `light` — cool accent on light bg | `dark` — dark bg | `elegant` — warm accent on light bg
92
+
93
+ ## Workflow Examples
94
+
95
+ ### Create theme from source
96
+
97
+ ```bash
98
+ colamd-themes extract <source> --name "my-theme" --auto-match
99
+ ```
100
+
101
+ ### Export with theme
102
+
103
+ ```bash
104
+ colamd-themes export-html doc.md -t elegant -o output.html
105
+ colamd-themes export-pdf doc.md -t my-brand -o output.pdf
106
+ ```
107
+
108
+ ### Batch export
109
+
110
+ ```bash
111
+ colamd-themes export docs/ --format pdf -t dark -d output/
112
+ ```
113
+
114
+ ### Validate theme
115
+
116
+ ```bash
117
+ colamd-themes validate themes/my-theme.css
118
+ ```
119
+
120
+ ## Rendering Pipeline
121
+
122
+ Export uses Puppeteer (headless Chromium) with `@bytechain.cn/colamd/renderer`:
123
+
124
+ 1. Local HTTP server serves renderer ESM bundle (avoids CORS)
125
+ 2. Puppeteer navigates to server page (same-origin)
126
+ 3. `createColaMDEditor({ editable: false })` initializes Milkdown editor
127
+ 4. `applyTheme(name, customCSS?)` sets theme
128
+ 5. `setMarkdown(content)` loads document
129
+ 6. `ensureAllPluginsRendered()` waits for mermaid/KaTeX
130
+ 7. `buildExportHTML()` → standalone HTML with embedded CSS
131
+ 8. For PDF: `emulateMediaType('screen')` + `page.pdf()`
132
+
133
+ ## v3.0 Paradigm Constraints
134
+
135
+ - No bare hex colors in selectors — use `var(--seed-*)`
136
+ - No bare font sizes — use `var(--font-*)`
137
+ - No Mermaid SVG selectors (handled by built-in `variables.css`)
138
+ - `!important` only in `@media print`
139
+ - Generated CSS ≤ 300 lines
140
+ - Font stacks must include CJK fallback and end with generic family
141
+
142
+ ## Architecture
143
+
144
+ ```
145
+ Source → Extractor → ThemeStyle → Color System + Mermaid Preset + Validators → CSS
146
+
147
+ Markdown → Puppeteer + ColaMD Renderer → HTML / PDF
148
+ ```
package/src/cli.ts ADDED
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
5
+ import { join, isAbsolute } from "node:path";
6
+ import chalk from "chalk";
7
+ import ora from "ora";
8
+ import { UrlExtractor } from "./extractors/url-extractor.js";
9
+ import { DocxExtractor } from "./extractors/docx-extractor.js";
10
+ import { PdfExtractor } from "./extractors/pdf-extractor.js";
11
+ import type { Extractor } from "./extractors/base.js";
12
+ import { writeThemeFile } from "./generator.js";
13
+ import { COLOR_SYSTEMS, getColorSystem } from "./color-systems.js";
14
+ import { MERMAID_PRESETS } from "./mermaid-presets.js";
15
+ import { validateSeedPalette, summarizeIssues } from "./validators.js";
16
+ import type { ThemeStyle } from "./models.js";
17
+ import { registerExportCommands } from "./export-cli.js";
18
+
19
+ const program = new Command();
20
+
21
+ program
22
+ .name("colamd-themes")
23
+ .description("ColaMD Theme Development Tool — extract formatting and generate v3.0 paradigm CSS themes")
24
+ .version("0.2.0");
25
+
26
+ // ── Shared extract options builder ──
27
+
28
+ function addExtractOptions(cmd: Command): Command {
29
+ return cmd
30
+ .option("-n, --name <name>", "Theme name")
31
+ .option("-o, --output <path>", "Output CSS file path")
32
+ .option("-d, --themes-dir <dir>", "Themes directory", "themes")
33
+ .option("-c, --color-system <id>", "Force a color system (use `color-systems` to list)")
34
+ .option("-m, --mermaid-preset <id>", "Force a Mermaid preset (light/dark/elegant)")
35
+ .option("-A, --auto-match", "Auto-match color system and mermaid preset");
36
+ }
37
+
38
+ async function runExtraction(
39
+ extractor: Extractor,
40
+ source: string,
41
+ options: Record<string, string>
42
+ ) {
43
+ const spinner = ora("Extracting styles...").start();
44
+ try {
45
+ const style = await extractor.extract(source);
46
+ if (options.name) style.name = options.name;
47
+ if (options.colorSystem) {
48
+ const cs = getColorSystem(options.colorSystem);
49
+ if (!cs) { spinner.fail(`Unknown color system: ${options.colorSystem}`); process.exit(1); }
50
+ style.seed = { ...cs.palette };
51
+ style.colorSystemId = cs.id;
52
+ }
53
+ if (options.mermaidPreset) style.mermaidPresetId = options.mermaidPreset;
54
+ if (options.autoMatch) { style.colorSystemId = ""; style.mermaidPresetId = ""; }
55
+ spinner.text = "Generating CSS (v3.0 paradigm)...";
56
+ const outputPath = writeThemeFile(style, options.output || options.themesDir);
57
+ spinner.succeed(chalk.green(`Theme "${style.name}" generated: ${outputPath}`));
58
+ printSummary(style);
59
+ } catch (err) {
60
+ spinner.fail(chalk.red(`Failed: ${(err as Error).message}`));
61
+ process.exit(1);
62
+ }
63
+ }
64
+
65
+ // ── from-url ──
66
+ addExtractOptions(
67
+ program.command("from-url")
68
+ .description("Extract theme from a web page URL")
69
+ .argument("<url>", "URL of the web page")
70
+ ).action(async (url: string, options: Record<string, string>) => {
71
+ await runExtraction(new UrlExtractor(), url, options);
72
+ });
73
+
74
+ // ── from-docx ──
75
+ addExtractOptions(
76
+ program.command("from-docx")
77
+ .description("Extract theme from a .docx document")
78
+ .argument("<file>", "Path to the .docx file")
79
+ ).action(async (file: string, options: Record<string, string>) => {
80
+ if (!existsSync(file)) { console.error(chalk.red(`File not found: ${file}`)); process.exit(1); }
81
+ await runExtraction(new DocxExtractor(), file, options);
82
+ });
83
+
84
+ // ── from-pdf ──
85
+ addExtractOptions(
86
+ program.command("from-pdf")
87
+ .description("Extract theme from a PDF document")
88
+ .argument("<file>", "Path to the PDF file")
89
+ ).action(async (file: string, options: Record<string, string>) => {
90
+ if (!existsSync(file)) { console.error(chalk.red(`File not found: ${file}`)); process.exit(1); }
91
+ await runExtraction(new PdfExtractor(), file, options);
92
+ });
93
+
94
+ // ── extract (auto-detect) ──
95
+ addExtractOptions(
96
+ program.command("extract")
97
+ .description("Auto-detect source type and extract theme")
98
+ .argument("<source>", "URL, .docx file, or .pdf file")
99
+ ).action(async (source: string, options: Record<string, string>) => {
100
+ const extractors: Extractor[] = [new UrlExtractor(), new DocxExtractor(), new PdfExtractor()];
101
+ const ext = extractors.find((e) => e.canHandle(source));
102
+ if (!ext) { console.error(chalk.red(`Cannot detect source type for: ${source}`)); process.exit(1); }
103
+ await runExtraction(ext, source, options);
104
+ });
105
+
106
+ // ── color-systems ──
107
+ program
108
+ .command("color-systems")
109
+ .description("List all 15 built-in color systems")
110
+ .option("-j, --json", "Output as JSON")
111
+ .action((options: Record<string, string>) => {
112
+ if (options.json) { console.log(JSON.stringify(COLOR_SYSTEMS, null, 2)); return; }
113
+ const families: Record<string, typeof COLOR_SYSTEMS> = {};
114
+ for (const cs of COLOR_SYSTEMS) {
115
+ const fam = cs.nameCN.split(" ")[0];
116
+ (families[fam] ??= []).push(cs);
117
+ }
118
+ for (const [fam, systems] of Object.entries(families)) {
119
+ console.log(chalk.bold(`\n${fam}`));
120
+ for (const cs of systems) {
121
+ console.log(` ${chalk.cyan(cs.id.padEnd(24))} ${chalk.white(cs.nameCN.split(" ").slice(1).join(" "))} ${chalk.dim(cs.palette.accent)}`);
122
+ }
123
+ }
124
+ console.log();
125
+ });
126
+
127
+ // ── mermaid-presets ──
128
+ program
129
+ .command("mermaid-presets")
130
+ .description("List Mermaid theme presets (light / dark / elegant)")
131
+ .option("-j, --json", "Output as JSON")
132
+ .action((options: Record<string, string>) => {
133
+ if (options.json) { console.log(JSON.stringify(MERMAID_PRESETS, null, 2)); return; }
134
+ for (const p of MERMAID_PRESETS) {
135
+ console.log(` ${chalk.cyan(p.id.padEnd(12))} ${chalk.white(p.name)} ${chalk.dim(p.description)}`);
136
+ }
137
+ console.log();
138
+ });
139
+
140
+ // ── list ──
141
+ program
142
+ .command("list")
143
+ .description("List all generated themes")
144
+ .option("-d, --themes-dir <dir>", "Themes directory", "themes")
145
+ .action((options: Record<string, string>) => {
146
+ const dirPath = isAbsolute(options.themesDir) ? options.themesDir : join(process.cwd(), options.themesDir);
147
+ if (!existsSync(dirPath)) { console.log(chalk.yellow(`No themes directory: ${dirPath}`)); return; }
148
+ const files = readdirSync(dirPath).filter((f) => f.endsWith(".css"));
149
+ if (files.length === 0) { console.log(chalk.yellow("No themes found.")); return; }
150
+ console.log(chalk.bold(`\nThemes in ${dirPath}/:`));
151
+ for (const f of files) console.log(` ${chalk.cyan("●")} ${chalk.white(f.replace(".css", ""))}`);
152
+ console.log(`\n${files.length} theme(s) total.\n`);
153
+ });
154
+
155
+ // ── validate ──
156
+ program
157
+ .command("validate")
158
+ .description("Validate a theme CSS against v3.0 paradigm rules")
159
+ .argument("<file>", "Path to theme CSS file")
160
+ .action((file: string) => {
161
+ if (!existsSync(file)) { console.error(chalk.red(`File not found: ${file}`)); process.exit(1); }
162
+ const css = readFileSync(file, "utf-8");
163
+ const extract = (name: string) => {
164
+ const m = css.match(new RegExp(`${name}:\\s*([^;]+);`));
165
+ return m ? m[1].trim() : "";
166
+ };
167
+ const seed = {
168
+ accent: extract("--seed-accent"), accentLight: extract("--seed-accent-light"),
169
+ accentDark: extract("--seed-accent-dark"), surface: extract("--seed-surface"),
170
+ panel: extract("--seed-panel"), panelAlt: extract("--seed-panel-alt"),
171
+ ink: extract("--seed-ink"), inkMuted: extract("--seed-ink-muted"),
172
+ inkDim: extract("--seed-ink-dim"), border: extract("--seed-border"),
173
+ borderStrong: extract("--seed-border-strong"),
174
+ };
175
+ if (!seed.accent) { console.error(chalk.yellow("Not a v3.0 paradigm theme (no seed palette found).")); process.exit(0); }
176
+ console.log(chalk.bold("Seed palette:"));
177
+ for (const [k, v] of Object.entries(seed)) console.log(` ${chalk.dim(k.padEnd(18))} ${v}`);
178
+ const issues = validateSeedPalette(seed);
179
+ console.log(chalk.bold(`\nParadigm validation (${issues.length} issues):`));
180
+ console.log(summarizeIssues(issues));
181
+ if (issues.filter((i) => i.level === "MUST" || i.level === "MUST_NOT").length > 0) process.exit(1);
182
+ });
183
+
184
+ // ── Export commands (set-theme, export-html, export-pdf, export) ──
185
+ registerExportCommands(program);
186
+
187
+ // ── Summary ──
188
+
189
+ function printSummary(style: ThemeStyle): void {
190
+ const s = style.seed;
191
+ console.log(chalk.dim("\n Seed palette:"));
192
+ console.log(` ${chalk.white("surface:")} ${s.surface} ${chalk.white("ink:")} ${s.ink} ${chalk.white("accent:")} ${s.accent}`);
193
+ console.log(` ${chalk.white("panel:")} ${s.panel} ${chalk.white("panel-alt:")} ${s.panelAlt}`);
194
+ if (style.colorSystemId) {
195
+ console.log(chalk.dim(` Color system: ${style.colorSystemId} Mermaid: ${style.mermaidPresetId}`));
196
+ }
197
+ }
198
+
199
+ export function main(): void { program.parse(); }
200
+ main();