@bytechain.cn/colamd-themes 0.5.1 → 0.5.2

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 (61) hide show
  1. package/README.md +25 -11
  2. package/cordis.patch.yml +9 -0
  3. package/dist/cli.js +3 -3
  4. package/dist/dsh-plugin.d.ts +44 -0
  5. package/dist/dsh-plugin.js +72 -0
  6. package/dist/dsh-plugin.js.map +1 -0
  7. package/dist/generator.d.ts +1 -1
  8. package/dist/generator.js +2 -2
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +2 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/models.d.ts +1 -1
  13. package/dist/models.js +1 -1
  14. package/dist/renderer.d.ts +2 -2
  15. package/dist/renderer.js +3 -3
  16. package/package.json +22 -2
  17. package/skills/build-skills.sh +5 -5
  18. package/skills/colamd-themes/SKILL.md +5 -5
  19. package/.claude/skills/colamd-themes-preview.skill.md +0 -333
  20. package/.claude/skills/colamd-themes.skill.md +0 -10
  21. package/.dsh/skills/colamd-themes/SKILL.md +0 -14
  22. package/.github/supported-topics.yml +0 -17
  23. package/.hermes/skills/tools/colamd-themes/SKILL.md +0 -14
  24. package/.openclaw/skills/colamd-themes/SKILL.md +0 -21
  25. package/.opencode/skills/colamd-themes/SKILL.md +0 -17
  26. package/.trae/rules/colamd-themes.md +0 -5
  27. package/CHANGELOG.md +0 -331
  28. package/DSH_SUPPORT_SUMMARY.md +0 -87
  29. package/README_CN.md +0 -412
  30. package/RELEASENOTE.md +0 -541
  31. package/mcp-server/dist/index.d.ts +0 -13
  32. package/mcp-server/dist/index.d.ts.map +0 -1
  33. package/mcp-server/dist/index.js.map +0 -1
  34. package/mcp-server/package-lock.json +0 -1759
  35. package/mcp-server/package.json +0 -24
  36. package/mcp-server/src/index.ts +0 -0
  37. package/mcp-server/tsconfig.json +0 -19
  38. package/src/cli.ts +0 -474
  39. package/src/color-systems.ts +0 -289
  40. package/src/color-utils.ts +0 -137
  41. package/src/contrast.ts +0 -66
  42. package/src/docx-style-paradigm.ts +0 -811
  43. package/src/export-cli.ts +0 -356
  44. package/src/extractors/base.ts +0 -8
  45. package/src/extractors/docx-extractor.ts +0 -417
  46. package/src/extractors/pdf-extractor.ts +0 -533
  47. package/src/extractors/url-extractor.ts +0 -846
  48. package/src/generator.ts +0 -197
  49. package/src/index.ts +0 -29
  50. package/src/models.ts +0 -212
  51. package/src/renderer.ts +0 -310
  52. package/src/templates/template.css +0 -220
  53. package/src/templates/theme-paradigm.md +0 -530
  54. package/src/templates/theme-template.hbs +0 -256
  55. package/src/theme-store.ts +0 -185
  56. package/src/types/pdf-parse.d.ts +0 -12
  57. package/src/utils/cli-utils.ts +0 -84
  58. package/src/utils/document-paradigm.ts +0 -118
  59. package/src/validators.ts +0 -298
  60. package/themes-preview/demo-content.md +0 -187
  61. package/tsconfig.json +0 -18
@@ -1,417 +0,0 @@
1
- /**
2
- * DOCX 文档样式提取器 — v2.0
3
- *
4
- * 增强功能:
5
- * 1. 提取各级标题(H1-H6)的完整字体样式定义
6
- * 2. 提取正文字体、字号、颜色、行间距
7
- * 3. 提取表格样式(表头/单元格字体、背景色、边框)
8
- * 4. 对无法提取的样式应用 DocxStyleParadigm 标准参考值
9
- * 5. 自动验证 WCAG 对比度合规性
10
- */
11
-
12
- import { readFileSync } from "node:fs";
13
- import { basename } from "node:path";
14
- import mammoth from "mammoth";
15
- import type { Extractor } from "./base.js";
16
- import {
17
- makeThemeStyle,
18
- type ThemeStyle,
19
- type ColorPalette,
20
- type Typography,
21
- } from "../models.js";
22
- import { normalizeThemeStyle, hexToRgb, rgbToHex, lighten, darken } from "../color-utils.js";
23
- import {
24
- ACADEMIC_PAPER_DEFAULTS,
25
- getRecommendedPreset,
26
- validateDocumentStyle,
27
- normalizeForPrinting,
28
- type DocumentStyleParadigm,
29
- type FontStyleDefinition,
30
- type TableCellStyle,
31
- } from "../docx-style-paradigm.js";
32
-
33
- export class DocxExtractor implements Extractor {
34
- canHandle(source: string): boolean {
35
- return /\.docx$/i.test(source);
36
- }
37
-
38
- async extract(source: string): Promise<ThemeStyle> {
39
- const buffer = readFileSync(source);
40
- const name = basename(source).replace(/\.docx$/i, "");
41
- const theme = makeThemeStyle(name, "docx", source);
42
-
43
- const result = await mammoth.convertToHtml(
44
- { buffer },
45
- {
46
- styleMap: [
47
- "p[style-name='Heading 1'] => h1:fresh",
48
- "p[style-name='Heading 2'] => h2:fresh",
49
- "p[style-name='Heading 3'] => h3:fresh",
50
- "p[style-name='Heading 4'] => h4:fresh",
51
- "p[style-name='Heading 5'] => h5:fresh",
52
- "p[style-name='Heading 6'] => h6:fresh",
53
- "r[style-name='Strong'] => strong",
54
- ],
55
- }
56
- );
57
-
58
- if (result.value) {
59
- this.extractDetailedStyles(theme, result.value);
60
- }
61
-
62
- this.applyParadigmDefaults(theme);
63
- normalizeThemeStyle(theme);
64
- return theme;
65
- }
66
-
67
- /**
68
- * 从 HTML 输出中提取详细的文档元素样式
69
- */
70
- private extractDetailedStyles(theme: ThemeStyle, html: string): void {
71
- const p = theme.palette;
72
- const t = theme.typography;
73
-
74
- this.extractBodyStyles(t, html);
75
- this.extractHeadingStyles(theme, html);
76
- this.extractTableStyles(p, html);
77
- this.extractColorPalette(p, html);
78
- this.extractBlockStyles(p, html);
79
- }
80
-
81
- /**
82
- * 提取正文基础样式(字体、字号、行高)
83
- */
84
- private extractBodyStyles(typography: Typography, html: string): void {
85
- const fontMatch = html.match(/font-family:\s*['"]?([^;'"]+)['"]?/i);
86
- if (fontMatch) {
87
- typography.bodyFontFamily = fontMatch[1].trim();
88
- }
89
-
90
- const fontSizeMatch = html.match(/font-size:\s*([\d.]+)(pt|px)/i);
91
- if (fontSizeMatch) {
92
- const size = parseFloat(fontSizeMatch[1]);
93
- const unit = fontSizeMatch[2];
94
- typography.baseFontSize = unit === "pt" ? `${size * 1.333}px` : `${size}px`;
95
- }
96
- }
97
-
98
- /**
99
- * 提取各级标题的详细样式
100
- */
101
- private extractHeadingStyles(theme: ThemeStyle, html: string): void {
102
- const headingLevels = ["h1", "h2", "h3", "h4", "h5", "h6"];
103
-
104
- for (const level of headingLevels) {
105
- const regex = new RegExp(
106
- `<${level}[^>]*style="[^"]*"`,
107
- "gi"
108
- );
109
- let match = regex.exec(html);
110
-
111
- if (match) {
112
- const styleStr = match[0];
113
- const extracted = this.parseInlineStyle(styleStr);
114
-
115
- if (extracted.fontSize) {
116
- const size = parseFloat(extracted.fontSize);
117
- const unit = extracted.fontSize.replace(/[\d.]/g, "") || "px";
118
- const pxSize = unit === "pt" ? size * 1.333 : size;
119
-
120
- switch (level) {
121
- case "h1": theme.seedTypography.fontScaleH1 = pxSize / 16; break;
122
- case "h2": theme.seedTypography.fontScaleH2 = pxSize / 16; break;
123
- case "h3": theme.seedTypography.fontScaleH3 = pxSize / 16; break;
124
- case "h4": theme.seedTypography.fontScaleH4 = pxSize / 16; break;
125
- case "h5": theme.seedTypography.fontScaleH5 = pxSize / 16; break;
126
- case "h6": theme.seedTypography.fontScaleH6 = pxSize / 16; break;
127
- }
128
- }
129
-
130
- if (extracted.fontWeight) {
131
- const weight = parseInt(extracted.fontWeight) || 700;
132
- if (weight > theme.typography.headingWeight) {
133
- theme.typography.headingWeight = weight;
134
- }
135
- }
136
-
137
- if (extracted.color && level === "h1") {
138
- theme.palette.headingColor = extracted.color;
139
- }
140
- }
141
- }
142
- }
143
-
144
- /**
145
- * 提取表格详细样式(表头、单元格、边框)
146
- */
147
- private extractTableStyles(palette: ColorPalette, html: string): void {
148
- const thRegex = /<th[^>]*style="([^"]*)"/gi;
149
- let thMatch = thRegex.exec(html);
150
-
151
- if (thMatch) {
152
- const thStyle = this.parseInlineStyle(thMatch[0]);
153
- if (thStyle.backgroundColor) {
154
- palette.tableHeaderBg = thStyle.backgroundColor;
155
- }
156
- if (thStyle.color) {
157
- palette.headingColor = palette.headingColor || thStyle.color;
158
- }
159
- if (thStyle.fontSize) {
160
- const size = parseFloat(thStyle.fontSize);
161
- palette.tableHeaderBg = palette.tableHeaderBg || "#f0f0f0";
162
- }
163
- }
164
-
165
- const tdRegex = /<td[^>]*style="([^"]*)"/gi;
166
- let tdMatch = tdRegex.exec(html);
167
- if (tdMatch) {
168
- const tdStyle = this.parseInlineStyle(tdMatch[0]);
169
- if (tdStyle.backgroundColor) {
170
- palette.codeBg = tdStyle.backgroundColor;
171
- }
172
- }
173
-
174
- const tableBorderMatch = html.match(/<table[^>]*style="[^"]*border[^;]*:[^;]*([^;"'\s]+)/i);
175
- if (tableBorderMatch) {
176
- palette.borderColor = palette.borderColor || "#cccccc";
177
- }
178
-
179
- if (!palette.tableHeaderBg) {
180
- palette.tableHeaderBg = "#f0f0f0";
181
- }
182
- }
183
-
184
- /**
185
- * 提取全局色彩方案
186
- */
187
- private extractColorPalette(palette: ColorPalette, html: string): void {
188
- const colorMatch = html.match(/color:\s*(#[0-9a-fA-F]{3,6})/i);
189
- if (colorMatch) {
190
- palette.textColor = colorMatch[1];
191
- }
192
-
193
- const bgMatch = html.match(/background(?:-color)?:\s*(#[0-9a-fA-F]{3,6})/i);
194
- if (bgMatch && bgMatch[1].toLowerCase() !== "#ffffff") {
195
- palette.bgColor = bgMatch[1];
196
- }
197
-
198
- const strongMatch = html.match(/<strong[^>]*style="[^"]*color:\s*(#[0-9a-fA-F]{3,6})/i);
199
- if (strongMatch) {
200
- palette.accentColor = strongMatch[1];
201
- }
202
-
203
- const linkMatch = html.match(/<a[^>]*style="[^"]*color:\s*(#[0-9a-fA-F]{3,6})/i);
204
- if (linkMatch) {
205
- palette.accentColor = linkMatch[1];
206
- }
207
- }
208
-
209
- /**
210
- * 提取引用块和代码块样式
211
- */
212
- private extractBlockStyles(palette: ColorPalette, html: string): void {
213
- const preMatch = html.match(/<pre[^>]*style="[^"]*background(?:-color)?:\s*(#[0-9a-fA-F]{3,6})/i);
214
- if (preMatch) {
215
- palette.codeBlockBg = preMatch[1];
216
- }
217
-
218
- const preColorMatch = html.match(/<pre[^>]*style="[^"]*color:\s*(#[0-9a-fA-F]{3,6})/i);
219
- if (preColorMatch) {
220
- palette.codeBlockText = preColorMatch[1];
221
- }
222
-
223
- if (!palette.blockquoteBorder) {
224
- palette.blockquoteBorder = palette.accentColor;
225
- }
226
- }
227
-
228
- /**
229
- * 解析内联 style 字符串为键值对对象
230
- */
231
- private parseInlineStyle(styleTag: string): Record<string, string> {
232
- const styleMatch = styleTag.match(/style="([^"]*)"/i);
233
- if (!styleMatch) return {};
234
-
235
- const styles: Record<string, string> = {};
236
- const declarations = styleMatch[1].split(";");
237
-
238
- for (const decl of declarations) {
239
- const [prop, ...valueParts] = decl.split(":").map((s) => s.trim());
240
- if (prop && valueParts.length > 0) {
241
- const camelProp = prop.replace(/-([a-z])/g, (_m, c) => c.toUpperCase());
242
- styles[camelProp] = valueParts.join(":").trim();
243
- }
244
- }
245
-
246
- return styles;
247
- }
248
-
249
- /**
250
- * 应用 DocxStyleParadigm 标准参考值填充缺失字段
251
- */
252
- private applyParadigmDefaults(theme: ThemeStyle): void {
253
- const preset = getRecommendedPreset();
254
- const p = theme.palette;
255
- const t = theme.typography;
256
- const st = theme.seedTypography;
257
-
258
- if (!p.textColor || p.textColor === "#000000") {
259
- p.textColor = preset.body.color;
260
- }
261
- if (!p.headingColor) {
262
- p.headingColor = preset.headings.h1.color;
263
- }
264
- if (!p.bgColor || p.bgColor === "#ffffff") {
265
- p.bgColor = preset.pageBackground;
266
- }
267
- if (!p.textMuted) {
268
- p.textMuted = preset.mutedTextColor;
269
- }
270
- if (!p.accentColor || p.accentColor === "#0000ff") {
271
- p.accentColor = preset.accentColor;
272
- }
273
- if (!p.tableHeaderBg) {
274
- p.tableHeaderBg = preset.table.headerBgColor;
275
- }
276
- if (!p.codeBg) {
277
- p.codeBg = preset.inlineCode.bgColor;
278
- }
279
- if (!p.codeBlockBg) {
280
- p.codeBlockBg = preset.codeBlock.bgColor;
281
- }
282
- if (!p.blockquoteBg) {
283
- p.blockquoteBg = preset.blockquote.bgColor;
284
- }
285
- if (!p.selectionBg) {
286
- p.selectionBg = preset.selectionBg;
287
- }
288
-
289
- if (!t.bodyFontFamily || t.bodyFontFamily === "Georgia, serif") {
290
- t.bodyFontFamily = preset.body.fontFamily;
291
- }
292
- if (!t.headingFontFamily) {
293
- t.headingFontFamily = preset.headings.h1.fontFamily;
294
- }
295
- if (!t.baseFontSize) {
296
- t.baseFontSize = `${preset.body.fontSize}px`;
297
- }
298
- if (t.baseLineHeight < 1.5) {
299
- t.baseLineHeight = preset.body.lineHeight;
300
- }
301
-
302
- st.fontBody = preset.body.fontFamily;
303
- st.fontHeading = preset.headings.h1.fontFamily;
304
- st.lineHeightBody = preset.body.lineHeight;
305
- st.lineHeightHeading = preset.headings.h1.lineHeight;
306
-
307
- const issues = validateDocumentStyle(this.themeToParadigm(theme));
308
- for (const issue of issues) {
309
- if (issue.level === "error") {
310
- console.warn(`[DocxExtractor] ${issue.element}: ${issue.message}`);
311
- }
312
- }
313
- }
314
-
315
- /**
316
- * 将 ThemeStyle 转换为 DocumentStyleParadigm 用于验证
317
- */
318
- private themeToParadigm(theme: ThemeStyle): DocumentStyleParadigm {
319
- return {
320
- pageBackground: theme.palette.bgColor,
321
- headings: {
322
- h1: {
323
- fontFamily: theme.seedTypography.fontHeading,
324
- fontSize: 16 * theme.seedTypography.fontScaleH1,
325
- fontWeight: theme.typography.headingWeight,
326
- color: theme.palette.headingColor,
327
- lineHeight: theme.seedTypography.lineHeightHeading,
328
- },
329
- h2: {
330
- fontFamily: theme.seedTypography.fontHeading,
331
- fontSize: 16 * theme.seedTypography.fontScaleH2,
332
- fontWeight: theme.typography.headingWeight - 100 || 600,
333
- color: theme.palette.headingColor,
334
- lineHeight: theme.seedTypography.lineHeightHeading,
335
- },
336
- h3: {
337
- fontFamily: theme.seedTypography.fontHeading,
338
- fontSize: 16 * theme.seedTypography.fontScaleH3,
339
- fontWeight: 600,
340
- color: theme.palette.headingColor,
341
- lineHeight: theme.seedTypography.lineHeightHeading + 0.05,
342
- },
343
- h4: {
344
- fontFamily: theme.seedTypography.fontHeading,
345
- fontSize: 16 * theme.seedTypography.fontScaleH4,
346
- fontWeight: 600,
347
- color: theme.palette.textMuted,
348
- lineHeight: theme.seedTypography.lineHeightHeading + 0.05,
349
- },
350
- h5: {
351
- fontFamily: theme.seedTypography.fontHeading,
352
- fontSize: 16 * theme.seedTypography.fontScaleH5,
353
- fontWeight: 500,
354
- color: theme.palette.textMuted,
355
- lineHeight: theme.seedTypography.lineHeightBody - 0.25,
356
- },
357
- h6: {
358
- fontFamily: theme.seedTypography.fontHeading,
359
- fontSize: 16 * theme.seedTypography.fontScaleH6,
360
- fontWeight: 500,
361
- color: theme.palette.textMuted,
362
- lineHeight: theme.seedTypography.lineHeightBody - 0.2,
363
- },
364
- },
365
- body: {
366
- fontFamily: theme.seedTypography.fontBody,
367
- fontSize: parseInt(theme.typography.baseFontSize) || 16,
368
- fontWeight: 400,
369
- color: theme.palette.textColor,
370
- lineHeight: theme.typography.baseLineHeight,
371
- },
372
- blockquote: {
373
- fontFamily: theme.seedTypography.fontBody,
374
- fontSize: Math.max(13, (parseInt(theme.typography.baseFontSize) || 16) - 2),
375
- fontWeight: 400,
376
- color: theme.palette.textMuted,
377
- lineHeight: theme.typography.baseLineHeight - 0.15,
378
- borderLeftColor: theme.palette.blockquoteBorder,
379
- bgColor: theme.palette.blockquoteBg,
380
- },
381
- codeBlock: {
382
- fontFamily: theme.typography.codeFontFamily,
383
- fontSize: Math.max(12, (parseInt(theme.typography.baseFontSize) || 16) - 3),
384
- fontWeight: 400,
385
- color: theme.palette.codeBlockText || theme.palette.textColor,
386
- lineHeight: theme.seedTypography.lineHeightCode,
387
- bgColor: theme.palette.codeBlockBg,
388
- },
389
- inlineCode: {
390
- fontSize: Math.max(12, (parseInt(theme.typography.baseFontSize) || 16) - 3),
391
- color: theme.palette.accentColor,
392
- bgColor: theme.palette.codeBg,
393
- },
394
- table: {
395
- headerFontFamily: theme.seedTypography.fontHeading,
396
- headerFontSize: Math.max(12, (parseInt(theme.typography.baseFontSize) || 16) - 3),
397
- headerFontWeight: 700,
398
- headerColor: "#ffffff",
399
- headerBgColor: theme.palette.tableHeaderBg,
400
- headerLineHeight: 1.4,
401
-
402
- cellFontFamily: theme.seedTypography.fontBody,
403
- cellFontSize: Math.max(11, (parseInt(theme.typography.baseFontSize) || 16) - 4),
404
- cellColor: theme.palette.textColor,
405
- cellBgColor: theme.palette.bgColor,
406
- cellLineHeight: 1.5,
407
- cellPadding: "8px 12px",
408
- borderColor: theme.palette.borderColor,
409
- borderWidth: "1px",
410
- },
411
-
412
- accentColor: theme.palette.accentColor,
413
- mutedTextColor: theme.palette.textMuted,
414
- selectionBg: theme.palette.selectionBg,
415
- };
416
- }
417
- }