poly-weaver 0.9.1 → 0.9.3
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 +8 -288
- package/dist/agents/implementors/handler.js +1 -1
- package/dist/agents/implementors/handler.js.map +1 -1
- package/dist/agents/planners/adapter.d.ts.map +1 -1
- package/dist/agents/planners/adapter.js +1 -0
- package/dist/agents/planners/adapter.js.map +1 -1
- package/dist/agents/planners/handler.js +1 -1
- package/dist/agents/planners/handler.js.map +1 -1
- package/dist/agents/reviewers/handler.d.ts.map +1 -1
- package/dist/agents/reviewers/handler.js +1 -0
- package/dist/agents/reviewers/handler.js.map +1 -1
- package/dist/agents/reviewers/prompts.d.ts.map +1 -1
- package/dist/agents/reviewers/prompts.js +1 -0
- package/dist/agents/reviewers/prompts.js.map +1 -1
- package/dist/ansi.d.ts +1 -0
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +15 -0
- package/dist/ansi.js.map +1 -1
- package/dist/cli.js +20 -16
- package/dist/cli.js.map +1 -1
- package/dist/dump/types.d.ts +2 -1
- package/dist/dump/types.d.ts.map +1 -1
- package/dist/flow/executor.d.ts.map +1 -1
- package/dist/flow/executor.js +1 -0
- package/dist/flow/executor.js.map +1 -1
- package/dist/flow-editor/tui.d.ts +2 -2
- package/dist/flow-editor/tui.d.ts.map +1 -1
- package/dist/flow-editor/tui.js +42 -34
- package/dist/flow-editor/tui.js.map +1 -1
- package/dist/markdown.d.ts +12 -21
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +519 -150
- package/dist/markdown.js.map +1 -1
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +0 -17
- package/dist/orchestrator.js.map +1 -1
- package/dist/preflight.d.ts +0 -11
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +2 -29
- package/dist/preflight.js.map +1 -1
- package/dist/preview-panel.d.ts +7 -6
- package/dist/preview-panel.d.ts.map +1 -1
- package/dist/preview-panel.js +69 -108
- package/dist/preview-panel.js.map +1 -1
- package/dist/providers/claude/completion-plan-mode.d.ts +0 -1
- package/dist/providers/claude/completion-plan-mode.d.ts.map +1 -1
- package/dist/providers/claude/completion-plan-mode.js +0 -23
- package/dist/providers/claude/completion-plan-mode.js.map +1 -1
- package/dist/providers/codex/session.d.ts.map +1 -1
- package/dist/providers/codex/session.js +1 -0
- package/dist/providers/codex/session.js.map +1 -1
- package/dist/resume-tui.d.ts +2 -2
- package/dist/resume-tui.d.ts.map +1 -1
- package/dist/resume-tui.js +13 -13
- package/dist/resume-tui.js.map +1 -1
- package/dist/session/manifest.d.ts +3 -1
- package/dist/session/manifest.d.ts.map +1 -1
- package/dist/session/restore.d.ts +3 -2
- package/dist/session/restore.d.ts.map +1 -1
- package/dist/session/restore.js +6 -2
- package/dist/session/restore.js.map +1 -1
- package/dist/session/resume.d.ts.map +1 -1
- package/dist/session/resume.js +0 -22
- package/dist/session/resume.js.map +1 -1
- package/dist/session/session-store.d.ts.map +1 -1
- package/dist/session/session-store.js +1 -0
- package/dist/session/session-store.js.map +1 -1
- package/dist/startup-tui.d.ts +4 -45
- package/dist/startup-tui.d.ts.map +1 -1
- package/dist/startup-tui.js +21 -83
- package/dist/startup-tui.js.map +1 -1
- package/dist/terminal/win32-key-translator.d.ts.map +1 -1
- package/dist/terminal/win32-key-translator.js +16 -0
- package/dist/terminal/win32-key-translator.js.map +1 -1
- package/dist/terminal-input.d.ts +3 -3
- package/dist/terminal-input.d.ts.map +1 -1
- package/dist/terminal-input.js +22 -6
- package/dist/terminal-input.js.map +1 -1
- package/dist/text-editing/emacs-input.d.ts +84 -0
- package/dist/text-editing/emacs-input.d.ts.map +1 -0
- package/dist/text-editing/emacs-input.js +233 -0
- package/dist/text-editing/emacs-input.js.map +1 -0
- package/dist/text-editing/readline-ops.d.ts +17 -0
- package/dist/text-editing/readline-ops.d.ts.map +1 -0
- package/dist/text-editing/readline-ops.js +172 -0
- package/dist/text-editing/readline-ops.js.map +1 -0
- package/dist/user/handler.d.ts +3 -3
- package/dist/user/handler.d.ts.map +1 -1
- package/dist/user/handler.js +11 -3
- package/dist/user/handler.js.map +1 -1
- package/dist/user/host-curate-prompt.d.ts.map +1 -1
- package/dist/user/host-curate-prompt.js +59 -29
- package/dist/user/host-curate-prompt.js.map +1 -1
- package/dist/user/host-prompt.d.ts.map +1 -1
- package/dist/user/host-prompt.js +64 -40
- package/dist/user/host-prompt.js.map +1 -1
- package/dist/user/index.d.ts +2 -1
- package/dist/user/index.d.ts.map +1 -1
- package/dist/user/index.js +1 -1
- package/dist/user/index.js.map +1 -1
- package/dist/user/plan-pane.d.ts +3 -25
- package/dist/user/plan-pane.d.ts.map +1 -1
- package/dist/user/plan-pane.js +13 -153
- package/dist/user/plan-pane.js.map +1 -1
- package/dist/user/prompt.d.ts +2 -49
- package/dist/user/prompt.d.ts.map +1 -1
- package/dist/user/prompt.js +1 -88
- package/dist/user/prompt.js.map +1 -1
- package/docs/startup-tui.png +0 -0
- package/docs/windows-terminal.md +15 -0
- package/package.json +5 -3
package/dist/markdown.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Marked } from "marked";
|
|
2
|
-
import { markedTerminal } from "marked-terminal";
|
|
3
|
-
import Table from "cli-table3";
|
|
4
1
|
import chalk from "chalk";
|
|
2
|
+
import { highlight, supportsLanguage } from "cli-highlight";
|
|
3
|
+
import Table from "cli-table3";
|
|
4
|
+
import MarkdownIt from "markdown-it";
|
|
5
|
+
import { visibleLength } from "./ansi.js";
|
|
5
6
|
import { hardWrapLine } from "./user/hard-wrap.js";
|
|
6
7
|
// Force truecolor: the @xterm/headless host is not a TTY, so chalk autodetects
|
|
7
8
|
// `level = 0` and emits no SGR. The host renders ANSI through its own pipeline,
|
|
@@ -10,9 +11,7 @@ chalk.level = 3;
|
|
|
10
11
|
// OSC 8 hyperlink stripper. Form:
|
|
11
12
|
// ESC ] 8 ; params ; URL (BEL or ESC \) text
|
|
12
13
|
// ESC ] 8 ; ; (BEL or ESC \)
|
|
13
|
-
// Belt-and-braces sanitizer in case any renderer path
|
|
14
|
-
// titles, table cell links, future marked-terminal changes) leaks OSC 8 past
|
|
15
|
-
// our `renderer.link` override.
|
|
14
|
+
// Belt-and-braces sanitizer in case any renderer path leaks OSC 8.
|
|
16
15
|
// eslint-disable-next-line no-control-regex
|
|
17
16
|
const OSC8_RE = /\x1b\]8;[^\x07\x1b]*(?:\x07|\x1b\\)/g;
|
|
18
17
|
const LANGUAGE_ALIASES = {
|
|
@@ -29,7 +28,7 @@ const LANGUAGE_ALIASES = {
|
|
|
29
28
|
* `sum(colWidths) + (cols + 1)` to account for the inter-column and outer
|
|
30
29
|
* borders. Each column needs at least 3 cols (1 padding + 1 char + 1 padding),
|
|
31
30
|
* so when the requested width is too narrow for the column count we fall back
|
|
32
|
-
* to the minimum
|
|
31
|
+
* to the minimum - the table will overflow, but its internal structure stays
|
|
33
32
|
* consistent (the outer hard-wrap will still slice it).
|
|
34
33
|
*/
|
|
35
34
|
function tableColWidths(cols, width) {
|
|
@@ -47,159 +46,44 @@ function tableColWidths(cols, width) {
|
|
|
47
46
|
}
|
|
48
47
|
return widths;
|
|
49
48
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
* marked-terminal's renderer runs.
|
|
58
|
-
* - Suppresses OSC 8 hyperlinks two ways (defense-in-depth):
|
|
59
|
-
* 1. Override `renderer.link` AFTER applying the marked-terminal
|
|
60
|
-
* extension. Marked's `use({ renderer })` is last-wins, so we replace
|
|
61
|
-
* the link renderer with a pure-SGR implementation. The `link` option
|
|
62
|
-
* on `markedTerminal(...)` is styling-only and runs after OSC 8 has
|
|
63
|
-
* already wrapped the text — it cannot suppress OSC 8 by itself.
|
|
64
|
-
* 2. Strip any residual OSC 8 escapes from the final string.
|
|
65
|
-
* - Drops stray stdout/stderr writes during marked's parse pass so renderer
|
|
66
|
-
* library warnings cannot overlay the host's alternate-screen frame.
|
|
67
|
-
* - Defensively hard-wraps each output line so an over-long token (e.g.
|
|
68
|
-
* inline code with a long URL) cannot break layout.
|
|
69
|
-
* - Strips the trailing blank line that marked emits after a final paragraph.
|
|
70
|
-
*/
|
|
71
|
-
export function renderMarkdown(md, width) {
|
|
49
|
+
export function renderMarkdown(md, width, options) {
|
|
50
|
+
const rows = renderMarkdownRows(md, width);
|
|
51
|
+
if (options?.withSources)
|
|
52
|
+
return rows;
|
|
53
|
+
return rows.map((row) => row.line);
|
|
54
|
+
}
|
|
55
|
+
function renderMarkdownRows(md, width) {
|
|
72
56
|
if (md.length === 0)
|
|
73
57
|
return [];
|
|
74
58
|
const w = Math.max(1, width);
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
walkTokens(token) {
|
|
80
|
-
if (token.type !== "code")
|
|
81
|
-
return;
|
|
82
|
-
const lang = typeof token.lang === "string" ? token.lang.trim() : "";
|
|
83
|
-
if (lang === "")
|
|
84
|
-
return;
|
|
85
|
-
const firstWord = lang.match(/^\S+/)?.[0].toLowerCase() ?? "";
|
|
86
|
-
const resolved = LANGUAGE_ALIASES[firstWord];
|
|
87
|
-
if (resolved)
|
|
88
|
-
token.lang = resolved;
|
|
89
|
-
},
|
|
59
|
+
const parser = new MarkdownIt({
|
|
60
|
+
html: false,
|
|
61
|
+
linkify: false,
|
|
62
|
+
typographer: false,
|
|
90
63
|
});
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
-
const text = this.parser.parseInline(tokens);
|
|
99
|
-
return chalk.cyan.underline(text) + " " + chalk.dim(`(${href})`);
|
|
100
|
-
},
|
|
101
|
-
// Override marked-terminal's table renderer. The upstream version passes
|
|
102
|
-
// no width hint to cli-table3, which then auto-sizes columns to the
|
|
103
|
-
// longest cell — long cells produce tables wider than the pane, and our
|
|
104
|
-
// outer hard-wrap slices each row mid-border (orphaned `│` + trailing
|
|
105
|
-
// `─`s on the next visual line). Compute `colWidths` to fit `w` and let
|
|
106
|
-
// cli-table3's `wordWrap` handle long content cleanly.
|
|
107
|
-
table({ header, rows,
|
|
108
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
109
|
-
}) {
|
|
110
|
-
const cols = header.length;
|
|
111
|
-
if (cols === 0)
|
|
112
|
-
return "";
|
|
113
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
114
|
-
const parser = this.parser;
|
|
115
|
-
const head = header.map((c) => parser.parseInline(c.tokens));
|
|
116
|
-
const body = rows.map((row) => row.map((c) => parser.parseInline(c.tokens)));
|
|
117
|
-
const t = new Table({
|
|
118
|
-
head,
|
|
119
|
-
colWidths: tableColWidths(cols, w),
|
|
120
|
-
wordWrap: true,
|
|
121
|
-
wrapOnWordBoundary: false,
|
|
122
|
-
});
|
|
123
|
-
for (const row of body)
|
|
124
|
-
t.push(row);
|
|
125
|
-
return t.toString() + "\n\n";
|
|
126
|
-
},
|
|
127
|
-
list(token) {
|
|
128
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
129
|
-
const parser = this.parser;
|
|
130
|
-
const startNum = typeof token.start === "number" ? token.start : 1;
|
|
131
|
-
let out = "";
|
|
132
|
-
for (let i = 0; i < token.items.length; i++) {
|
|
133
|
-
const item = token.items[i];
|
|
134
|
-
let prefix;
|
|
135
|
-
let prefixWidth;
|
|
136
|
-
if (item.task) {
|
|
137
|
-
prefix = (item.checked ? chalk.green("☒") : chalk.dim("☐")) + " ";
|
|
138
|
-
prefixWidth = 2;
|
|
139
|
-
}
|
|
140
|
-
else if (token.ordered) {
|
|
141
|
-
const raw = `${startNum + i}. `;
|
|
142
|
-
prefix = chalk.dim(raw);
|
|
143
|
-
prefixWidth = raw.length;
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
prefix = chalk.dim("•") + " ";
|
|
147
|
-
prefixWidth = 2;
|
|
148
|
-
}
|
|
149
|
-
let body = parser.parse(item.tokens, !!item.loose);
|
|
150
|
-
body = body.replace(/\n+$/, "");
|
|
151
|
-
const indent = " ".repeat(prefixWidth);
|
|
152
|
-
const lines = body.split("\n");
|
|
153
|
-
out += prefix + lines[0] + "\n";
|
|
154
|
-
for (let k = 1; k < lines.length; k++) {
|
|
155
|
-
out += indent + lines[k] + "\n";
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return out + "\n";
|
|
159
|
-
},
|
|
160
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
161
|
-
listitem(item) {
|
|
162
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
163
|
-
return this.parser.parse(item.tokens, !!item.loose);
|
|
164
|
-
},
|
|
165
|
-
// marked-terminal's `text` renderer returns `token.text` raw, dropping
|
|
166
|
-
// inline children. Restore marked v15's default behavior of dispatching
|
|
167
|
-
// `token.tokens` through `parseInline` so inline markup (links, code,
|
|
168
|
-
// bold, italic) inside tight list items is styled by our renderer chain.
|
|
169
|
-
//
|
|
170
|
-
// Also preserve a trailing newline from `token.raw`: in marked v15, the
|
|
171
|
-
// lexer keeps a trailing `\n` on a text token's raw when a block element
|
|
172
|
-
// follows (e.g. a nested list under a tight item). Block-level
|
|
173
|
-
// `parser.parse(tokens, false)` concatenates token outputs without
|
|
174
|
-
// inserting separators, so without that newline our list item body
|
|
175
|
-
// would glue "parent" directly to the nested "• child".
|
|
176
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
177
|
-
text(token) {
|
|
178
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
179
|
-
const out = token.tokens
|
|
180
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
181
|
-
? this.parser.parseInline(token.tokens)
|
|
182
|
-
: token.text;
|
|
183
|
-
return typeof token.raw === "string" && token.raw.endsWith("\n")
|
|
184
|
-
? out + "\n"
|
|
185
|
-
: out;
|
|
186
|
-
},
|
|
187
|
-
},
|
|
64
|
+
const rows = silenceRendererOutput(() => {
|
|
65
|
+
const tokens = parser.parse(md, {});
|
|
66
|
+
return renderBlocks(tokens, 0, tokens.length, {
|
|
67
|
+
width: w,
|
|
68
|
+
topLevel: true,
|
|
69
|
+
});
|
|
188
70
|
});
|
|
71
|
+
return finalizeRows(rows, w);
|
|
72
|
+
}
|
|
73
|
+
function silenceRendererOutput(fn) {
|
|
189
74
|
const originalConsoleLog = console.log;
|
|
190
75
|
const originalConsoleError = console.error;
|
|
191
76
|
const originalConsoleWarn = console.warn;
|
|
192
77
|
const originalStdoutWrite = process.stdout.write;
|
|
193
78
|
const originalStderrWrite = process.stderr.write;
|
|
194
79
|
const noopWrite = (() => true);
|
|
195
|
-
let raw;
|
|
196
80
|
try {
|
|
197
81
|
console.log = () => { };
|
|
198
82
|
console.error = () => { };
|
|
199
83
|
console.warn = () => { };
|
|
200
84
|
process.stdout.write = noopWrite;
|
|
201
85
|
process.stderr.write = noopWrite;
|
|
202
|
-
|
|
86
|
+
return fn();
|
|
203
87
|
}
|
|
204
88
|
finally {
|
|
205
89
|
console.log = originalConsoleLog;
|
|
@@ -208,15 +92,500 @@ export function renderMarkdown(md, width) {
|
|
|
208
92
|
process.stdout.write = originalStdoutWrite;
|
|
209
93
|
process.stderr.write = originalStderrWrite;
|
|
210
94
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
95
|
+
}
|
|
96
|
+
function renderBlocks(tokens, start, end, ctx) {
|
|
97
|
+
const rows = [];
|
|
98
|
+
let index = start;
|
|
99
|
+
let emittedBlock = false;
|
|
100
|
+
while (index < end) {
|
|
101
|
+
const result = renderBlock(tokens, index, end, ctx);
|
|
102
|
+
if (result.next <= index) {
|
|
103
|
+
index++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
index = result.next;
|
|
107
|
+
if (result.rows.length === 0)
|
|
108
|
+
continue;
|
|
109
|
+
if (ctx.topLevel && emittedBlock) {
|
|
110
|
+
rows.push({ line: "", source: undefined });
|
|
111
|
+
}
|
|
112
|
+
rows.push(...result.rows);
|
|
113
|
+
emittedBlock = true;
|
|
114
|
+
}
|
|
115
|
+
return rows;
|
|
116
|
+
}
|
|
117
|
+
function renderBlock(tokens, index, end, ctx) {
|
|
118
|
+
const token = tokens[index];
|
|
119
|
+
switch (token.type) {
|
|
120
|
+
case "heading_open":
|
|
121
|
+
return renderHeading(tokens, index, ctx);
|
|
122
|
+
case "paragraph_open":
|
|
123
|
+
return renderParagraph(tokens, index, ctx);
|
|
124
|
+
case "hr":
|
|
125
|
+
return {
|
|
126
|
+
rows: [{ line: chalk.dim("─".repeat(ctx.width)), source: sourceFromMap(token.map) }],
|
|
127
|
+
next: index + 1,
|
|
128
|
+
};
|
|
129
|
+
case "fence":
|
|
130
|
+
return {
|
|
131
|
+
rows: renderFence(token),
|
|
132
|
+
next: index + 1,
|
|
133
|
+
};
|
|
134
|
+
case "code_block":
|
|
135
|
+
return {
|
|
136
|
+
rows: renderIndentedCode(token),
|
|
137
|
+
next: index + 1,
|
|
138
|
+
};
|
|
139
|
+
case "blockquote_open":
|
|
140
|
+
return renderBlockquote(tokens, index, ctx);
|
|
141
|
+
case "bullet_list_open":
|
|
142
|
+
case "ordered_list_open":
|
|
143
|
+
return renderList(tokens, index, ctx);
|
|
144
|
+
case "table_open":
|
|
145
|
+
return renderTable(tokens, index, ctx);
|
|
146
|
+
case "html_block":
|
|
147
|
+
return renderHtmlBlock(token, index);
|
|
148
|
+
default:
|
|
149
|
+
return { rows: [], next: index + 1 };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function renderHeading(tokens, index, ctx) {
|
|
153
|
+
const open = tokens[index];
|
|
154
|
+
const close = findMatchingClose(tokens, index);
|
|
155
|
+
const inline = tokens[index + 1]?.type === "inline" ? tokens[index + 1] : undefined;
|
|
156
|
+
const text = inline ? renderInline(inline.children ?? []) : "";
|
|
157
|
+
const level = Number.parseInt(open.tag.slice(1), 10);
|
|
158
|
+
const heading = `${"#".repeat(level)} ${text}`;
|
|
159
|
+
const styled = level === 1
|
|
160
|
+
? chalk.magenta.underline.bold(heading)
|
|
161
|
+
: chalk.green.bold(heading);
|
|
162
|
+
return {
|
|
163
|
+
rows: wrapStyledText(styled, ctx.width).map((line) => ({
|
|
164
|
+
line,
|
|
165
|
+
source: sourceFromMap(open.map),
|
|
166
|
+
})),
|
|
167
|
+
next: close + 1,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function renderParagraph(tokens, index, ctx) {
|
|
171
|
+
const open = tokens[index];
|
|
172
|
+
const close = findMatchingClose(tokens, index);
|
|
173
|
+
const parts = [];
|
|
174
|
+
for (let i = index + 1; i < close; i++) {
|
|
175
|
+
const token = tokens[i];
|
|
176
|
+
if (token.type === "inline") {
|
|
177
|
+
parts.push(renderInline(token.children ?? []));
|
|
178
|
+
}
|
|
179
|
+
else if (token.content) {
|
|
180
|
+
parts.push(token.content);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const source = sourceFromMap(open.map);
|
|
184
|
+
return {
|
|
185
|
+
rows: wrapStyledText(parts.join(""), ctx.width).map((line) => ({
|
|
186
|
+
line,
|
|
187
|
+
source,
|
|
188
|
+
})),
|
|
189
|
+
next: close + 1,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function renderFence(token) {
|
|
193
|
+
if (token.content.length === 0)
|
|
194
|
+
return [];
|
|
195
|
+
const highlighted = highlightCode(token.content, token.info);
|
|
196
|
+
const withoutFinalNewline = highlighted.endsWith("\n")
|
|
197
|
+
? highlighted.slice(0, -1)
|
|
198
|
+
: highlighted;
|
|
199
|
+
const lines = withoutFinalNewline.split("\n");
|
|
200
|
+
const rows = [];
|
|
201
|
+
const mapStart = token.map?.[0];
|
|
202
|
+
for (let i = 0; i < lines.length; i++) {
|
|
203
|
+
const sourceLine = mapStart === undefined ? undefined : mapStart + 2 + i;
|
|
204
|
+
rows.push({
|
|
205
|
+
line: ` ${lines[i]}`,
|
|
206
|
+
source: sourceLine === undefined
|
|
207
|
+
? undefined
|
|
208
|
+
: { start: sourceLine, end: sourceLine },
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return rows;
|
|
212
|
+
}
|
|
213
|
+
function renderIndentedCode(token) {
|
|
214
|
+
if (token.content.length === 0)
|
|
215
|
+
return [];
|
|
216
|
+
const withoutFinalNewline = token.content.endsWith("\n")
|
|
217
|
+
? token.content.slice(0, -1)
|
|
218
|
+
: token.content;
|
|
219
|
+
const lines = withoutFinalNewline.split("\n");
|
|
220
|
+
const rows = [];
|
|
221
|
+
const mapStart = token.map?.[0];
|
|
222
|
+
for (let i = 0; i < lines.length; i++) {
|
|
223
|
+
const sourceLine = mapStart === undefined ? undefined : mapStart + 1 + i;
|
|
224
|
+
rows.push({
|
|
225
|
+
line: ` ${lines[i]}`,
|
|
226
|
+
source: sourceLine === undefined
|
|
227
|
+
? undefined
|
|
228
|
+
: { start: sourceLine, end: sourceLine },
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return rows;
|
|
232
|
+
}
|
|
233
|
+
function renderBlockquote(tokens, index, ctx) {
|
|
234
|
+
const open = tokens[index];
|
|
235
|
+
const close = findMatchingClose(tokens, index);
|
|
236
|
+
const prefix = `${chalk.dim("│")} `;
|
|
237
|
+
const prefixWidth = visibleLength(prefix);
|
|
238
|
+
const source = sourceFromMap(open.map);
|
|
239
|
+
const innerRows = renderBlocks(tokens, index + 1, close, {
|
|
240
|
+
width: Math.max(1, ctx.width - prefixWidth),
|
|
241
|
+
topLevel: false,
|
|
242
|
+
outerListSource: ctx.outerListSource,
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
rows: innerRows.map((row) => ({
|
|
246
|
+
line: `${prefix}${row.line}`,
|
|
247
|
+
source,
|
|
248
|
+
})),
|
|
249
|
+
next: close + 1,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function renderList(tokens, index, ctx) {
|
|
253
|
+
const open = tokens[index];
|
|
254
|
+
const close = findMatchingClose(tokens, index);
|
|
255
|
+
const source = ctx.outerListSource ?? sourceFromMap(open.map);
|
|
256
|
+
const rows = [];
|
|
257
|
+
const loose = isLooseList(tokens, index + 1, close);
|
|
258
|
+
let itemIndex = index + 1;
|
|
259
|
+
let ordinal = orderedListStart(open);
|
|
260
|
+
let renderedItems = 0;
|
|
261
|
+
while (itemIndex < close) {
|
|
262
|
+
const itemOpen = tokens[itemIndex];
|
|
263
|
+
if (itemOpen.type !== "list_item_open") {
|
|
264
|
+
itemIndex++;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const itemClose = findMatchingClose(tokens, itemIndex);
|
|
268
|
+
const childTokens = tokens
|
|
269
|
+
.slice(itemIndex + 1, itemClose)
|
|
270
|
+
.map((token) => cloneToken(token));
|
|
271
|
+
const task = stripLeadingTaskMarker(childTokens);
|
|
272
|
+
const marker = listMarker(open, ordinal, task);
|
|
273
|
+
const markerWidth = visibleLength(marker);
|
|
274
|
+
const innerRows = renderBlocks(childTokens, 0, childTokens.length, {
|
|
275
|
+
width: Math.max(1, ctx.width - markerWidth),
|
|
276
|
+
topLevel: false,
|
|
277
|
+
outerListSource: source,
|
|
278
|
+
});
|
|
279
|
+
const itemRows = innerRows.length > 0
|
|
280
|
+
? innerRows
|
|
281
|
+
: [{ line: "", source }];
|
|
282
|
+
rows.push({
|
|
283
|
+
line: `${marker}${itemRows[0].line}`,
|
|
284
|
+
source,
|
|
285
|
+
});
|
|
286
|
+
const indent = " ".repeat(markerWidth);
|
|
287
|
+
for (let i = 1; i < itemRows.length; i++) {
|
|
288
|
+
rows.push({
|
|
289
|
+
line: `${indent}${itemRows[i].line}`,
|
|
290
|
+
source,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
itemIndex = itemClose + 1;
|
|
294
|
+
renderedItems++;
|
|
295
|
+
ordinal++;
|
|
296
|
+
if (loose && hasMoreListItems(tokens, itemIndex, close)) {
|
|
297
|
+
rows.push({ line: "", source });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
rows: renderedItems > 0 ? rows : [],
|
|
302
|
+
next: close + 1,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function renderTable(tokens, index, ctx) {
|
|
306
|
+
const open = tokens[index];
|
|
307
|
+
const close = findMatchingClose(tokens, index);
|
|
308
|
+
const source = sourceFromMap(open.map);
|
|
309
|
+
const header = [];
|
|
310
|
+
const body = [];
|
|
311
|
+
let currentRow;
|
|
312
|
+
let inHead = false;
|
|
313
|
+
let inBody = false;
|
|
314
|
+
for (let i = index + 1; i < close; i++) {
|
|
315
|
+
const token = tokens[i];
|
|
316
|
+
switch (token.type) {
|
|
317
|
+
case "thead_open":
|
|
318
|
+
inHead = true;
|
|
319
|
+
break;
|
|
320
|
+
case "thead_close":
|
|
321
|
+
inHead = false;
|
|
322
|
+
break;
|
|
323
|
+
case "tbody_open":
|
|
324
|
+
inBody = true;
|
|
325
|
+
break;
|
|
326
|
+
case "tbody_close":
|
|
327
|
+
inBody = false;
|
|
328
|
+
break;
|
|
329
|
+
case "tr_open":
|
|
330
|
+
currentRow = [];
|
|
331
|
+
break;
|
|
332
|
+
case "tr_close":
|
|
333
|
+
if (currentRow) {
|
|
334
|
+
if (inHead)
|
|
335
|
+
header.push(...currentRow);
|
|
336
|
+
else if (inBody)
|
|
337
|
+
body.push(currentRow);
|
|
338
|
+
}
|
|
339
|
+
currentRow = undefined;
|
|
340
|
+
break;
|
|
341
|
+
case "inline":
|
|
342
|
+
currentRow?.push(renderInline(token.children ?? []));
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (header.length === 0) {
|
|
347
|
+
return { rows: [], next: close + 1 };
|
|
348
|
+
}
|
|
349
|
+
const table = new Table({
|
|
350
|
+
head: header,
|
|
351
|
+
colWidths: tableColWidths(header.length, ctx.width),
|
|
352
|
+
wordWrap: true,
|
|
353
|
+
wrapOnWordBoundary: false,
|
|
354
|
+
});
|
|
355
|
+
for (const row of body)
|
|
356
|
+
table.push(row);
|
|
357
|
+
return {
|
|
358
|
+
rows: table.toString().split(/\r?\n/).map((line) => ({ line, source })),
|
|
359
|
+
next: close + 1,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function renderHtmlBlock(token, index) {
|
|
363
|
+
const source = sourceFromMap(token.map);
|
|
364
|
+
const content = token.content.endsWith("\n")
|
|
365
|
+
? token.content.slice(0, -1)
|
|
366
|
+
: token.content;
|
|
367
|
+
return {
|
|
368
|
+
rows: content.split(/\r?\n/).map((line) => ({ line, source })),
|
|
369
|
+
next: index + 1,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function renderInline(tokens) {
|
|
373
|
+
let out = "";
|
|
374
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
375
|
+
const token = tokens[i];
|
|
376
|
+
switch (token.type) {
|
|
377
|
+
case "text":
|
|
378
|
+
out += token.content;
|
|
379
|
+
break;
|
|
380
|
+
case "code_inline":
|
|
381
|
+
out += chalk.yellow(token.content);
|
|
382
|
+
break;
|
|
383
|
+
case "softbreak":
|
|
384
|
+
out += " ";
|
|
385
|
+
break;
|
|
386
|
+
case "hardbreak":
|
|
387
|
+
out += "\n";
|
|
388
|
+
break;
|
|
389
|
+
case "strong_open": {
|
|
390
|
+
const close = findInlineClose(tokens, i, "strong_open", "strong_close");
|
|
391
|
+
out += chalk.bold(renderInline(tokens.slice(i + 1, close)));
|
|
392
|
+
i = close;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
case "em_open": {
|
|
396
|
+
const close = findInlineClose(tokens, i, "em_open", "em_close");
|
|
397
|
+
out += chalk.italic(renderInline(tokens.slice(i + 1, close)));
|
|
398
|
+
i = close;
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
case "link_open": {
|
|
402
|
+
const close = findInlineClose(tokens, i, "link_open", "link_close");
|
|
403
|
+
const text = renderInline(tokens.slice(i + 1, close));
|
|
404
|
+
const href = token.attrGet("href") ?? "";
|
|
405
|
+
out += chalk.cyan.underline(text) + " " + chalk.dim(`(${href})`);
|
|
406
|
+
i = close;
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
case "image":
|
|
410
|
+
out += token.content;
|
|
411
|
+
break;
|
|
412
|
+
case "html_inline":
|
|
413
|
+
out += token.content;
|
|
414
|
+
break;
|
|
415
|
+
default:
|
|
416
|
+
if (token.children)
|
|
417
|
+
out += renderInline(token.children);
|
|
418
|
+
else
|
|
419
|
+
out += token.content;
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return out;
|
|
424
|
+
}
|
|
425
|
+
function highlightCode(code, info) {
|
|
426
|
+
const language = resolveLanguage(info);
|
|
427
|
+
if (!language || !supportsLanguage(language))
|
|
428
|
+
return code;
|
|
429
|
+
try {
|
|
430
|
+
return highlight(code, {
|
|
431
|
+
language,
|
|
432
|
+
ignoreIllegals: true,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
return code;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function resolveLanguage(info) {
|
|
440
|
+
const firstWord = info.trim().match(/^\S+/)?.[0].toLowerCase();
|
|
441
|
+
if (!firstWord)
|
|
442
|
+
return undefined;
|
|
443
|
+
return LANGUAGE_ALIASES[firstWord] ?? firstWord;
|
|
444
|
+
}
|
|
445
|
+
function listMarker(listOpen, ordinal, task) {
|
|
446
|
+
if (task)
|
|
447
|
+
return (task.checked ? chalk.green("☒") : chalk.dim("☐")) + " ";
|
|
448
|
+
if (listOpen.type === "ordered_list_open")
|
|
449
|
+
return chalk.dim(`${ordinal}. `);
|
|
450
|
+
return chalk.dim("•") + " ";
|
|
451
|
+
}
|
|
452
|
+
function orderedListStart(token) {
|
|
453
|
+
const attr = token.attrGet("start");
|
|
454
|
+
if (attr) {
|
|
455
|
+
const parsed = Number.parseInt(attr, 10);
|
|
456
|
+
if (Number.isFinite(parsed))
|
|
457
|
+
return parsed;
|
|
458
|
+
}
|
|
459
|
+
return 1;
|
|
460
|
+
}
|
|
461
|
+
function stripLeadingTaskMarker(tokens) {
|
|
462
|
+
for (const token of tokens) {
|
|
463
|
+
if (token.type !== "inline")
|
|
464
|
+
continue;
|
|
465
|
+
const match = token.content.match(/^\[([ xX])\][ \t]+/);
|
|
466
|
+
if (!match)
|
|
467
|
+
return undefined;
|
|
468
|
+
stripInlinePrefix(token, match[0].length);
|
|
469
|
+
return { checked: match[1].toLowerCase() === "x" };
|
|
470
|
+
}
|
|
471
|
+
return undefined;
|
|
472
|
+
}
|
|
473
|
+
function stripInlinePrefix(token, length) {
|
|
474
|
+
token.content = token.content.slice(length);
|
|
475
|
+
let remaining = length;
|
|
476
|
+
for (const child of token.children ?? []) {
|
|
477
|
+
if (remaining <= 0)
|
|
478
|
+
break;
|
|
479
|
+
if (child.type !== "text")
|
|
480
|
+
continue;
|
|
481
|
+
const take = Math.min(remaining, child.content.length);
|
|
482
|
+
child.content = child.content.slice(take);
|
|
483
|
+
remaining -= take;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function isLooseList(tokens, start, end) {
|
|
487
|
+
for (let i = start; i < end; i++) {
|
|
488
|
+
if (tokens[i].type === "paragraph_open" && !tokens[i].hidden)
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
function hasMoreListItems(tokens, start, end) {
|
|
494
|
+
for (let i = start; i < end; i++) {
|
|
495
|
+
if (tokens[i].type === "list_item_open")
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
function wrapStyledText(text, width) {
|
|
501
|
+
if (width < 1)
|
|
502
|
+
width = 1;
|
|
503
|
+
if (text.length === 0)
|
|
504
|
+
return [""];
|
|
215
505
|
const out = [];
|
|
216
|
-
for (const
|
|
217
|
-
|
|
218
|
-
|
|
506
|
+
for (const paragraph of text.split("\n")) {
|
|
507
|
+
const words = paragraph.split(/\s+/).filter((word) => word.length > 0);
|
|
508
|
+
if (words.length === 0) {
|
|
509
|
+
out.push("");
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
let line = "";
|
|
513
|
+
for (const word of words) {
|
|
514
|
+
const separator = line.length === 0 ? "" : " ";
|
|
515
|
+
if (visibleLength(word) > width) {
|
|
516
|
+
if (line.length > 0) {
|
|
517
|
+
out.push(line);
|
|
518
|
+
line = "";
|
|
519
|
+
}
|
|
520
|
+
const chunks = hardWrapLine(word, width);
|
|
521
|
+
out.push(...chunks.slice(0, -1));
|
|
522
|
+
line = chunks[chunks.length - 1] ?? "";
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (line.length > 0 &&
|
|
526
|
+
visibleLength(line) + 1 + visibleLength(word) > width) {
|
|
527
|
+
out.push(line);
|
|
528
|
+
line = word;
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
line += separator + word;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (line.length > 0)
|
|
535
|
+
out.push(line);
|
|
219
536
|
}
|
|
537
|
+
return out.length > 0 ? out : [""];
|
|
538
|
+
}
|
|
539
|
+
function finalizeRows(rows, width) {
|
|
540
|
+
const out = [];
|
|
541
|
+
for (const row of rows) {
|
|
542
|
+
const sanitized = row.line.replace(OSC8_RE, "");
|
|
543
|
+
for (const line of hardWrapLine(sanitized, width)) {
|
|
544
|
+
out.push({ line: line.replace(OSC8_RE, ""), source: row.source });
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
while (out.length > 0 && out[out.length - 1].line === "")
|
|
548
|
+
out.pop();
|
|
220
549
|
return out;
|
|
221
550
|
}
|
|
551
|
+
function sourceFromMap(map) {
|
|
552
|
+
if (!map)
|
|
553
|
+
return undefined;
|
|
554
|
+
return { start: map[0] + 1, end: map[1] };
|
|
555
|
+
}
|
|
556
|
+
function findMatchingClose(tokens, openIndex) {
|
|
557
|
+
const openType = tokens[openIndex].type;
|
|
558
|
+
const closeType = openType.replace(/_open$/, "_close");
|
|
559
|
+
let depth = 0;
|
|
560
|
+
for (let i = openIndex; i < tokens.length; i++) {
|
|
561
|
+
if (tokens[i].type === openType)
|
|
562
|
+
depth++;
|
|
563
|
+
else if (tokens[i].type === closeType) {
|
|
564
|
+
depth--;
|
|
565
|
+
if (depth === 0)
|
|
566
|
+
return i;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return openIndex;
|
|
570
|
+
}
|
|
571
|
+
function findInlineClose(tokens, openIndex, openType, closeType) {
|
|
572
|
+
let depth = 0;
|
|
573
|
+
for (let i = openIndex; i < tokens.length; i++) {
|
|
574
|
+
if (tokens[i].type === openType)
|
|
575
|
+
depth++;
|
|
576
|
+
else if (tokens[i].type === closeType) {
|
|
577
|
+
depth--;
|
|
578
|
+
if (depth === 0)
|
|
579
|
+
return i;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return openIndex;
|
|
583
|
+
}
|
|
584
|
+
function cloneToken(token) {
|
|
585
|
+
const clone = Object.create(Object.getPrototypeOf(token));
|
|
586
|
+
Object.assign(clone, token);
|
|
587
|
+
if (token.children)
|
|
588
|
+
clone.children = token.children.map((child) => cloneToken(child));
|
|
589
|
+
return clone;
|
|
590
|
+
}
|
|
222
591
|
//# sourceMappingURL=markdown.js.map
|