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.
Files changed (111) hide show
  1. package/README.md +8 -288
  2. package/dist/agents/implementors/handler.js +1 -1
  3. package/dist/agents/implementors/handler.js.map +1 -1
  4. package/dist/agents/planners/adapter.d.ts.map +1 -1
  5. package/dist/agents/planners/adapter.js +1 -0
  6. package/dist/agents/planners/adapter.js.map +1 -1
  7. package/dist/agents/planners/handler.js +1 -1
  8. package/dist/agents/planners/handler.js.map +1 -1
  9. package/dist/agents/reviewers/handler.d.ts.map +1 -1
  10. package/dist/agents/reviewers/handler.js +1 -0
  11. package/dist/agents/reviewers/handler.js.map +1 -1
  12. package/dist/agents/reviewers/prompts.d.ts.map +1 -1
  13. package/dist/agents/reviewers/prompts.js +1 -0
  14. package/dist/agents/reviewers/prompts.js.map +1 -1
  15. package/dist/ansi.d.ts +1 -0
  16. package/dist/ansi.d.ts.map +1 -1
  17. package/dist/ansi.js +15 -0
  18. package/dist/ansi.js.map +1 -1
  19. package/dist/cli.js +20 -16
  20. package/dist/cli.js.map +1 -1
  21. package/dist/dump/types.d.ts +2 -1
  22. package/dist/dump/types.d.ts.map +1 -1
  23. package/dist/flow/executor.d.ts.map +1 -1
  24. package/dist/flow/executor.js +1 -0
  25. package/dist/flow/executor.js.map +1 -1
  26. package/dist/flow-editor/tui.d.ts +2 -2
  27. package/dist/flow-editor/tui.d.ts.map +1 -1
  28. package/dist/flow-editor/tui.js +42 -34
  29. package/dist/flow-editor/tui.js.map +1 -1
  30. package/dist/markdown.d.ts +12 -21
  31. package/dist/markdown.d.ts.map +1 -1
  32. package/dist/markdown.js +519 -150
  33. package/dist/markdown.js.map +1 -1
  34. package/dist/orchestrator.d.ts.map +1 -1
  35. package/dist/orchestrator.js +0 -17
  36. package/dist/orchestrator.js.map +1 -1
  37. package/dist/preflight.d.ts +0 -11
  38. package/dist/preflight.d.ts.map +1 -1
  39. package/dist/preflight.js +2 -29
  40. package/dist/preflight.js.map +1 -1
  41. package/dist/preview-panel.d.ts +7 -6
  42. package/dist/preview-panel.d.ts.map +1 -1
  43. package/dist/preview-panel.js +69 -108
  44. package/dist/preview-panel.js.map +1 -1
  45. package/dist/providers/claude/completion-plan-mode.d.ts +0 -1
  46. package/dist/providers/claude/completion-plan-mode.d.ts.map +1 -1
  47. package/dist/providers/claude/completion-plan-mode.js +0 -23
  48. package/dist/providers/claude/completion-plan-mode.js.map +1 -1
  49. package/dist/providers/codex/session.d.ts.map +1 -1
  50. package/dist/providers/codex/session.js +1 -0
  51. package/dist/providers/codex/session.js.map +1 -1
  52. package/dist/resume-tui.d.ts +2 -2
  53. package/dist/resume-tui.d.ts.map +1 -1
  54. package/dist/resume-tui.js +13 -13
  55. package/dist/resume-tui.js.map +1 -1
  56. package/dist/session/manifest.d.ts +3 -1
  57. package/dist/session/manifest.d.ts.map +1 -1
  58. package/dist/session/restore.d.ts +3 -2
  59. package/dist/session/restore.d.ts.map +1 -1
  60. package/dist/session/restore.js +6 -2
  61. package/dist/session/restore.js.map +1 -1
  62. package/dist/session/resume.d.ts.map +1 -1
  63. package/dist/session/resume.js +0 -22
  64. package/dist/session/resume.js.map +1 -1
  65. package/dist/session/session-store.d.ts.map +1 -1
  66. package/dist/session/session-store.js +1 -0
  67. package/dist/session/session-store.js.map +1 -1
  68. package/dist/startup-tui.d.ts +4 -45
  69. package/dist/startup-tui.d.ts.map +1 -1
  70. package/dist/startup-tui.js +21 -83
  71. package/dist/startup-tui.js.map +1 -1
  72. package/dist/terminal/win32-key-translator.d.ts.map +1 -1
  73. package/dist/terminal/win32-key-translator.js +16 -0
  74. package/dist/terminal/win32-key-translator.js.map +1 -1
  75. package/dist/terminal-input.d.ts +3 -3
  76. package/dist/terminal-input.d.ts.map +1 -1
  77. package/dist/terminal-input.js +22 -6
  78. package/dist/terminal-input.js.map +1 -1
  79. package/dist/text-editing/emacs-input.d.ts +84 -0
  80. package/dist/text-editing/emacs-input.d.ts.map +1 -0
  81. package/dist/text-editing/emacs-input.js +233 -0
  82. package/dist/text-editing/emacs-input.js.map +1 -0
  83. package/dist/text-editing/readline-ops.d.ts +17 -0
  84. package/dist/text-editing/readline-ops.d.ts.map +1 -0
  85. package/dist/text-editing/readline-ops.js +172 -0
  86. package/dist/text-editing/readline-ops.js.map +1 -0
  87. package/dist/user/handler.d.ts +3 -3
  88. package/dist/user/handler.d.ts.map +1 -1
  89. package/dist/user/handler.js +11 -3
  90. package/dist/user/handler.js.map +1 -1
  91. package/dist/user/host-curate-prompt.d.ts.map +1 -1
  92. package/dist/user/host-curate-prompt.js +59 -29
  93. package/dist/user/host-curate-prompt.js.map +1 -1
  94. package/dist/user/host-prompt.d.ts.map +1 -1
  95. package/dist/user/host-prompt.js +64 -40
  96. package/dist/user/host-prompt.js.map +1 -1
  97. package/dist/user/index.d.ts +2 -1
  98. package/dist/user/index.d.ts.map +1 -1
  99. package/dist/user/index.js +1 -1
  100. package/dist/user/index.js.map +1 -1
  101. package/dist/user/plan-pane.d.ts +3 -25
  102. package/dist/user/plan-pane.d.ts.map +1 -1
  103. package/dist/user/plan-pane.js +13 -153
  104. package/dist/user/plan-pane.js.map +1 -1
  105. package/dist/user/prompt.d.ts +2 -49
  106. package/dist/user/prompt.d.ts.map +1 -1
  107. package/dist/user/prompt.js +1 -88
  108. package/dist/user/prompt.js.map +1 -1
  109. package/docs/startup-tui.png +0 -0
  110. package/docs/windows-terminal.md +15 -0
  111. 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 (autolinks, image
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 the table will overflow, but its internal structure stays
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
- * Render markdown to an array of ANSI-styled lines fit to `width`.
52
- *
53
- * - Builds a per-call `Marked` instance so the `width` option does not leak
54
- * between callers (plan pane vs. issue description use different widths).
55
- * - Resolves common fenced-code language aliases via `LANGUAGE_ALIASES` in a
56
- * `walkTokens` hook, rewriting code token `lang` fields before
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 m = new Marked();
76
- m.use(markedTerminal({ width: w, reflowText: true, tab: 2 }));
77
- m.use({
78
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- m.use({
92
- renderer: {
93
- link({ href, tokens }) {
94
- // `this.parser.parseInline(tokens)` re-renders the link's child tokens
95
- // with the active renderer chain (so any inline emphasis/code keeps
96
- // its styling) but as plain SGR, not OSC 8.
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
- raw = m.parse(md);
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
- const sanitized = raw.replace(OSC8_RE, "");
212
- const split = sanitized.split(/\r?\n/);
213
- while (split.length > 0 && split[split.length - 1] === "")
214
- split.pop();
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 line of split) {
217
- for (const wrapped of hardWrapLine(line, w))
218
- out.push(wrapped);
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