///|
/// ANSI Escape Sequences for terminal output
///|
pub fn ansi_reset() -> String {
"\u001b[0m"
}
///|
pub fn ansi_bold() -> String {
"\u001b[1m"
}
///|
pub fn ansi_underline() -> String {
"\u001b[4m"
}
///|
pub fn ansi_reverse() -> String {
"\u001b[7m"
}
///|
pub fn ansi_fg_256(color_idx : Int) -> String {
"\u001b[38;5;" + color_idx.to_string() + "m"
}
///|
pub fn ansi_bg_256(color_idx : Int) -> String {
"\u001b[48;5;" + color_idx.to_string() + "m"
}
///|
/// Reset background color to default
pub fn ansi_bg_reset() -> String {
"\u001b[49m"
}
///|
pub fn ansi_move_to(row : Int, col : Int) -> String {
"\u001b[" + row.to_string() + ";" + col.to_string() + "H"
}
///|
pub fn ansi_clear_screen() -> String {
"\u001b[2J"
}
///|
pub fn ansi_clear_line() -> String {
"\u001b[2K"
}
///|
pub fn ansi_hide_cursor() -> String {
"\u001b[?25l"
}
///|
pub fn ansi_show_cursor() -> String {
"\u001b[?25h"
}
///|
pub fn ansi_enter_alt_screen() -> String {
"\u001b[?1049h"
}
///|
pub fn ansi_leave_alt_screen() -> String {
"\u001b[?1049l"
}
///|
/// Reset all terminal attributes (colors, styles)
pub fn ansi_reset_attrs() -> String {
"\u001b[0m"
}
///|
/// Full terminal reset sequence for recovering from corrupted state
pub fn ansi_full_reset() -> String {
// Reset attributes + enter alt screen + hide cursor + clear + move to origin
"\u001b[0m\u001b[?1049h\u001b[?25l\u001b[2J\u001b[H"
}
///|
/// Convert RGB color to 256-color palette index
pub fn rgb_to_256(r : Int, g : Int, b : Int) -> Int {
if r == g && g == b {
if r < 8 {
16
} else if r > 248 {
231
} else {
232 + (r - 8) * 24 / 240
}
} else {
let r6 = r * 6 / 256
let g6 = g * 6 / 256
let b6 = b * 6 / 256
16 + r6 * 36 + g6 * 6 + b6
}
}
///|
pub fn color_to_256(color : Color) -> Int {
rgb_to_256(color.r, color.g, color.b)
}
///|
pub fn ansi_fg_color(color : Color) -> String {
ansi_fg_256(color_to_256(color))
}
///|
pub fn ansi_bg_color(color : Color) -> String {
ansi_bg_256(color_to_256(color))
}
///|
/// Convert CharBuffer to ANSI-encoded string
pub fn buffer_to_ansi(buf : CharBuffer) -> String {
let sb = StringBuilder::new()
let mut last_fg = -1
let mut last_bg = -1
let mut last_bold = false
let mut last_underline = false
for row = 0; row < buf.height; row = row + 1 {
sb.write_string(ansi_move_to(row + 1, 1))
for col = 0; col < buf.width; col = col + 1 {
let cell = buf.get_cell(col, row)
if cell.char == '\u0000' {
continue
}
let fg_idx = if cell.fg.is_transparent() {
7
} else {
color_to_256(cell.fg)
}
let bg_idx = if cell.bg.is_transparent() {
-1
} else {
color_to_256(cell.bg)
}
let need_reset = (last_bold && not(cell.bold)) ||
(last_underline && not(cell.underline))
if need_reset {
sb.write_string(ansi_reset())
last_fg = -1
last_bg = -1
last_bold = false
last_underline = false
}
if cell.bold && not(last_bold) {
sb.write_string(ansi_bold())
last_bold = true
}
if cell.underline && not(last_underline) {
sb.write_string(ansi_underline())
last_underline = true
}
if fg_idx != last_fg {
sb.write_string(ansi_fg_256(fg_idx))
last_fg = fg_idx
}
if bg_idx != last_bg {
if bg_idx >= 0 {
sb.write_string(ansi_bg_256(bg_idx))
} else {
sb.write_string(ansi_bg_reset())
}
last_bg = bg_idx
}
sb.write_char(cell.char)
}
}
sb.write_string(ansi_reset())
sb.to_string()
}
///|
/// Optimized ANSI output with diff (only output changed cells)
pub fn buffer_diff_to_ansi(current : CharBuffer, prev : CharBuffer) -> String {
let sb = StringBuilder::new()
let mut last_row = -1
let mut last_col = -1
let mut last_fg = -1
let mut last_bg = -1
let mut last_bold = false
let mut last_underline = false
for row = 0; row < current.height; row = row + 1 {
for col = 0; col < current.width; col = col + 1 {
let cell = current.get_cell(col, row)
let prev_cell = prev.get_cell(col, row)
if cell == prev_cell {
continue
}
if cell.char == '\u0000' {
continue
}
if row != last_row || col != last_col + 1 {
sb.write_string(ansi_move_to(row + 1, col + 1))
}
let fg_idx = if cell.fg.is_transparent() {
7
} else {
color_to_256(cell.fg)
}
let bg_idx = if cell.bg.is_transparent() {
-1
} else {
color_to_256(cell.bg)
}
let need_reset = (last_bold && not(cell.bold)) ||
(last_underline && not(cell.underline))
if need_reset {
sb.write_string(ansi_reset())
last_fg = -1
last_bg = -1
last_bold = false
last_underline = false
}
if cell.bold && not(last_bold) {
sb.write_string(ansi_bold())
last_bold = true
}
if cell.underline && not(last_underline) {
sb.write_string(ansi_underline())
last_underline = true
}
if fg_idx != last_fg {
sb.write_string(ansi_fg_256(fg_idx))
last_fg = fg_idx
}
if bg_idx != last_bg {
if bg_idx >= 0 {
sb.write_string(ansi_bg_256(bg_idx))
} else {
sb.write_string(ansi_bg_reset())
}
last_bg = bg_idx
}
sb.write_char(cell.char)
last_row = row
last_col = col
}
}
sb.write_string(ansi_reset())
sb.to_string()
}
///|
pub fn enable_mouse() -> String {
"\u001b[?1000h\u001b[?1006h"
}
///|
pub fn disable_mouse() -> String {
"\u001b[?1006l\u001b[?1000l"
}