@wenyan-md/core 1.0.14 → 1.0.16

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
@@ -9,30 +9,47 @@
9
9
  ![NPM Downloads](https://img.shields.io/npm/dm/%40wenyan-md%2Fcore)
10
10
  [![Stars](https://img.shields.io/github/stars/caol64/wenyan-core?style=social)](https://github.com/caol64/wenyan-core)
11
11
 
12
- 「文颜」是一款多平台排版美化工具,让你将 Markdown 一键发布至微信公众号、知乎、今日头条等主流写作平台。
12
+ ## 简介
13
13
 
14
- **文颜**现已推出多个版本:
14
+ **文颜(Wenyan)** 是一款多平台 Markdown 排版与发布工具,支持将 Markdown 一键转换并发布至:
15
15
 
16
- * [macOS App Store 版](https://github.com/caol64/wenyan) - MAC 桌面应用
17
- * [跨平台版本](https://github.com/caol64/wenyan-pc) - Windows/Linux 跨平台桌面应用
18
- * [CLI 版本](https://github.com/caol64/wenyan-cli) - CI/CD 或脚本自动化发布公众号文章
19
- * [MCP 版本](https://github.com/caol64/wenyan-mcp) - 让 AI 自动发布公众号文章
20
- * [嵌入版本](https://github.com/caol64/wenyan-core) - 将文颜的核心功能嵌入 Node 或者 Web 项目
16
+ - 微信公众号
17
+ - 知乎
18
+ - 今日头条
19
+ - 以及其它内容平台(持续扩展中)
20
+
21
+ 文颜的目标是:**让写作者与开发者专注内容,而不是排版和平台适配**。
22
+
23
+ 本仓库是 **文颜的核心库(CORE)**,适合以下场景:
24
+
25
+ - 嵌入 Node.js / Web 项目
26
+ - 构建自定义写作或发布系统
27
+ - 与 CLI / 桌面端 / MCP / AI 系统集成
28
+ - 二次开发排版或发布能力
29
+
30
+ ## 文颜的不同版本
21
31
 
22
- 本项目是 **文颜的核心库文件**,你可以将其方便地嵌入自己的应用中,以实现排版美化和自动发布功能。
32
+ 文颜目前提供多种形态,覆盖不同使用场景:
23
33
 
24
- ## 功能
34
+ * [macOS App Store 版](https://github.com/caol64/wenyan) - MAC 桌面应用
35
+ * [跨平台桌面版](https://github.com/caol64/wenyan-pc) - Windows / Linux
36
+ * [CLI 版本](https://github.com/caol64/wenyan-cli) - 命令行 / CI 自动化发布
37
+ * [MCP 版本](https://github.com/caol64/wenyan-mcp) - AI 自动发文
38
+ * 👉 **CORE 版本**(本项目)- 文颜核心能力库
39
+
40
+ ## 功能特性
25
41
 
26
42
  * 使用内置主题对 Markdown 内容排版
27
- * 支持图片自动上传
28
- * 支持数学公式渲染
43
+ * 自动处理并上传图片(本地 / 网络)
44
+ * 支持数学公式(MathJax)
29
45
  * 一键发布文章到微信公众号草稿箱
46
+ * 可在 Node / 浏览器 环境中运行
30
47
 
31
- ## 主题效果
48
+ ## 主题效果预览
32
49
 
33
50
  👉 [内置主题预览](https://yuzhi.tech/docs/wenyan/theme)
34
51
 
35
- 文颜采用了多个开源的 Typora 主题,在此向各位作者表示感谢:
52
+ 文颜内置并适配了多个优秀的 Typora 主题,在此感谢原作者:
36
53
 
37
54
  - [Orange Heart](https://github.com/evgo2017/typora-theme-orange-heart)
38
55
  - [Rainbow](https://github.com/thezbm/typora-theme-rainbow)
@@ -52,7 +69,7 @@ npm install @wenyan-md/core
52
69
  yarn add @wenyan-md/core
53
70
  ```
54
71
 
55
- ## 使用示例
72
+ ## 基本用法
56
73
 
57
74
  ### 1. Markdown 排版美化
58
75
 
@@ -68,50 +85,37 @@ const { title, cover, content, description } = await getGzhContent(
68
85
  inputContent,
69
86
  theme,
70
87
  highlightTheme,
71
- isMacStyle
88
+ isMacStyle,
89
+ isAddFootnote,
72
90
  );
73
91
  ```
74
92
 
75
93
  #### 参数说明
76
94
 
77
- | 参数名 | 类型 | 说明 |
78
- | ---------------- | --------- | ----------------------------------------------- |
79
- | `inputContent` | `string` | 输入的 Markdown 文本 |
80
- | `theme` | `string` | 排版主题 ID(如 `"lapis"`, `"default"` 等,见下文) |
81
- | `highlightTheme` | `string` | 代码高亮主题(如 `"github"`, `"solarized-light"`, 见下文) |
82
- | `isMacStyle` | `boolean` | 代码块是否启用 Mac 风格 |
83
-
84
- 排版主题可选参数:
85
-
86
- - default
87
- - orangeheart
88
- - rainbow
89
- - lapis
90
- - pie
91
- - maize
92
- - purple
93
- - phycat
94
-
95
- 高亮主题可选参数:
96
-
97
- - atom-one-dark
98
- - atom-one-light
99
- - dracula
100
- - github-dark
101
- - github
102
- - monokai
103
- - solarized-dark
104
- - solarized-light
105
- - xcode
95
+ | 参数名 | 类型 | 说明 |
96
+ | --- | --- | --- |
97
+ | `inputContent` | `string` | 输入的 Markdown 文本,必填 |
98
+ | `theme` | `string` | 排版主题 ID,必填 |
99
+ | `highlightTheme` | `string` | 代码高亮主题,必填 |
100
+ | `isMacStyle` | `boolean` | 是否启用代码块 Mac 风格,默认开启 |
101
+ | `isAddFootnote` | `boolean` | 是否将链接转脚注,默认开启 |
102
+
103
+ 排版主题:
104
+
105
+ - default / orangeheart / rainbow / lapis / pie / maize / purple / phycat
106
+
107
+ 高亮主题:
108
+
109
+ - atom-one-dark / atom-one-light / dracula / github-dark / github / monokai / solarized-dark / solarized-light / xcode
106
110
 
107
111
  #### 返回值
108
112
 
109
- | 字段 | 类型 | 说明 |
110
- | ------------- | -------- | ---------------------- |
111
- | `title` | `string` | `frontmatter`中的文章标题,见下文 |
112
- | `cover` | `string` | `frontmatter`中的文章封面图,见下文 |
113
- | `content` | `string` | 转换后的 HTML 文章内容,发布接口需要用到 |
114
- | `description` | `string` | `frontmatter`中的文章简介,见下文 |
113
+ | 字段 | 类型 | 说明 |
114
+ | --- | --- | --- |
115
+ | `title` | `string` | frontmatter 中获取的文章标题 |
116
+ | `cover` | `string` | 封面图 |
117
+ | `content` | `string` | 转换后的 HTML 内容 |
118
+ | `description` | `string` | frontmatter 中的文章简介 |
115
119
 
116
120
  ---
117
121
 
@@ -120,129 +124,87 @@ const { title, cover, content, description } = await getGzhContent(
120
124
  ```ts
121
125
  import { publishToDraft } from "@wenyan-md/core/publish";
122
126
 
123
- // 方式1,你可以通过环境变量注入WECHAT_APP_ID和WECHAT_APP_SECRET
124
- const wechatAppId = process.env.WECHAT_APP_ID;
125
- const wechatAppSecret = process.env.WECHAT_APP_SECRET;
126
-
127
- if (!wechatAppId || !wechatAppSecret) {
128
- console.error("WECHAT_APP_ID and WECHAT_APP_SECRET must be set as environment variables.");
129
- process.exit(1);
130
- }
131
-
132
- const data = await publishToDraft(title, content, cover);
127
+ const data = await publishToDraft(title, content, cover, wechatAppId, wechatAppSecret);
133
128
 
134
129
  if (data.media_id) {
135
- console.log(`上传成功,media_id: ${data.media_id}`);
136
- } else {
137
- console.error(`上传失败,\n${data}`);
130
+ console.log("上传成功:", data.media_id);
138
131
  }
139
-
140
- // 方式2,你可以直接以参数形式传入WECHAT_APP_ID和WECHAT_APP_SECRET
141
-
142
- const data = await publishToDraft(title, content, cover, wechatAppId, wechatAppSecret);
143
132
  ```
144
133
 
145
134
  #### 参数说明
146
135
 
147
- | 参数名 | 类型 | 说明 |
148
- | --------- | -------- | ---------- |
149
- | `title` | `string` | 文章标题 |
150
- | `content` | `string` | 文章 HTML 内容 |
151
- | `cover` | `string` | 封面图 URL |
152
-
153
- #### 返回值
136
+ | 参数名 | 类型 | 说明 |
137
+ | --- | --- | --- |
138
+ | `title` | `string` | 文章标题 |
139
+ | `content` | `string` | HTML 内容 |
140
+ | `cover` | `string` | 封面图 URL |
141
+ | `wechatAppId` | `string` | 微信公众号 APPID |
142
+ | `wechatAppSecret` | `string` | 微信公众号 APP_SECRET |
154
143
 
155
- 返回 **微信公众号 API 的响应对象**,常见字段:
144
+ #### 环境变量注入APPID和APP_SECRET
156
145
 
157
- | 字段 | 类型 | 说明 |
158
- | ---------- | -------- | --------------------- |
159
- | `media_id` | `string` | 草稿的 media\_id,后续发布时需要 |
146
+ 也可以通过环境变量注入APPID和APP_SECRET:
160
147
 
161
- ## 环境变量
148
+ ```sh
149
+ export WECHAT_APP_ID=xxx
150
+ export WECHAT_APP_SECRET=yyy
151
+ ```
162
152
 
163
- 在使用 `publishToDraft` 前,需要在环境中配置:
153
+ ```ts
154
+ import { publishToDraft } from "@wenyan-md/core/publish";
164
155
 
165
- * `WECHAT_APP_ID`
166
- * `WECHAT_APP_SECRET`
156
+ const data = await publishToDraft(title, content, cover);
167
157
 
168
- 推荐通过 `.env` 文件或 CI/CD 环境变量注入。
158
+ if (data.media_id) {
159
+ console.log("上传成功:", data.media_id);
160
+ }
161
+ ```
169
162
 
170
163
  ## 浏览器直接使用
171
164
 
172
- 除了通过 `npm` 安装外,你也可以直接在浏览器环境中引入打包好的版本(IIFE 格式),无需构建工具。
173
-
174
- 推荐使用 **[unpkg](https://unpkg.com/)** 或 **[jsDelivr](https://www.jsdelivr.com/)**。
175
-
176
- 浏览器版本的文颜需自行引入依赖`highlight.js`和`csstree`。
165
+ 文颜 CORE 提供浏览器可直接引入的 IIFE 构建版本,适合前端或纯静态页面使用。目前不支持“发布到微信公众号草稿箱”功能。
177
166
 
178
167
  ```html
179
- <!-- 添加依赖 -->
180
168
  <script src="https://cdn.jsdelivr.net/npm/css-tree/dist/csstree.js"></script>
181
169
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
182
-
183
- <!-- 从 unpkg 引入 -->
184
- <script src="https://unpkg.com/@wenyan-md/core/dist/math/wenyan-math.js"></script>
185
- <script src="https://unpkg.com/@wenyan-md/core/dist/styles/wenyan-styles.js"></script>
186
- <script src="https://unpkg.com/@wenyan-md/core/dist/browser/wenyan-core.js"></script>
187
-
188
- <!-- 或者从 jsDelivr 引入 -->
189
- <script src="https://cdn.jsdelivr.net/npm/@wenyan-md/core/dist/math/wenyan-math.js"></script>
190
- <script src="https://cdn.jsdelivr.net/npm/@wenyan-md/core/dist/styles/wenyan-styles.js"></script>
191
170
  <script src="https://cdn.jsdelivr.net/npm/@wenyan-md/core/dist/browser/wenyan-core.js"></script>
192
171
 
193
172
  <script>
194
- // 使用全局变量 WenyanCore
195
173
  const { configureMarked, renderMarkdown, themes } = WenyanCore;
196
-
197
- (async () => {
198
- configureMarked();
199
- const input = "# Hello from Browser";
200
- const content = await renderMarkdown(input);
201
- const theme = themes["lapis"];
202
- const styledCss = await theme.getCss();
203
- const style = document.createElement("style");
204
- style.textContent = styledCss;
205
- document.head.appendChild(style);
206
- document.body.innerHTML = content;
207
- })();
174
+ configureMarked();
175
+ const html = await renderMarkdown('# Hello from Browser');
176
+ document.body.innerHTML = html;
208
177
  </script>
209
178
  ```
210
179
 
211
- 这样你就可以在 **任意前端项目** **纯静态页面** 中直接使用文颜的功能。
212
-
213
- ## 微信公众号 IP 白名单
214
-
215
- 请务必将服务器 IP 加入公众号平台的 IP 白名单,以确保上传接口调用成功。
216
- 详细配置说明请参考:[https://yuzhi.tech/docs/wenyan/upload](https://yuzhi.tech/docs/wenyan/upload)
217
-
218
- ## 配置说明(Frontmatter)
180
+ ## Markdown Frontmatter 说明
219
181
 
220
- 为了可以正确上传文章,需要在每一篇 Markdown 文章的开头添加一段`frontmatter`,提供`title`、`cover`两个字段:
182
+ 每篇文章顶部需包含 frontmatter
221
183
 
222
184
  ```md
223
185
  ---
224
- title: 在本地跑一个大语言模型(2) - 给模型提供外部知识库
225
- cover: /Users/lei/Downloads/result_image.jpg
226
- description: 本文介绍如何为本地大语言模型提供外部知识库。
186
+ title: 示例文章
187
+ cover: /path/to/cover.jpg
188
+ description: 文章简介
227
189
  ---
228
190
  ```
229
191
 
230
- * `title` 是文章标题,必填。
231
- * `cover` 是文章封面,支持本地路径和网络图片:
232
-
233
- * 如果正文有至少一张图片,可省略,此时将使用其中一张作为封面;
234
- * 如果正文无图片,则必须提供 cover。
192
+ * `title`:必填
193
+ * `cover`:本地路径或网络图片(正文有图可省略)
235
194
 
236
- ## 关于图片自动上传
195
+ ## 微信公众号 IP 白名单
237
196
 
238
- * 支持图片路径:
197
+ > ⚠️ 重要
198
+ >
199
+ > 请确保运行服务的服务器 IP 已加入微信公众号后台 IP 白名单。
239
200
 
240
- * 本地路径(如:`/Users/lei/Downloads/result_image.jpg`)
241
- * 网络路径(如:`https://example.com/image.jpg`)
201
+ 配置说明:[https://yuzhi.tech/docs/wenyan/upload](https://yuzhi.tech/docs/wenyan/upload)
242
202
 
243
203
  ## 赞助
244
204
 
245
- 如果您觉得不错,可以给我家猫咪买点罐头吃。[喂猫❤️](https://yuzhi.tech/sponsor)
205
+ 如果你觉得文颜对你有帮助,可以给我家猫咪买点罐头 ❤️
206
+
207
+ [https://yuzhi.tech/sponsor](https://yuzhi.tech/sponsor)
246
208
 
247
209
  ## License
248
210
 
@@ -90,5 +90,5 @@ Please report this to https://github.com/markedjs/marked.`,r){const o="<p>An err
90
90
  `:`<p>${o.parseInline(t.tokens)}</p>
91
91
  `},i.image=function(t,a,s){return`<img src="${t.href}" alt="${t.text||""}" title="${t.text||""}">`},$.use({renderer:i}),Xr=!0})(),Pe)}function yi(r){const n=new Map;return r.split(/\s+/).forEach(i=>{const[o,t]=i.split("=");o&&t&&n.set(o,t.replace(/^["']|["']$/g,""))}),n}async function wi(r){const{attributes:n,body:i}=vi(r),o={};let t="";const{title:a,description:s,cover:u}=n;return a&&(o.title=a),s&&(t+="> "+s+`
92
92
 
93
- `,o.description=s),u&&(o.cover=u),o.body=t+i,o}async function Ai(r){await et();const n=$.parse(r);return await ot.renderMathInHtml(n)}async function Si(r,n,i,o=!0,t=!0){let a=Xe.themes.default;if(n&&(a=Xe.themes[n],a||(a=Object.values(Xe.themes).find(p=>p.name.toLowerCase()===n.toLowerCase()))),!a)throw new Error("主题不存在");if(!(i in Wn.hlThemes))throw new Error("代码块主题不存在");const s=rt(await a.getCss()),c=await Wn.hlThemes[i].getCss();return nt(r,s,c,o,t)}async function nt(r,n,i,o=!0,t=!0){t&&it(!1,r),n=tt(n,{"#wenyan pre code":[{property:"font-family",value:qn,append:!0}],"#wenyan pre":[{property:"font-size",value:"12px",append:!0}]});const a=N.parse(n,{context:"stylesheet",positions:!1,parseAtrulePrelude:!1,parseCustomProperty:!1,parseValue:!1}),s=N.parse(i,{context:"stylesheet",positions:!1,parseAtrulePrelude:!1,parseCustomProperty:!1,parseValue:!1});if(a.children.appendList(s.children),o){const c=N.parse(at,{context:"stylesheet",positions:!1,parseAtrulePrelude:!1,parseCustomProperty:!1,parseValue:!1});a.children.appendList(c.children)}N.walk(a,{visit:"Rule",enter(c,p,f){const b=c.prelude.children;b&&b.forEach(k=>{const E=N.generate(k),T=c.block.children.toArray();E==="#wenyan"?T.forEach(L=>{const H=N.generate(L.value);r.style[L.property]=H}):r.querySelectorAll(E).forEach(H=>{T.forEach(Z=>{const K=N.generate(Z.value);H.style[Z.property]=K})})})}});let u=r.querySelectorAll("mjx-container");return u.forEach(c=>{const p=c.querySelector("svg");p.style.width=p.getAttribute("width"),p.style.height=p.getAttribute("height"),p.removeAttribute("width"),p.removeAttribute("height");const f=c.parentElement;c.remove(),f.appendChild(p),f.classList.contains("block-equation")&&f.setAttribute("style","text-align: center; margin-bottom: 1rem;")}),u=r.querySelectorAll("pre code"),u.forEach(c=>{c.innerHTML=c.innerHTML.replace(/\n/g,"<br>").replace(/(>[^<]+)|(^[^<]+)/g,p=>p.replace(/\s/g,"&nbsp;"))}),u=r.querySelectorAll("h1, h2, h3, h4, h5, h6, blockquote, pre"),u.forEach(c=>{const p=new Map,f=new Map;N.walk(a,{visit:"Rule",enter(b){const k=N.generate(b.prelude),E=c.tagName.toLowerCase();k.includes(`${E}::after`)?Nn(b,p):k.includes(`${E}::before`)&&Nn(b,f)}}),p.size>0&&c.appendChild(Bn(p,r.ownerDocument)),f.size>0&&c.insertBefore(Bn(f,r.ownerDocument),c.firstChild)}),u=r.querySelectorAll("li"),u.forEach(c=>{const p=r.ownerDocument.createElement("section");for(;c.firstChild;)p.appendChild(c.firstChild);c.appendChild(p)}),r.setAttribute("data-provider","WenYan"),`${r.outerHTML.replace(/class="mjx-solid"/g,'fill="none" stroke-width="70"')}`}function rt(r){const n=/--([a-zA-Z0-9\-]+):\s*([^;()]*\((?:[^()]*|\([^()]*\))*\)[^;()]*|[^;]+);/g,i=/var\(--([a-zA-Z0-9\-]+)\)/g,o={};let t;for(;(t=n.exec(r))!==null;){const u=t[1],c=t[2].trim().replaceAll(`
93
+ `,o.description=s),u&&(o.cover=u),o.body=t+i,o}async function Ai(r){await et();const n=$.parse(r);return await ot.renderMathInHtml(n)}async function Si(r,n,i,o=!0,t=!0){let a=Xe.themes.default;if(n&&(a=Xe.themes[n],a||(a=Object.values(Xe.themes).find(p=>p.name.toLowerCase()===n.toLowerCase()))),!a)throw new Error("主题不存在");if(!(i in Wn.hlThemes))throw new Error("代码块主题不存在");const s=rt(await a.getCss()),c=await Wn.hlThemes[i].getCss();return nt(r,s,c,o,t)}async function nt(r,n,i,o=!0,t=!0){t&&it(!1,r),n=tt(n,{"#wenyan pre code":[{property:"font-family",value:qn,append:!0}],"#wenyan pre":[{property:"font-size",value:"12px",append:!0}]});const a=N.parse(n,{context:"stylesheet",positions:!1,parseAtrulePrelude:!1,parseCustomProperty:!1,parseValue:!1}),s=N.parse(i,{context:"stylesheet",positions:!1,parseAtrulePrelude:!1,parseCustomProperty:!1,parseValue:!1});if(a.children.appendList(s.children),o){const c=N.parse(at,{context:"stylesheet",positions:!1,parseAtrulePrelude:!1,parseCustomProperty:!1,parseValue:!1});a.children.appendList(c.children)}N.walk(a,{visit:"Rule",enter(c,p,f){const b=c.prelude.children;b&&b.forEach(k=>{const E=N.generate(k),T=c.block.children.toArray();E==="#wenyan"?T.forEach(L=>{const H=N.generate(L.value);r.style[L.property]=H}):r.querySelectorAll(E).forEach(H=>{T.forEach(Z=>{const K=N.generate(Z.value);H.style[Z.property]=K})})})}});let u=r.querySelectorAll("mjx-container");return u.forEach(c=>{const p=c.querySelector("svg");p.style.width=p.getAttribute("width"),p.style.height=p.getAttribute("height"),p.removeAttribute("width"),p.removeAttribute("height");const f=c.parentElement;c.remove(),f.appendChild(p),f.classList.contains("block-equation")&&f.setAttribute("style","text-align: center; margin-bottom: 1rem;")}),u=r.querySelectorAll("pre code"),u.forEach(c=>{c.innerHTML=c.innerHTML.replace(/\n/g,"<br>").replace(/(>[^<]+)|(^[^<]+)/g,p=>p.replace(/\s/g,"&nbsp;"))}),u=r.querySelectorAll("h1, h2, h3, h4, h5, h6, blockquote, pre"),u.forEach(c=>{const p=new Map,f=new Map;N.walk(a,{visit:"Rule",enter(b){const k=N.generate(b.prelude),E=c.tagName.toLowerCase();k.includes(`${E}::after`)?Nn(b,p):k.includes(`${E}::before`)&&Nn(b,f)}}),p.size>0&&c.appendChild(Bn(p,r.ownerDocument)),f.size>0&&c.insertBefore(Bn(f,r.ownerDocument),c.firstChild)}),u=r.querySelectorAll("li"),u.forEach(c=>{const p=r.ownerDocument.createElement("section");for(;c.firstChild;)p.appendChild(c.firstChild);c.appendChild(p)}),r.setAttribute("data-provider","WenYan"),`${r.outerHTML.replace(/class="mjx-solid"/g,'fill="none" stroke-width="70"').replace(/\n<li/g,"<li").replace(/<\/li>\n/g,"</li>")}`}function rt(r){const n=/--([a-zA-Z0-9\-]+):\s*([^;()]*\((?:[^()]*|\([^()]*\))*\)[^;()]*|[^;]+);/g,i=/var\(--([a-zA-Z0-9\-]+)\)/g,o={};let t;for(;(t=n.exec(r))!==null;){const u=t[1],c=t[2].trim().replaceAll(`
94
94
  `,"");o[u]=c}o["sans-serif-font"]||(o["sans-serif-font"]=Jr),o["monospace-font"]||(o["monospace-font"]=qn);function a(u,c,p=new Set){if(p.has(u))return u;p.add(u);let f=u,b;for(;(b=i.exec(f))!==null;){const k=b[1];if(c[k]){const E=a(c[k],c,p);f=f.replace(b[0],E)}}return f}for(const u in o){const c=a(o[u],o);o[u]=c}let s=r;for(;(t=i.exec(r))!==null;){const u=t[1];o[u]&&(s=s.replace(t[0],o[u]))}return s.replace(/:root\s*\{[^}]*\}/g,"")}function tt(r,n){const i=N.parse(r,{context:"stylesheet",positions:!1,parseAtrulePrelude:!1,parseCustomProperty:!1,parseValue:!1});return N.walk(i,{visit:"Rule",leave:(o,t,a)=>{if(o.prelude.type!=="SelectorList")return;const s=o.prelude.children.toArray().map(u=>N.generate(u));if(s){const u=s[0],c=n[u];if(!c)return;for(const{property:p,value:f,append:b}of c)if(f){let k=!1;N.walk(o.block,E=>{E.type==="Declaration"&&E.property===p&&(E.value=N.parse(f,{context:"value"}),k=!0)}),!k&&b&&o.block.children.prepend(a.createItem({type:"Declaration",property:p,value:N.parse(f,{context:"value"})}))}}}}),N.generate(i)}function Nn(r,n){N.walk(r.block,{visit:"Declaration",enter(i){const o=i.property,t=N.generate(i.value);n.set(o,t)}})}function Bn(r,n){const i=n.createElement("section");r.get("content")&&(i.textContent=r.get("content").replace(/['"]/g,""),r.delete("content"));for(const[a,s]of r)if(s.includes("url(")){const u=s.match(/data:image\/svg\+xml;utf8,(.*<\/svg>)/),c=s.match(/data:image\/svg\+xml;base64,([^"'\)]*)["']?\)/),p=s.match(/(?:"|')?(https?[^"'\)]*)(?:"|')?\)/);if(u){const f=decodeURIComponent(u[1]);i.innerHTML=f}else if(c){const f=atob(c[1]);i.innerHTML=f}else if(p){const f=n.createElement("img");f.src=p[1],f.setAttribute("style","vertical-align: top;"),i.appendChild(f)}r.delete(a)}const t=Array.from(r.entries()).map(([a,s])=>`${a}: ${s}`).join("; ");return i.style.cssText=t,i}function it(r,n){let i=[],o=0;if(n.querySelectorAll("a[href]").forEach(a=>{const s=a.textContent||a.innerText,u=a.getAttribute("href");i.push([++o,s,u]);const c=n.ownerDocument.createElement("sup");c.setAttribute("class","footnote"),c.innerHTML=`[${o}]`,a.after(c)}),o>0)if(r){const s=`<h3>引用链接</h3><div id="footnotes"><ul>${i.map(u=>u[1]===u[2]?`<li id="#footnote-${u[0]}">[${u[0]}]: <i>${u[1]}</i></li>`:`<li id="#footnote-${u[0]}">[${u[0]}] ${u[1]}: <i>${u[2]}</i></li>`).join("")}</ul></div>`;n.innerHTML+=s}else{const s=`<h3>引用链接</h3><section id="footnotes">${i.map(u=>u[1]===u[2]?`<p><span class="footnote-num">[${u[0]}]</span><span class="footnote-txt"><i>${u[1]}</i></span></p>`:`<p><span class="footnote-num">[${u[0]}]</span><span class="footnote-txt">${u[1]}: <i>${u[2]}</i></span></p>`).join("")}</section>`;n.innerHTML+=s}}return U.addFootnotes=it,U.buildPseudoSpan=Bn,U.configureMarked=et,U.extractDeclarations=Nn,U.getContentForGzhBuiltinTheme=Si,U.getContentForGzhCustomCss=nt,U.handleFrontMatter=wi,U.modifyCss=tt,U.monospace=qn,U.renderMarkdown=Ai,U.replaceCSSVariables=rt,U.sansSerif=Jr,U.serif=bi,Object.defineProperty(U,Symbol.toStringTag,{value:"Module"}),U})({},hljs,csstree,WenyanMath,macStyleCss,themes,hlThemes);
package/dist/core.js CHANGED
@@ -329,7 +329,7 @@ async function getContentForGzhCustomCss(wenyanElement, customCss, highlightCss,
329
329
  li.appendChild(section);
330
330
  });
331
331
  wenyanElement.setAttribute("data-provider", "WenYan");
332
- return `${wenyanElement.outerHTML.replace(/class="mjx-solid"/g, 'fill="none" stroke-width="70"')}`;
332
+ return `${wenyanElement.outerHTML.replace(/class="mjx-solid"/g, 'fill="none" stroke-width="70"').replace(/\n<li/g, "<li").replace(/<\/li>\n/g, "</li>")}`;
333
333
  }
334
334
  function replaceCSSVariables(css) {
335
335
  const variablePattern = /--([a-zA-Z0-9\-]+):\s*([^;()]*\((?:[^()]*|\([^()]*\))*\)[^;()]*|[^;]+);/g;
package/dist/hltheme.js CHANGED
@@ -1,3 +1,13 @@
1
+ import { n as normalizeCssLoader } from "./utils-YieK94fG.js";
2
+ const __vite_glob_0_0 = "pre{background:#282c34}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}";
3
+ const __vite_glob_0_1 = "pre{background:#fafafa}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}";
4
+ const __vite_glob_0_2 = "pre{background:#282936}\n/*!\n Theme: Dracula\n Author: Mike Barkmin (http://github.com/mikebarkmin) based on Dracula Theme (http://github.com/dracula)\n License: ~ MIT (or more permissive) [via base16-schemes-source]\n Maintainer: @highlightjs/core-team\n Version: 2021.09.0\n*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#e9e9f4;background:#282936}.hljs ::selection,.hljs::selection{background-color:#4d4f68;color:#e9e9f4}.hljs-comment{color:#626483}.hljs-tag{color:#62d6e8}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#e9e9f4}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#ea51b2}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#b45bcf}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#00f769}.hljs-strong{font-weight:700;color:#00f769}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#ebff87}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#a1efe4}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#62d6e8}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#b45bcf}.hljs-emphasis{color:#b45bcf;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#00f769}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700}";
5
+ const __vite_glob_0_3 = "pre{background:#0d1117}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!\n Theme: GitHub Dark\n Description: Dark theme as seen on github.com\n Author: github.com\n Maintainer: @Hirse\n Updated: 2021-05-15\n\n Outdated base version: https://github.com/primer/github-syntax-dark\n Current colors taken from GitHub's CSS\n*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}";
6
+ const __vite_glob_0_4 = "pre{background:#fff}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!\n Theme: GitHub\n Description: Light theme as seen on github.com\n Author: github.com\n Maintainer: @Hirse\n Updated: 2021-05-15\n\n Outdated base version: https://github.com/primer/github-syntax-light\n Current colors taken from GitHub's CSS\n*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}\n";
7
+ const __vite_glob_0_5 = "pre{background:#272822}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#272822;color:#ddd}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-number,.hljs-selector-tag,.hljs-strong,.hljs-tag{color:#f92672}.hljs-code{color:#66d9ef}.hljs-attr,.hljs-attribute,.hljs-link,.hljs-regexp,.hljs-symbol{color:#bf79db}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-emphasis,.hljs-section,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-string,.hljs-subst,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#a6e22e}.hljs-class .hljs-title,.hljs-title.class_{color:#fff}.hljs-comment,.hljs-deletion,.hljs-meta,.hljs-quote{color:#75715e}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-section,.hljs-selector-id,.hljs-selector-tag,.hljs-title,.hljs-type{font-weight:700}";
8
+ const __vite_glob_0_6 = "pre{background:#002b36}\n/*!\n Theme: Solarized Dark\n Author: Ethan Schoonover (modified by aramisgithub)\n License: ~ MIT (or more permissive) [via base16-schemes-source]\n Maintainer: @highlightjs/core-team\n Version: 2021.09.0\n*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#93a1a1;background:#002b36}.hljs ::selection,.hljs::selection{background-color:#586e75;color:#93a1a1}.hljs-comment{color:#657b83}.hljs-tag{color:#839496}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#93a1a1}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#dc322f}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#cb4b16}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#b58900}.hljs-strong{font-weight:700;color:#b58900}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#859900}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#2aa198}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#268bd2}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#6c71c4}.hljs-emphasis{color:#6c71c4;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#d33682}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700}";
9
+ const __vite_glob_0_7 = "pre{background:#fdf6e3}\n/*!\n Theme: Solarized Light\n Author: Ethan Schoonover (modified by aramisgithub)\n License: ~ MIT (or more permissive) [via base16-schemes-source]\n Maintainer: @highlightjs/core-team\n Version: 2021.09.0\n*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#586e75;background:#fdf6e3}.hljs ::selection,.hljs::selection{background-color:#93a1a1;color:#586e75}.hljs-comment{color:#839496}.hljs-tag{color:#657b83}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#586e75}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#dc322f}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#cb4b16}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#b58900}.hljs-strong{font-weight:700;color:#b58900}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#859900}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#2aa198}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#268bd2}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#6c71c4}.hljs-emphasis{color:#6c71c4;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#d33682}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700}";
10
+ const __vite_glob_0_8 = "pre{background:#fff}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.xml .hljs-meta{color:silver}.hljs-comment,.hljs-quote{color:#007400}.hljs-attribute,.hljs-keyword,.hljs-literal,.hljs-name,.hljs-selector-tag,.hljs-tag{color:#aa0d91}.hljs-template-variable,.hljs-variable{color:#3f6e74}.hljs-code,.hljs-meta .hljs-string,.hljs-string{color:#c41a16}.hljs-link,.hljs-regexp{color:#0e0eff}.hljs-bullet,.hljs-number,.hljs-symbol,.hljs-title{color:#1c00cf}.hljs-meta,.hljs-section{color:#643820}.hljs-built_in,.hljs-class .hljs-title,.hljs-params,.hljs-title.class_,.hljs-type{color:#5c2699}.hljs-attr{color:#836c28}.hljs-subst{color:#000}.hljs-formula{background-color:#eee;font-style:italic}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-selector-class,.hljs-selector-id{color:#9b703f}.hljs-doctag,.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}";
1
11
  const themeMeta = [
2
12
  {
3
13
  id: "atom-one-dark"
@@ -28,15 +38,15 @@ const themeMeta = [
28
38
  }
29
39
  ];
30
40
  const themeCssModules = /* @__PURE__ */ Object.assign({
31
- "./highlight/styles/atom-one-dark.min.css": () => import("./atom-one-dark.min-Du_gemwz.js").then((m) => m["default"]),
32
- "./highlight/styles/atom-one-light.min.css": () => import("./atom-one-light.min-BameqVOr.js").then((m) => m["default"]),
33
- "./highlight/styles/dracula.min.css": () => import("./dracula.min-DhYK88zj.js").then((m) => m["default"]),
34
- "./highlight/styles/github-dark.min.css": () => import("./github-dark.min-rUG1U3df.js").then((m) => m["default"]),
35
- "./highlight/styles/github.min.css": () => import("./github.min-DEfTaph9.js").then((m) => m["default"]),
36
- "./highlight/styles/monokai.min.css": () => import("./monokai.min-BszscX_9.js").then((m) => m["default"]),
37
- "./highlight/styles/solarized-dark.min.css": () => import("./solarized-dark.min-mpQq1Jcj.js").then((m) => m["default"]),
38
- "./highlight/styles/solarized-light.min.css": () => import("./solarized-light.min-C5Ersr9g.js").then((m) => m["default"]),
39
- "./highlight/styles/xcode.min.css": () => import("./xcode.min-DWdHLdoo.js").then((m) => m["default"])
41
+ "./highlight/styles/atom-one-dark.min.css": __vite_glob_0_0,
42
+ "./highlight/styles/atom-one-light.min.css": __vite_glob_0_1,
43
+ "./highlight/styles/dracula.min.css": __vite_glob_0_2,
44
+ "./highlight/styles/github-dark.min.css": __vite_glob_0_3,
45
+ "./highlight/styles/github.min.css": __vite_glob_0_4,
46
+ "./highlight/styles/monokai.min.css": __vite_glob_0_5,
47
+ "./highlight/styles/solarized-dark.min.css": __vite_glob_0_6,
48
+ "./highlight/styles/solarized-light.min.css": __vite_glob_0_7,
49
+ "./highlight/styles/xcode.min.css": __vite_glob_0_8
40
50
  });
41
51
  const hlThemes = {};
42
52
  for (const meta of themeMeta) {
@@ -45,7 +55,7 @@ for (const meta of themeMeta) {
45
55
  if (cssModuleLoader) {
46
56
  hlThemes[meta.id] = {
47
57
  ...meta,
48
- getCss: cssModuleLoader
58
+ getCss: normalizeCssLoader(cssModuleLoader)
49
59
  };
50
60
  } else {
51
61
  console.warn(`[Highlight Themes] CSS file not found for theme: ${meta.id}`);
package/dist/publish.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { JSDOM } from "jsdom";
2
2
  import { fileFromPath } from "formdata-node/file-from-path";
3
3
  import { FormData, Blob } from "formdata-node";
4
- import path from "node:path";
4
+ import path$1 from "node:path";
5
5
  import { stat } from "node:fs/promises";
6
6
  import { FormDataEncoder } from "form-data-encoder";
7
7
  import { Readable } from "node:stream";
8
+ import path from "path";
8
9
  const tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
9
10
  const publishUrl = "https://api.weixin.qq.com/cgi-bin/draft/add";
10
11
  const uploadUrl = `https://api.weixin.qq.com/cgi-bin/material/add_material`;
@@ -62,9 +63,59 @@ async function publishArticle(title, content, thumbMediaId, accessToken) {
62
63
  }
63
64
  return await response.json();
64
65
  }
65
- const hostImagePath = process.env.HOST_IMAGE_PATH || "";
66
- const dockerImagePath = "/mnt/host-downloads";
67
- async function uploadImage(imageUrl, accessToken, fileName) {
66
+ function normalizePath(p) {
67
+ return p.replace(/\\/g, "/").replace(/\/+$/, "");
68
+ }
69
+ function isAbsolutePath(path2) {
70
+ if (!path2) return false;
71
+ const winAbsPattern = /^[a-zA-Z]:\//;
72
+ const linuxAbsPattern = /^\//;
73
+ return winAbsPattern.test(path2) || linuxAbsPattern.test(path2);
74
+ }
75
+ const RuntimeEnv = {
76
+ isContainer: !!process.env.CONTAINERIZED,
77
+ hostFilePath: normalizePath(process.env.HOST_FILE_PATH || ""),
78
+ containerFilePath: normalizePath(process.env.CONTAINER_FILE_PATH || "/mnt/host-downloads"),
79
+ resolveLocalPath(inputPath, relativeBase) {
80
+ if (!this.isContainer) {
81
+ if (relativeBase) {
82
+ return path.resolve(relativeBase, inputPath);
83
+ } else {
84
+ if (!path.isAbsolute(inputPath)) {
85
+ throw new Error(
86
+ `Invalid input: '${inputPath}'. When relativeBase is not provided, inputPath must be an absolute path.`
87
+ );
88
+ }
89
+ return path.normalize(inputPath);
90
+ }
91
+ }
92
+ let normalizedInput = normalizePath(inputPath);
93
+ relativeBase = normalizePath(relativeBase || "");
94
+ if (relativeBase) {
95
+ if (!isAbsolutePath(normalizedInput)) {
96
+ normalizedInput = relativeBase + (normalizedInput.startsWith("/") ? "" : "/") + normalizedInput;
97
+ }
98
+ } else {
99
+ if (!isAbsolutePath(normalizedInput)) {
100
+ throw new Error(
101
+ `Invalid input: '${inputPath}'. When relativeBase is not provided, inputPath must be an absolute path.`
102
+ );
103
+ }
104
+ }
105
+ if (normalizedInput.startsWith(this.hostFilePath)) {
106
+ let relativePart = normalizedInput.slice(this.hostFilePath.length);
107
+ if (relativePart && !relativePart.startsWith("/")) {
108
+ return normalizedInput;
109
+ }
110
+ if (!relativePart.startsWith("/")) {
111
+ relativePart = "/" + relativePart;
112
+ }
113
+ return this.containerFilePath + relativePart;
114
+ }
115
+ return normalizedInput;
116
+ }
117
+ };
118
+ async function uploadImage(imageUrl, accessToken, fileName, relativePath) {
68
119
  let fileData;
69
120
  let finalName;
70
121
  if (imageUrl.startsWith("http")) {
@@ -72,8 +123,8 @@ async function uploadImage(imageUrl, accessToken, fileName) {
72
123
  if (!response.ok || !response.body) {
73
124
  throw new Error(`Failed to download image from URL: ${imageUrl}`);
74
125
  }
75
- const fileNameFromUrl = path.basename(imageUrl.split("?")[0]);
76
- const ext = path.extname(fileNameFromUrl);
126
+ const fileNameFromUrl = path$1.basename(imageUrl.split("?")[0]);
127
+ const ext = path$1.extname(fileNameFromUrl);
77
128
  finalName = fileName ?? (ext === "" ? `${fileNameFromUrl}.jpg` : fileNameFromUrl);
78
129
  const buffer = await response.arrayBuffer();
79
130
  if (buffer.byteLength === 0) {
@@ -82,16 +133,15 @@ async function uploadImage(imageUrl, accessToken, fileName) {
82
133
  const contentType = response.headers.get("content-type") || "image/jpeg";
83
134
  fileData = new Blob([buffer], { type: contentType });
84
135
  } else {
85
- const localImagePath = hostImagePath ? imageUrl.replace(hostImagePath, dockerImagePath) : imageUrl;
86
- const safePath = path.resolve(localImagePath);
87
- const stats = await stat(safePath);
136
+ const resolvedPath = RuntimeEnv.resolveLocalPath(imageUrl, relativePath);
137
+ const stats = await stat(resolvedPath);
88
138
  if (stats.size === 0) {
89
- throw new Error(`本地图片大小为0,无法上传: ${safePath}`);
139
+ throw new Error(`本地图片大小为0,无法上传: ${resolvedPath}`);
90
140
  }
91
- const fileNameFromLocal = path.basename(localImagePath);
92
- const ext = path.extname(fileNameFromLocal);
141
+ const fileNameFromLocal = path$1.basename(resolvedPath);
142
+ const ext = path$1.extname(fileNameFromLocal);
93
143
  finalName = fileName ?? (ext === "" ? `${fileNameFromLocal}.jpg` : fileNameFromLocal);
94
- fileData = await fileFromPath(safePath);
144
+ fileData = await fileFromPath(resolvedPath);
95
145
  }
96
146
  const data = await uploadMaterial("image", fileData, finalName, accessToken);
97
147
  if (data.errcode) {
@@ -99,7 +149,7 @@ async function uploadImage(imageUrl, accessToken, fileName) {
99
149
  }
100
150
  return data;
101
151
  }
102
- async function uploadImages(content, accessToken) {
152
+ async function uploadImages(content, accessToken, relativePath) {
103
153
  if (!content.includes("<img")) {
104
154
  return { html: content, firstImageId: "" };
105
155
  }
@@ -110,7 +160,7 @@ async function uploadImages(content, accessToken) {
110
160
  const dataSrc = element.getAttribute("src");
111
161
  if (dataSrc) {
112
162
  if (!dataSrc.startsWith("https://mmbiz.qpic.cn")) {
113
- const resp = await uploadImage(dataSrc, accessToken);
163
+ const resp = await uploadImage(dataSrc, accessToken, void 0, relativePath);
114
164
  element.setAttribute("src", resp.url);
115
165
  return resp.media_id;
116
166
  } else {
@@ -124,7 +174,7 @@ async function uploadImages(content, accessToken) {
124
174
  const updatedHtml = dom.serialize();
125
175
  return { html: updatedHtml, firstImageId };
126
176
  }
127
- async function publishToDraft(title, content, cover, appId, appSecret) {
177
+ async function publishToDraft(title, content, cover, appId, appSecret, relativePath) {
128
178
  const accessToken = await fetchAccessToken(appId, appSecret);
129
179
  if (!accessToken.access_token) {
130
180
  if (accessToken.errcode) {
@@ -133,14 +183,14 @@ async function publishToDraft(title, content, cover, appId, appSecret) {
133
183
  throw new Error(`获取 Access Token 失败: ${accessToken}`);
134
184
  }
135
185
  }
136
- const { html, firstImageId } = await uploadImages(content, accessToken.access_token);
186
+ const { html, firstImageId } = await uploadImages(content, accessToken.access_token, relativePath);
137
187
  let thumbMediaId = "";
138
188
  if (cover) {
139
- const resp = await uploadImage(cover, accessToken.access_token, "cover.jpg");
189
+ const resp = await uploadImage(cover, accessToken.access_token, "cover.jpg", relativePath);
140
190
  thumbMediaId = resp.media_id;
141
191
  } else {
142
192
  if (firstImageId.startsWith("https://mmbiz.qpic.cn")) {
143
- const resp = await uploadImage(firstImageId, accessToken.access_token, "cover.jpg");
193
+ const resp = await uploadImage(firstImageId, accessToken.access_token, "cover.jpg", relativePath);
144
194
  thumbMediaId = resp.media_id;
145
195
  } else {
146
196
  thumbMediaId = firstImageId;