@oh-my-pi/pi-utils 16.0.6 → 16.0.8

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 (87) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/types/mermaid-ascii.d.ts +1 -1
  3. package/dist/types/vendor/mermaid-ascii/ascii/ansi.d.ts +41 -0
  4. package/dist/types/vendor/mermaid-ascii/ascii/canvas.d.ts +89 -0
  5. package/dist/types/vendor/mermaid-ascii/ascii/class-diagram.d.ts +7 -0
  6. package/dist/types/vendor/mermaid-ascii/ascii/converter.d.ts +12 -0
  7. package/dist/types/vendor/mermaid-ascii/ascii/draw.d.ts +66 -0
  8. package/dist/types/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +48 -0
  9. package/dist/types/vendor/mermaid-ascii/ascii/edge-routing.d.ts +43 -0
  10. package/dist/types/vendor/mermaid-ascii/ascii/er-diagram.d.ts +7 -0
  11. package/dist/types/vendor/mermaid-ascii/ascii/grid.d.ts +56 -0
  12. package/dist/types/vendor/mermaid-ascii/ascii/index.d.ts +65 -0
  13. package/dist/types/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +27 -0
  14. package/dist/types/vendor/mermaid-ascii/ascii/pathfinder.d.ts +17 -0
  15. package/dist/types/vendor/mermaid-ascii/ascii/sequence.d.ts +7 -0
  16. package/dist/types/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +11 -0
  17. package/dist/types/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +34 -0
  18. package/dist/types/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +11 -0
  19. package/dist/types/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +11 -0
  20. package/dist/types/vendor/mermaid-ascii/ascii/shapes/index.d.ts +26 -0
  21. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +31 -0
  22. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +11 -0
  23. package/dist/types/vendor/mermaid-ascii/ascii/shapes/special.d.ts +59 -0
  24. package/dist/types/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +17 -0
  25. package/dist/types/vendor/mermaid-ascii/ascii/shapes/state.d.ts +30 -0
  26. package/dist/types/vendor/mermaid-ascii/ascii/shapes/types.d.ts +55 -0
  27. package/dist/types/vendor/mermaid-ascii/ascii/types.d.ts +206 -0
  28. package/dist/types/vendor/mermaid-ascii/ascii/validate.d.ts +51 -0
  29. package/dist/types/vendor/mermaid-ascii/ascii/xychart.d.ts +2 -0
  30. package/dist/types/vendor/mermaid-ascii/class/parser.d.ts +6 -0
  31. package/dist/types/vendor/mermaid-ascii/class/types.d.ts +102 -0
  32. package/dist/types/vendor/mermaid-ascii/er/parser.d.ts +6 -0
  33. package/dist/types/vendor/mermaid-ascii/er/types.d.ts +76 -0
  34. package/dist/types/vendor/mermaid-ascii/index.d.ts +1 -0
  35. package/dist/types/vendor/mermaid-ascii/multiline-utils.d.ts +9 -0
  36. package/dist/types/vendor/mermaid-ascii/parser.d.ts +7 -0
  37. package/dist/types/vendor/mermaid-ascii/sequence/parser.d.ts +6 -0
  38. package/dist/types/vendor/mermaid-ascii/sequence/types.d.ts +130 -0
  39. package/dist/types/vendor/mermaid-ascii/text-metrics.d.ts +21 -0
  40. package/dist/types/vendor/mermaid-ascii/types.d.ts +114 -0
  41. package/dist/types/vendor/mermaid-ascii/xychart/colors.d.ts +25 -0
  42. package/dist/types/vendor/mermaid-ascii/xychart/parser.d.ts +6 -0
  43. package/dist/types/vendor/mermaid-ascii/xychart/types.d.ts +145 -0
  44. package/package.json +2 -3
  45. package/src/mermaid-ascii.ts +1 -1
  46. package/src/vendor/mermaid-ascii/NOTICE +33 -0
  47. package/src/vendor/mermaid-ascii/ascii/ansi.ts +409 -0
  48. package/src/vendor/mermaid-ascii/ascii/canvas.ts +476 -0
  49. package/src/vendor/mermaid-ascii/ascii/class-diagram.ts +699 -0
  50. package/src/vendor/mermaid-ascii/ascii/converter.ts +271 -0
  51. package/src/vendor/mermaid-ascii/ascii/draw.ts +1382 -0
  52. package/src/vendor/mermaid-ascii/ascii/edge-bundling.ts +328 -0
  53. package/src/vendor/mermaid-ascii/ascii/edge-routing.ts +297 -0
  54. package/src/vendor/mermaid-ascii/ascii/er-diagram.ts +441 -0
  55. package/src/vendor/mermaid-ascii/ascii/grid.ts +578 -0
  56. package/src/vendor/mermaid-ascii/ascii/index.ts +187 -0
  57. package/src/vendor/mermaid-ascii/ascii/multiline-utils.ts +78 -0
  58. package/src/vendor/mermaid-ascii/ascii/pathfinder.ts +215 -0
  59. package/src/vendor/mermaid-ascii/ascii/sequence.ts +460 -0
  60. package/src/vendor/mermaid-ascii/ascii/shapes/circle.ts +27 -0
  61. package/src/vendor/mermaid-ascii/ascii/shapes/corners.ts +127 -0
  62. package/src/vendor/mermaid-ascii/ascii/shapes/diamond.ts +27 -0
  63. package/src/vendor/mermaid-ascii/ascii/shapes/hexagon.ts +27 -0
  64. package/src/vendor/mermaid-ascii/ascii/shapes/index.ts +101 -0
  65. package/src/vendor/mermaid-ascii/ascii/shapes/rectangle.ts +175 -0
  66. package/src/vendor/mermaid-ascii/ascii/shapes/rounded.ts +27 -0
  67. package/src/vendor/mermaid-ascii/ascii/shapes/special.ts +296 -0
  68. package/src/vendor/mermaid-ascii/ascii/shapes/stadium.ts +114 -0
  69. package/src/vendor/mermaid-ascii/ascii/shapes/state.ts +192 -0
  70. package/src/vendor/mermaid-ascii/ascii/shapes/types.ts +73 -0
  71. package/src/vendor/mermaid-ascii/ascii/types.ts +273 -0
  72. package/src/vendor/mermaid-ascii/ascii/validate.ts +120 -0
  73. package/src/vendor/mermaid-ascii/ascii/xychart.ts +875 -0
  74. package/src/vendor/mermaid-ascii/class/parser.ts +290 -0
  75. package/src/vendor/mermaid-ascii/class/types.ts +121 -0
  76. package/src/vendor/mermaid-ascii/er/parser.ts +181 -0
  77. package/src/vendor/mermaid-ascii/er/types.ts +91 -0
  78. package/src/vendor/mermaid-ascii/index.ts +14 -0
  79. package/src/vendor/mermaid-ascii/multiline-utils.ts +30 -0
  80. package/src/vendor/mermaid-ascii/parser.ts +645 -0
  81. package/src/vendor/mermaid-ascii/sequence/parser.ts +207 -0
  82. package/src/vendor/mermaid-ascii/sequence/types.ts +146 -0
  83. package/src/vendor/mermaid-ascii/text-metrics.ts +71 -0
  84. package/src/vendor/mermaid-ascii/types.ts +164 -0
  85. package/src/vendor/mermaid-ascii/xychart/colors.ts +140 -0
  86. package/src/vendor/mermaid-ascii/xychart/parser.ts +115 -0
  87. package/src/vendor/mermaid-ascii/xychart/types.ts +150 -0
@@ -0,0 +1,409 @@
1
+ // ============================================================================
2
+ // ASCII renderer — color utilities
3
+ //
4
+ // Provides color output for themed ASCII diagrams.
5
+ // Supports ANSI terminal modes (16/256/truecolor) and HTML <span> tags
6
+ // for browser rendering.
7
+ // ============================================================================
8
+
9
+ import type { CharRole, AsciiTheme, ColorMode } from './types'
10
+
11
+ declare const document: unknown
12
+
13
+ // ============================================================================
14
+ // Default theme — matches SVG theme colors for consistency
15
+ // ============================================================================
16
+
17
+ /**
18
+ * Default ASCII theme derived from the SVG renderer's color palette.
19
+ * Uses the same mixing ratios to maintain visual consistency.
20
+ */
21
+ export const DEFAULT_ASCII_THEME: AsciiTheme = {
22
+ fg: '#27272a', // zinc-800 — primary text
23
+ border: '#a1a1aa', // zinc-400 — node borders (12% mix)
24
+ line: '#71717a', // zinc-500 — edge lines (35% mix)
25
+ arrow: '#52525b', // zinc-600 — arrowheads (60% mix)
26
+ corner: '#71717a', // same as line
27
+ junction: '#a1a1aa', // same as border
28
+ }
29
+
30
+ // ============================================================================
31
+ // Color mode detection
32
+ // ============================================================================
33
+
34
+ /**
35
+ * Detect the best color mode for the current environment.
36
+ *
37
+ * Terminal detection order:
38
+ * 1. COLORTERM=truecolor or COLORTERM=24bit → truecolor
39
+ * 2. TERM contains "256color" → ansi256
40
+ * 3. TERM is set and not "dumb" → ansi16
41
+ *
42
+ * Browser: returns 'html' (uses <span> tags with inline styles).
43
+ * Unknown/piped: returns 'none'.
44
+ */
45
+ export function detectColorMode(): ColorMode {
46
+ // Check if we're in a Node.js-like environment with process object
47
+ // Use globalThis to safely check for process without TypeScript errors
48
+ const proc = (globalThis as { process?: { stdout?: { isTTY?: boolean }, env?: Record<string, string | undefined> } }).process
49
+
50
+ if (proc) {
51
+ // Check if stdout is a TTY (not piped/redirected)
52
+ if (!proc.stdout?.isTTY) {
53
+ return 'none'
54
+ }
55
+
56
+ const colorTerm = proc.env?.COLORTERM?.toLowerCase() ?? ''
57
+ const term = proc.env?.TERM?.toLowerCase() ?? ''
58
+
59
+ // True color support
60
+ if (colorTerm === 'truecolor' || colorTerm === '24bit') {
61
+ return 'truecolor'
62
+ }
63
+
64
+ // 256 color support
65
+ if (term.includes('256color') || term.includes('256')) {
66
+ return 'ansi256'
67
+ }
68
+
69
+ // Basic color support
70
+ if (term && term !== 'dumb') {
71
+ return 'ansi16'
72
+ }
73
+
74
+ return 'none'
75
+ }
76
+
77
+ // No process object → browser environment → use HTML color output
78
+ if (typeof document !== 'undefined') {
79
+ return 'html'
80
+ }
81
+
82
+ return 'none'
83
+ }
84
+
85
+ // ============================================================================
86
+ // Hex color parsing
87
+ // ============================================================================
88
+
89
+ /**
90
+ * Parse a hex color string to RGB values.
91
+ * Supports both 3-char (#RGB) and 6-char (#RRGGBB) formats.
92
+ */
93
+ function parseHex(hex: string): { r: number; g: number; b: number } {
94
+ const h = hex.replace('#', '')
95
+ if (h.length === 3) {
96
+ return {
97
+ r: parseInt(h[0]! + h[0]!, 16),
98
+ g: parseInt(h[1]! + h[1]!, 16),
99
+ b: parseInt(h[2]! + h[2]!, 16),
100
+ }
101
+ }
102
+ return {
103
+ r: parseInt(h.substring(0, 2), 16),
104
+ g: parseInt(h.substring(2, 4), 16),
105
+ b: parseInt(h.substring(4, 6), 16),
106
+ }
107
+ }
108
+
109
+ // ============================================================================
110
+ // ANSI escape code generation
111
+ // ============================================================================
112
+
113
+ /** ANSI escape sequence prefix */
114
+ const ESC = '\x1b['
115
+ /** Reset all attributes */
116
+ const RESET = `${ESC}0m`
117
+
118
+ /**
119
+ * Generate ANSI foreground color escape sequence for 24-bit true color.
120
+ * Format: ESC[38;2;R;G;Bm
121
+ */
122
+ function truecolorFg(hex: string): string {
123
+ const { r, g, b } = parseHex(hex)
124
+ return `${ESC}38;2;${r};${g};${b}m`
125
+ }
126
+
127
+ /**
128
+ * Find the closest 256-color palette index for an RGB color.
129
+ * The 256-color palette has:
130
+ * - 0-15: Standard colors (duplicates of 16-color)
131
+ * - 16-231: 6x6x6 color cube (216 colors)
132
+ * - 232-255: Grayscale ramp (24 shades)
133
+ */
134
+ function rgbTo256(r: number, g: number, b: number): number {
135
+ // Check if it's close to grayscale
136
+ const avg = (r + g + b) / 3
137
+ const maxDiff = Math.max(Math.abs(r - avg), Math.abs(g - avg), Math.abs(b - avg))
138
+
139
+ if (maxDiff < 10) {
140
+ // Use grayscale ramp (232-255)
141
+ // Each step is ~10.625 (256/24)
142
+ const gray = Math.round((avg / 255) * 23)
143
+ return 232 + Math.min(23, Math.max(0, gray))
144
+ }
145
+
146
+ // Use 6x6x6 color cube (16-231)
147
+ // Each channel maps to 0-5: 0, 95, 135, 175, 215, 255
148
+ const toIndex = (v: number): number => {
149
+ if (v < 48) return 0
150
+ if (v < 115) return 1
151
+ return Math.min(5, Math.floor((v - 35) / 40))
152
+ }
153
+
154
+ const ri = toIndex(r)
155
+ const gi = toIndex(g)
156
+ const bi = toIndex(b)
157
+
158
+ return 16 + (36 * ri) + (6 * gi) + bi
159
+ }
160
+
161
+ /**
162
+ * Generate ANSI foreground color escape sequence for 256-color mode.
163
+ * Format: ESC[38;5;Nm
164
+ */
165
+ function ansi256Fg(hex: string): string {
166
+ const { r, g, b } = parseHex(hex)
167
+ const index = rgbTo256(r, g, b)
168
+ return `${ESC}38;5;${index}m`
169
+ }
170
+
171
+ /**
172
+ * Map an RGB color to the closest 16-color ANSI code.
173
+ * Returns the foreground color escape sequence.
174
+ *
175
+ * Standard 16 colors:
176
+ * 0=black, 1=red, 2=green, 3=yellow, 4=blue, 5=magenta, 6=cyan, 7=white
177
+ * 8-15 = bright versions
178
+ */
179
+ function ansi16Fg(hex: string): string {
180
+ const { r, g, b } = parseHex(hex)
181
+ const luma = 0.299 * r + 0.587 * g + 0.114 * b
182
+
183
+ // Determine brightness (use bright colors for better visibility)
184
+ const bright = luma > 100 ? 0 : 60 // 60 = bright variant offset
185
+
186
+ // Determine base color based on dominant channel
187
+ let code: number
188
+ if (r > 180 && g < 100 && b < 100) code = 31 // red
189
+ else if (g > 180 && r < 100 && b < 100) code = 32 // green
190
+ else if (r > 150 && g > 150 && b < 100) code = 33 // yellow
191
+ else if (b > 180 && r < 100 && g < 100) code = 34 // blue
192
+ else if (r > 150 && b > 150 && g < 100) code = 35 // magenta
193
+ else if (g > 150 && b > 150 && r < 100) code = 36 // cyan
194
+ else if (luma > 200) code = 37 // white
195
+ else if (luma < 50) code = 30 // black
196
+ else code = 37 // default to white for grays
197
+
198
+ return `${ESC}${code + bright}m`
199
+ }
200
+
201
+ // ============================================================================
202
+ // HTML color output (for browser rendering)
203
+ // ============================================================================
204
+
205
+ /** Escape characters that would break HTML output. */
206
+ function escapeHtml(text: string): string {
207
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
208
+ }
209
+
210
+ /** Wrap text in a <span> with an inline color style. */
211
+ function htmlSpan(hex: string, text: string): string {
212
+ return `<span style="color:${hex}">${escapeHtml(text)}</span>`
213
+ }
214
+
215
+ // ============================================================================
216
+ // Role → color mapping
217
+ // ============================================================================
218
+
219
+ /**
220
+ * Get the color for a character role from the theme.
221
+ */
222
+ function getRoleColor(role: CharRole, theme: AsciiTheme): string {
223
+ switch (role) {
224
+ case 'text': return theme.fg
225
+ case 'border': return theme.border
226
+ case 'line': return theme.line
227
+ case 'arrow': return theme.arrow
228
+ case 'corner': return theme.corner ?? theme.line
229
+ case 'junction': return theme.junction ?? theme.border
230
+ default: return theme.fg
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Generate the ANSI escape sequence for a role color.
236
+ */
237
+ export function getAnsiColor(role: CharRole, theme: AsciiTheme, mode: ColorMode): string {
238
+ if (mode === 'none') return ''
239
+
240
+ const hex = getRoleColor(role, theme)
241
+
242
+ switch (mode) {
243
+ case 'truecolor': return truecolorFg(hex)
244
+ case 'ansi256': return ansi256Fg(hex)
245
+ case 'ansi16': return ansi16Fg(hex)
246
+ default: return ''
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Get the ANSI reset sequence.
252
+ */
253
+ export function getAnsiReset(mode: ColorMode): string {
254
+ return mode === 'none' ? '' : RESET
255
+ }
256
+
257
+ /**
258
+ * Wrap a character with ANSI color codes based on its role.
259
+ */
260
+ export function colorizeChar(
261
+ char: string,
262
+ role: CharRole | null,
263
+ theme: AsciiTheme,
264
+ mode: ColorMode,
265
+ ): string {
266
+ if (mode === 'none' || role === null || char === ' ') {
267
+ return char
268
+ }
269
+
270
+ const colorCode = getAnsiColor(role, theme, mode)
271
+ return `${colorCode}${char}${RESET}`
272
+ }
273
+
274
+ /**
275
+ * Colorize an entire line efficiently by grouping consecutive same-role characters.
276
+ * This reduces the number of escape sequences (ANSI) or span tags (HTML) in the output.
277
+ */
278
+ export function colorizeLine(
279
+ chars: string[],
280
+ roles: (CharRole | null)[],
281
+ theme: AsciiTheme,
282
+ mode: ColorMode,
283
+ ): string {
284
+ if (mode === 'none') {
285
+ return chars.join('')
286
+ }
287
+
288
+ if (mode === 'html') {
289
+ return colorizeLineHtml(chars, roles, theme)
290
+ }
291
+
292
+ let result = ''
293
+ let currentRole: CharRole | null = null
294
+ let buffer = ''
295
+
296
+ for (let i = 0; i < chars.length; i++) {
297
+ const char = chars[i]!
298
+ const role = roles[i] ?? null
299
+
300
+ // Whitespace doesn't need coloring
301
+ if (char === ' ') {
302
+ // Flush any buffered characters (with or without color)
303
+ if (buffer.length > 0) {
304
+ if (currentRole !== null) {
305
+ result += getAnsiColor(currentRole, theme, mode) + buffer + RESET
306
+ } else {
307
+ result += buffer
308
+ }
309
+ buffer = ''
310
+ currentRole = null
311
+ }
312
+ result += char
313
+ continue
314
+ }
315
+
316
+ // Same role as previous — accumulate
317
+ if (role === currentRole) {
318
+ buffer += char
319
+ continue
320
+ }
321
+
322
+ // Role changed — flush buffer (with or without color) and start new
323
+ if (buffer.length > 0) {
324
+ if (currentRole !== null) {
325
+ result += getAnsiColor(currentRole, theme, mode) + buffer + RESET
326
+ } else {
327
+ result += buffer
328
+ }
329
+ }
330
+ buffer = char
331
+ currentRole = role
332
+ }
333
+
334
+ // Flush remaining buffer
335
+ if (buffer.length > 0 && currentRole !== null) {
336
+ result += getAnsiColor(currentRole, theme, mode) + buffer + RESET
337
+ } else if (buffer.length > 0) {
338
+ result += buffer
339
+ }
340
+
341
+ return result
342
+ }
343
+
344
+ /**
345
+ * HTML-specific line colorization.
346
+ * Groups consecutive same-role characters into <span> tags with inline color styles.
347
+ * Whitespace is emitted bare (no wrapping) to keep output compact.
348
+ */
349
+ function colorizeLineHtml(
350
+ chars: string[],
351
+ roles: (CharRole | null)[],
352
+ theme: AsciiTheme,
353
+ ): string {
354
+ let result = ''
355
+ let currentRole: CharRole | null = null
356
+ let buffer = ''
357
+
358
+ const flush = () => {
359
+ if (buffer.length === 0) return
360
+ if (currentRole !== null) {
361
+ result += htmlSpan(getRoleColor(currentRole, theme), buffer)
362
+ } else {
363
+ result += escapeHtml(buffer)
364
+ }
365
+ buffer = ''
366
+ currentRole = null
367
+ }
368
+
369
+ for (let i = 0; i < chars.length; i++) {
370
+ const char = chars[i]!
371
+ const role = roles[i] ?? null
372
+
373
+ if (char === ' ') {
374
+ flush()
375
+ result += ' '
376
+ continue
377
+ }
378
+
379
+ if (role === currentRole) {
380
+ buffer += char
381
+ continue
382
+ }
383
+
384
+ flush()
385
+ buffer = char
386
+ currentRole = role
387
+ }
388
+
389
+ flush()
390
+ return result
391
+ }
392
+
393
+ /**
394
+ * Colorize a text string with a direct hex color.
395
+ * Used by renderers that need per-cell color control (e.g. multi-series xychart).
396
+ * Handles all output modes: ANSI (16/256/truecolor) and HTML.
397
+ */
398
+ export function colorizeText(text: string, hex: string, mode: ColorMode): string {
399
+ if (mode === 'none' || text.length === 0) return text
400
+ if (mode === 'html') return htmlSpan(hex, text)
401
+ let code: string
402
+ switch (mode) {
403
+ case 'truecolor': code = truecolorFg(hex); break
404
+ case 'ansi256': code = ansi256Fg(hex); break
405
+ case 'ansi16': code = ansi16Fg(hex); break
406
+ default: return text
407
+ }
408
+ return `${code}${text}${RESET}`
409
+ }