afterpresets 0.2.0 → 0.3.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 +6 -4
- package/native-runtime/CMakeLists.txt +82 -0
- package/native-runtime/README.md +40 -0
- package/native-runtime/THIRD_PARTY_NOTICES.md +14 -0
- package/native-runtime/include/ap_native.h +223 -0
- package/native-runtime/src/apply.cpp +199 -0
- package/native-runtime/src/crypto.cpp +225 -0
- package/native-runtime/src/crypto.hpp +51 -0
- package/native-runtime/src/dialog.cpp +62 -0
- package/native-runtime/src/dialog.hpp +35 -0
- package/native-runtime/src/engine.cpp +909 -0
- package/native-runtime/src/engine.hpp +232 -0
- package/native-runtime/src/fsutil.cpp +291 -0
- package/native-runtime/src/fsutil.hpp +57 -0
- package/native-runtime/src/json.cpp +326 -0
- package/native-runtime/src/json.hpp +70 -0
- package/native-runtime/src/platform.hpp +83 -0
- package/native-runtime/src/platform_common.cpp +60 -0
- package/native-runtime/src/platform_mac.mm +314 -0
- package/native-runtime/src/platform_win.cpp +277 -0
- package/native-runtime/src/runtime.cpp +473 -0
- package/native-runtime/src/util.cpp +169 -0
- package/native-runtime/src/util.hpp +32 -0
- package/native-runtime/src/zip.cpp +243 -0
- package/native-runtime/src/zip.hpp +58 -0
- package/native-runtime/third_party/monocypher/LICENCE.md +167 -0
- package/native-runtime/third_party/monocypher/monocypher-ed25519.c +500 -0
- package/native-runtime/third_party/monocypher/monocypher-ed25519.h +140 -0
- package/native-runtime/third_party/monocypher/monocypher.c +2956 -0
- package/native-runtime/third_party/monocypher/monocypher.h +321 -0
- package/native-runtime/third_party/tinf/LICENSE +22 -0
- package/native-runtime/third_party/tinf/crc32.c +57 -0
- package/native-runtime/third_party/tinf/tinf.h +140 -0
- package/native-runtime/third_party/tinf/tinflate.c +636 -0
- package/native-runtime/vendor-files.json +39 -0
- package/package.json +6 -3
- package/runtime/docs/cep/host.md +8 -0
- package/runtime/docs/common/README.md +1 -1
- package/runtime/docs/native/README.md +44 -0
- package/runtime/docs/native/debugging.md +140 -0
- package/runtime/docs/native/rules.md +402 -0
- package/runtime/docs/native/testing.md +154 -0
- package/runtime/package.json +1 -1
- package/runtime/scripts/ae-sdk-docs/config.js +102 -0
- package/runtime/scripts/ae-sdk-docs/convert.js +430 -0
- package/runtime/scripts/ae-sdk-docs/index.js +314 -0
- package/runtime/scripts/ae-sdk-docs/source.js +205 -0
- package/runtime/scripts/ae-sdk-docs/split.js +170 -0
- package/runtime/scripts/ae-sdk-docs.js +72 -0
- package/runtime/src/host.js +1 -1
- package/src/api/error-messages.js +1 -1
- package/src/check/check.js +3 -3
- package/src/check/native.js +14 -0
- package/src/check/pricing.js +1 -1
- package/src/cli.js +2 -2
- package/src/commands/build.js +8 -2
- package/src/commands/check.js +10 -2
- package/src/commands/dev.js +9 -2
- package/src/commands/docs.js +5 -1
- package/src/commands/init.js +10 -7
- package/src/commands/push.js +5 -2
- package/src/commands/sdk.js +14 -0
- package/src/commands/testers.js +1 -1
- package/src/commands/upgrade.js +2 -2
- package/src/commands/whoami.js +1 -1
- package/src/dev/bridge.js +8 -0
- package/src/dev/native.js +3 -0
- package/src/dev/session.js +4 -3
- package/src/mcp/extendscript.js +9 -8
- package/src/mcp/host-target.js +1 -0
- package/src/mcp/panels.js +1 -1
- package/src/mcp/png.js +1 -0
- package/src/mcp/server.js +2 -2
- package/src/mcp/tools/capture.js +3 -0
- package/src/mcp/tools/host.js +1 -1
- package/src/mcp/tools/native.js +6 -0
- package/src/mcp/tools/project.js +48 -32
- package/src/mcp/tools/releases.js +23 -20
- package/src/native/build.js +27 -0
- package/src/native/cmake/CMakeLists.txt +88 -0
- package/src/native/cmake/zig-windows.cmake +19 -0
- package/src/native/effect.js +3 -0
- package/src/native/install.js +8 -0
- package/src/native/installers.js +4 -0
- package/src/native/pipl.js +20 -0
- package/src/native/restart.js +2 -0
- package/src/native/sdk.js +8 -0
- package/src/project/docs.js +3 -3
- package/src/project/project.js +2 -2
- package/src/project/template.js +5 -4
- package/src/prompt.js +1 -1
- package/src/release/native.js +3 -0
- package/src/release/publish.js +1 -1
- package/src/system/run-command.js +1 -1
- package/src/targets.js +1 -1
- package/template/cep/AGENTS.md +99 -164
- package/template/cep/README.md +7 -2
- package/template/cep/docs/code_guide.md +67 -0
- package/template/cep/docs/initial_setup.md +58 -12
- package/template/cep/docs/release_checklist.md +24 -0
- package/template/hosts/AGENTS.two-apps.md +12 -0
- package/template/native/.mcp.json +8 -0
- package/template/native/AGENTS.md +105 -0
- package/template/native/README.md +71 -0
- package/template/native/docs/code_guide.md +72 -0
- package/template/native/docs/initial_setup.md +179 -0
- package/template/native/docs/release_checklist.md +24 -0
- package/template/native/effect.json +16 -0
- package/template/native/gitignore +8 -0
- package/template/native/package.json +14 -0
- package/template/native/src/Effect.cpp +224 -0
- package/template/native/src/License.cpp +95 -0
- package/template/native/src/License.h +29 -0
- package/template/native/src/Render.h +68 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Markdown clean-up for one page of the After Effects SDK guide (MkDocs
|
|
4
|
+
// source). Turns what only renders well in a browser into compact Markdown an
|
|
5
|
+
// agent reads cheaply:
|
|
6
|
+
//
|
|
7
|
+
// grid tables (+----+ ... +====+, padded to the widest cell, which is
|
|
8
|
+
// most of the guide's bytes) -> pipe tables when every cell
|
|
9
|
+
// is one short line, otherwise one list item per row
|
|
10
|
+
// <pre lang="cpp"> (function signatures inside table cells, with <br/> as
|
|
11
|
+
// line breaks) -> fenced code blocks
|
|
12
|
+
// !!! note admonitions -> "> **Note:** ..." quotes
|
|
13
|
+
// --- horizontal rules -> dropped
|
|
14
|
+
//  screenshots -> "(Screenshot: alt)"
|
|
15
|
+
//
|
|
16
|
+
// Code fences pass through untouched. Links are left alone here; split.js
|
|
17
|
+
// rewrites them once it knows which output file holds each heading.
|
|
18
|
+
|
|
19
|
+
const FENCE = /^(\s*)(`{3,}|~{3,})/;
|
|
20
|
+
const GRID_BORDER = /^\s*\+[-=:+]+\+\s*$/;
|
|
21
|
+
const ADMONITION = /^(\s*)(!!!|\?\?\?\+?)\s+([A-Za-z]+)(?:\s+"([^"]*)")?\s*$/;
|
|
22
|
+
const RULE = /^\s*(-{3,}|\*{3,}|_{3,})\s*$/;
|
|
23
|
+
const PIPE_SEPARATOR = /^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s*$/;
|
|
24
|
+
const PIPE_TABLE_MAX_LINE = 180;
|
|
25
|
+
|
|
26
|
+
/** Heading slugs the way Python-Markdown's toc extension makes them (MkDocs default). */
|
|
27
|
+
function slugify(text) {
|
|
28
|
+
const plain = stripInline(text)
|
|
29
|
+
.normalize("NFKD")
|
|
30
|
+
.replace(/[^\x00-\x7f]/g, "");
|
|
31
|
+
return plain
|
|
32
|
+
.replace(/[^\w\s-]/g, "")
|
|
33
|
+
.trim()
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.replace(/[-\s]+/g, "-");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Heading text without Markdown: what the browser shows. */
|
|
39
|
+
function stripInline(text) {
|
|
40
|
+
return String(text)
|
|
41
|
+
.replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1")
|
|
42
|
+
.replace(/<[^>]+>/g, "")
|
|
43
|
+
.replace(/[`*]/g, "")
|
|
44
|
+
.replace(/\\(.)/g, "$1")
|
|
45
|
+
.trim();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Assigns unique slugs in document order: "x", "x_1", "x_2" like Python-Markdown. */
|
|
49
|
+
function uniqueSlugger() {
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
return function (text) {
|
|
52
|
+
const base = slugify(text);
|
|
53
|
+
let slug = base;
|
|
54
|
+
let n = 1;
|
|
55
|
+
while (seen.has(slug)) slug = base + "_" + n++;
|
|
56
|
+
seen.add(slug);
|
|
57
|
+
return slug;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** <pre> content in the guide escapes "*" as Markdown ("\\*"); in C it is always a plain "*". */
|
|
62
|
+
function preText(text) {
|
|
63
|
+
return decodeEntities(text).replace(/\\\*/g, "*");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function decodeEntities(text) {
|
|
67
|
+
return text
|
|
68
|
+
.replace(/</g, "<")
|
|
69
|
+
.replace(/>/g, ">")
|
|
70
|
+
.replace(/"/g, '"')
|
|
71
|
+
.replace(/'/g, "'")
|
|
72
|
+
.replace(/ /g, " ")
|
|
73
|
+
.replace(/&/g, "&");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isFenceOpen(line) {
|
|
77
|
+
return FENCE.test(line);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Index after the fence that opens at `start` (or lines.length if unclosed). */
|
|
81
|
+
function fenceEnd(lines, start) {
|
|
82
|
+
const m = lines[start].match(FENCE);
|
|
83
|
+
const marker = m[2][0];
|
|
84
|
+
const len = m[2].length;
|
|
85
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
86
|
+
const close = lines[i].match(/^\s*(`{3,}|~{3,})\s*$/);
|
|
87
|
+
if (close && close[1][0] === marker && close[1].length >= len) return i + 1;
|
|
88
|
+
}
|
|
89
|
+
return lines.length;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function dedent(lines) {
|
|
93
|
+
let min = Infinity;
|
|
94
|
+
lines.forEach((l) => {
|
|
95
|
+
if (l.trim()) min = Math.min(min, l.match(/^ */)[0].length);
|
|
96
|
+
});
|
|
97
|
+
if (!isFinite(min) || min === 0) return lines.slice();
|
|
98
|
+
return lines.map((l) => l.slice(Math.min(min, l.match(/^ */)[0].length)));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function trimBlankEdges(lines) {
|
|
102
|
+
let a = 0;
|
|
103
|
+
let b = lines.length;
|
|
104
|
+
while (a < b && !lines[a].trim()) a++;
|
|
105
|
+
while (b > a && !lines[b - 1].trim()) b--;
|
|
106
|
+
return lines.slice(a, b);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Grid tables
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Parses a pandoc/markdown_grid_tables table. Returns { header: [text] | null,
|
|
114
|
+
* rows: [[cellLines]] } where each cell is an array of Markdown lines.
|
|
115
|
+
* Throws if a row does not line up with the border (the caller keeps the raw
|
|
116
|
+
* table then).
|
|
117
|
+
*/
|
|
118
|
+
function parseGridTable(lines) {
|
|
119
|
+
const border = lines[0].replace(/\s+$/, "");
|
|
120
|
+
const cuts = [];
|
|
121
|
+
for (let i = 0; i < border.length; i++) if (border[i] === "+") cuts.push(i);
|
|
122
|
+
if (cuts.length < 2) throw new Error("not a grid table");
|
|
123
|
+
const groups = [];
|
|
124
|
+
let headerCount = -1;
|
|
125
|
+
let current = [];
|
|
126
|
+
for (let i = 1; i < lines.length; i++) {
|
|
127
|
+
const line = lines[i].replace(/\s+$/, "");
|
|
128
|
+
if (line.trimStart()[0] === "+") {
|
|
129
|
+
if (current.length) groups.push(current);
|
|
130
|
+
current = [];
|
|
131
|
+
if (line.indexOf("=") !== -1) headerCount = groups.length;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
cuts.forEach((c) => {
|
|
135
|
+
if (line[c] !== "|") throw new Error("grid table row does not line up: " + line.slice(0, 60));
|
|
136
|
+
});
|
|
137
|
+
current.push(line);
|
|
138
|
+
}
|
|
139
|
+
if (current.length) groups.push(current);
|
|
140
|
+
const width = cuts.length - 1;
|
|
141
|
+
const cells = groups.map((group) => {
|
|
142
|
+
const columns = cuts.slice(0, -1).map(() => []);
|
|
143
|
+
group.forEach((line) => {
|
|
144
|
+
const segs = cuts.slice(0, -1).map((start, k) => line.slice(start + 1, cuts[k + 1]));
|
|
145
|
+
joinSplitPre(segs);
|
|
146
|
+
segs.forEach((seg, k) => columns[k].push(seg.replace(/\s+$/, "")));
|
|
147
|
+
});
|
|
148
|
+
// Cells hold Markdown too (notes, lists): convert it like page text.
|
|
149
|
+
return columns.map((raw) => tidy(convertLines(cleanCell(trimBlankEdges(dedent(raw))), [])));
|
|
150
|
+
});
|
|
151
|
+
if (!width) throw new Error("not a grid table");
|
|
152
|
+
let header = null;
|
|
153
|
+
let rows = cells;
|
|
154
|
+
if (headerCount > 0) {
|
|
155
|
+
header = cells[0].map((cell) => stripInline(cell.join(" ")));
|
|
156
|
+
rows = cells.slice(headerCount);
|
|
157
|
+
}
|
|
158
|
+
return { header, rows };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The guide has a table where one <pre> signature runs across several column
|
|
163
|
+
* borders on the same line. Glue such a <pre> back into its first cell.
|
|
164
|
+
*/
|
|
165
|
+
function joinSplitPre(segs) {
|
|
166
|
+
for (let k = 0; k < segs.length; k++) {
|
|
167
|
+
const opened = (segs[k].match(/<pre[\s>]/g) || []).length;
|
|
168
|
+
const closed = (segs[k].match(/<\/pre>/g) || []).length;
|
|
169
|
+
if (opened <= closed) continue;
|
|
170
|
+
let j = k + 1;
|
|
171
|
+
while (j < segs.length && segs[k].lastIndexOf("</pre>") < segs[k].lastIndexOf("<pre")) {
|
|
172
|
+
segs[k] = segs[k].replace(/\s+$/, "") + segs[j].replace(/^\s+/, " ");
|
|
173
|
+
segs[j] = "";
|
|
174
|
+
j++;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Splits a pipe-table row into trimmed cell texts (escaped \| stays in the cell). */
|
|
180
|
+
function pipeCells(line) {
|
|
181
|
+
const inner = line.trim().replace(/^\|/, "").replace(/\|$/, "");
|
|
182
|
+
const cells = [];
|
|
183
|
+
let cur = "";
|
|
184
|
+
for (let i = 0; i < inner.length; i++) {
|
|
185
|
+
if (inner[i] === "\\" && inner[i + 1] === "|") {
|
|
186
|
+
cur += "\\|";
|
|
187
|
+
i++;
|
|
188
|
+
} else if (inner[i] === "|") {
|
|
189
|
+
cells.push(cur.trim());
|
|
190
|
+
cur = "";
|
|
191
|
+
} else cur += inner[i];
|
|
192
|
+
}
|
|
193
|
+
cells.push(cur.trim());
|
|
194
|
+
return cells;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Parses a pipe table (header, |---| row, rows) into the shape of parseGridTable. */
|
|
198
|
+
function parsePipeTable(lines) {
|
|
199
|
+
const header = pipeCells(lines[0]).map((c) => stripInline(c));
|
|
200
|
+
const rows = lines.slice(2).map((line) => {
|
|
201
|
+
const cells = pipeCells(line);
|
|
202
|
+
while (cells.length < header.length) cells.push("");
|
|
203
|
+
return cells.slice(0, header.length).map((c) => cleanCell(c ? [c] : []));
|
|
204
|
+
});
|
|
205
|
+
return { header, rows };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Converts HTML that only appears inside cells: <pre lang> signatures and <br>. */
|
|
209
|
+
function cleanCell(lines) {
|
|
210
|
+
const out = [];
|
|
211
|
+
lines.forEach((line) => {
|
|
212
|
+
const pre = line.match(/^\s*<pre(?:\s+lang="([^"]*)")?>([\s\S]*?)<\/pre>\s*$/);
|
|
213
|
+
if (pre) {
|
|
214
|
+
if (out.length && out[out.length - 1].trim()) out.push("");
|
|
215
|
+
out.push("```" + (pre[1] || ""));
|
|
216
|
+
preText(pre[2])
|
|
217
|
+
.split(/<br\s*\/?>/)
|
|
218
|
+
.forEach((l) => out.push(l.replace(/\s+$/, "")));
|
|
219
|
+
out.push("```");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const inlined = line.replace(/<pre(?:\s+lang="[^"]*")?>([\s\S]*?)<\/pre>/g, (m, code) => "`" + preText(code).replace(/<br\s*\/?>/g, " ") + "`");
|
|
223
|
+
inlined.split(/<br\s*\/?>/).forEach((l) => out.push(l.replace(/\s+$/, "")));
|
|
224
|
+
});
|
|
225
|
+
// Blank line after a code block that is followed by text.
|
|
226
|
+
const spaced = [];
|
|
227
|
+
out.forEach((line, i) => {
|
|
228
|
+
spaced.push(line);
|
|
229
|
+
if (line === "```" && i + 1 < out.length && out[i + 1].trim() && isClosingFence(out, i)) spaced.push("");
|
|
230
|
+
});
|
|
231
|
+
return spaced;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function isClosingFence(lines, index) {
|
|
235
|
+
let count = 0;
|
|
236
|
+
for (let i = 0; i <= index; i++) if (/^```/.test(lines[i])) count++;
|
|
237
|
+
return count % 2 === 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function isSingleLine(cell) {
|
|
241
|
+
return cell.length <= 1 && !/^```/.test(cell[0] || "");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function escapePipes(text) {
|
|
245
|
+
return text.replace(/(^|[^\\])\|/g, "$1\\|");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** A plain paragraph line: can follow "- **key**: " on the same line. */
|
|
249
|
+
function isParagraphLine(line) {
|
|
250
|
+
return !!line && !/^\s*(```|~~~|[-*+]\s|\d+[.)]\s|>|#|\|)/.test(line);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function renderTable(table) {
|
|
254
|
+
const { header, rows } = table;
|
|
255
|
+
const width = rows.length ? rows[0].length : header ? header.length : 0;
|
|
256
|
+
const allShort = rows.every((row) => row.every(isSingleLine));
|
|
257
|
+
if (header && allShort && width <= 4) {
|
|
258
|
+
const lineOf = (cells) => "| " + cells.map((c) => escapePipes(c)).join(" | ") + " |";
|
|
259
|
+
const body = rows.map((row) => lineOf(row.map((c) => c[0] || "")));
|
|
260
|
+
if (body.every((l) => l.length <= PIPE_TABLE_MAX_LINE)) {
|
|
261
|
+
return [lineOf(header), "|" + header.map(() => "---").join("|") + "|"].concat(body);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return renderRowsAsList(header, rows, width);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function renderRowsAsList(header, rows, width) {
|
|
268
|
+
// The key is column 0; the body is the longest other column; the rest are
|
|
269
|
+
// short "Label: value" notes next to the key.
|
|
270
|
+
let bodyCol = width > 1 ? 1 : 0;
|
|
271
|
+
if (width > 2) {
|
|
272
|
+
let best = -1;
|
|
273
|
+
for (let k = 1; k < width; k++) {
|
|
274
|
+
const total = rows.reduce((sum, row) => sum + row[k].join("\n").length, 0);
|
|
275
|
+
if (total > best) {
|
|
276
|
+
best = total;
|
|
277
|
+
bodyCol = k;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const out = [];
|
|
282
|
+
rows.forEach((row) => {
|
|
283
|
+
const keyCell = row[0];
|
|
284
|
+
let body = width > 1 ? row[bodyCol].slice() : [];
|
|
285
|
+
let key = keyCell[0] || "";
|
|
286
|
+
if (keyCell.length > 1) body = keyCell.slice(1).concat(body.length ? [""] : [], body);
|
|
287
|
+
const notes = [];
|
|
288
|
+
const longNotes = [];
|
|
289
|
+
for (let k = 1; k < width; k++) {
|
|
290
|
+
if (k === bodyCol) continue;
|
|
291
|
+
const cell = row[k];
|
|
292
|
+
if (!cell.length || !cell.join("").trim()) continue;
|
|
293
|
+
const label = header && header[k] ? header[k] + ": " : "";
|
|
294
|
+
if (isSingleLine(cell)) notes.push(label + cell[0]);
|
|
295
|
+
else longNotes.push([""].concat(label ? ["*" + header[k] + ":*"] : [], cell));
|
|
296
|
+
}
|
|
297
|
+
longNotes.forEach((note) => (body = body.concat(note)));
|
|
298
|
+
body = trimBlankEdges(body);
|
|
299
|
+
let head = "- " + (key ? (/\*\*/.test(key) ? key : "**" + key + "**") : "");
|
|
300
|
+
if (notes.length) head += " (" + notes.join("; ") + ")";
|
|
301
|
+
if (body.length && isParagraphLine(body[0])) {
|
|
302
|
+
head += (key || notes.length ? ": " : "") + body[0].trim();
|
|
303
|
+
body = body.slice(1);
|
|
304
|
+
}
|
|
305
|
+
out.push(head);
|
|
306
|
+
body.forEach((line) => out.push(line ? " " + line : ""));
|
|
307
|
+
out.push("");
|
|
308
|
+
});
|
|
309
|
+
return trimBlankEdges(out);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
// Blocks
|
|
314
|
+
|
|
315
|
+
function convertLines(lines, warnings) {
|
|
316
|
+
const out = [];
|
|
317
|
+
let i = 0;
|
|
318
|
+
while (i < lines.length) {
|
|
319
|
+
const line = lines[i];
|
|
320
|
+
if (isFenceOpen(line)) {
|
|
321
|
+
const end = fenceEnd(lines, i);
|
|
322
|
+
for (let k = i; k < end; k++) out.push(lines[k].replace(/\s+$/, ""));
|
|
323
|
+
i = end;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (GRID_BORDER.test(line)) {
|
|
327
|
+
let end = i + 1;
|
|
328
|
+
while (end < lines.length && /^\s*[+|]/.test(lines[end])) end++;
|
|
329
|
+
const indent = line.match(/^\s*/)[0];
|
|
330
|
+
const raw = lines.slice(i, end);
|
|
331
|
+
let rendered;
|
|
332
|
+
try {
|
|
333
|
+
rendered = renderTable(parseGridTable(raw.map((l) => l.slice(indent.length))));
|
|
334
|
+
} catch (error) {
|
|
335
|
+
warnings.push("kept a table as is: " + error.message);
|
|
336
|
+
rendered = raw.map((l) => l.replace(/\s+$/, ""));
|
|
337
|
+
}
|
|
338
|
+
out.push("");
|
|
339
|
+
rendered.forEach((l) => out.push(l ? indent + l : ""));
|
|
340
|
+
out.push("");
|
|
341
|
+
i = end;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (/^\s*\|/.test(line) && i + 1 < lines.length && PIPE_SEPARATOR.test(lines[i + 1])) {
|
|
345
|
+
let end = i + 2;
|
|
346
|
+
while (end < lines.length && /^\s*\|/.test(lines[end])) end++;
|
|
347
|
+
const indent = line.match(/^\s*/)[0];
|
|
348
|
+
out.push("");
|
|
349
|
+
renderTable(parsePipeTable(lines.slice(i, end))).forEach((l) => out.push(l ? indent + l : ""));
|
|
350
|
+
out.push("");
|
|
351
|
+
i = end;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const pre = line.match(/^(\s*)<pre(?:\s+lang="([^"]*)")?>/);
|
|
355
|
+
if (pre) {
|
|
356
|
+
let end = i;
|
|
357
|
+
while (end < lines.length && lines[end].indexOf("</pre>") === -1) end++;
|
|
358
|
+
const raw = lines.slice(i, Math.min(end + 1, lines.length)).join("\n");
|
|
359
|
+
const code = preText(raw.replace(/^\s*<pre[^>]*>/, "").replace(/<\/pre>[\s\S]*$/, ""))
|
|
360
|
+
.split(/<br\s*\/?>|\n/)
|
|
361
|
+
.map((l) => l.replace(/\s+$/, ""));
|
|
362
|
+
out.push("", pre[1] + "```" + (pre[2] || ""));
|
|
363
|
+
code.forEach((l) => out.push(l ? pre[1] + l : ""));
|
|
364
|
+
out.push(pre[1] + "```", "");
|
|
365
|
+
i = end + 1;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const adm = line.match(ADMONITION);
|
|
369
|
+
if (adm) {
|
|
370
|
+
const indent = adm[1].length;
|
|
371
|
+
let end = i + 1;
|
|
372
|
+
while (end < lines.length && (!lines[end].trim() || lines[end].match(/^ */)[0].length >= indent + 4)) end++;
|
|
373
|
+
while (end > i + 1 && !lines[end - 1].trim()) end--;
|
|
374
|
+
const body = trimBlankEdges(convertLines(dedent(lines.slice(i + 1, end)), warnings));
|
|
375
|
+
const kind = adm[3].charAt(0).toUpperCase() + adm[3].slice(1).toLowerCase();
|
|
376
|
+
const label = "**" + (adm[4] ? adm[4] : kind) + ":**";
|
|
377
|
+
if (body.length && isParagraphLine(body[0])) body[0] = label + " " + body[0];
|
|
378
|
+
else body.unshift(label);
|
|
379
|
+
out.push("");
|
|
380
|
+
body.forEach((l) => out.push(adm[1] + (l ? "> " + l : ">")));
|
|
381
|
+
out.push("");
|
|
382
|
+
i = end;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (RULE.test(line)) {
|
|
386
|
+
out.push("");
|
|
387
|
+
i++;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
out.push(
|
|
391
|
+
line
|
|
392
|
+
.replace(/!\[([^\]]*)\]\([^)]*\)/g, (m, alt) => "(Screenshot: " + (alt || "image") + ")")
|
|
393
|
+
.replace(/<br\s*\/?>/g, " ")
|
|
394
|
+
.replace(/\s+$/, "")
|
|
395
|
+
);
|
|
396
|
+
i++;
|
|
397
|
+
}
|
|
398
|
+
return out;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** Collapses runs of blank lines (outside code) to one. */
|
|
402
|
+
function tidy(lines) {
|
|
403
|
+
const out = [];
|
|
404
|
+
let inFence = false;
|
|
405
|
+
lines.forEach((line) => {
|
|
406
|
+
if (/^\s*(`{3,}|~{3,})/.test(line)) inFence = !inFence;
|
|
407
|
+
if (!inFence && !line.trim() && (!out.length || !out[out.length - 1].trim())) return;
|
|
408
|
+
out.push(line);
|
|
409
|
+
});
|
|
410
|
+
return trimBlankEdges(out);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Returns { lines, warnings } for one source page. */
|
|
414
|
+
function convertPage(markdown) {
|
|
415
|
+
const warnings = [];
|
|
416
|
+
const lines = String(markdown).replace(/\r\n?/g, "\n").split("\n");
|
|
417
|
+
return { lines: tidy(convertLines(lines, warnings)), warnings };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
module.exports = {
|
|
421
|
+
convertPage,
|
|
422
|
+
parseGridTable,
|
|
423
|
+
parsePipeTable,
|
|
424
|
+
renderTable,
|
|
425
|
+
slugify,
|
|
426
|
+
stripInline,
|
|
427
|
+
uniqueSlugger,
|
|
428
|
+
fenceEnd,
|
|
429
|
+
isFenceOpen,
|
|
430
|
+
};
|