1+ local M = {}
2+
3+ -- Xterm 256-color palette (0-255) as RGB hex strings
4+ local xterm_palette = {
5+ " 000000" , " 800000" , " 008000" , " 808000" , " 000080" , " 800080" , " 008080" , " c0c0c0" ,
6+ " 808080" , " ff0000" , " 00ff00" , " ffff00" , " 0000ff" , " ff00ff" , " 00ffff" , " ffffff" ,
7+ -- 16-231: 6x6x6 color cube
8+ }
9+ -- Fill in the 6x6x6 color cube
10+ for r = 0 , 5 do
11+ for g = 0 , 5 do
12+ for b = 0 , 5 do
13+ local idx = 16 + 36 * r + 6 * g + b
14+ local function scale (x ) return x == 0 and 0 or 95 + 40 * (x - 1 ) end
15+ xterm_palette [idx + 1 ] = string.format (" %02x%02x%02x" , scale (r ), scale (g ), scale (b ))
16+ end
17+ end
18+ end
19+ -- 232-255: grayscale ramp
20+ for i = 0 , 23 do
21+ local level = 8 + i * 10
22+ xterm_palette [233 + i ] = string.format (" %02x%02x%02x" , level , level , level )
23+ end
24+
25+ --- Parses #xNN and \e[38;5;NNNm xterm color codes and returns (length, rgb_hex)
26+ --- @param line string : The line of text
27+ --- @param i number : The index to start parsing from
28+ --- @return number | nil , string | nil : Length of match and RGB hex string
29+ function M .parser (line , i )
30+ -- #xNN (decimal, 0-255)
31+ local hash_x = line :sub (i , i + 1 )
32+ if hash_x == " #x" then
33+ local num = line :sub (i + 2 ):match (" ^(%d?%d?%d)" )
34+ if num then
35+ local idx = tonumber (num )
36+ if idx and idx >= 0 and idx <= 255 then
37+ local next_char = line :sub (i + 2 + # num , i + 2 + # num )
38+ if next_char == " " or not next_char :match (" %w" ) then
39+ return 2 + # num , xterm_palette [idx + 1 ]
40+ end
41+ end
42+ end
43+ end
44+ -- \e[38;5;NNNm (decimal, 0-255), support both literal '\e' and actual escape char
45+ local ansi_patterns = {
46+ " ^\\ e%[38;5;(%d?%d?%d)m" , -- literal '\e'
47+ " ^\27 %[38;5;(%d?%d?%d)m" , -- ASCII 27
48+ " ^\x1b %[38;5;(%d?%d?%d)m" , -- hex escape
49+ }
50+ for _ , esc_pat in ipairs (ansi_patterns ) do
51+ local esc_match = line :sub (i ):match (esc_pat )
52+ if esc_match then
53+ local idx = tonumber (esc_match )
54+ if idx and idx >= 0 and idx <= 255 then
55+ -- Use string.find to get the end index of the match
56+ local _ , end_idx = line :sub (i ):find (esc_pat )
57+ if end_idx then
58+ return end_idx , xterm_palette [idx + 1 ]
59+ else
60+ return 7 + # esc_match , xterm_palette [idx + 1 ]
61+ end
62+ end
63+ end
64+ end
65+ return nil
66+ end
67+
68+ return M
0 commit comments