toolcraft-openapi 0.0.23 → 0.0.24

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 (63) hide show
  1. package/README.md +2 -3
  2. package/dist/auth/bearer-token-auth.js +12 -3
  3. package/dist/auth/types.d.ts +1 -1
  4. package/dist/bin/generate.d.ts +1 -1
  5. package/dist/bin/generate.js +46 -21
  6. package/dist/generate.js +6 -2
  7. package/dist/http.js +29 -17
  8. package/dist/interpreter.js +12 -3
  9. package/dist/mock/fetch.js +22 -5
  10. package/dist/network-error.js +5 -3
  11. package/dist/redaction.d.ts +3 -0
  12. package/dist/redaction.js +38 -0
  13. package/node_modules/@poe-code/design-system/dist/acp/components.js +3 -1
  14. package/node_modules/@poe-code/design-system/dist/components/browser.d.ts +1 -1
  15. package/node_modules/@poe-code/design-system/dist/components/browser.js +6 -1
  16. package/node_modules/@poe-code/design-system/dist/components/color.js +9 -8
  17. package/node_modules/@poe-code/design-system/dist/components/command-errors.js +3 -2
  18. package/node_modules/@poe-code/design-system/dist/components/detail-card.d.ts +22 -0
  19. package/node_modules/@poe-code/design-system/dist/components/detail-card.js +69 -0
  20. package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +88 -11
  21. package/node_modules/@poe-code/design-system/dist/components/index.d.ts +1 -1
  22. package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
  23. package/node_modules/@poe-code/design-system/dist/components/table.d.ts +2 -0
  24. package/node_modules/@poe-code/design-system/dist/components/table.js +82 -5
  25. package/node_modules/@poe-code/design-system/dist/components/template.d.ts +4 -0
  26. package/node_modules/@poe-code/design-system/dist/components/template.js +198 -32
  27. package/node_modules/@poe-code/design-system/dist/components/text.js +29 -5
  28. package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +2 -2
  29. package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +77 -32
  30. package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +28 -5
  31. package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +45 -28
  32. package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.d.ts +4 -0
  33. package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.js +71 -0
  34. package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
  35. package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +6 -0
  36. package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +32 -10
  37. package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +3 -0
  38. package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +57 -6
  39. package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +1 -0
  40. package/node_modules/@poe-code/design-system/dist/explorer/state.js +12 -15
  41. package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -1
  42. package/node_modules/@poe-code/design-system/dist/index.js +2 -1
  43. package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -1
  44. package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +8 -5
  45. package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +1 -1
  46. package/node_modules/@poe-code/design-system/dist/static/menu.js +8 -2
  47. package/node_modules/@poe-code/design-system/dist/static/spinner.js +10 -4
  48. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +9 -2
  49. package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +19 -2
  50. package/node_modules/@poe-code/design-system/package.json +2 -1
  51. package/node_modules/auth-store/dist/create-secret-store.js +4 -1
  52. package/node_modules/auth-store/dist/encrypted-file-store.d.ts +7 -0
  53. package/node_modules/auth-store/dist/encrypted-file-store.js +69 -7
  54. package/node_modules/auth-store/dist/index.d.ts +1 -1
  55. package/node_modules/auth-store/dist/keychain-store.d.ts +4 -1
  56. package/node_modules/auth-store/dist/keychain-store.js +18 -16
  57. package/node_modules/auth-store/dist/provider-store.d.ts +5 -1
  58. package/node_modules/auth-store/dist/provider-store.js +55 -7
  59. package/node_modules/auth-store/dist/types.d.ts +3 -1
  60. package/node_modules/auth-store/package.json +2 -1
  61. package/package.json +3 -3
  62. package/dist/lock.d.ts +0 -14
  63. package/dist/lock.js +0 -152
@@ -1,3 +1,4 @@
1
+ const MAX_PARTIAL_DEPTH = 100;
1
2
  const HTML_ESCAPE = {
2
3
  "&": "&",
3
4
  "<": "&lt;",
@@ -11,14 +12,34 @@ const HTML_ESCAPE = {
11
12
  export function renderTemplate(template, view, options = {}) {
12
13
  const prepared = options.yield === undefined
13
14
  ? template
14
- : template.split("{{yield}}").join(options.yield);
15
+ : resolveTemplatePartials(template, options.partials ?? {})
16
+ .split("{{yield}}")
17
+ .join(options.yield);
15
18
  const tokens = parseTemplate(prepared);
16
- const escape = options.escape === "none" ? String : escapeHtml;
17
- const preserveMissing = options.yield !== undefined && options.escape === "none";
18
- return renderTokens(tokens, { view }, prepared, escape, preserveMissing);
19
+ validatePartialReferences(tokens, options.partials ?? {}, []);
20
+ if (options.validate === true) {
21
+ const expanded = resolveTemplatePartials(prepared, options.partials ?? {});
22
+ validateVariables(parseTemplate(expanded), { view });
23
+ }
24
+ const state = {
25
+ escape: options.escape === "none" ? String : escapeHtml,
26
+ partials: options.partials ?? {},
27
+ partialStack: [],
28
+ preserveMissing: options.yield !== undefined && options.escape === "none",
29
+ validate: options.validate === true
30
+ };
31
+ return renderTokens(tokens, { view }, prepared, state);
32
+ }
33
+ export function getTemplatePartialNames(template) {
34
+ const names = new Set();
35
+ collectPartialNames(parseTemplate(template), names);
36
+ return [...names];
37
+ }
38
+ export function resolveTemplatePartials(template, partials) {
39
+ return expandTemplatePartials(template, partials, []);
19
40
  }
20
- function renderTemplateInContext(template, context, escape, preserveMissing) {
21
- return renderTokens(parseTemplate(template), context, template, escape, preserveMissing);
41
+ function renderTemplateInContext(template, context, state) {
42
+ return renderTokens(parseTemplate(template), context, template, state);
22
43
  }
23
44
  function parseTemplate(template) {
24
45
  const root = [];
@@ -41,9 +62,6 @@ function parseTemplate(template) {
41
62
  index = standalone?.nextIndex ?? parsed.end;
42
63
  continue;
43
64
  }
44
- if (parsed.kind === "partial") {
45
- throw new Error(`Partials are not supported: "${parsed.name}"`);
46
- }
47
65
  if (parsed.kind === "delimiter") {
48
66
  throw new Error("Custom delimiters are not supported");
49
67
  }
@@ -61,6 +79,15 @@ function parseTemplate(template) {
61
79
  index = standalone?.nextIndex ?? parsed.end;
62
80
  continue;
63
81
  }
82
+ if (parsed.kind === "partial") {
83
+ tokens.push({
84
+ type: "partial",
85
+ name: parsed.name,
86
+ indent: standalone === undefined ? "" : template.slice(standalone.lineStart, open)
87
+ });
88
+ index = standalone?.nextIndex ?? parsed.end;
89
+ continue;
90
+ }
64
91
  if (parsed.kind === "close") {
65
92
  const frame = stack.pop();
66
93
  if (frame === undefined) {
@@ -138,7 +165,7 @@ function getStandalone(template, tagStart, tagEnd, kind) {
138
165
  }
139
166
  return undefined;
140
167
  }
141
- function renderTokens(tokens, context, template, escape, preserveMissing) {
168
+ function renderTokens(tokens, context, template, state) {
142
169
  let output = "";
143
170
  for (const token of tokens) {
144
171
  switch (token.type) {
@@ -147,48 +174,76 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
147
174
  continue;
148
175
  case "name":
149
176
  case "unescaped": {
150
- const value = lookup(context, token.name);
151
- if (value == null) {
152
- if (preserveMissing) {
177
+ const result = lookup(context, token.name);
178
+ if (!result.hit || result.value == null) {
179
+ if (state.validate) {
180
+ throw new Error(`Template variable "${token.name}" not found.`);
181
+ }
182
+ if (state.preserveMissing) {
153
183
  output += token.raw;
154
184
  }
155
185
  continue;
156
186
  }
157
- const rendered = String(value);
158
- output += token.type === "name" ? escape(rendered) : rendered;
187
+ const rendered = String(result.value);
188
+ output += token.type === "name" ? state.escape(rendered) : rendered;
189
+ continue;
190
+ }
191
+ case "partial": {
192
+ if (!Object.hasOwn(state.partials, token.name)) {
193
+ throw new Error(`Partial "${token.name}" not found.`);
194
+ }
195
+ if (state.partialStack.includes(token.name)) {
196
+ throw new Error(`Circular partial reference detected: ${[...state.partialStack, token.name].join(" -> ")}.`);
197
+ }
198
+ if (state.partialStack.length >= MAX_PARTIAL_DEPTH) {
199
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
200
+ }
201
+ const partial = indentPartial(state.partials[token.name], token.indent);
202
+ output += renderTokens(parseTemplate(partial), context, partial, {
203
+ ...state,
204
+ partialStack: [...state.partialStack, token.name]
205
+ });
159
206
  continue;
160
207
  }
161
208
  case "inverted": {
162
- const value = lookup(context, token.name);
209
+ const result = lookup(context, token.name);
210
+ if (!result.hit && state.validate) {
211
+ throw new Error(`Template variable "${token.name}" not found.`);
212
+ }
213
+ const value = result.value;
163
214
  if (!value || (Array.isArray(value) && value.length === 0)) {
164
- output += renderTokens(token.children, context, template, escape, preserveMissing);
215
+ output += renderTokens(token.children, context, template, state);
165
216
  }
166
217
  continue;
167
218
  }
168
219
  case "section": {
169
- const value = lookup(context, token.name);
220
+ const result = lookup(context, token.name);
221
+ if (!result.hit && state.validate) {
222
+ throw new Error(`Template variable "${token.name}" not found.`);
223
+ }
224
+ const value = result.value;
170
225
  if (!value) {
171
226
  continue;
172
227
  }
173
228
  if (Array.isArray(value)) {
174
229
  for (const item of value) {
175
- output += renderTokens(token.children, pushContext(context, item), template, escape, preserveMissing);
230
+ output += renderTokens(token.children, pushContext(context, item), template, state);
176
231
  }
177
232
  continue;
178
233
  }
179
234
  if (typeof value === "function") {
180
235
  const raw = template.slice(token.rawStart, token.rawEnd);
181
- const rendered = value.call(context.view, raw, (nextTemplate) => renderTemplateInContext(nextTemplate, context, escape, preserveMissing));
236
+ const rendered = value.call(context.view, raw, (nextTemplate) => renderTemplateInContext(nextTemplate, context, state));
182
237
  if (rendered != null) {
183
238
  output += String(rendered);
184
239
  }
185
240
  continue;
186
241
  }
187
242
  if (typeof value === "object" || typeof value === "string" || typeof value === "number") {
188
- output += renderTokens(token.children, pushContext(context, value), template, escape, preserveMissing);
243
+ output += renderTokens(token.children, pushContext(context, value), template, state);
189
244
  continue;
190
245
  }
191
- output += renderTokens(token.children, context, template, escape, preserveMissing);
246
+ output += renderTokens(token.children, context, template, state);
192
247
  }
193
248
  }
194
249
  }
@@ -196,7 +251,7 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
196
251
  }
197
252
  function lookup(context, name) {
198
253
  if (name === ".") {
199
- return callLambda(context.view, context.view);
254
+ return { hit: true, value: callLambda(context.view, context.view) };
200
255
  }
201
256
  let cursor = context;
202
257
  while (cursor !== undefined) {
@@ -204,11 +259,124 @@ function lookup(context, name) {
204
259
  ? lookupDotted(cursor.view, name)
205
260
  : lookupName(cursor.view, name);
206
261
  if (result.hit) {
207
- return callLambda(result.value, cursor.view);
262
+ return { hit: true, value: callLambda(result.value, cursor.view) };
208
263
  }
209
264
  cursor = cursor.parent;
210
265
  }
211
- return undefined;
266
+ return { hit: false, value: undefined };
267
+ }
268
+ function collectPartialNames(tokens, names) {
269
+ for (const token of tokens) {
270
+ if (token.type === "partial") {
271
+ names.add(token.name);
272
+ continue;
273
+ }
274
+ if (token.type === "section" || token.type === "inverted") {
275
+ collectPartialNames(token.children, names);
276
+ }
277
+ }
278
+ }
279
+ function validateVariables(tokens, context) {
280
+ for (const token of tokens) {
281
+ if (token.type === "text" || token.type === "partial") {
282
+ continue;
283
+ }
284
+ if (token.type === "name" || token.type === "unescaped") {
285
+ if (!lookup(context, token.name).hit) {
286
+ throw new Error(`Template variable "${token.name}" not found.`);
287
+ }
288
+ continue;
289
+ }
290
+ if (token.type !== "section" && token.type !== "inverted") {
291
+ continue;
292
+ }
293
+ const result = lookup(context, token.name);
294
+ if (!result.hit) {
295
+ throw new Error(`Template variable "${token.name}" not found.`);
296
+ }
297
+ if (Array.isArray(result.value) && result.value.length > 0) {
298
+ for (const item of result.value) {
299
+ validateVariables(token.children, pushContext(context, item));
300
+ }
301
+ continue;
302
+ }
303
+ if (typeof result.value === "object" && result.value !== null ||
304
+ typeof result.value === "string" ||
305
+ typeof result.value === "number") {
306
+ validateVariables(token.children, pushContext(context, result.value));
307
+ continue;
308
+ }
309
+ validateVariables(token.children, context);
310
+ }
311
+ }
312
+ function validatePartialReferences(tokens, partials, partialStack) {
313
+ for (const token of tokens) {
314
+ if (token.type === "section" || token.type === "inverted") {
315
+ validatePartialReferences(token.children, partials, partialStack);
316
+ continue;
317
+ }
318
+ if (token.type !== "partial") {
319
+ continue;
320
+ }
321
+ if (!Object.hasOwn(partials, token.name)) {
322
+ throw new Error(`Partial "${token.name}" not found.`);
323
+ }
324
+ if (partialStack.includes(token.name)) {
325
+ throw new Error(`Circular partial reference detected: ${[...partialStack, token.name].join(" -> ")}.`);
326
+ }
327
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
328
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
329
+ }
330
+ validatePartialReferences(parseTemplate(partials[token.name]), partials, [
331
+ ...partialStack,
332
+ token.name
333
+ ]);
334
+ }
335
+ }
336
+ function indentPartial(partial, indent) {
337
+ if (indent === "") {
338
+ return partial;
339
+ }
340
+ return partial
341
+ .split("\n")
342
+ .map((line) => (line === "" ? "" : `${indent}${line}`))
343
+ .join("\n");
344
+ }
345
+ function expandTemplatePartials(template, partials, partialStack) {
346
+ let output = "";
347
+ let index = 0;
348
+ while (index < template.length) {
349
+ const open = template.indexOf("{{", index);
350
+ if (open === -1) {
351
+ output += template.slice(index);
352
+ break;
353
+ }
354
+ const parsed = parseTag(template, open);
355
+ if (parsed.kind !== "partial") {
356
+ output += template.slice(index, parsed.end);
357
+ index = parsed.end;
358
+ continue;
359
+ }
360
+ if (!Object.hasOwn(partials, parsed.name)) {
361
+ throw new Error(`Partial "${parsed.name}" not found.`);
362
+ }
363
+ if (partialStack.includes(parsed.name)) {
364
+ throw new Error(`Circular partial reference detected: ${[...partialStack, parsed.name].join(" -> ")}.`);
365
+ }
366
+ if (partialStack.length >= MAX_PARTIAL_DEPTH) {
367
+ throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
368
+ }
369
+ const standalone = getStandalone(template, open, parsed.end, parsed.kind);
370
+ const beforePartial = standalone === undefined
371
+ ? template.slice(index, open)
372
+ : template.slice(index, standalone.lineStart);
373
+ const indent = standalone === undefined ? "" : template.slice(standalone.lineStart, open);
374
+ const partial = indentPartial(partials[parsed.name], indent);
375
+ output +=
376
+ beforePartial + expandTemplatePartials(partial, partials, [...partialStack, parsed.name]);
377
+ index = standalone?.nextIndex ?? parsed.end;
378
+ }
379
+ return output;
212
380
  }
213
381
  function lookupName(view, name) {
214
382
  if (!isPropertyContainer(view) || !hasProperty(view, name)) {
@@ -219,15 +387,13 @@ function lookupName(view, name) {
219
387
  function lookupDotted(view, name) {
220
388
  const parts = name.split(".");
221
389
  let value = view;
222
- let hit = false;
223
- for (let index = 0; value != null && index < parts.length; index += 1) {
224
- const part = parts[index] ?? "";
225
- if (index === parts.length - 1) {
226
- hit = hasProperty(Object(value), part);
390
+ for (const part of parts) {
391
+ if (!isPropertyContainer(value) || !hasProperty(value, part)) {
392
+ return { hit: false, value: undefined };
227
393
  }
228
394
  value = Object(value)[part];
229
395
  }
230
- return { hit, value };
396
+ return { hit: true, value };
231
397
  }
232
398
  function callLambda(value, view) {
233
399
  return typeof value === "function" ? value.call(view) : value;
@@ -267,5 +433,5 @@ function isPropertyContainer(value) {
267
433
  return (typeof value === "object" && value !== null) || typeof value === "function";
268
434
  }
269
435
  function hasProperty(value, key) {
270
- return key in value;
436
+ return Object.prototype.hasOwnProperty.call(value, key);
271
437
  }
@@ -2,6 +2,30 @@ import { color } from "./color.js";
2
2
  import { resolveOutputFormat } from "../internal/output-format.js";
3
3
  import { getTheme } from "../internal/theme-detect.js";
4
4
  import { typography } from "../tokens/typography.js";
5
+ function renderMarkdownInline(content) {
6
+ return content.replaceAll("\r\n", " ").replaceAll("\n", " ").replaceAll("\r", " ");
7
+ }
8
+ function renderMarkdownCode(content) {
9
+ const value = renderMarkdownInline(content);
10
+ let longestRun = 0;
11
+ let currentRun = 0;
12
+ for (const char of value) {
13
+ if (char === "`") {
14
+ currentRun += 1;
15
+ longestRun = Math.max(longestRun, currentRun);
16
+ continue;
17
+ }
18
+ currentRun = 0;
19
+ }
20
+ const delimiter = "`".repeat(longestRun + 1);
21
+ return `${delimiter}${value}${delimiter}`;
22
+ }
23
+ function renderMarkdownLink(content) {
24
+ const value = renderMarkdownInline(content);
25
+ const label = value.replaceAll("\\", "\\\\").replaceAll("[", "\\[").replaceAll("]", "\\]");
26
+ const url = value.replaceAll("\\", "\\\\").replaceAll("(", "\\(").replaceAll(")", "\\)");
27
+ return `[${label}](${url})`;
28
+ }
5
29
  export const text = {
6
30
  intro(content) {
7
31
  const format = resolveOutputFormat();
@@ -40,7 +64,7 @@ export const text = {
40
64
  if (format === "json")
41
65
  return content;
42
66
  if (format === "markdown")
43
- return `\`${content}\``;
67
+ return renderMarkdownCode(content);
44
68
  return getTheme().accent(content);
45
69
  },
46
70
  argument(content) {
@@ -56,7 +80,7 @@ export const text = {
56
80
  if (format === "json")
57
81
  return content;
58
82
  if (format === "markdown")
59
- return `\`${content}\``;
83
+ return renderMarkdownCode(content);
60
84
  return color.yellow(content);
61
85
  },
62
86
  example(content) {
@@ -64,7 +88,7 @@ export const text = {
64
88
  if (format === "json")
65
89
  return content;
66
90
  if (format === "markdown")
67
- return `\`${content}\``;
91
+ return renderMarkdownCode(content);
68
92
  return getTheme().muted(content);
69
93
  },
70
94
  usageCommand(content) {
@@ -72,7 +96,7 @@ export const text = {
72
96
  if (format === "json")
73
97
  return content;
74
98
  if (format === "markdown")
75
- return `\`${content}\``;
99
+ return renderMarkdownCode(content);
76
100
  return color.green(content);
77
101
  },
78
102
  link(content) {
@@ -80,7 +104,7 @@ export const text = {
80
104
  if (format === "json")
81
105
  return content;
82
106
  if (format === "markdown")
83
- return `[${content}](${content})`;
107
+ return renderMarkdownLink(content);
84
108
  return getTheme().accent(content);
85
109
  },
86
110
  muted(content) {
@@ -12,7 +12,7 @@ export declare function hasAnsi(text: string): boolean;
12
12
  * logical lines split on "\n". Each line is a list of styled segments: contiguous
13
13
  * printable runs of characters sharing the same style.
14
14
  *
15
- * Non-SGR CSI sequences and other control characters are discarded. `baseStyle` is
16
- * used as the initial style and as the restore target for SGR reset / default color.
15
+ * Common cursor-affecting line controls are rendered into their visible result.
16
+ * `baseStyle` is used as the initial style and as the restore target for SGR reset / default color.
17
17
  */
18
18
  export declare function parseAnsi(text: string, baseStyle?: CellStyle): StyledLine[];
@@ -7,38 +7,25 @@ export function hasAnsi(text) {
7
7
  * logical lines split on "\n". Each line is a list of styled segments: contiguous
8
8
  * printable runs of characters sharing the same style.
9
9
  *
10
- * Non-SGR CSI sequences and other control characters are discarded. `baseStyle` is
11
- * used as the initial style and as the restore target for SGR reset / default color.
10
+ * Common cursor-affecting line controls are rendered into their visible result.
11
+ * `baseStyle` is used as the initial style and as the restore target for SGR reset / default color.
12
12
  */
13
13
  export function parseAnsi(text, baseStyle) {
14
14
  const base = normalizeStyle(baseStyle);
15
15
  let style = { ...base };
16
+ let concealed = false;
16
17
  const lines = [];
17
- let segments = [];
18
- let pending = "";
19
- const flushSegment = () => {
20
- if (pending.length === 0) {
21
- return;
22
- }
23
- const last = segments[segments.length - 1];
24
- if (last && stylesEqual(last.style, style)) {
25
- last.text += pending;
26
- }
27
- else {
28
- segments.push({ text: pending, style: { ...style } });
29
- }
30
- pending = "";
31
- };
18
+ let cells = [];
19
+ let column = 0;
32
20
  const finishLine = () => {
33
- flushSegment();
34
- lines.push({ segments });
35
- segments = [];
21
+ lines.push({ segments: cellsToSegments(cells) });
22
+ cells = [];
23
+ column = 0;
36
24
  };
37
25
  let index = 0;
38
26
  while (index < text.length) {
39
27
  const ch = text[index];
40
28
  if (ch === ESC && text[index + 1] === "[") {
41
- flushSegment();
42
29
  const paramsStart = index + 2;
43
30
  let cursor = paramsStart;
44
31
  while (cursor < text.length && !isCsiFinalByte(text[cursor])) {
@@ -51,13 +38,17 @@ export function parseAnsi(text, baseStyle) {
51
38
  const params = text.slice(paramsStart, cursor);
52
39
  const finalByte = text[cursor];
53
40
  if (finalByte === "m") {
54
- style = applySgr(style, parseParams(params), base);
41
+ const sgr = applySgr(style, concealed, parseParams(params), base);
42
+ style = sgr.style;
43
+ concealed = sgr.concealed;
44
+ }
45
+ else if (finalByte === "K" && parseParams(params)[0] === 2) {
46
+ cells = [];
55
47
  }
56
48
  index = cursor + 1;
57
49
  continue;
58
50
  }
59
51
  if (ch === ESC) {
60
- flushSegment();
61
52
  const next = text[index + 1];
62
53
  if (next === "]" || next === "P" || next === "X" || next === "^" || next === "_") {
63
54
  index = skipStringTerminated(text, index + 2);
@@ -67,6 +58,7 @@ export function parseAnsi(text, baseStyle) {
67
58
  continue;
68
59
  }
69
60
  if (ch === "\r") {
61
+ column = 0;
70
62
  index += 1;
71
63
  continue;
72
64
  }
@@ -75,17 +67,37 @@ export function parseAnsi(text, baseStyle) {
75
67
  index += 1;
76
68
  continue;
77
69
  }
70
+ if (ch === "\b") {
71
+ column = Math.max(0, column - 1);
72
+ index += 1;
73
+ continue;
74
+ }
78
75
  const code = ch.charCodeAt(0);
79
76
  if (code < 0x20 && ch !== "\t") {
80
77
  index += 1;
81
78
  continue;
82
79
  }
83
- pending += ch;
80
+ cells[column] = { ch: concealed ? " " : ch, style: { ...style } };
81
+ column += 1;
84
82
  index += 1;
85
83
  }
86
84
  finishLine();
87
85
  return lines;
88
86
  }
87
+ function cellsToSegments(cells) {
88
+ const segments = [];
89
+ for (const cell of cells) {
90
+ const nextCell = cell ?? { ch: " ", style: {} };
91
+ const last = segments[segments.length - 1];
92
+ if (last && stylesEqual(last.style, nextCell.style)) {
93
+ last.text += nextCell.ch;
94
+ }
95
+ else {
96
+ segments.push({ text: nextCell.ch, style: { ...nextCell.style } });
97
+ }
98
+ }
99
+ return segments;
100
+ }
89
101
  function isCsiFinalByte(ch) {
90
102
  const code = ch.charCodeAt(0);
91
103
  return code >= 0x40 && code <= 0x7e;
@@ -108,14 +120,21 @@ function parseParams(params) {
108
120
  if (params.length === 0) {
109
121
  return [0];
110
122
  }
111
- return params.split(";").map((part) => {
112
- if (part.length === 0) {
113
- return 0;
123
+ return params.split(";").flatMap((part) => {
124
+ const colonParams = part.split(":");
125
+ if (colonParams.length > 2 && colonParams[1] === "2") {
126
+ colonParams.splice(2, 1);
114
127
  }
115
- const parsed = Number.parseInt(part, 10);
116
- return Number.isFinite(parsed) ? parsed : 0;
128
+ return colonParams.map(parseParam);
117
129
  });
118
130
  }
131
+ function parseParam(part) {
132
+ if (part.length === 0) {
133
+ return 0;
134
+ }
135
+ const parsed = Number.parseInt(part, 10);
136
+ return Number.isFinite(parsed) ? parsed : 0;
137
+ }
119
138
  const BASIC_COLORS = [
120
139
  "black",
121
140
  "red",
@@ -136,13 +155,15 @@ const BRIGHT_COLORS = [
136
155
  "cyanBright",
137
156
  "whiteBright"
138
157
  ];
139
- function applySgr(style, params, base) {
158
+ function applySgr(style, concealed, params, base) {
140
159
  let next = { ...style };
160
+ let nextConcealed = concealed;
141
161
  let index = 0;
142
162
  while (index < params.length) {
143
163
  const code = params[index];
144
164
  if (code === 0) {
145
165
  next = { ...base };
166
+ nextConcealed = false;
146
167
  index += 1;
147
168
  continue;
148
169
  }
@@ -156,12 +177,32 @@ function applySgr(style, params, base) {
156
177
  index += 1;
157
178
  continue;
158
179
  }
180
+ if (code === 7) {
181
+ next.inverse = true;
182
+ index += 1;
183
+ continue;
184
+ }
159
185
  if (code === 22) {
160
186
  delete next.bold;
161
187
  delete next.dim;
162
188
  index += 1;
163
189
  continue;
164
190
  }
191
+ if (code === 8) {
192
+ nextConcealed = true;
193
+ index += 1;
194
+ continue;
195
+ }
196
+ if (code === 28) {
197
+ nextConcealed = false;
198
+ index += 1;
199
+ continue;
200
+ }
201
+ if (code === 27) {
202
+ delete next.inverse;
203
+ index += 1;
204
+ continue;
205
+ }
165
206
  if (code >= 30 && code <= 37) {
166
207
  next.fg = BASIC_COLORS[code - 30];
167
208
  index += 1;
@@ -242,7 +283,7 @@ function applySgr(style, params, base) {
242
283
  }
243
284
  index += 1;
244
285
  }
245
- return next;
286
+ return { style: next, concealed: nextConcealed };
246
287
  }
247
288
  function convert256(palette) {
248
289
  if (palette < 0 || palette > 255) {
@@ -278,7 +319,8 @@ function stylesEqual(left, right) {
278
319
  return left.fg === right.fg
279
320
  && left.bg === right.bg
280
321
  && left.bold === right.bold
281
- && left.dim === right.dim;
322
+ && left.dim === right.dim
323
+ && left.inverse === right.inverse;
282
324
  }
283
325
  function normalizeStyle(style) {
284
326
  const next = {};
@@ -294,5 +336,8 @@ function normalizeStyle(style) {
294
336
  if (style?.dim !== undefined) {
295
337
  next.dim = style.dim;
296
338
  }
339
+ if (style?.inverse !== undefined) {
340
+ next.inverse = style.inverse;
341
+ }
297
342
  return next;
298
343
  }