@zzclub/pipeline 0.4.0 → 0.9.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.
- package/README.md +102 -3
- package/package.json +1 -1
- package/src/adapter-types.ts +6 -0
- package/src/adapters/builtin-image-renderer.ts +7 -1
- package/src/adapters/builtin-markdown-renderer.ts +1 -0
- package/src/commands/analytics.test.ts +155 -0
- package/src/commands/analytics.ts +223 -0
- package/src/commands/init.ts +42 -3
- package/src/commands/publish.ts +44 -67
- package/src/commands/republish.ts +146 -0
- package/src/commands/sync-blog.ts +19 -3
- package/src/commands/topic.test.ts +146 -0
- package/src/commands/topic.ts +717 -0
- package/src/commands/wechat-export.ts +2 -0
- package/src/commands/wx-draft-delete.ts +36 -0
- package/src/commands/wx-drafts.ts +47 -0
- package/src/config.ts +0 -0
- package/src/db.test.ts +47 -0
- package/src/db.ts +95 -0
- package/src/imgx/assets/icons/ancientone-logo.png +0 -0
- package/src/imgx/assets/templates/wechat-cover-split.html +1 -1
- package/src/imgx/render-card.ts +1 -5
- package/src/plugins.ts +11 -1
- package/src/providers/blog.ts +9 -3
- package/src/providers/index.test.ts +1 -1
- package/src/providers/index.ts +29 -5
- package/src/providers/publish-core.ts +124 -0
- package/src/providers/wechat.ts +260 -28
- package/src/routes.ts +1 -1
- package/src/schema/analytics.test.ts +54 -0
- package/src/schema/analytics.ts +19 -0
- package/src/schema/config.ts +25 -1
- package/src/schema/state.ts +10 -0
- package/src/schema/topic.test.ts +69 -0
- package/src/schema/topic.ts +55 -0
- package/src/state.ts +1 -0
- package/src/text.test.ts +11 -6
- package/src/text.ts +27 -9
- package/src/wechat-preview/assets/browser-dist/.vite/manifest.json +2 -7
- package/src/wechat-preview/assets/browser-dist/assets/{style-BYNXnUvc.css → style-vhHYSP8T.css} +1 -1
- package/src/wechat-preview/assets/browser-dist/editor-export.js +27 -27
- package/src/wechat-preview/browser/editor-export.css +248 -159
- package/src/wechat-preview/index.ts +28 -11
- package/src/wechat-preview/themes.ts +70 -24
- package/src/wechat-preview/wechat-formatter.test.ts +0 -1
- package/src/wechat-preview/wechat-formatter.ts +48 -23
- package/src/workflow.test.ts +294 -1
- package/src/wechat-preview/assets/browser-dist/assets/SweiCurveLegCJKsc-Regular-B3Ut5bwH.woff +0 -0
|
@@ -1,241 +1,330 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
/*
|
|
2
|
+
* WeChat Article Typography — 温暖人文
|
|
3
|
+
* Brand: #ca6093 (玫瑰暖调)
|
|
4
|
+
*
|
|
5
|
+
* Design tokens (:root custom properties) anchor the numeric scale.
|
|
6
|
+
* All values derive from:
|
|
7
|
+
* --font-base = 16px (body)
|
|
8
|
+
* --leading = 2.0 (body line-height ratio)
|
|
9
|
+
* --brand = #ca6093
|
|
10
|
+
* --text = #2c2c2c (warm black)
|
|
11
|
+
*
|
|
12
|
+
* When converting to WeChat inline styles, resolve each var() to its
|
|
13
|
+
* computed value. The token layer makes future rebranding a one-line edit.
|
|
14
|
+
*/
|
|
8
15
|
|
|
9
16
|
:root {
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
/* ── Core tokens ── */
|
|
18
|
+
--font-base: 16px;
|
|
19
|
+
--font-sans: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
|
|
20
|
+
-apple-system, "SF Pro Text", "Noto Sans SC", sans-serif;
|
|
21
|
+
--font-mono: "JetBrains Mono", "Fira Code", "SF Mono", "Consolas",
|
|
22
|
+
"Monaco", "Courier New", monospace;
|
|
23
|
+
--font-serif: "Noto Serif SC", "Source Han Serif SC", "Songti SC",
|
|
24
|
+
"STSong", "Georgia", serif;
|
|
12
25
|
|
|
13
|
-
|
|
14
|
-
body {
|
|
15
|
-
margin: 0;
|
|
16
|
-
min-height: 100%;
|
|
17
|
-
background: hsl(var(--background));
|
|
18
|
-
}
|
|
26
|
+
--leading: 2.0;
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
--brand: #ca6093;
|
|
29
|
+
--brand-soft: #d4789e; /* hover / secondary accent */
|
|
30
|
+
--brand-bg: rgba(202, 96, 147, 0.08);
|
|
31
|
+
--brand-bg-deep: rgba(202, 96, 147, 0.14);
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
background: hsl(var(--background));
|
|
29
|
-
}
|
|
33
|
+
--text: #2c2c2c; /* warm black, never pure #000 */
|
|
34
|
+
--text-soft: #8c8c8c; /* captions, meta */
|
|
35
|
+
--text-mid: #5c5c5c; /* blockquote, secondary text */
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
--
|
|
35
|
-
|
|
36
|
-
--
|
|
37
|
-
--
|
|
38
|
-
--crepe-color-on-surface: hsl(var(--card-foreground));
|
|
39
|
-
--crepe-color-outline: hsl(var(--border));
|
|
40
|
-
--crepe-color-primary: hsl(var(--primary));
|
|
41
|
-
--crepe-color-on-primary: hsl(var(--primary-foreground));
|
|
37
|
+
--border: #e8e4e5; /* warm gray */
|
|
38
|
+
--divider: #d8d0d3; /* slightly warmer than border */
|
|
39
|
+
--bg-page: #fefefe;
|
|
40
|
+
--bg-warm: #faf8f9; /* subtle warm tint for blockquote */
|
|
41
|
+
|
|
42
|
+
--radius-sm: 3px;
|
|
43
|
+
--radius-md: 6px;
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
47
|
+
Container
|
|
48
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
49
|
+
|
|
44
50
|
.milkdown .editor {
|
|
45
|
-
|
|
46
|
-
max-width: 900px;
|
|
51
|
+
max-width: 680px;
|
|
47
52
|
margin: 0 auto;
|
|
48
|
-
padding:
|
|
49
|
-
font-family:
|
|
50
|
-
font-size:
|
|
51
|
-
line-height:
|
|
52
|
-
|
|
53
|
-
background: transparent;
|
|
53
|
+
padding: 48px 24px 80px;
|
|
54
|
+
font-family: var(--font-sans);
|
|
55
|
+
font-size: var(--font-base);
|
|
56
|
+
line-height: var(--leading);
|
|
57
|
+
color: var(--text);
|
|
54
58
|
text-rendering: optimizeLegibility;
|
|
55
59
|
-webkit-font-smoothing: antialiased;
|
|
60
|
+
-moz-osx-font-smoothing: grayscale;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
64
|
+
Paragraph
|
|
65
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
66
|
+
|
|
67
|
+
.milkdown .editor p {
|
|
68
|
+
margin: 0 0 1.4em 0;
|
|
69
|
+
line-height: var(--leading);
|
|
70
|
+
text-align: justify;
|
|
71
|
+
hyphens: auto;
|
|
72
|
+
word-break: break-all;
|
|
73
|
+
overflow-wrap: break-word;
|
|
61
74
|
}
|
|
62
75
|
|
|
63
|
-
.milkdown
|
|
64
|
-
|
|
65
|
-
font-weight: 700;
|
|
66
|
-
line-height: 1.2;
|
|
67
|
-
margin-top: 2rem;
|
|
68
|
-
margin-bottom: 1rem;
|
|
69
|
-
letter-spacing: -0.02em;
|
|
76
|
+
.milkdown .editor p:first-child {
|
|
77
|
+
margin-top: 0;
|
|
70
78
|
}
|
|
71
79
|
|
|
72
|
-
.milkdown .editor
|
|
73
|
-
|
|
80
|
+
.milkdown .editor p:last-child {
|
|
81
|
+
margin-bottom: 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
85
|
+
Headings
|
|
86
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
87
|
+
|
|
88
|
+
.milkdown .editor h1 {
|
|
89
|
+
font-size: 1.625rem; /* 26px */
|
|
74
90
|
font-weight: 700;
|
|
75
|
-
color: hsl(var(--primary));
|
|
76
91
|
line-height: 1.3;
|
|
77
|
-
margin
|
|
78
|
-
|
|
92
|
+
margin: 3em 0 1em 0;
|
|
93
|
+
color: var(--text);
|
|
79
94
|
letter-spacing: -0.01em;
|
|
80
95
|
}
|
|
81
96
|
|
|
82
|
-
|
|
83
|
-
font-size: 18px;
|
|
84
|
-
font-weight: 400;
|
|
85
|
-
line-height: 1.4;
|
|
86
|
-
margin-top: 1.5rem;
|
|
87
|
-
margin-bottom: 0.5rem;
|
|
88
|
-
}
|
|
97
|
+
/* ── h2: brand color, clean style ── */
|
|
89
98
|
|
|
90
|
-
.milkdown
|
|
91
|
-
font-size: 1.
|
|
92
|
-
font-weight:
|
|
93
|
-
line-height: 1.
|
|
94
|
-
margin
|
|
95
|
-
|
|
99
|
+
.milkdown .editor h2 {
|
|
100
|
+
font-size: 1.25rem; /* 20px */
|
|
101
|
+
font-weight: 700;
|
|
102
|
+
line-height: 1.35;
|
|
103
|
+
margin: 2.4em 0 0.9em 0;
|
|
104
|
+
color: var(--brand);
|
|
105
|
+
letter-spacing: 0.02em;
|
|
96
106
|
}
|
|
97
107
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
108
|
+
/* ── h3: warm black, semi-bold, underlined by a thin rule ── */
|
|
109
|
+
|
|
110
|
+
.milkdown .editor h3 {
|
|
111
|
+
font-size: 1.125rem; /* 18px */
|
|
101
112
|
font-weight: 600;
|
|
102
|
-
line-height: 1.
|
|
103
|
-
margin
|
|
104
|
-
|
|
113
|
+
line-height: 1.45;
|
|
114
|
+
margin: 2em 0 0.65em 0;
|
|
115
|
+
padding-bottom: 0.5em;
|
|
116
|
+
color: var(--text);
|
|
117
|
+
letter-spacing: 0.015em;
|
|
118
|
+
border-bottom: 1px solid var(--border);
|
|
105
119
|
}
|
|
106
120
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
color: hsl(var(--foreground));
|
|
111
|
-
}
|
|
121
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
122
|
+
Bold — brand-colored emphasis
|
|
123
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
112
124
|
|
|
113
|
-
.milkdown .editor
|
|
114
|
-
|
|
125
|
+
.milkdown .editor strong,
|
|
126
|
+
.milkdown .editor b {
|
|
127
|
+
font-weight: 700;
|
|
128
|
+
color: var(--text);
|
|
115
129
|
}
|
|
116
130
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
132
|
+
Links
|
|
133
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
120
134
|
|
|
121
|
-
.milkdown .editor
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
.milkdown .editor del,
|
|
127
|
-
.milkdown .editor span {
|
|
128
|
-
font-size: inherit;
|
|
129
|
-
line-height: inherit;
|
|
130
|
-
letter-spacing: inherit;
|
|
135
|
+
.milkdown .editor a {
|
|
136
|
+
color: var(--brand);
|
|
137
|
+
text-decoration: none;
|
|
138
|
+
border-bottom: 1px solid var(--brand-soft);
|
|
139
|
+
transition: border-color 0.15s;
|
|
131
140
|
}
|
|
132
141
|
|
|
133
|
-
.milkdown .editor
|
|
134
|
-
|
|
135
|
-
padding-left: 1rem;
|
|
136
|
-
border-left: 4px solid hsl(var(--border));
|
|
137
|
-
background: color-mix(in srgb, hsl(var(--primary)) 5%, white);
|
|
142
|
+
.milkdown .editor a:hover {
|
|
143
|
+
border-bottom-color: var(--brand);
|
|
138
144
|
}
|
|
139
145
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
color: hsl(var(--muted-foreground));
|
|
144
|
-
margin: 0.45rem 0;
|
|
145
|
-
}
|
|
146
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
147
|
+
Inline code
|
|
148
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
146
149
|
|
|
147
|
-
.milkdown .editor
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
.milkdown .editor code {
|
|
151
|
+
font-family: var(--font-mono);
|
|
152
|
+
font-size: 0.875em;
|
|
153
|
+
color: var(--brand);
|
|
154
|
+
background: var(--brand-bg);
|
|
155
|
+
padding: 2px 6px;
|
|
156
|
+
border-radius: var(--radius-sm);
|
|
157
|
+
word-break: break-all;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.milkdown .editor pre {
|
|
161
|
+
background: #f5f2f3;
|
|
162
|
+
border: 1px solid var(--border);
|
|
163
|
+
border-radius: var(--radius-md);
|
|
164
|
+
padding: 1.2em 1.3em;
|
|
165
|
+
margin: 1.5em 0;
|
|
166
|
+
overflow-x: auto;
|
|
167
|
+
line-height: 1.65;
|
|
151
168
|
}
|
|
152
169
|
|
|
153
|
-
.milkdown .editor
|
|
154
|
-
|
|
155
|
-
|
|
170
|
+
.milkdown .editor pre code {
|
|
171
|
+
background: none;
|
|
172
|
+
padding: 0;
|
|
173
|
+
font-size: 0.875em;
|
|
174
|
+
color: var(--text);
|
|
156
175
|
}
|
|
157
176
|
|
|
158
|
-
|
|
159
|
-
|
|
177
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
178
|
+
Image caption
|
|
179
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
180
|
+
|
|
181
|
+
.milkdown .editor figure {
|
|
182
|
+
margin: 1.6em auto;
|
|
183
|
+
text-align: center;
|
|
160
184
|
}
|
|
161
185
|
|
|
162
|
-
.milkdown .editor
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
padding: 1px 2px;
|
|
168
|
-
border-radius: 4px;
|
|
186
|
+
.milkdown .editor figure img {
|
|
187
|
+
display: block;
|
|
188
|
+
max-width: 100%;
|
|
189
|
+
margin: 0 auto;
|
|
190
|
+
border-radius: var(--radius-sm);
|
|
169
191
|
}
|
|
170
192
|
|
|
171
|
-
.milkdown
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
overflow-x: auto;
|
|
193
|
+
.milkdown .editor figure figcaption,
|
|
194
|
+
.milkdown .editor .img-caption {
|
|
195
|
+
display: block;
|
|
196
|
+
margin-top: 10px;
|
|
197
|
+
font-size: 0.875rem; /* 14px */
|
|
177
198
|
line-height: 1.6;
|
|
199
|
+
color: var(--text-soft);
|
|
200
|
+
text-align: center;
|
|
178
201
|
}
|
|
179
202
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
203
|
+
/* For standalone images (not wrapped in figure) */
|
|
204
|
+
.milkdown .editor p:has(img) + p.img-caption,
|
|
205
|
+
.milkdown .editor img + .img-caption {
|
|
206
|
+
display: block;
|
|
207
|
+
margin-top: 8px;
|
|
208
|
+
font-size: 0.875rem;
|
|
209
|
+
color: var(--text-soft);
|
|
210
|
+
text-align: center;
|
|
184
211
|
}
|
|
185
212
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
213
|
+
/* Standalone image between paragraphs */
|
|
214
|
+
.milkdown .editor p img {
|
|
215
|
+
display: block;
|
|
216
|
+
max-width: 100%;
|
|
217
|
+
margin: 0 auto;
|
|
191
218
|
}
|
|
192
219
|
|
|
193
|
-
|
|
220
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
221
|
+
Divider
|
|
222
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
223
|
+
|
|
224
|
+
.milkdown .editor hr {
|
|
194
225
|
display: block;
|
|
195
226
|
height: 0;
|
|
196
227
|
border: none;
|
|
197
|
-
|
|
198
|
-
|
|
228
|
+
margin: 2.2em 0;
|
|
229
|
+
text-align: center;
|
|
230
|
+
/* thin warm line */
|
|
231
|
+
border-top: 1px solid var(--divider);
|
|
199
232
|
padding: 0;
|
|
200
233
|
background: transparent;
|
|
201
|
-
opacity: 0.72;
|
|
202
234
|
}
|
|
203
235
|
|
|
236
|
+
/*
|
|
237
|
+
* Decorative centered-dot divider variant.
|
|
238
|
+
* Override by adding class="hr-dots" on the <hr>.
|
|
239
|
+
*/
|
|
240
|
+
.milkdown .editor hr.hr-dots {
|
|
241
|
+
border-top: none;
|
|
242
|
+
line-height: 1;
|
|
243
|
+
letter-spacing: 0.6em;
|
|
244
|
+
color: var(--text-soft);
|
|
245
|
+
font-size: 0.75rem;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.milkdown .editor hr.hr-dots::after {
|
|
249
|
+
content: "· · ·";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
253
|
+
Blockquote
|
|
254
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
255
|
+
|
|
256
|
+
.milkdown .editor blockquote {
|
|
257
|
+
margin: 1.5em 0;
|
|
258
|
+
padding: 0.8em 1em 0.8em 1.15em;
|
|
259
|
+
border-left: 3px solid var(--brand-soft);
|
|
260
|
+
background: var(--bg-warm);
|
|
261
|
+
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.milkdown .editor blockquote p {
|
|
265
|
+
font-size: 0.9375rem; /* 15px */
|
|
266
|
+
line-height: 1.85;
|
|
267
|
+
color: var(--text-mid);
|
|
268
|
+
margin: 0.45em 0;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
272
|
+
Lists
|
|
273
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
274
|
+
|
|
275
|
+
.milkdown .editor ul,
|
|
276
|
+
.milkdown .editor ol {
|
|
277
|
+
margin: 1.1em 0;
|
|
278
|
+
padding-left: 1.5em;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.milkdown .editor li {
|
|
282
|
+
margin: 0.5em 0;
|
|
283
|
+
line-height: 1.85;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.milkdown .editor li > p {
|
|
287
|
+
margin: 0.2em 0;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
291
|
+
Tables
|
|
292
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
293
|
+
|
|
204
294
|
.milkdown .editor table {
|
|
205
295
|
width: 100%;
|
|
206
|
-
margin: 1.
|
|
296
|
+
margin: 1.4em 0;
|
|
207
297
|
border-collapse: collapse;
|
|
208
|
-
font-size:
|
|
298
|
+
font-size: 0.9375rem;
|
|
209
299
|
line-height: 1.75;
|
|
210
300
|
}
|
|
211
301
|
|
|
212
302
|
.milkdown .editor th,
|
|
213
303
|
.milkdown .editor td {
|
|
214
|
-
padding: 10px
|
|
215
|
-
border: 1px solid
|
|
304
|
+
padding: 10px 14px;
|
|
305
|
+
border: 1px solid var(--border);
|
|
216
306
|
text-align: left;
|
|
217
307
|
vertical-align: top;
|
|
218
308
|
}
|
|
219
309
|
|
|
220
310
|
.milkdown .editor th {
|
|
221
|
-
color:
|
|
222
|
-
background:
|
|
311
|
+
color: var(--text);
|
|
312
|
+
background: var(--brand-bg);
|
|
223
313
|
font-weight: 700;
|
|
224
314
|
}
|
|
225
315
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
margin: 14px auto;
|
|
230
|
-
}
|
|
316
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
317
|
+
Responsive
|
|
318
|
+
══════════════════════════════════════════════════════════════════════ */
|
|
231
319
|
|
|
232
320
|
@media (max-width: 768px) {
|
|
233
|
-
.preview-root {
|
|
234
|
-
padding: 20px 0;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
321
|
.milkdown .editor {
|
|
238
|
-
|
|
322
|
+
max-width: 100%;
|
|
323
|
+
padding: 28px 20px 64px;
|
|
239
324
|
font-size: 15px;
|
|
240
325
|
}
|
|
326
|
+
|
|
327
|
+
.milkdown .editor h1 { font-size: 1.4rem; }
|
|
328
|
+
.milkdown .editor h2 { font-size: 1.15rem; }
|
|
329
|
+
.milkdown .editor h3 { font-size: 1.05rem; }
|
|
241
330
|
}
|
|
@@ -38,6 +38,10 @@ export interface ExportMarkdownToWechatHtmlInput {
|
|
|
38
38
|
title?: string;
|
|
39
39
|
previewShellOutPath?: string;
|
|
40
40
|
customCss?: string | null;
|
|
41
|
+
themeOverrides?: {
|
|
42
|
+
editorVars?: Record<string, string>;
|
|
43
|
+
exportTheme?: Record<string, string>;
|
|
44
|
+
};
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
export interface ExportMarkdownToWechatHtmlResult {
|
|
@@ -103,12 +107,17 @@ export function resolveMarkdownAsset(rawPath: string, baseDir: string): string {
|
|
|
103
107
|
|
|
104
108
|
export function rewriteRelativeImagePaths(markdown: string, baseDir: string): string {
|
|
105
109
|
return markdown
|
|
106
|
-
.replace(/!\[([^\]]*)\]\((
|
|
107
|
-
const
|
|
110
|
+
.replace(/!\[([^\]]*)\]\((?:<([^>]+)>|([^)]+?))(?:\s+(["'])([^"']*)\4)?\)/g, (_match, alt, angleUrl, plainUrl, _quote, title) => {
|
|
111
|
+
const src = angleUrl ?? plainUrl ?? "";
|
|
112
|
+
const hasAngleBrackets = angleUrl !== undefined;
|
|
113
|
+
const resolved = resolveMarkdownAsset(src, baseDir);
|
|
114
|
+
const needsBrackets = resolved ? resolved.includes(" ") : src.includes(" ");
|
|
115
|
+
const titleStr = title ? ` "${title}"` : "";
|
|
108
116
|
if (!resolved || resolved === src) {
|
|
109
|
-
|
|
117
|
+
const wrap = !!(hasAngleBrackets || needsBrackets);
|
|
118
|
+
return ``;
|
|
110
119
|
}
|
|
111
|
-
return ``;
|
|
112
121
|
})
|
|
113
122
|
.replace(/(<img\b[^>]*\bsrc=["'])([^"']+)(["'][^>]*>)/gi, (_match, prefix, src, suffix) => {
|
|
114
123
|
const resolved = resolveMarkdownAsset(String(src ?? ""), baseDir);
|
|
@@ -118,18 +127,26 @@ export function rewriteRelativeImagePaths(markdown: string, baseDir: string): st
|
|
|
118
127
|
|
|
119
128
|
function rewriteRelativeImagePathsForPreview(markdown: string, baseDir: string, previewDir: string): string {
|
|
120
129
|
return markdown
|
|
121
|
-
.replace(/!\[([^\]]*)\]\((
|
|
122
|
-
const
|
|
130
|
+
.replace(/!\[([^\]]*)\]\((?:<([^>]+)>|([^)]+?))(?:\s+(["'])([^"']*)\4)?\)/g, (_match, alt, angleUrl, plainUrl, _quote, title) => {
|
|
131
|
+
const src = angleUrl ?? plainUrl ?? "";
|
|
132
|
+
const hasAngleBrackets = angleUrl !== undefined;
|
|
133
|
+
const raw = src.trim();
|
|
123
134
|
if (!raw || isExternalUrl(raw)) {
|
|
124
|
-
|
|
135
|
+
const wrap = !!(hasAngleBrackets || raw.includes(" "));
|
|
136
|
+
const titleStr = title ? ` "${title}"` : "";
|
|
137
|
+
return ``;
|
|
125
138
|
}
|
|
126
139
|
const resolved = resolveMarkdownAsset(raw, baseDir);
|
|
127
140
|
if (!resolved) {
|
|
128
|
-
|
|
141
|
+
const wrap = !!(hasAngleBrackets || raw.includes(" "));
|
|
142
|
+
const titleStr = title ? ` "${title}"` : "";
|
|
143
|
+
return ``;
|
|
129
144
|
}
|
|
130
145
|
const previewRelative = relative(previewDir, resolved).replaceAll("\\", "/");
|
|
131
146
|
const normalized = previewRelative.startsWith(".") ? previewRelative : `./${previewRelative}`;
|
|
132
|
-
|
|
147
|
+
const wrap = !!(hasAngleBrackets || normalized.includes(" "));
|
|
148
|
+
const titleStr = title ? ` "${title}"` : "";
|
|
149
|
+
return ``;
|
|
133
150
|
})
|
|
134
151
|
.replace(/(<img\b[^>]*\bsrc=["'])([^"']+)(["'][^>]*>)/gi, (_match, prefix, src, suffix) => {
|
|
135
152
|
const raw = String(src ?? "").trim();
|
|
@@ -226,9 +243,9 @@ export async function exportMarkdownToWechatHtml(
|
|
|
226
243
|
const bundle = ensureBundle();
|
|
227
244
|
const sourceMarkdown = await readFile(input.markdownPath, "utf-8");
|
|
228
245
|
const stripped = extractFrontmatter(sourceMarkdown).content;
|
|
229
|
-
const bodyMarkdown = stripLeadingH1(stripped);
|
|
246
|
+
const bodyMarkdown = stripLeadingH1(stripped).trimStart();
|
|
230
247
|
const markdown = rewriteRelativeImagePaths(bodyMarkdown, dirname(input.markdownPath));
|
|
231
|
-
const theme = getWechatPreviewTheme(input.account);
|
|
248
|
+
const theme = getWechatPreviewTheme(input.account, input.themeOverrides);
|
|
232
249
|
const shell = readUtf8(TEMPLATE_PATH);
|
|
233
250
|
const payloadJson = escapeInlineJson(
|
|
234
251
|
JSON.stringify({
|