@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.
- package/lib/render.js +58 -3
- 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