dby-theme
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese公众号排版主题工作室(都爆鸭)
WeChat Official Account Layout Theme Studio (Doubaoya)
排版是一份声明式的 (配色 palette + 每个标签的 inline-style 模板),渲染器按它把
Markdown 确定性地渲成公众号内联样式 HTML。按描述生成 / 修改合法的 themeJson,
本地自检 + 预览,再存回服务端设为默认;发文钉了 / 时再落一份本机文件。
渲染 / 存主题都免费不扣点。
themeJson--themeconfig.mdThemeLayout is a declarative (palette + inline-style template for each tag), and the renderer deterministically converts Markdown into WeChat inline-style HTML according to it. Generate / modify a valid themeJson based on the description, run local self-check + preview, then save it back to the server as the default; when publishing with / pinned, also write a local file. Rendering / saving themes is free, no credits deducted.
themeJson--themeconfig.mdTheme⚠️ 四条硬红线(先读,全程守住)
⚠️ Four Hard Red Lines (Read First, Hold Throughout)
红线 1 · 微信兼容:标题装饰只能挂在有文字的元素自身
Red Line 1 · WeChat Compatibility: Title decorations must be attached to the element with text itself
公众号编辑器会 strip 掉没有文字、纯靠 inline-block 宽高 + background 撑色块的空 /。
标题引导条 / 强调竖条 / 下划线必须作为 inline style 直接写在标题元素自身,绝不用独立的空装饰块。
<section><span>- ✅ 对:里直接写
elements.h2.styleborder-left:4px solid {{accent}};border-bottom:1px solid {{border}};padding:0 0 8px 11px; - ❌ 错:用 注入一个空的
wrapBefore当色条(无文字 → 被 strip)。<section style='width:32px;height:4px;background:…'>
The WeChat editor strips empty / elements that have no text and rely purely on inline-block width/height + background color blocks. Title guide bars / emphasis vertical bars / underlines must be written directly as inline styles on the title element itself, never as separate empty decoration blocks.
<section><span>- ✅ Correct: write directly in
border-left:4px solid {{accent}};border-bottom:1px solid {{border}};padding:0 0 8px 11px;elements.h2.style - ❌ Wrong: use to inject an empty
wrapBeforeas a color bar (no text → stripped).<section style='width:32px;height:4px;background:…'>
红线 2 · 微信兼容:别给整篇套 decorations.articleWrap
外框
decorations.articleWrapRed Line 2 · WeChat Compatibility: Don't wrap the whole article in a decorations.articleWrap
outer frame
decorations.articleWrapdecorations.articleWraparticleWrap.beforeaftermarginline-heightdecorations.articleWraparticleWrap.beforeaftermarginline-height红线 3 · 安全:themeJson 会内联进公众号草稿,服务端会拒收不合法主题
Red Line 3 · Safety: themeJson will be inlined into WeChat drafts, and the server will reject invalid themes
服务端 // 都会 ,不过直接 400。生成后先跑
自检——具体校验规则(顶层键、
palette/page 必填字段、style 禁写清单、体积上限)以它为准,不在这里另存一份会漂移的清单。
POSTPUTrendervalidateThemescripts/validate-theme.mjs微信编辑器官方规范另有八条「不拒但走样」的写法(、、固定像素宽、自定义字体栈、文字底下铺渐变…),
清单与可读性基线见 末尾;校验器对其中四条打 warning。
positiontext-align:startreferences/theme-schema.mdThe server's // all call , returning 400 if invalid. After generating, first run for self-check — the specific validation rules (top-level keys, required fields in palette/page, forbidden style list, size limits) follow that script, and we don't keep a separate drifting list here.
POSTPUTrendervalidateThemescripts/validate-theme.mjsWeChat editor's official spec also has eight "not rejected but will look wrong" patterns (, , fixed pixel widths, custom font stacks, gradients under text…). The list and readability baseline are at the end of ; the validator warns on four of them.
positiontext-align:startreferences/theme-schema.md红线 4 · 数据安全:动手改前必须落盘备份,没有备份不改
Red Line 4 · Data Safety: Must back up to disk before modifying; no backup, no modification
改主题是就地覆写( / ),服务端不保留历史版本,改坏了没有第二处能找回。
第 1 步的 GET 命令已经把落盘备份写进命令本身——照抄不删;备份文件路径记下来,改坏了照
「回滚」一节把它 PUT 回去。
PUTPOST + isDefault:truereferences/deploy-scope.mdChanging a theme is an in-place overwrite ( / ). The server does not keep history versions, and if you break it, there is no second place to recover. The GET command in Step 1 already includes disk backup in the command itself — copy it as-is, don't delete. Note the backup file path; if you break it, PUT it back according to the "Rollback" section of .
PUTPOST + isDefault:truereferences/deploy-scope.md契约:服务端主题 CRUD + 渲染(以 doubaoya.com 主仓 render-routes.ts 为准)
Contract: Server-side Theme CRUD + Rendering (as per doubaoya.com main repo render-routes.ts)
⚠️ 先读 再发请求(鉴权、密钥怎么拿、
信封与报错码全在那一份)。本 skill 自己的六条主题路由(、
、、/)
的请求体与返回 → 读 ,不需要就别读。
dby-gateway/references/protocol.mdGET/POST /api/wechat/themeGET /api/wechat/themesPOST /api/wechat/renderPUTDELETE /api/wechat/theme/:idreferences/crud.mdbash
export DOUBAOYA_API_KEY="dyh_你的密钥"
BASE="${DOUBAOYA_BASE_URL:-https://doubaoya.com}"
AUTH="Authorization: Bearer $DOUBAOYA_API_KEY"render 的主题解析优先级 (传了就用这份)> >
我的默认主题 > 兜底 ,所以预览编辑中的主题就传 ;
一人一默认, 时服务端在事务里把我其他主题的 置反。
themeJsonthemeIdbenya-cleanthemeJsonisDefault:trueisDefault⚠️ Read first before making requests (auth, how to get the key, envelope and error codes are all in that one file). For this skill's six theme routes (, , , /), the request bodies and responses are in ; don't read unless needed.
dby-gateway/references/protocol.mdGET/POST /api/wechat/themeGET /api/wechat/themesPOST /api/wechat/renderPUTDELETE /api/wechat/theme/:idreferences/crud.mdbash
export DOUBAOYA_API_KEY="dyh_你的密钥"
BASE="${DOUBAOYA_BASE_URL:-https://doubaoya.com}"
AUTH="Authorization: Bearer $DOUBAOYA_API_KEY"Theme resolution priority for render: (if passed, use this) > > my default theme > fallback . So pass when previewing a theme being edited; one default per user, when the server transaction resets on other themes of mine.
themeJsonthemeIdbenya-cleanthemeJsonisDefault:trueisDefault闭环步骤
Closed-loop Steps
1. 读起点主题并落盘备份(红线 4,照抄不删)
1. Read starting theme and back up to disk (Red Line 4, copy as-is don't delete)
bash
undefinedbash
undefineda) 读我当前的默认主题(有就在它上面改)并强制落盘备份——主题体在 data.theme.themeJson
a) Read my current default theme (modify on top of it if exists) and force disk backup — theme body is in data.theme.themeJson
BACKUP="/tmp/dby-theme-backup-$(date +%s).json"
curl -s -H "$AUTH" "$BASE/api/wechat/theme" | tee "$BACKUP"
echo "已备份到 $BACKUP —— 改坏了见 references/deploy-scope.md「回滚」一节"
BACKUP="/tmp/dby-theme-backup-$(date +%s).json"
curl -s -H "$AUTH" "$BASE/api/wechat/theme" | tee "$BACKUP"
echo "已备份到 $BACKUP —— 改坏了见 references/deploy-scope.md「回滚」一节"
b) 或列出内置主题,挑一个复制起步(推荐 benya-clean——已按红线 1 / 2 改造过)
b) Or list built-in themes, pick one to copy as a starting point (benya-clean recommended — already adapted per Red Lines 1 / 2)
curl -s -H "$AUTH" "$BASE/api/wechat/themes"
> 本 skill 也自带一个 WeChat-safe 起手模板 [`themes/theme.example.json`](./themes/theme.example.json)(暖橘编辑风,复制改配色即可)。curl -s -H "$AUTH" "$BASE/api/wechat/themes"
> This skill also includes a WeChat-safe starting template [`themes/theme.example.json`](./themes/theme.example.json) (warm orange editing style; just copy and change the palette).2. 按用户口语描述改 themeJson
2. Modify themeJson according to the user's spoken description
把「换成暖橘色 / 标题要竖条 / 引用做成卡片 / 字再大一点 / 排版长得像某某号」翻译成合法 themeJson。
字段全貌见 ;守住上面四条红线。改配色最省事:只动 八个键。
references/theme-schema.mdpaletteTranslate "make it warm orange / titles need a vertical bar / quotes as cards / make text bigger / layout looks like XXX account" into a valid themeJson. Field overview in ; hold the four red lines above. The easiest way to change colors: only touch the eight keys.
references/theme-schema.mdpalette3. 本地先自检
3. Local self-check first
把生成的主题写成本地文件,过本 skill 自带的零依赖校验器:
bash
node scripts/validate-theme.mjs /tmp/my-theme.json # 有硬错误按提示改;exit 0 = 合法Write the generated theme to a local file and run this skill's zero-dependency validator:
bash
node scripts/validate-theme.mjs /tmp/my-theme.json # follow hints for hard errors; exit 0 = valid4. 预览(改一版看一版)
4. Preview (preview after each change)
API 渲染 → 存本地 .html 打开:
bash
undefinedAPI render → save local .html and open:
bash
undefined用编辑中的 themeJson 渲染样例文章,拿回 HTML 存本地打开肉眼看
Render sample article with the themeJson being edited, get HTML back, save locally and open visually
curl -s -H "$AUTH" -H 'Content-Type: application/json'
-d "$(jq -n --arg md "$(cat sample.md)" --slurpfile t /tmp/my-theme.json
'{markdown:$md, title:"预览", themeJson:$t[0]}')"
"$BASE/api/wechat/render" | jq -r '.data.html' > /tmp/preview.html open /tmp/preview.html # macOS;别的平台用浏览器打开
-d "$(jq -n --arg md "$(cat sample.md)" --slurpfile t /tmp/my-theme.json
'{markdown:$md, title:"预览", themeJson:$t[0]}')"
"$BASE/api/wechat/render" | jq -r '.data.html' > /tmp/preview.html open /tmp/preview.html # macOS;别的平台用浏览器打开
> `sample.md` 别带 `#` 一级标题:`title` 会被插成 h1,正文再有就显示两个大标题。
> render 返回的 HTML 与最终存进草稿的正文**逐字一致**。`data.warnings` 若有(如未知 `{{token}}`)一并看一眼。
→ 本机已装 `dby-publish` 且想在本机「改文件重跑即换肤」地预览,读 `references/live-preview.md`,
不需要就别读。改 themeJson 本身以上面 API 渲染那条路为准。curl -s -H "$AUTH" -H 'Content-Type: application/json'
-d "$(jq -n --arg md "$(cat sample.md)" --slurpfile t /tmp/my-theme.json
'{markdown:$md, title:"预览", themeJson:$t[0]}')"
"$BASE/api/wechat/render" | jq -r '.data.html' > /tmp/preview.html open /tmp/preview.html # macOS; use browser on other platforms
-d "$(jq -n --arg md "$(cat sample.md)" --slurpfile t /tmp/my-theme.json
'{markdown:$md, title:"预览", themeJson:$t[0]}')"
"$BASE/api/wechat/render" | jq -r '.data.html' > /tmp/preview.html open /tmp/preview.html # macOS; use browser on other platforms
> Don't include a `#` first-level heading in `sample.md`: the `title` will be inserted as h1, and having one in the body would show two big headings.
> The HTML returned by render is **character-for-character identical** to the final draft body. Check `data.warnings` (e.g., unknown `{{token}}`) if present.
→ If local `dby-publish` is installed and you want to preview by "modifying file then re-run to switch skins", read `references/live-preview.md`; don't read unless needed. For modifying themeJson itself, the API rendering path above is authoritative.5. ⚠️ 落地生效范围(两条渲染路的主题源)
5. ⚠️ Scope of effect (theme source for the two rendering paths)
→ 主题调好、要存回去时读 ——它回答「存了之后哪条路会变、
哪条不会」,并给出存回服务端 / 落本机文件 / 改坏了回滚的完整命令。
references/deploy-scope.md存回服务端设为默认是主路(/ + ):doubaoya.com 网页排版
工作室、、以及未钉本机主题的 发文都读它。
仍需落一份本机 theme 文件的只有三种情形:发文时钉着 / 、
离线或拉取失败要兜底、用的是不带自动拉取的旧版 pipeline。
落完向用户汇报时如实说清各自的生效范围,别混着说。
POSTPUTisDefault:truePOST /api/wechat/renderdby-publish--themeconfig.mdTheme→ Read when the theme is tuned and ready to save back — it answers "after saving, which path changes and which doesn't", and gives complete commands for saving to server / local file / rollback if broken.
references/deploy-scope.mdSaving back to server as default is the main path (/ + ): doubaoya.com web layout studio, , and publishing that does not pin a local theme all read from it. A local theme file is only needed in three cases: publishing with / pinned, offline or fetch failure fallback, or older pipeline without auto-fetch. When reporting to the user, truthfully clarify the scope for each, don't mix them up.
POSTPUTisDefault:truePOST /api/wechat/renderdby-publish--themeconfig.mdThemethemeJson 结构(速查)
themeJson Structure (Quick Reference)
→ 真要动手写 / 改 themeJson 的字段时读 (top-level 键、
支持的标签、 插值、 / / 这些位置),
不需要就别读。只换配色的话改 八个键就够,其余用 自动跟随。
references/theme-schema.mdelements{{token}}li.markerimg.figureStylehr.htmlpalette{{token}}主题契约以本包为准(跑它,别照文档记); 本 skill 的scripts/validate-theme.mjs+references/theme-schema.md足够独立完成一次改主题。themes/theme.example.json
→ Read when actually writing/modifying themeJson fields (top-level keys, supported tags in , interpolation, positions like / / ); don't read unless needed. For just changing colors, editing the eight keys is enough, the rest follows automatically via .
references/theme-schema.mdelements{{token}}li.markerimg.figureStylehr.htmlpalette{{token}}The theme contract is based on this package's(run it, don't memorize the docs); this skill'sscripts/validate-theme.mjs+references/theme-schema.mdare sufficient to independently complete a theme modification.themes/theme.example.json
下一步(先看用户要的终态是什么)
Next Steps (First Determine the User's Desired End State)
别默认往下推——用户要的终态停在哪一档,就在哪一档收手:
| 用户要的终态 | 到这一步够不够 | 下一步 |
|---|---|---|
| 只要把默认排版改成自己想要的样子 | ✅ 够了,到这里就结束(以后每次渲染自动生效) | — |
| 还要用这套主题排版某篇正文、存进自己公众号草稿箱 | 不够 | |
| 已有排好版的图文,只想让它进草稿箱 | 不够 | |
| 正文还没写 | 不够 | |
| 正文没过违禁词 | 不够 | |
| 说不清要到哪一步 | — | |
Don't default to pushing further — stop at the tier where the user's desired end state lies:
| User's Desired End State | Is This Step Enough? | Next Step |
|---|---|---|
| Only need to change the default layout to what I want | ✅ Enough, stop here (takes effect automatically for every render from now on) | — |
| Also want to layout a specific article with this theme and save it to my WeChat draft box | Not enough | |
| Already have typeset content, just want it into the draft box | Not enough | |
| Content not written yet | Not enough | |
| Content hasn't passed banned-word check | Not enough | |
| Unclear about the next step | — | |