@peteryuan/wxformat 0.1.0 → 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 (2) hide show
  1. package/lib/render.js +58 -3
  2. package/package.json +1 -1
package/lib/render.js CHANGED
@@ -30,7 +30,10 @@ export const THEMES = {
30
30
  hr: { border: "none", height: "4px", background: "radial-gradient(circle,#333333 1.2px,transparent 1.8px) repeat-x", backgroundSize: "8px 4px", margin: "28px 0" },
31
31
  tag: { bg: "#f2f2f2", color: "#555555" },
32
32
  a: { color: "#111111", textDecoration: "underline" },
33
- img: { maxWidth: "100%", borderRadius: "6px", margin: "10px 0" }
33
+ img: { maxWidth: "100%", borderRadius: "6px", margin: "10px 0" },
34
+ table: { borderCollapse: "collapse", width: "100%", margin: "18px 0", fontSize: "14.5px" },
35
+ th: { border: "1px solid #d5d5d5", padding: "8px 10px", background: "#f2f2f2", color: "#111111", fontWeight: "700", textAlign: "left", lineHeight: "1.6" },
36
+ td: { border: "1px solid #d5d5d5", padding: "8px 10px", color: "#2b2b2b", lineHeight: "1.7", verticalAlign: "top" }
34
37
  },
35
38
  paper: {
36
39
  id: "paper",
@@ -51,7 +54,10 @@ export const THEMES = {
51
54
  hr: { border: "none", height: "4px", background: "radial-gradient(circle,#333333 1.2px,transparent 1.8px) repeat-x", backgroundSize: "8px 4px", margin: "28px 0" },
52
55
  tag: { bg: "#efe8d9", color: "#8a6d3b" },
53
56
  a: { color: "#8a6d3b", textDecoration: "underline" },
54
- img: { maxWidth: "100%", borderRadius: "6px", margin: "10px 0" }
57
+ img: { maxWidth: "100%", borderRadius: "6px", margin: "10px 0" },
58
+ table: { borderCollapse: "collapse", width: "100%", margin: "18px 0", fontSize: "15px" },
59
+ th: { border: "1px solid #d8cdb4", padding: "8px 10px", background: "#efe8d9", color: "#6b5736", fontWeight: "700", textAlign: "left", lineHeight: "1.6" },
60
+ td: { border: "1px solid #d8cdb4", padding: "8px 10px", color: "#4a4238", lineHeight: "1.75", verticalAlign: "top" }
55
61
  },
56
62
  celadon: {
57
63
  id: "celadon",
@@ -72,7 +78,10 @@ export const THEMES = {
72
78
  hr: { border: "none", height: "4px", background: "radial-gradient(circle,#333333 1.2px,transparent 1.8px) repeat-x", backgroundSize: "8px 4px", margin: "28px 0" },
73
79
  tag: { bg: "#e3efec", color: "#2e6e63" },
74
80
  a: { color: "#2e6e63", textDecoration: "underline" },
75
- img: { maxWidth: "100%", borderRadius: "6px", margin: "10px 0" }
81
+ img: { maxWidth: "100%", borderRadius: "6px", margin: "10px 0" },
82
+ table: { borderCollapse: "collapse", width: "100%", margin: "18px 0", fontSize: "15px" },
83
+ th: { border: "1px solid #9cc4bd", padding: "8px 10px", background: "#e3efec", color: "#1f3d3a", fontWeight: "700", textAlign: "left", lineHeight: "1.6" },
84
+ td: { border: "1px solid #9cc4bd", padding: "8px 10px", color: "#33433f", lineHeight: "1.75", verticalAlign: "top" }
76
85
  }
77
86
  };
78
87
 
@@ -120,6 +129,9 @@ function parseBlocks(bodyMd) {
120
129
  } else if (/^【插图[::】]/.test(t)) {
121
130
  // 文内插图占位(草稿正文中的 `【插图:画面描述|提示词】` 行)→ 渲染为可见占位块
122
131
  out.push({ type: "imgph", text: t });
132
+ } else if (isTableBlock(t)) {
133
+ // GFM 表格(首行 `| ... |` + 次行 `|---|` 分隔行)
134
+ out.push(parseTable(t));
123
135
  } else {
124
136
  out.push({ type: "p", text: t.replace(/\n/g, "<br />") });
125
137
  }
@@ -138,6 +150,47 @@ function parseBlocks(bodyMd) {
138
150
  * section — 内联样式正文(复制给公众号后台用)
139
151
  * document — 完整 HTML 文档(浏览器预览用)
140
152
  */
153
+ /* ── GFM 表格 ──────────────────────────────────────────────────────── */
154
+
155
+ /** 判断一个文本块是否为 Markdown 表格(首行 `| ... |` + 次行 `|---|` 分隔行)。 */
156
+ function isTableBlock(t) {
157
+ const lines = t.split("\n").map((l) => l.trim()).filter(Boolean);
158
+ if (lines.length < 2) return false;
159
+ if (!/^\|/.test(lines[0])) return false;
160
+ return /^\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/.test(lines[1]);
161
+ }
162
+
163
+ /** 把表格行按 `|` 拆成单元格(去掉首尾管道),返回去空白后的字符串数组。 */
164
+ function splitRow(line) {
165
+ let s = line.trim();
166
+ if (s.startsWith("|")) s = s.slice(1);
167
+ if (s.endsWith("|")) s = s.slice(0, -1);
168
+ return s.split("|").map((c) => c.trim());
169
+ }
170
+
171
+ /** 解析 GFM 表格 → { type, header, aligns, rows }(aligns 记录 `:---:` 对齐)。 */
172
+ function parseTable(t) {
173
+ const lines = t.split("\n").map((l) => l.trim()).filter(Boolean);
174
+ const header = splitRow(lines[0]);
175
+ const aligns = splitRow(lines[1]).map((c) => {
176
+ const l = c.startsWith(":");
177
+ const r = c.endsWith(":");
178
+ return l && r ? "center" : r ? "right" : l ? "left" : "";
179
+ });
180
+ const rows = lines.slice(2).map(splitRow);
181
+ return { type: "table", header, aligns, rows };
182
+ }
183
+
184
+ /** 渲染表格为公众号友好的内联样式 <table>(首行为表头 th,其余为 td)。 */
185
+ function renderTable(b, t) {
186
+ const cellStyle = (base, i) => css(base) + (b.aligns[i] ? `;text-align:${b.aligns[i]}` : "");
187
+ const head = b.header.map((h, i) => `<th style="${cellStyle(t.th, i)}">${inline(h, t)}</th>`).join("");
188
+ const body = b.rows
189
+ .map((r) => `<tr>${b.header.map((_, i) => `<td style="${cellStyle(t.td, i)}">${inline(r[i] ?? "", t)}</td>`).join("")}</tr>`)
190
+ .join("");
191
+ return `<table style="${css(t.table)}"><tr>${head}</tr>${body}</table>`;
192
+ }
193
+
141
194
  export function renderArticle(title, bodyMd, themeId = DEFAULT_THEME, opts = {}) {
142
195
  const t = THEMES[themeId] ?? THEMES[DEFAULT_THEME];
143
196
  const blocks = parseBlocks(bodyMd);
@@ -160,6 +213,8 @@ export function renderArticle(title, bodyMd, themeId = DEFAULT_THEME, opts = {})
160
213
  return `<ol style="${css(t.ol)}">${b.items.map((i) => `<li style="${css(t.li)}">${inline(i, t)}</li>`).join("")}</ol>`;
161
214
  case "hr":
162
215
  return `<hr style="${css(t.hr)}" />`;
216
+ case "table":
217
+ return renderTable(b, t);
163
218
  default:
164
219
  return "";
165
220
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peteryuan/wxformat",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "公众号文章排版 CLI:草稿一键转纯净 Markdown + 公众号 HTML(全内联样式),支持浏览器预览、排版面板一键复制、剪贴板自动复制、JSON 输出,可被脚本、CI 与大模型直接调用。",
5
5
  "type": "module",
6
6
  "main": "lib/core.mjs",