@zzclub/pipeline 0.13.2 → 0.16.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 (60) hide show
  1. package/ARTICLE-THEMES.md +86 -0
  2. package/LICENSE +21 -0
  3. package/README.md +182 -788
  4. package/THIRD-PARTY-NOTICES.md +25 -0
  5. package/package.json +6 -2
  6. package/src/adapter-types.ts +3 -0
  7. package/src/adapters/builtin-markdown-renderer.ts +1 -0
  8. package/src/article-theme.test.ts +87 -0
  9. package/src/article-theme.ts +115 -0
  10. package/src/commands/abandon.test.ts +1 -1
  11. package/src/commands/article-theme.ts +16 -0
  12. package/src/commands/checkpoint.test.ts +2 -2
  13. package/src/commands/ingest-handoff.ts +6 -0
  14. package/src/commands/init.ts +10 -0
  15. package/src/commands/prepare.ts +5 -1
  16. package/src/commands/publish.ts +4 -0
  17. package/src/commands/reset.ts +2 -0
  18. package/src/commands/wechat-export.ts +4 -0
  19. package/src/commands/wechat-preview.ts +35 -0
  20. package/src/plugins.ts +2 -0
  21. package/src/providers/index.ts +17 -2
  22. package/src/providers/publish-core.ts +32 -1
  23. package/src/providers/wechat.test.ts +12 -0
  24. package/src/providers/wechat.ts +60 -39
  25. package/src/render-branding.ts +1 -1
  26. package/src/schema/config.ts +12 -0
  27. package/src/schema/state.ts +5 -0
  28. package/src/wechat-preview/assets/browser-dist/.vite/manifest.json +21 -1
  29. package/src/wechat-preview/assets/browser-dist/assets/style-C9-YtTIm.css +1 -0
  30. package/src/wechat-preview/assets/browser-dist/assets/wechat-renderer-D1jHoaM2.js +624 -0
  31. package/src/wechat-preview/assets/browser-dist/editor-export.js +1 -512
  32. package/src/wechat-preview/assets/browser-dist/src/wechat-preview/browser/studio/studio.html +293 -0
  33. package/src/wechat-preview/assets/browser-dist/studio.js +332 -0
  34. package/src/wechat-preview/browser/editor-export.ts +17 -2
  35. package/src/wechat-preview/browser/highlight.ts +2 -0
  36. package/src/wechat-preview/browser/studio/color-palettes.test.ts +31 -0
  37. package/src/wechat-preview/browser/studio/color-palettes.ts +115 -0
  38. package/src/wechat-preview/browser/studio/studio.css +638 -0
  39. package/src/wechat-preview/browser/studio/studio.html +292 -0
  40. package/src/wechat-preview/browser/studio/studio.ts +721 -0
  41. package/src/wechat-preview/browser/studio/syntax-presets.test.ts +74 -0
  42. package/src/wechat-preview/browser/studio/syntax-presets.ts +590 -0
  43. package/src/wechat-preview/fixtures/all-nodes.md +38 -0
  44. package/src/wechat-preview/index.ts +36 -5
  45. package/src/wechat-preview/plugins/builtin-badge.ts +152 -0
  46. package/src/wechat-preview/plugins/builtin-callout.ts +261 -0
  47. package/src/wechat-preview/plugins/builtin-highlight.ts +46 -0
  48. package/src/wechat-preview/plugins/builtin-kbd.ts +133 -0
  49. package/src/wechat-preview/plugins/index.test.ts +313 -0
  50. package/src/wechat-preview/plugins/index.ts +6 -0
  51. package/src/wechat-preview/plugins/registry.ts +62 -0
  52. package/src/wechat-preview/plugins/types.ts +31 -0
  53. package/src/wechat-preview/server/http.ts +160 -3
  54. package/src/wechat-preview/server/paths.ts +2 -2
  55. package/src/wechat-preview/server/singleton.ts +14 -12
  56. package/src/wechat-preview/server/studio.test.ts +92 -0
  57. package/src/wechat-preview/server/ui.ts +1 -0
  58. package/src/wechat-preview/wechat-export.integration.test.ts +9 -1
  59. package/src/wechat-preview/wechat-renderer.ts +72 -7
  60. package/src/workflow.test.ts +1 -0
@@ -0,0 +1,74 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { SYNTAX_CATEGORIES, buildCombinedPresetsCss } from "./syntax-presets";
3
+
4
+ describe("syntax presets module", () => {
5
+ test("defines comprehensive markdown categories including h1, p, table, code, callout, etc.", () => {
6
+ const keys = SYNTAX_CATEGORIES.map((c) => c.key);
7
+ expect(keys).toContain("h1");
8
+ expect(keys).toContain("h2");
9
+ expect(keys).toContain("h3");
10
+ expect(keys).toContain("h4");
11
+ expect(keys).toContain("p");
12
+ expect(keys).toContain("strong");
13
+ expect(keys).toContain("del");
14
+ expect(keys).toContain("mark");
15
+ expect(keys).toContain("inline-code");
16
+ expect(keys).toContain("code-block");
17
+ expect(keys).toContain("blockquote");
18
+ expect(keys).toContain("list");
19
+ expect(keys).toContain("task-list");
20
+ expect(keys).toContain("divider");
21
+ expect(keys).toContain("link");
22
+ expect(keys).toContain("image");
23
+ expect(keys).toContain("table");
24
+ expect(keys).toContain("callout");
25
+ expect(keys).toContain("kbd");
26
+ expect(keys).toContain("badge");
27
+ });
28
+
29
+ test("h2 category includes pillar, bottom-line, gradient-pill, contrast-green, card-tag, and plain presets", () => {
30
+ const h2Cat = SYNTAX_CATEGORIES.find((c) => c.key === "h2");
31
+ expect(h2Cat).toBeDefined();
32
+ const presetIds = h2Cat!.presets!.map((p) => p.id);
33
+ expect(presetIds).toContain("pillar");
34
+ expect(presetIds).toContain("bottom-line");
35
+ expect(presetIds).toContain("gradient-pill");
36
+ expect(presetIds).toContain("contrast-green");
37
+ expect(presetIds).toContain("card-tag");
38
+ expect(presetIds).toContain("plain");
39
+ });
40
+
41
+ test("single-syntax categories provide baseline CSS and descriptions", () => {
42
+ const h1Cat = SYNTAX_CATEGORIES.find((c) => c.key === "h1");
43
+ expect(h1Cat?.css).toContain(".milkdown .editor h1");
44
+ expect(h1Cat?.description).toBeTruthy();
45
+
46
+ const tableCat = SYNTAX_CATEGORIES.find((c) => c.key === "table");
47
+ expect(tableCat?.css).toContain(".milkdown .editor table");
48
+
49
+ const codeCat = SYNTAX_CATEGORIES.find((c) => c.key === "code-block");
50
+ expect(codeCat?.css).toContain(".milkdown .editor pre");
51
+ });
52
+
53
+ test("buildCombinedPresetsCss generates valid aggregated CSS blocks", () => {
54
+ const css = buildCombinedPresetsCss({
55
+ h2: "gradient-pill",
56
+ h3: "left-bar",
57
+ blockquote: "tint-card",
58
+ divider: "dashed",
59
+ });
60
+
61
+ expect(css).toContain("linear-gradient");
62
+ expect(css).toContain(".milkdown .editor h2");
63
+ expect(css).toContain(".milkdown .editor h3");
64
+ expect(css).toContain(".milkdown .editor blockquote");
65
+ expect(css).toContain(".milkdown .editor hr");
66
+ expect(css).toContain("dashed");
67
+ });
68
+
69
+ test("buildCombinedPresetsCss falls back to default preset when key missing", () => {
70
+ const css = buildCombinedPresetsCss({});
71
+ expect(css).toContain(".milkdown .editor h2");
72
+ expect(css).toContain("border-left: 2.5px solid");
73
+ });
74
+ });
@@ -0,0 +1,590 @@
1
+ export interface SyntaxPreset {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ css: string;
6
+ }
7
+
8
+ export interface SyntaxCategory {
9
+ key: string;
10
+ label: string;
11
+ icon: string;
12
+ defaultPresetId?: string;
13
+ presets?: SyntaxPreset[];
14
+ css?: string;
15
+ description?: string;
16
+ }
17
+
18
+ export const SYNTAX_CATEGORIES: SyntaxCategory[] = [
19
+ // ── 标题体系 ──────────────────────────────────────────────
20
+ {
21
+ key: "h1",
22
+ label: "一级标题 (H1)",
23
+ icon: "👑",
24
+ description: "文章大标题:26px,加粗,紧贴后续正文,无边框背景修饰",
25
+ css: `.milkdown .editor h1 {
26
+ margin: 2.45em 0 0.85em;
27
+ color: var(--wx-h2-color, #1f1b1c);
28
+ font-size: 26px;
29
+ font-weight: 700;
30
+ line-height: 1.38;
31
+ letter-spacing: -0.01em;
32
+ }`,
33
+ },
34
+ {
35
+ key: "h2",
36
+ label: "二级标题 (H2)",
37
+ icon: "📌",
38
+ defaultPresetId: "pillar",
39
+ presets: [
40
+ {
41
+ id: "pillar",
42
+ name: "左侧呼吸柱(推荐)",
43
+ description: "2.5px 品牌色垂直微线 + 9px 内边距 + 2.8em 非对称呼吸留白",
44
+ css: `.milkdown .editor h2 {
45
+ margin: 2.8em 0 0.65em;
46
+ padding-left: 9px;
47
+ border-left: 2.5px solid var(--wx-brand-accent, #ca6093);
48
+ border-bottom: none;
49
+ background: none;
50
+ font-size: 20px;
51
+ font-weight: 700;
52
+ line-height: 1.36;
53
+ letter-spacing: 0.01em;
54
+ color: var(--wx-h2-color, #1f1b1c);
55
+ }`,
56
+ },
57
+ {
58
+ id: "bottom-line",
59
+ name: "极浅底部分割",
60
+ description: "1px 细底线 + 紧凑下边距 + 3.0em 开阔章节上留白",
61
+ css: `.milkdown .editor h2 {
62
+ margin: 3.0em 0 0.8em;
63
+ padding-bottom: 0.45em;
64
+ padding-left: 0;
65
+ border-left: none;
66
+ border-bottom: 1px solid var(--wx-divider-color, #ebe6e8);
67
+ background: none;
68
+ font-size: 20px;
69
+ font-weight: 700;
70
+ line-height: 1.35;
71
+ letter-spacing: 0.015em;
72
+ color: var(--wx-h2-color, #1f1b1c);
73
+ }`,
74
+ },
75
+ {
76
+ id: "gradient-pill",
77
+ name: "渐变微衬底(背景色块)",
78
+ description: "水平柔和渐变微底色 + 左侧 3px 标柱 + 4px 圆角",
79
+ css: `.milkdown .editor h2 {
80
+ margin: 2.8em 0 0.7em;
81
+ padding: 7px 14px;
82
+ border-left: 3px solid var(--wx-brand-accent, #ca6093);
83
+ border-bottom: none;
84
+ background: linear-gradient(90deg, rgba(202, 96, 147, 0.09) 0%, rgba(202, 96, 147, 0.01) 100%);
85
+ border-radius: 4px;
86
+ font-size: 19px;
87
+ font-weight: 700;
88
+ line-height: 1.38;
89
+ letter-spacing: 0.01em;
90
+ color: var(--wx-h2-color, #1f1b1c);
91
+ }`,
92
+ },
93
+ {
94
+ id: "contrast-green",
95
+ name: "反差色绿底(白字反色块)",
96
+ description: "高级生态绿底色 + 纯白文字 + 4px 圆角 + 8px 14px 饱满内边距",
97
+ css: `.milkdown .editor h2 {
98
+ margin: 2.8em 0 0.8em;
99
+ padding: 8px 14px;
100
+ background-color: #1f7a4d;
101
+ border-left: none;
102
+ border-bottom: none;
103
+ border-radius: 4px;
104
+ color: #ffffff;
105
+ font-size: 18px;
106
+ font-weight: 700;
107
+ line-height: 1.36;
108
+ letter-spacing: 0.02em;
109
+ }
110
+ .milkdown .editor h2 span {
111
+ color: #ffffff;
112
+ }`,
113
+ },
114
+ {
115
+ id: "card-tag",
116
+ name: "温润小卡片",
117
+ description: "包裹式浅色暖底 + 居左微内边距 + 紧实字号",
118
+ css: `.milkdown .editor h2 {
119
+ margin: 2.7em 0 0.75em;
120
+ padding: 6px 12px;
121
+ border-left: none;
122
+ border-bottom: none;
123
+ background-color: var(--wx-soft-surface, #faf8f9);
124
+ border-radius: 4px;
125
+ font-size: 18px;
126
+ font-weight: 700;
127
+ line-height: 1.4;
128
+ letter-spacing: 0.01em;
129
+ color: var(--wx-h2-color, #1f1b1c);
130
+ }`,
131
+ },
132
+ {
133
+ id: "plain",
134
+ name: "经典纯文字",
135
+ description: "纯字号与字重,无边框与背景修饰",
136
+ css: `.milkdown .editor h2 {
137
+ margin: 2.7em 0 0.7em;
138
+ padding: 0;
139
+ border-left: none;
140
+ border-bottom: none;
141
+ background: none;
142
+ font-size: 20px;
143
+ font-weight: 700;
144
+ line-height: 1.38;
145
+ letter-spacing: 0.005em;
146
+ color: var(--wx-h2-color, #1f1b1c);
147
+ }`,
148
+ },
149
+ ],
150
+ },
151
+ {
152
+ key: "h3",
153
+ label: "三级标题 (H3)",
154
+ icon: "🔹",
155
+ defaultPresetId: "plain",
156
+ presets: [
157
+ {
158
+ id: "plain",
159
+ name: "经典纯文字(默认)",
160
+ description: "16px,深灰次级字色,font-weight 700",
161
+ css: `.milkdown .editor h3 {
162
+ margin: 1.55em 0 0.4em;
163
+ padding-left: 0;
164
+ border-left: none;
165
+ background: none;
166
+ font-size: 16px;
167
+ font-weight: 700;
168
+ line-height: 1.48;
169
+ letter-spacing: 0.02em;
170
+ color: var(--wx-h3-color, #5c5658);
171
+ }`,
172
+ },
173
+ {
174
+ id: "left-bar",
175
+ name: "左侧微短线",
176
+ description: "2px 细色柱 + 7px 内边距",
177
+ css: `.milkdown .editor h3 {
178
+ margin: 1.6em 0 0.45em;
179
+ padding-left: 7px;
180
+ border-left: 2px solid var(--wx-brand-accent, #ca6093);
181
+ background: none;
182
+ font-size: 16px;
183
+ font-weight: 700;
184
+ line-height: 1.45;
185
+ letter-spacing: 0.015em;
186
+ color: var(--wx-h3-color, #5c5658);
187
+ }`,
188
+ },
189
+ {
190
+ id: "badge",
191
+ name: "胶囊小微标",
192
+ description: "行内微底色衬托,紧凑突出",
193
+ css: `.milkdown .editor h3 {
194
+ display: inline-block;
195
+ margin: 1.6em 0 0.45em;
196
+ padding: 3px 8px;
197
+ border-left: none;
198
+ background-color: var(--wx-soft-surface, #faf8f9);
199
+ border-radius: 3px;
200
+ font-size: 15px;
201
+ font-weight: 700;
202
+ line-height: 1.4;
203
+ color: var(--wx-brand-ink, #ca6093);
204
+ }`,
205
+ },
206
+ ],
207
+ },
208
+ {
209
+ key: "h4",
210
+ label: "四级/次级标题 (H4-H6)",
211
+ icon: "🔸",
212
+ description: "更低层级小标题:16px 次深灰,1.55 行高,紧贴段落",
213
+ css: `.milkdown .editor h4,
214
+ .milkdown .editor h5,
215
+ .milkdown .editor h6 {
216
+ margin: 1.45em 0 0.45em;
217
+ color: var(--wx-h3-color, #5c5658);
218
+ font-size: 16px;
219
+ font-weight: 700;
220
+ line-height: 1.55;
221
+ letter-spacing: 0.02em;
222
+ }`,
223
+ },
224
+
225
+ // ── 正文与行内样式 ─────────────────────────────────────────
226
+ {
227
+ key: "p",
228
+ label: "正文段落 (Paragraph)",
229
+ icon: "📄",
230
+ description: "标准段落:两端对齐,排版行高 1.84,下边距 1.2em",
231
+ css: `.milkdown .editor p {
232
+ margin: 0 0 1.2em;
233
+ color: var(--wx-body-color, #292526);
234
+ font-size: 16px;
235
+ line-height: 1.84;
236
+ letter-spacing: 0.012em;
237
+ text-align: justify;
238
+ }`,
239
+ },
240
+ {
241
+ key: "strong",
242
+ label: "加粗与斜体 (Bold / Italic)",
243
+ icon: "🖋️",
244
+ description: "重点强调:深沉字重 700 与斜体字色跟随",
245
+ css: `.milkdown .editor strong,
246
+ .milkdown .editor b {
247
+ color: var(--wx-body-color, #292526);
248
+ font-weight: 700;
249
+ }
250
+ .milkdown .editor em,
251
+ .milkdown .editor i {
252
+ color: var(--wx-body-color, #292526);
253
+ font-style: italic;
254
+ }`,
255
+ },
256
+ {
257
+ key: "del",
258
+ label: "删除线 (Strikethrough)",
259
+ icon: "✂️",
260
+ description: "废弃内容:静音深灰与中线划除",
261
+ css: `.milkdown .editor s,
262
+ .milkdown .editor del {
263
+ color: var(--wx-muted-color, #6f696b);
264
+ text-decoration: line-through;
265
+ text-decoration-color: var(--wx-muted-color, #6f696b);
266
+ }`,
267
+ },
268
+ {
269
+ key: "mark",
270
+ label: "文本高亮 (Mark ==...==)",
271
+ icon: "🖍️",
272
+ description: "马克笔荧光衬底:柔和品牌色淡背景 + 1.5px 下划强调线",
273
+ css: `.milkdown .editor mark {
274
+ margin: 0 2px;
275
+ padding: 2px 5px;
276
+ background-color: rgba(202, 96, 147, 0.16);
277
+ color: var(--wx-body-color, #292526);
278
+ border-radius: 3px;
279
+ border-bottom: 1.5px solid var(--wx-brand-accent, #ca6093);
280
+ }`,
281
+ },
282
+ {
283
+ key: "inline-code",
284
+ label: "行内代码 (Inline Code)",
285
+ icon: "💻",
286
+ description: "行内等宽代码:14px,等宽字体,浅色微边框衬底",
287
+ css: `.milkdown .editor code:not(pre code),
288
+ .milkdown .editor [data-wechat-node="inline-code"] {
289
+ margin: 0 2px;
290
+ padding: 2px 6px;
291
+ border: 1px solid var(--wx-divider-color, #ded9db);
292
+ border-radius: 4px;
293
+ background-color: var(--wx-soft-surface, #f7f5f6);
294
+ color: #4d484a;
295
+ font-family: Menlo, Monaco, Consolas, monospace;
296
+ font-size: 14px;
297
+ line-height: 1.55;
298
+ }`,
299
+ },
300
+
301
+ // ── 容器与代码块 ─────────────────────────────────────────
302
+ {
303
+ key: "code-block",
304
+ label: "代码块 (Code Block)",
305
+ icon: "📟",
306
+ description: "多行代码框:浅灰底色,6px 圆角,深色等宽字符与水平横滚",
307
+ css: `.milkdown .editor pre {
308
+ margin: 1.55em 0;
309
+ padding: 14px 16px;
310
+ border: 1px solid var(--wx-divider-color, #e2dcdf);
311
+ border-radius: 6px;
312
+ background-color: var(--wx-soft-surface, #f8fafc);
313
+ color: var(--wx-body-color, #292526);
314
+ font-family: Menlo, Monaco, Consolas, monospace;
315
+ font-size: 13px;
316
+ line-height: 1.72;
317
+ overflow-x: auto;
318
+ }
319
+ .milkdown .editor pre code {
320
+ font-family: inherit;
321
+ font-size: inherit;
322
+ }`,
323
+ },
324
+ {
325
+ key: "blockquote",
326
+ label: "引用块 (Blockquote)",
327
+ icon: "💬",
328
+ defaultPresetId: "left-border",
329
+ presets: [
330
+ {
331
+ id: "left-border",
332
+ name: "经典左单线(默认)",
333
+ description: "3px 品牌色微边框 + 浅灰底色 + 4px 右圆角",
334
+ css: `.milkdown .editor blockquote {
335
+ margin: 1.6em 0;
336
+ padding: 10px 16px;
337
+ border: none;
338
+ border-left: 3px solid var(--wx-blockquote-border, #ca6093);
339
+ border-radius: 0 4px 4px 0;
340
+ background-color: var(--wx-soft-surface, #fbfafb);
341
+ }`,
342
+ },
343
+ {
344
+ id: "tint-card",
345
+ name: "全包裹微卡片",
346
+ description: "四边 1px 极细边框 + 6px 圆角,整体成盒",
347
+ css: `.milkdown .editor blockquote {
348
+ margin: 1.6em 0;
349
+ padding: 12px 18px;
350
+ border: 1px solid var(--wx-divider-color, #ebe6e8);
351
+ border-radius: 6px;
352
+ background-color: var(--wx-soft-surface, #fbfafb);
353
+ }`,
354
+ },
355
+ {
356
+ id: "minimal-indent",
357
+ name: "极简纯缩进",
358
+ description: "无底色,仅左侧细灰线与自然缩进",
359
+ css: `.milkdown .editor blockquote {
360
+ margin: 1.5em 0;
361
+ padding: 6px 0 6px 16px;
362
+ border: none;
363
+ border-left: 2px solid rgba(0, 0, 0, 0.15);
364
+ border-radius: 0;
365
+ background: none;
366
+ }`,
367
+ },
368
+ ],
369
+ },
370
+ {
371
+ key: "list",
372
+ label: "列表 (Unordered & Ordered)",
373
+ icon: "📋",
374
+ description: "标准列表:自然外缩进,1.78 行高,小圆点与阿拉伯数字序号",
375
+ css: `.milkdown .editor ul,
376
+ .milkdown .editor ol {
377
+ margin: 1.05em 0 1.3em;
378
+ padding-left: 1.6em;
379
+ color: var(--wx-body-color, #292526);
380
+ }
381
+ .milkdown .editor li {
382
+ margin: 0.4em 0;
383
+ line-height: 1.78;
384
+ }
385
+ .milkdown .editor li p {
386
+ margin: 0.15em 0;
387
+ }`,
388
+ },
389
+ {
390
+ key: "task-list",
391
+ label: "任务清单 (Task List)",
392
+ icon: "☑️",
393
+ description: "待办复选框:品牌色勾选标记,紧凑缩进",
394
+ css: `.milkdown .editor [data-wechat-node="task-list-item"] {
395
+ list-style: none;
396
+ }
397
+ .milkdown .editor [data-wechat-node="task-marker"] {
398
+ display: inline-block;
399
+ min-width: 1.35em;
400
+ color: var(--wx-brand-accent, #ca6093);
401
+ font-weight: 700;
402
+ }`,
403
+ },
404
+ {
405
+ key: "divider",
406
+ label: "分割线 (Divider)",
407
+ icon: "➖",
408
+ defaultPresetId: "hairline",
409
+ presets: [
410
+ {
411
+ id: "hairline",
412
+ name: "极细实线(默认)",
413
+ description: "1px 浅灰色水平实线",
414
+ css: `.milkdown .editor hr {
415
+ margin: 2.4em 0;
416
+ width: 100%;
417
+ border: none;
418
+ border-top: 1px solid var(--wx-divider-color, #e2dcdf);
419
+ }`,
420
+ },
421
+ {
422
+ id: "dashed",
423
+ name: "极简虚线",
424
+ description: "1px 虚线划分节奏",
425
+ css: `.milkdown .editor hr {
426
+ margin: 2.4em 0;
427
+ width: 100%;
428
+ border: none;
429
+ border-top: 1px dashed var(--wx-divider-color, #d4cecf);
430
+ }`,
431
+ },
432
+ {
433
+ id: "short-center",
434
+ name: "居中短呼吸线",
435
+ description: "宽度 36%,居中呼吸线",
436
+ css: `.milkdown .editor hr {
437
+ margin: 2.8em auto;
438
+ width: 36%;
439
+ border: none;
440
+ border-top: 1px solid var(--wx-divider-color, #e2dcdf);
441
+ }`,
442
+ },
443
+ ],
444
+ },
445
+
446
+ // ── 媒体、链接与表格 ─────────────────────────────────────────
447
+ {
448
+ key: "link",
449
+ label: "超链接与附注 (Links & Ref)",
450
+ icon: "🔗",
451
+ description: "文章链接与文末尾注:品牌色下划细线与上标数字角标",
452
+ css: `.milkdown .editor a,
453
+ .milkdown .editor [data-wechat-node="external-link"] {
454
+ border-bottom: 1px solid var(--wx-brand-accent, #ca6093);
455
+ color: var(--wx-brand-accent, #ca6093);
456
+ text-decoration: none;
457
+ }
458
+ .milkdown .editor [data-wechat-node="link-marker"] {
459
+ margin-left: 2px;
460
+ color: var(--wx-brand-accent, #ca6093);
461
+ font-size: 11px;
462
+ vertical-align: super;
463
+ }`,
464
+ },
465
+ {
466
+ key: "image",
467
+ label: "图片与图注 (Image & Caption)",
468
+ icon: "🖼️",
469
+ description: "文章插图与下方图注:居中包裹,4px 微圆角与 13px 浅灰居中说明",
470
+ css: `.milkdown .editor figure,
471
+ .milkdown .editor [data-wechat-node="image-block"] {
472
+ margin: 1.65em 0 1.8em;
473
+ text-align: center;
474
+ }
475
+ .milkdown .editor img {
476
+ display: block;
477
+ max-width: 100%;
478
+ height: auto;
479
+ margin: 0 auto;
480
+ border-radius: 4px;
481
+ }
482
+ .milkdown .editor figcaption,
483
+ .milkdown .editor [data-wechat-node="image-caption"] {
484
+ margin-top: 8px;
485
+ color: var(--wx-muted-color, #6f696b);
486
+ font-size: 13px;
487
+ line-height: 1.62;
488
+ text-align: center;
489
+ }`,
490
+ },
491
+ {
492
+ key: "table",
493
+ label: "数据表格 (Table)",
494
+ icon: "📊",
495
+ description: "数据对比表格:折叠边框,浅暖色表头背景,紧凑对齐内边距",
496
+ css: `.milkdown .editor table {
497
+ width: 100%;
498
+ margin: 1.5em 0;
499
+ border-collapse: collapse;
500
+ table-layout: fixed;
501
+ font-size: 14px;
502
+ line-height: 1.62;
503
+ }
504
+ .milkdown .editor th,
505
+ .milkdown .editor td {
506
+ padding: 8px 10px;
507
+ border: 1px solid var(--wx-divider-color, #e2dcdf);
508
+ text-align: left;
509
+ }
510
+ .milkdown .editor th {
511
+ background-color: var(--wx-soft-surface, #faf8f9);
512
+ color: var(--wx-h2-color, #1f1b1c);
513
+ font-weight: 700;
514
+ }`,
515
+ },
516
+
517
+ // ── 扩展组件 ───────────────────────────────────────────────
518
+ {
519
+ key: "callout",
520
+ label: "扩展:提示卡片 (Callout Alert)",
521
+ icon: "💡",
522
+ description: "高亮提示盒:左侧品牌色标线,浅底色包裹与加粗小标",
523
+ css: `.milkdown .editor [data-wechat-node="callout"] {
524
+ margin: 1.4em 0;
525
+ padding: 12px 16px;
526
+ border-left: 3px solid var(--wx-brand-accent, #ca6093);
527
+ border-radius: 0 6px 6px 0;
528
+ background-color: var(--wx-soft-surface, #fbfafb);
529
+ }
530
+ .milkdown .editor [data-wechat-node="callout-header"],
531
+ .milkdown .editor .wechat-callout-header {
532
+ font-weight: 700;
533
+ font-size: 14px;
534
+ margin-bottom: 6px;
535
+ color: var(--wx-h2-color, #1f1b1c);
536
+ }`,
537
+ },
538
+ {
539
+ key: "kbd",
540
+ label: "扩展:键盘键帽 (Kbd Key)",
541
+ icon: "⌨️",
542
+ description: "按键徽章:立体立体阴影边框,12px 等宽字体",
543
+ css: `.milkdown .editor kbd,
544
+ .milkdown .editor [data-wechat-node="kbd"] {
545
+ display: inline-block;
546
+ margin: 0 3px;
547
+ padding: 2px 6px;
548
+ font-family: Menlo, Monaco, Consolas, monospace;
549
+ font-size: 12px;
550
+ font-weight: 600;
551
+ color: #374151;
552
+ background-color: #f3f4f6;
553
+ border: 1px solid #d1d5db;
554
+ border-bottom: 2px solid #9ca3af;
555
+ border-radius: 4px;
556
+ line-height: 1.3;
557
+ }`,
558
+ },
559
+ {
560
+ key: "badge",
561
+ label: "扩展:彩色徽章 (Badge Pill)",
562
+ icon: "🏷️",
563
+ description: "胶囊微标签:全圆角胶囊底色,紧凑居中",
564
+ css: `.milkdown .editor [data-wechat-node="badge"] {
565
+ display: inline-block;
566
+ margin: 0 2px;
567
+ padding: 2px 8px;
568
+ font-size: 12px;
569
+ font-weight: 600;
570
+ border-radius: 9999px;
571
+ line-height: 1.3;
572
+ background-color: rgba(202, 96, 147, 0.12);
573
+ color: var(--wx-brand-accent, #ca6093);
574
+ }`,
575
+ },
576
+ ];
577
+
578
+ export function buildCombinedPresetsCss(selected: Record<string, string>): string {
579
+ const cssBlocks: string[] = [];
580
+ for (const cat of SYNTAX_CATEGORIES) {
581
+ if (cat.presets && cat.presets.length > 1) {
582
+ const selectedId = selected[cat.key] || cat.defaultPresetId || cat.presets[0].id;
583
+ const preset = cat.presets.find((p) => p.id === selectedId) || cat.presets[0];
584
+ if (preset && preset.css.trim()) {
585
+ cssBlocks.push(`/* [${cat.label}] - ${preset.name} */\n${preset.css.trim()}`);
586
+ }
587
+ }
588
+ }
589
+ return cssBlocks.join("\n\n");
590
+ }
@@ -66,3 +66,41 @@ const article: Article = {
66
66
  </div>
67
67
 
68
68
  文末再放一个[资料链接](https://example.org/reference),用于检查编号脚注与“相关链接”。
69
+
70
+ ##### 五级标题:补充材料
71
+
72
+ ###### 六级标题:来源说明
73
+
74
+ 高亮支持 ==重点结论==、**加粗里的 ==重点==**,以及 ==带 *强调* 的重点==。
75
+
76
+ 转义必须保持原样:\=\=不高亮\=\=。行内代码 `a == b`,代码中的 `==不是高亮==`。
77
+
78
+ ```js
79
+ if (a == b) console.log("==只是代码==");
80
+ ```
81
+
82
+ - ==列表重点==
83
+ 1. **嵌套重点**
84
+ > ==引用中的重点==
85
+
86
+ Setext 标题
87
+ ----------
88
+
89
+ 自动链接:<https://example.com>,邮箱 <reader@example.com>。
90
+
91
+ 实体:&copy; &amp; &#x4e2d;。反斜杠转义:\*普通星号\*。
92
+
93
+ 四个空格的代码:a == b
94
+
95
+ | 左对齐 | 居中 | 右对齐 |
96
+ | :--- | :---: | ---: |
97
+ | **重点** | ==关注== | 123 |
98
+ | 转义竖线 \| | ~~删除~~ | 4.50 |
99
+
100
+ ![引用式图片][reference-image]
101
+
102
+ [reference-image]: ../../imgx/assets/icons/logo.png "引用式图注"
103
+
104
+ ```markdown
105
+ ![代码示例](./missing-example.png)
106
+ ```