@mks2508/better-logger 3.0.0 → 4.0.0

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/.claude/settings.local.json +3 -1
  2. package/CHANGELOG.json +98 -1
  3. package/dist/Logger.d.ts +24 -12
  4. package/dist/Logger.d.ts.map +1 -1
  5. package/dist/chunks/{Logger-C126NTrD.js → Logger-D-gmmgR2.js} +207 -18
  6. package/dist/chunks/Logger-D-gmmgR2.js.map +1 -0
  7. package/dist/chunks/Logger-D7cfaz15.js +2 -0
  8. package/dist/chunks/Logger-D7cfaz15.js.map +1 -0
  9. package/dist/chunks/{environment-TI2ByCPT.js → environment-COWvu6Wz.js} +466 -98
  10. package/dist/chunks/environment-COWvu6Wz.js.map +1 -0
  11. package/dist/chunks/environment-C_8J-zQ_.js +4 -0
  12. package/dist/chunks/environment-C_8J-zQ_.js.map +1 -0
  13. package/dist/chunks/{formatting-CuNUqGks.js → formatting-CYjT9yhO.js} +2 -2
  14. package/dist/chunks/{formatting-CuNUqGks.js.map → formatting-CYjT9yhO.js.map} +1 -1
  15. package/dist/chunks/{formatting-Blwy-f0W.js → formatting-Cg5YhB9Y.js} +2 -2
  16. package/dist/chunks/{formatting-Blwy-f0W.js.map → formatting-Cg5YhB9Y.js.map} +1 -1
  17. package/dist/core.cjs +1 -1
  18. package/dist/core.js +2 -2
  19. package/dist/exports.cjs +1 -1
  20. package/dist/exports.js +2 -2
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.ts +10 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +151 -20
  26. package/dist/index.js.map +1 -1
  27. package/dist/styling.cjs +1 -1
  28. package/dist/styling.js +3 -3
  29. package/dist/terminal/color-converter.d.ts +73 -0
  30. package/dist/terminal/color-converter.d.ts.map +1 -0
  31. package/dist/terminal/formatter.d.ts +20 -0
  32. package/dist/terminal/formatter.d.ts.map +1 -0
  33. package/dist/terminal/terminal-renderer.d.ts +20 -6
  34. package/dist/terminal/terminal-renderer.d.ts.map +1 -1
  35. package/dist/types/core.d.ts +61 -0
  36. package/dist/types/core.d.ts.map +1 -1
  37. package/dist/types/index.d.ts +1 -1
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/utils/environment-detector.d.ts +10 -0
  40. package/dist/utils/environment-detector.d.ts.map +1 -1
  41. package/dist/writers/BufferWriter.d.ts +96 -0
  42. package/dist/writers/BufferWriter.d.ts.map +1 -0
  43. package/dist/writers/index.d.ts +6 -0
  44. package/dist/writers/index.d.ts.map +1 -0
  45. package/package.json +1 -1
  46. package/packages/core/package.json +1 -1
  47. package/packages/styling/package.json +2 -2
  48. package/src/Logger.ts +63 -21
  49. package/src/index.ts +61 -2
  50. package/src/terminal/color-converter.ts +315 -0
  51. package/src/terminal/formatter.ts +236 -0
  52. package/src/terminal/terminal-renderer.ts +74 -79
  53. package/src/types/core.ts +68 -0
  54. package/src/types/index.ts +5 -0
  55. package/src/utils/environment-detector.ts +23 -1
  56. package/src/writers/BufferWriter.ts +157 -0
  57. package/src/writers/index.ts +6 -0
  58. package/dist/chunks/Logger-BCfx_yCK.js +0 -2
  59. package/dist/chunks/Logger-BCfx_yCK.js.map +0 -1
  60. package/dist/chunks/Logger-C126NTrD.js.map +0 -1
  61. package/dist/chunks/environment-Ba5kShbx.js +0 -4
  62. package/dist/chunks/environment-Ba5kShbx.js.map +0 -1
  63. package/dist/chunks/environment-TI2ByCPT.js.map +0 -1
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @fileoverview Output writers for better-logger
3
+ * @version 4.0.0
4
+ */
5
+ export { BufferWriter, type BufferEntry } from './BufferWriter.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/writers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mks2508/better-logger",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "type": "module",
5
5
  "description": "Logger avanzado para consola con estilos CSS, temas adaptativos, badges automáticos, SVG, animaciones, CLI integrado y soporte dual browser/terminal con colores ANSI",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mks2508/better-logger-core",
3
- "version": "0.15.0-alpha.1",
3
+ "version": "0.16.1-alpha.1",
4
4
  "type": "module",
5
5
  "description": "Universal core logging functionality for Better Logger - Works in both Node.js and browser environments",
6
6
  "main": "dist/index.cjs",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mks2508/better-logger-styling",
3
- "version": "0.13.0-alpha.1",
3
+ "version": "0.16.0-alpha.1",
4
4
  "type": "module",
5
5
  "description": "Advanced CSS styling system for Better Logger - Console styling with gradients, animations and SVG support",
6
6
  "main": "dist/index.js",
@@ -36,6 +36,6 @@
36
36
  "README.md"
37
37
  ],
38
38
  "peerDependencies": {
39
- "@mks2508/better-logger-core": "^0.13.0-alpha.1"
39
+ "@mks2508/better-logger-core": "^0.16.0-alpha.1"
40
40
  }
41
41
  }
package/src/Logger.ts CHANGED
@@ -36,7 +36,8 @@ import { TransportManager } from './transports/index.js';
36
36
  import { parseStackTrace } from './utils/stackTrace.js';
37
37
  import { formatTimestamp } from './utils/timestamps.js';
38
38
  import { createStyledOutput, setupThemeChangeListener } from './utils/output.js';
39
- import { getEnvironment } from './utils/environment-detector.js';
39
+ import { getEnvironment, getColorCapability } from './utils/environment-detector.js';
40
+ import { formatBadge } from './terminal/formatter.js';
40
41
 
41
42
  // Styling imports
42
43
  import {
@@ -420,9 +421,9 @@ export class Logger {
420
421
  *
421
422
  * @since 0.3.0
422
423
  */
423
- hideTimestamp(): void {
424
+ hideTimestamp(): this {
424
425
  this.displaySettings.showTimestamp = false;
425
- this.success('Timestamp hidden');
426
+ return this;
426
427
  }
427
428
 
428
429
  /**
@@ -434,9 +435,9 @@ export class Logger {
434
435
  *
435
436
  * @since 0.3.0
436
437
  */
437
- showTimestamp(): void {
438
+ showTimestamp(): this {
438
439
  this.displaySettings.showTimestamp = true;
439
- this.success('Timestamp shown');
440
+ return this;
440
441
  }
441
442
 
442
443
  /**
@@ -448,9 +449,9 @@ export class Logger {
448
449
  *
449
450
  * @since 0.3.0
450
451
  */
451
- hideLocation(): void {
452
+ hideLocation(): this {
452
453
  this.displaySettings.showLocation = false;
453
- this.success('Location info hidden');
454
+ return this;
454
455
  }
455
456
 
456
457
  /**
@@ -462,9 +463,9 @@ export class Logger {
462
463
  *
463
464
  * @since 0.3.0
464
465
  */
465
- showLocation(): void {
466
+ showLocation(): this {
466
467
  this.displaySettings.showLocation = true;
467
- this.success('Location info shown');
468
+ return this;
468
469
  }
469
470
 
470
471
  /**
@@ -477,9 +478,9 @@ export class Logger {
477
478
  *
478
479
  * @since 0.3.0
479
480
  */
480
- hideBadges(): void {
481
+ hideBadges(): this {
481
482
  this.displaySettings.showBadges = false;
482
- this.success('Badges hidden');
483
+ return this;
483
484
  }
484
485
 
485
486
  /**
@@ -492,9 +493,9 @@ export class Logger {
492
493
  *
493
494
  * @since 0.3.0
494
495
  */
495
- showBadges(): void {
496
+ showBadges(): this {
496
497
  this.displaySettings.showBadges = true;
497
- this.success('Badges shown');
498
+ return this;
498
499
  }
499
500
 
500
501
  /**
@@ -942,12 +943,8 @@ export class Logger {
942
943
  const groupIndent = ' '.repeat(this.groupDepth);
943
944
  const finalFormat = groupIndent + format;
944
945
 
945
- // Output to console
946
- if (additionalArgs.length > 0) {
947
- console.log(finalFormat, ...styles, ...additionalArgs);
948
- } else {
949
- console.log(finalFormat, ...styles);
950
- }
946
+ // Output via configured writer (console by default)
947
+ this.writeOutput(finalFormat, level, styles, additionalArgs);
951
948
 
952
949
  // Update export handler group info
953
950
  if (this.exportHandler) {
@@ -999,11 +996,13 @@ export class Logger {
999
996
  if (!this.shouldLog(level)) return;
1000
997
 
1001
998
  let prefix = '';
999
+ const colorCapability = getColorCapability();
1000
+
1002
1001
  if (bindings.badges?.length) {
1003
- prefix += bindings.badges.map(b => `[${b}]`).join('');
1002
+ prefix += bindings.badges.map(b => formatBadge(b, 'pill', colorCapability, '#00ff88')).join(' ') + ' ';
1004
1003
  }
1005
1004
  if (bindings.scope) {
1006
- prefix += `[${bindings.scope}] `;
1005
+ prefix += formatBadge(bindings.scope, 'pill', colorCapability, '#00ffff') + ' ';
1007
1006
  }
1008
1007
 
1009
1008
  if (prefix && args.length > 0) {
@@ -1463,6 +1462,49 @@ export class Logger {
1463
1462
  }
1464
1463
  }
1465
1464
 
1465
+ // ===== OUTPUT WRITER SYSTEM =====
1466
+
1467
+ /**
1468
+ * Writes formatted output to the configured destination.
1469
+ * Respects outputMode configuration for console, silent, or custom output.
1470
+ *
1471
+ * @private
1472
+ * @param {string} message - Formatted log message
1473
+ * @param {LogLevel} level - Log level
1474
+ * @param {string[]} styles - CSS styles for browser console
1475
+ * @param {any[]} additionalArgs - Additional arguments to log
1476
+ * @since 4.0.0
1477
+ */
1478
+ private writeOutput(
1479
+ message: string,
1480
+ level: LogLevel,
1481
+ styles: string[],
1482
+ additionalArgs: any[]
1483
+ ): void {
1484
+ const mode = this.config.outputMode ?? 'console';
1485
+
1486
+ // Silent mode: no output
1487
+ if (mode === 'silent') {
1488
+ return;
1489
+ }
1490
+
1491
+ // Custom mode: use configured writer
1492
+ if (mode === 'custom' && this.config.outputWriter) {
1493
+ const fullMessage = additionalArgs.length > 0
1494
+ ? `${message} ${additionalArgs.map(a => String(a)).join(' ')}`
1495
+ : message;
1496
+ this.config.outputWriter.write(fullMessage, level, styles);
1497
+ return;
1498
+ }
1499
+
1500
+ // Default: console output
1501
+ if (additionalArgs.length > 0) {
1502
+ console.log(message, ...styles, ...additionalArgs);
1503
+ } else {
1504
+ console.log(message, ...styles);
1505
+ }
1506
+ }
1507
+
1466
1508
  // ===== CLI SYSTEM =====
1467
1509
 
1468
1510
  /**
package/src/index.ts CHANGED
@@ -146,7 +146,12 @@ export type {
146
146
  TransportOptions,
147
147
  TransportTarget,
148
148
  ITransport,
149
- StyleCacheConfig
149
+ StyleCacheConfig,
150
+ BadgeStyle,
151
+ TimestampFormat,
152
+ ColumnAlign,
153
+ ColumnConfig,
154
+ LogOptions,
150
155
  } from './types/index.js';
151
156
 
152
157
  // Styling utilities
@@ -316,4 +321,58 @@ export const stylePresets = {
316
321
  warning: StylePresets.warning().build(),
317
322
  info: StylePresets.info().build(),
318
323
  accent: StylePresets.accent().build(),
319
- };
324
+ };
325
+
326
+ // Terminal formatting utilities
327
+ export {
328
+ formatWithRightAlign,
329
+ formatColumns,
330
+ formatKeyValue,
331
+ formatBadge,
332
+ formatTimestamp as formatTimestampExt,
333
+ formatRelativeTime,
334
+ formatElapsedTime,
335
+ resetStartTime,
336
+ isKeyValueObject,
337
+ stripAnsi,
338
+ getVisibleLength,
339
+ padToWidth,
340
+ applyLogOptions,
341
+ } from './terminal/formatter.js';
342
+
343
+ // Color converter utilities
344
+ export {
345
+ hexToRgb,
346
+ rgbToHex,
347
+ hexTo256,
348
+ colorToHex,
349
+ getANSIForeground,
350
+ getANSIBackground,
351
+ getANSI256Foreground,
352
+ getANSI256Background,
353
+ cssColorToANSI,
354
+ ANSI,
355
+ } from './terminal/color-converter.js';
356
+
357
+ export type { RGB, ColorCapability } from './terminal/color-converter.js';
358
+
359
+ // Terminal renderer
360
+ export { TerminalRenderer } from './terminal/terminal-renderer.js';
361
+ export type { ANSIStyle, ChalkLikeInterface } from './terminal/terminal-renderer.js';
362
+
363
+ // Environment detection
364
+ export {
365
+ getEnvironment,
366
+ isRunningInTerminal,
367
+ supportsANSI,
368
+ getColorCapability,
369
+ getTerminalWidth,
370
+ getTerminalHeight,
371
+ getEnvironmentInfo,
372
+ } from './utils/environment-detector.js';
373
+
374
+ export type { Environment } from './utils/environment-detector.js';
375
+
376
+ // Output writers for custom log destinations
377
+ export { BufferWriter, type BufferEntry } from './writers/index.js';
378
+ export type { OutputWriter, OutputMode } from './types/core.js';
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Color Converter - CSS colors to ANSI extended color codes
3
+ * Supports 256-color and truecolor (24-bit) terminals
4
+ */
5
+
6
+ export type ColorCapability = 'full' | 'basic' | 'none';
7
+
8
+ export interface RGB {
9
+ r: number;
10
+ g: number;
11
+ b: number;
12
+ }
13
+
14
+ const ANSI_16_COLORS: Record<string, { fg: string; bg: string; bright: { fg: string; bg: string } }> = {
15
+ black: { fg: '30', bg: '40', bright: { fg: '90', bg: '100' } },
16
+ red: { fg: '31', bg: '41', bright: { fg: '91', bg: '101' } },
17
+ green: { fg: '32', bg: '42', bright: { fg: '92', bg: '102' } },
18
+ yellow: { fg: '33', bg: '43', bright: { fg: '93', bg: '103' } },
19
+ blue: { fg: '34', bg: '44', bright: { fg: '94', bg: '104' } },
20
+ magenta: { fg: '35', bg: '45', bright: { fg: '95', bg: '105' } },
21
+ cyan: { fg: '36', bg: '46', bright: { fg: '96', bg: '106' } },
22
+ white: { fg: '37', bg: '47', bright: { fg: '97', bg: '107' } },
23
+ gray: { fg: '90', bg: '100', bright: { fg: '37', bg: '47' } },
24
+ grey: { fg: '90', bg: '100', bright: { fg: '37', bg: '47' } },
25
+ };
26
+
27
+ const CSS_NAMED_COLORS: Record<string, string> = {
28
+ aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff',
29
+ aquamarine: '#7fffd4', azure: '#f0ffff', beige: '#f5f5dc',
30
+ bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd',
31
+ blue: '#0000ff', blueviolet: '#8a2be2', brown: '#a52a2a',
32
+ burlywood: '#deb887', cadetblue: '#5f9ea0', chartreuse: '#7fff00',
33
+ chocolate: '#d2691e', coral: '#ff7f50', cornflowerblue: '#6495ed',
34
+ cornsilk: '#fff8dc', crimson: '#dc143c', cyan: '#00ffff',
35
+ darkblue: '#00008b', darkcyan: '#008b8b', darkgoldenrod: '#b8860b',
36
+ darkgray: '#a9a9a9', darkgreen: '#006400', darkkhaki: '#bdb76b',
37
+ darkmagenta: '#8b008b', darkolivegreen: '#556b2f', darkorange: '#ff8c00',
38
+ darkorchid: '#9932cc', darkred: '#8b0000', darksalmon: '#e9967a',
39
+ darkseagreen: '#8fbc8f', darkslateblue: '#483d8b', darkslategray: '#2f4f4f',
40
+ darkturquoise: '#00ced1', darkviolet: '#9400d3', deeppink: '#ff1493',
41
+ deepskyblue: '#00bfff', dimgray: '#696969', dodgerblue: '#1e90ff',
42
+ firebrick: '#b22222', floralwhite: '#fffaf0', forestgreen: '#228b22',
43
+ fuchsia: '#ff00ff', gainsboro: '#dcdcdc', ghostwhite: '#f8f8ff',
44
+ gold: '#ffd700', goldenrod: '#daa520', gray: '#808080',
45
+ green: '#008000', greenyellow: '#adff2f', honeydew: '#f0fff0',
46
+ hotpink: '#ff69b4', indianred: '#cd5c5c', indigo: '#4b0082',
47
+ ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa',
48
+ lavenderblush: '#fff0f5', lawngreen: '#7cfc00', lemonchiffon: '#fffacd',
49
+ lightblue: '#add8e6', lightcoral: '#f08080', lightcyan: '#e0ffff',
50
+ lightgoldenrodyellow: '#fafad2', lightgray: '#d3d3d3', lightgreen: '#90ee90',
51
+ lightpink: '#ffb6c1', lightsalmon: '#ffa07a', lightseagreen: '#20b2aa',
52
+ lightskyblue: '#87cefa', lightslategray: '#778899', lightsteelblue: '#b0c4de',
53
+ lightyellow: '#ffffe0', lime: '#00ff00', limegreen: '#32cd32',
54
+ linen: '#faf0e6', magenta: '#ff00ff', maroon: '#800000',
55
+ mediumaquamarine: '#66cdaa', mediumblue: '#0000cd', mediumorchid: '#ba55d3',
56
+ mediumpurple: '#9370db', mediumseagreen: '#3cb371', mediumslateblue: '#7b68ee',
57
+ mediumspringgreen: '#00fa9a', mediumturquoise: '#48d1cc', mediumvioletred: '#c71585',
58
+ midnightblue: '#191970', mintcream: '#f5fffa', mistyrose: '#ffe4e1',
59
+ moccasin: '#ffe4b5', navajowhite: '#ffdead', navy: '#000080',
60
+ oldlace: '#fdf5e6', olive: '#808000', olivedrab: '#6b8e23',
61
+ orange: '#ffa500', orangered: '#ff4500', orchid: '#da70d6',
62
+ palegoldenrod: '#eee8aa', palegreen: '#98fb98', paleturquoise: '#afeeee',
63
+ palevioletred: '#db7093', papayawhip: '#ffefd5', peachpuff: '#ffdab9',
64
+ peru: '#cd853f', pink: '#ffc0cb', plum: '#dda0dd',
65
+ powderblue: '#b0e0e6', purple: '#800080', rebeccapurple: '#663399',
66
+ red: '#ff0000', rosybrown: '#bc8f8f', royalblue: '#4169e1',
67
+ saddlebrown: '#8b4513', salmon: '#fa8072', sandybrown: '#f4a460',
68
+ seagreen: '#2e8b57', seashell: '#fff5ee', sienna: '#a0522d',
69
+ silver: '#c0c0c0', skyblue: '#87ceeb', slateblue: '#6a5acd',
70
+ slategray: '#708090', snow: '#fffafa', springgreen: '#00ff7f',
71
+ steelblue: '#4682b4', tan: '#d2b48c', teal: '#008080',
72
+ thistle: '#d8bfd8', tomato: '#ff6347', turquoise: '#40e0d0',
73
+ violet: '#ee82ee', wheat: '#f5deb3', white: '#ffffff',
74
+ whitesmoke: '#f5f5f5', yellow: '#ffff00', yellowgreen: '#9acd32',
75
+ };
76
+
77
+ export function hexToRgb(hex: string): RGB {
78
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
79
+ if (!result || !result[1] || !result[2] || !result[3]) {
80
+ return { r: 255, g: 255, b: 255 };
81
+ }
82
+ return {
83
+ r: parseInt(result[1], 16),
84
+ g: parseInt(result[2], 16),
85
+ b: parseInt(result[3], 16),
86
+ };
87
+ }
88
+
89
+ export function rgbToHex(r: number, g: number, b: number): string {
90
+ return '#' + [r, g, b].map(x => x.toString(16).padStart(2, '0')).join('');
91
+ }
92
+
93
+ export function hexTo256(hex: string): number {
94
+ const { r, g, b } = hexToRgb(hex);
95
+
96
+ if (r === g && g === b) {
97
+ if (r < 8) return 16;
98
+ if (r > 248) return 231;
99
+ return Math.round(((r - 8) / 247) * 24) + 232;
100
+ }
101
+
102
+ const ri = Math.round(r / 255 * 5);
103
+ const gi = Math.round(g / 255 * 5);
104
+ const bi = Math.round(b / 255 * 5);
105
+
106
+ return 16 + (36 * ri) + (6 * gi) + bi;
107
+ }
108
+
109
+ export function colorToHex(color: string): string {
110
+ if (color.startsWith('#')) {
111
+ return color;
112
+ }
113
+
114
+ const named = CSS_NAMED_COLORS[color.toLowerCase()];
115
+ if (named) {
116
+ return named;
117
+ }
118
+
119
+ const rgbMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
120
+ if (rgbMatch && rgbMatch[1] && rgbMatch[2] && rgbMatch[3]) {
121
+ return rgbToHex(
122
+ parseInt(rgbMatch[1]),
123
+ parseInt(rgbMatch[2]),
124
+ parseInt(rgbMatch[3])
125
+ );
126
+ }
127
+
128
+ return '#ffffff';
129
+ }
130
+
131
+ export function getANSIForeground(color: string, capability: ColorCapability): string {
132
+ if (capability === 'none') return '';
133
+
134
+ const lowerColor = color.toLowerCase();
135
+
136
+ if (capability === 'basic' && ANSI_16_COLORS[lowerColor]) {
137
+ return `\x1b[${ANSI_16_COLORS[lowerColor].fg}m`;
138
+ }
139
+
140
+ if (capability === 'basic') {
141
+ const hex = colorToHex(color);
142
+ const nearestBasic = findNearestBasicColor(hex);
143
+ const basicColor = ANSI_16_COLORS[nearestBasic];
144
+ return basicColor ? `\x1b[${basicColor.fg}m` : '\x1b[37m';
145
+ }
146
+
147
+ const hex = colorToHex(color);
148
+ const { r, g, b } = hexToRgb(hex);
149
+ return `\x1b[38;2;${r};${g};${b}m`;
150
+ }
151
+
152
+ export function getANSIBackground(color: string, capability: ColorCapability): string {
153
+ if (capability === 'none') return '';
154
+
155
+ const lowerColor = color.toLowerCase();
156
+
157
+ if (capability === 'basic' && ANSI_16_COLORS[lowerColor]) {
158
+ return `\x1b[${ANSI_16_COLORS[lowerColor].bg}m`;
159
+ }
160
+
161
+ if (capability === 'basic') {
162
+ const hex = colorToHex(color);
163
+ const nearestBasic = findNearestBasicColor(hex);
164
+ const basicColor = ANSI_16_COLORS[nearestBasic];
165
+ return basicColor ? `\x1b[${basicColor.bg}m` : '\x1b[47m';
166
+ }
167
+
168
+ const hex = colorToHex(color);
169
+ const { r, g, b } = hexToRgb(hex);
170
+ return `\x1b[48;2;${r};${g};${b}m`;
171
+ }
172
+
173
+ export function getANSI256Foreground(colorIndex: number): string {
174
+ return `\x1b[38;5;${colorIndex}m`;
175
+ }
176
+
177
+ export function getANSI256Background(colorIndex: number): string {
178
+ return `\x1b[48;5;${colorIndex}m`;
179
+ }
180
+
181
+ function findNearestBasicColor(hex: string): string {
182
+ const { r, g, b } = hexToRgb(hex);
183
+ const basicColors: Record<string, RGB> = {
184
+ black: { r: 0, g: 0, b: 0 },
185
+ red: { r: 205, g: 0, b: 0 },
186
+ green: { r: 0, g: 205, b: 0 },
187
+ yellow: { r: 205, g: 205, b: 0 },
188
+ blue: { r: 0, g: 0, b: 238 },
189
+ magenta: { r: 205, g: 0, b: 205 },
190
+ cyan: { r: 0, g: 205, b: 205 },
191
+ white: { r: 229, g: 229, b: 229 },
192
+ };
193
+
194
+ let nearest = 'white';
195
+ let minDistance = Infinity;
196
+
197
+ for (const [name, rgb] of Object.entries(basicColors)) {
198
+ const distance = Math.sqrt(
199
+ Math.pow(r - rgb.r, 2) +
200
+ Math.pow(g - rgb.g, 2) +
201
+ Math.pow(b - rgb.b, 2)
202
+ );
203
+ if (distance < minDistance) {
204
+ minDistance = distance;
205
+ nearest = name;
206
+ }
207
+ }
208
+
209
+ return nearest;
210
+ }
211
+
212
+ export function extractGradientColors(gradient: string): string[] {
213
+ const hexMatches = gradient.match(/#[a-fA-F0-9]{6}/g) || [];
214
+ const rgbMatches = gradient.match(/rgba?\([^)]+\)/g) || [];
215
+ const namedMatches = gradient.match(/,\s*([a-z]+)(?:\s|,|\))/gi) || [];
216
+
217
+ const colors: string[] = [
218
+ ...hexMatches,
219
+ ...rgbMatches.map(colorToHex),
220
+ ...namedMatches.map(m => {
221
+ const name = m.replace(/[,\s)]/g, '').toLowerCase();
222
+ return CSS_NAMED_COLORS[name] || null;
223
+ }).filter((c): c is string => c !== null)
224
+ ];
225
+
226
+ return colors.length > 0 ? colors : ['#ffffff'];
227
+ }
228
+
229
+ export function gradientToSingleColor(gradient: string): string {
230
+ const colors = extractGradientColors(gradient);
231
+ if (colors.length === 0) return '#ffffff';
232
+ if (colors.length === 1) return colors[0] ?? '#ffffff';
233
+
234
+ const rgbs = colors.map(c => hexToRgb(colorToHex(c)));
235
+ const avgR = Math.round(rgbs.reduce((sum, rgb) => sum + rgb.r, 0) / rgbs.length);
236
+ const avgG = Math.round(rgbs.reduce((sum, rgb) => sum + rgb.g, 0) / rgbs.length);
237
+ const avgB = Math.round(rgbs.reduce((sum, rgb) => sum + rgb.b, 0) / rgbs.length);
238
+
239
+ return rgbToHex(avgR, avgG, avgB);
240
+ }
241
+
242
+ export function cssColorToANSI(
243
+ cssColor: string,
244
+ capability: ColorCapability,
245
+ isBackground: boolean = false
246
+ ): string {
247
+ if (capability === 'none') return '';
248
+
249
+ let resolvedColor: string;
250
+
251
+ if (cssColor.includes('gradient')) {
252
+ resolvedColor = gradientToSingleColor(cssColor);
253
+ } else {
254
+ resolvedColor = colorToHex(cssColor);
255
+ }
256
+
257
+ return isBackground
258
+ ? getANSIBackground(resolvedColor, capability)
259
+ : getANSIForeground(resolvedColor, capability);
260
+ }
261
+
262
+ export const ANSI = {
263
+ reset: '\x1b[0m',
264
+ bold: '\x1b[1m',
265
+ dim: '\x1b[2m',
266
+ italic: '\x1b[3m',
267
+ underline: '\x1b[4m',
268
+ blink: '\x1b[5m',
269
+ inverse: '\x1b[7m',
270
+ hidden: '\x1b[8m',
271
+ strikethrough: '\x1b[9m',
272
+
273
+ fg: {
274
+ black: '\x1b[30m',
275
+ red: '\x1b[31m',
276
+ green: '\x1b[32m',
277
+ yellow: '\x1b[33m',
278
+ blue: '\x1b[34m',
279
+ magenta: '\x1b[35m',
280
+ cyan: '\x1b[36m',
281
+ white: '\x1b[37m',
282
+ gray: '\x1b[90m',
283
+ brightRed: '\x1b[91m',
284
+ brightGreen: '\x1b[92m',
285
+ brightYellow: '\x1b[93m',
286
+ brightBlue: '\x1b[94m',
287
+ brightMagenta: '\x1b[95m',
288
+ brightCyan: '\x1b[96m',
289
+ brightWhite: '\x1b[97m',
290
+ },
291
+
292
+ bg: {
293
+ black: '\x1b[40m',
294
+ red: '\x1b[41m',
295
+ green: '\x1b[42m',
296
+ yellow: '\x1b[43m',
297
+ blue: '\x1b[44m',
298
+ magenta: '\x1b[45m',
299
+ cyan: '\x1b[46m',
300
+ white: '\x1b[47m',
301
+ gray: '\x1b[100m',
302
+ brightRed: '\x1b[101m',
303
+ brightGreen: '\x1b[102m',
304
+ brightYellow: '\x1b[103m',
305
+ brightBlue: '\x1b[104m',
306
+ brightMagenta: '\x1b[105m',
307
+ brightCyan: '\x1b[106m',
308
+ brightWhite: '\x1b[107m',
309
+ },
310
+
311
+ rgb: (r: number, g: number, b: number) => `\x1b[38;2;${r};${g};${b}m`,
312
+ bgRgb: (r: number, g: number, b: number) => `\x1b[48;2;${r};${g};${b}m`,
313
+ color256: (n: number) => `\x1b[38;5;${n}m`,
314
+ bgColor256: (n: number) => `\x1b[48;5;${n}m`,
315
+ };