afterpresets 0.2.0 → 0.4.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 (139) hide show
  1. package/README.md +9 -6
  2. package/native-runtime/CMakeLists.txt +82 -0
  3. package/native-runtime/README.md +40 -0
  4. package/native-runtime/THIRD_PARTY_NOTICES.md +14 -0
  5. package/native-runtime/include/ap_native.h +223 -0
  6. package/native-runtime/src/apply.cpp +199 -0
  7. package/native-runtime/src/crypto.cpp +225 -0
  8. package/native-runtime/src/crypto.hpp +51 -0
  9. package/native-runtime/src/dialog.cpp +62 -0
  10. package/native-runtime/src/dialog.hpp +35 -0
  11. package/native-runtime/src/engine.cpp +909 -0
  12. package/native-runtime/src/engine.hpp +232 -0
  13. package/native-runtime/src/fsutil.cpp +291 -0
  14. package/native-runtime/src/fsutil.hpp +57 -0
  15. package/native-runtime/src/json.cpp +326 -0
  16. package/native-runtime/src/json.hpp +70 -0
  17. package/native-runtime/src/platform.hpp +83 -0
  18. package/native-runtime/src/platform_common.cpp +60 -0
  19. package/native-runtime/src/platform_mac.mm +314 -0
  20. package/native-runtime/src/platform_win.cpp +277 -0
  21. package/native-runtime/src/runtime.cpp +473 -0
  22. package/native-runtime/src/util.cpp +169 -0
  23. package/native-runtime/src/util.hpp +32 -0
  24. package/native-runtime/src/zip.cpp +243 -0
  25. package/native-runtime/src/zip.hpp +58 -0
  26. package/native-runtime/third_party/monocypher/LICENCE.md +167 -0
  27. package/native-runtime/third_party/monocypher/monocypher-ed25519.c +500 -0
  28. package/native-runtime/third_party/monocypher/monocypher-ed25519.h +140 -0
  29. package/native-runtime/third_party/monocypher/monocypher.c +2956 -0
  30. package/native-runtime/third_party/monocypher/monocypher.h +321 -0
  31. package/native-runtime/third_party/tinf/LICENSE +22 -0
  32. package/native-runtime/third_party/tinf/crc32.c +57 -0
  33. package/native-runtime/third_party/tinf/tinf.h +140 -0
  34. package/native-runtime/third_party/tinf/tinflate.c +636 -0
  35. package/native-runtime/vendor-files.json +39 -0
  36. package/package.json +7 -4
  37. package/runtime/docs/cep/host-after-effects.md +3 -3
  38. package/runtime/docs/cep/host-premiere.md +4 -4
  39. package/runtime/docs/cep/host.md +10 -2
  40. package/runtime/docs/common/README.md +1 -1
  41. package/runtime/docs/native/README.md +44 -0
  42. package/runtime/docs/native/debugging.md +140 -0
  43. package/runtime/docs/native/rules.md +402 -0
  44. package/runtime/docs/native/testing.md +154 -0
  45. package/runtime/package.json +1 -1
  46. package/runtime/scripts/ae-sdk-docs/config.js +102 -0
  47. package/runtime/scripts/ae-sdk-docs/convert.js +430 -0
  48. package/runtime/scripts/ae-sdk-docs/index.js +314 -0
  49. package/runtime/scripts/ae-sdk-docs/source.js +205 -0
  50. package/runtime/scripts/ae-sdk-docs/split.js +170 -0
  51. package/runtime/scripts/ae-sdk-docs.js +72 -0
  52. package/runtime/src/host.js +1 -1
  53. package/src/agent/cli-group.js +9 -0
  54. package/src/agent/extendscript.js +10 -0
  55. package/src/agent/host-target.js +1 -0
  56. package/src/agent/panels.js +1 -0
  57. package/src/agent/png.js +1 -0
  58. package/src/agent/run-tool.js +3 -0
  59. package/src/{mcp → agent}/snapshot.js +2 -2
  60. package/src/agent/tool-error.js +1 -0
  61. package/src/agent/tools/capture.js +3 -0
  62. package/src/agent/tools/host.js +1 -0
  63. package/src/agent/tools/native.js +1 -0
  64. package/src/agent/tools/panel.js +7 -0
  65. package/src/agent/tools.js +2 -0
  66. package/src/api/error-messages.js +1 -1
  67. package/src/build/build.js +9 -8
  68. package/src/check/check.js +3 -3
  69. package/src/check/native.js +14 -0
  70. package/src/check/pricing.js +1 -1
  71. package/src/cli.js +3 -3
  72. package/src/commands/build.js +8 -2
  73. package/src/commands/check.js +10 -2
  74. package/src/commands/comp.js +6 -0
  75. package/src/commands/dev.js +29 -2
  76. package/src/commands/docs.js +5 -1
  77. package/src/commands/host.js +1 -0
  78. package/src/commands/init.js +12 -7
  79. package/src/commands/panel.js +1 -0
  80. package/src/commands/push.js +9 -2
  81. package/src/commands/releases.js +8 -3
  82. package/src/commands/sdk.js +14 -0
  83. package/src/commands/testers.js +1 -1
  84. package/src/commands/upgrade.js +2 -2
  85. package/src/commands/whoami.js +1 -1
  86. package/src/dev/bridge.js +8 -0
  87. package/src/dev/native.js +3 -0
  88. package/src/dev/session.js +5 -4
  89. package/src/native/build.js +27 -0
  90. package/src/native/cmake/CMakeLists.txt +88 -0
  91. package/src/native/cmake/zig-windows.cmake +19 -0
  92. package/src/native/effect.js +3 -0
  93. package/src/native/install.js +8 -0
  94. package/src/native/installers.js +4 -0
  95. package/src/native/pipl.js +20 -0
  96. package/src/native/restart.js +2 -0
  97. package/src/native/sdk.js +8 -0
  98. package/src/project/docs.js +3 -3
  99. package/src/project/project.js +2 -2
  100. package/src/project/template.js +5 -4
  101. package/src/prompt.js +1 -1
  102. package/src/release/confirm.js +5 -0
  103. package/src/release/native.js +3 -0
  104. package/src/release/publish.js +1 -1
  105. package/src/system/run-command.js +1 -1
  106. package/src/targets.js +1 -1
  107. package/template/cep/AGENTS.md +102 -164
  108. package/template/cep/README.md +7 -3
  109. package/template/cep/docs/code_guide.md +67 -0
  110. package/template/cep/docs/initial_setup.md +63 -42
  111. package/template/cep/docs/release_checklist.md +25 -0
  112. package/template/cep/gitignore +2 -1
  113. package/template/cep/src/features/update-screen/update-screen.js +1 -1
  114. package/template/hosts/AGENTS.two-apps.md +12 -0
  115. package/template/native/AGENTS.md +106 -0
  116. package/template/native/README.md +71 -0
  117. package/template/native/docs/code_guide.md +72 -0
  118. package/template/native/docs/initial_setup.md +154 -0
  119. package/template/native/docs/release_checklist.md +25 -0
  120. package/template/native/effect.json +16 -0
  121. package/template/native/gitignore +9 -0
  122. package/template/native/package.json +14 -0
  123. package/template/native/src/Effect.cpp +224 -0
  124. package/template/native/src/License.cpp +95 -0
  125. package/template/native/src/License.h +29 -0
  126. package/template/native/src/Render.h +68 -0
  127. package/src/commands/mcp.js +0 -7
  128. package/src/mcp/extendscript.js +0 -9
  129. package/src/mcp/panels.js +0 -1
  130. package/src/mcp/protocol.js +0 -4
  131. package/src/mcp/server.js +0 -2
  132. package/src/mcp/tools/account.js +0 -3
  133. package/src/mcp/tools/host.js +0 -1
  134. package/src/mcp/tools/panel.js +0 -7
  135. package/src/mcp/tools/project.js +0 -40
  136. package/src/mcp/tools/releases.js +0 -29
  137. package/template/cep/.mcp.json +0 -8
  138. /package/src/{mcp → agent}/capture.js +0 -0
  139. /package/src/{mcp → agent}/redact.js +0 -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
+ // ![alt](img.png) 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(/&lt;/g, "<")
69
+ .replace(/&gt;/g, ">")
70
+ .replace(/&quot;/g, '"')
71
+ .replace(/&#39;/g, "'")
72
+ .replace(/&nbsp;/g, " ")
73
+ .replace(/&amp;/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
+ };