@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,256 +0,0 @@
1
- /*
2
- * ColaMD Theme: {{ name }}
3
- * {{ description }}
4
- * Generated by colamd-themes ({{ sourceType }}: {{ sourceUrl }})
5
- *
6
- * 契约:ColaMD Extend 2.7(CodeMirror 6 编辑器 + Pele 图表引擎)
7
- *
8
- * 两条硬规矩:
9
- * 1. 变量一律写在 :root。没写的活变量继承 Light 主题默认值;写在别的选择器里
10
- * 编辑器继承不到。
11
- * 2. 编辑器里没有 <h1>/<blockquote> 这种元素:标题是「一行」带一个类,引用与
12
- * 代码块是每一行一个 div,加粗是一个 span。所以选择器只认 .cm-md-* 钩子,
13
- * 并且引用/代码块**不能加纵向 padding**——那会按行累加。
14
- *
15
- * ═══════════════════════════════════════════════════════════
16
- * SECTION 1 — Design Tokens
17
- * 改 11 个种子色 + 3 组字体即可换一套主题
18
- * ═══════════════════════════════════════════════════════════ */
19
-
20
- :root {
21
-
22
- /* ── 1.1 Seed Palette ──
23
- 本工具的再生成与校验读这一层;编辑器不消费。 */
24
- --seed-accent: {{ seed.accent }};
25
- --seed-accent-light: {{ seed.accentLight }};
26
- --seed-accent-dark: {{ seed.accentDark }};
27
- --seed-surface: {{ seed.surface }};
28
- --seed-panel: {{ seed.panel }};
29
- --seed-panel-alt: {{ seed.panelAlt }};
30
- --seed-ink: {{ seed.ink }};
31
- --seed-ink-muted: {{ seed.inkMuted }};
32
- --seed-ink-dim: {{ seed.inkDim }};
33
- --seed-border: {{ seed.border }};
34
- --seed-border-strong: {{ seed.borderStrong }};
35
-
36
- /* ── 1.2 Semantic Colors(活变量)── */
37
- --bg-color: var(--seed-surface);
38
- --text-color: var(--seed-ink);
39
- --text-secondary: var(--seed-ink-muted);
40
- --text-muted: var(--seed-ink-dim);
41
- --border-color: var(--seed-border);
42
- --link-color: var(--seed-accent);
43
-
44
- --code-bg: var(--seed-panel);
45
- --code-block-bg: var(--seed-panel-alt);
46
- --code-block-text: {{ codeBlockText }};
47
-
48
- --blockquote-border: var(--seed-accent);
49
- --blockquote-bg: var(--seed-panel);
50
- --table-header-bg: var(--seed-panel);
51
-
52
- --selection-bg: {{rgba seed.accent 0.18}};
53
- --highlight-bg: {{rgba seed.accent 0.18}};
54
-
55
- /* ── 1.3 Typography & Metrics(活变量)── */
56
- --font-body: {{{ seedTypography.fontBody }}};
57
- --font-mono: {{{ seedTypography.fontCode }}};
58
- --editor-font-size: {{ seedTypography.fontSizeRoot }};
59
- --editor-column-width: {{ contentWidth }};
60
-
61
- /* ── 1.4 Pele Diagram(9 个变量,全部可省略)──
62
- 图直接画在代码块底色上,所以底色与字色都从 --code-block-* 推导。 */
63
- --pele-bg: var(--code-block-bg);
64
- --pele-fg: var(--code-block-text);
65
- --pele-muted: color-mix(in srgb, var(--code-block-text) 70%, var(--code-block-bg));
66
- --pele-line: var(--code-block-text);
67
- --pele-surface: color-mix(in srgb, var(--code-block-text) 18%, var(--code-block-bg));
68
- --pele-surface-alt: color-mix(in srgb, var(--code-block-text) 28%, var(--code-block-bg));
69
- --pele-border: color-mix(in srgb, var(--code-block-text) 45%, var(--code-block-bg));
70
- --pele-accent: var(--link-color);
71
- --pele-radius: {{ radiusMd }};
72
- }
73
-
74
- /* ═══════════════════════════════════════════════════════════
75
- SECTION 2 — Editor DOM(#editor .cm-content + .cm-md-*)
76
- ═══════════════════════════════════════════════════════════ */
77
-
78
- #editor .cm-content {
79
- font-family: var(--font-body);
80
- line-height: {{ seedTypography.lineHeightBody }};
81
- }
82
-
83
- /* ── 2.1 Headings ── */
84
- #editor .cm-content .cm-md-atxheading1,
85
- #editor .cm-content .cm-md-atxheading2,
86
- #editor .cm-content .cm-md-atxheading3,
87
- #editor .cm-content .cm-md-atxheading4,
88
- #editor .cm-content .cm-md-atxheading5,
89
- #editor .cm-content .cm-md-atxheading6 {
90
- font-family: {{{ seedTypography.fontHeading }}};
91
- color: var(--text-color);
92
- font-weight: 700;
93
- line-height: {{ seedTypography.lineHeightHeading }};
94
- }
95
-
96
- #editor .cm-content .cm-md-atxheading1 {
97
- font-size: {{ seedTypography.fontScaleH1 }}em;
98
- padding-bottom: 0.3em;
99
- border-bottom: 1px solid var(--border-color);
100
- }
101
- #editor .cm-content .cm-md-atxheading2 {
102
- font-size: {{ seedTypography.fontScaleH2 }}em;
103
- padding-bottom: 0.25em;
104
- border-bottom: 1px solid var(--border-color);
105
- }
106
- #editor .cm-content .cm-md-atxheading3 { font-size: {{ seedTypography.fontScaleH3 }}em; }
107
- #editor .cm-content .cm-md-atxheading4 { font-size: {{ seedTypography.fontScaleH4 }}em; }
108
- #editor .cm-content .cm-md-atxheading5 { font-size: {{ seedTypography.fontScaleH5 }}em; }
109
- #editor .cm-content .cm-md-atxheading6 { font-size: {{ seedTypography.fontScaleH6 }}em; }
110
-
111
- /* ── 2.2 Inline ── */
112
- #editor .cm-content .cm-md-strong { color: var(--link-color); font-weight: 700; }
113
- #editor .cm-content .cm-md-em { font-style: italic; }
114
- #editor .cm-content .cm-md-strike { text-decoration: line-through; }
115
- #editor .cm-content .cm-md-highlight { background: var(--highlight-bg); }
116
- #editor .cm-content .cm-md-link { color: var(--link-color); }
117
- #editor .cm-content .cm-md-url { color: var(--text-muted); }
118
-
119
- #editor .cm-content .cm-md-inlinecode {
120
- font-family: var(--font-mono);
121
- background: var(--code-bg);
122
- color: var(--text-color);
123
- border-radius: {{ radiusSm }};
124
- }
125
-
126
- #editor .cm-content .cm-md-marker,
127
- #editor .cm-content .cm-md-quotemark,
128
- #editor .cm-content .cm-md-listmark {
129
- color: var(--text-muted);
130
- }
131
-
132
- /* ── 2.3 Blockquote / Code Block(每行一个 div,禁用纵向 padding)── */
133
- #editor .cm-content .cm-md-blockquote {
134
- background: var(--blockquote-bg);
135
- color: var(--text-secondary);
136
- border-left: 3px solid var(--blockquote-border);
137
- padding-left: 16px;
138
- padding-right: 16px;
139
- }
140
-
141
- #editor .cm-content .cm-md-codeblock {
142
- background: var(--code-block-bg);
143
- color: var(--code-block-text);
144
- padding-left: 16px;
145
- padding-right: 16px;
146
- }
147
- #editor .cm-content .cm-md-codeblock-first {
148
- border-top-left-radius: {{ radiusMd }};
149
- border-top-right-radius: {{ radiusMd }};
150
- }
151
- #editor .cm-content .cm-md-codeblock-last {
152
- border-bottom-left-radius: {{ radiusMd }};
153
- border-bottom-right-radius: {{ radiusMd }};
154
- }
155
-
156
- /* ── 2.4 Table / Rule ── */
157
- #editor .cm-content .cm-md-table-widget th,
158
- #editor .cm-content .cm-md-table-widget td {
159
- border-color: var(--border-color);
160
- }
161
- #editor .cm-content .cm-md-table-widget th {
162
- background: var(--table-header-bg);
163
- color: var(--text-color);
164
- font-weight: 700;
165
- border-bottom: 2px solid var(--link-color);
166
- }
167
- #editor .cm-content .cm-md-hr { border-top-color: var(--border-color); }
168
-
169
- /* ── 2.5 中文首行缩进 ──
170
- CM6 里一行就是一个 div:段落缩进落在 .cm-line 上,标题/引用/代码/列表行归零。 */
171
- #editor .cm-content .cm-line { text-indent: 2em; }
172
-
173
- #editor .cm-content .cm-md-atxheading1,
174
- #editor .cm-content .cm-md-atxheading2,
175
- #editor .cm-content .cm-md-atxheading3,
176
- #editor .cm-content .cm-md-atxheading4,
177
- #editor .cm-content .cm-md-atxheading5,
178
- #editor .cm-content .cm-md-atxheading6,
179
- #editor .cm-content .cm-md-blockquote,
180
- #editor .cm-content .cm-md-codeblock,
181
- #editor .cm-content .cm-md-hr,
182
- #editor .cm-content .cm-md-mermaid,
183
- #editor .cm-content .cm-md-li-0,
184
- #editor .cm-content .cm-md-li-1,
185
- #editor .cm-content .cm-md-li-2,
186
- #editor .cm-content .cm-md-li-3,
187
- #editor .cm-content .cm-md-li-4,
188
- #editor .cm-content .cm-md-li-5,
189
- #editor .cm-content .cm-md-li-6 {
190
- text-indent: 0;
191
- }
192
-
193
- /* ═══════════════════════════════════════════════════════════
194
- SECTION 3 — Exported HTML(.colamd-document,语义标签)
195
- HTML/PDF 导出不走编辑器 DOM;这段让导出件沿用同一套配色。
196
- ═══════════════════════════════════════════════════════════ */
197
-
198
- .colamd-document {
199
- font-family: var(--font-body);
200
- font-size: {{ seedTypography.fontSizeRoot }};
201
- line-height: {{ seedTypography.lineHeightBody }};
202
- }
203
-
204
- .colamd-document h1,
205
- .colamd-document h2,
206
- .colamd-document h3,
207
- .colamd-document h4,
208
- .colamd-document h5,
209
- .colamd-document h6 {
210
- font-family: {{{ seedTypography.fontHeading }}};
211
- color: var(--text-color);
212
- line-height: {{ seedTypography.lineHeightHeading }};
213
- }
214
-
215
- .colamd-document strong { color: var(--link-color); }
216
- .colamd-document code {
217
- background: var(--code-bg);
218
- color: var(--text-color);
219
- font-family: var(--font-mono);
220
- }
221
- .colamd-document pre { background: var(--code-block-bg); }
222
- .colamd-document pre code { color: var(--code-block-text); }
223
- .colamd-document blockquote {
224
- background: var(--blockquote-bg);
225
- color: var(--text-secondary);
226
- border-left-color: var(--blockquote-border);
227
- }
228
- .colamd-document mark { background: var(--highlight-bg); }
229
- .colamd-document th {
230
- background: var(--table-header-bg);
231
- border-bottom: 2px solid var(--link-color);
232
- }
233
- .colamd-document hr { border-top-color: var(--border-color); }
234
-
235
- .colamd-document p { text-indent: 2em; }
236
- .colamd-document li p,
237
- .colamd-document td p,
238
- .colamd-document th p,
239
- .colamd-document blockquote p {
240
- text-indent: 0;
241
- }
242
-
243
- /* ═══════════════════════════════════════════════════════════
244
- SECTION 4 — Print
245
- 应用直接对当前窗口 printToPDF,屏幕规则原样生效;这里只补保真。
246
- ═══════════════════════════════════════════════════════════ */
247
-
248
- @media print {
249
- html,
250
- body {
251
- background: var(--bg-color);
252
- color: var(--text-color);
253
- -webkit-print-color-adjust: exact;
254
- print-color-adjust: exact;
255
- }
256
- }
@@ -1,185 +0,0 @@
1
- /**
2
- * Theme configuration persistence.
3
- *
4
- * Stores default theme and registered custom themes in ~/.colamd-themes/config.json
5
- * Uses async file I/O to avoid blocking the event loop.
6
- */
7
-
8
- import { mkdir, readFile, writeFile, access, rename } from "node:fs/promises";
9
- import { statSync, unlink } from "node:fs";
10
- import { join, basename, dirname } from "node:path";
11
- import { fileURLToPath } from "node:url";
12
- import { homedir } from "node:os";
13
-
14
- const CONFIG_DIR = join(homedir(), ".colamd-themes");
15
- const CONFIG_FILE = join(CONFIG_DIR, "config.json");
16
-
17
- /** Maximum allowed CSS file size (1MB) to prevent OOM attacks */
18
- const MAX_CSS_SIZE = 1024 * 1024;
19
-
20
- /** Built-in themes provided by @bytechain.cn/colamd */
21
- export const BUILTIN_THEMES = ["light", "dark", "elegant", "newsprint"] as const;
22
- export type BuiltinTheme = (typeof BUILTIN_THEMES)[number];
23
-
24
- export interface ThemeConfig {
25
- /** Default theme name (built-in or custom) */
26
- defaultTheme: string;
27
- /** Registered custom themes: name → CSS file path */
28
- themes: Record<string, string>;
29
- }
30
-
31
- async function readConfig(): Promise<ThemeConfig> {
32
- try {
33
- await access(CONFIG_FILE);
34
- const data = await readFile(CONFIG_FILE, "utf-8");
35
- return JSON.parse(data) as ThemeConfig;
36
- } catch {
37
- return { defaultTheme: "elegant", themes: {} };
38
- }
39
- }
40
-
41
- async function writeConfig(config: ThemeConfig): Promise<void> {
42
- try {
43
- await access(CONFIG_DIR);
44
- } catch {
45
- await mkdir(CONFIG_DIR, { recursive: true });
46
- }
47
- const tmpPath = `${CONFIG_FILE}.${process.pid}.${Date.now()}.tmp`;
48
- await writeFile(tmpPath, JSON.stringify(config, null, 2), "utf-8");
49
- await rename(tmpPath, CONFIG_FILE);
50
- }
51
-
52
- /** Get the full config. */
53
- export async function getConfig(): Promise<ThemeConfig> {
54
- return readConfig();
55
- }
56
-
57
- /** Set the default theme (built-in name or registered custom name). */
58
- export async function setDefaultTheme(name: string): Promise<void> {
59
- const config = await readConfig();
60
- config.defaultTheme = name;
61
- await writeConfig(config);
62
- }
63
-
64
- /**
65
- * Register a custom theme.
66
- * @param name Unique name (will be prefixed with "custom:" when applied)
67
- * @param cssPath Absolute path to the CSS file
68
- */
69
- export async function registerTheme(name: string, cssPath: string): Promise<void> {
70
- const config = await readConfig();
71
- config.themes[name] = cssPath;
72
- await writeConfig(config);
73
- }
74
-
75
- /** Remove a registered custom theme. */
76
- export async function unregisterTheme(name: string): Promise<boolean> {
77
- const config = await readConfig();
78
- if (!(name in config.themes)) return false;
79
- delete config.themes[name];
80
- await writeConfig(config);
81
- return true;
82
- }
83
-
84
- /**
85
- * Sanitize theme name from file path.
86
- * Removes special characters and limits length to prevent injection.
87
- */
88
- function sanitizeName(path: string): string {
89
- return basename(path, ".css")
90
- .replace(/[^a-zA-Z0-9_-]/g, "-")
91
- .replace(/-+/g, "-")
92
- .slice(0, 50) || "custom";
93
- }
94
-
95
- /**
96
- * Validate CSS file before reading.
97
- * Checks extension, size, and basic content validity.
98
- */
99
- async function validateCssFile(filePath: string): Promise<void> {
100
- // Check file extension
101
- if (!filePath.toLowerCase().endsWith(".css")) {
102
- throw new Error(`Theme file must have .css extension: ${filePath}`);
103
- }
104
-
105
- // Check file size (synchronous stat is acceptable for metadata)
106
- let stats;
107
- try {
108
- stats = statSync(filePath);
109
- } catch {
110
- throw new Error(`Cannot access theme file: ${filePath}`);
111
- }
112
-
113
- if (stats.size > MAX_CSS_SIZE) {
114
- throw new Error(
115
- `Theme file too large (${(stats.size / 1024).toFixed(1)}KB). ` +
116
- `Maximum size: ${(MAX_CSS_SIZE / 1024).toFixed(0)}MB`
117
- );
118
- }
119
-
120
- // Read and validate content
121
- const content = await readFile(filePath, "utf-8");
122
- if (content.length < 10) {
123
- throw new Error(`Theme file appears to be empty or invalid: ${filePath}`);
124
- }
125
- }
126
-
127
- /**
128
- * Resolve a theme name to the arguments needed by the ColaMD renderer.
129
- *
130
- * @param name Theme name. If omitted, uses the configured default.
131
- * @returns { themeName, customCSS? } — themeName is either a built-in name
132
- * or "custom:<name>"; customCSS is the CSS content for custom themes.
133
- */
134
- export async function resolveTheme(name?: string): Promise<{
135
- themeName: string;
136
- customCSS?: string;
137
- }> {
138
- const config = await readConfig();
139
- const resolved = name ?? config.defaultTheme;
140
-
141
- // Check if it's a registered custom theme
142
- if (config.themes[resolved]) {
143
- const cssPath = config.themes[resolved];
144
- await validateCssFile(cssPath);
145
- const customCSS = await readFile(cssPath, "utf-8");
146
- return { themeName: `custom:${resolved}`, customCSS };
147
- }
148
-
149
- // Check if it's a built-in theme
150
- if (BUILTIN_THEMES.includes(resolved as BuiltinTheme)) {
151
- // Prefer our 2.7.8-rc.1 contract CSS when a local theme file exists — our
152
- // variant of a built-in name (e.g. elegant) wins over the renderer's own
153
- // palette. applyTheme(name, css) takes over completely.
154
- for (const base of [process.cwd(), join(dirname(fileURLToPath(import.meta.url)), "..")]) {
155
- const cssPath = join(base, "themes", `${resolved}.css`);
156
- try {
157
- await validateCssFile(cssPath);
158
- return { themeName: resolved, customCSS: await readFile(cssPath, "utf-8") };
159
- } catch {
160
- // No local contract CSS file for this built-in — fall back to the renderer's own.
161
- }
162
- }
163
- return { themeName: resolved };
164
- }
165
-
166
- // Treat as a direct CSS file path
167
- try {
168
- await access(resolved);
169
- } catch {
170
- throw new Error(
171
- `Unknown theme: "${resolved}". Use a built-in theme (${BUILTIN_THEMES.join(", ")}), ` +
172
- `a registered custom theme, or a path to a .css file.`
173
- );
174
- }
175
-
176
- // Validate and read direct CSS file
177
- await validateCssFile(resolved);
178
- const nameFromPath = sanitizeName(resolved);
179
- const customCSS = await readFile(resolved, "utf-8");
180
-
181
- return {
182
- themeName: `custom:${nameFromPath}`,
183
- customCSS,
184
- };
185
- }
@@ -1,12 +0,0 @@
1
- declare module "pdf-parse" {
2
- interface PDFData {
3
- text: string;
4
- numpages: number;
5
- info: Record<string, unknown>;
6
- metadata: Record<string, unknown>;
7
- version: string;
8
- }
9
-
10
- function pdfParse(buffer: Buffer): Promise<PDFData>;
11
- export default pdfParse;
12
- }
@@ -1,84 +0,0 @@
1
- /**
2
- * Shared utility functions for CLI commands.
3
- * Provides consistent file path resolution and error handling.
4
- */
5
-
6
- import { existsSync } from "node:fs";
7
- import { resolve, isAbsolute, basename } from "node:path";
8
-
9
- /**
10
- * Resolve input file path with validation.
11
- * Converts relative paths to absolute and checks existence.
12
- *
13
- * @param input File path provided by user
14
- * @returns Resolved absolute path
15
- * @throws Error if file does not exist
16
- */
17
- export function resolveInputPath(input: string): string {
18
- const path = isAbsolute(input) ? input : resolve(process.cwd(), input);
19
- if (!existsSync(path)) {
20
- throw new Error(`File not found: ${path}`);
21
- }
22
- return path;
23
- }
24
-
25
- /**
26
- * Resolve output file path with sensible defaults.
27
- * If output path is not specified, derives it from input path by replacing extension.
28
- *
29
- * @param inputPath Resolved absolute input path
30
- * @param output Optional explicit output path
31
- * @param ext Target extension (default: ".html")
32
- * @returns Resolved absolute output path
33
- */
34
- export function resolveOutputPath(inputPath: string, output?: string, ext = ".html"): string {
35
- if (output) {
36
- return isAbsolute(output) ? output : resolve(process.cwd(), output);
37
- }
38
- return inputPath.replace(/\.md$/i, ext);
39
- }
40
-
41
- /**
42
- * Get base filename without extension for use as document title.
43
- *
44
- * @param filePath Absolute or relative file path
45
- * @returns Filename without extension
46
- */
47
- export function getDocumentTitle(filePath: string): string {
48
- return basename(filePath, ".md");
49
- }
50
-
51
- /**
52
- * Standard error codes for CLI operations.
53
- * Enables consistent error handling across all commands.
54
- */
55
- export enum ErrorCode {
56
- /** Input file not found */
57
- FILE_NOT_FOUND = 1,
58
- /** Theme resolution failed */
59
- THEME_ERROR = 2,
60
- /** Rendering/export failed */
61
- RENDER_ERROR = 3,
62
- /** Invalid arguments or configuration */
63
- INVALID_INPUT = 4,
64
- /** Unknown/unexpected error */
65
- UNKNOWN = 99,
66
- }
67
-
68
- /**
69
- * Custom error class for CLI operations with structured error codes.
70
- * Replaces direct process.exit() calls for better error propagation.
71
- */
72
- export class CLIError extends Error {
73
- constructor(
74
- message: string,
75
- public code: ErrorCode = ErrorCode.UNKNOWN,
76
- public cause?: Error
77
- ) {
78
- super(message);
79
- this.name = "CLIError";
80
- if (cause) {
81
- this.stack = `${this.message}\nCaused by: ${cause.stack}`;
82
- }
83
- }
84
- }
@@ -1,118 +0,0 @@
1
- /**
2
- * Shared document paradigm utilities used by all extractors.
3
- * Extracted from url-extractor, docx-extractor, and pdf-extractor
4
- * to eliminate code duplication.
5
- */
6
-
7
- import type { ThemeStyle } from "../models.js";
8
- import type { DocumentStyleParadigm } from "../docx-style-paradigm.js";
9
- import {
10
- ACADEMIC_PAPER_DEFAULTS,
11
- validateDocumentStyle,
12
- } from "../docx-style-paradigm.js";
13
-
14
- export function themeToParadigm(theme: ThemeStyle): DocumentStyleParadigm {
15
- const baseFontSize = parseInt(theme.typography.baseFontSize) || 16;
16
-
17
- return {
18
- pageBackground: theme.palette.bgColor,
19
- headings: {
20
- h1: {
21
- fontFamily: theme.seedTypography.fontHeading,
22
- fontSize: 16 * theme.seedTypography.fontScaleH1,
23
- fontWeight: theme.typography.headingWeight,
24
- color: theme.palette.headingColor,
25
- lineHeight: theme.seedTypography.lineHeightHeading,
26
- },
27
- h2: {
28
- fontFamily: theme.seedTypography.fontHeading,
29
- fontSize: 16 * theme.seedTypography.fontScaleH2,
30
- fontWeight: Math.max(600, theme.typography.headingWeight - 100),
31
- color: theme.palette.headingColor,
32
- lineHeight: theme.seedTypography.lineHeightHeading,
33
- },
34
- h3: {
35
- fontFamily: theme.seedTypography.fontHeading,
36
- fontSize: 16 * theme.seedTypography.fontScaleH3,
37
- fontWeight: 600,
38
- color: theme.palette.headingColor,
39
- lineHeight: theme.seedTypography.lineHeightHeading + 0.05,
40
- },
41
- h4: {
42
- fontFamily: theme.seedTypography.fontHeading,
43
- fontSize: 16 * theme.seedTypography.fontScaleH4,
44
- fontWeight: 600,
45
- color: theme.palette.textMuted,
46
- lineHeight: theme.seedTypography.lineHeightHeading + 0.05,
47
- },
48
- h5: {
49
- fontFamily: theme.seedTypography.fontHeading,
50
- fontSize: 16 * theme.seedTypography.fontScaleH5,
51
- fontWeight: 500,
52
- color: theme.palette.textMuted,
53
- lineHeight: theme.seedTypography.lineHeightBody - 0.25,
54
- },
55
- h6: {
56
- fontFamily: theme.seedTypography.fontHeading,
57
- fontSize: 16 * theme.seedTypography.fontScaleH6,
58
- fontWeight: 500,
59
- color: theme.palette.textMuted,
60
- lineHeight: theme.seedTypography.lineHeightBody - 0.2,
61
- },
62
- },
63
- body: {
64
- fontFamily: theme.seedTypography.fontBody,
65
- fontSize: baseFontSize,
66
- fontWeight: 400,
67
- color: theme.palette.textColor,
68
- lineHeight: theme.typography.baseLineHeight,
69
- },
70
- blockquote: {
71
- fontFamily: theme.seedTypography.fontBody,
72
- fontSize: Math.max(13, baseFontSize - 2),
73
- fontWeight: 400,
74
- color: theme.palette.textMuted,
75
- lineHeight: theme.typography.baseLineHeight - 0.15,
76
- borderLeftColor: theme.palette.blockquoteBorder,
77
- bgColor: theme.palette.blockquoteBg,
78
- },
79
- codeBlock: {
80
- fontFamily: theme.typography.codeFontFamily,
81
- fontSize: Math.max(12, baseFontSize - 3),
82
- fontWeight: 400,
83
- color: theme.palette.codeBlockText || theme.palette.textColor,
84
- lineHeight: theme.seedTypography.lineHeightCode,
85
- bgColor: theme.palette.codeBlockBg,
86
- },
87
- inlineCode: {
88
- fontSize: Math.max(12, baseFontSize - 3),
89
- color: theme.palette.accentColor,
90
- bgColor: theme.palette.codeBg,
91
- },
92
- table: {
93
- headerFontFamily: theme.seedTypography.fontHeading,
94
- headerFontSize: Math.max(12, baseFontSize - 3),
95
- headerFontWeight: 700,
96
- headerColor: "#ffffff",
97
- headerBgColor: theme.palette.tableHeaderBg,
98
- headerLineHeight: 1.4,
99
- cellFontFamily: theme.seedTypography.fontBody,
100
- cellFontSize: Math.max(11, baseFontSize - 4),
101
- cellColor: theme.palette.textColor,
102
- cellBgColor: theme.palette.bgColor,
103
- cellLineHeight: 1.5,
104
- cellPadding: "8px 12px",
105
- borderColor: theme.palette.borderColor,
106
- borderWidth: "1px",
107
- },
108
- accentColor: theme.palette.accentColor,
109
- mutedTextColor: theme.palette.textMuted,
110
- selectionBg: theme.palette.selectionBg,
111
- };
112
- }
113
-
114
- export function logExtractorWarnings(extractorName: string, issues: { element: string; message: string }[]): void {
115
- for (const issue of issues) {
116
- console.warn(`[${extractorName}] ${issue.element}: ${issue.message}`);
117
- }
118
- }