@linxiraos/pi-utils 1.1.20 → 1.1.23

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 (113) hide show
  1. package/CHANGELOG.md +7 -5
  2. package/README.md +1 -1
  3. package/THIRD-PARTY-NOTICES.txt +3 -40
  4. package/dist/types/dirs.d.ts +26 -4
  5. package/dist/types/file-lock.d.ts +2 -6
  6. package/dist/types/fs-open.d.ts +16 -0
  7. package/dist/types/index.d.ts +13 -0
  8. package/dist/types/logger/rotating-file.d.ts +9 -0
  9. package/dist/types/mermaid-ascii.d.ts +12 -4
  10. package/dist/types/postmortem.d.ts +23 -0
  11. package/dist/types/procmgr.d.ts +3 -0
  12. package/dist/types/stderr-guard.d.ts +23 -4
  13. package/dist/types/stream.d.ts +11 -4
  14. package/package.json +2 -2
  15. package/src/cli.ts +1 -1
  16. package/src/dirs.ts +54 -12
  17. package/src/file-lock.ts +7 -9
  18. package/src/fs-open.ts +31 -0
  19. package/src/index.ts +40 -0
  20. package/src/logger/rotating-file.ts +22 -9
  21. package/src/logger.ts +10 -9
  22. package/src/mermaid-ascii.ts +13 -7
  23. package/src/module-timer.ts +7 -4
  24. package/src/postmortem.ts +57 -0
  25. package/src/procmgr.ts +18 -18
  26. package/src/ptree.ts +3 -3
  27. package/src/runtime-install.ts +43 -7
  28. package/src/sqlite.ts +29 -2
  29. package/src/stderr-guard.ts +53 -21
  30. package/src/stream.ts +26 -8
  31. package/dist/types/vendor/mermaid-ascii/ascii/ansi.d.ts +0 -41
  32. package/dist/types/vendor/mermaid-ascii/ascii/canvas.d.ts +0 -89
  33. package/dist/types/vendor/mermaid-ascii/ascii/class-diagram.d.ts +0 -7
  34. package/dist/types/vendor/mermaid-ascii/ascii/converter.d.ts +0 -12
  35. package/dist/types/vendor/mermaid-ascii/ascii/draw.d.ts +0 -66
  36. package/dist/types/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +0 -48
  37. package/dist/types/vendor/mermaid-ascii/ascii/edge-routing.d.ts +0 -43
  38. package/dist/types/vendor/mermaid-ascii/ascii/er-diagram.d.ts +0 -7
  39. package/dist/types/vendor/mermaid-ascii/ascii/grid.d.ts +0 -56
  40. package/dist/types/vendor/mermaid-ascii/ascii/index.d.ts +0 -65
  41. package/dist/types/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +0 -27
  42. package/dist/types/vendor/mermaid-ascii/ascii/pathfinder.d.ts +0 -17
  43. package/dist/types/vendor/mermaid-ascii/ascii/sequence.d.ts +0 -7
  44. package/dist/types/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +0 -11
  45. package/dist/types/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +0 -34
  46. package/dist/types/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +0 -11
  47. package/dist/types/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +0 -11
  48. package/dist/types/vendor/mermaid-ascii/ascii/shapes/index.d.ts +0 -26
  49. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +0 -31
  50. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +0 -11
  51. package/dist/types/vendor/mermaid-ascii/ascii/shapes/special.d.ts +0 -59
  52. package/dist/types/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +0 -17
  53. package/dist/types/vendor/mermaid-ascii/ascii/shapes/state.d.ts +0 -30
  54. package/dist/types/vendor/mermaid-ascii/ascii/shapes/types.d.ts +0 -55
  55. package/dist/types/vendor/mermaid-ascii/ascii/types.d.ts +0 -206
  56. package/dist/types/vendor/mermaid-ascii/ascii/validate.d.ts +0 -51
  57. package/dist/types/vendor/mermaid-ascii/ascii/xychart.d.ts +0 -2
  58. package/dist/types/vendor/mermaid-ascii/class/parser.d.ts +0 -6
  59. package/dist/types/vendor/mermaid-ascii/class/types.d.ts +0 -102
  60. package/dist/types/vendor/mermaid-ascii/er/parser.d.ts +0 -6
  61. package/dist/types/vendor/mermaid-ascii/er/types.d.ts +0 -76
  62. package/dist/types/vendor/mermaid-ascii/index.d.ts +0 -1
  63. package/dist/types/vendor/mermaid-ascii/multiline-utils.d.ts +0 -9
  64. package/dist/types/vendor/mermaid-ascii/parser.d.ts +0 -7
  65. package/dist/types/vendor/mermaid-ascii/sequence/parser.d.ts +0 -6
  66. package/dist/types/vendor/mermaid-ascii/sequence/types.d.ts +0 -130
  67. package/dist/types/vendor/mermaid-ascii/text-metrics.d.ts +0 -23
  68. package/dist/types/vendor/mermaid-ascii/types.d.ts +0 -114
  69. package/dist/types/vendor/mermaid-ascii/xychart/colors.d.ts +0 -25
  70. package/dist/types/vendor/mermaid-ascii/xychart/parser.d.ts +0 -6
  71. package/dist/types/vendor/mermaid-ascii/xychart/types.d.ts +0 -145
  72. package/src/vendor/mermaid-ascii/NOTICE +0 -33
  73. package/src/vendor/mermaid-ascii/ascii/ansi.ts +0 -409
  74. package/src/vendor/mermaid-ascii/ascii/canvas.ts +0 -478
  75. package/src/vendor/mermaid-ascii/ascii/class-diagram.ts +0 -699
  76. package/src/vendor/mermaid-ascii/ascii/converter.ts +0 -271
  77. package/src/vendor/mermaid-ascii/ascii/draw.ts +0 -1389
  78. package/src/vendor/mermaid-ascii/ascii/edge-bundling.ts +0 -328
  79. package/src/vendor/mermaid-ascii/ascii/edge-routing.ts +0 -297
  80. package/src/vendor/mermaid-ascii/ascii/er-diagram.ts +0 -441
  81. package/src/vendor/mermaid-ascii/ascii/grid.ts +0 -593
  82. package/src/vendor/mermaid-ascii/ascii/index.ts +0 -187
  83. package/src/vendor/mermaid-ascii/ascii/multiline-utils.ts +0 -78
  84. package/src/vendor/mermaid-ascii/ascii/pathfinder.ts +0 -277
  85. package/src/vendor/mermaid-ascii/ascii/sequence.ts +0 -460
  86. package/src/vendor/mermaid-ascii/ascii/shapes/circle.ts +0 -27
  87. package/src/vendor/mermaid-ascii/ascii/shapes/corners.ts +0 -127
  88. package/src/vendor/mermaid-ascii/ascii/shapes/diamond.ts +0 -27
  89. package/src/vendor/mermaid-ascii/ascii/shapes/hexagon.ts +0 -27
  90. package/src/vendor/mermaid-ascii/ascii/shapes/index.ts +0 -101
  91. package/src/vendor/mermaid-ascii/ascii/shapes/rectangle.ts +0 -175
  92. package/src/vendor/mermaid-ascii/ascii/shapes/rounded.ts +0 -27
  93. package/src/vendor/mermaid-ascii/ascii/shapes/special.ts +0 -296
  94. package/src/vendor/mermaid-ascii/ascii/shapes/stadium.ts +0 -114
  95. package/src/vendor/mermaid-ascii/ascii/shapes/state.ts +0 -192
  96. package/src/vendor/mermaid-ascii/ascii/shapes/types.ts +0 -73
  97. package/src/vendor/mermaid-ascii/ascii/types.ts +0 -273
  98. package/src/vendor/mermaid-ascii/ascii/validate.ts +0 -120
  99. package/src/vendor/mermaid-ascii/ascii/xychart.ts +0 -875
  100. package/src/vendor/mermaid-ascii/class/parser.ts +0 -290
  101. package/src/vendor/mermaid-ascii/class/types.ts +0 -121
  102. package/src/vendor/mermaid-ascii/er/parser.ts +0 -181
  103. package/src/vendor/mermaid-ascii/er/types.ts +0 -91
  104. package/src/vendor/mermaid-ascii/index.ts +0 -14
  105. package/src/vendor/mermaid-ascii/multiline-utils.ts +0 -30
  106. package/src/vendor/mermaid-ascii/parser.ts +0 -645
  107. package/src/vendor/mermaid-ascii/sequence/parser.ts +0 -207
  108. package/src/vendor/mermaid-ascii/sequence/types.ts +0 -146
  109. package/src/vendor/mermaid-ascii/text-metrics.ts +0 -74
  110. package/src/vendor/mermaid-ascii/types.ts +0 -164
  111. package/src/vendor/mermaid-ascii/xychart/colors.ts +0 -140
  112. package/src/vendor/mermaid-ascii/xychart/parser.ts +0 -115
  113. package/src/vendor/mermaid-ascii/xychart/types.ts +0 -150
@@ -2,20 +2,17 @@
2
2
  * Terminal stderr guard: keeps unmanaged fd-2 writes off the terminal while a
3
3
  * TUI owns the viewport.
4
4
  *
5
- * On macOS, runtime diagnostics are written by the platform directly to file
6
- * descriptor 2 at arbitrary times — e.g. libmalloc's "MallocStackLogging:
7
- * can't turn off malloc stack logging because it was not enabled" when the OS
8
- * broadcasts a memory-diagnostic event to long-lived processes. Those bytes
9
- * bypass the renderer and paint straight into the viewport. Stripping the
10
- * MallocStackLogging* env vars (cli.ts) only protects child processes; it
11
- * cannot stop libmalloc inside THIS process from logging.
5
+ * Native runtimes and libraries can write directly to file descriptor 2 at
6
+ * arbitrary times. Examples include macOS libmalloc diagnostics and Bun HTTP
7
+ * decompression diagnostics on Linux. Those bytes bypass the renderer and
8
+ * paint straight into the viewport.
12
9
  *
13
- * Fix (mirrors openai/codex#24459): while the TUI owns the terminal, dup fd 2
14
- * aside and dup2 a redirect target over it; restore the saved fd whenever
15
- * terminal ownership is released (external editor, Ctrl+Z suspend, shutdown,
16
- * crash restore). Unlike codex we redirect to the omp log file — not
17
- * /dev/null — so the diagnostics stay greppable and Bun native-crash reports
18
- * (which abort before any JS cleanup can restore fd 2) are preserved.
10
+ * While the TUI owns the terminal, dup fd 2 aside and dup2 a redirect target
11
+ * over it; restore the saved fd whenever terminal ownership is released
12
+ * (external editor, Ctrl+Z suspend, shutdown, crash restore). This mirrors
13
+ * openai/codex#24459, but redirects to the omp log file instead of /dev/null
14
+ * so diagnostics stay greppable and Bun native-crash reports (which abort
15
+ * before any JS cleanup can restore fd 2) are preserved.
19
16
  *
20
17
  * Only dup/dup2 go through bun:ffi. fcntl is deliberately avoided: it is
21
18
  * variadic, and the arm64-darwin ABI passes variadic arguments on the stack,
@@ -79,35 +76,41 @@ function stderrSharesStdoutTerminal(): boolean {
79
76
  /** Saved dup of the real stderr while suppression is active, else null. */
80
77
  let savedStderrFd: number | null = null;
81
78
 
79
+ /** Newest log file the guard knows about; see {@link setStderrRedirectTarget}. */
80
+ let redirectTarget: string | null = null;
81
+
82
82
  export interface SuppressTerminalStderrOptions {
83
- /** Redirect target path; defaults to today's omp log file, then /dev/null. */
83
+ /**
84
+ * Redirect target path; defaults to the log file the rotating sink last
85
+ * reported (see {@link setStderrRedirectTarget}), then today's omp log
86
+ * file, then /dev/null.
87
+ */
84
88
  redirectPath?: string;
85
- /** Bypass the macOS + same-terminal gate. Tests only. */
89
+ /** Bypass the same-terminal gate. Tests only. */
86
90
  force?: boolean;
87
91
  }
88
92
 
89
93
  /**
90
94
  * Redirect fd 2 away from the terminal while the TUI owns the viewport.
91
95
  * Returns true when suppression is (already) active. No-op — returning
92
- * false — off macOS, when stderr does not target the stdout terminal, or
93
- * when the libc fd ops are unavailable.
96
+ * false — when stderr does not target the stdout terminal or when libc fd
97
+ * operations are unavailable.
94
98
  */
95
99
  export function suppressTerminalStderr(options?: SuppressTerminalStderrOptions): boolean {
96
100
  if (savedStderrFd !== null) return true;
97
- if (!options?.force && (process.platform !== "darwin" || !stderrSharesStdoutTerminal())) {
98
- return false;
99
- }
101
+ if (!options?.force && !stderrSharesStdoutTerminal()) return false;
100
102
  const libc = libcFdOps();
101
103
  if (!libc) return false;
102
104
 
103
105
  let redirectFd: number;
104
106
  try {
105
- const redirectPath = options?.redirectPath ?? getLogPath();
107
+ const redirectPath = options?.redirectPath ?? redirectTarget ?? getLogPath();
106
108
  // getLogsDir() only computes the path; the logger creates it lazily, so
107
109
  // on a fresh profile ~/.zeta/logs may not exist yet. Create it here so
108
110
  // diagnostics land in the log instead of falling through to /dev/null.
109
111
  fs.mkdirSync(path.dirname(redirectPath), { recursive: true });
110
112
  redirectFd = fs.openSync(redirectPath, "a");
113
+ redirectTarget = redirectPath;
111
114
  } catch {
112
115
  try {
113
116
  redirectFd = fs.openSync("/dev/null", "w");
@@ -131,6 +134,35 @@ export function suppressTerminalStderr(options?: SuppressTerminalStderrOptions):
131
134
  return true;
132
135
  }
133
136
 
137
+ /**
138
+ * Point fd 2 at `logPath`, the log file the rotating sink is currently
139
+ * writing.
140
+ *
141
+ * The logger calls this on every sink rotation. Without it fd 2 keeps the
142
+ * descriptor opened at TUI start, so after local midnight raw stderr — this
143
+ * process's and every child that inherited fd 2 — keeps landing in the
144
+ * previous day's file, which the sink eventually prunes out from under it.
145
+ *
146
+ * The path is recorded even while suppression is inactive, so a later
147
+ * {@link suppressTerminalStderr} redirects to the live file instead of
148
+ * re-deriving a dated name. Best-effort: if the new file cannot be opened,
149
+ * fd 2 keeps its current target.
150
+ */
151
+ export function setStderrRedirectTarget(logPath: string): void {
152
+ redirectTarget = logPath;
153
+ if (savedStderrFd === null) return;
154
+ const libc = libcFdOps();
155
+ if (!libc) return;
156
+ let redirectFd: number;
157
+ try {
158
+ redirectFd = fs.openSync(logPath, "a");
159
+ } catch {
160
+ return;
161
+ }
162
+ libc.dup2(redirectFd, STDERR_FILENO);
163
+ fs.closeSync(redirectFd);
164
+ }
165
+
134
166
  /**
135
167
  * Re-point fd 2 at the saved terminal stderr. Safe to call unconditionally:
136
168
  * no-op when suppression is not active. Called at every terminal-ownership
package/src/stream.ts CHANGED
@@ -292,13 +292,14 @@ type SseFrame<T> = { ok: true; value: T } | { ok: false; raw: string; error: Syn
292
292
  /**
293
293
  * Shared `data:`-line framing for {@link readSseJson} and
294
294
  * {@link readSseJsonOrText}: skips empty events, stops at the OpenAI `[DONE]`
295
- * sentinel, notifies the diagnostic observer, and treats a container-shaped
296
- * stream tail as a clean end of iteration.
295
+ * sentinel (reporting it through `onDone`), notifies the diagnostic observer,
296
+ * and treats a container-shaped stream tail as a clean end of iteration.
297
297
  */
298
298
  async function* readSseFrames<T>(
299
299
  stream: ReadableStream<Uint8Array>,
300
300
  signal?: AbortSignal,
301
301
  onEvent?: SseEventObserver,
302
+ onDone?: () => void,
302
303
  ): AsyncGenerator<SseFrame<T>> {
303
304
  // The diagnostic observer is the only reader of `raw`; capture it exactly
304
305
  // when one is attached so the hot path stays allocation-free.
@@ -307,7 +308,10 @@ async function* readSseFrames<T>(
307
308
  notifySseEventObserver(onEvent, sse);
308
309
  const data = sse.data;
309
310
  if (data === "" || data === "[DONE]") {
310
- if (data === "[DONE]") return;
311
+ if (data === "[DONE]") {
312
+ onDone?.();
313
+ return;
314
+ }
311
315
  continue;
312
316
  }
313
317
  try {
@@ -358,8 +362,14 @@ export async function* readSseJsonOrText<T>(
358
362
  stream: ReadableStream<Uint8Array>,
359
363
  signal?: AbortSignal,
360
364
  onEvent?: SseEventObserver,
365
+ /**
366
+ * Called when iteration ends on the OpenAI `[DONE]` sentinel. Lets a
367
+ * consumer tell a server-agreed end from a bare EOF without attaching an
368
+ * `onEvent` observer (which turns on per-line raw capture).
369
+ */
370
+ onDone?: () => void,
361
371
  ): AsyncGenerator<T | string> {
362
- for await (const frame of readSseFrames<T>(stream, signal, onEvent)) {
372
+ for await (const frame of readSseFrames<T>(stream, signal, onEvent, onDone)) {
363
373
  if (!frame.ok) yield frame.raw;
364
374
  else yield frame.value;
365
375
  }
@@ -385,12 +395,13 @@ export interface ServerSentEvent {
385
395
  * Decoded wire lines for this event (`event:`/`data:`/etc.), for the
386
396
  * diagnostic pipeline. Populated only when the reader opts in via
387
397
  * {@link ReadSseEventsOptions.captureRaw} (or attaches an `onSseEvent`
388
- * observer to the JSON readers, which opt in automatically); otherwise
389
- * `[]`. Direct `readSseEvents` callers that need wire text must pass
398
+ * observer to the JSON readers, which opt in automatically); otherwise a
399
+ * shared, frozen empty array — copy or reassign it, never mutate it in
400
+ * place. Direct `readSseEvents` callers that need wire text must pass
390
401
  * `{ captureRaw: true }` — the field is allocation-free by default so
391
402
  * the token path pays no per-frame array/slice cost.
392
403
  */
393
- raw: string[];
404
+ raw: readonly string[];
394
405
  id?: string;
395
406
  retry?: number;
396
407
  }
@@ -414,6 +425,13 @@ interface SseEventState {
414
425
  // an ASCII line-ending byte, which cannot split a multi-byte UTF-8 sequence.
415
426
  const SSE_DECODER = new TextDecoder("utf-8");
416
427
 
428
+ /**
429
+ * `raw` of every event dispatched with capture off. Shared instead of one
430
+ * fresh array per event; frozen so an in-place mutation throws rather than
431
+ * leaking lines into every other event (consumers copy or reassign `raw`).
432
+ */
433
+ const EMPTY_RAW: readonly string[] = Object.freeze<string[]>([]);
434
+
417
435
  function flushSseEvent(state: SseEventState): ServerSentEvent | null {
418
436
  if (state.event === null && state.data === null && state.id === undefined && state.retry === undefined) {
419
437
  if (state.raw !== null) state.raw = [];
@@ -422,7 +440,7 @@ function flushSseEvent(state: SseEventState): ServerSentEvent | null {
422
440
  const event: ServerSentEvent = {
423
441
  event: state.event,
424
442
  data: state.data ?? "",
425
- raw: state.raw ?? [],
443
+ raw: state.raw ?? EMPTY_RAW,
426
444
  };
427
445
  if (state.id !== undefined) event.id = state.id;
428
446
  if (state.retry !== undefined) event.retry = state.retry;
@@ -1,41 +0,0 @@
1
- import type { CharRole, AsciiTheme, ColorMode } from './types.js';
2
- /**
3
- * Default ASCII theme derived from the SVG renderer's color palette.
4
- * Uses the same mixing ratios to maintain visual consistency.
5
- */
6
- export declare const DEFAULT_ASCII_THEME: AsciiTheme;
7
- /**
8
- * Detect the best color mode for the current environment.
9
- *
10
- * Terminal detection order:
11
- * 1. COLORTERM=truecolor or COLORTERM=24bit → truecolor
12
- * 2. TERM contains "256color" → ansi256
13
- * 3. TERM is set and not "dumb" → ansi16
14
- *
15
- * Browser: returns 'html' (uses <span> tags with inline styles).
16
- * Unknown/piped: returns 'none'.
17
- */
18
- export declare function detectColorMode(): ColorMode;
19
- /**
20
- * Generate the ANSI escape sequence for a role color.
21
- */
22
- export declare function getAnsiColor(role: CharRole, theme: AsciiTheme, mode: ColorMode): string;
23
- /**
24
- * Get the ANSI reset sequence.
25
- */
26
- export declare function getAnsiReset(mode: ColorMode): string;
27
- /**
28
- * Wrap a character with ANSI color codes based on its role.
29
- */
30
- export declare function colorizeChar(char: string, role: CharRole | null, theme: AsciiTheme, mode: ColorMode): string;
31
- /**
32
- * Colorize an entire line efficiently by grouping consecutive same-role characters.
33
- * This reduces the number of escape sequences (ANSI) or span tags (HTML) in the output.
34
- */
35
- export declare function colorizeLine(chars: string[], roles: (CharRole | null)[], theme: AsciiTheme, mode: ColorMode): string;
36
- /**
37
- * Colorize a text string with a direct hex color.
38
- * Used by renderers that need per-cell color control (e.g. multi-series xychart).
39
- * Handles all output modes: ANSI (16/256/truecolor) and HTML.
40
- */
41
- export declare function colorizeText(text: string, hex: string, mode: ColorMode): string;
@@ -1,89 +0,0 @@
1
- import type { Canvas, DrawingCoord, RoleCanvas, CharRole, AsciiTheme, ColorMode } from './types.js';
2
- /**
3
- * Create a blank canvas filled with spaces.
4
- * Dimensions are inclusive: mkCanvas(3, 2) creates a 4x3 grid (indices 0..3, 0..2).
5
- */
6
- export declare function mkCanvas(x: number, y: number): Canvas;
7
- /** Create a blank canvas with the same dimensions as the given canvas. */
8
- export declare function copyCanvas(source: Canvas): Canvas;
9
- /**
10
- * Create a blank role canvas filled with nulls.
11
- * Same dimensions as mkCanvas — column-major, roleCanvas[x][y].
12
- */
13
- export declare function mkRoleCanvas(x: number, y: number): RoleCanvas;
14
- /** Create a blank role canvas with the same dimensions as the given role canvas. */
15
- export declare function copyRoleCanvas(source: RoleCanvas): RoleCanvas;
16
- /**
17
- * Grow the role canvas to fit at least (newX, newY), preserving existing roles.
18
- * Mutates the role canvas in place and returns it.
19
- */
20
- export declare function increaseRoleCanvasSize(roleCanvas: RoleCanvas, newX: number, newY: number): RoleCanvas;
21
- /**
22
- * Set a role at a specific coordinate.
23
- * Expands the role canvas if necessary.
24
- */
25
- export declare function setRole(roleCanvas: RoleCanvas, x: number, y: number, role: CharRole): void;
26
- /**
27
- * Merge role canvases — same logic as mergeCanvases but for roles.
28
- * Non-null roles in overlays overwrite null roles in base.
29
- */
30
- export declare function mergeRoleCanvases(base: RoleCanvas, offset: DrawingCoord, ...overlays: RoleCanvas[]): RoleCanvas;
31
- /** Returns [maxX, maxY] — the highest valid indices in each dimension. */
32
- export declare function getCanvasSize(canvas: Canvas): [number, number];
33
- /**
34
- * Grow the canvas to fit at least (newX, newY), preserving existing content.
35
- * Mutates the canvas in place and returns it.
36
- */
37
- export declare function increaseSize(canvas: Canvas, newX: number, newY: number): Canvas;
38
- export declare function isJunctionChar(c: string): boolean;
39
- export declare function mergeJunctions(c1: string, c2: string): string;
40
- /**
41
- * Merge overlay canvases onto a base canvas at the given offset.
42
- * Non-space characters in overlays overwrite the base.
43
- * When both characters are Unicode junction chars, they're merged intelligently.
44
- */
45
- export declare function mergeCanvases(base: Canvas, offset: DrawingCoord, useAscii: boolean, ...overlays: Canvas[]): Canvas;
46
- /** Options for converting canvas to string with optional coloring. */
47
- export interface CanvasToStringOptions {
48
- /** Role canvas for applying colors. If not provided, output is plain text. */
49
- roleCanvas?: RoleCanvas;
50
- /** Color mode for terminal output. Default: 'none' */
51
- colorMode?: ColorMode;
52
- /** Theme colors for ASCII output. Uses default theme if not provided. */
53
- theme?: AsciiTheme;
54
- }
55
- /**
56
- * Convert the canvas to a multi-line string (row by row, left to right).
57
- * Optionally applies ANSI color codes based on character roles.
58
- */
59
- export declare function canvasToString(canvas: Canvas, options?: CanvasToStringOptions): string;
60
- /**
61
- * Flip the canvas vertically (mirror across the horizontal center).
62
- * Reverses row order within each column and remaps directional characters
63
- * (arrows, corners, junctions) so they point the correct way after flip.
64
- *
65
- * Used to transform a TD-rendered canvas into BT output.
66
- * Mutates the canvas in place and returns it.
67
- */
68
- export declare function flipCanvasVertically(canvas: Canvas): Canvas;
69
- /**
70
- * Flip the role canvas vertically to match flipCanvasVertically.
71
- * Mutates the role canvas in place and returns it.
72
- */
73
- export declare function flipRoleCanvasVertically(roleCanvas: RoleCanvas): RoleCanvas;
74
- /**
75
- * Draw text string onto the canvas starting at the given coordinate.
76
- * By default, preserves existing non-space characters (labels don't overwrite each other).
77
- * Set forceOverwrite=true to always overwrite (for box content).
78
- */
79
- export declare function drawText(canvas: Canvas, start: DrawingCoord, text: string, forceOverwrite?: boolean): void;
80
- /**
81
- * Set the canvas size to fit all grid columns and rows.
82
- * Called after layout to ensure the canvas covers the full drawing area.
83
- */
84
- export declare function setCanvasSizeToGrid(canvas: Canvas, columnWidth: Map<number, number>, rowHeight: Map<number, number>): void;
85
- /**
86
- * Set the role canvas size to match the grid dimensions.
87
- * Should be called alongside setCanvasSizeToGrid.
88
- */
89
- export declare function setRoleCanvasSizeToGrid(roleCanvas: RoleCanvas, columnWidth: Map<number, number>, rowHeight: Map<number, number>): void;
@@ -1,7 +0,0 @@
1
- import type { AsciiConfig, AsciiTheme, ColorMode } from './types.js';
2
- /**
3
- * Render a Mermaid class diagram to ASCII/Unicode text.
4
- *
5
- * Pipeline: parse → build boxes → level-based layout → draw boxes → draw relationships → string.
6
- */
7
- export declare function renderClassAscii(text: string, config: AsciiConfig, colorMode?: ColorMode, theme?: AsciiTheme): string;
@@ -1,12 +0,0 @@
1
- import type { MermaidGraph } from '../types.js';
2
- import type { AsciiGraph, AsciiConfig } from './types.js';
3
- /**
4
- * Convert a parsed MermaidGraph into an AsciiGraph ready for grid layout.
5
- *
6
- * Key mappings:
7
- * - MermaidGraph.nodes (Map) → ordered AsciiNode[] preserving insertion order
8
- * - MermaidGraph.edges → AsciiEdge[] with resolved node references
9
- * - MermaidGraph.subgraphs → AsciiSubgraph[] with parent/child tree
10
- * - Node labels are used as display names (not raw IDs)
11
- */
12
- export declare function convertToAsciiGraph(parsed: MermaidGraph, config: AsciiConfig): AsciiGraph;
@@ -1,66 +0,0 @@
1
- import type { Canvas, DrawingCoord, AsciiGraph, AsciiNode, AsciiEdge, AsciiSubgraph, AsciiEdgeStyle } from './types.js';
2
- /**
3
- * Draw a node using its shape type.
4
- * Returns a standalone canvas containing the rendered shape.
5
- *
6
- * For basic shapes (rectangle, rounded), uses grid-determined dimensions
7
- * to ensure consistent sizing across nodes in the same column.
8
- * For special shapes (diamond, circle, state pseudo-states, etc.),
9
- * uses shape-specific dimension calculation but centers the content
10
- * within the grid cell dimensions to ensure proper vertical alignment.
11
- */
12
- export declare function drawNode(node: AsciiNode, graph: AsciiGraph): Canvas;
13
- /**
14
- * Draw a node box with centered label text.
15
- * Returns a standalone canvas containing just the box.
16
- * Box size is determined by the grid column/row sizes for the node's position.
17
- */
18
- export declare function drawBox(node: AsciiNode, graph: AsciiGraph): Canvas;
19
- /**
20
- * Draw a multi-section box with horizontal dividers between sections.
21
- * Used by class diagrams (header | attributes | methods) and ER diagrams (header | attributes).
22
- * Each section is an array of text lines to render left-aligned with padding.
23
- *
24
- * @param sections - Array of sections, each section is an array of text lines
25
- * @param useAscii - true for ASCII chars, false for Unicode box-drawing
26
- * @param padding - horizontal padding inside the box (default 1)
27
- * @returns A standalone Canvas containing the multi-section box
28
- */
29
- export declare function drawMultiBox(sections: string[][], useAscii: boolean, padding?: number): Canvas;
30
- /**
31
- * Draw a line between two drawing coordinates using orthogonal Manhattan routing.
32
- * Returns the list of coordinates that were drawn on.
33
- * offsetFrom/offsetTo control how many cells to skip at the start/end.
34
- *
35
- * All lines use 90° bends only - no diagonal lines are produced.
36
- * For diagonal directions, uses horizontal-first routing (draws horizontal
37
- * segment, then vertical segment).
38
- */
39
- export declare function drawLine(canvas: Canvas, from: DrawingCoord, to: DrawingCoord, offsetFrom: number, offsetTo: number, useAscii: boolean, style?: AsciiEdgeStyle): DrawingCoord[];
40
- /**
41
- * Draw a complete arrow (edge) between two nodes.
42
- * Returns 6 separate canvases for layered compositing:
43
- * [path, boxStart, arrowHeadEnd, arrowHeadStart, corners, label]
44
- *
45
- * Supports bidirectional arrows via edge.hasArrowStart and edge.hasArrowEnd.
46
- */
47
- export declare function drawArrow(graph: AsciiGraph, edge: AsciiEdge): [Canvas, Canvas, Canvas, Canvas, Canvas, Canvas];
48
- /** Draw a subgraph border rectangle. */
49
- export declare function drawSubgraphBox(sg: AsciiSubgraph, graph: AsciiGraph): Canvas;
50
- /** Draw a subgraph label centered in its header area. Supports multi-line labels. */
51
- export declare function drawSubgraphLabel(sg: AsciiSubgraph, graph: AsciiGraph): [Canvas, DrawingCoord];
52
- /**
53
- * Main draw function — renders the entire graph onto the canvas.
54
- * Drawing order matters for correct layering:
55
- * 1. Subgraph borders (bottom layer)
56
- * 2. Node boxes
57
- * 3. Edge paths (lines)
58
- * 4. Edge corners
59
- * 5. Arrowheads
60
- * 6. Box-start junctions
61
- * 7. Edge labels
62
- * 8. Subgraph labels (top layer)
63
- *
64
- * Also fills the roleCanvas with character roles for colored output.
65
- */
66
- export declare function drawGraph(graph: AsciiGraph): Canvas;
@@ -1,48 +0,0 @@
1
- import type { AsciiGraph, EdgeBundle, GridCoord } from './types.js';
2
- /**
3
- * Analyze graph edges and create bundles for parallel links.
4
- *
5
- * Groups edges by:
6
- * - Fan-in: Multiple edges sharing the same target (A & B --> C)
7
- * - Fan-out: Multiple edges sharing the same source (A --> B & C)
8
- *
9
- * Only creates bundles when:
10
- * - Graph direction is TD (top-down) - LR routing handles merging naturally
11
- * - 2+ edges share the endpoint
12
- * - All edges have the same style (solid/dotted/thick)
13
- * - None of the edges have labels (labels would overlap at junction)
14
- * - Edges are not self-loops
15
- *
16
- * @returns Array of bundles. Each edge can belong to at most one bundle.
17
- */
18
- export declare function analyzeEdgeBundles(graph: AsciiGraph): EdgeBundle[];
19
- /**
20
- * Calculate the optimal junction point for a bundle.
21
- *
22
- * For fan-in (A & B --> C):
23
- * - Junction is placed between the sources and the target
24
- * - In TD: above the target, horizontally centered between sources
25
- * - In LR: left of the target, vertically centered between sources
26
- *
27
- * For fan-out (A --> B & C):
28
- * - Junction is placed between the source and the targets
29
- * - In TD: below the source, horizontally centered between targets
30
- * - In LR: right of the source, vertically centered between targets
31
- */
32
- export declare function calculateJunctionPoint(graph: AsciiGraph, bundle: EdgeBundle): GridCoord;
33
- /**
34
- * Route all edges in a bundle through the junction point.
35
- *
36
- * For fan-in bundles:
37
- * 1. Route each source → junction (stored in edge.pathToJunction)
38
- * 2. Route junction → target (stored in bundle.sharedPath)
39
- *
40
- * For fan-out bundles:
41
- * 1. Route source → junction (stored in bundle.sharedPath)
42
- * 2. Route junction → each target (stored in edge.pathToJunction)
43
- */
44
- export declare function routeBundledEdges(graph: AsciiGraph, bundle: EdgeBundle): void;
45
- /**
46
- * Process all bundles in a graph: calculate junction points and route edges.
47
- */
48
- export declare function processBundles(graph: AsciiGraph): void;
@@ -1,43 +0,0 @@
1
- import type { Direction, AsciiEdge, AsciiGraph } from './types.js';
2
- export declare function getOpposite(d: Direction): Direction;
3
- /** Compare directions by value (not reference). */
4
- export declare function dirEquals(a: Direction, b: Direction): boolean;
5
- /**
6
- * Determine 8-way direction from one coordinate to another.
7
- * Uses the coordinate difference to pick one of 8 cardinal/ordinal directions.
8
- */
9
- export declare function determineDirection(from: {
10
- x: number;
11
- y: number;
12
- }, to: {
13
- x: number;
14
- y: number;
15
- }): Direction;
16
- /**
17
- * Determine preferred and alternative start/end directions for an edge.
18
- * Returns [preferredStart, preferredEnd, alternativeStart, alternativeEnd].
19
- *
20
- * The edge routing tries both pairs and picks the shorter path.
21
- * Direction selection depends on relative node positions and graph direction (LR vs TD).
22
- */
23
- export declare function determineStartAndEndDir(edge: AsciiEdge, graphDirection: string): [Direction, Direction, Direction, Direction];
24
- /**
25
- * Determine the path for an edge by trying two candidate routes (preferred + alternative)
26
- * and picking the shorter one. Sets edge.path, edge.startDir, edge.endDir.
27
- *
28
- * When both A* paths fail (common for edges crossing subgraph boundaries), falls back
29
- * to a direct path using the start/end points. This ensures edges always have a path
30
- * for arrowhead rendering.
31
- *
32
- * Uses the effective direction for edge routing, respecting subgraph direction overrides
33
- * when both source and target are in the same subgraph.
34
- */
35
- export declare function determinePath(graph: AsciiGraph, edge: AsciiEdge): void;
36
- /**
37
- * Find the best line segment in an edge's path to place a label on.
38
- * Prefers vertical segments for TD/BT graphs and horizontal for LR/RL to avoid
39
- * label collisions when multiple edges share initial segments.
40
- * Falls back to the widest segment if none are suitable.
41
- * Also increases the column width at the label position to fit the text.
42
- */
43
- export declare function determineLabelLine(graph: AsciiGraph, edge: AsciiEdge): void;
@@ -1,7 +0,0 @@
1
- import type { AsciiConfig, AsciiTheme, ColorMode } from './types.js';
2
- /**
3
- * Render a Mermaid ER diagram to ASCII/Unicode text.
4
- *
5
- * Pipeline: parse → build boxes → component-aware layout → draw boxes → draw relationships → string.
6
- */
7
- export declare function renderErAscii(text: string, config: AsciiConfig, colorMode?: ColorMode, theme?: AsciiTheme): string;
@@ -1,56 +0,0 @@
1
- import type { GridCoord, DrawingCoord, Direction, AsciiGraph, AsciiNode, AsciiSubgraph } from './types.js';
2
- /**
3
- * Convert a grid coordinate to a drawing (character) coordinate.
4
- * Sums column widths up to the target column, and row heights up to the target row,
5
- * then centers within the cell.
6
- */
7
- export declare function gridToDrawingCoord(graph: AsciiGraph, c: GridCoord, dir?: Direction): DrawingCoord;
8
- /** Convert a path of grid coords to drawing coords. */
9
- export declare function lineToDrawing(graph: AsciiGraph, line: GridCoord[]): DrawingCoord[];
10
- /**
11
- * Reserve a 3x3 block in the grid for a node.
12
- * If the requested position is occupied, recursively shift by 4 grid units
13
- * (in the perpendicular direction based on effective direction) until a free spot is found.
14
- *
15
- * @param effectiveDir - Optional direction override. If not provided, uses the node's
16
- * effective direction (subgraph direction if in a subgraph with override,
17
- * otherwise graph direction).
18
- */
19
- export declare function reserveSpotInGrid(graph: AsciiGraph, node: AsciiNode, requested: GridCoord, effectiveDir?: 'LR' | 'TD'): GridCoord;
20
- /**
21
- * Set column widths and row heights for a node's 3x3 grid block.
22
- * Each node occupies 3 columns (border, content, border) and 3 rows.
23
- * Uses shape-aware dimensions to properly size non-rectangular shapes.
24
- */
25
- export declare function setColumnWidth(graph: AsciiGraph, node: AsciiNode): void;
26
- /** Ensure grid has width/height entries for all cells along an edge path. */
27
- export declare function increaseGridSizeForPath(graph: AsciiGraph, path: GridCoord[]): void;
28
- /**
29
- * Get the innermost subgraph that directly contains this node.
30
- * Returns null if node is not in any subgraph.
31
- */
32
- export declare function getNodeSubgraph(graph: AsciiGraph, node: AsciiNode): AsciiSubgraph | null;
33
- /**
34
- * Get the effective direction for a node's layout.
35
- * Returns the subgraph's direction override if the node is in a subgraph with one,
36
- * otherwise returns the graph-level direction.
37
- */
38
- export declare function getEffectiveDirection(graph: AsciiGraph, node: AsciiNode): 'LR' | 'TD';
39
- export declare function calculateSubgraphBoundingBoxes(graph: AsciiGraph): void;
40
- /**
41
- * Offset all drawing coordinates so subgraph borders don't go negative.
42
- * If any subgraph has negative min coordinates, shift everything positive.
43
- */
44
- export declare function offsetDrawingForSubgraphs(graph: AsciiGraph): void;
45
- /**
46
- * createMapping performs the full grid layout:
47
- * 1. Place root nodes on the grid
48
- * 2. Place child nodes level by level
49
- * 3. Compute column widths and row heights
50
- * 4. Run A* pathfinding for all edges
51
- * 5. Determine label placement
52
- * 6. Convert grid coords → drawing coords
53
- * 7. Generate node box drawings
54
- * 8. Calculate subgraph bounding boxes
55
- */
56
- export declare function createMapping(graph: AsciiGraph): void;
@@ -1,65 +0,0 @@
1
- import { detectColorMode, DEFAULT_ASCII_THEME } from './ansi.js';
2
- import type { AsciiTheme, ColorMode } from './types.js';
3
- import type { Direction } from '../types.js';
4
- export type { AsciiTheme, ColorMode };
5
- export { DEFAULT_ASCII_THEME, detectColorMode };
6
- export interface AsciiRenderOptions {
7
- /** true = ASCII chars (+,-,|,>), false = Unicode box-drawing (┌,─,│,►). Default: false */
8
- useAscii?: boolean;
9
- /** Horizontal spacing between nodes. Default: 5 */
10
- paddingX?: number;
11
- /** Vertical spacing between nodes. Default: 5 */
12
- paddingY?: number;
13
- /** Padding inside node boxes. Default: 1 */
14
- boxBorderPadding?: number;
15
- /**
16
- * Force the layout direction, overriding the direction parsed from the
17
- * diagram source. Applies to the flowchart + state-diagram grid pipeline
18
- * (sequence/class/ER/xychart renderers ignore it). Useful for re-fitting a
19
- * wide `LR` graph into a narrow viewport by laying it out top-down.
20
- * Default: undefined (use the source's own direction).
21
- */
22
- direction?: Direction;
23
- /**
24
- * Color mode for output.
25
- * - 'none': No colors (plain text)
26
- * - 'auto': Auto-detect (terminal ANSI capabilities, or HTML in browsers)
27
- * - 'ansi16': 16-color ANSI
28
- * - 'ansi256': 256-color xterm
29
- * - 'truecolor': 24-bit RGB
30
- * - 'html': HTML <span> tags with inline color styles (for browser rendering)
31
- * Default: 'auto'
32
- */
33
- colorMode?: ColorMode | 'auto';
34
- /** Theme colors for ASCII output. Uses default theme if not provided. */
35
- theme?: Partial<AsciiTheme>;
36
- }
37
- /**
38
- * Render Mermaid diagram text to an ASCII/Unicode string.
39
- *
40
- * Synchronous — no async layout engine needed (unlike the SVG renderer).
41
- * Auto-detects diagram type from the header line and dispatches to
42
- * the appropriate renderer.
43
- *
44
- * @param text - Mermaid source text (any supported diagram type)
45
- * @param options - Rendering options
46
- * @returns Multi-line ASCII/Unicode string
47
- *
48
- * @example
49
- * ```ts
50
- * const result = renderMermaidAscii(`
51
- * graph LR
52
- * A --> B --> C
53
- * `, { useAscii: true })
54
- *
55
- * // Output:
56
- * // +---+ +---+ +---+
57
- * // | | | | | |
58
- * // | A |---->| B |---->| C |
59
- * // | | | | | |
60
- * // +---+ +---+ +---+
61
- * ```
62
- */
63
- export declare function renderMermaidASCII(text: string, options?: AsciiRenderOptions): string;
64
- /** Lowercase alias kept as the public name used by the pi-utils wrapper. */
65
- export declare const renderMermaidAscii: typeof renderMermaidASCII;