# Wechat Publish

> 本地预览 Markdown 文章的公众号排版（含微信暗色模式），一键上传图片和封面并同步到公众号草稿箱。用于“预览公众号排版”“发到公众号草稿”“同步草稿箱”“调公众号样式”。

本地预览 Markdown 文章的公众号排版（含微信暗色模式），一键上传图片和封面并同步到公众号草稿箱。用于“预览公众号排版”“发到公众号草稿”“同步草稿箱”“调公众号样式”。

- Page: https://ss33.ai/skills/simonwong/wechat-publish-7480355340
- Publisher: simonwong
- Category: [Business](https://ss33.ai/skills/category/business-y1h0vy) › [Social media & video content](https://ss33.ai/skills/category/business-y1h0vy/social-media-and-video-content-0pzewj)
- License: MIT
- Source: https://github.com/simonwong/skills/blob/5a74bfaa251d03093634ec311ec30cdf804c0742/in-progress/wechat-publish/SKILL.md

## Skill

SKILL.md: https://ss33.ai/api/v1/ss33/download/simonwong/wechat-publish-7480355340

# WeChat Publish

把一篇 Markdown 文章转成公众号正文 HTML：本地预览排版，并同步到公众号草稿箱。脚本在 `scripts/`，唯一一套正文样式在 `assets/theme.css`。命令都在 `scripts/` 目录下运行。

## 准备

1. `scripts/node_modules` 不存在时，先在 `scripts/` 运行 `bun install`。
2. 发布需要凭证：环境变量 `WECHAT_APP_ID`、`WECHAT_APP_SECRET`，可选 `WECHAT_AUTHOR`（草稿作者）、`WECHAT_OPEN_COMMENT=0`（账号没有留言权限时关掉留言）；也可以写进 `~/.config/wechat-publish/.env`，环境变量优先。只预览时不需要凭证。
3. 第一次发布前运行 `bun cli.ts check`，确认凭证、IP 白名单、素材和草稿接口权限。失败时把错误提示原样转告用户。

## 按请求选模式

- **预览**：在后台运行 `bun cli.ts preview <文章.md>`，把输出的地址告诉用户。
  - 没有浏览器可打开时（Linux、远程环境）加 `--no-open`。
  - 默认端口 4321。端口上已经在预览同一篇文章时，沿用原来的服务，命令直接退出；端口被别的程序占用时，会自动换一个端口，以输出的地址为准。
  - 页面左栏是原文，右栏默认显示 375px 宽的正文，可切换亮色和微信暗色，也可切到消息卡片视图。
  - 原文上方列出写法提示，比如标题超长、一级标题和文章标题重复、公众号会删掉的 `<video>`、mermaid 图、无意义的图注。提示只提醒、不改正文，要不要改写由用户决定。
  - 用户保存 md 或 `theme.css` 后页面自动刷新。md 改名或移动后，用新路径重新运行 preview。
  - 页面上的「发布到草稿」按钮执行的就是发布模式。
- **发布**：运行 `bun cli.ts publish <文章.md>`，按输出的 JSON 告诉用户：新建还是更新了草稿，上传了几张图片，封面是否重传。`warnings` 不为空时一并转告（比如没有设置作者）。
- **调样式**：运行 `bun cli.ts preview`（不带文章）打开样例 `assets/sample.md`，样例预览不能发布。
  - 样式只改 `assets/theme.css`。改之前先读文件头的注释：写了哪些 CSS 能生效，以及暗色下颜色怎么选。颜色、字号、间距优先改 `:root` 里的变量。
  - 样例覆盖不到的元素，补进 `sample.md`。
  - 改完让用户在亮色和暗色下都看一遍效果。

## 发布会改什么

- **md 原文**：本地图片（`![[x.png]]` 或相对路径）和外链图片会被上传，原文里的图片改写成 `![说明](mmbiz 地址)`，`coverImage` 换成 mmbiz 地址。`![[x.png|300]]` 的尺寸不保留。已经是 mmbiz 地址的图片会跳过。除此之外 frontmatter 不改。
- **上传前检查**：所有图片先读进来检查格式，有一张不行就整篇不上传。超过 1MB 的 png、jpg 在 macOS 上用 `sips` 压缩后再传；gif 走永久素材接口。`<img>` 标签、引用式图片 `![x][ref]`、嵌入的笔记和 pdf 会直接报错，要用户改成 `![说明](地址)`。
- **草稿**：按 md 的真实路径在 `~/.config/wechat-publish/state.json` 记录草稿 ID 和封面素材 ID。同一路径再次发布，就更新同一篇草稿；草稿已经发表或被删掉时，新建一篇。md 改名或移动后再发布，会新建草稿。同一篇文章同时发布两次时，第二次直接报错。
- **字段**：`title` 和 `coverImage` 必填，`coverImage` 写成一行字符串（`"[[cover.png]]"` 要加引号）。`summary` 用作摘要。
- **封面裁剪**：`coverImage` 是 3.35:1 的拼接图（codex-wechat-cover 的默认输出，比如 2010×600）时，消息列表用左边 2.35:1，转发卡片和次条用右边 1:1。其他比例由公众号居中裁剪。预览页的消息卡片视图会按同样的裁剪显示。
- **终点**：流程只到草稿箱为止。草稿接口不支持原创声明，发布后提醒用户：在公众号后台勾选原创再群发。勾选后如果再同步草稿，发表前要确认原创还在。

正文渲染规则：

- frontmatter 的 `title` 不进正文。
- 链接处理参照 doocs/md：
  - 只有 `mp.weixin.qq.com` 的链接保留可点。
  - 其他外链显示为「文字[n]」，文末「引用链接」按「[n] 标题：网址」列出。标题取链接的 title，没有就用链接文字。标题和网址都相同的链接共用编号。
  - 链接文字就是网址时原样显示。`#锚点`、`./a.md` 这类不是网址的链接只显示文字。
- 列表符号渲染成文字（无序 `•`、有序 `1.`），由主题的 `.bullet`、`.number` 控制样式；公众号里原生列表符号改不了颜色和大小。
- 独占一段或独占一行的图片用 alt 作图注，alt 为空就不显示图注。
- 单个换行不换行，和 Obsidian 的 `strictLineBreaks` 一致；中文之间的换行直接去掉，不留空格。
- `**` 紧贴中文标点时也能加粗，和 Obsidian 一致。
- `%%批注%%` 和 HTML 注释不进正文，里面的图片也不上传。
