@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
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Export CLI commands: set-theme, export-html, export-pdf, export (batch).
3
+ *
4
+ * These are registered onto the main Commander program in cli.ts.
5
+ */
6
+
7
+ import { Command } from "commander";
8
+ import { existsSync, readFileSync, readdirSync, statSync, mkdirSync } from "node:fs";
9
+ import { join, isAbsolute, resolve, basename } from "node:path";
10
+ import chalk from "chalk";
11
+ import ora from "ora";
12
+ import {
13
+ BUILTIN_THEMES,
14
+ getConfig,
15
+ setDefaultTheme as storeSetDefault,
16
+ registerTheme as storeRegister,
17
+ unregisterTheme as storeUnregister,
18
+ resolveTheme,
19
+ } from "./theme-store.js";
20
+ import { ColamdRenderer, type RenderInput } from "./renderer.js";
21
+
22
+ // ── set-theme ──
23
+
24
+ function registerSetTheme(program: Command): void {
25
+ program
26
+ .command("set-theme")
27
+ .description("Set or register a theme for export")
28
+ .argument("<name>", "Theme name (built-in or custom)")
29
+ .option("--css <file>", "Register as custom theme using this CSS file")
30
+ .option("--default", "Set as the default export theme")
31
+ .option("--remove", "Unregister a custom theme")
32
+ .action((name: string, options: { css?: string; default?: boolean; remove?: boolean }) => {
33
+ // Remove
34
+ if (options.remove) {
35
+ if (storeUnregister(name)) {
36
+ console.log(chalk.green(`✔ Unregistered custom theme "${name}"`));
37
+ } else {
38
+ console.log(chalk.yellow(`Theme "${name}" is not registered.`));
39
+ }
40
+ return;
41
+ }
42
+
43
+ // Register custom theme
44
+ if (options.css) {
45
+ const cssPath = isAbsolute(options.css) ? options.css : resolve(process.cwd(), options.css);
46
+ if (!existsSync(cssPath)) {
47
+ console.error(chalk.red(`CSS file not found: ${cssPath}`));
48
+ process.exit(1);
49
+ }
50
+ storeRegister(name, cssPath);
51
+ console.log(chalk.green(`✔ Registered custom theme "${name}" → ${cssPath}`));
52
+ if (options.default) {
53
+ storeSetDefault(name);
54
+ console.log(chalk.green(`✔ Set "${name}" as default theme`));
55
+ }
56
+ return;
57
+ }
58
+
59
+ // Set as default (built-in or already registered)
60
+ if (!BUILTIN_THEMES.includes(name as any)) {
61
+ const config = getConfig();
62
+ if (!(name in config.themes)) {
63
+ console.error(
64
+ chalk.red(`Unknown theme "${name}". `) +
65
+ chalk.dim(`Built-in: ${BUILTIN_THEMES.join(", ")}. `) +
66
+ chalk.dim("Or register a custom theme with --css <file>.")
67
+ );
68
+ process.exit(1);
69
+ }
70
+ }
71
+ storeSetDefault(name);
72
+ console.log(chalk.green(`✔ Default theme set to "${name}"`));
73
+
74
+ // Show current config
75
+ const config = getConfig();
76
+ console.log(chalk.dim(`\n Default: ${config.defaultTheme}`));
77
+ const customs = Object.keys(config.themes);
78
+ if (customs.length) {
79
+ console.log(chalk.dim(` Custom themes: ${customs.join(", ")}`));
80
+ }
81
+ });
82
+ }
83
+
84
+ // ── export-html ──
85
+
86
+ function registerExportHTML(program: Command): void {
87
+ program
88
+ .command("export-html")
89
+ .description("Export a Markdown file to standalone HTML with theme applied")
90
+ .argument("<input>", "Path to .md file")
91
+ .option("-t, --theme <name>", "Theme name (built-in, custom, or CSS file path)")
92
+ .option("-o, --output <path>", "Output HTML file path")
93
+ .action(async (input: string, options: { theme?: string; output?: string }) => {
94
+ const inputPath = isAbsolute(input) ? input : resolve(process.cwd(), input);
95
+ if (!existsSync(inputPath)) {
96
+ console.error(chalk.red(`File not found: ${inputPath}`));
97
+ process.exit(1);
98
+ }
99
+
100
+ const md = readFileSync(inputPath, "utf-8");
101
+ const title = basename(inputPath, ".md");
102
+ const outputPath = options.output
103
+ ? (isAbsolute(options.output) ? options.output : resolve(process.cwd(), options.output))
104
+ : inputPath.replace(/\.md$/i, ".html");
105
+
106
+ let theme;
107
+ try {
108
+ theme = resolveTheme(options.theme);
109
+ } catch (err) {
110
+ console.error(chalk.red((err as Error).message));
111
+ process.exit(1);
112
+ }
113
+
114
+ const spinner = ora(`Rendering ${basename(inputPath)} → HTML (${theme.themeName})...`).start();
115
+ try {
116
+ const { renderMarkdown } = await import("./renderer.js");
117
+ const result = await renderMarkdown({
118
+ markdown: md,
119
+ themeName: theme.themeName,
120
+ customCSS: theme.customCSS,
121
+ title,
122
+ });
123
+
124
+ const { writeFileSync } = await import("node:fs");
125
+ writeFileSync(outputPath, result.html, "utf-8");
126
+ spinner.succeed(chalk.green(`Exported: ${outputPath}`));
127
+ } catch (err) {
128
+ spinner.fail(chalk.red(`Export failed: ${(err as Error).message}`));
129
+ process.exit(1);
130
+ }
131
+ });
132
+ }
133
+
134
+ // ── export-pdf ──
135
+
136
+ function registerExportPDF(program: Command): void {
137
+ program
138
+ .command("export-pdf")
139
+ .description("Export a Markdown file to PDF with theme applied")
140
+ .argument("<input>", "Path to .md file")
141
+ .option("-t, --theme <name>", "Theme name (built-in, custom, or CSS file path)")
142
+ .option("-o, --output <path>", "Output PDF file path")
143
+ .option("--format <size>", "Page format: A4, Letter, A3", "A4")
144
+ .action(async (input: string, options: { theme?: string; output?: string; format?: string }) => {
145
+ const inputPath = isAbsolute(input) ? input : resolve(process.cwd(), input);
146
+ if (!existsSync(inputPath)) {
147
+ console.error(chalk.red(`File not found: ${inputPath}`));
148
+ process.exit(1);
149
+ }
150
+
151
+ const md = readFileSync(inputPath, "utf-8");
152
+ const title = basename(inputPath, ".md");
153
+ const outputPath = options.output
154
+ ? (isAbsolute(options.output) ? options.output : resolve(process.cwd(), options.output))
155
+ : inputPath.replace(/\.md$/i, ".pdf");
156
+
157
+ let theme;
158
+ try {
159
+ theme = resolveTheme(options.theme);
160
+ } catch (err) {
161
+ console.error(chalk.red((err as Error).message));
162
+ process.exit(1);
163
+ }
164
+
165
+ const spinner = ora(`Rendering ${basename(inputPath)} → PDF (${theme.themeName})...`).start();
166
+ try {
167
+ const { renderPDF } = await import("./renderer.js");
168
+ const pdfBuffer = await renderPDF(
169
+ {
170
+ markdown: md,
171
+ themeName: theme.themeName,
172
+ customCSS: theme.customCSS,
173
+ title,
174
+ },
175
+ { format: options.format as any }
176
+ );
177
+
178
+ const { writeFileSync } = await import("node:fs");
179
+ writeFileSync(outputPath, pdfBuffer);
180
+ spinner.succeed(chalk.green(`Exported: ${outputPath}`));
181
+ } catch (err) {
182
+ spinner.fail(chalk.red(`Export failed: ${(err as Error).message}`));
183
+ process.exit(1);
184
+ }
185
+ });
186
+ }
187
+
188
+ // ── export (batch) ──
189
+
190
+ function registerExport(program: Command): void {
191
+ program
192
+ .command("export")
193
+ .description("Batch export Markdown files to HTML or PDF")
194
+ .argument("<inputs...>", "Paths to .md files (supports globs)")
195
+ .option("--format <type>", "Output format: html or pdf", "html")
196
+ .option("-t, --theme <name>", "Theme name")
197
+ .option("-d, --output-dir <dir>", "Output directory", ".")
198
+ .action(async (inputs: string[], options: { format?: string; theme?: string; outputDir?: string }) => {
199
+ // Resolve glob inputs (shell usually expands, but handle edge cases)
200
+ const files: string[] = [];
201
+ for (const input of inputs) {
202
+ const p = isAbsolute(input) ? input : resolve(process.cwd(), input);
203
+ if (existsSync(p)) {
204
+ if (statSync(p).isDirectory()) {
205
+ // Directory: include all .md files
206
+ const dirFiles = readdirSync(p)
207
+ .filter((f) => f.endsWith(".md"))
208
+ .map((f) => join(p, f));
209
+ files.push(...dirFiles);
210
+ } else {
211
+ files.push(p);
212
+ }
213
+ } else {
214
+ console.log(chalk.yellow(`Skipping (not found): ${input}`));
215
+ }
216
+ }
217
+
218
+ if (files.length === 0) {
219
+ console.error(chalk.red("No valid input files found."));
220
+ process.exit(1);
221
+ }
222
+
223
+ let theme;
224
+ try {
225
+ theme = resolveTheme(options.theme);
226
+ } catch (err) {
227
+ console.error(chalk.red((err as Error).message));
228
+ process.exit(1);
229
+ }
230
+
231
+ const outDir = isAbsolute(options.outputDir ?? ".")
232
+ ? (options.outputDir ?? ".")
233
+ : resolve(process.cwd(), options.outputDir ?? ".");
234
+ const fmt = options.format ?? "html";
235
+
236
+ // Ensure output directory exists
237
+ if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
238
+
239
+ console.log(chalk.bold(`\nBatch export: ${files.length} file(s) → ${fmt.toUpperCase()}`));
240
+ console.log(chalk.dim(` Theme: ${theme.themeName}`));
241
+ console.log(chalk.dim(` Output: ${outDir}/\n`));
242
+
243
+ const renderer = new ColamdRenderer();
244
+ let success = 0;
245
+ let failed = 0;
246
+
247
+ try {
248
+ await renderer.init();
249
+
250
+ for (const file of files) {
251
+ const name = basename(file, ".md");
252
+ const md = readFileSync(file, "utf-8");
253
+ const outPath = join(outDir, `${name}.${fmt === "pdf" ? "pdf" : "html"}`);
254
+ const spinner = ora(` ${name}.md`).start();
255
+
256
+ try {
257
+ const input: RenderInput = {
258
+ markdown: md,
259
+ themeName: theme.themeName,
260
+ customCSS: theme.customCSS,
261
+ title: name,
262
+ };
263
+
264
+ if (fmt === "pdf") {
265
+ const pdfBuffer = await renderer.exportPDF(input);
266
+ const { writeFileSync } = await import("node:fs");
267
+ writeFileSync(outPath, pdfBuffer);
268
+ } else {
269
+ const result = await renderer.render(input);
270
+ const { writeFileSync } = await import("node:fs");
271
+ writeFileSync(outPath, result.html, "utf-8");
272
+ }
273
+
274
+ spinner.succeed(`${name}.${fmt === "pdf" ? "pdf" : "html"}`);
275
+ success++;
276
+ } catch (err) {
277
+ spinner.fail(`${name}.md — ${(err as Error).message}`);
278
+ failed++;
279
+ }
280
+ }
281
+ } finally {
282
+ await renderer.close();
283
+ }
284
+
285
+ console.log(
286
+ chalk.bold(`\nDone: ${chalk.green(`${success} exported`)}`) +
287
+ (failed ? chalk.red(`, ${failed} failed`) : "") +
288
+ "\n"
289
+ );
290
+ });
291
+ }
292
+
293
+ /**
294
+ * Register all export-related commands onto the program.
295
+ */
296
+ export function registerExportCommands(program: Command): void {
297
+ registerSetTheme(program);
298
+ registerExportHTML(program);
299
+ registerExportPDF(program);
300
+ registerExport(program);
301
+ }
@@ -0,0 +1,8 @@
1
+ /** Abstract base class for format extractors. */
2
+
3
+ import type { ThemeStyle } from "../models.js";
4
+
5
+ export interface Extractor {
6
+ extract(source: string): Promise<ThemeStyle>;
7
+ canHandle(source: string): boolean;
8
+ }