feishu-docs-cli 0.1.0-beta.8 → 1.0.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/README.md +70 -13
- package/README.zh.md +53 -13
- package/dist/auth.js +31 -17
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +16 -4
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +4 -2
- package/dist/client.js +177 -61
- package/dist/client.js.map +1 -1
- package/dist/commands/authorize.d.ts +3 -0
- package/dist/commands/authorize.js +16 -34
- package/dist/commands/authorize.js.map +1 -1
- package/dist/commands/cp.d.ts +9 -0
- package/dist/commands/cp.js +70 -0
- package/dist/commands/cp.js.map +1 -0
- package/dist/commands/create.js +21 -7
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/delete.js +53 -54
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/login.js +6 -2
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/ls.js +38 -38
- package/dist/commands/ls.js.map +1 -1
- package/dist/commands/mkdir.d.ts +6 -0
- package/dist/commands/mkdir.js +49 -0
- package/dist/commands/mkdir.js.map +1 -0
- package/dist/commands/mv.d.ts +9 -0
- package/dist/commands/mv.js +72 -0
- package/dist/commands/mv.js.map +1 -0
- package/dist/commands/read.d.ts +1 -1
- package/dist/commands/read.js +17 -354
- package/dist/commands/read.js.map +1 -1
- package/dist/commands/search.js +57 -55
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/share.d.ts +1 -1
- package/dist/commands/share.js +164 -91
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/update.js +43 -60
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/wiki.js +8 -20
- package/dist/commands/wiki.js.map +1 -1
- package/dist/parser/block-types.d.ts +0 -1
- package/dist/parser/block-types.js +0 -22
- package/dist/parser/block-types.js.map +1 -1
- package/dist/parser/blocks-to-md.d.ts +10 -18
- package/dist/parser/blocks-to-md.js +341 -450
- package/dist/parser/blocks-to-md.js.map +1 -1
- package/dist/scopes.d.ts +5 -47
- package/dist/scopes.js +9 -54
- package/dist/scopes.js.map +1 -1
- package/dist/services/block-writer.d.ts +3 -2
- package/dist/services/block-writer.js +29 -13
- package/dist/services/block-writer.js.map +1 -1
- package/dist/services/doc-blocks.d.ts +1 -1
- package/dist/services/doc-blocks.js +1 -1
- package/dist/services/doc-blocks.js.map +1 -1
- package/dist/services/doc-enrichment.d.ts +64 -0
- package/dist/services/doc-enrichment.js +397 -0
- package/dist/services/doc-enrichment.js.map +1 -0
- package/dist/services/image-download.d.ts +31 -0
- package/dist/services/image-download.js +127 -0
- package/dist/services/image-download.js.map +1 -0
- package/dist/services/markdown-convert.d.ts +20 -0
- package/dist/services/markdown-convert.js +55 -1
- package/dist/services/markdown-convert.js.map +1 -1
- package/dist/services/wiki-nodes.d.ts +1 -1
- package/dist/services/wiki-nodes.js +2 -3
- package/dist/services/wiki-nodes.js.map +1 -1
- package/dist/types/api-responses.d.ts +34 -0
- package/dist/types/api-responses.js +8 -0
- package/dist/types/api-responses.js.map +1 -0
- package/dist/types/index.d.ts +9 -17
- package/dist/utils/concurrency.d.ts +12 -0
- package/dist/utils/concurrency.js +37 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/errors.d.ts +3 -1
- package/dist/utils/errors.js +11 -7
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/retry.d.ts +49 -0
- package/dist/utils/retry.js +70 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/scope-prompt.d.ts +23 -18
- package/dist/utils/scope-prompt.js +62 -51
- package/dist/utils/scope-prompt.js.map +1 -1
- package/package.json +3 -1
- package/skills/feishu-docs/SKILL.md +37 -2
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Input: Block[] (flat array with parent_id + children)
|
|
5
|
-
* Output: Markdown string
|
|
6
|
-
*/
|
|
7
|
-
import { BlockType, isHeading, headingLevel, CODE_LANGUAGES, } from "./block-types.js";
|
|
1
|
+
/** Convert Feishu document blocks (flat array with parent_id + children) to Markdown string. */
|
|
2
|
+
import { BlockType, headingLevel, CODE_LANGUAGES, } from "./block-types.js";
|
|
8
3
|
import { elementsToMarkdown } from "./text-elements.js";
|
|
9
|
-
/**
|
|
10
|
-
* Emoji ID to Unicode mapping for common callout emojis.
|
|
11
|
-
*/
|
|
12
4
|
const EMOJI_MAP = {
|
|
13
5
|
round_pushpin: "\u{1F4CD}",
|
|
14
6
|
bulb: "\u{1F4A1}",
|
|
@@ -80,10 +72,7 @@ const EMOJI_MAP = {
|
|
|
80
72
|
function emojiIdToUnicode(emojiId) {
|
|
81
73
|
return EMOJI_MAP[emojiId] || `:${emojiId}:`;
|
|
82
74
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Build a tree from flat block array.
|
|
85
|
-
* Each block gains a `_children` array of child block objects.
|
|
86
|
-
*/
|
|
75
|
+
/** Build a tree from flat block array. */
|
|
87
76
|
function buildTree(blocks) {
|
|
88
77
|
const map = new Map();
|
|
89
78
|
for (const block of blocks) {
|
|
@@ -99,7 +88,6 @@ function buildTree(blocks) {
|
|
|
99
88
|
root = node;
|
|
100
89
|
}
|
|
101
90
|
}
|
|
102
|
-
// Sort children according to `children` order if available
|
|
103
91
|
for (const node of map.values()) {
|
|
104
92
|
if (node.children && node.children.length > 0) {
|
|
105
93
|
const order = new Map(node.children.map((id, i) => [id, i]));
|
|
@@ -112,9 +100,7 @@ function buildTree(blocks) {
|
|
|
112
100
|
}
|
|
113
101
|
return root;
|
|
114
102
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Main entry: convert blocks to markdown string.
|
|
117
|
-
*/
|
|
103
|
+
/** Main entry: convert blocks to markdown string. */
|
|
118
104
|
export function blocksToMarkdown(blocks, options = {}) {
|
|
119
105
|
if (!blocks || blocks.length === 0)
|
|
120
106
|
return "";
|
|
@@ -130,7 +116,6 @@ export function blocksToMarkdown(blocks, options = {}) {
|
|
|
130
116
|
sheetDataMap: options.sheetDataMap || new Map(),
|
|
131
117
|
warnings: [],
|
|
132
118
|
};
|
|
133
|
-
// Extract document title from root PAGE block
|
|
134
119
|
if (root.block_type === BlockType.PAGE) {
|
|
135
120
|
const titleText = getElements(root, "page", ctx);
|
|
136
121
|
if (titleText) {
|
|
@@ -149,7 +134,6 @@ export function blocksToMarkdown(blocks, options = {}) {
|
|
|
149
134
|
renderNode(child, lines, ctx, 0, {});
|
|
150
135
|
}
|
|
151
136
|
}
|
|
152
|
-
// Emit warnings to stderr
|
|
153
137
|
for (const w of ctx.warnings) {
|
|
154
138
|
process.stderr.write(`feishu-docs: warning: ${w}\n`);
|
|
155
139
|
}
|
|
@@ -158,9 +142,7 @@ export function blocksToMarkdown(blocks, options = {}) {
|
|
|
158
142
|
.replace(/\n{3,}/g, "\n\n")
|
|
159
143
|
.trim() + "\n");
|
|
160
144
|
}
|
|
161
|
-
/**
|
|
162
|
-
* Render children with ordered list index tracking.
|
|
163
|
-
*/
|
|
145
|
+
/** Render children with ordered list index tracking. */
|
|
164
146
|
function renderChildren(children, lines, ctx, depth) {
|
|
165
147
|
const childState = { orderedIndex: 0 };
|
|
166
148
|
for (const child of children) {
|
|
@@ -174,409 +156,344 @@ function renderChildren(children, lines, ctx, depth) {
|
|
|
174
156
|
}
|
|
175
157
|
}
|
|
176
158
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Render a single node and its children.
|
|
179
|
-
*/
|
|
159
|
+
/** Render a single node via dispatch table lookup. */
|
|
180
160
|
function renderNode(node, lines, ctx, depth, state) {
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
renderChildren(node._children, lines, ctx, depth);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
if (type === BlockType.TEXT) {
|
|
188
|
-
const text = getElements(node, "text", ctx);
|
|
189
|
-
lines.push(indent + text);
|
|
190
|
-
lines.push("");
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
if (isHeading(type)) {
|
|
194
|
-
const level = headingLevel(type);
|
|
195
|
-
const key = `heading${level}`;
|
|
196
|
-
const text = getElements(node, key, ctx);
|
|
197
|
-
const hashes = "#".repeat(Math.min(level, 6));
|
|
198
|
-
lines.push(`${hashes} ${text}`);
|
|
199
|
-
lines.push("");
|
|
200
|
-
// Headings in Feishu can have children (folded/collapsible content)
|
|
201
|
-
renderChildren(node._children, lines, ctx, depth);
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
if (type === BlockType.BULLET) {
|
|
205
|
-
const text = getElements(node, "bullet", ctx);
|
|
206
|
-
lines.push(`${indent}- ${text}`);
|
|
207
|
-
renderChildren(node._children, lines, ctx, depth + 1);
|
|
208
|
-
if (depth === 0)
|
|
209
|
-
lines.push("");
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
if (type === BlockType.ORDERED) {
|
|
213
|
-
const idx = (state.orderedIndex || 0) + 1;
|
|
214
|
-
const text = getElements(node, "ordered", ctx);
|
|
215
|
-
lines.push(`${indent}${idx}. ${text}`);
|
|
216
|
-
renderChildren(node._children, lines, ctx, depth + 1);
|
|
217
|
-
if (depth === 0)
|
|
218
|
-
lines.push("");
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
if (type === BlockType.TODO) {
|
|
222
|
-
const todo = (node.todo || {});
|
|
223
|
-
const text = getElements(node, "todo", ctx);
|
|
224
|
-
const check = todo.done ? "x" : " ";
|
|
225
|
-
lines.push(`${indent}- [${check}] ${text}`);
|
|
226
|
-
renderChildren(node._children, lines, ctx, depth + 1);
|
|
227
|
-
if (depth === 0)
|
|
228
|
-
lines.push("");
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
if (type === BlockType.CODE) {
|
|
232
|
-
const codeData = (node.code || {});
|
|
233
|
-
const langNum = codeData.style?.language;
|
|
234
|
-
const lang = langNum !== undefined
|
|
235
|
-
? CODE_LANGUAGES[langNum] || ""
|
|
236
|
-
: "";
|
|
237
|
-
const text = getElements(node, "code", ctx);
|
|
238
|
-
lines.push(`\`\`\`${lang}`);
|
|
239
|
-
lines.push(text);
|
|
240
|
-
lines.push("```");
|
|
241
|
-
lines.push("");
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
if (type === BlockType.QUOTE) {
|
|
245
|
-
const text = getElements(node, "quote", ctx);
|
|
246
|
-
const quotedLines = text.split("\n").map((l) => `> ${l}`);
|
|
247
|
-
lines.push(...quotedLines);
|
|
248
|
-
for (const child of node._children) {
|
|
249
|
-
const childLines = [];
|
|
250
|
-
renderNode(child, childLines, ctx, 0, {});
|
|
251
|
-
lines.push(...childLines.map((l) => (l ? `> ${l}` : ">")));
|
|
252
|
-
}
|
|
253
|
-
lines.push("");
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
if (type === BlockType.QUOTE_CONTAINER) {
|
|
257
|
-
for (const child of node._children) {
|
|
258
|
-
const childLines = [];
|
|
259
|
-
renderNode(child, childLines, ctx, 0, {});
|
|
260
|
-
lines.push(...childLines.map((l) => (l ? `> ${l}` : ">")));
|
|
261
|
-
}
|
|
262
|
-
lines.push("");
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (type === BlockType.EQUATION) {
|
|
266
|
-
const eq = (node.equation || {});
|
|
267
|
-
const content = (eq.content || "").trim();
|
|
268
|
-
lines.push(`$$`);
|
|
269
|
-
lines.push(content);
|
|
270
|
-
lines.push(`$$`);
|
|
271
|
-
lines.push("");
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
if (type === BlockType.DIVIDER) {
|
|
275
|
-
lines.push("---");
|
|
276
|
-
lines.push("");
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
if (type === BlockType.IMAGE) {
|
|
280
|
-
const imageData = (node.image || {});
|
|
281
|
-
const fileToken = imageData.token;
|
|
282
|
-
const url = fileToken ? ctx.imageUrlMap.get(fileToken) || "" : "";
|
|
283
|
-
const alt = imageData.alt || "";
|
|
284
|
-
if (url) {
|
|
285
|
-
lines.push(``);
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
lines.push(``);
|
|
289
|
-
}
|
|
290
|
-
lines.push("");
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
if (type === BlockType.TABLE) {
|
|
294
|
-
renderTable(node, lines, ctx);
|
|
295
|
-
lines.push("");
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
if (type === BlockType.CALLOUT) {
|
|
299
|
-
const callout = (node.callout || {});
|
|
300
|
-
const emoji = callout.emoji_id
|
|
301
|
-
? emojiIdToUnicode(callout.emoji_id) + " "
|
|
302
|
-
: "";
|
|
303
|
-
let isFirst = true;
|
|
304
|
-
for (const child of node._children) {
|
|
305
|
-
const childLines = [];
|
|
306
|
-
renderNode(child, childLines, ctx, 0, {});
|
|
307
|
-
const first = childLines.shift() || "";
|
|
308
|
-
// Emoji only on the very first line of the callout
|
|
309
|
-
const prefix = isFirst ? emoji : "";
|
|
310
|
-
isFirst = false;
|
|
311
|
-
lines.push(`> ${prefix}${first}`);
|
|
312
|
-
for (const cl of childLines) {
|
|
313
|
-
lines.push(cl ? `> ${cl}` : ">");
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
lines.push("");
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
if (type === BlockType.DIAGRAM) {
|
|
320
|
-
const text = getElements(node, "diagram", ctx);
|
|
321
|
-
lines.push("```mermaid");
|
|
322
|
-
lines.push(sanitizeMermaid(text));
|
|
323
|
-
lines.push("```");
|
|
324
|
-
lines.push("");
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
if (type === BlockType.IFRAME) {
|
|
328
|
-
const iframe = (node.iframe || {});
|
|
329
|
-
const url = iframe.component?.url || "";
|
|
330
|
-
lines.push(`[嵌入](${url})`);
|
|
331
|
-
lines.push("");
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
if (type === BlockType.GRID) {
|
|
335
|
-
for (const child of node._children) {
|
|
336
|
-
for (const grandchild of child._children) {
|
|
337
|
-
renderNode(grandchild, lines, ctx, depth, {});
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
if (type === BlockType.GRID_COLUMN) {
|
|
343
|
-
for (const child of node._children) {
|
|
344
|
-
renderNode(child, lines, ctx, depth, {});
|
|
345
|
-
}
|
|
346
|
-
return;
|
|
161
|
+
const renderer = RENDERERS.get(node.block_type);
|
|
162
|
+
if (renderer) {
|
|
163
|
+
renderer(node, { lines, ctx, depth, state });
|
|
347
164
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
if (type === BlockType.FILE) {
|
|
353
|
-
const fileData = (node.file || {});
|
|
354
|
-
const name = fileData.name || "文件";
|
|
355
|
-
const token = fileData.token || "";
|
|
356
|
-
const url = ctx.imageUrlMap.get(token) || "";
|
|
357
|
-
lines.push(`[${name}](${url || token})`);
|
|
165
|
+
else {
|
|
166
|
+
ctx.warnings.push(`\u4E0D\u652F\u6301\u7684\u5185\u5BB9\u7C7B\u578B: ${node.block_type}`);
|
|
167
|
+
lines.push(`[\u4E0D\u652F\u6301\u7684\u5185\u5BB9\u7C7B\u578B: ${node.block_type}]`);
|
|
358
168
|
lines.push("");
|
|
359
|
-
return;
|
|
360
169
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
170
|
+
}
|
|
171
|
+
function renderPage(node, rctx) {
|
|
172
|
+
renderChildren(node._children, rctx.lines, rctx.ctx, rctx.depth);
|
|
173
|
+
}
|
|
174
|
+
function renderText(node, rctx) {
|
|
175
|
+
const text = getElements(node, "text", rctx.ctx);
|
|
176
|
+
rctx.lines.push(" ".repeat(rctx.depth) + text);
|
|
177
|
+
rctx.lines.push("");
|
|
178
|
+
}
|
|
179
|
+
function renderHeading(node, rctx) {
|
|
180
|
+
const level = headingLevel(node.block_type);
|
|
181
|
+
const text = getElements(node, `heading${level}`, rctx.ctx);
|
|
182
|
+
rctx.lines.push(`${"#".repeat(Math.min(level, 6))} ${text}`);
|
|
183
|
+
rctx.lines.push("");
|
|
184
|
+
renderChildren(node._children, rctx.lines, rctx.ctx, rctx.depth);
|
|
185
|
+
}
|
|
186
|
+
function renderBullet(node, rctx) {
|
|
187
|
+
const text = getElements(node, "bullet", rctx.ctx);
|
|
188
|
+
rctx.lines.push(`${" ".repeat(rctx.depth)}- ${text}`);
|
|
189
|
+
renderChildren(node._children, rctx.lines, rctx.ctx, rctx.depth + 1);
|
|
190
|
+
if (rctx.depth === 0)
|
|
191
|
+
rctx.lines.push("");
|
|
192
|
+
}
|
|
193
|
+
function renderOrdered(node, rctx) {
|
|
194
|
+
const idx = (rctx.state.orderedIndex || 0) + 1;
|
|
195
|
+
const text = getElements(node, "ordered", rctx.ctx);
|
|
196
|
+
rctx.lines.push(`${" ".repeat(rctx.depth)}${idx}. ${text}`);
|
|
197
|
+
renderChildren(node._children, rctx.lines, rctx.ctx, rctx.depth + 1);
|
|
198
|
+
if (rctx.depth === 0)
|
|
199
|
+
rctx.lines.push("");
|
|
200
|
+
}
|
|
201
|
+
function renderTodo(node, rctx) {
|
|
202
|
+
const todo = (node.todo || {});
|
|
203
|
+
const text = getElements(node, "todo", rctx.ctx);
|
|
204
|
+
const check = todo.done ? "x" : " ";
|
|
205
|
+
rctx.lines.push(`${" ".repeat(rctx.depth)}- [${check}] ${text}`);
|
|
206
|
+
renderChildren(node._children, rctx.lines, rctx.ctx, rctx.depth + 1);
|
|
207
|
+
if (rctx.depth === 0)
|
|
208
|
+
rctx.lines.push("");
|
|
209
|
+
}
|
|
210
|
+
function renderCode(node, rctx) {
|
|
211
|
+
const codeData = (node.code || {});
|
|
212
|
+
const langNum = codeData.style?.language;
|
|
213
|
+
const lang = langNum !== undefined
|
|
214
|
+
? CODE_LANGUAGES[langNum] || ""
|
|
215
|
+
: "";
|
|
216
|
+
const text = getElements(node, "code", rctx.ctx);
|
|
217
|
+
rctx.lines.push(`\`\`\`${lang}`, text, "```", "");
|
|
218
|
+
}
|
|
219
|
+
function renderQuote(node, rctx) {
|
|
220
|
+
const text = getElements(node, "quote", rctx.ctx);
|
|
221
|
+
rctx.lines.push(...text.split("\n").map((l) => `> ${l}`));
|
|
222
|
+
for (const child of node._children) {
|
|
223
|
+
const childLines = [];
|
|
224
|
+
renderNode(child, childLines, rctx.ctx, 0, {});
|
|
225
|
+
rctx.lines.push(...childLines.map((l) => (l ? `> ${l}` : ">")));
|
|
226
|
+
}
|
|
227
|
+
rctx.lines.push("");
|
|
228
|
+
}
|
|
229
|
+
function renderQuoteContainer(node, rctx) {
|
|
230
|
+
for (const child of node._children) {
|
|
231
|
+
const childLines = [];
|
|
232
|
+
renderNode(child, childLines, rctx.ctx, 0, {});
|
|
233
|
+
rctx.lines.push(...childLines.map((l) => (l ? `> ${l}` : ">")));
|
|
411
234
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
235
|
+
rctx.lines.push("");
|
|
236
|
+
}
|
|
237
|
+
function renderEquation(node, rctx) {
|
|
238
|
+
const eq = (node.equation || {});
|
|
239
|
+
rctx.lines.push("$$", (eq.content || "").trim(), "$$", "");
|
|
240
|
+
}
|
|
241
|
+
function renderDivider(_node, rctx) {
|
|
242
|
+
rctx.lines.push("---", "");
|
|
243
|
+
}
|
|
244
|
+
function renderImage(node, rctx) {
|
|
245
|
+
const imageData = (node.image || {});
|
|
246
|
+
const fileToken = imageData.token;
|
|
247
|
+
const url = fileToken ? rctx.ctx.imageUrlMap.get(fileToken) || "" : "";
|
|
248
|
+
const alt = imageData.alt || "";
|
|
249
|
+
rctx.lines.push(url ? `` : ``);
|
|
250
|
+
rctx.lines.push("");
|
|
251
|
+
}
|
|
252
|
+
function renderTableBlock(node, rctx) {
|
|
253
|
+
renderTable(node, rctx.lines, rctx.ctx);
|
|
254
|
+
rctx.lines.push("");
|
|
255
|
+
}
|
|
256
|
+
function renderCallout(node, rctx) {
|
|
257
|
+
const callout = (node.callout || {});
|
|
258
|
+
const emoji = callout.emoji_id
|
|
259
|
+
? emojiIdToUnicode(callout.emoji_id) + " "
|
|
260
|
+
: "";
|
|
261
|
+
let isFirst = true;
|
|
262
|
+
for (const child of node._children) {
|
|
263
|
+
const childLines = [];
|
|
264
|
+
renderNode(child, childLines, rctx.ctx, 0, {});
|
|
265
|
+
const first = childLines.shift() || "";
|
|
266
|
+
const prefix = isFirst ? emoji : "";
|
|
267
|
+
isFirst = false;
|
|
268
|
+
rctx.lines.push(`> ${prefix}${first}`);
|
|
269
|
+
for (const cl of childLines) {
|
|
270
|
+
rctx.lines.push(cl ? `> ${cl}` : ">");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
rctx.lines.push("");
|
|
274
|
+
}
|
|
275
|
+
function renderDiagram(node, rctx) {
|
|
276
|
+
const text = getElements(node, "diagram", rctx.ctx);
|
|
277
|
+
rctx.lines.push("```mermaid", sanitizeMermaid(text), "```", "");
|
|
278
|
+
}
|
|
279
|
+
function renderIframe(node, rctx) {
|
|
280
|
+
const url = (node.iframe || {}).component?.url ||
|
|
281
|
+
"";
|
|
282
|
+
rctx.lines.push(`[嵌入](${url})`, "");
|
|
283
|
+
}
|
|
284
|
+
function renderGrid(node, rctx) {
|
|
285
|
+
for (const child of node._children) {
|
|
286
|
+
for (const grandchild of child._children) {
|
|
287
|
+
renderNode(grandchild, rctx.lines, rctx.ctx, rctx.depth, {});
|
|
422
288
|
}
|
|
423
|
-
lines.push("");
|
|
424
|
-
return;
|
|
425
289
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (data && data.fields.length > 0) {
|
|
431
|
-
if (data.title) {
|
|
432
|
-
lines.push(`**${data.title}**`);
|
|
433
|
-
lines.push("");
|
|
434
|
-
}
|
|
435
|
-
lines.push("| " +
|
|
436
|
-
data.fields.map((f) => f.replace(/\|/g, "\\|")).join(" | ") +
|
|
437
|
-
" |");
|
|
438
|
-
lines.push("| " + data.fields.map(() => "---").join(" | ") + " |");
|
|
439
|
-
for (const row of data.records) {
|
|
440
|
-
lines.push("| " +
|
|
441
|
-
row
|
|
442
|
-
.map((c) => String(c).replace(/\|/g, "\\|").replace(/\n/g, " "))
|
|
443
|
-
.join(" | ") +
|
|
444
|
-
" |");
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
else {
|
|
448
|
-
lines.push(`[电子表格: ${token}]`);
|
|
449
|
-
}
|
|
450
|
-
lines.push("");
|
|
451
|
-
return;
|
|
290
|
+
}
|
|
291
|
+
function renderGridColumn(node, rctx) {
|
|
292
|
+
for (const child of node._children) {
|
|
293
|
+
renderNode(child, rctx.lines, rctx.ctx, rctx.depth, {});
|
|
452
294
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
295
|
+
}
|
|
296
|
+
function renderTableCell() {
|
|
297
|
+
/* Handled by renderTable */
|
|
298
|
+
}
|
|
299
|
+
function renderFile(node, rctx) {
|
|
300
|
+
const fileData = (node.file || {});
|
|
301
|
+
const name = fileData.name || "文件";
|
|
302
|
+
const token = fileData.token || "";
|
|
303
|
+
const url = rctx.ctx.imageUrlMap.get(token) || "";
|
|
304
|
+
rctx.lines.push(`[${name}](${url || token})`, "");
|
|
305
|
+
}
|
|
306
|
+
const MERMAID_KEYWORDS = [
|
|
307
|
+
"graph",
|
|
308
|
+
"flowchart",
|
|
309
|
+
"sequenceDiagram",
|
|
310
|
+
"classDiagram",
|
|
311
|
+
"gantt",
|
|
312
|
+
"pie",
|
|
313
|
+
"erDiagram",
|
|
314
|
+
];
|
|
315
|
+
function renderAddons(node, rctx) {
|
|
316
|
+
const addOns = node["add_ons"] || {};
|
|
317
|
+
try {
|
|
318
|
+
const record = JSON.parse(addOns.record || "{}");
|
|
319
|
+
if (record.data &&
|
|
320
|
+
typeof record.data === "string" &&
|
|
321
|
+
MERMAID_KEYWORDS.some((k) => record.data.includes(k))) {
|
|
322
|
+
rctx.lines.push("```mermaid", sanitizeMermaid(record.data.trim()), "```", "");
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
/* not parseable, skip */
|
|
473
328
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
lines.push(
|
|
480
|
-
|
|
481
|
-
|
|
329
|
+
}
|
|
330
|
+
function renderMdTable(fields, records, lines) {
|
|
331
|
+
lines.push("| " + fields.map((f) => f.replace(/\|/g, "\\|")).join(" | ") + " |");
|
|
332
|
+
lines.push("| " + fields.map(() => "---").join(" | ") + " |");
|
|
333
|
+
for (const row of records) {
|
|
334
|
+
lines.push("| " +
|
|
335
|
+
row
|
|
336
|
+
.map((c) => String(c).replace(/\|/g, "\\|").replace(/\n/g, " "))
|
|
337
|
+
.join(" | ") +
|
|
338
|
+
" |");
|
|
482
339
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (url) {
|
|
490
|
-
lines.push(`[JIRA: ${key || summary}](${url})`);
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
493
|
-
lines.push(`[JIRA: ${key || summary}]`);
|
|
494
|
-
}
|
|
495
|
-
lines.push("");
|
|
496
|
-
return;
|
|
340
|
+
}
|
|
341
|
+
function renderBitable(node, rctx) {
|
|
342
|
+
const token = node.bitable?.token || "";
|
|
343
|
+
const data = rctx.ctx.bitableDataMap.get(token);
|
|
344
|
+
if (data && data.fields.length > 0) {
|
|
345
|
+
renderMdTable(data.fields, data.records, rctx.lines);
|
|
497
346
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if (node._children.length > 0) {
|
|
501
|
-
renderChildren(node._children, lines, ctx, depth);
|
|
502
|
-
}
|
|
503
|
-
else {
|
|
504
|
-
lines.push("[知识库目录]");
|
|
505
|
-
lines.push("");
|
|
506
|
-
}
|
|
507
|
-
return;
|
|
347
|
+
else {
|
|
348
|
+
rctx.lines.push(`[多维表格: ${token}]`);
|
|
508
349
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
350
|
+
rctx.lines.push("");
|
|
351
|
+
}
|
|
352
|
+
function renderBoard(node, rctx) {
|
|
353
|
+
const token = node.board
|
|
354
|
+
?.token || "";
|
|
355
|
+
const imagePath = rctx.ctx.boardImageMap.get(token);
|
|
356
|
+
rctx.lines.push(imagePath ? `` : `[画板: ${token}]`);
|
|
357
|
+
rctx.lines.push("");
|
|
358
|
+
}
|
|
359
|
+
function renderSheet(node, rctx) {
|
|
360
|
+
const token = node.sheet?.token || "";
|
|
361
|
+
const data = rctx.ctx.sheetDataMap.get(token);
|
|
362
|
+
if (data && data.fields.length > 0) {
|
|
363
|
+
if (data.title) {
|
|
364
|
+
rctx.lines.push(`**${data.title}**`, "");
|
|
517
365
|
}
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
// Agenda blocks — meeting agenda rendering
|
|
521
|
-
if (type === BlockType.AGENDA || type === BlockType.AGENDA_ITEM) {
|
|
522
|
-
renderChildren(node._children, lines, ctx, depth);
|
|
523
|
-
return;
|
|
366
|
+
renderMdTable(data.fields, data.records, rctx.lines);
|
|
524
367
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
lines.push(`${indent}**${text}**`);
|
|
528
|
-
lines.push("");
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
if (type === BlockType.AGENDA_ITEM_CONTENT) {
|
|
532
|
-
renderChildren(node._children, lines, ctx, depth);
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
// OKR blocks — complex business data, skip silently
|
|
536
|
-
if (type === BlockType.OKR ||
|
|
537
|
-
type === BlockType.OKR_OBJECTIVE ||
|
|
538
|
-
type === BlockType.OKR_KEY_RESULT ||
|
|
539
|
-
type === BlockType.OKR_PROGRESS) {
|
|
540
|
-
return;
|
|
368
|
+
else {
|
|
369
|
+
rctx.lines.push(`[电子表格: ${token}]`);
|
|
541
370
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
371
|
+
rctx.lines.push("");
|
|
372
|
+
}
|
|
373
|
+
function renderTask(node, rctx) {
|
|
374
|
+
const indent = " ".repeat(rctx.depth);
|
|
375
|
+
const task = (node.task || {});
|
|
376
|
+
const text = getElements(node, "task", rctx.ctx);
|
|
377
|
+
const summary = text || task.summary || task.task_id || "未命名任务";
|
|
378
|
+
const parts = [];
|
|
379
|
+
if (task.assignees && task.assignees.length > 0) {
|
|
380
|
+
parts.push(task.assignees.map((a) => `@${a.name || a.id || "?"}`).join(", "));
|
|
381
|
+
}
|
|
382
|
+
if (task.due)
|
|
383
|
+
parts.push(`截止: ${task.due}`);
|
|
384
|
+
const meta = parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
385
|
+
rctx.lines.push(`${indent}- [${task.completed ? "x" : " "}] ${summary}${meta}`);
|
|
386
|
+
if (rctx.depth === 0)
|
|
387
|
+
rctx.lines.push("");
|
|
388
|
+
}
|
|
389
|
+
function renderLinkPreview(node, rctx) {
|
|
390
|
+
const preview = node["link_preview"] || {};
|
|
391
|
+
const url = preview.url || "";
|
|
392
|
+
rctx.lines.push(`[${preview.title || url || "链接"}](${url})`, "");
|
|
393
|
+
}
|
|
394
|
+
function renderJiraIssue(node, rctx) {
|
|
395
|
+
const jira = node["jira_issue"] || {};
|
|
396
|
+
const key = jira.key || "";
|
|
397
|
+
const url = jira.url || "";
|
|
398
|
+
const label = key || jira.summary || "JIRA Issue";
|
|
399
|
+
rctx.lines.push(url ? `[JIRA: ${label}](${url})` : `[JIRA: ${label}]`);
|
|
400
|
+
rctx.lines.push("");
|
|
401
|
+
}
|
|
402
|
+
function renderChildrenOrPlaceholder(placeholder) {
|
|
403
|
+
return (node, rctx) => {
|
|
404
|
+
if (node._children.length > 0)
|
|
405
|
+
renderChildren(node._children, rctx.lines, rctx.ctx, rctx.depth);
|
|
406
|
+
else
|
|
407
|
+
rctx.lines.push(placeholder, "");
|
|
554
408
|
};
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
409
|
+
}
|
|
410
|
+
const renderWikiCatalog = renderChildrenOrPlaceholder("[知识库目录]");
|
|
411
|
+
const renderSubPageList = renderChildrenOrPlaceholder("[子页面列表]");
|
|
412
|
+
function renderDelegateChildren(node, rctx) {
|
|
413
|
+
renderChildren(node._children, rctx.lines, rctx.ctx, rctx.depth);
|
|
414
|
+
}
|
|
415
|
+
function renderAgendaItemTitle(node, rctx) {
|
|
416
|
+
const text = getElements(node, "agenda_item_title", rctx.ctx);
|
|
417
|
+
rctx.lines.push(`${" ".repeat(rctx.depth)}**${text}**`, "");
|
|
418
|
+
}
|
|
419
|
+
function renderNoop() {
|
|
420
|
+
/* OKR, synced blocks, AI template -- no content */
|
|
421
|
+
}
|
|
422
|
+
const REF_TYPE_LABELS = {
|
|
423
|
+
[BlockType.MINDNOTE]: "思维笔记",
|
|
424
|
+
[BlockType.VIEW]: "视图",
|
|
425
|
+
[BlockType.CHAT_CARD]: "群消息卡片",
|
|
426
|
+
[BlockType.ISV]: "三方块",
|
|
427
|
+
};
|
|
428
|
+
function renderRefType(node, rctx) {
|
|
429
|
+
const label = REF_TYPE_LABELS[node.block_type] || "未知引用";
|
|
430
|
+
const data = node[Object.keys(node).find((k) => typeof node[k] === "object" &&
|
|
431
|
+
node[k] !== null &&
|
|
432
|
+
node[k]
|
|
433
|
+
?.token) || ""] || {};
|
|
434
|
+
rctx.lines.push(`[${label}: ${data.token || ""}]`, "");
|
|
435
|
+
}
|
|
436
|
+
/** Block type dispatch table. */
|
|
437
|
+
const RENDERERS = new Map([
|
|
438
|
+
[BlockType.PAGE, renderPage],
|
|
439
|
+
[BlockType.TEXT, renderText],
|
|
440
|
+
[BlockType.HEADING1, renderHeading],
|
|
441
|
+
[BlockType.HEADING2, renderHeading],
|
|
442
|
+
[BlockType.HEADING3, renderHeading],
|
|
443
|
+
[BlockType.HEADING4, renderHeading],
|
|
444
|
+
[BlockType.HEADING5, renderHeading],
|
|
445
|
+
[BlockType.HEADING6, renderHeading],
|
|
446
|
+
[BlockType.HEADING7, renderHeading],
|
|
447
|
+
[BlockType.HEADING8, renderHeading],
|
|
448
|
+
[BlockType.HEADING9, renderHeading],
|
|
449
|
+
[BlockType.BULLET, renderBullet],
|
|
450
|
+
[BlockType.ORDERED, renderOrdered],
|
|
451
|
+
[BlockType.CODE, renderCode],
|
|
452
|
+
[BlockType.QUOTE, renderQuote],
|
|
453
|
+
[BlockType.EQUATION, renderEquation],
|
|
454
|
+
[BlockType.TODO, renderTodo],
|
|
455
|
+
[BlockType.BITABLE, renderBitable],
|
|
456
|
+
[BlockType.CALLOUT, renderCallout],
|
|
457
|
+
[BlockType.CHAT_CARD, renderRefType],
|
|
458
|
+
[BlockType.DIAGRAM, renderDiagram],
|
|
459
|
+
[BlockType.DIVIDER, renderDivider],
|
|
460
|
+
[BlockType.FILE, renderFile],
|
|
461
|
+
[BlockType.GRID, renderGrid],
|
|
462
|
+
[BlockType.GRID_COLUMN, renderGridColumn],
|
|
463
|
+
[BlockType.IFRAME, renderIframe],
|
|
464
|
+
[BlockType.IMAGE, renderImage],
|
|
465
|
+
[BlockType.ISV, renderRefType],
|
|
466
|
+
[BlockType.MINDNOTE, renderRefType],
|
|
467
|
+
[BlockType.SHEET, renderSheet],
|
|
468
|
+
[BlockType.TABLE, renderTableBlock],
|
|
469
|
+
[BlockType.TABLE_CELL, renderTableCell],
|
|
470
|
+
[BlockType.VIEW, renderRefType],
|
|
471
|
+
[BlockType.QUOTE_CONTAINER, renderQuoteContainer],
|
|
472
|
+
[BlockType.TASK, renderTask],
|
|
473
|
+
[BlockType.OKR, renderNoop],
|
|
474
|
+
[BlockType.OKR_OBJECTIVE, renderNoop],
|
|
475
|
+
[BlockType.OKR_KEY_RESULT, renderNoop],
|
|
476
|
+
[BlockType.OKR_PROGRESS, renderNoop],
|
|
477
|
+
[BlockType.ADDONS, renderAddons],
|
|
478
|
+
[BlockType.JIRA_ISSUE, renderJiraIssue],
|
|
479
|
+
[BlockType.WIKI_CATALOG, renderWikiCatalog],
|
|
480
|
+
[BlockType.BOARD, renderBoard],
|
|
481
|
+
[BlockType.AGENDA, renderDelegateChildren],
|
|
482
|
+
[BlockType.AGENDA_ITEM, renderDelegateChildren],
|
|
483
|
+
[BlockType.AGENDA_ITEM_TITLE, renderAgendaItemTitle],
|
|
484
|
+
[BlockType.AGENDA_ITEM_CONTENT, renderDelegateChildren],
|
|
485
|
+
[BlockType.LINK_PREVIEW, renderLinkPreview],
|
|
486
|
+
[BlockType.SOURCE_SYNCED, renderNoop],
|
|
487
|
+
[BlockType.REFERENCE_SYNCED, renderNoop],
|
|
488
|
+
[BlockType.SUB_PAGE_LIST, renderSubPageList],
|
|
489
|
+
[BlockType.AI_TEMPLATE, renderNoop],
|
|
490
|
+
]);
|
|
491
|
+
/** Get inline text from a block's elements. */
|
|
573
492
|
function getElements(node, key, ctx) {
|
|
574
493
|
const data = node[key] || {};
|
|
575
494
|
return elementsToMarkdown(data.elements, ctx);
|
|
576
495
|
}
|
|
577
|
-
/**
|
|
578
|
-
* Render a table block as Markdown table.
|
|
579
|
-
*/
|
|
496
|
+
/** Render a table block as Markdown table. */
|
|
580
497
|
function renderTable(node, lines, ctx) {
|
|
581
498
|
const tableData = (node.table || {});
|
|
582
499
|
const property = tableData.property || {};
|
|
@@ -584,38 +501,26 @@ function renderTable(node, lines, ctx) {
|
|
|
584
501
|
const colSize = property.column_size || 0;
|
|
585
502
|
if (rowSize === 0 || colSize === 0)
|
|
586
503
|
return;
|
|
587
|
-
// Build 2D grid from table_cell children
|
|
588
504
|
const cells = node._children;
|
|
589
505
|
const grid = [];
|
|
590
506
|
for (let r = 0; r < rowSize; r++) {
|
|
591
507
|
const row = [];
|
|
592
508
|
for (let c = 0; c < colSize; c++) {
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
if (cell) {
|
|
596
|
-
const text = cellToText(cell, ctx);
|
|
597
|
-
row.push(text);
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
row.push("");
|
|
601
|
-
}
|
|
509
|
+
const cell = cells[r * colSize + c];
|
|
510
|
+
row.push(cell ? cellToText(cell, ctx) : "");
|
|
602
511
|
}
|
|
603
512
|
grid.push(row);
|
|
604
513
|
}
|
|
605
514
|
if (grid.length === 0)
|
|
606
515
|
return;
|
|
607
|
-
|
|
608
|
-
lines.push("| " + grid[0].map(
|
|
609
|
-
// Separator
|
|
516
|
+
const escape = (s) => s.replace(/\|/g, "\\|");
|
|
517
|
+
lines.push("| " + grid[0].map(escape).join(" | ") + " |");
|
|
610
518
|
lines.push("| " + grid[0].map(() => "---").join(" | ") + " |");
|
|
611
|
-
// Data rows
|
|
612
519
|
for (let r = 1; r < grid.length; r++) {
|
|
613
|
-
lines.push("| " + grid[r].map(
|
|
520
|
+
lines.push("| " + grid[r].map(escape).join(" | ") + " |");
|
|
614
521
|
}
|
|
615
522
|
}
|
|
616
|
-
/**
|
|
617
|
-
* Render a table_cell block content as inline text.
|
|
618
|
-
*/
|
|
523
|
+
/** Render a table_cell block content as inline text. */
|
|
619
524
|
function cellToText(cell, ctx) {
|
|
620
525
|
const parts = [];
|
|
621
526
|
for (const child of cell._children) {
|
|
@@ -629,36 +534,22 @@ function cellToText(cell, ctx) {
|
|
|
629
534
|
}
|
|
630
535
|
return parts.join(" ").replace(/\n/g, " ");
|
|
631
536
|
}
|
|
632
|
-
/**
|
|
633
|
-
* Sanitize Feishu mermaid content for standard mermaid compatibility.
|
|
634
|
-
*
|
|
635
|
-
* Feishu's mermaid renderer is more lenient than standard mermaid.
|
|
636
|
-
* This function fixes common incompatibilities:
|
|
637
|
-
* - Block labels (alt, else, loop, opt, rect, par, critical, break, note)
|
|
638
|
-
* don't support <br/> in standard mermaid — replace with comma-space
|
|
639
|
-
* - Arrow messages missing space after ':' — add the space
|
|
640
|
-
*/
|
|
537
|
+
/** Sanitize Feishu mermaid content for standard mermaid compatibility. */
|
|
641
538
|
function sanitizeMermaid(content) {
|
|
642
|
-
|
|
643
|
-
const
|
|
539
|
+
const blockKw = /^(\s*(?:alt|else|loop|opt|par|critical|break)\s+)(.+)$/;
|
|
540
|
+
const arrowRe = /^(\s*\S+\s*-[-.)>x]+[+-]?\s*\S+\s*):(\S)/;
|
|
644
541
|
return content
|
|
645
542
|
.split("\n")
|
|
646
543
|
.map((line) => {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
return
|
|
656
|
-
}
|
|
657
|
-
// Fix missing space after ':' in arrow messages (e.g., A->>B:text → A->>B: text)
|
|
658
|
-
const arrowMatch = line.match(/^(\s*\S+\s*-[-.)>x]+[+-]?\s*\S+\s*):(\S)/);
|
|
659
|
-
if (arrowMatch) {
|
|
660
|
-
return line.replace(/^(\s*\S+\s*-[-.)>x]+[+-]?\s*\S+\s*):(\S)/, "$1: $2");
|
|
661
|
-
}
|
|
544
|
+
const bm = line.match(blockKw);
|
|
545
|
+
if (bm)
|
|
546
|
+
return (bm[1] +
|
|
547
|
+
bm[2]
|
|
548
|
+
.replace(/<br\s*\/?>/gi, ", ")
|
|
549
|
+
.replace(/\(/g, "\uFF08")
|
|
550
|
+
.replace(/\)/g, "\uFF09"));
|
|
551
|
+
if (line.match(arrowRe))
|
|
552
|
+
return line.replace(arrowRe, "$1: $2");
|
|
662
553
|
return line;
|
|
663
554
|
})
|
|
664
555
|
.join("\n");
|