/ ink / termio / esc.ts
esc.ts
 1  /**
 2   * ESC Sequence Parser
 3   *
 4   * Handles simple escape sequences: ESC + one or two characters
 5   */
 6  
 7  import type { Action } from './types.js'
 8  
 9  /**
10   * Parse a simple ESC sequence
11   *
12   * @param chars - Characters after ESC (not including ESC itself)
13   */
14  export function parseEsc(chars: string): Action | null {
15    if (chars.length === 0) return null
16  
17    const first = chars[0]!
18  
19    // Full reset (RIS)
20    if (first === 'c') {
21      return { type: 'reset' }
22    }
23  
24    // Cursor save (DECSC)
25    if (first === '7') {
26      return { type: 'cursor', action: { type: 'save' } }
27    }
28  
29    // Cursor restore (DECRC)
30    if (first === '8') {
31      return { type: 'cursor', action: { type: 'restore' } }
32    }
33  
34    // Index - move cursor down (IND)
35    if (first === 'D') {
36      return {
37        type: 'cursor',
38        action: { type: 'move', direction: 'down', count: 1 },
39      }
40    }
41  
42    // Reverse index - move cursor up (RI)
43    if (first === 'M') {
44      return {
45        type: 'cursor',
46        action: { type: 'move', direction: 'up', count: 1 },
47      }
48    }
49  
50    // Next line (NEL)
51    if (first === 'E') {
52      return { type: 'cursor', action: { type: 'nextLine', count: 1 } }
53    }
54  
55    // Horizontal tab set (HTS)
56    if (first === 'H') {
57      return null // Tab stop, not commonly needed
58    }
59  
60    // Charset selection (ESC ( X, ESC ) X, etc.) - silently ignore
61    if ('()'.includes(first) && chars.length >= 2) {
62      return null
63    }
64  
65    // Unknown
66    return { type: 'unknown', sequence: `\x1b${chars}` }
67  }