SuperAPI AI Directory

wechat-theme-studio

Office & productivity

View repository

Updated 10-16 11:53

About this skill

WeChat Official Account Layout Theme Studio (Doubaoya)

Help users take their WeChat Official Account articles'layout style(color scheme / heading bars / quote cards / captions / dividers…) and change it to what they want based on a colloquial description, with instant local preview; finally this theme issaved back to the server as the default(the doubaoya.com web layout studio,POST /api/wechat/render, and publishing that hasn't pinned a local theme wechat-article-pipeline will all read it), and optionally also save a copyas a local theme file (only needed when publishing with --theme/config.mdTheme pinned or for an offline fallback). See §5 for the scope of each.

Layout is a declarative themeJson(color scheme palette + an inline-style template for each tag). The renderer uses it to deterministically render Markdown into WeChat Official Account inline-style HTML. Your (the agent's) job is togenerate / modify a valid themeJson based on the description, self-check + preview it locally, thensave it back to the server。


⚠️ Three hard red lines (read first, uphold throughout)

Red line 1 · WeChat compatibility: heading decorations can only be attached to the text-bearing element itself

The WeChat Official Account editor will strip out "empty <section>/<span>」that have no text and rely purely on inline-block width/height + background to create a color block. So decorations like heading lead bars / emphasis vertical bars / underlinesmust be written directly as inline styles on the heading element itself, neveruse a separate empty decoration block (e.g. stuffing an empty <section style='width:32px;height:4px;background:…'>before the h2 — it will disappear when published to WeChat Official Account).

  • ✅ Right:elements.h2.style write directly inside border-left:4px solid {{accent}};border-bottom:1px solid {{border}};padding:0 0 8px 11px; (the vertical bar + hairline underline live on the h2 itself; the h2 has text, so the editor keeps it).
  • ❌ Wrong: use wrapBefore to inject an empty <section> as the color bar (no text → gets stripped).

Red line 2 · WeChat compatibility: don't wrap the whole article in a decorations.articleWrap outer frame

Do notuse decorations.articleWrap to wrap the whole body in a light-background + rounded-corner card outer frame — in the WeChat Official Account body it will turn into ajarring border。 articleWrap.before / after with an empty string left. If you want "breathing room", use paragraph spacing (margin / line-height) and local cards (such as a blockquote rounded card) instead.

Red line 3 · Security: themeJson gets inlined into the WeChat Official Account draft; the server will reject an invalid theme

themeJson is user-controllable and gets inlined into the final HTML, so the server POST/PUT/render will all validateTheme, but directly 400. The generated thememustsatisfy (see scripts/validate-theme.mjs and the schema below for details):

  • top-level only allows meta / palette / page / elements / decorations / components, any other key = hard error.
  • palette values must look like colors (#hex / rgb() / hsl() / named colors / {{token}}). The 8 standard keys:text heading accent accent2 muted bgSoft border link。
  • page keys:fontFamily fontSize lineHeight letterSpacing color, values are all strings.
  • elements.<tag>.style is inline CSS,cannot contain < or >(put markup in wrapBefore/wrapAfter/hr.html)。
  • Six hard red lines: No string may contain <script / <style / class= / src= / javascript: / onX=(such as onclick=)。
  • The entire themeJson ≤ 64KB。

After generating, be sure to self-check locally first(see §3), and only submit to the server once it passes, saving one 400 round trip.


Contract: server-side theme CRUD + rendering (based on render-routes.ts in the doubaoya.com main repo)

All requests use your own key for Bearer authentication (login cookie sessions are also supported). Base URL defaults to https://doubaoya.com (can be overridden with DOUBAOYA_BASE_URL ). Responses use a unified envelope:{ success, requestId, data, error }—on success read data, on failure read error.{code,message}。

export DOUBAOYA_API_KEY="dyh_你的密钥"   # doubaoya.com → 登录 → 密钥中心 → 生成
BASE="${DOUBAOYA_BASE_URL:-https://doubaoya.com}"
AUTH="Authorization: Bearer $DOUBAOYA_API_KEY"
Method & path Request body Success data Purpose
GET /api/wechat/theme — { theme: <我的默认主题行> | null } Read my current default theme
GET /api/wechat/themes — { themes: [<我的全部主题行>], builtin: [<内置主题>] } List my themes + built-in theme catalog
POST /api/wechat/render { markdown, title?, themeId?, themeJson? } { html, themeSource, warnings? } Markdown→WeChat official account HTML(free, no credits deducted)
POST /api/wechat/theme { name?, themeJson, isDefault? } { theme: <新建行> }(HTTP 201) Create theme;isDefault:true set as default
PUT /api/wechat/theme/:id { name?, themeJson?, isDefault? } { theme: <更新行> } Modify an existing theme (only your own)
DELETE /api/wechat/theme/:id — { deleted:true, id } Delete a theme (only your own)

Key points (all from real code):

  • Theme row fields:{ id, userId, name, themeJson, isDefault, createdAt, updatedAt }(themeJson is that JSON).
  • render theme resolution priority:themeJson(if passed, use this one) > themeId > my default theme > fallback benya-clean. Soto preview a theme being edited, just pass themeJson。
  • one default per person:isDefault:true when the server, within a transaction, sets isDefault on my other themes to the opposite value, ensuring there is only one default.
  • name default = "我的主题";PUT only updates the fields that were passed(themeJson/name/isDefault as needed).
  • themeId invalid → 400 UNKNOWN_THEME;themeJson invalid → 400 UNSAFE_THEME/VALIDATION_ERROR/THEME_TOO_LARGE。

Closed-loop steps

1. Read the starting theme

Start from the current default or some built-in theme:

# a) 读我当前的默认主题(有就在它上面改)
curl -s -H "$AUTH" "$BASE/api/wechat/theme"

# b) 或列出内置主题,挑一个复制起步(推荐 benya-clean——已是微信兼容范本)
curl -s -H "$AUTH" "$BASE/api/wechat/themes"

Recommended to start from benya-clean start: it has already been adapted per red line 1 / red line 2 (title decoration is inline on the title, no articleWrap outer frame), making it the most worry-free WeChat-compatible base. This skill also comes with a WeChat-safe starter template themes/theme.example.json(warm orange editorial style, can be directly copied and recolored).

2. Modify themeJson according to the user's colloquial description

Translate "change to warm orange / titles need a vertical bar / make quotes into cards / make the text a bit larger / make the layout look like some account" into valid themeJson. For the full schema, see §themeJson structure below; uphold the three red lines above. Changing colors is the easiest:Only touch palette eight keys, the rest use {{token}} to follow automatically.

3. Self-check locally first (saving one 400 round trip)

Write the generated theme to a local file and run it through this skill's bundled zero-dependency validator:

node scripts/validate-theme.mjs /tmp/my-theme.json   # 有硬错误按提示改;exit 0 = 合法

This validate-theme.mjs and the server-side security boundarysame origin, same rules; if it passes locally, it will basically pass on the server. (If wechat-article-pipelineis also installed on this machine, its scripts/validate-theme.mjs is completely equivalent.)

4. Two preview paths (change one version, view one version)

(a) API render → save locally as .html and open(no other skill needed, just curl, most reliable):

# 用编辑中的 themeJson 渲染样例文章,拿回 HTML 存本地打开肉眼看
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;别的平台用浏览器打开

The HTML returned by render and the body text finally saved into the draft areword-for-word identical—This is the most reliable safeguard: what you preview is what gets published. data.warnings If there is any (such as unknown {{token}}) take a look at it as well.

(b) Local real-time skin-switching workbench(optional, requires wechat-article-pipeline, which comes with design-studio.mjs and themes/): Save the theme being edited as wechat-article-pipeline/themes/<名>.json, then start the workbench; the phone WeChat official account frame on the left will preview it in real time as a theme card, and reselecting after editing the file switches the skin:

node wechat-article-pipeline/scripts/design-studio.mjs --md sample.md --title "预览"   # 默认 127.0.0.1:4599

(This workbench only binds to the local machine, only writes local artifacts, does not publish, and does not commit. It focuses on "choosing a theme + cover + illustrations"; for modifying themeJson itself, route (a) still prevails.)

5. ⚠️ Effective scope (theme sources for the two rendering routes)

Server-side default theme(POST/PUT + isDefault:true, stored in the userWechatTheme table) determines doubaoya.com web typesetting studioand POST /api/wechat/render the rendered HTML. wechat-article-pipeline publishingthe theme at that time is taken by --theme x.json > config.json the path written in mdTheme > server-side compiled theme (GET /api/wechat/theme?format=compiled, i.e. the default theme saved back above, falling back if it cannot be fetched) > project default (themes/benya-clean.json).

Therefore:Saving back to the server and setting it as default is the main route—as long as the publishing machine has DOUBAOYA_API_KEY configured andhas notused --theme / config.mdTheme to pin down a local theme, pipeline publishing will automatically fetch this default typesetting, with no need to drop local files. Still need to drop local filesin these cases: a local theme is pinned at publishing time (--theme/config.mdTheme path), offline/fetch failure needs a fallback, or an old pipeline version without automatic fetching is used—in these cases, only saving to the server means what gets published is still the old (local) typesetting.

Step 1 · Save back to the server (effective for the route of web studio + /api/wechat/render + pipeline publishing without a pinned local theme):

# 新建并设为默认(首次)
curl -s -X POST -H "$AUTH" -H 'Content-Type: application/json' \
  -d "$(jq -n --arg name "暖橘编辑" --slurpfile t /tmp/my-theme.json \
        '{name:$name, themeJson:$t[0], isDefault:true}')" \
  "$BASE/api/wechat/theme" | jq '.data.theme.id'

# 或更新已有主题(从 GET /api/wechat/theme 或 /themes 拿到 id 后)
curl -s -X PUT -H "$AUTH" -H 'Content-Type: application/json' \
  -d "$(jq -n --slurpfile t /tmp/my-theme.json '{themeJson:$t[0], isDefault:true}')" \
  "$BASE/api/wechat/theme/<主题id>" | jq '.data.theme.updatedAt'

Step 2 (as needed) · Drop it as a local theme file (only needed when a local theme is pinned at publishing time, or when an offline fallback is required):

# 放进 pipeline 的 themes/ 目录(路径按本机 wechat-article-pipeline 的实际安装位置)
cp /tmp/my-theme.json <wechat-article-pipeline>/themes/my-theme.json

# 发文时显式指定这份主题
node scripts/pipeline.mjs --md a.md --title "标题" --theme themes/my-theme.json
# 或把 config.json 的 "mdTheme" 指向 themes/my-theme.json,以后不必每次带 --theme

Was the theme adjusted in the doubaoya.com web typesetting studio? First pull it down and then drop it locally; do not copy it by hand:

curl -s -H "$AUTH" "$BASE/api/wechat/theme" | jq '.data.theme.themeJson' > /tmp/my-theme.json

After dropping it, report back to the user, andtruthfully state the effective scope of each: **The server-side default typesetting has been updated—the web typesetting studio and POST /api/wechat/render take effect immediately; when the publishing machine has DOUBAOYA_API_KEY configured and no local theme is pinned, pipeline publishing will also automatically fetch this default typesetting (if it cannot be fetched, it falls back to the local theme without interruption).** If a local theme is pinned at publishing time (--theme or config.mdTheme path), then the just-dropped themes/my-theme.json prevails—do not mix them up; explain clearly according to the user's actual configuration which route takes effect.


themeJson structure (quick reference)

five top-level sections (+ advanced components):

{
  "meta":     { "name": "暖橘编辑", "source": "description", "notes": "" },
  "palette":  { "text":"#33302b","heading":"#1f1c18","accent":"#d2691e","accent2":"#a8501a",
                "muted":"#a09a90","bgSoft":"#fbf1e7","border":"#ece3d8","link":"#a8501a" },
  "page":     { "fontFamily":"…,'PingFang SC',sans-serif","fontSize":"16px","lineHeight":"1.8",
                "letterSpacing":"0.01em","color":"{{text}}" },
  "elements": { /* 每个标签一套 inline-style 模板,见下 */ },
  "decorations": { "articleWrap": { "before":"", "after":"" }, "sectionDivider":"…" }
}
  • {{token}} interpolation: any {{key}} in a style/HTML string takes its value first from palette, then from page. To change colors, only touch palette.
  • elements supported tags:h1 h2 h3 h4 p blockquote ul ol li img hr strong em del a code pre, deep-merged by tag (overriding h2 does not affect p).
  • Each element field:style(inline CSS, excluding </>)、wrapBefore/wrapAfter(HTML injected before and after block-level elements—but do not make decorations into empty blocks, see red line 1)、li.marker(custom bullet),img.figureStyle/captionStyle(caption),hr Only html(replace the whole line with a decorative divider, such as a centered short colored line).
  • The correct way to decorate a title(obey red line 1): color bars / vertical bars / underlines should be written with border-left / border-bottom / padding written into the title's own style, don't use an empty wrapBefore block. Refer to benya-clean and this skill's themes/theme.example.json。
  • decorations.articleWrap leave blank(obey red line 2).

The authoritative document for the theme contract is at wechat-article-pipeline/themes/THEME-SCHEMA.md(if installed). This skill's §themeJson structure + theme.example.json is enough to independently complete one theme change.


Prerequisites

  • Node ≥ 18(built-in fetch)、curl、jq(used for assembling JSON; if you don't want to use jq, you can also hand-write the JSON body).
  • One doubaoya.com account + one DOUBAOYA_API_KEY(doubaoya.com → Key Center → Generate).
  • Rendering / saving themes bothfree and no credits deducted. Actually publishing (pushing an article with a theme applied into the draft box) goes through wechat-article-pipeline。

Next step (first see what final state the user wants)

What this skill delivers isa set of layout themes(saved back to the server + a local theme file written), which determines "what every future article looks like." The final state the user wants determines whether this is the endpoint—don't push forward by default:

The final state the user wants Is this step enough Next step
Just want to change the default layout to the way they want ✅ Enough,end here(automatically takes effect on every future render) —
Also wantto use this theme to lay out some article body and save it into their own WeChat official account draft box Not enough wechat-article-pipeline(the default layout saved back to the server will be pulled automatically; for the local theme file use --theme to point to it)
Already have a laid-out article with images and text, just want it to go into the draft box Not enough wechat-draft-publish
The body hasn't been written yet Not enough wechat-hot-write
The body hasn't passed the prohibited-word check Not enough wechat-banned-words
Can't tell which step they want to reach — dby(hop-by-hop navigation of the WeChat official account flywheel)

Language

  • If the user uses Chinese, reply in Chinese; if they use English, reply in English. Chinese replies follow the "Chinese Copywriting Typography Guidelines".