@lyhue1991/wxgzh 0.2.3 → 0.2.5

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 CHANGED
@@ -195,7 +195,7 @@ wxgzh config --list
195
195
  wxgzh config --list-themes
196
196
  ```
197
197
 
198
- 当前内置主题来自 `styles/` 目录,例如:`default`、`blue`、`green`、`red`、`yellow`、`brown`、`black`、`orange`。
198
+ 当前内置主题来自 `styles/` 目录:`default`、`blue`、`brown`、`black`(默认)。
199
199
 
200
200
 
201
201
  ## 三、快速流程
@@ -294,6 +294,7 @@ wxgzh fix .wxgzh/article.html
294
294
  - 移除不适合公众号的结构(如 `script`、`iframe`)
295
295
  - 处理图片样式
296
296
  - 将本地图片上传到微信,替换为微信返回的图片地址
297
+ - 兼容懒加载结构:图片真实地址在 `data-src`、`src` 为空白占位图时,会自动以 `data-src` 为准回写 `src`;无法处理的图片(如纯占位图)会逐张告警,不再静默跳过
297
298
 
298
299
  如果你只想修结构,不上传图片:
299
300
 
package/dist/cli/fix.js CHANGED
@@ -37,5 +37,8 @@ function registerFixCommand(program) {
37
37
  wechat
38
38
  });
39
39
  (0, logger_1.success)(`HTML 修复完成,共处理 ${result.imageCount} 张图片`);
40
+ for (const message of result.skippedImages) {
41
+ (0, logger_1.warn)(message);
42
+ }
40
43
  });
41
44
  }
package/dist/cli/index.js CHANGED
@@ -101,8 +101,11 @@ async function runDefaultPipeline(articlePath, options) {
101
101
  const { fixHtmlFile } = await Promise.resolve().then(() => __importStar(require('../core/fixer')));
102
102
  const { WechatClient } = await Promise.resolve().then(() => __importStar(require('../core/wechat')));
103
103
  const wechat = new WechatClient((0, config_1.resolveWechatCredentials)(config));
104
- await fixHtmlFile(htmlPath, { upload: true, wechat });
104
+ const fixResult = await fixHtmlFile(htmlPath, { upload: true, wechat });
105
105
  (0, logger_1.info)(`已修复并上传正文图片: ${htmlPath}`);
106
+ for (const message of fixResult.skippedImages) {
107
+ (0, logger_1.warn)(message);
108
+ }
106
109
  let finalCoverPath = resolveCoverValue(options.cover) ?? parsed.metadata.cover;
107
110
  if (finalCoverPath && !node_path_1.default.isAbsolute(finalCoverPath)) {
108
111
  finalCoverPath = node_path_1.default.resolve(node_path_1.default.dirname(absoluteArticle), finalCoverPath);
@@ -127,6 +130,9 @@ async function runDefaultPipeline(articlePath, options) {
127
130
  }
128
131
  async function main() {
129
132
  const program = new commander_1.Command();
133
+ // 外层命令与子命令存在同名选项(如 publish 的 --cover),必须启用 positional options:
134
+ // 子命令名之后出现的选项只归子命令所有,避免外层 --cover/--no-cover 吞掉 publish 的 --cover 参数
135
+ program.enablePositionalOptions();
130
136
  program
131
137
  .name('wxgzh')
132
138
  .description('把 Markdown 文章处理成微信公众号草稿,支持主题、图片修复、封面生成与一键发布')
@@ -41,9 +41,27 @@ const node_path_1 = __importDefault(require("node:path"));
41
41
  const cheerio = __importStar(require("cheerio"));
42
42
  const converter_1 = require("./converter");
43
43
  const fs_1 = require("../utils/fs");
44
+ const LAZY_SOURCE_ATTRIBUTE = 'data-src';
44
45
  function isRemoteUrl(value) {
45
46
  return /^https?:\/\//i.test(value);
46
47
  }
48
+ function isPlaceholderValue(value) {
49
+ const trimmed = value.trim();
50
+ return trimmed === '' || trimmed.startsWith('data:') || trimmed === 'about:blank';
51
+ }
52
+ function isUsableSource(value) {
53
+ return value !== undefined && !isPlaceholderValue(value);
54
+ }
55
+ function pickImageSource(lazySource, directSource) {
56
+ // 微信保存页等懒加载结构:真实地址在 data-src,src 可能是空白占位图
57
+ if (isUsableSource(lazySource)) {
58
+ return lazySource.trim();
59
+ }
60
+ if (isUsableSource(directSource)) {
61
+ return directSource.trim();
62
+ }
63
+ return undefined;
64
+ }
47
65
  function toCdnUrl(cdn, source) {
48
66
  const cleanBase = cdn.replace(/\/+$/, '');
49
67
  const fileName = node_path_1.default.basename(source);
@@ -70,20 +88,26 @@ async function fixHtmlFile(articlePath, options) {
70
88
  const sourceBaseDir = metadata.sourceDir ? node_path_1.default.resolve(metadata.sourceDir) : node_path_1.default.dirname(articlePath);
71
89
  $('script,iframe').remove();
72
90
  const images = $('img').toArray();
73
- for (const element of images) {
91
+ const skippedImages = [];
92
+ let imageCount = 0;
93
+ for (const [index, element] of images.entries()) {
74
94
  const image = $(element);
75
- const source = image.attr('src');
76
- if (!source) {
95
+ const source = pickImageSource(image.attr(LAZY_SOURCE_ATTRIBUTE), image.attr('src'));
96
+ if (source === undefined) {
97
+ skippedImages.push(`第 ${index + 1} 张图片缺少可用地址(src 与 data-src 均为空或占位图),已跳过`);
77
98
  continue;
78
99
  }
100
+ let finalSource = source;
79
101
  if (options.upload && options.wechat && !source.includes('mmbiz.qpic.cn')) {
80
- const uploadedUrl = await options.wechat.uploadArticleImage(resolveImageSource(sourceBaseDir, source));
81
- image.attr('src', uploadedUrl);
102
+ finalSource = await options.wechat.uploadArticleImage(resolveImageSource(sourceBaseDir, source));
82
103
  image.attr('data-original-src', source);
83
104
  }
84
105
  else if (options.cdn && !isRemoteUrl(source)) {
85
- image.attr('src', toCdnUrl(options.cdn, source));
106
+ finalSource = toCdnUrl(options.cdn, source);
86
107
  }
108
+ // 真实地址统一回写 src,并移除懒加载占位属性,避免渲染端读到占位图
109
+ image.attr('src', finalSource);
110
+ image.removeAttr(LAZY_SOURCE_ATTRIBUTE);
87
111
  image.attr('style', [
88
112
  'display:block',
89
113
  'max-width:100%',
@@ -91,7 +115,8 @@ async function fixHtmlFile(articlePath, options) {
91
115
  'margin:0 auto',
92
116
  'border-radius:6px'
93
117
  ].join(';'));
118
+ imageCount += 1;
94
119
  }
95
120
  await (0, fs_1.writeTextFile)(articlePath, $.html());
96
- return { imageCount: images.length };
121
+ return { imageCount, skippedImages };
97
122
  }
@@ -56,7 +56,11 @@ function sanitizeAccountConfig(input) {
56
56
  output.author = input.author.trim();
57
57
  }
58
58
  if (typeof input.defaultTheme === 'string' && input.defaultTheme.trim()) {
59
- output.defaultTheme = (0, themes_1.assertThemeExists)(input.defaultTheme.trim());
59
+ const candidateTheme = input.defaultTheme.trim();
60
+ // 主题可能随版本被移除:清洗时直接丢弃失效值(回落默认主题),避免读取历史配置时抛错导致 CLI 不可用
61
+ if ((0, themes_1.isValidTheme)(candidateTheme)) {
62
+ output.defaultTheme = candidateTheme;
63
+ }
60
64
  }
61
65
  if (typeof input.enableComment === 'boolean') {
62
66
  output.enableComment = input.enableComment;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lyhue1991/wxgzh",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Markdown to WeChat Official Account draft CLI",
5
5
  "license": "MIT",
6
6
  "main": "dist/cli/index.js",
package/styles/red.css CHANGED
@@ -1,4 +1,4 @@
1
- /* 与 blue 主题保持一致,仅二级标题主题色不同 */
1
+ /* red 主题:仿「闪电SKILL」文章排版风格,红色主色 #d32f2f */
2
2
  ::marker {
3
3
  font-size: 14px;
4
4
  }
@@ -65,9 +65,7 @@ h6 {
65
65
  }
66
66
  h1 {
67
67
  font-size: 30px;
68
- /*30 / 16*/
69
68
  line-height: 1.6em;
70
- /* 48 / 30*/
71
69
  margin-top: 2em;
72
70
  }
73
71
 
@@ -75,41 +73,33 @@ h2{
75
73
  display: block;
76
74
  width: 100%;
77
75
  font-size: 18px;
78
- font-weight: 800;
79
- padding: 8px 14px;
76
+ font-weight: 600;
77
+ padding: 10px 18px;
80
78
  line-height: 1.4;
81
- /*24 / 21*/
82
79
  margin-top: 1.85em;
83
- /*48 / 21*/
84
80
  margin-bottom: 1.15em;
85
- /*24 / 21*/
86
81
  margin-left: 0;
87
82
  border-bottom: none;
88
83
  box-sizing: border-box;
89
- background:#DC143C;
84
+ border-radius: 4px;
85
+ background:#d32f2f;
90
86
  color:#fff;
91
87
  }
92
88
 
93
89
  h3 {
94
90
  font-size: 16px;
95
- /*21 / 16*/
96
- line-height: 1.15;
97
- /*24 / 21*/
91
+ font-weight: 600;
92
+ line-height: 1.45;
98
93
  margin-top: 2.285714em;
99
- /*48 / 21*/
100
94
  margin-bottom: 1.15em;
101
- /*24 / 21*/
95
+ border-bottom: 1px solid #d8dee4;
96
+ color: #d32f2f;
102
97
  }
103
98
 
104
99
  h4 {
105
100
  font-size: 16px;
106
- /*18 / 16*/
107
101
  margin-top: 2.67em;
108
- /*48 / 18*/
109
102
  }
110
- /* links */
111
-
112
-
113
103
 
114
104
  pre {
115
105
  line-height: 1.4em;
@@ -130,7 +120,7 @@ pre code {
130
120
  }
131
121
 
132
122
  code {
133
- color: #c7254e;
123
+ color: #d32f2f;
134
124
  white-space: pre-wrap;
135
125
  word-break: break-word;
136
126
  padding: 3px;
@@ -141,13 +131,12 @@ code {
141
131
 
142
132
  a {
143
133
  text-decoration: none;
144
- color: #065588;
134
+ color: #d32f2f;
145
135
  }
146
136
  a:hover,
147
137
  a:active {
148
138
  text-decoration: underline;
149
139
  }
150
- /* block spacing */
151
140
 
152
141
  p {
153
142
  margin: 1.2em 3%;
@@ -156,24 +145,23 @@ p {
156
145
 
157
146
 
158
147
  blockquote, q {
159
- border: 2px dotted #dddddd;
160
- border-radius: 0.7em;
161
- padding: 0 10px;
162
- color: #c7254e;
148
+ border: none;
149
+ border-left: 4px solid #d32f2f;
150
+ border-radius: 4px;
151
+ background-color: #f5f5f5;
152
+ padding: 10px 16px;
153
+ color: #1a1a1a;
163
154
  quotes: none;
164
155
  margin: auto 3%;
165
156
  }
166
157
 
167
- /* tables */
168
158
  .md-meta,.md-before, .md-after {
169
159
  color:#999;
170
160
  }
171
161
 
172
162
  table {
173
163
  margin-bottom: 1.5em;
174
- /*24 / 16*/
175
164
  font-size: 1em;
176
- /* width: 100%; */
177
165
  }
178
166
  thead th,
179
167
  tfoot th {
@@ -192,10 +180,6 @@ td {
192
180
  color: #0079bb;
193
181
  }
194
182
 
195
-
196
-
197
-
198
-
199
183
  tt {
200
184
  font-size: .875em;
201
185
  line-height: 1.714285em;
@@ -245,8 +229,7 @@ h1 {
245
229
  border-color: #c5c5c5;
246
230
  }
247
231
  blockquote {
248
-
249
- border-color: #bababa;
232
+ border-color: #d32f2f;
250
233
  }
251
234
 
252
235
  blockquote ul,
@@ -307,6 +290,16 @@ hr {
307
290
  font-family: Microsoft YaHei;
308
291
  }
309
292
 
293
+ img {
294
+ border-radius: 6px;
295
+ border: 2px solid #d32f2f;
296
+ box-shadow: 0 4px 12px rgba(211, 47, 47, 0.12);
297
+ }
298
+
299
+ strong {
300
+ color: #d32f2f;
301
+ }
302
+
310
303
 
311
304
  #write>h3.md-focus:before{
312
305
  left: -1.5em;
@@ -390,8 +383,6 @@ hr {
390
383
  list-style-type: none;
391
384
  }
392
385
 
393
- /** UI for electron */
394
-
395
386
  .megamenu-menu,
396
387
  #top-titlebar, #top-titlebar *,
397
388
  .megamenu-content {
@@ -503,7 +494,7 @@ input {
503
494
  }
504
495
 
505
496
  .megamenu-menu-header #megamenu-menu-header-title,
506
- .megamenu-menu-header:hover,
497
+ .megamenu-menu-header:hover,
507
498
  .megamenu-menu-header:focus {
508
499
  color: inherit;
509
500
  }
@@ -513,7 +504,6 @@ input {
513
504
  opacity: 1;
514
505
  }
515
506
 
516
- /* https://github.com/typora/typora-issues/issues/2046 */
517
507
  .os-windows-7 strong,
518
508
  .os-windows-7 strong {
519
509
  }
@@ -536,75 +526,68 @@ input {
536
526
  color: white;
537
527
  }
538
528
 
539
- /**
540
- 代码高亮
541
- */
542
-
543
529
  .highlight .hll { background-color: #ffffcc }
544
- .highlight .c { color: #60a0b0; font-style: italic } /* Comment */
545
- .highlight .err { border: 1px solid #FF0000 } /* Error */
546
- .highlight .k { color: #007020; font-weight: bold } /* Keyword */
547
- .highlight .o { color: #666666 } /* Operator */
548
- .highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */
549
- .highlight .cp { color: #007020 } /* Comment.Preproc */
550
- .highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */
551
- .highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */
552
- .highlight .gd { color: #A00000 } /* Generic.Deleted */
553
- .highlight .ge { font-style: italic } /* Generic.Emph */
554
- .highlight .gr { color: #FF0000 } /* Generic.Error */
555
- .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
556
- .highlight .gi { color: #00A000 } /* Generic.Inserted */
557
- .highlight .go { color: #808080 } /* Generic.Output */
558
- .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
559
- .highlight .gs { font-weight: bold } /* Generic.Strong */
560
- .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
561
- .highlight .gt { color: #0040D0 } /* Generic.Traceback */
562
- .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
563
- .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
564
- .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
565
- .highlight .kp { color: #007020 } /* Keyword.Pseudo */
566
- .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
567
- .highlight .kt { color: #902000 } /* Keyword.Type */
568
- .highlight .m { color: #40a070 } /* Literal.Number */
569
- .highlight .s { color: #4070a0 } /* Literal.String */
570
- .highlight .na { color: #4070a0 } /* Name.Attribute */
571
- .highlight .nb { color: #007020 } /* Name.Builtin */
572
- .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
573
- .highlight .no { color: #60add5 } /* Name.Constant */
574
- .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
575
- .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
576
- .highlight .ne { color: #007020 } /* Name.Exception */
577
- .highlight .nf { color: #06287e } /* Name.Function */
578
- .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
579
- .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
580
- .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
581
- .highlight .nv { color: #bb60d5 } /* Name.Variable */
582
- .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
583
- .highlight .w { color: #bbbbbb } /* Text.Whitespace */
584
- .highlight .mf { color: #40a070 } /* Literal.Number.Float */
585
- .highlight .mh { color: #40a070 } /* Literal.Number.Hex */
586
- .highlight .mi { color: #40a070 } /* Literal.Number.Integer */
587
- .highlight .mo { color: #40a070 } /* Literal.Number.Oct */
588
- .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
589
- .highlight .sc { color: #4070a0 } /* Literal.String.Char */
590
- .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
591
- .highlight .s2 { color: #4070a0 } /* Literal.String.Double */
592
- .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
593
- .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
594
- .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
595
- .highlight .sx { color: #c65d09 } /* Literal.String.Other */
596
- .highlight .sr { color: #235388 } /* Literal.String.Regex */
597
- .highlight .s1 { color: #4070a0 } /* Literal.String.Single */
598
- .highlight .ss { color: #517918 } /* Literal.String.Symbol */
599
- .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
600
- .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
601
- .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
602
- .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
603
- .highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */
604
-
605
-
606
-
607
- /*大纲紧凑*/
530
+ .highlight .c { color: #60a0b0; font-style: italic }
531
+ .highlight .err { border: 1px solid #FF0000 }
532
+ .highlight .k { color: #007020; font-weight: bold }
533
+ .highlight .o { color: #666666 }
534
+ .highlight .cm { color: #60a0b0; font-style: italic }
535
+ .highlight .cp { color: #007020 }
536
+ .highlight .c1 { color: #60a0b0; font-style: italic }
537
+ .highlight .cs { color: #60a0b0; background-color: #fff0f0 }
538
+ .highlight .gd { color: #A00000 }
539
+ .highlight .ge { font-style: italic }
540
+ .highlight .gr { color: #FF0000 }
541
+ .highlight .gh { color: #000080; font-weight: bold }
542
+ .highlight .gi { color: #00A000 }
543
+ .highlight .go { color: #808080 }
544
+ .highlight .gp { color: #c65d09; font-weight: bold }
545
+ .highlight .gs { font-weight: bold }
546
+ .highlight .gu { color: #800080; font-weight: bold }
547
+ .highlight .gt { color: #0040D0 }
548
+ .highlight .kc { color: #007020; font-weight: bold }
549
+ .highlight .kd { color: #007020; font-weight: bold }
550
+ .highlight .kn { color: #007020; font-weight: bold }
551
+ .highlight .kp { color: #007020 }
552
+ .highlight .kr { color: #007020; font-weight: bold }
553
+ .highlight .kt { color: #902000 }
554
+ .highlight .m { color: #40a070 }
555
+ .highlight .s { color: #4070a0 }
556
+ .highlight .na { color: #4070a0 }
557
+ .highlight .nb { color: #007020 }
558
+ .highlight .nc { color: #0e84b5; font-weight: bold }
559
+ .highlight .no { color: #60add5 }
560
+ .highlight .nd { color: #555555; font-weight: bold }
561
+ .highlight .ni { color: #d55537; font-weight: bold }
562
+ .highlight .ne { color: #007020 }
563
+ .highlight .nf { color: #06287e }
564
+ .highlight .nl { color: #002070; font-weight: bold }
565
+ .highlight .nn { color: #0e84b5; font-weight: bold }
566
+ .highlight .nt { color: #062873; font-weight: bold }
567
+ .highlight .nv { color: #bb60d5 }
568
+ .highlight .ow { color: #007020; font-weight: bold }
569
+ .highlight .w { color: #bbbbbb }
570
+ .highlight .mf { color: #40a070 }
571
+ .highlight .mh { color: #40a070 }
572
+ .highlight .mi { color: #40a070 }
573
+ .highlight .mo { color: #40a070 }
574
+ .highlight .sb { color: #4070a0 }
575
+ .highlight .sc { color: #4070a0 }
576
+ .highlight .sd { color: #4070a0; font-style: italic }
577
+ .highlight .s2 { color: #4070a0 }
578
+ .highlight .se { color: #4070a0; font-weight: bold }
579
+ .highlight .sh { color: #4070a0 }
580
+ .highlight .si { color: #70a0d0; font-style: italic }
581
+ .highlight .sx { color: #c65d09 }
582
+ .highlight .sr { color: #235388 }
583
+ .highlight .s1 { color: #4070a0 }
584
+ .highlight .ss { color: #517918 }
585
+ .highlight .bp { color: #007020 }
586
+ .highlight .vc { color: #bb60d5 }
587
+ .highlight .vg { color: #bb60d5 }
588
+ .highlight .vi { color: #bb60d5 }
589
+ .highlight .il { color: #40a070 }
590
+
608
591
  .outline-item {
609
592
  padding-top: 0px;
610
593
  padding-bottom: 0px;