@topmindspace/tms-skills 0.1.18 → 0.2.0

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.
Files changed (98) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.en.md +6 -3
  3. package/README.md +6 -3
  4. package/package.json +5 -2
  5. package/top-ppt-html/README.en.md +2 -2
  6. package/top-ppt-html/README.md +1 -1
  7. package/top-ppt-html/SKILL.md +4 -4
  8. package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.html +24 -12
  9. package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.html +24 -12
  10. package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.html +24 -12
  11. package/top-ppt-html/assets/examples/2026-09-26-topmind-tms-skills-showcase.html +94 -165
  12. package/top-ppt-html/assets/examples/2026-09-26-topmind-tms-skills-showcase.model.json +32 -38
  13. package/top-ppt-html/assets/pptx-export.js +3 -2
  14. package/top-ppt-html/assets/style-gallery.html +2 -1
  15. package/top-ppt-html/assets/templates/architecture.html +38 -15
  16. package/top-ppt-html/assets/templates/engine.css +20 -9
  17. package/top-ppt-html/assets/templates/presentation.html +38 -17
  18. package/top-ppt-html/assets/templates/research.html +44 -23
  19. package/top-ppt-html/package-lock.json +2 -2
  20. package/top-ppt-html/package.json +1 -1
  21. package/top-ppt-html/references/components-atoms.md +1 -1
  22. package/top-ppt-html/references/content-rules.md +17 -11
  23. package/top-ppt-html/references/failure-modes.md +6 -4
  24. package/top-ppt-html/references/high-fidelity.md +14 -1
  25. package/top-ppt-html/references/icons.md +2 -2
  26. package/top-ppt-html/references/layout-grammar.md +3 -1
  27. package/top-ppt-html/references/layouts-research.md +1 -1
  28. package/top-ppt-html/references/pptx-export.md +2 -2
  29. package/top-ppt-html/references/tech-design.md +2 -0
  30. package/top-ppt-html/scripts/build_icon_assets.js +92 -0
  31. package/top-ppt-html/scripts/build_pptx.js +125 -29
  32. package/top-ppt-html/scripts/extract_model.py +21 -1
  33. package/top-ppt-html/scripts/icon_lib.js +53 -0
  34. package/top-ppt-html/scripts/icon_raster.js +86 -0
  35. package/top-ppt-html/scripts/layout-constants.json +4 -1
  36. package/top-ppt-html/scripts/lib_layout_regions.js +3 -1
  37. package/top-ppt-html/scripts/model-schema.json +7 -3
  38. package/top-ppt-html/scripts/package_skill.py +1 -0
  39. package/top-ppt-html/scripts/render_from_model.py +125 -24
  40. package/top-ppt-html/scripts/scaffold_report.py +47 -24
  41. package/top-ppt-html/scripts/sync_runtime.py +2 -1
  42. package/top-ppt-html/scripts/validate_pptx.py +14 -3
  43. package/top-ppt-html/scripts/validate_report.py +1 -1
  44. package/topmind-cover/README.md +40 -0
  45. package/topmind-cover/SKILL.md +66 -0
  46. package/topmind-cover/package-lock.json +16 -0
  47. package/topmind-cover/package.json +16 -0
  48. package/topmind-cover/references/cover-styles.md +40 -0
  49. package/topmind-cover/scripts/audit_css.py +39 -0
  50. package/topmind-cover/scripts/audit_docs.py +58 -0
  51. package/topmind-cover/scripts/audit_skill.py +60 -0
  52. package/topmind-cover/scripts/audit_styles.py +54 -0
  53. package/topmind-cover/scripts/crop-cover.py +69 -0
  54. package/topmind-cover/scripts/negative_tests.py +71 -0
  55. package/topmind-cover/scripts/package_skill.py +188 -0
  56. package/topmind-wechat-post/README.md +49 -0
  57. package/topmind-wechat-post/SKILL.md +208 -0
  58. package/topmind-wechat-post/assets/themes/amber-review.json +35 -0
  59. package/topmind-wechat-post/assets/themes/graphite.json +35 -0
  60. package/topmind-wechat-post/assets/themes/minimal-ink.json +35 -0
  61. package/topmind-wechat-post/assets/themes/newsprint.json +35 -0
  62. package/topmind-wechat-post/assets/themes/tech-blue.json +35 -0
  63. package/topmind-wechat-post/package-lock.json +16 -0
  64. package/topmind-wechat-post/package.json +16 -0
  65. package/topmind-wechat-post/references/design.md +50 -0
  66. package/topmind-wechat-post/references/element-spec.md +162 -0
  67. package/topmind-wechat-post/references/known-pits.md +86 -0
  68. package/topmind-wechat-post/references/theme-map.md +139 -0
  69. package/topmind-wechat-post/references/typography-rules.md +62 -0
  70. package/topmind-wechat-post/references/wechat-constraints.md +106 -0
  71. package/topmind-wechat-post/references/workflow.md +102 -0
  72. package/topmind-wechat-post/references/writing-quality.md +129 -0
  73. package/topmind-wechat-post/scripts/audit_css.py +39 -0
  74. package/topmind-wechat-post/scripts/audit_docs.py +58 -0
  75. package/topmind-wechat-post/scripts/audit_skill.py +60 -0
  76. package/topmind-wechat-post/scripts/audit_styles.py +54 -0
  77. package/topmind-wechat-post/scripts/lint-wechat.py +446 -0
  78. package/topmind-wechat-post/scripts/md2wechat.py +1602 -0
  79. package/topmind-wechat-post/scripts/negative_tests.py +65 -0
  80. package/topmind-wechat-post/scripts/new-article.py +183 -0
  81. package/topmind-wechat-post/scripts/package_skill.py +188 -0
  82. package/topmind-wechat-post/scripts/push-to-topstream.py +226 -0
  83. package/topmind-wechat-post/scripts/scan_ai_flavor.py +464 -0
  84. package/topmind-wechat-post/scripts/sync-mapping.py +220 -0
  85. package/topmind-wechat-post/scripts/sync-status.py +187 -0
  86. package/topmind-x-article/README.md +40 -0
  87. package/topmind-x-article/SKILL.md +62 -0
  88. package/topmind-x-article/package-lock.json +16 -0
  89. package/topmind-x-article/package.json +16 -0
  90. package/topmind-x-article/references/publish-checklist.md +24 -0
  91. package/topmind-x-article/references/x-format.md +24 -0
  92. package/topmind-x-article/scripts/audit_css.py +39 -0
  93. package/topmind-x-article/scripts/audit_docs.py +58 -0
  94. package/topmind-x-article/scripts/audit_skill.py +60 -0
  95. package/topmind-x-article/scripts/audit_styles.py +54 -0
  96. package/topmind-x-article/scripts/md2x.py +121 -0
  97. package/topmind-x-article/scripts/negative_tests.py +74 -0
  98. package/topmind-x-article/scripts/package_skill.py +188 -0
@@ -0,0 +1,208 @@
1
+ ---
2
+ name: topmind-wechat-post
3
+ description: "公众号文章全生命周期技能:交付包、审校改写、质量三关(事实/逻辑/去AI味)、状态同步、微信内联排版与发布清单。Use when 写公众号、公众号排版、定稿、发公众号。Do NOT use for 只改错别字、小红书/知乎、纯网页发布。"
4
+ triggers:
5
+ - 公众号
6
+ - 微信排版
7
+ - 公众号排版
8
+ - 公众号稿
9
+ - 排版这篇文章
10
+ - 定稿
11
+ - 发公众号
12
+ - 公众号交付
13
+ - wechat
14
+ - mp format
15
+ tags: [wechat, typography, formatting, publishing, markdown, write]
16
+ author: TopMindSpace
17
+ license: MIT
18
+ homepage: https://github.com/topmindspace/tms-skills
19
+ updated: 2026-09-29
20
+ ---
21
+
22
+ # topmind-wechat-post · 公众号创作技能
23
+
24
+ **公众号专用写作技能**。管一篇公众号文章从选题/底稿到发布清单的完整生命周期。
25
+
26
+ ```
27
+ 选题/底稿 → 创作 → 质量三关 → 定稿(状态+目录) → 排版 → 发布清单 →(可选)回推 notes
28
+ ```
29
+
30
+ > 用户明确说公众号 / 微信排版时进本技能;通用长文写作不在本技能范围内。
31
+
32
+ ## 脚本(真源)
33
+
34
+ | 脚本 | 用途 |
35
+ |------|------|
36
+ | `scripts/new-article.py` | 建交付包(骨架稿 + frontmatter + images/diagrams) |
37
+ | `scripts/sync-status.py` | 状态 ⇄ 目录名 ⇄ 字数(草稿 / 定稿 / 已发布) |
38
+ | `scripts/sync-mapping.py` | 映射一致性(frontmatter + 目录 + 可选 notes + topic 总表) |
39
+ | `scripts/push-to-topstream.py` | 可选回推:公众号稿降级为纯 Markdown notes |
40
+ | `scripts/lint-wechat.py` | 排版体检 + 自动修复(中英文间距、段长、AI 腔…) |
41
+ | `scripts/md2wechat.py` | Markdown → 全内联 HTML;**必加 `--embed-images`** |
42
+ | `scripts/scan_ai_flavor.py` | 中文去 AI 味扫描(与 `qu-aiwei-zh` 同源) |
43
+
44
+ ```bash
45
+ # 路径解析:CLI --base/--workspace → env TOPMIND_WECHAT_BASE / TOPMIND_WORKSPACE / TOPSTREAM_ROOT → 惯例
46
+ export TOPMIND_WORKSPACE=/path/to/workspace # 推荐
47
+ python3 scripts/new-article.py --slug demo --title "标题" --direction reverse
48
+ python3 scripts/lint-wechat.py --input <包>/公众号稿.md --fix
49
+ python3 scripts/scan_ai_flavor.py <包>/公众号稿.md # 目标 ≥85
50
+ python3 scripts/md2wechat.py --input <包>/公众号稿.md --out-dir <包> --slug demo \
51
+ --asset-root <素材根> --embed-images
52
+ python3 scripts/sync-status.py --set 定稿 <包> --apply
53
+ ```
54
+
55
+ 细节见 [`references/workflow.md`](references/workflow.md) · [`references/known-pits.md`](references/known-pits.md)。
56
+
57
+ ## 路径默认
58
+
59
+ | 用途 | 解析 |
60
+ |------|------|
61
+ | 交付包根 | `--base` → `TOPMIND_WECHAT_BASE` → `{ws}/40-创作/2026-公众号` 或 `{ws}/20-专题/2026-公众号` |
62
+ | 工作区 | `TOPMIND_WORKSPACE` |
63
+ | 底稿/回推 | `--topstream` → `TOPSTREAM_ROOT` → 可选;不存在则跳过 notes 校验 |
64
+ | 终稿交付 | 可 `save-output` 拷贝到 role:delivery(`88-交付/`),包仍留在创作类专题 |
65
+
66
+ ## 三条路径
67
+
68
+ ### forward(底稿 → 公众号)
69
+
70
+ ```
71
+ 底稿 notes/*.md → 审校改写 → 质量三关 → 定稿 → 排版 → 发布
72
+ ```
73
+
74
+ `new-article.py --direction forward --source-file notes/xxx.md`
75
+
76
+ ### reverse(选题原创)
77
+
78
+ ```
79
+ 选题包 → 调研素材 → 多轮改稿 → 三关 → 定稿 → 排版 → 发布 →(可选)push-to-topstream
80
+ ```
81
+
82
+ `new-article.py --direction reverse`(`target_file: pending`)
83
+
84
+ ### 站外拉取(转载整合 / 在线精选站)
85
+
86
+ 源不在本工作区、也不在 topstream `notes/` 时,**仍落 `reverse` + `target_file: pending`**。
87
+ **不要用 `forward`**:它要求 `source_file` 以 `notes/` 开头且文件真实存在,站外源必然过不了 `sync-mapping.py`。
88
+
89
+ ```bash
90
+ python3 scripts/new-article.py --slug <中文短名> --title "<标题>" --direction reverse
91
+ ```
92
+
93
+ 取源坑(Next.js 站点):正文在 RSC 载荷里,优先 `GET /api/notes/<id>`;图片在 `/api/uploads/<hash>`,记 hash→本地名映射;同图双 hash 用 `md5` 去重。差异与口径写进包内 `README.md`。
94
+
95
+ **回推纪律**:notes 保持纯 Markdown。`::: 容器` / 徽章 / `==高亮==` 只进公众号稿。回推**务必带 `--assets`**(否则 GitHub 上 `images/` 死链):
96
+
97
+ ```bash
98
+ python3 scripts/push-to-topstream.py <包> --target notes/xxx.md --assets # dry-run
99
+ python3 scripts/push-to-topstream.py <包> --target notes/xxx.md --assets --apply \
100
+ --asset-names "00-封面.jpg=01-cover.jpg,…"
101
+ ```
102
+
103
+ ## 交付包与状态
104
+
105
+ ```text
106
+ YYYY-MM-DD-<中文短名> # 草稿
107
+ YYYY-MM-DD-<中文短名>-released # 定稿 / 已发布
108
+ ├── 公众号稿.md # 唯一改稿入口
109
+ ├── <slug>-公众号版.html # 浏览器打开 → 复制正文 → 粘贴后台
110
+ ├── 图片上传清单.md
111
+ ├── images/ diagrams/
112
+ └── README.md # 包说明(可选)
113
+ ```
114
+
115
+ **frontmatter(映射真源)**
116
+
117
+ ```yaml
118
+ status: 草稿 # 草稿 | 定稿 | 已发布
119
+ direction: reverse # forward | reverse
120
+ source_file: "" # forward 填 notes/xxx.md
121
+ target_file: pending # reverse:pending | notes/xxx.md
122
+ word_count: 0 # 纯中文字数,sync-status 维护
123
+ ```
124
+
125
+ 状态与目录名**不要手改**:
126
+
127
+ ```bash
128
+ python3 scripts/sync-status.py --set 定稿 <包> --apply
129
+ ```
130
+
131
+ ## 质量三关(定稿前必过)
132
+
133
+ ### 关 1 · 事实
134
+
135
+ - 承重数字回**一手来源**;厂商口径 / 据报道 分开写
136
+ - 查不到一手来源的传闻**删**
137
+ - 改稿续写:正文已有数字**回源重核**(上一轮文本最不可信)
138
+ - 多口径(主轮/复跑)显式拆开;表格从数据源生成,禁止手抄
139
+ - 外部工具改过的稿:**先核数字再动文字**;「比值对但绝对值错」= 全段重核
140
+
141
+ ### 关 2 · 逻辑
142
+
143
+ - 单边结论旁配反方证据
144
+ - 结构前后一致;同一事实多处同值
145
+
146
+ ### 关 3 · 文字(去 AI 味)
147
+
148
+ ```bash
149
+ python3 scripts/scan_ai_flavor.py <包>/公众号稿.md # ≥85(人话)
150
+ ```
151
+
152
+ - 删套话/黑话/工程圈行话;降调段末加粗金句
153
+ - **满分 ≠ 有人味**:再查「段末金句癖 / 节奏过分整齐 / 没有场景与我 / 报告体标注」
154
+ - **口语化 ≠ 有人味**:删社交垫词(元叙述、空转过渡、姿态句)
155
+ - 判据:**这句话删掉之后,读者少知道了什么?**
156
+
157
+ 详见 [`references/writing-quality.md`](references/writing-quality.md)。
158
+
159
+ ## 排版要点(写稿时)
160
+
161
+ - 开头 150 字内钩子;单段 ≤110 字;列表项 ≤70 字
162
+ - 二级标题序号化;容器:`::: stat|pull|note|tip|warn|danger|dialogue`
163
+ - **`::: stat` 内必须是 `数值 | 说明` 管道行**,否则静默丢弃
164
+ - 评测稿:**图承担数据,正文只解读**;健康密度 **300–450 字/图**
165
+ - 个股用词红线:禁用 买入/推荐/目标价…;文末投资声明
166
+
167
+ 更多:[`references/typography-rules.md`](references/typography-rules.md) · [`references/wechat-constraints.md`](references/wechat-constraints.md)。
168
+
169
+ ## 排版与导出(必读)
170
+
171
+ ```bash
172
+ python3 scripts/md2wechat.py \
173
+ --input <包>/公众号稿.md --out-dir <包> --slug <slug> \
174
+ --asset-root <素材根> --embed-images \
175
+ [--theme assets/themes/minimal-ink.json]
176
+ ```
177
+
178
+ 1. **永远 `--embed-images`**,否则粘贴丢图(相对路径被序列化成 file://)
179
+ 2. **图片 basename 铁律**:正文引用名 = `images/` 目标名;禁止两套同名图共处
180
+ 3. 合规自检出现 `✗` 改生成器,不要手改 HTML
181
+
182
+ 坑清单:[`references/known-pits.md`](references/known-pits.md)。
183
+
184
+ ## 主题
185
+
186
+ | 文件 | 风格 | 适用 |
187
+ |------|------|------|
188
+ | `assets/themes/minimal-ink.json`(默认) | 黑白灰 + 砖红 | 深度研析 / 观点 / 随笔 |
189
+ | `assets/themes/tech-blue.json` | 科技蓝 | AI/技术 |
190
+ | `assets/themes/newsprint.json` | 报纸衬线 | 人文评论 |
191
+ | `assets/themes/graphite.json` | 石墨克制 | 严肃报告 |
192
+ | `assets/themes/amber-review.json` | 琥珀评测 | 产品评测 |
193
+
194
+ `md2wechat.py --list-themes` 看全部;`--theme genre:评测` 可按题材自动选。
195
+ 渲染规格见 [`references/element-spec.md`](references/element-spec.md) · 主题映射见 [`references/theme-map.md`](references/theme-map.md)。
196
+
197
+ **平台红线速查**(详见 [`references/wechat-constraints.md`](references/wechat-constraints.md)):禁 `div`/`pre`/`h1`/`figure`/`thead`/flex/float/gradient/shadow;表格 `table-layout` 不写 fixed;列数 ≥4 转卡片。
198
+
199
+ ## 与 Desktop「公众号创作」
200
+
201
+ 可视化工作流伴面(若有):包列表 → 改稿 → 三关 → 预览 → 导出。脚本语义以本技能为准。
202
+
203
+ ## When NOT to use
204
+
205
+ - 只想改几个句子 → 直接编辑
206
+ - 小红书 / 知乎 / 掘金 → 平台约束不同
207
+ - 通用长文交付 → 不在本技能范围
208
+ - 只去 AI 味不排版 → 中文走 `humanizer-zh`(先立保真边界)+ `qu-aiwei-zh`(再扫描定位);英文走 `humanizer`
@@ -0,0 +1,35 @@
1
+ {
2
+ "id": "amber-review",
3
+ "name": "琥珀评测",
4
+ "genre": ["评测", "模型评测", "数据对比", "榜单", "速览"],
5
+ "accent": "#B45309",
6
+ "accent_light": "#E8C79A",
7
+ "accent_soft": "#FBF2E4",
8
+ "text": "#3A3226",
9
+ "text_strong": "#231A0E",
10
+ "text_muted": "#8A7A62",
11
+ "text_light": "#A3927A",
12
+ "surface": "#FAF6EF",
13
+ "border": "#EADFCB",
14
+ "code_bg": "#2B2620",
15
+ "code_text": "#EFE7DA",
16
+ "code_scheme": "mono",
17
+ "c_note": "#C7B9A2",
18
+ "c_tip": "#4F8763",
19
+ "c_warn": "#C98A2B",
20
+ "c_danger": "#BC4F3E",
21
+ "radius": "6px",
22
+ "font_size": "16px",
23
+ "line_height": "1.75",
24
+ "para_gap": "22px",
25
+ "align": "left",
26
+ "h2_style": "number",
27
+ "toc": true,
28
+ "toc_max": 5,
29
+ "toc_min_h2": 4,
30
+ "toc_min_chars": 2000,
31
+ "table_max_cols": 3,
32
+ "img_border": "#EDE2D0",
33
+ "font_stack": "-apple-system,BlinkMacSystemFont,'Helvetica Neue','PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif",
34
+ "mono_stack": "Menlo,Monaco,Consolas,'Courier New',monospace"
35
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "id": "graphite",
3
+ "name": "石墨灰阶",
4
+ "genre": ["技术说明", "专业评论", "方法", "原理"],
5
+ "accent": "#52525B",
6
+ "accent_light": "#52525B",
7
+ "accent_soft": "#EFEFF1",
8
+ "text": "#3A3A3A",
9
+ "text_strong": "#1C1C1C",
10
+ "text_muted": "#7A7A7A",
11
+ "text_light": "#9E9E9E",
12
+ "surface": "#F4F4F4",
13
+ "border": "#E2E2E2",
14
+ "code_bg": "#262626",
15
+ "code_text": "#E4E4E4",
16
+ "code_scheme": "mono",
17
+ "c_note": "#BDBDBD",
18
+ "c_tip": "#6E7F6E",
19
+ "c_warn": "#A08B5E",
20
+ "c_danger": "#9C5B52",
21
+ "radius": "0px",
22
+ "font_size": "16px",
23
+ "line_height": "1.75",
24
+ "para_gap": "22px",
25
+ "align": "left",
26
+ "h2_style": "underline",
27
+ "toc": true,
28
+ "toc_max": 5,
29
+ "toc_min_h2": 4,
30
+ "toc_min_chars": 2000,
31
+ "table_max_cols": 3,
32
+ "img_border": "#EAEAEA",
33
+ "font_stack": "-apple-system,BlinkMacSystemFont,'Helvetica Neue','PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif",
34
+ "mono_stack": "'SF Mono',Menlo,Consolas,'Courier New',monospace"
35
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "id": "minimal-ink",
3
+ "name": "极简黑白灰",
4
+ "genre": ["默认", "观点", "深度分析", "随笔"],
5
+ "accent": "#A6524A",
6
+ "accent_light": "#E3BAB3",
7
+ "accent_soft": "#FAF0EE",
8
+ "text": "#333333",
9
+ "text_strong": "#1A1A1A",
10
+ "text_muted": "#888888",
11
+ "text_light": "#999999",
12
+ "surface": "#F7F7F7",
13
+ "border": "#E8E8E8",
14
+ "code_bg": "#2C2C2C",
15
+ "code_text": "#E8E8E8",
16
+ "code_scheme": "mono",
17
+ "c_note": "#C9C9C9",
18
+ "c_tip": "#5B8C5A",
19
+ "c_warn": "#C08A3E",
20
+ "c_danger": "#B5544C",
21
+ "radius": "4px",
22
+ "font_size": "16px",
23
+ "line_height": "1.75",
24
+ "para_gap": "22px",
25
+ "align": "left",
26
+ "h2_style": "number",
27
+ "toc": true,
28
+ "toc_max": 5,
29
+ "toc_min_h2": 4,
30
+ "toc_min_chars": 2000,
31
+ "table_max_cols": 3,
32
+ "img_border": "#F0F0F0",
33
+ "font_stack": "-apple-system,BlinkMacSystemFont,'Helvetica Neue','PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif",
34
+ "mono_stack": "Menlo,Monaco,Consolas,'Courier New',monospace"
35
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "id": "newsprint",
3
+ "name": "报纸纸感",
4
+ "genre": ["复盘", "特稿", "行业观察", "长文"],
5
+ "accent": "#8C5A3C",
6
+ "accent_light": "#D8C0A8",
7
+ "accent_soft": "#F3E9DE",
8
+ "text": "#2B2B2B",
9
+ "text_strong": "#14110E",
10
+ "text_muted": "#7A7268",
11
+ "text_light": "#9C938A",
12
+ "surface": "#F5F3EE",
13
+ "border": "#E2DDD2",
14
+ "code_bg": "#2E2B27",
15
+ "code_text": "#EDE8DF",
16
+ "code_scheme": "mono",
17
+ "c_note": "#B4AEA2",
18
+ "c_tip": "#5F7A55",
19
+ "c_warn": "#B07C33",
20
+ "c_danger": "#A24B3C",
21
+ "radius": "0px",
22
+ "font_size": "16px",
23
+ "line_height": "1.8",
24
+ "para_gap": "24px",
25
+ "align": "left",
26
+ "h2_style": "center",
27
+ "toc": true,
28
+ "toc_max": 5,
29
+ "toc_min_h2": 4,
30
+ "toc_min_chars": 2000,
31
+ "table_max_cols": 3,
32
+ "img_border": "#E5E1D8",
33
+ "font_stack": "'Songti SC','Noto Serif CJK SC','Source Han Serif SC',Georgia,'Times New Roman',serif",
34
+ "mono_stack": "'SF Mono',Menlo,Consolas,'Courier New',monospace"
35
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "id": "tech-blue",
3
+ "name": "科技蓝",
4
+ "genre": ["教程", "实操", "工具测评", "建站"],
5
+ "accent": "#185FA5",
6
+ "accent_light": "#A9C9E8",
7
+ "accent_soft": "#EAF2FA",
8
+ "text": "#333333",
9
+ "text_strong": "#12233A",
10
+ "text_muted": "#6B7C93",
11
+ "text_light": "#96A3B4",
12
+ "surface": "#F2F6FB",
13
+ "border": "#DCE6F2",
14
+ "code_bg": "#1B2733",
15
+ "code_text": "#DCE6F2",
16
+ "code_scheme": "mono",
17
+ "c_note": "#A8B8C8",
18
+ "c_tip": "#3F8F6A",
19
+ "c_warn": "#C08A3E",
20
+ "c_danger": "#C0554A",
21
+ "radius": "6px",
22
+ "font_size": "16px",
23
+ "line_height": "1.75",
24
+ "para_gap": "22px",
25
+ "align": "left",
26
+ "h2_style": "number",
27
+ "toc": true,
28
+ "toc_max": 5,
29
+ "toc_min_h2": 4,
30
+ "toc_min_chars": 2000,
31
+ "table_max_cols": 3,
32
+ "img_border": "#E4EDF7",
33
+ "font_stack": "-apple-system,BlinkMacSystemFont,'Helvetica Neue','PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif",
34
+ "mono_stack": "Menlo,Monaco,Consolas,'Courier New',monospace"
35
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "topmind-wechat-post",
3
+ "version": "0.1.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "topmind-wechat-post",
9
+ "version": "0.1.0",
10
+ "license": "MIT",
11
+ "engines": {
12
+ "node": ">=18"
13
+ }
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "topmind-wechat-post",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "公众号文章全生命周期:交付包、审校改写、质量三关、状态同步、微信内联排版与发布清单。",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/topmindspace/tms-skills.git",
10
+ "directory": "topmind-wechat-post"
11
+ },
12
+ "type": "commonjs",
13
+ "engines": {
14
+ "node": ">=18"
15
+ }
16
+ }
@@ -0,0 +1,50 @@
1
+ # topmind-wechat-post · 技能侧设计摘要
2
+
3
+ > 技能边界以本文件、`SKILL.md` 和 `references/` 为准。不要另做一份用浏览器本地存储的公众号编辑器。
4
+
5
+ ## 定位
6
+
7
+ - write 族**子技能**(`action_category: write` · `entrypoint: false` · pack `optional: true`)
8
+ - 触发意图:公众号 / 微信排版 / 定稿 / 发公众号
9
+ - 能力真源:`scripts/*.py`(stdlib)+ `references/*` + `assets/themes/*`(5 套,含 `amber-review`)
10
+
11
+ ## 路径解析
12
+
13
+ `--base` → `TOPMIND_WECHAT_BASE` → `{TOPMIND_WORKSPACE}/40-创作|20-专题/{year}-公众号`;topstream 可选(`TOPSTREAM_ROOT`)。
14
+ **禁止**把个人绝对路径写进脚本默认值;兜底惯例路径必须经环境变量可覆盖。
15
+
16
+ ## 三条路径
17
+
18
+ - **forward**:`notes/*.md` → 公众号稿(`source_file`)
19
+ - **reverse**:选题原创 → 公众号稿 → 可选回推(`target_file: pending`;回推**必须** `--assets`)
20
+ - **站外拉取**:在线精选站/转载整合 → 仍用 `reverse` + `pending`(站外源不进 notes)
21
+
22
+ ## 质量三关 + 排版关
23
+
24
+ 事实 / 逻辑 / 文字(scan_ai_flavor ≥85 + 报告腔四症状)→ lint 0 error → md2wechat `--embed-images`。
25
+
26
+ Desktop / 移动伴面只实现**约束子集**;完整语义以本目录 Python 为准。
27
+
28
+ ## references 索引
29
+
30
+ | 文件 | 管什么 |
31
+ |------|--------|
32
+ | `workflow.md` | 流程/状态机/路径/站外拉取 |
33
+ | `known-pits.md` | 实操坑 |
34
+ | `element-spec.md` | 元素 → HTML 渲染规格 |
35
+ | `theme-map.md` | 主题键 ↔ 题材映射 |
36
+ | `wechat-constraints.md` | 平台硬约束(转换产物) |
37
+ | `typography-rules.md` | 阅读节奏/段长 |
38
+ | `writing-quality.md` | 质量三关 |
39
+
40
+ ## 已知分叉与对拍要求
41
+
42
+ | 项 | 真源 | Desktop 子集 | 要求 |
43
+ |----|------|--------------|------|
44
+ | max-item | lint-wechat.py **70** | `WECHAT_MAX_ITEM` | 必须同值 |
45
+ | AI 结构扣分 | scan_ai_flavor.structural | `structuralDeduction()` | 同规则 |
46
+ | stat 管道行 | md2wechat 非 `\|` 丢弃 | wechat-format 同 | 同语义 |
47
+ | embed-images | `--embed-images` | `WechatService.exportViaScript` **优先**;否则 `embedLocalImagesWithChecklist` | 同效果 + 上传清单 |
48
+ | 导出真源 | `md2wechat.py` | 有 Python + 脚本时走 `wechat.exportViaScript` | 内置为回退 |
49
+
50
+ 改动任一侧规则时,**同一次改动**更新另一侧并跑 `tests/plugin-wechat-ui.test.mjs` 对拍断言。
@@ -0,0 +1,162 @@
1
+ # 元素渲染规范
2
+
3
+ `md2wechat.py` 把 Markdown 转成公众号内联样式 HTML 的**唯一依据**。
4
+ 改渲染逻辑或新增语法时,先改本文件再改代码;`audit_inline()` 是本文件的自动校验版本。
5
+
6
+ 合规分级(贯穿全篇):
7
+
8
+ | 级别 | 含义 | 处理原则 |
9
+ |---|---|---|
10
+ | **S** 安全 | 内联纯色/边框/字号,不会静默失效 | 可以放心依赖 |
11
+ | **D** 降级 | 可能被丢弃,但丢了不破坏结构与可读性 | 可用,但**不得作为唯一区分手段** |
12
+ | **X** 禁用 | 会静默失效或整段被吞 | 一律不用;`audit_inline` 判 ERROR |
13
+
14
+ ---
15
+
16
+ ## 一、块级元素
17
+
18
+ | Markdown | 产物 | 级 | 说明 |
19
+ |---|---|---|---|
20
+ | `# 标题` | `<p style="20px/700/center">` | S | 不用 `<h1>`(微信按标题语义覆盖字号) |
21
+ | `## 标题` | `<h2 style="18px/700;color:text_strong">` + 自动编号 | S | 见下方「章节编号」 |
22
+ | `### 标题` | `<h3 style="16px/600">` | S | |
23
+ | `#### 标题` | `<h4 style="15px/600">` | S | |
24
+ | 段落 | `<p style="margin:0 0 {para_gap};line-height:{line_height};letter-spacing:0.5px;text-align:left;word-break:break-word">` | S | `text-align` 只允许 left/center/right |
25
+ | `---` | `<p style="center"><span style="display:inline-block;width:12%;height:2px;background:accent">` | D | 短横线依赖 `inline-block`;丢了会退化成一条不可见的行内元素 |
26
+ | `> 引用` | `<section>` + `background:surface` + `border-left:3px solid accent` | S | **不用 `<blockquote>`**:微信有原生引用样式会覆盖我们的配色 |
27
+ | `- 项` / `1. 项` | `<ul>`/`<ol>` + `<li>`,支持嵌套 | S | 圆点颜色取 accent |
28
+ | ```` ```lang ```` | `<section background:code_bg>` → 语言标签 `<section>` → 代码体 `<section style="white-space:pre-wrap;word-break:break-all">` | S | **不用 `<pre>`**;详见「代码块」 |
29
+ | 表格(≤3 列) | `<table style="width:100%">`,表头行是首个 `<tr>` | S | **不写 `table-layout:fixed`**,**不用 `thead/tbody/colgroup`** |
30
+ | 表格(≥4 列) | 自动转卡片:每行一张 `<section>`,首列作标题、其余「列名 + 值」成对 | S | 公众号列数 >4 必然横向溢出,卡片化是唯一稳的解法 |
31
+ | `![说明](src)` | `<section>` → `<p center>` → `<img style="max-width:100%;height:auto;display:block;margin:0 auto">` | S | **不用 `width:100%`**(小图被拉满变糊);**不用 `<figure>`** |
32
+ | `*图注*`(紧随图片) | `<p style="13px;color:text_light;center">` | S | 独立 `<p>`,不嵌在图片块里 |
33
+
34
+ ---
35
+
36
+ ## 二、行内元素
37
+
38
+ | Markdown | 产物 | 级 | 视觉层级 |
39
+ |---|---|---|---|
40
+ | `**文字**` | `<strong style="font-weight:600;color:text_strong">` | S | **锚点层**,全文 ≤5 处 |
41
+ | `*文字*` | `<em style="font-style:normal;color:text_muted">` | S | 弱化 |
42
+ | `` `代码` `` | `<code style="background:surface;color:accent;font-family:mono">` | S | |
43
+ | `==文字==` | `<strong>` + `background:accent_soft` | S | 容器层(高亮) |
44
+ | `++文字++` 或 `<u>文字</u>` | `<span style="border-bottom:2px solid accent_light;font-weight:600;color:text_strong">` | S | **标记层**,每段 1–3 个 |
45
+ | `~~文字~~` | `<span style="background:accent_soft;font-weight:600">` | S | 半高亮 |
46
+ | `[!文字]` | `<span style="11px;color:accent;background:accent_soft;padding:1px 6px">` | S | 徽章;不用 `inline-block` |
47
+ | `[文字](url)` | 正文 `文字` + 上标 `[n]`,文末「参考链接」块 | S | 公众号正文不支持可点外链 |
48
+
49
+ ### 视觉层级(三层,写稿时的标记纪律)
50
+
51
+ | 层 | 手段 | 频率 | 用错会怎样 |
52
+ |---|---|---|---|
53
+ | **锚点层** | `**加粗**` | 全文 **≤5 处** | 到处加粗 = 没有重点 |
54
+ | **标记层** | `++下划线++` | **覆盖 ≥60% 的正文段落**,每段 1–3 个 4–15 字短语 | 缺失 = 正文变成一堵灰墙,读者无处落脚 |
55
+ | **容器层** | `==高亮==`、`::: 容器`、引用块 | 按需 | 堆太多会显得花 |
56
+
57
+ 标记层是**出现频率最高**的一层,也是最容易被漏掉的一层。
58
+
59
+ `lint-wechat.py` 分两层判:**标记覆盖率 ≥60%**(带标记的正文段落数 ÷ 正文段落数,低于就报「跳读时没有落点」)+ **锚点加粗 ≤5 处**(超出就报「把标记当锚点用」)。
60
+
61
+ **不用「强调占比」这个口径**——它把两层合成一个数,会同时掩盖两种病:锚点爆表(加粗 123 处)和标记归零(下划线 0 处)能算出相近的占比,但修法完全不同。占比还容易凑:35 段里只标 5 段、每段标 20 字也能到 10%,而整段划线等于没标。覆盖率对应的是「还有几段没标」这个可执行动作。
62
+
63
+ ---
64
+
65
+ ## 三、容器
66
+
67
+ `:::` 是自研中间标记(公众号不认 Markdown,由 `md2wechat.py` 转成内联样式 HTML)。
68
+
69
+ | 写法 | 产物 | 用在哪 |
70
+ |---|---|---|
71
+ | `::: note` | 灰边说明块 | 补充说明 |
72
+ | `::: tip` | 绿边提示块 | 实操建议 |
73
+ | `::: warn` | 琥珀边注意块 | 风险提醒 |
74
+ | `::: danger` | 红边警告块 | 严重风险 |
75
+ | `::: pull` | 上下框线 + 居中金句 | 全文点睛,1–2 处 |
76
+ | `::: stat` | 浅底卡片 + 大号 accent 数字 | 关键数据(`数值 \| 说明` 每行一条) |
77
+ | `::: dialogue` | 说话人 + 气泡 | 访谈、问答 |
78
+ | `::: toc` | 前言导读卡 | 一般不用手写,见「自动导读」 |
79
+ | `::: sign` | 尾部签名区 | 一般不用手写,见「自动签名」 |
80
+
81
+ 容器名拼错会**静默降级成 note**——`lint-wechat.py` 会报未知容器。
82
+ 别名(`info`/`memo`→note,`success`/`ok`→tip,`quote`/`golden`→pull,`data`/`metric`→stat,
83
+ `chat`/`talk`→dialogue)等价。
84
+
85
+ ---
86
+
87
+ ## 四、三段自动行为
88
+
89
+ ### 章节编号
90
+
91
+ - 标题里**手写的序号会被剥离**:`## 一、官方总表` → `01 官方总表`
92
+ - 剥离规则:中文序号(`一、`/`第一、`)、阿拉伯序号(`1.`/`01 `)
93
+ - 带负向断言保护:`## 3.4× 成本` **不会**被误剥成 `4× 成本`
94
+ - 编号由 `h2_style` 决定:`number`(01 + 左竖条,默认)/ `bar`(仅左竖条)/
95
+ `underline`(下划线)/ `center`(上下框线)/ `plain`(无装饰)
96
+ - **末章若为收束类用 `∞` 而不是顺延数字**:标题匹配
97
+ 总结/结语/小结/尾声/后记/写在最后/写在后面/收尾/结尾/余论 时,编号取 `∞`
98
+ (`number` 样式下生效)。理由:数字编号暗示"还有下一节",与收束章的定位打架。
99
+ 实测 `## 六、总结` → `∞ 总结`,其余仍为 `01`…`05`。
100
+ - **写稿时不要手写序号**:手写后序号变成不可编程的字符串,
101
+ 换编号样式、加引用、做导航都得手改一遍
102
+
103
+ ### 前言导读
104
+
105
+ - 触发条件(满足任一,且 `toc` 未被 `--no-toc` 关闭):
106
+ - 章节数 ≥ `toc_min_h2`(默认 4)
107
+ - 章节数 ≥ 3 且正文 ≥ `toc_min_chars`(默认 2000 中文字)
108
+ - 位置:**首个正文段落之后**(不是标题与封面图之间)
109
+ - 内容:最多 `toc_max`(默认 5)条,超出时附注「……共 N 节」
110
+ ——**不假装全文只有这几节**
111
+ - **导读的标记必须与正文标题一致**:
112
+ - 标题带编号(`number`)→ 导读也编号,末章收束类同为 `∞`
113
+ - 标题不带编号(`center`/`underline`/`plain`)→ 导读用中性圆点 `·`,
114
+ **不能凭空造出 01/02**(否则读者在正文里找不到对应的编号)
115
+ - 显式 `::: toc` 优先于自动插入
116
+
117
+ ### 尾部签名
118
+
119
+ `--signature` 三态:
120
+
121
+ | 值 | 行为 |
122
+ |---|---|
123
+ | `auto`(默认) | 稿件末尾**已自带**签名/CTA 则不再追加,避免出现两处签名区 |
124
+ | `on` | 无条件追加 |
125
+ | `off` | 只在显式写了 `::: sign` 时渲染 |
126
+
127
+ - 署名用占位 `{{作者名}}`,**不替用户编造人名**;`--author` / `--author-bio` 可填
128
+ - 判定「已自带」的口径:正文末尾 8 个非空段落内出现
129
+ `点赞 / 在看 / 三连 / 点个关注 / 欢迎关注 / 关注「 / 扫码 / 分享给 / 我是…,`
130
+
131
+ ---
132
+
133
+ ## 五、代码块
134
+
135
+ - **不用 `<pre>`**:公众号端不自动换行,窄屏横向溢出,且格式检测会标风险项
136
+ - 用 `<section>` + `white-space:pre-wrap` + `word-break:break-all`
137
+ - 缩进与换行由代码自身提供:**HTML 源码里不在 pre-wrap 容器内插入任何格式化换行**
138
+ (插了就会渲染出伪空行和大左缩进)
139
+ - **色板跟随主题**(`code_scheme`):
140
+ - `mono`(默认):只用「主色 + 明暗层级」。注释最暗、字符串次之、
141
+ 关键字取主色的提亮版、其余用 `code_text`
142
+ - `palenight`:Material Palenight 的 16 色,**已不推荐**——
143
+ 天蓝/翠绿/橙红与砖红、琥珀等主色毫无关系,一篇文章里会出现两套色彩语言
144
+ - 语言标签显示在代码体上方,字号 11px,颜色取 accent
145
+
146
+ ---
147
+
148
+ ## 六、`audit_inline()` 检查清单
149
+
150
+ 每次生成 HTML 后自动跑,`ERROR` 必须清零。检查项已按本文件分级:
151
+
152
+ **ERROR**:`<div>` `<figure>` `<figcaption>` `<pre>` `<thead>` `<tbody>` `<colgroup>`
153
+ `<h1>` `<iframe>` `<video>` `<script>` `<style>`;`text-align` 非标准值;
154
+ px 固定宽度;`white-space:pre/nowrap`;`position/float/transform/opacity`;
155
+ `display:flex/grid`;`-webkit-*`;`linear-gradient`;`box-shadow`;`rgba()/hsla()`;
156
+ `var(--)`;`@media/@keyframes`;`class` 属性;Markdown 标记残留(`==`、`**`)
157
+
158
+ **WARN**:`table-layout:fixed`(列宽均分)、`display:inline-block`(非常规 display 值)、
159
+ `border-radius`(可能被丢,属优雅降级)、图片 `width:100%`、`id` 属性、
160
+ `<img width="...">` 属性
161
+
162
+ WARN 不是「可以忽略」,而是「已知风险,需确认是否可接受」。