@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,811 @@
1
+ /**
2
+ * DOCX 文档样式规范体系 — v1.0
3
+ *
4
+ * 核心功能:
5
+ * 1. 定义完整的文档元素样式层级(标题 H1-H6、正文、表格等)
6
+ * 2. 提供 WCAG 2.1 AAA 级对比度合规的标准参考值
7
+ * 3. 实现打印友好型灰度/低饱和度配色方案
8
+ * 4. 提供样式提取 fallback 与规范化工具
9
+ *
10
+ * 设计原则:
11
+ * - 文字色使用灰度轴(HSL S ≤ 0.05)或极低饱和度(S ≤ 0.15)
12
+ * - 背景色保持近白/近黑中性,饱和度 ≤ 0.03
13
+ * - 所有前景/背景对比度满足 WCAG AA (4.5:1) 或 AAA (7:1)
14
+ * - 行间距遵循中文排版规范(正文 1.5-1.9,标题 1.25-1.4)
15
+ */
16
+
17
+ import { hexToRgb, rgbToHex, lighten, darken } from "./color-utils.js";
18
+ import { relativeLuminance, contrastRatio, hslSaturation, hslLightness } from "./contrast.js";
19
+
20
+ // ═══════════════════════════════════════════════════════════
21
+ // 类型定义
22
+ // ═══════════════════════════════════════════════════════════
23
+
24
+ /** 单个字体样式的完整定义 */
25
+ export interface FontStyleDefinition {
26
+ /** 字体家族(CSS font-family 值) */
27
+ fontFamily: string;
28
+ /** 字号(px) */
29
+ fontSize: number;
30
+ /** 字重(100-900) */
31
+ fontWeight: number;
32
+ /** 文字颜色(hex) */
33
+ color: string;
34
+ /** 行高倍率 */
35
+ lineHeight: number;
36
+ /** 字间距(em,可选) */
37
+ letterSpacing?: number;
38
+ /** 段后间距(em,可选) */
39
+ marginBottom?: number;
40
+ }
41
+
42
+ /** 表格单元格样式定义 */
43
+ export interface TableCellStyle {
44
+ /** 表头字体家族 */
45
+ headerFontFamily: string;
46
+ /** 表头字号(px) */
47
+ headerFontSize: number;
48
+ /** 表头字重 */
49
+ headerFontWeight: number;
50
+ /** 表头文字颜色 */
51
+ headerColor: string;
52
+ /** 表头背景颜色 */
53
+ headerBgColor: string;
54
+ /** 表头行高 */
55
+ headerLineHeight: number;
56
+
57
+ /** 单元格字体家族 */
58
+ cellFontFamily: string;
59
+ /** 单元格字号(px) */
60
+ cellFontSize: number;
61
+ /** 单元格文字颜色 */
62
+ cellColor: string;
63
+ /** 单元格背景颜色 */
64
+ cellBgColor: string;
65
+ /** 单元格行高 */
66
+ cellLineHeight: number;
67
+ /** 单元格内边距 */
68
+ cellPadding: string;
69
+ /** 边框颜色 */
70
+ borderColor: string;
71
+ /** 边框宽度 */
72
+ borderWidth: string;
73
+ }
74
+
75
+ /** 完整的文档样式规范 */
76
+ export interface DocumentStyleParadigm {
77
+ /** 文档背景颜色 */
78
+ pageBackground: string;
79
+ /** 各级标题样式映射 */
80
+ headings: Record<"h1" | "h2" | "h3" | "h4" | "h5" | "h6", FontStyleDefinition>;
81
+ /** 正文样式 */
82
+ body: FontStyleDefinition;
83
+ /** 引用块样式 */
84
+ blockquote: FontStyleDefinition & { borderLeftColor: string; bgColor: string };
85
+ /** 代码块样式 */
86
+ codeBlock: FontStyleDefinition & { bgColor: string };
87
+ /** 内联代码样式 */
88
+ inlineCode: { fontSize: number; color: string; bgColor: string };
89
+ /** 表格样式 */
90
+ table: TableCellStyle;
91
+ /** 链接/强调色 */
92
+ accentColor: string;
93
+ /** 弱化文字颜色(脚注、辅助信息) */
94
+ mutedTextColor: string;
95
+ /** 选区高亮色 */
96
+ selectionBg: string;
97
+ }
98
+
99
+ /** 打印优化配置 */
100
+ export interface PrintOptimization {
101
+ /** 是否启用纯灰度模式(强制所有彩色转灰度) */
102
+ forceGrayscale: boolean;
103
+ /** 最大文字饱和度阈值(0-1) */
104
+ maxTextSaturation: number;
105
+ /** 最小对比度要求(WCAG 级别) */
106
+ minContrastRatio: "AA" | "AAA";
107
+ /** 背景色亮度调整(-1 到 1,负值变亮) */
108
+ bgBrightnessOffset: number;
109
+ }
110
+
111
+ // ═══════════════════════════════════════════════════════════
112
+ // 常量:标准参考值(Fallback Defaults)
113
+ // ═══════════════════════════════════════════════════════════
114
+
115
+ /**
116
+ * 学术论文标准参考值
117
+ * 基于 GB/T 7713-1987 中文学术论文编排规则 + WCAG AAA
118
+ */
119
+ export const ACADEMIC_PAPER_DEFAULTS: DocumentStyleParadigm = {
120
+ pageBackground: "#ffffff",
121
+
122
+ headings: {
123
+ h1: {
124
+ fontFamily: '"SimHei", "Heiti SC", "Source Han Sans SC", sans-serif',
125
+ fontSize: 22,
126
+ fontWeight: 700,
127
+ color: "#1a1a1a",
128
+ lineHeight: 1.35,
129
+ marginBottom: 0.8,
130
+ },
131
+ h2: {
132
+ fontFamily: '"SimHei", "Heiti SC", "Source Han Sans SC", sans-serif',
133
+ fontSize: 18,
134
+ fontWeight: 700,
135
+ color: "#1a1a1a",
136
+ lineHeight: 1.35,
137
+ marginBottom: 0.7,
138
+ },
139
+ h3: {
140
+ fontFamily: '"SimHei", "Heiti SC", "Source Han Sans SC", sans-serif',
141
+ fontSize: 16,
142
+ fontWeight: 600,
143
+ color: "#2a2a2a",
144
+ lineHeight: 1.4,
145
+ marginBottom: 0.6,
146
+ },
147
+ h4: {
148
+ fontFamily: '"SimSun", "Songti SC", serif',
149
+ fontSize: 14,
150
+ fontWeight: 600,
151
+ color: "#333333",
152
+ lineHeight: 1.4,
153
+ marginBottom: 0.5,
154
+ },
155
+ h5: {
156
+ fontFamily: '"SimSun", "Songti SC", serif',
157
+ fontSize: 13,
158
+ fontWeight: 600,
159
+ color: "#444444",
160
+ lineHeight: 1.45,
161
+ marginBottom: 0.4,
162
+ },
163
+ h6: {
164
+ fontFamily: '"SimSun", "Songti SC", serif',
165
+ fontSize: 12,
166
+ fontWeight: 600,
167
+ color: "#555555",
168
+ lineHeight: 1.5,
169
+ marginBottom: 0.3,
170
+ },
171
+ },
172
+
173
+ body: {
174
+ fontFamily: '"SimSun", "Songti SC", "Noto Serif SC", Georgia, serif',
175
+ fontSize: 16,
176
+ fontWeight: 400,
177
+ color: "#222222",
178
+ lineHeight: 1.75,
179
+ letterSpacing: 0.02,
180
+ },
181
+
182
+ blockquote: {
183
+ fontFamily: '"SimSun", "Songti SC", serif',
184
+ fontSize: 14,
185
+ fontWeight: 400,
186
+ color: "#555555",
187
+ lineHeight: 1.65,
188
+ borderLeftColor: "#888888",
189
+ bgColor: "#fafafa",
190
+ },
191
+
192
+ codeBlock: {
193
+ fontFamily: '"Courier New", Consolas, monospace',
194
+ fontSize: 13,
195
+ fontWeight: 400,
196
+ color: "#333333",
197
+ lineHeight: 1.5,
198
+ bgColor: "#f5f5f5",
199
+ },
200
+
201
+ inlineCode: {
202
+ fontSize: 13,
203
+ color: "#c7254e",
204
+ bgColor: "#f9f2f4",
205
+ },
206
+
207
+ table: {
208
+ headerFontFamily: '"SimHei", "Heiti SC", sans-serif',
209
+ headerFontSize: 13,
210
+ headerFontWeight: 700,
211
+ headerColor: "#ffffff",
212
+ headerBgColor: "#4a4a4a",
213
+ headerLineHeight: 1.4,
214
+
215
+ cellFontFamily: '"SimSun", "Songti SC", serif',
216
+ cellFontSize: 12,
217
+ cellColor: "#333333",
218
+ cellBgColor: "#ffffff",
219
+ cellLineHeight: 1.5,
220
+ cellPadding: "8px 12px",
221
+ borderColor: "#cccccc",
222
+ borderWidth: "1px",
223
+ },
224
+
225
+ accentColor: "#2b579a",
226
+ mutedTextColor: "#666666",
227
+ selectionBg: "rgba(43, 87, 154, 0.15)",
228
+ };
229
+
230
+ /**
231
+ * 商务报告标准参考值
232
+ * 现代、简洁、打印友好
233
+ */
234
+ export const BUSINESS_REPORT_DEFAULTS: DocumentStyleParadigm = {
235
+ pageBackground: "#ffffff",
236
+
237
+ headings: {
238
+ h1: {
239
+ fontFamily: '"Inter", "SF Pro Display", -apple-system, sans-serif',
240
+ fontSize: 24,
241
+ fontWeight: 700,
242
+ color: "#1f2937",
243
+ lineHeight: 1.3,
244
+ marginBottom: 1.0,
245
+ },
246
+ h2: {
247
+ fontFamily: '"Inter", "SF Pro Display", -apple-system, sans-serif',
248
+ fontSize: 20,
249
+ fontWeight: 600,
250
+ color: "#374151",
251
+ lineHeight: 1.35,
252
+ marginBottom: 0.8,
253
+ },
254
+ h3: {
255
+ fontFamily: '"Inter", "SF Pro Display", -apple-system, sans-serif',
256
+ fontSize: 17,
257
+ fontWeight: 600,
258
+ color: "#4b5563",
259
+ lineHeight: 1.38,
260
+ marginBottom: 0.6,
261
+ },
262
+ h4: {
263
+ fontFamily: '"Inter", -apple-system, sans-serif',
264
+ fontSize: 15,
265
+ fontWeight: 600,
266
+ color: "#6b7280",
267
+ lineHeight: 1.4,
268
+ marginBottom: 0.5,
269
+ },
270
+ h5: {
271
+ fontFamily: '"Inter", -apple-system, sans-serif',
272
+ fontSize: 14,
273
+ fontWeight: 500,
274
+ color: "#6b7280",
275
+ lineHeight: 1.45,
276
+ marginBottom: 0.4,
277
+ },
278
+ h6: {
279
+ fontFamily: '"Inter", -apple-system, sans-serif',
280
+ fontSize: 13,
281
+ fontWeight: 500,
282
+ color: "#9ca3af",
283
+ lineHeight: 1.5,
284
+ marginBottom: 0.3,
285
+ },
286
+ },
287
+
288
+ body: {
289
+ fontFamily: '"Inter", "SF Pro Text", -apple-system, "Noto Sans SC", sans-serif',
290
+ fontSize: 15,
291
+ fontWeight: 400,
292
+ color: "#374151",
293
+ lineHeight: 1.7,
294
+ letterSpacing: 0.01,
295
+ },
296
+
297
+ blockquote: {
298
+ fontFamily: '"Inter", sans-serif',
299
+ fontSize: 14,
300
+ fontWeight: 400,
301
+ color: "#6b7280",
302
+ lineHeight: 1.6,
303
+ borderLeftColor: "#9ca3af",
304
+ bgColor: "#f9fafb",
305
+ },
306
+
307
+ codeBlock: {
308
+ fontFamily: '"JetBrains Mono", "Fira Code", monospace',
309
+ fontSize: 12.5,
310
+ fontWeight: 400,
311
+ color: "#374151",
312
+ lineHeight: 1.55,
313
+ bgColor: "#f3f4f6",
314
+ },
315
+
316
+ inlineCode: {
317
+ fontSize: 12.5,
318
+ color: "#dc2626",
319
+ bgColor: "#fef2f2",
320
+ },
321
+
322
+ table: {
323
+ headerFontFamily: '"Inter", -apple-system, sans-serif',
324
+ headerFontSize: 12.5,
325
+ headerFontWeight: 600,
326
+ headerColor: "#ffffff",
327
+ headerBgColor: "#4b5563",
328
+ headerLineHeight: 1.4,
329
+
330
+ cellFontFamily: '"Inter", sans-serif',
331
+ cellFontSize: 12,
332
+ cellColor: "#374151",
333
+ cellBgColor: "#ffffff",
334
+ cellLineHeight: 1.5,
335
+ cellPadding: "10px 14px",
336
+ borderColor: "#e5e7eb",
337
+ borderWidth: "1px",
338
+ },
339
+
340
+ accentColor: "#2563eb",
341
+ mutedTextColor: "#9ca3af",
342
+ selectionBg: "rgba(37, 99, 235, 0.12)",
343
+ };
344
+
345
+ /**
346
+ * 技术文档标准参考值
347
+ * 适合 API 文档、技术手册
348
+ */
349
+ export const TECHNICAL_DOC_DEFAULTS: DocumentStyleParadigm = {
350
+ pageBackground: "#fefefe",
351
+
352
+ headings: {
353
+ h1: {
354
+ fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
355
+ fontSize: 28,
356
+ fontWeight: 800,
357
+ color: "#111827",
358
+ lineHeight: 1.25,
359
+ marginBottom: 1.2,
360
+ },
361
+ h2: {
362
+ fontFamily: '"Inter", system-ui, sans-serif',
363
+ fontSize: 22,
364
+ fontWeight: 700,
365
+ color: "#1f2937",
366
+ lineHeight: 1.3,
367
+ marginBottom: 0.9,
368
+ },
369
+ h3: {
370
+ fontFamily: '"Inter", system-ui, sans-serif',
371
+ fontSize: 18,
372
+ fontWeight: 600,
373
+ color: "#374151",
374
+ lineHeight: 1.35,
375
+ marginBottom: 0.7,
376
+ },
377
+ h4: {
378
+ fontFamily: '"Inter", system-ui, sans-serif',
379
+ fontSize: 16,
380
+ fontWeight: 600,
381
+ color: "#4b5563",
382
+ lineHeight: 1.38,
383
+ marginBottom: 0.5,
384
+ },
385
+ h5: {
386
+ fontFamily: '"Inter", system-ui, sans-serif',
387
+ fontSize: 14,
388
+ fontWeight: 500,
389
+ color: "#6b7280",
390
+ lineHeight: 1.42,
391
+ marginBottom: 0.4,
392
+ },
393
+ h6: {
394
+ fontFamily: '"JetBrains Mono", monospace',
395
+ fontSize: 13,
396
+ fontWeight: 500,
397
+ color: "#9ca3af",
398
+ lineHeight: 1.5,
399
+ marginBottom: 0.3,
400
+ },
401
+ },
402
+
403
+ body: {
404
+ fontFamily: '"Inter", "SF Pro Text", system-ui, "Noto Sans SC", sans-serif',
405
+ fontSize: 16,
406
+ fontWeight: 400,
407
+ color: "#1f2937",
408
+ lineHeight: 1.75,
409
+ },
410
+
411
+ blockquote: {
412
+ fontFamily: '"Inter", system-ui, sans-serif',
413
+ fontSize: 14.5,
414
+ fontWeight: 400,
415
+ color: "#6b7280",
416
+ lineHeight: 1.65,
417
+ borderLeftColor: "#d1d5db",
418
+ bgColor: "#f9fafb",
419
+ },
420
+
421
+ codeBlock: {
422
+ fontFamily: '"JetBrains Mono", "SF Mono", "Fira Code", monospace',
423
+ fontSize: 13.5,
424
+ fontWeight: 400,
425
+ color: "#e5e7eb",
426
+ lineHeight: 1.6,
427
+ bgColor: "#1f2937",
428
+ },
429
+
430
+ inlineCode: {
431
+ fontSize: 13,
432
+ color: "#dc2626",
433
+ bgColor: "#fef2f2",
434
+ },
435
+
436
+ table: {
437
+ headerFontFamily: '"Inter", system-ui, sans-serif',
438
+ headerFontSize: 13,
439
+ headerFontWeight: 600,
440
+ headerColor: "#f9fafb",
441
+ headerBgColor: "#374151",
442
+ headerLineHeight: 1.4,
443
+
444
+ cellFontFamily: '"Inter", sans-serif',
445
+ cellFontSize: 13,
446
+ cellColor: "#374151",
447
+ cellBgColor: "#ffffff",
448
+ cellLineHeight: 1.55,
449
+ cellPadding: "10px 14px",
450
+ borderColor: "#e5e7eb",
451
+ borderWidth: "1px",
452
+ },
453
+
454
+ accentColor: "#2563eb",
455
+ mutedTextColor: "#9ca3af",
456
+ selectionBg: "rgba(37, 99, 235, 0.15)",
457
+ };
458
+
459
+ // ═══════════════════════════════════════════════════════════
460
+ // 打印友好型灰度/低饱和度配色生成器
461
+ // ═══════════════════════════════════════════════════════════
462
+
463
+ /**
464
+ * 生成打印优化的灰度色板
465
+ * @param baseBrightness 页面基础亮度(0=纯黑,1=纯白)
466
+ * @param options 打印优化配置
467
+ */
468
+ export function generatePrintFriendlyGrayscale(
469
+ baseBrightness: number = 0.97,
470
+ options?: Partial<PrintOptimization>
471
+ ): DocumentStyleParadigm {
472
+ const opts: PrintOptimization = {
473
+ forceGrayscale: true,
474
+ maxTextSaturation: 0.05,
475
+ minContrastRatio: "AAA",
476
+ bgBrightnessOffset: 0,
477
+ ...options,
478
+ };
479
+
480
+ const surfaceValue = Math.round(baseBrightness * 255);
481
+ const surfaceHex = rgbToHex(surfaceValue, surfaceValue, surfaceValue);
482
+
483
+ const inkValue = Math.max(0, Math.round((1 - baseBrightness) * 255 * 0.92));
484
+ const inkHex = rgbToHex(inkValue, inkValue, inkValue);
485
+
486
+ const mutedValue = Math.round(inkValue + (surfaceValue - inkValue) * 0.55);
487
+ const mutedHex = rgbToHex(mutedValue, mutedValue, mutedValue);
488
+
489
+ const dimValue = Math.round(inkValue + (surfaceValue - inkValue) * 0.75);
490
+ const dimHex = rgbToHex(dimValue, dimValue, dimValue);
491
+
492
+ const panelValue = Math.max(0, Math.round(surfaceValue - 12));
493
+ const panelHex = rgbToHex(panelValue, panelValue, panelValue);
494
+
495
+ const panelAltValue = Math.max(0, Math.round(surfaceValue - 24));
496
+ const panelAltHex = rgbToHex(panelAltValue, panelAltValue, panelAltValue);
497
+
498
+ const accentGray = opts.forceGrayscale
499
+ ? rgbToHex(Math.round(inkValue * 0.4), Math.round(inkValue * 0.4), Math.round(inkValue * 0.4))
500
+ : "#4a5568";
501
+
502
+ return {
503
+ pageBackground: surfaceHex,
504
+
505
+ headings: {
506
+ h1: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h1.fontFamily, fontSize: 22, fontWeight: 700, color: inkHex, lineHeight: 1.35, marginBottom: 0.8 },
507
+ h2: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h2.fontFamily, fontSize: 18, fontWeight: 700, color: inkHex, lineHeight: 1.35, marginBottom: 0.7 },
508
+ h3: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h3.fontFamily, fontSize: 16, fontWeight: 600, color: mutedHex, lineHeight: 1.4, marginBottom: 0.6 },
509
+ h4: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h4.fontFamily, fontSize: 14, fontWeight: 600, color: mutedHex, lineHeight: 1.4, marginBottom: 0.5 },
510
+ h5: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h5.fontFamily, fontSize: 13, fontWeight: 600, color: dimHex, lineHeight: 1.45, marginBottom: 0.4 },
511
+ h6: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h6.fontFamily, fontSize: 12, fontWeight: 600, color: dimHex, lineHeight: 1.5, marginBottom: 0.3 },
512
+ },
513
+
514
+ body: {
515
+ fontFamily: ACADEMIC_PAPER_DEFAULTS.body.fontFamily,
516
+ fontSize: 16,
517
+ fontWeight: 400,
518
+ color: rgbToHex(Math.round(inkValue * 0.88), Math.round(inkValue * 0.88), Math.round(inkValue * 0.88)),
519
+ lineHeight: 1.75,
520
+ letterSpacing: 0.02,
521
+ },
522
+
523
+ blockquote: {
524
+ fontFamily: ACADEMIC_PAPER_DEFAULTS.body.fontFamily,
525
+ fontSize: 14,
526
+ fontWeight: 400,
527
+ color: mutedHex,
528
+ lineHeight: 1.65,
529
+ borderLeftColor: dimHex,
530
+ bgColor: panelHex,
531
+ },
532
+
533
+ codeBlock: {
534
+ fontFamily: '"Courier New", Consolas, monospace',
535
+ fontSize: 13,
536
+ fontWeight: 400,
537
+ color: inkHex,
538
+ lineHeight: 1.5,
539
+ bgColor: panelAltHex,
540
+ },
541
+
542
+ inlineCode: {
543
+ fontSize: 13,
544
+ color: accentGray,
545
+ bgColor: lighten(panelAltHex, 0.3),
546
+ },
547
+
548
+ table: {
549
+ headerFontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h2.fontFamily,
550
+ headerFontSize: 13,
551
+ headerFontWeight: 700,
552
+ headerColor: surfaceHex,
553
+ headerBgColor: rgbToHex(
554
+ Math.round(inkValue * 0.72),
555
+ Math.round(inkValue * 0.72),
556
+ Math.round(inkValue * 0.72)
557
+ ),
558
+ headerLineHeight: 1.4,
559
+
560
+ cellFontFamily: ACADEMIC_PAPER_DEFAULTS.body.fontFamily,
561
+ cellFontSize: 12,
562
+ cellColor: rgbToHex(Math.round(inkValue * 0.82), Math.round(inkValue * 0.82), Math.round(inkValue * 0.82)),
563
+ cellBgColor: surfaceHex,
564
+ cellLineHeight: 1.5,
565
+ cellPadding: "8px 12px",
566
+ borderColor: rgbToHex(
567
+ Math.round(surfaceValue - 40),
568
+ Math.round(surfaceValue - 40),
569
+ Math.round(surfaceValue - 40)
570
+ ),
571
+ borderWidth: "1px",
572
+ },
573
+
574
+ accentColor: accentGray,
575
+ mutedTextColor: mutedHex,
576
+ selectionBg: `rgba(${Math.round(inkValue * 0.4)}, ${Math.round(inkValue * 0.4)}, ${Math.round(inkValue * 0.4)}, 0.15)`,
577
+ };
578
+ }
579
+
580
+ /**
581
+ * 生成低饱和度莫兰迪风格色板(适合打印)
582
+ * 保持微弱色相但大幅降低饱和度
583
+ */
584
+ export function generateMorandiPrintPalette(
585
+ hueDeg: number = 210,
586
+ saturation: number = 0.08,
587
+ ): DocumentStyleParadigm {
588
+ const hslToRgb = (h: number, s: number, l: number): [number, number, number] => {
589
+ const c = (1 - Math.abs(2 * l - 1)) * s;
590
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
591
+ const m = l - c / 2;
592
+ let r = 0, g = 0, b = 0;
593
+ if (h < 60) { r = c; g = x; }
594
+ else if (h < 120) { r = x; g = c; }
595
+ else if (h < 180) { g = c; b = x; }
596
+ else if (h < 240) { g = x; b = c; }
597
+ else if (h < 300) { r = x; b = c; }
598
+ else { r = c; b = x; }
599
+ return [
600
+ Math.round((r + m) * 255),
601
+ Math.round((g + m) * 255),
602
+ Math.round((b + m) * 255),
603
+ ];
604
+ };
605
+
606
+ const surface = rgbToHex(...hslToRgb(hueDeg, 0.01, 0.98));
607
+ const ink = rgbToRgbString(hslToRgb(hueDeg, 0.04, 0.12));
608
+ const muted = rgbToRgbString(hslToRgb(hueDeg, 0.03, 0.42));
609
+ const dim = rgbToRgbString(hslToRgb(hueDeg, 0.02, 0.58));
610
+ const panel = rgbToRgbString(hslToRgb(hueDeg, 0.02, 0.95));
611
+ const panelAlt = rgbToRgbString(hslToRgb(hueDeg, 0.03, 0.91));
612
+ const accent = rgbToRgbString(hslToRgb(hueDeg, saturation, 0.38));
613
+ const headerBg = rgbToRgbString(hslToRgb(hueDeg, 0.06, 0.32));
614
+
615
+ return {
616
+ pageBackground: surface,
617
+ headings: {
618
+ h1: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h1.fontFamily, fontSize: 22, fontWeight: 700, color: ink, lineHeight: 1.35, marginBottom: 0.8 },
619
+ h2: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h2.fontFamily, fontSize: 18, fontWeight: 700, color: ink, lineHeight: 1.35, marginBottom: 0.7 },
620
+ h3: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h3.fontFamily, fontSize: 16, fontWeight: 600, color: muted, lineHeight: 1.4, marginBottom: 0.6 },
621
+ h4: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h4.fontFamily, fontSize: 14, fontWeight: 600, color: muted, lineHeight: 1.4, marginBottom: 0.5 },
622
+ h5: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h5.fontFamily, fontSize: 13, fontWeight: 600, color: dim, lineHeight: 1.45, marginBottom: 0.4 },
623
+ h6: { fontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h6.fontFamily, fontSize: 12, fontWeight: 600, color: dim, lineHeight: 1.5, marginBottom: 0.3 },
624
+ },
625
+ body: {
626
+ fontFamily: ACADEMIC_PAPER_DEFAULTS.body.fontFamily,
627
+ fontSize: 16,
628
+ fontWeight: 400,
629
+ color: ink,
630
+ lineHeight: 1.75,
631
+ letterSpacing: 0.02,
632
+ },
633
+ blockquote: {
634
+ fontFamily: ACADEMIC_PAPER_DEFAULTS.body.fontFamily,
635
+ fontSize: 14,
636
+ fontWeight: 400,
637
+ color: muted,
638
+ lineHeight: 1.65,
639
+ borderLeftColor: dim,
640
+ bgColor: panel,
641
+ },
642
+ codeBlock: {
643
+ fontFamily: '"Courier New", Consolas, monospace',
644
+ fontSize: 13,
645
+ fontWeight: 400,
646
+ color: ink,
647
+ lineHeight: 1.5,
648
+ bgColor: panelAlt,
649
+ },
650
+ inlineCode: { fontSize: 13, color: accent, bgColor: lighten(panelAlt, 0.4) },
651
+ table: {
652
+ headerFontFamily: ACADEMIC_PAPER_DEFAULTS.headings.h2.fontFamily,
653
+ headerFontSize: 13,
654
+ headerFontWeight: 700,
655
+ headerColor: surface,
656
+ headerBgColor: headerBg,
657
+ headerLineHeight: 1.4,
658
+ cellFontFamily: ACADEMIC_PAPER_DEFAULTS.body.fontFamily,
659
+ cellFontSize: 12,
660
+ cellColor: muted,
661
+ cellBgColor: surface,
662
+ cellLineHeight: 1.5,
663
+ cellPadding: "8px 12px",
664
+ borderColor: rgbToRgbString(hslToRgb(hueDeg, 0.04, 0.85)),
665
+ borderWidth: "1px",
666
+ },
667
+ accentColor: accent,
668
+ mutedTextColor: muted,
669
+ selectionBg: `rgba(${hexToRgb(accent).join(",")}, 0.12)`,
670
+ };
671
+ }
672
+
673
+ function rgbToRgbString(rgb: [number, number, number]): string {
674
+ return rgbToHex(rgb[0], rgb[1], rgb[2]);
675
+ }
676
+
677
+ // ═══════════════════════════════════════════════════════════
678
+ // 样式验证工具
679
+ // ═══════════════════════════════════════════════════════════
680
+
681
+ export interface StyleValidationIssue {
682
+ level: "error" | "warning" | "info";
683
+ element: string;
684
+ message: string;
685
+ actualValue?: string;
686
+ expectedRange?: string;
687
+ }
688
+
689
+ /**
690
+ * 验证文档样式是否满足 WCAG 对比度要求和打印友好性
691
+ */
692
+ export function validateDocumentStyle(style: DocumentStyleParadigm): StyleValidationIssue[] {
693
+ const issues: StyleValidationIssue[] = [];
694
+ const bg = style.pageBackground;
695
+
696
+ const checkContrast = (
697
+ fg: string,
698
+ label: string,
699
+ minRatio: number,
700
+ requiredLevel: string
701
+ ) => {
702
+ const ratio = contrastRatio(fg, bg);
703
+ if (ratio < minRatio) {
704
+ issues.push({
705
+ level: "error",
706
+ element: label,
707
+ message: `${label} 对比度 ${ratio.toFixed(1)}:1 不满足 ${requiredLevel} 要求 (≥${minRatio}:1)`,
708
+ actualValue: `${ratio.toFixed(1)}:1`,
709
+ expectedRange: `≥${minRatio}:1`,
710
+ });
711
+ }
712
+ };
713
+
714
+ checkContrast(style.body.color, "正文", 7.0, "WCAG AAA");
715
+ checkContrast(style.headings.h1.color, "H1标题", 7.0, "WCAG AAA");
716
+ checkContrast(style.headings.h2.color, "H2标题", 7.0, "WCAG AAA");
717
+ checkContrast(style.mutedTextColor, "弱化文字", 4.5, "WCAG AA");
718
+
719
+ const checkSaturation = (color: string, label: string, maxSat: number) => {
720
+ const sat = hslSaturation(color);
721
+ if (sat > maxSat) {
722
+ issues.push({
723
+ level: "warning",
724
+ element: label,
725
+ message: `${label} 饱和度 ${(sat * 100).toFixed(0)}% 超过打印建议值 (${(maxSat * 100).toFixed(0)}%)`,
726
+ actualValue: `${(sat * 100).toFixed(0)}%`,
727
+ expectedRange: `≤${(maxSat * 100).toFixed(0)}%`,
728
+ });
729
+ }
730
+ };
731
+
732
+ checkSaturation(style.body.color, "正文颜色", 0.08);
733
+ checkSaturation(style.headings.h1.color, "H1颜色", 0.08);
734
+ checkSaturation(style.table.headerBgColor, "表头背景", 0.20);
735
+
736
+ if (style.codeBlock.bgColor !== "#1f2937" && style.codeBlock.bgColor !== "#000000") {
737
+ const codeBgLum = relativeLuminance(style.codeBlock.bgColor);
738
+ const surfLum = relativeLuminance(bg);
739
+ const diff = Math.abs(codeBgLum - surfLum);
740
+ if (diff < 0.06) {
741
+ issues.push({
742
+ level: "warning",
743
+ element: "代码块背景",
744
+ message: "代码块背景与页面底色区分度过低",
745
+ actualValue: `ΔL=${diff.toFixed(3)}`,
746
+ expectedRange: "ΔL ≥ 0.06",
747
+ });
748
+ }
749
+ }
750
+
751
+ return issues;
752
+ }
753
+
754
+ /**
755
+ * 强制将样式规范化为打印友好格式
756
+ * 降低饱和度、确保对比度达标
757
+ */
758
+ export function normalizeForPrinting(
759
+ style: DocumentStyleParadigm,
760
+ forceGrayscale: boolean = false
761
+ ): DocumentStyleParadigm {
762
+ const normalized = JSON.parse(JSON.stringify(style)) as DocumentStyleParadigm;
763
+
764
+ const desaturateColor = (hex: string, targetSat: number = 0.05): string => {
765
+ if (forceGrayscale) {
766
+ const [r, g, b] = hexToRgb(hex);
767
+ const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
768
+ return rgbToHex(gray, gray, gray);
769
+ }
770
+ return hex;
771
+ };
772
+
773
+ normalized.body.color = desaturateColor(normalized.body.color);
774
+ normalized.headings.h1.color = desaturateColor(normalized.headings.h1.color);
775
+ normalized.headings.h2.color = desaturateColor(normalized.headings.h2.color);
776
+ normalized.headings.h3.color = desaturateColor(normalized.headings.h3.color);
777
+ normalized.mutedTextColor = desaturateColor(normalized.mutedTextColor);
778
+
779
+ return normalized;
780
+ }
781
+
782
+ // ═══════════════════════════════════════════════════════════
783
+ // 预设模板注册表
784
+ // ═══════════════════════════════════════════════════════════
785
+
786
+ export const DOCUMENT_STYLE_PRESETS: Record<string, DocumentStyleParadigm> = {
787
+ "academic-paper": ACADEMIC_PAPER_DEFAULTS,
788
+ "business-report": BUSINESS_REPORT_DEFAULTS,
789
+ "technical-doc": TECHNICAL_DOC_DEFAULTS,
790
+ "print-grayscale": generatePrintFriendlyGrayscale(),
791
+ "print-morandi-blue": generateMorandiPrintPalette(210),
792
+ "print-morandi-green": generateMorandiPrintPalette(140),
793
+ "print-morandi-warm": generateMorandiPrintPalette(30),
794
+ };
795
+
796
+ /**
797
+ * 根据文档类型获取推荐预设
798
+ */
799
+ export function getRecommendedPreset(docType?: string): DocumentStyleParadigm {
800
+ const type = (docType || "").toLowerCase();
801
+ if (type.includes("学术") || type.includes("论文") || type.includes("paper")) {
802
+ return ACADEMIC_PAPER_DEFAULTS;
803
+ }
804
+ if (type.includes("商务") || type.includes("报告") || type.includes("report")) {
805
+ return BUSINESS_REPORT_DEFAULTS;
806
+ }
807
+ if (type.includes("技术") || type.includes("api") || type.includes("tech")) {
808
+ return TECHNICAL_DOC_DEFAULTS;
809
+ }
810
+ return ACADEMIC_PAPER_DEFAULTS;
811
+ }