The only CLI that takes you from idea to published — not just formatting.
一行命令,从 Markdown 到发布 —— 不只是排版。
Markdown → WeChat HTML → Magazine Cover → Social Cards → Publish.
| 公众号排版 | 杂志封面 |
|---|---|
| 社交卡片 | 一条命令全流程 |
|---|---|
图为实际产出截图。
Inkwell 是一条从 Markdown 直通发布的内容流水线:写完文章之后那些最耗时的机械活, 它一次做完 —— 排版成能直接粘进公众号的 HTML、压缩并上传配图、生成杂志封面与社交卡片, 最后推送到剪贴板或公众号草稿箱。
| 维度 | 说明 |
|---|---|
| 形态 | Python CLI(inkwell),另提供 Docker 镜像与 AI Agent Skill |
| 覆盖环节 | 排版 · 图片处理 · 封面 · 社交卡片 · 合规检测 · 发布 |
| 给谁用 | 一个人运营多个平台的创作者;需要批量产出、想接进自动化流水线的内容团队 |
| 第一次使用 | 一条 inkwell run article.md,产出的 HTML 可直接粘进编辑器 |
| 不需要 | 美术功底、AI 生图 API;没装 Playwright 也照常出 HTML,只是跳过 PNG |
| 和同类工具的差别 | doocs/md、mdnice 只解决「排版」一环,Inkwell 覆盖整条链路 |
| 技术栈 | Python 3.11+ · Pillow · MIT · 197 条测试 · CI 三平台矩阵 |
⚠️ 本项目的 PyPI 发行名是inkwell-press,不是inkwell
pip install inkwell→ 装到无关的 Qt 深色主题(pkkid/python-inkwell)pip install inkwell-cli→ 装到无关的 播客转 Markdown 笔记工具(chekos/inkwell-cli)。该项目的导入包名与命令名也叫inkwell,不要与本项目装进同一个 Python 环境✅ 本项目的发行名是
inkwell-press(尚未上传 PyPI,当前请从源码安装,见安装一节)
写完文章 → 手动排版 30 分钟 → 粘贴公众号 → 样式全崩 → 重新调 →
再粘贴 → 代码块乱了 → 又调 → 找封面图 → 翻素材库 → 不够好看 →
用 Canva 做 → 导出 → 手动传 → 再写小红书 → 又排一遍……
一次内容发布,平均花在「排版和视觉」上的时间超过 1 小时。
Inkwell 把这整个流程压成一条命令。
inkwell run article.md # 排版 + 图片处理 + 校验
inkwell cover --title "标题" --png # 杂志封面 + PNG
inkwell card --platform xhs --png # 小红书卡片 + PNG
inkwell publish --target clipboard # 复制到剪贴板,粘贴即发市面已有优秀的排版工具(doocs/md 11.7k stars、mdnice、xiaohu-wechat-format),但它们都只解决「排版」这一个环节。Inkwell 是唯一覆盖全链路的 CLI:
| 能力 | doocs/md | mdnice | xiaohu-wechat-format | Inkwell |
|---|---|---|---|---|
| Markdown → 公众号 HTML | Web 应用 | Web 应用 | Python CLI | Python CLI |
| 排版主题数 | 30+ | 20+ | 30 | 15(持续扩展) |
| 杂志封面生成 | Docker 部署 | 无 | Gemini API | 内置(无需 AI API) |
| 封面设计建议 | 无 | 无 | 无 | 6 种高转化模式智能推荐 |
| 小红书社交卡片 | 无 | 无 | 无 | 11 平台覆盖(含 IG/X/LinkedIn/Pinterest/YT) |
| PNG 截图输出 | 无 | 无 | 无 | Playwright 2x Retina |
| 设计质量审计 | 无 | 无 | 无 | 三门审计(主题/密度/交付) |
| 敏感词 / 合规检测 | 无 | 无 | 无 | 广告法极限词 + 平台违规词 + 通用敏感词 |
| 文章目录 TOC | 无 | 无 | 无 | 自动提取 H2/H3 + 编号 + 公众号兼容卡片 |
| 字数统计 / 阅读时间 | 无 | 无 | 无 | 中英分开计数 + 阅读时长 + 结构分析 |
| 选题搜索 | 无 | 无 | 无 | GitHub trending + 自定义后端 |
| 草稿骨架生成 | 无 | 无 | 无 | 5 种风格 + 搜索自动填充 |
| 公众号草稿箱推送 | 无 | 无 | 有 | 有 |
| 剪贴板一键复制 | 手动 | 手动 | 无 | 有 |
| AI Agent 集成 | 无 | 无 | Claude Code | 任何 Agent(通用 Skill) |
| Docker 多阶段 | 有 | 无 | 无 | 有(120MB) |
| 形态 | Web 编辑器 | Web + 商业化 | Python 脚本 | Python CLI + Docker + Skill |
一句话总结区别:它们是「排版器」,Inkwell 是「出版流水线」。
| 命令 | 用途 |
|---|---|
inkwell run article.md |
完整流水线:排版 + 图片处理 + 校验 |
inkwell search "AI工具" |
搜索选题灵感(GitHub trending / web) |
inkwell generate "主题" --search |
自动生成 Markdown 草稿(接搜索自动填充) |
inkwell cover --png |
杂志封面生成 + PNG 截图 |
inkwell card --png |
社交卡片生成 + PNG 截图 |
inkwell audit output/cover.html |
设计质量审计 |
inkwell advise --topic "..." |
封面设计建议 |
inkwell publish output/article.html |
发布(剪贴板 / 公众号草稿箱) |
inkwell wordcheck article.md |
敏感词 / 合规检测(广告法 + 平台违规 + 通用敏感) |
inkwell stats article.md |
字数统计 + 阅读时间 + 结构分析 |
inkwell toc article.md |
自动生成文章目录卡片(H2/H3 提取 + 编号) |
inkwell validate output/article.html |
兼容性校验 |
inkwell themes |
列出可用主题 |
# 从源码安装(当前可用)
git clone https://github.com/lijinhongucl-pixel/inkwell.git
cd inkwell
pip install -e .
pip install -e ".[dev]" # 要跑测试或 lint 时才需要
# Docker
docker build -t inkwell .pip install inkwell-press为什么发行名和命令名不一样:PyPI 上的 inkwell 已被一个无关的 Qt 主题占用,
所以本项目的发行名定为 inkwell-press;装好之后导入包名与命令行都还是 inkwell。
No matching distribution found。请先用上面的源码方式安装。
--png 参数需要 Playwright,不装也能用——所有命令都会正常输出 HTML,
只是跳过 PNG 并给出提示。
npm install -g playwright-core
npx playwright install chromium
# 使用已有环境(自定义 node_modules 或浏览器路径)
export PLAYWRIGHT_NODE_PATH=/path/to/node_modules
export PLAYWRIGHT_CHROMIUM_PATH=/path/to/chrome-headless-shell# 完整流水线
inkwell run article.md -o ./output --theme moyu-green
# 搜索选题
inkwell search "AI agent" --limit 10
# 生成草稿(自动搜索填充)
inkwell generate "Docker 镜像优化" --style tutorial --search --limit 5
# 杂志封面 + PNG
inkwell cover \
--title "最懂" --title-em "生活" --kicker "开源 · 出海" \
--subtitle-en "When AI meets life." \
--lede "一句话导读叙事。" \
--slogan "探索前沿" \
--issue 09 --date 2026.09 \
-o ./covers/ --png
# 小红书 3 页轮播 + PNG
inkwell card \
--platform xhs --style editorial \
--pages 3 --brand "作者" --tag "#AI工具" \
--titles "封面标题" "核心要点" "总结" \
-o ./cards/ --png
# 发布到剪贴板(以富文本写入,粘贴到公众号即为排版后的效果)
inkwell publish output/article_preview.html --target clipboard
# 发布到公众号草稿箱(正文图与封面自动转存微信图床,详见下面「发布到公众号草稿箱」)
inkwell publish output/article_preview.html \
--target wechat --title "文章标题" --author "作者" \
--thumb-image ./covers/cover_01_21x9.png
# 只做兼容性校验
inkwell validate output/article_preview.html关于两版产出:
run会同时生成_预览.html(发布版,图片走 CDN 外链, 用于复制到公众号)和_预览_本地版.html(图片内嵌 base64,用于 IDE 预览面板 等加载不了外网的环境)。两版正文完全一致,只有图片承载方式不同。正文图本来就是外链(比如你自己图床上的
https://cdn.jsdelivr.net/...)时, 发布版保留原地址(公众号粘贴时会自己转存),同时会把图下载压缩后内嵌进本地版 —— 否则本地版只会跟发布版逐字节相同,离线打开满屏裂图。加--no-embed-external可关掉这个下载(内网环境或不想出网时用)。要拿到 CDN 外链,三项配置缺一不可:
GITHUB_TOKEN环境变量、--github-repo owner/repo、--cdn-base <外链前缀>。缺任一项,图片都会降级为 内嵌 base64(此时两版内容相同),命令行会直接提示你还缺什么。export GITHUB_TOKEN=ghp_xxx inkwell run article.md \ --github-repo your-name/your-image-repo \ --cdn-base https://cdn.jsdelivr.net/gh/your-name/your-image-repo@main \ --subdir my-article
⚠️ 含内嵌 base64 的 HTML 不要直接粘进公众号编辑器——图片会变乱码。请按上面的 方式配好外链,或在浏览器里打开该 HTML 全选复制。
平台支持(重要):
--target clipboard写入富文本依赖系统能力 —— macOS 走 AppleScript 写入«class HTML»flavor;Linux 走xclip(需apt install xclip)。Windows 目前没有富文本剪贴板实现: 会降级为纯文本并明确提示,直接粘贴到公众号只能得到无格式文字。 Windows 用户请改为在浏览器打开_预览.html→ 全选 → 复制。 其余功能(排版、封面、社交卡片、兼容性校验、字数统计)三平台行为一致。
--target wechat 会把文章直接推进公众号草稿箱,正文图片与封面都会自动转存到微信图床,
不需要你手工处理图片:
export WECHAT_APP_ID=wx****************
export WECHAT_APP_SECRET=****************
inkwell publish output/article_预览.html \
--target wechat \
--title "文章标题" --author "作者" \
--thumb-image ./covers/cover_01_21x9.png \
--digest "一句话摘要"- 正文图片:本地相对路径、CDN 外链、base64 内嵌都支持,会逐张调
media/uploadimg换成微信图床 URL。这一步不能省 —— 微信会把content里的外部图片 url 直接过滤掉, 所以「发布版 HTML 里是 CDN 外链」并不等于可以直接推草稿 - 封面:
--thumb-image <本地图>会自动调material/add_material换永久素材media_id;已经有素材 id 的话用--thumb <media_id> - 长度提前校验:标题 ≤32 字、作者 ≤16 字、摘要 ≤128 字,超了在本地就报错,不发请求
- 前置条件:草稿箱接口只有服务号能调(订阅号没有该权限);调用方的出口 IP 必须 加入公众号后台的 IP 白名单,否则取 token 时报 40164
- 某张图转存失败不会中断推送,但会在输出里逐条告警 —— 那几张图在草稿里会缺失, 请按告警补齐后重推
# 把 Markdown 放进 ./input,产出在 ./output
docker compose run --rm pipeline run /app/input/article.md -o /app/outputfrom inkwell import Pipeline, PipelineConfig
from pathlib import Path
cfg = PipelineConfig(
input_md=Path("article.md"),
output_dir=Path("./output"),
theme="moyu-green",
cdn_base="https://cdn.example.com/images",
github_repo="your-org/your-image-repo",
)
result = Pipeline(cfg).run()
print(result.publish_html)内置 15 套排版主题,覆盖从科技到生活、从商务到创意的全场景:
| 主题 | 主色 | 适用场景 |
|---|---|---|
moyu-green 摸鱼绿 |
#059669 |
教程、测评、清单(默认推荐) |
red-white 红白色系 |
#DC2626 |
深度分析、观点、力量感话题 |
graphite-minimal 石墨极简 |
#52525B |
设计、科技评论、专业观点 |
zen-whitespace 留白禅意 |
#4A5D52 |
禅意、极简生活、深度随笔 |
moyu-ticket 摸鱼票据 |
#059669 |
测评、工具对比(票据视觉隐喻) |
olive-journal 橄榄手记 |
#1E1F23 |
内刊手记、深度评测、案例复盘 |
classic-serif 经典衬线 |
#1B3A2E |
深度长文、编辑部风格 |
cyber-neon 赛博霓虹 |
#00F0FF |
科技、AI、编程、暗色酷炫 |
coffee-mocha 咖啡摩卡 |
#6F4E37 |
生活、美食、旅行、暖调随笔 |
ocean-blue 海洋蓝 |
#0066CC |
商务、财经、行业分析、职场 |
sunset-warm 日落暖橙 |
#E76F51 |
生活方式、品牌故事、情感 |
ink-wash 水墨山水 |
#1A1A1A |
传统文化、诗词、人文历史 |
forest-deep 森林深绿 |
#2D6A4F |
环保、自然、可持续发展、健康 |
royal-purple 皇家紫 |
#6A1B9A |
创意、设计、艺术、灵感 |
sakura-pink 樱花粉 |
#E91E63 |
时尚、美妆、穿搭、少女心 |
from inkwell.processors.markdown_proc import MarkdownProcessor, THEMES
THEMES["my-brand"] = {
"bg": "#FFFFFF", "text": "#333333",
"primary": "#0066CC", "accent": "#FF6600",
"highlight": "#CC0000", "font": "Georgia, serif",
}
proc = MarkdownProcessor(theme="my-brand")11 个平台,中文 + 海外全覆盖:
中文平台
| 平台 | 比例 | 尺寸 |
|---|---|---|
xhs 小红书 |
3:4 | 1080×1440 |
wechat-cover 公众号 |
21:9 + 1:1 | 2100×900 + 1080×1080 |
wechat-article 正文 |
16:9 | 1920×1080 |
weibo 微博 |
1:1 | 1080×1080 |
海外平台
| 平台 | 比例 | 尺寸 |
|---|---|---|
instagram-feed IG Feed |
1:1 | 1080×1080 |
instagram-story IG Story / Reels |
9:16 | 1080×1920 |
x-twitter X (Twitter) |
16:9 | 1600×900 |
linkedin LinkedIn |
1.91:1 | 1200×627 |
pinterest Pinterest |
2:3 | 1000×1500 |
youtube-thumb YouTube |
16:9 | 1280×720 |
# Instagram Feed 轮播
inkwell card --platform instagram-feed --style swiss --pages 5 --png
# X/Twitter 单图
inkwell card --platform x-twitter --style dark-bold --pages 1 --png
# Pinterest 竖图
inkwell card --platform pinterest --style editorial --pages 1 --png| 风格 | 背景 | 主色 | 字体 |
|---|---|---|---|
swiss Swiss 极简 |
#F5F5F0 |
#D62828 |
Inter |
editorial 杂志 |
#0E0D0C |
#D4A04A |
Noto Serif SC |
clean-light 浅色 |
#FFFFFF |
#0066CC |
PingFang SC |
dark-bold 深色大胆 |
#1A1A2E |
#E94560 |
Inter |
内置 6 种高转化封面模式:
| 模式 | 适用 |
|---|---|
| 大标题冲击型 | 干货教程、工具推荐 |
| 数据对比型 | 测评、横评、性能对比 |
| 截图证据型 | 工具实操、代码教程 |
| 提问钩子型 | 观点分析、趋势解读 |
| 清单/盘点型 | 合集、Top N、工具箱 |
| 前后对比型 | 优化技巧、改造教程 |
# 单个推荐
inkwell advise --topic "Docker 镜像优化" --type tutorial
# 多个备选
inkwell advise --topic "10 个好工具" --type list --multi| 门 | 检查内容 |
|---|---|
| Gate 1 主题与载体合理性 | 有无标题、内容是否完整、有无空 section |
| Gate 2 视觉密度基准 | 字号层级 ≥3、视觉锚点 ≥3、颜色数 ≥2 |
| Gate 3 可编辑交付 | 无 base64 图片、有 charset、无内嵌 script |
| 禁用 | 替代方案 |
|---|---|
display:flex / grid |
<table> + <td width> 均分布局 |
display:inline-block 自制图形 |
● 字符圆点、→ 字符箭头 |
linear-gradient 渐变 |
实色背景 |
<svg> 装饰 |
CSS border 或字符 |
data:image;base64 内嵌图(发布版) |
CDN 外链图片 |
必须做的一件事:正文每个文字节点都要包在 <span leaf=""> 里。公众号编辑器
只保留 leaf 内的文字样式,漏包会导致粘贴后样式大面积丢失 —— 这是硬门槛而不是风格偏好,
Inkwell 输出的每一段文字都会自动包裹。
安装为 AI Agent 的 Skill:
# WorkBuddy
cp -r skill/ ~/.workbuddy/skills/inkwell/
# Claude / 其他 Agent(按你的 Agent Skill 目录调整)
cp -r skill/ /path/to/your-agent/skills/inkwell/Agent 内调用示例:
用户:帮我写一篇关于 Docker 多阶段构建的推文并排版
Agent:
1. inkwell generate "Docker 多阶段构建" --style tutorial --search → draft.md
2. inkwell run draft.md --theme graphite-minimal → output/
3. inkwell cover --title "..." --png → covers/
4. inkwell publish output/draft_preview.html --target clipboard
| 阶段 | 基础镜像 | 作用 | 产物 |
|---|---|---|---|
builder |
python:3.13-slim |
装依赖、生成 venv | /opt/venv |
runtime |
python:3.13-slim |
只拷 venv | 精简运行时镜像 |
优化点:非 root 用户运行、dumb-init 信号转发、无编译工具、层缓存友好。
inkwell/
├── src/inkwell/
│ ├── cli.py # 13 个子命令入口
│ ├── core.py # 流水线编排(md → html → 图片 → 校验)
│ ├── processors/
│ │ ├── markdown_proc.py # Markdown → 内联样式 HTML,15 套主题
│ │ ├── image_proc.py # 压缩 / CDN 上传 / base64 双轨
│ │ └── validator.py # 公众号兼容性静态扫描
│ ├── cover_gen.py # 21:9 + 1:1 杂志封面
│ ├── social_card.py # 11 个平台社交卡片
│ ├── publisher.py # 富文本剪贴板 / 微信公众号草稿箱
│ ├── wordcheck.py # 敏感词与广告法合规检测
│ ├── toc.py # 目录卡片
│ ├── stats.py # 字数与阅读时长
│ ├── generator.py # 5 种风格草稿骨架
│ ├── searcher.py # 选题素材搜索
│ ├── screenshot.py # Playwright HTML → PNG
│ ├── design_audit.py # 三门设计审计
│ └── cover_advisor.py # 封面设计建议
├── tests/ # 197 条测试
├── sample/ # 示例文章(含 demo、docker-build、sample 三篇)
└── skill/ # Agent Skill 定义
pytest tests/ -v --cov=inkwell --cov-report=term-missing
ruff check src/ tests/
ruff format --check src/ tests/提交前建议安装本地钩子,与 CI 门禁保持一致:
pip install pre-commit && pre-commit install| 文档 | 说明 |
|---|---|
| CONTRIBUTING.md | 开发环境、分支约定、公众号兼容铁律 |
| CODE_OF_CONDUCT.md | 社区行为准则 |
| SECURITY.md | 漏洞报告渠道与关注的风险面 |
| CHANGELOG.md | 版本变更记录 |
发现可复现的缺陷或想提功能请求,请用 Issue 模板; 用法咨询与想法讨论更适合走 Discussions。
⚠️ The PyPI distribution isinkwell-press, notinkwell
pip install inkwell→ an unrelated Qt dark theme (pkkid/python-inkwell)pip install inkwell-cli→ an unrelated podcast-to-markdown tool (chekos/inkwell-cli). That project also ships aninkwellimport package and aninkwellconsole command, so do not install both into the same Python environment✅ This project's distribution name is
inkwell-press(not uploaded to PyPI yet — install from source for now, see Install)
Inkwell is a content pipeline that runs from Markdown straight to publishable output. It takes the mechanical work that eats your time after writing — formatting for WeChat, compressing and hosting the images, generating magazine covers and social cards, then pushing the result to the clipboard or the WeChat draft box.
| Aspect | Detail |
|---|---|
| Form | Python CLI (inkwell), plus a Docker image and an AI Agent Skill |
| Pipeline | Formatting · image handling · cover · social cards · compliance check · publish |
| For whom | Solo creators running several platforms; teams that need batch output or automation |
| First run | One command — inkwell run article.md — and the HTML is ready to paste |
| Not required | Design skills, an AI image API, or even Playwright (HTML still ships, PNG is skipped) |
| Versus others | doocs/md and mdnice solve formatting only; Inkwell covers the whole chain |
| Stack | Python 3.11+ · Pillow · MIT · 197 tests · CI matrix across three platforms |
Write article → Format for 30 min → Paste to WeChat → Styles break → Fix →
Paste again → Code blocks messed up → Fix again → Need cover image →
Search stock photos → Not good enough → Open Canva → Export → Upload →
Now adapt for Xiaohongshu → Format again...
The average content creator spends 1+ hour on formatting and visuals per article.
Inkwell compresses this entire workflow into one command.
inkwell run article.md # Format + images + validation
inkwell cover --title "Title" --png # Magazine cover + PNG
inkwell card --platform xhs --png # Xiaohongshu cards + PNG
inkwell publish --target clipboard # Copy to clipboard, paste and publish| Feature | doocs/md (11.7k stars) | mdnice | xiaohu-wechat-format | Inkwell |
|---|---|---|---|---|
| Markdown → WeChat HTML | Web app | Web app | Python CLI | Python CLI |
| Magazine cover generator | Docker deploy | No | Gemini API | Built-in (no AI API needed) |
| Cover design advisor | No | No | No | 6 high-conversion patterns |
| Social cards (IG/X/LinkedIn/Pinterest/YT/XHS) | No | No | No | 11 platforms, 4 visual styles |
| PNG screenshot output | No | No | No | Playwright 2x Retina |
| Design quality audit | No | No | No | 3-gate (theme/density/delivery) |
| Sensitive word / compliance check | No | No | No | Ad law + platform + general |
| Auto TOC generation | No | No | No | H2/H3 extraction + numbering |
| Word count / reading time | No | No | No | CN/EN split + structural analysis |
| Topic search | No | No | No | GitHub trending + custom backend |
| Draft skeleton generator | No | No | No | 5 styles + search auto-fill |
| WeChat draft push | No | No | Yes | Yes |
| AI Agent integration | No | No | Claude Code | Any Agent (universal Skill) |
| Docker multi-stage | Yes | No | No | Yes (~120MB) |
In one sentence: They are formatters. Inkwell is a publishing pipeline.
# From source (works today)
git clone https://github.com/lijinhongucl-pixel/inkwell.git && cd inkwell
pip install -e .
pip install -e ".[dev]" # only if you need tests / lint
# Docker
docker build -t inkwell .pip install inkwell-pressWhy the distribution name differs from the command name. The plain inkwell name on
PyPI is taken by an unrelated Qt theme, so this project's distribution is named
inkwell-press. After installing, both the import package and the console command are
still named inkwell.
No matching distribution found. Use the source install for now.
# Full pipeline
inkwell run article.md -o ./output --theme moyu-green
# Search trending topics
inkwell search "AI agent" --limit 10
# Generate draft with auto-search
inkwell generate "Docker optimization" --style tutorial --search
# Magazine cover + PNG
inkwell cover --title "AI" --kicker "2026" -o ./covers/ --png
# Xiaohongshu cards + PNG
inkwell card --platform xhs --style editorial --pages 3 -o ./cards/ --png
# Publish to clipboard or WeChat draft
inkwell publish output/article_preview.html --target clipboardGetting CDN image links:
runneeds all three — theGITHUB_TOKENenvironment variable,--github-repo owner/repo, and--cdn-base <prefix>— to upload images and emit external URLs. If any one is missing, images fall back to inline base64 (and the two output files become identical); the CLI tells you what is missing.export GITHUB_TOKEN=ghp_xxx inkwell run article.md \ --github-repo your-name/your-image-repo \ --cdn-base https://cdn.jsdelivr.net/gh/your-name/your-image-repo@main
runwrites two files:_预览.html(publish version — images as external CDN URLs, for pasting into WeChat) and_预览_本地版.html(preview version — images embedded as base64, for the IDE preview panel and other offline viewers). When an image already points at an external URL, the publish version keeps that URL and the image is downloaded and embedded into the local version — otherwise both files would be byte-identical and the local preview would show broken images offline. Pass--no-embed-externalto skip those downloads.Every text node is wrapped in
<span leaf="">— that is what makes the WeChat editor keep the inline styles after pasting.
⚠️ An HTML file with inline base64 images should not be pasted into the WeChat editor directly — the images turn into garbled text. Configure the CDN link above, or open the file in a browser and select-all copy.
--target wechat pushes the article straight into the WeChat draft box, and both the
in-article images and the cover are moved onto WeChat's own image host automatically:
export WECHAT_APP_ID=wx****************
export WECHAT_APP_SECRET=****************
inkwell publish output/article_preview.html \
--target wechat \
--title "Title" --author "Author" \
--thumb-image ./covers/cover_01_21x9.png \
--digest "One-line summary"- In-article images — local paths, CDN URLs and inline base64 all work: each image goes
through
media/uploadimgand itssrcis swapped for a WeChat-hosted URL. This step is mandatory, because WeChat filters out external image URLs insidecontent - Cover —
--thumb-image <local file>uploads it viamaterial/add_materialand uses the returned permanentmedia_id; use--thumb <media_id>if you already have one - Limits checked up front — title ≤32 characters, author ≤16, digest ≤128; violations fail locally without spending an API call
- Requirements — the draft API is only available to service accounts (subscription accounts have no access), and the caller's egress IP must be whitelisted in the WeChat console, otherwise the token call fails with 40164
- A failed image transfer does not abort the push, but every failure is reported as a warning — those images will be missing from the draft
| Module | Description |
|---|---|
MarkdownProcessor |
Markdown → WeChat-compatible HTML (15 themes, inline styles, <span leaf=""> wrapping) |
ImageProcessor |
PIL compression + GitHub Contents API CDN upload, with base64 dual-track fallback (external images are embedded into the local preview) |
CopyCompatValidator |
Static scan for banned clipboard patterns + the <span leaf=""> requirement |
ContentSearcher |
GitHub API search + web/custom adapters |
DraftGenerator |
5 article style skeletons with search auto-fill |
CoverGenerator |
Magazine cover (21:9 + 1:1, dark editorial style) |
SocialCardGenerator |
Social cards (11 platforms: IG/X/LinkedIn/Pinterest/YT/XHS/WeChat, 4 styles) |
DesignAuditor |
3-gate design quality audit |
CoverAdvisor |
6 high-conversion cover pattern advisor |
Screenshotter |
Playwright headless PNG capture, degrades gracefully when unavailable |
Publisher |
Rich-text (HTML flavor) clipboard + WeChat draft API |
WordChecker |
Sensitive word / compliance detection (ad law + platform + general) |
TOCGenerator |
Auto table-of-contents card from Markdown headings |
ArticleAnalyzer |
Word count, reading time, structural analysis |
Typed: the package ships a py.typed marker, so downstream type checkers pick up the inline annotations.
pytest tests/ -v --cov=inkwell --cov-report=term-missing
ruff check src/ tests/
ruff format --check src/ tests/See CONTRIBUTING.md. All contributions welcome!
For security issues, please follow SECURITY.md instead of opening a public issue. By participating you agree to the Code of Conduct.
See CHANGELOG.md.
MIT — Copyright (c) 2026 Inkwell contributors