@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.
- package/.claude/skills/colamd-themes-preview.skill.md +333 -0
- package/.claude/skills/colamd-themes.skill.md +155 -0
- package/.hermes/skills/tools/colamd-themes/SKILL.md +159 -0
- package/.openclaw/skills/colamd-themes/SKILL.md +166 -0
- package/.opencode/skills/colamd-themes/SKILL.md +162 -0
- package/.trae/rules/colamd-themes.md +150 -0
- package/CHANGELOG.md +102 -0
- package/README.md +373 -0
- package/README_CN.md +373 -0
- package/RELEASENOTE.md +177 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +208 -0
- package/dist/cli.js.map +1 -0
- package/dist/color-systems.d.ts +17 -0
- package/dist/color-systems.js +251 -0
- package/dist/color-systems.js.map +1 -0
- package/dist/color-utils.d.ts +9 -0
- package/dist/color-utils.js +112 -0
- package/dist/color-utils.js.map +1 -0
- package/dist/contrast.d.ts +15 -0
- package/dist/contrast.js +63 -0
- package/dist/contrast.js.map +1 -0
- package/dist/docx-style-paradigm.d.ts +153 -0
- package/dist/docx-style-paradigm.js +640 -0
- package/dist/docx-style-paradigm.js.map +1 -0
- package/dist/export-cli.d.ts +10 -0
- package/dist/export-cli.js +266 -0
- package/dist/export-cli.js.map +1 -0
- package/dist/extractors/base.d.ts +6 -0
- package/dist/extractors/base.js +3 -0
- package/dist/extractors/base.js.map +1 -0
- package/dist/extractors/docx-extractor.d.ts +52 -0
- package/dist/extractors/docx-extractor.js +370 -0
- package/dist/extractors/docx-extractor.js.map +1 -0
- package/dist/extractors/pdf-extractor.d.ts +66 -0
- package/dist/extractors/pdf-extractor.js +430 -0
- package/dist/extractors/pdf-extractor.js.map +1 -0
- package/dist/extractors/url-extractor.d.ts +104 -0
- package/dist/extractors/url-extractor.js +761 -0
- package/dist/extractors/url-extractor.js.map +1 -0
- package/dist/generator.d.ts +4 -0
- package/dist/generator.js +71 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/mermaid-presets.d.ts +14 -0
- package/dist/mermaid-presets.js +122 -0
- package/dist/mermaid-presets.js.map +1 -0
- package/dist/models.d.ts +106 -0
- package/dist/models.js +92 -0
- package/dist/models.js.map +1 -0
- package/dist/renderer.d.ts +70 -0
- package/dist/renderer.js +204 -0
- package/dist/renderer.js.map +1 -0
- package/dist/templates/template.css +498 -0
- package/dist/templates/theme-paradigm.md +658 -0
- package/dist/templates/theme-template.hbs +370 -0
- package/dist/theme-store.d.ts +37 -0
- package/dist/theme-store.js +90 -0
- package/dist/theme-store.js.map +1 -0
- package/dist/validators.d.ts +26 -0
- package/dist/validators.js +144 -0
- package/dist/validators.js.map +1 -0
- package/package.json +36 -0
- package/skills/build-skills.sh +166 -0
- package/skills/colamd-themes/SKILL.md +148 -0
- package/src/cli.ts +200 -0
- package/src/color-systems.ts +289 -0
- package/src/color-utils.ts +120 -0
- package/src/contrast.ts +66 -0
- package/src/docx-style-paradigm.ts +811 -0
- package/src/export-cli.ts +301 -0
- package/src/extractors/base.ts +8 -0
- package/src/extractors/docx-extractor.ts +417 -0
- package/src/extractors/pdf-extractor.ts +527 -0
- package/src/extractors/url-extractor.ts +927 -0
- package/src/generator.ts +88 -0
- package/src/index.ts +29 -0
- package/src/mermaid-presets.ts +142 -0
- package/src/models.ts +212 -0
- package/src/renderer.ts +277 -0
- package/src/templates/template.css +498 -0
- package/src/templates/theme-paradigm.md +658 -0
- package/src/templates/theme-template.hbs +370 -0
- package/src/theme-store.ts +112 -0
- package/src/types/pdf-parse.d.ts +12 -0
- package/src/validators.ts +176 -0
- package/themes/.gitkeep +0 -0
- package/themes/academic-paper-pt.css +1805 -0
- package/themes/academic-paper.css +1099 -0
- package/themes/academic-style.css +2430 -0
- package/themes/colly.css +370 -0
- package/themes/elegant.css +671 -0
- package/themes/forest-ink.css +664 -0
- package/themes/guizang.css +732 -0
- package/themes/meeting-minutes.css +370 -0
- package/themes/pixso-design.css +1261 -0
- package/themes/salvator-blog.css +370 -0
- package/themes/swiss-design copy.css +746 -0
- package/themes/swiss-design.css +746 -0
- package/themes/template.css +498 -0
- package/themes/trae-blue.css +737 -0
- package/themes-preview/colly-preview.html +623 -0
- package/themes-preview/demo-content.md +187 -0
- package/themes-preview/meeting-minutes-preview.html +625 -0
- package/themes-preview/salvator-blog-preview.html +623 -0
- 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
|
+
}
|