@bytechain.cn/colamd-themes 0.3.2 → 0.5.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 (100) hide show
  1. package/.claude/skills/colamd-themes-preview.skill.md +333 -0
  2. package/.claude/skills/colamd-themes.skill.md +10 -0
  3. package/.dsh/skills/colamd-themes/SKILL.md +14 -0
  4. package/.github/supported-topics.yml +17 -0
  5. package/.hermes/skills/tools/colamd-themes/SKILL.md +14 -0
  6. package/.openclaw/skills/colamd-themes/SKILL.md +1 -37
  7. package/.opencode/skills/colamd-themes/SKILL.md +17 -0
  8. package/.trae/rules/colamd-themes.md +0 -36
  9. package/CHANGELOG.md +318 -241
  10. package/DSH_SUPPORT_SUMMARY.md +87 -0
  11. package/README.md +414 -384
  12. package/README_CN.md +412 -384
  13. package/RELEASENOTE.md +541 -389
  14. package/bytechain.cn-colamd-themes-0.5.0.tgz +0 -0
  15. package/dist/cli.js +36 -55
  16. package/dist/cli.js.map +1 -1
  17. package/dist/color-utils.d.ts +1 -0
  18. package/dist/color-utils.js +25 -6
  19. package/dist/color-utils.js.map +1 -1
  20. package/dist/extractors/pdf-extractor.js +6 -1
  21. package/dist/extractors/pdf-extractor.js.map +1 -1
  22. package/dist/extractors/url-extractor.d.ts +0 -4
  23. package/dist/extractors/url-extractor.js +13 -126
  24. package/dist/extractors/url-extractor.js.map +1 -1
  25. package/dist/generator.d.ts +1 -1
  26. package/dist/generator.js +32 -11
  27. package/dist/generator.js.map +1 -1
  28. package/dist/models.d.ts +5 -4
  29. package/dist/models.js +2 -3
  30. package/dist/models.js.map +1 -1
  31. package/dist/renderer.d.ts +4 -2
  32. package/dist/renderer.js +64 -84
  33. package/dist/renderer.js.map +1 -1
  34. package/dist/templates/template.css +220 -498
  35. package/dist/templates/theme-paradigm.md +530 -658
  36. package/dist/templates/theme-template.hbs +256 -370
  37. package/dist/theme-store.js +19 -3
  38. package/dist/theme-store.js.map +1 -1
  39. package/dist/utils/document-paradigm.d.ts +12 -0
  40. package/dist/utils/document-paradigm.js +109 -0
  41. package/dist/utils/document-paradigm.js.map +1 -0
  42. package/dist/validators.d.ts +4 -1
  43. package/dist/validators.js +115 -1
  44. package/dist/validators.js.map +1 -1
  45. package/mcp-server/dist/index.d.ts +13 -0
  46. package/mcp-server/dist/index.d.ts.map +1 -0
  47. package/mcp-server/dist/index.js +0 -0
  48. package/mcp-server/dist/index.js.map +1 -0
  49. package/mcp-server/package-lock.json +1759 -0
  50. package/mcp-server/package.json +24 -0
  51. package/mcp-server/src/index.ts +0 -0
  52. package/mcp-server/tsconfig.json +19 -0
  53. package/package.json +54 -36
  54. package/skills/build-skills.sh +30 -5
  55. package/skills/colamd-themes/SKILL.md +180 -178
  56. package/src/cli.ts +474 -487
  57. package/src/color-systems.ts +289 -289
  58. package/src/color-utils.ts +137 -120
  59. package/src/contrast.ts +66 -66
  60. package/src/docx-style-paradigm.ts +811 -811
  61. package/src/export-cli.ts +356 -356
  62. package/src/extractors/base.ts +8 -8
  63. package/src/extractors/docx-extractor.ts +417 -417
  64. package/src/extractors/pdf-extractor.ts +533 -527
  65. package/src/extractors/url-extractor.ts +846 -981
  66. package/src/generator.ts +197 -169
  67. package/src/index.ts +29 -29
  68. package/src/models.ts +212 -212
  69. package/src/renderer.ts +332 -359
  70. package/src/templates/template.css +220 -498
  71. package/src/templates/theme-paradigm.md +530 -658
  72. package/src/templates/theme-template.hbs +256 -370
  73. package/src/theme-store.ts +185 -170
  74. package/src/types/pdf-parse.d.ts +12 -12
  75. package/src/utils/cli-utils.ts +84 -84
  76. package/src/utils/document-paradigm.ts +118 -0
  77. package/src/validators.ts +298 -176
  78. package/themes/academic-paper-pt.css +353 -1805
  79. package/themes/academic-paper.css +350 -1099
  80. package/themes/academic-style.css +342 -2430
  81. package/themes/colly.css +256 -370
  82. package/themes/elegant.css +288 -671
  83. package/themes/forest-ink.css +240 -664
  84. package/themes/guizang.css +250 -732
  85. package/themes/hermes-security.css +279 -0
  86. package/themes/meeting-minutes.css +256 -370
  87. package/themes/pixso-design.css +278 -1261
  88. package/themes/salvator-blog.css +256 -370
  89. package/themes/shimo-theme.css +256 -0
  90. package/themes/swiss-design.css +285 -746
  91. package/themes/template.css +220 -498
  92. package/themes/trae-blue.css +280 -737
  93. package/themes/word-contract.css +249 -0
  94. package/themes-preview/demo-content.md +187 -187
  95. package/tsconfig.json +18 -18
  96. package/src/mermaid-presets.ts +0 -142
  97. package/themes/swiss-design copy.css +0 -746
  98. package/themes-preview/colly-preview.html +0 -623
  99. package/themes-preview/meeting-minutes-preview.html +0 -625
  100. package/themes-preview/salvator-blog-preview.html +0 -623
package/src/generator.ts CHANGED
@@ -1,169 +1,197 @@
1
- /** CSS theme file generator — v3.0 seed palette pipeline. */
2
-
3
- import Handlebars from "handlebars";
4
- import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync } from "node:fs";
5
- import { join, dirname, isAbsolute } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
- import type { ThemeStyle } from "./models.js";
8
- import { paletteToSeed } from "./models.js";
9
- import { toRgba, normalizeThemeStyle } from "./color-utils.js";
10
- import { matchColorSystem } from "./color-systems.js";
11
- import { selectMermaidPreset } from "./mermaid-presets.js";
12
- import { validateSeedPalette, summarizeIssues } from "./validators.js";
13
-
14
- const __dirname = dirname(fileURLToPath(import.meta.url));
15
- const templatePath = join(__dirname, "templates", "theme-template.hbs");
16
-
17
- // ── Handlebars helpers ──
18
-
19
- Handlebars.registerHelper("rgba", (hex: unknown, alpha: unknown) => {
20
- return toRgba(String(hex), Number(alpha));
21
- });
22
-
23
- // ── Template Cache with Size Limits and Auto-Invalidation ──
24
-
25
- interface CachedTemplate {
26
- compiled: HandlebarsTemplateDelegate<ThemeStyle>;
27
- mtimeMs: number;
28
- compiledAt: number;
29
- }
30
-
31
- const TEMPLATE_CACHE_MAX_SIZE = 10;
32
- const TEMPLATE_CACHE_TTL_MS = 30 * 60 * 1000; // 30 minutes
33
- const templateCache = new Map<string, CachedTemplate>();
34
-
35
- /**
36
- * Get or compile a Handlebars template with caching.
37
- *
38
- * Features:
39
- * - LRU-style cache (max 10 templates)
40
- * - File modification time detection for auto-invalidation
41
- * - TTL-based expiration (30 minutes)
42
- * - Automatic cleanup of expired entries on access
43
- */
44
- function getTemplate(path?: string): HandlebarsTemplateDelegate<ThemeStyle> {
45
- const key = path || templatePath;
46
-
47
- // Check cache hit
48
- const cached = templateCache.get(key);
49
- if (cached) {
50
- // Check TTL expiration
51
- if (Date.now() - cached.compiledAt > TEMPLATE_CACHE_TTL_MS) {
52
- templateCache.delete(key);
53
- }
54
- // Check file modification time
55
- else {
56
- try {
57
- const stats = statSync(key);
58
- if (stats.mtimeMs > cached.mtimeMs) {
59
- templateCache.delete(key);
60
- } else {
61
- return cached.compiled;
62
- }
63
- } catch {
64
- return cached.compiled;
65
- }
66
- }
67
- }
68
-
69
- // Enforce max cache size (evict oldest entry)
70
- if (templateCache.size >= TEMPLATE_CACHE_MAX_SIZE) {
71
- let oldestKey: string | null = null;
72
- let oldestTime = Infinity;
73
-
74
- for (const [k, v] of templateCache.entries()) {
75
- if (v.compiledAt < oldestTime) {
76
- oldestTime = v.compiledAt;
77
- oldestKey = k;
78
- }
79
- }
80
-
81
- if (oldestKey) {
82
- templateCache.delete(oldestKey);
83
- }
84
- }
85
-
86
- // Compile new template
87
- const source = readFileSync(key, "utf-8");
88
- const compiled = Handlebars.compile(source, { noEscape: true });
89
-
90
- // Get modification time for invalidation
91
- let mtimeMs = 0;
92
- try {
93
- mtimeMs = statSync(key).mtimeMs;
94
- } catch {
95
- mtimeMs = Date.now();
96
- }
97
-
98
- templateCache.set(key, {
99
- compiled,
100
- mtimeMs,
101
- compiledAt: Date.now(),
102
- });
103
-
104
- return compiled;
105
- }
106
-
107
- /**
108
- * Clear all cached templates to free memory.
109
- */
110
- export function clearTemplateCache(): void {
111
- templateCache.clear();
112
- }
113
-
114
- // ── Pipeline ──
115
-
116
- export function generateCss(style: ThemeStyle): string {
117
- // 1. Map legacy palette → seed palette if seed is still default
118
- if (style.seed.accent === "#6366f1") {
119
- style.seed = paletteToSeed(style.palette);
120
- }
121
-
122
- // 2. Normalize from legacy extractor path
123
- normalizeThemeStyle(style);
124
-
125
- // 3. Match color system
126
- if (!style.colorSystemId) {
127
- const cs = matchColorSystem(style.seed.accent, style.seed.surface);
128
- style.colorSystemId = cs.id;
129
- }
130
-
131
- // 4. Select Mermaid preset
132
- if (!style.mermaidPresetId) {
133
- const mp = selectMermaidPreset(style.seed.surface, style.seed.accent);
134
- style.mermaidPresetId = mp.id;
135
- }
136
-
137
- // 5. Validate against paradigm
138
- const issues = validateSeedPalette(style.seed);
139
- if (issues.filter((i) => i.level === "MUST" || i.level === "MUST_NOT").length > 0) {
140
- console.warn("Paradigm validation issues:", summarizeIssues(issues));
141
- }
142
-
143
- // 6. Render
144
- const template = getTemplate();
145
- return template(style);
146
- }
147
-
148
- export function writeThemeFile(
149
- style: ThemeStyle,
150
- target: string = "themes"
151
- ): string {
152
- const css = generateCss(style);
153
- const safeName = style.name.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-");
154
-
155
- let filePath: string;
156
- if (target.endsWith(".css")) {
157
- filePath = isAbsolute(target) ? target : join(process.cwd(), target);
158
- } else {
159
- const dirPath = isAbsolute(target) ? target : join(process.cwd(), target);
160
- if (!existsSync(dirPath)) mkdirSync(dirPath, { recursive: true });
161
- filePath = join(dirPath, `${safeName}.css`);
162
- }
163
-
164
- const parent = dirname(filePath);
165
- if (!existsSync(parent)) mkdirSync(parent, { recursive: true });
166
-
167
- writeFileSync(filePath, css, "utf-8");
168
- return filePath;
169
- }
1
+ /** CSS theme file generator — v4.0 seed palette pipeline (ColaMD 2.7.7 contract). */
2
+
3
+ import Handlebars from "handlebars";
4
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync } from "node:fs";
5
+ import { join, dirname, isAbsolute } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import type { ThemeStyle } from "./models.js";
8
+ import { paletteToSeed } from "./models.js";
9
+ import { toRgba, normalizeThemeStyle } from "./color-utils.js";
10
+ import { matchColorSystem } from "./color-systems.js";
11
+ import { contrastRatio } from "./contrast.js";
12
+ import { validateSeedPalette, validateThemeCss, summarizeIssues } from "./validators.js";
13
+
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ const templatePath = join(__dirname, "templates", "theme-template.hbs");
16
+
17
+ // ── Handlebars helpers ──
18
+
19
+ Handlebars.registerHelper("rgba", (hex: unknown, alpha: unknown) => {
20
+ return toRgba(String(hex), Number(alpha));
21
+ });
22
+
23
+ // ── Template Cache with Size Limits and Auto-Invalidation ──
24
+
25
+ /** 模板上下文:ThemeStyle + 生成阶段算出的派生值。 */
26
+ type TemplateContext = ThemeStyle & { codeBlockText: string };
27
+
28
+ interface CachedTemplate {
29
+ compiled: HandlebarsTemplateDelegate<TemplateContext>;
30
+ mtimeMs: number;
31
+ compiledAt: number;
32
+ }
33
+
34
+ const TEMPLATE_CACHE_MAX_SIZE = 10;
35
+ const TEMPLATE_CACHE_TTL_MS = 30 * 60 * 1000; // 30 minutes
36
+ const templateCache = new Map<string, CachedTemplate>();
37
+
38
+ /**
39
+ * Get or compile a Handlebars template with caching.
40
+ *
41
+ * Features:
42
+ * - LRU-style cache (max 10 templates)
43
+ * - File modification time detection for auto-invalidation
44
+ * - TTL-based expiration (30 minutes)
45
+ * - Automatic cleanup of expired entries on access
46
+ */
47
+ function getTemplate(path?: string): HandlebarsTemplateDelegate<TemplateContext> {
48
+ const key = path || templatePath;
49
+
50
+ // Check cache hit
51
+ const cached = templateCache.get(key);
52
+ if (cached) {
53
+ // Check TTL expiration
54
+ if (Date.now() - cached.compiledAt > TEMPLATE_CACHE_TTL_MS) {
55
+ templateCache.delete(key);
56
+ }
57
+ // Check file modification time
58
+ else {
59
+ try {
60
+ const stats = statSync(key);
61
+ if (stats.mtimeMs > cached.mtimeMs) {
62
+ templateCache.delete(key);
63
+ } else {
64
+ return cached.compiled;
65
+ }
66
+ } catch {
67
+ return cached.compiled;
68
+ }
69
+ }
70
+ }
71
+
72
+ // Enforce max cache size (evict oldest entry)
73
+ if (templateCache.size >= TEMPLATE_CACHE_MAX_SIZE) {
74
+ let oldestKey: string | null = null;
75
+ let oldestTime = Infinity;
76
+
77
+ for (const [k, v] of templateCache.entries()) {
78
+ if (v.compiledAt < oldestTime) {
79
+ oldestTime = v.compiledAt;
80
+ oldestKey = k;
81
+ }
82
+ }
83
+
84
+ if (oldestKey) {
85
+ templateCache.delete(oldestKey);
86
+ }
87
+ }
88
+
89
+ // Compile new template
90
+ const source = readFileSync(key, "utf-8");
91
+ const compiled = Handlebars.compile(source, { noEscape: true });
92
+
93
+ // Get modification time for invalidation
94
+ let mtimeMs = 0;
95
+ try {
96
+ mtimeMs = statSync(key).mtimeMs;
97
+ } catch {
98
+ mtimeMs = Date.now();
99
+ }
100
+
101
+ templateCache.set(key, {
102
+ compiled,
103
+ mtimeMs,
104
+ compiledAt: Date.now(),
105
+ });
106
+
107
+ return compiled;
108
+ }
109
+
110
+ /**
111
+ * Clear all cached templates to free memory.
112
+ */
113
+ export function clearTemplateCache(): void {
114
+ templateCache.clear();
115
+ }
116
+
117
+ /**
118
+ * 代码块文字色:从代码块底色出发,在白、浅灰、种子正文色里挑对比度最高的一档。
119
+ * 深色代码块(如 elegant)因此拿到浅色文字,浅色代码块拿到正文色。
120
+ */
121
+ function pickCodeBlockText(bg: string, seed: ThemeStyle["seed"]): string {
122
+ const candidates = Array.from(new Set(["#ffffff", "#f5f5f5", seed.surface, seed.ink]));
123
+ let best = candidates[0];
124
+ let bestRatio = 0;
125
+ for (const candidate of candidates) {
126
+ const ratio = contrastRatio(candidate, bg);
127
+ if (ratio > bestRatio) {
128
+ bestRatio = ratio;
129
+ best = candidate;
130
+ }
131
+ }
132
+ return best;
133
+ }
134
+
135
+ // ── Pipeline ──
136
+
137
+ export function generateCss(style: ThemeStyle): string {
138
+ // 1. Map legacy palette → seed palette if seed is still default
139
+ if (style.seed.accent === "#6366f1") {
140
+ style.seed = paletteToSeed(style.palette);
141
+ }
142
+
143
+ // 2. Normalize from legacy extractor path
144
+ normalizeThemeStyle(style);
145
+
146
+ // 3. Match color system
147
+ if (!style.colorSystemId) {
148
+ const cs = matchColorSystem(style.seed.accent, style.seed.surface);
149
+ style.colorSystemId = cs.id;
150
+ }
151
+
152
+ // 4. Validate seed palette against paradigm
153
+ const issues = validateSeedPalette(style.seed);
154
+ if (issues.filter((i) => i.level === "MUST" || i.level === "MUST_NOT").length > 0) {
155
+ console.warn("Paradigm validation issues:", summarizeIssues(issues));
156
+ }
157
+
158
+ // 5. Render
159
+ const template = getTemplate();
160
+ const css = template({
161
+ ...style,
162
+ codeBlockText: pickCodeBlockText(style.seed.panelAlt, style.seed),
163
+ });
164
+
165
+ // 6. Self-check the rendered CSS against the 2.7.7 contract
166
+ const cssIssues = validateThemeCss(css).filter(
167
+ (i) => i.level === "MUST" || i.level === "MUST_NOT"
168
+ );
169
+ if (cssIssues.length > 0) {
170
+ console.warn("CSS contract issues:", summarizeIssues(cssIssues));
171
+ }
172
+
173
+ return css;
174
+ }
175
+
176
+ export function writeThemeFile(
177
+ style: ThemeStyle,
178
+ target: string = "themes"
179
+ ): string {
180
+ const css = generateCss(style);
181
+ const safeName = style.name.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-");
182
+
183
+ let filePath: string;
184
+ if (target.endsWith(".css")) {
185
+ filePath = isAbsolute(target) ? target : join(process.cwd(), target);
186
+ } else {
187
+ const dirPath = isAbsolute(target) ? target : join(process.cwd(), target);
188
+ if (!existsSync(dirPath)) mkdirSync(dirPath, { recursive: true });
189
+ filePath = join(dirPath, `${safeName}.css`);
190
+ }
191
+
192
+ const parent = dirname(filePath);
193
+ if (!existsSync(parent)) mkdirSync(parent, { recursive: true });
194
+
195
+ writeFileSync(filePath, css, "utf-8");
196
+ return filePath;
197
+ }
package/src/index.ts CHANGED
@@ -1,29 +1,29 @@
1
- /** ColaMD Theme Development Tool. */
2
-
3
- export * from "./models.js";
4
- export * from "./color-utils.js";
5
- export * from "./generator.js";
6
- export { UrlExtractor } from "./extractors/url-extractor.js";
7
- export { DocxExtractor } from "./extractors/docx-extractor.js";
8
- export { PdfExtractor } from "./extractors/pdf-extractor.js";
9
- export type { Extractor } from "./extractors/base.js";
10
-
11
- export {
12
- ACADEMIC_PAPER_DEFAULTS,
13
- BUSINESS_REPORT_DEFAULTS,
14
- TECHNICAL_DOC_DEFAULTS,
15
- generatePrintFriendlyGrayscale,
16
- generateMorandiPrintPalette,
17
- validateDocumentStyle,
18
- normalizeForPrinting,
19
- getRecommendedPreset,
20
- DOCUMENT_STYLE_PRESETS,
21
- } from "./docx-style-paradigm.js";
22
-
23
- export type {
24
- DocumentStyleParadigm,
25
- FontStyleDefinition,
26
- TableCellStyle,
27
- PrintOptimization,
28
- StyleValidationIssue,
29
- } from "./docx-style-paradigm.js";
1
+ /** ColaMD Theme Development Tool. */
2
+
3
+ export * from "./models.js";
4
+ export * from "./color-utils.js";
5
+ export * from "./generator.js";
6
+ export { UrlExtractor } from "./extractors/url-extractor.js";
7
+ export { DocxExtractor } from "./extractors/docx-extractor.js";
8
+ export { PdfExtractor } from "./extractors/pdf-extractor.js";
9
+ export type { Extractor } from "./extractors/base.js";
10
+
11
+ export {
12
+ ACADEMIC_PAPER_DEFAULTS,
13
+ BUSINESS_REPORT_DEFAULTS,
14
+ TECHNICAL_DOC_DEFAULTS,
15
+ generatePrintFriendlyGrayscale,
16
+ generateMorandiPrintPalette,
17
+ validateDocumentStyle,
18
+ normalizeForPrinting,
19
+ getRecommendedPreset,
20
+ DOCUMENT_STYLE_PRESETS,
21
+ } from "./docx-style-paradigm.js";
22
+
23
+ export type {
24
+ DocumentStyleParadigm,
25
+ FontStyleDefinition,
26
+ TableCellStyle,
27
+ PrintOptimization,
28
+ StyleValidationIssue,
29
+ } from "./docx-style-paradigm.js";