@wenyan-md/core 3.0.10 → 3.0.12

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/dist/core.js CHANGED
@@ -202,7 +202,20 @@ async function handleFrontMatter(markdown) {
202
202
  const { attributes, body } = fm(markdown);
203
203
  const result = { content: body || "" };
204
204
  let head = "";
205
- const { title, description, cover, author, source_url, need_open_comment, only_fans_can_comment, image_list, type } = attributes;
205
+ const {
206
+ title,
207
+ description,
208
+ cover,
209
+ author,
210
+ source_url,
211
+ pic_crop_235_1,
212
+ pic_crop_1_1,
213
+ need_open_comment,
214
+ only_fans_can_comment,
215
+ image_list,
216
+ type,
217
+ digest
218
+ } = attributes;
206
219
  if (title) {
207
220
  result.title = title;
208
221
  }
@@ -219,6 +232,12 @@ async function handleFrontMatter(markdown) {
219
232
  if (source_url) {
220
233
  result.source_url = source_url;
221
234
  }
235
+ if (pic_crop_235_1) {
236
+ result.pic_crop_235_1 = String(pic_crop_235_1);
237
+ }
238
+ if (pic_crop_1_1) {
239
+ result.pic_crop_1_1 = String(pic_crop_1_1);
240
+ }
222
241
  if (need_open_comment !== void 0) {
223
242
  result.need_open_comment = need_open_comment;
224
243
  }
@@ -231,6 +250,9 @@ async function handleFrontMatter(markdown) {
231
250
  if (type) {
232
251
  result.type = type;
233
252
  }
253
+ if (digest) {
254
+ result.digest = digest;
255
+ }
234
256
  if (head) {
235
257
  result.content = head + result.content;
236
258
  }
@@ -996,15 +1018,34 @@ function wechatPostRender(element) {
996
1018
  }
997
1019
  li.appendChild(section);
998
1020
  });
999
- const nestedLists = element.querySelectorAll(
1000
- "li > section > ul, li > section > ol"
1001
- );
1002
- nestedLists.forEach((nestedList) => {
1003
- const li = nestedList.closest("li");
1004
- if (li) {
1005
- li.insertAdjacentElement("afterend", nestedList);
1021
+ const flattenNestedLists = () => {
1022
+ let nestedLists = element.querySelectorAll(
1023
+ "li > section > ul, li > section > ol"
1024
+ );
1025
+ while (nestedLists.length > 0) {
1026
+ Array.from(nestedLists).reverse().forEach((nestedList) => {
1027
+ const isOrdered = nestedList.tagName === "OL";
1028
+ const doc = element.ownerDocument;
1029
+ const items = Array.from(nestedList.children).filter(
1030
+ (child) => child.tagName === "LI"
1031
+ );
1032
+ const wrapper = doc.createElement("section");
1033
+ wrapper.style.marginLeft = "1em";
1034
+ items.forEach((item, index) => {
1035
+ const content = item.querySelector("section");
1036
+ const sectionEl = doc.createElement("section");
1037
+ const marker = isOrdered ? `${index + 1}. ` : "• ";
1038
+ sectionEl.innerHTML = marker + (content?.innerHTML ?? item.innerHTML);
1039
+ wrapper.appendChild(sectionEl);
1040
+ });
1041
+ nestedList.replaceWith(wrapper);
1042
+ });
1043
+ nestedLists = element.querySelectorAll(
1044
+ "li > section > ul, li > section > ol"
1045
+ );
1006
1046
  }
1007
- });
1047
+ };
1048
+ flattenNestedLists();
1008
1049
  element.style.color = "rgb(0, 0, 0)";
1009
1050
  element.style.caretColor = "rgb(0, 0, 0)";
1010
1051
  }
package/dist/publish.js CHANGED
@@ -196,12 +196,18 @@ class WechatPublisher {
196
196
  uploadCacheStore;
197
197
  uploadMaterial;
198
198
  publishArticle;
199
+ _listDraftsFn;
200
+ _getDraftFn;
201
+ _updateDraftFn;
199
202
  fetchAccessToken;
200
203
  constructor(httpAdapter, tokenStoreAdapter, uploadCacheStoreAdapter) {
201
- const { uploadMaterial, publishArticle, fetchAccessToken } = createWechatClient(httpAdapter);
204
+ const { uploadMaterial, publishArticle, fetchAccessToken, listDrafts, getDraft, updateDraft } = createWechatClient(httpAdapter);
202
205
  this.uploadMaterial = uploadMaterial;
203
206
  this.publishArticle = publishArticle;
204
207
  this.fetchAccessToken = fetchAccessToken;
208
+ this._listDraftsFn = listDrafts;
209
+ this._getDraftFn = getDraft;
210
+ this._updateDraftFn = updateDraft;
205
211
  this.tokenStore = tokenStoreAdapter ? new TokenStore(tokenStoreAdapter) : void 0;
206
212
  this.uploadCacheStore = uploadCacheStoreAdapter ? new UploadCacheStore(uploadCacheStoreAdapter) : void 0;
207
213
  }
@@ -242,6 +248,15 @@ class WechatPublisher {
242
248
  async publishToDraft(accessToken, options) {
243
249
  return await this.publishArticle(accessToken, options);
244
250
  }
251
+ async listDrafts(accessToken, offset = 0, count = 20, noContent = 0) {
252
+ return await this._listDraftsFn(accessToken, offset, count, noContent);
253
+ }
254
+ async getDraft(accessToken, mediaId) {
255
+ return await this._getDraftFn(accessToken, mediaId);
256
+ }
257
+ async updateDraft(accessToken, mediaId, articleIndex, options) {
258
+ await this._updateDraftFn(accessToken, mediaId, articleIndex, options);
259
+ }
245
260
  async clearCache() {
246
261
  if (this.tokenStore) {
247
262
  await this.tokenStore.clear();
@@ -5,9 +5,12 @@ export interface FrontMatterResult {
5
5
  cover?: string;
6
6
  author?: string;
7
7
  source_url?: string;
8
+ pic_crop_235_1?: string;
9
+ pic_crop_1_1?: string;
8
10
  need_open_comment?: boolean;
9
11
  only_fans_can_comment?: boolean;
10
12
  image_list?: string[];
11
13
  type?: string;
14
+ digest?: string;
12
15
  }
13
16
  export declare function handleFrontMatter(markdown: string): Promise<FrontMatterResult>;
@@ -1,15 +1,18 @@
1
1
  import { HttpAdapter } from "./http.js";
2
2
  import { TokenStorageAdapter } from "./tokenStore.js";
3
3
  import { UploadCacheStorageAdapter } from "./uploadCacheStore.js";
4
- import { WechatPublishOptions, WechatPublishResponse, WechatUploadResponse } from "./wechat.js";
4
+ import { WechatPublishOptions, WechatPublishResponse, WechatUploadResponse, WechatDraftListResponse, WechatDraftGetResponse } from "./wechat.js";
5
5
  export interface ArticleOptions {
6
6
  title: string;
7
7
  content: string;
8
8
  cover?: string;
9
9
  author?: string;
10
10
  source_url?: string;
11
+ pic_crop_235_1?: string;
12
+ pic_crop_1_1?: string;
11
13
  need_open_comment?: boolean;
12
14
  only_fans_can_comment?: boolean;
15
+ digest?: string;
13
16
  }
14
17
  export interface ImageTextArticleOptions extends ArticleOptions {
15
18
  images: string[];
@@ -19,11 +22,17 @@ export declare class WechatPublisher {
19
22
  private uploadCacheStore;
20
23
  private uploadMaterial;
21
24
  private publishArticle;
25
+ private _listDraftsFn;
26
+ private _getDraftFn;
27
+ private _updateDraftFn;
22
28
  private fetchAccessToken;
23
29
  constructor(httpAdapter: HttpAdapter, tokenStoreAdapter?: TokenStorageAdapter, uploadCacheStoreAdapter?: UploadCacheStorageAdapter);
24
30
  getAccessTokenWithCache(appId: string, appSecret: string): Promise<string>;
25
31
  uploadImage(file: Blob, filename: string, accessToken: string, appId?: string): Promise<WechatUploadResponse>;
26
32
  publishToDraft(accessToken: string, options: WechatPublishOptions): Promise<WechatPublishResponse>;
33
+ listDrafts(accessToken: string, offset?: number, count?: number, noContent?: number): Promise<WechatDraftListResponse>;
34
+ getDraft(accessToken: string, mediaId: string): Promise<WechatDraftGetResponse>;
35
+ updateDraft(accessToken: string, mediaId: string, articleIndex: number, options: Partial<WechatPublishOptions>): Promise<void>;
27
36
  clearCache(): Promise<void>;
28
37
  setExternalToken(appid: string, accessToken: string): Promise<void>;
29
38
  }
@@ -19,10 +19,13 @@ export interface WechatPublishOptions {
19
19
  content: string;
20
20
  thumb_media_id: string;
21
21
  content_source_url?: string;
22
+ pic_crop_235_1?: string;
23
+ pic_crop_1_1?: string;
22
24
  article_type?: "news" | "newspic";
23
25
  image_info?: ImageInfo;
24
26
  need_open_comment?: 0 | 1;
25
27
  only_fans_can_comment?: 0 | 1;
28
+ digest?: string;
26
29
  }
27
30
  export interface WechatErrorResponse {
28
31
  errcode: number;
@@ -39,9 +42,31 @@ export interface WechatTokenResponse {
39
42
  export interface WechatPublishResponse {
40
43
  media_id: string;
41
44
  }
45
+ export interface WechatDraftListItem {
46
+ media_id: string;
47
+ content: {
48
+ news_item: WechatPublishOptions[];
49
+ };
50
+ update_time: number;
51
+ }
52
+ export interface WechatDraftListResponse {
53
+ total_count: number;
54
+ item_count: number;
55
+ item: WechatDraftListItem[];
56
+ }
57
+ export interface WechatDraftGetResponse {
58
+ news_item: WechatPublishOptions[];
59
+ }
60
+ export interface WechatDraftUpdateResponse {
61
+ errcode: number;
62
+ errmsg: string;
63
+ }
42
64
  export declare function createWechatClient(httpAdapter: HttpAdapter): {
43
65
  fetchAccessToken(appId: string, appSecret: string): Promise<WechatTokenResponse>;
44
66
  uploadMaterial(type: string, file: Blob, filename: string, accessToken: string): Promise<WechatUploadResponse>;
45
67
  publishArticle(accessToken: string, options: WechatPublishOptions): Promise<WechatPublishResponse>;
68
+ listDrafts(accessToken: string, offset?: number, count?: number, noContent?: number): Promise<WechatDraftListResponse>;
69
+ getDraft(accessToken: string, mediaId: string): Promise<WechatDraftGetResponse>;
70
+ updateDraft(accessToken: string, mediaId: string, articleIndex: number, options: Partial<WechatPublishOptions>): Promise<void>;
46
71
  };
47
72
  export type WechatClient = ReturnType<typeof createWechatClient>;
package/dist/wechat.js CHANGED
@@ -1,6 +1,9 @@
1
1
  const tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
2
2
  const publishUrl = "https://api.weixin.qq.com/cgi-bin/draft/add";
3
3
  const uploadUrl = "https://api.weixin.qq.com/cgi-bin/material/add_material";
4
+ const draftListUrl = "https://api.weixin.qq.com/cgi-bin/draft/batchget";
5
+ const draftGetUrl = "https://api.weixin.qq.com/cgi-bin/draft/get";
6
+ const draftUpdateUrl = "https://api.weixin.qq.com/cgi-bin/draft/update";
4
7
  function createWechatClient(httpAdapter) {
5
8
  return {
6
9
  async fetchAccessToken(appId, appSecret) {
@@ -37,6 +40,39 @@ function createWechatClient(httpAdapter) {
37
40
  const data = await res.json();
38
41
  assertWechatSuccess(data);
39
42
  return data;
43
+ },
44
+ async listDrafts(accessToken, offset = 0, count = 20, noContent = 0) {
45
+ const res = await httpAdapter.fetch(`${draftListUrl}?access_token=${accessToken}`, {
46
+ method: "POST",
47
+ body: JSON.stringify({ offset, count, no_content: noContent })
48
+ });
49
+ if (!res.ok) throw new Error(await res.text());
50
+ const data = await res.json();
51
+ assertWechatSuccess(data);
52
+ return data;
53
+ },
54
+ async getDraft(accessToken, mediaId) {
55
+ const res = await httpAdapter.fetch(`${draftGetUrl}?access_token=${accessToken}`, {
56
+ method: "POST",
57
+ body: JSON.stringify({ media_id: mediaId })
58
+ });
59
+ if (!res.ok) throw new Error(await res.text());
60
+ const data = await res.json();
61
+ assertWechatSuccess(data);
62
+ return data;
63
+ },
64
+ async updateDraft(accessToken, mediaId, articleIndex, options) {
65
+ const res = await httpAdapter.fetch(`${draftUpdateUrl}?access_token=${accessToken}`, {
66
+ method: "POST",
67
+ body: JSON.stringify({
68
+ media_id: mediaId,
69
+ index: articleIndex,
70
+ articles: options
71
+ })
72
+ });
73
+ if (!res.ok) throw new Error(await res.text());
74
+ const data = await res.json();
75
+ assertWechatSuccess(data);
40
76
  }
41
77
  };
42
78
  }
@@ -44,7 +80,7 @@ const WECHAT_ERROR_HINTS = {
44
80
  45166: "内容超长。小绿书模式有内容长度限制,请精简正文后重试。"
45
81
  };
46
82
  function assertWechatSuccess(data) {
47
- if ("errcode" in data) {
83
+ if ("errcode" in data && data.errcode !== 0) {
48
84
  const hint = WECHAT_ERROR_HINTS[data.errcode];
49
85
  throw new Error(hint ? `${data.errcode}: ${hint} (${data.errmsg})` : `${data.errcode}: ${data.errmsg}`);
50
86
  }
package/dist/wrapper.js CHANGED
@@ -478,7 +478,18 @@ async function uploadImages(content, accessToken, relativePath, appId) {
478
478
  return { html: updatedHtml, firstImageId };
479
479
  }
480
480
  async function publishToWechatDraft(articleOptions, publishOptions = {}) {
481
- const { title, content, cover, author, source_url, need_open_comment, only_fans_can_comment } = articleOptions;
481
+ const {
482
+ title,
483
+ content,
484
+ cover,
485
+ author,
486
+ source_url,
487
+ pic_crop_235_1,
488
+ pic_crop_1_1,
489
+ need_open_comment,
490
+ only_fans_can_comment,
491
+ digest
492
+ } = articleOptions;
482
493
  const { appId, appSecret, relativePath } = publishOptions;
483
494
  const { appId: appIdFinal, appSecret: appSecretFinal } = await getAppIdAndSecret(appId, appSecret);
484
495
  const accessToken = await wechatPublisher.getAccessTokenWithCache(appIdFinal, appSecretFinal);
@@ -514,8 +525,11 @@ async function publishToWechatDraft(articleOptions, publishOptions = {}) {
514
525
  thumb_media_id: thumbMediaId,
515
526
  author,
516
527
  content_source_url: source_url,
528
+ pic_crop_235_1,
529
+ pic_crop_1_1,
517
530
  need_open_comment: need_open_comment ? 1 : 0,
518
- only_fans_can_comment: only_fans_can_comment ? 1 : 0
531
+ only_fans_can_comment: only_fans_can_comment ? 1 : 0,
532
+ digest
519
533
  });
520
534
  if (data.media_id) {
521
535
  return data;
@@ -571,11 +585,30 @@ async function publishImageTextToWechatDraft(articleOptions, publishOptions = {}
571
585
  if (!thumbMediaId) {
572
586
  throw new Error("未能获取封面图的 media_id");
573
587
  }
588
+ let plainContent = content || "";
589
+ if (plainContent) {
590
+ if (plainContent.includes("<br") || plainContent.includes("<p")) {
591
+ plainContent = plainContent.replace(/<br\s*\/?>\n/g, "\n");
592
+ const dom = new JSDOM(`<body>${plainContent}</body>`);
593
+ const document = dom.window.document;
594
+ const brs = document.querySelectorAll("br");
595
+ for (const br of brs) {
596
+ br.replaceWith(document.createTextNode("\n"));
597
+ }
598
+ const paragraphs = document.querySelectorAll("p");
599
+ for (const p of paragraphs) {
600
+ const text = document.createTextNode("\n" + p.textContent + "\n");
601
+ p.replaceWith(text);
602
+ }
603
+ plainContent = document.body.textContent?.trim() || "";
604
+ }
605
+ plainContent = plainContent.replace(/^#{1,6}\s+/gm, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/__(.*?)__/g, "$1").replace(/_(.*?)_/g, "$1").replace(/`{1,3}[^`]*`{1,3}/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/!\[([^\]]*)\]\([^)]+\)/g, "").replace(/^\s*[-*+]\s+/gm, "").replace(/^\s*\d+\.\s+/gm, "").replace(/^\s*>\s+/gm, "").replace(/\n{3,}/g, "\n\n").trim();
606
+ }
574
607
  const data = await wechatPublisher.publishToDraft(
575
608
  accessToken,
576
609
  {
577
610
  title,
578
- content,
611
+ content: plainContent,
579
612
  thumb_media_id: thumbMediaId,
580
613
  author,
581
614
  article_type: "newspic",
@@ -864,7 +897,21 @@ async function extractAndCleanImages(body) {
864
897
  parent.remove();
865
898
  }
866
899
  }
867
- return { imagePaths, cleanedHtml: wenyan.outerHTML };
900
+ for (const child of Array.from(wenyan.children)) {
901
+ if (!child.textContent?.trim()) {
902
+ child.remove();
903
+ }
904
+ }
905
+ const paragraphs = Array.from(wenyan.querySelectorAll("p"));
906
+ for (const p of paragraphs) {
907
+ const frag = document.createDocumentFragment();
908
+ while (p.firstChild) {
909
+ frag.appendChild(p.firstChild);
910
+ }
911
+ frag.appendChild(document.createElement("br"));
912
+ p.parentNode?.replaceChild(frag, p);
913
+ }
914
+ return { imagePaths, cleanedHtml: wenyan.innerHTML.trim() };
868
915
  }
869
916
  const nodeMermaidRenderer = createNodeMermaidRenderer();
870
917
  const wenyanCoreInstance = await createWenyanCore({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wenyan-md/core",
3
- "version": "3.0.10",
3
+ "version": "3.0.12",
4
4
  "description": "Core library for Wenyan markdown rendering & publishing",
5
5
  "author": "Lei <caol64@gmail.com> (https://github.com/caol64)",
6
6
  "license": "Apache-2.0",