glyphs.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. /**
  2. * Glyph selection for CLI output.
  3. *
  4. * On Windows, console output is interpreted via the active output
  5. * codepage. PowerShell 5.1 and cmd.exe default to OEM codepages
  6. * (CP437, CP936, ...), so UTF-8 bytes written to the console render
  7. * as mojibake (see #168). The shimmer worker is hit hardest because
  8. * it uses `fs.writeSync(1, ...)` (raw bytes, no TTY-aware encoding
  9. * conversion) to keep animation smooth while the main thread is
  10. * blocked in SQLite. To stay readable everywhere, we fall back to
  11. * ASCII glyphs whenever the terminal is not known to handle UTF-8.
  12. *
  13. * Detection is intentionally simple:
  14. * - `CODEGRAPH_ASCII=1` -> ASCII (escape hatch for any terminal)
  15. * - `CODEGRAPH_UNICODE=1` -> Unicode (opt-in on Windows)
  16. * - Windows -> ASCII by default
  17. * - Linux kernel console (`TERM=linux`) -> ASCII
  18. * - Everything else -> Unicode
  19. */
  20. export function supportsUnicode(): boolean {
  21. if (process.env.CODEGRAPH_ASCII === '1') return false;
  22. if (process.env.CODEGRAPH_UNICODE === '1') return true;
  23. if (process.platform === 'win32') return false;
  24. return process.env.TERM !== 'linux';
  25. }
  26. export interface Glyphs {
  27. ok: string;
  28. err: string;
  29. info: string;
  30. warn: string;
  31. spinner: string[];
  32. barFilled: string;
  33. barEmpty: string;
  34. rail: string;
  35. phaseDone: string;
  36. dash: string;
  37. hLine: string;
  38. treeBranch: string;
  39. treeLast: string;
  40. treePipe: string;
  41. }
  42. export const UNICODE_GLYPHS: Glyphs = {
  43. ok: '✓',
  44. err: '✗',
  45. info: 'ℹ',
  46. warn: '⚠',
  47. spinner: ['·', '✢', '✳', '✶', '✻', '✽'],
  48. barFilled: '█',
  49. barEmpty: '░',
  50. rail: '│',
  51. phaseDone: '◆',
  52. dash: '—',
  53. hLine: '─',
  54. treeBranch: '├── ',
  55. treeLast: '└── ',
  56. treePipe: '│ ',
  57. };
  58. export const ASCII_GLYPHS: Glyphs = {
  59. ok: '[OK]',
  60. err: '[ERR]',
  61. info: '[i]',
  62. warn: '[!]',
  63. spinner: ['.', '*', '+', 'x', 'o', 'O'],
  64. barFilled: '#',
  65. barEmpty: '-',
  66. rail: '|',
  67. phaseDone: '*',
  68. dash: '-',
  69. hLine: '-',
  70. treeBranch: '|-- ',
  71. treeLast: '`-- ',
  72. treePipe: '| ',
  73. };
  74. let cached: Glyphs | null = null;
  75. export function getGlyphs(): Glyphs {
  76. if (cached === null) {
  77. cached = supportsUnicode() ? UNICODE_GLYPHS : ASCII_GLYPHS;
  78. }
  79. return cached;
  80. }
  81. /** Reset the cached glyph set. Test-only; production code should call `getGlyphs()`. */
  82. export function _resetGlyphsCache(): void {
  83. cached = null;
  84. }