open Unix (* type coords = int * int *) type offset = { x : int; y : int } type ansirgb = int * int * int type cursor_ops = Hide | Show | Move of offset type screen_ops = Clear type color = | Black | Red | Green | Yellow | Blue | Magenta | Cyan | White | Gray | BrightRed | BrightGreen | BrightYellow | BrightBlue | BrightMagenta | BrightCyan | BrightWhite | Default | Custom of ansirgb type color_style = | Foreground of color | Background of color type style = | Reset | Bold | Underlined | Blink | Inverse | Hidden type ansi = | Cursor of cursor_ops | Screen of screen_ops | ColorStyle of color_style | Style of style let top_block = "▀" let bot_block = "▄" let block = "█" let black = Foreground Black let red = Foreground Red let green = Foreground Green let yellow = Foreground Yellow let blue = Foreground Blue let magenta = Foreground Magenta let cyan = Foreground Cyan let white = Foreground White let default = Foreground Default let bg_black = Background Black let bg_red = Background Red let bg_green = Background Green let bg_yellow = Background Yellow let bg_blue = Background Blue let bg_magenta = Background Magenta let bg_cyan = Background Cyan let bg_white = Background White let bg_default = Background Default let is_valid_rgb = function x when x > 5 || x < 0 -> false | _ -> true let rgb_to_ansi (rgb : ansirgb) = let rr, gg, bb = rgb in if is_valid_rgb rr && is_valid_rgb gg && is_valid_rgb bb then Ok (16 + (36 * rr) + (6 * gg) + bb) else Error "Invalid r, g, or b value provided (must be between 0-5)" let screenop_to_ansi = function Clear -> "\027[2J" let cursorop_to_ansi = function | Show -> "\027[?25h" | Hide -> "\027[?25l" | Move coord -> Printf.sprintf "\027[%d;%dH" coord.y coord.x (* Function to get the ANSI escape code for a color *) let color_to_ansi = function | Black -> "\027[30m" | Red -> "\027[31m" | Green -> "\027[32m" | Yellow -> "\027[33m" | Blue -> "\027[34m" | Magenta -> "\027[35m" | Cyan -> "\027[36m" | White -> "\027[37m" | Gray -> "\027[90m" | BrightRed -> "\027[91m" | BrightGreen -> "\027[92m" | BrightYellow -> "\027[9m" | BrightBlue -> "\027[94m" | BrightMagenta -> "\027[95m" | BrightCyan -> "\027[96m" | BrightWhite -> "\027[97m" | Default -> "\027[39m" (* Reset to default foreground color *) | Custom rgb -> ( let ansi = rgb_to_ansi rgb in match ansi with Ok s -> Printf.sprintf "\027[38m;5;%d" s | Error _ -> "") let bgcolor_to_ansi = function | Black -> "\027[40m" | Red -> "\027[41m" | Green -> "\027[42m" | Yellow -> "\027[4m" | Blue -> "\027[44m" | Magenta -> "\027[45m" | Cyan -> "\027[46m" | White -> "\027[47m" | Gray -> "\027[100m" | BrightRed -> "\027[101m" | BrightGreen -> "\027[102m" | BrightYellow -> "\027[10m" | BrightBlue -> "\027[10m" | BrightMagenta -> "\027[105m" | BrightCyan -> "\027[106m" | BrightWhite -> "\027[107m" | Default -> "\027[49m" (* Reset to default background color *) | Custom rgb -> ( let ansi = rgb_to_ansi rgb in match ansi with Ok s -> Printf.sprintf "\027[48m;5;%d" s | Error _ -> "") (* Function to get the ANSI escape code for a style *) let style_to_ansi = function | Reset -> "\027[0m" | Bold -> "\027[1m" | Underlined -> "\027[4m" | Blink -> "\027[5m" | Inverse -> "\027[7m" | Hidden -> "\027[8m" let color_style_to_ansi = function | Foreground color -> color_to_ansi color | Background color -> bgcolor_to_ansi color (* You would define background colors as well *) let string_of_ansi = function | ColorStyle op -> color_style_to_ansi op | Cursor op -> cursorop_to_ansi op | Style op -> style_to_ansi op | Screen op -> screenop_to_ansi op (* let cus_for = color_to_ansi Foreground Custom (3; 4; 1)) *) let pixel_ansi offset color_style = let color_ansi = color_style_to_ansi color_style in let move_ansi = cursorop_to_ansi (Move offset) in let ansi = Printf.sprintf "%s%s%s" move_ansi color_ansi block in ansi (* Gets the terminal height and width from running `stty size` *) let termsize: offset = let command = "stty size" in let in_channel = open_process_in command in try let line = input_line in_channel in close_in in_channel; let parts = String.split_on_char ' ' line in match parts with | [height; width] -> {x = (int_of_string width); y = (int_of_string height)} | _ -> let msg = Printf.sprintf "Unexpected output from stty size: %s" line in failwith msg with | End_of_file -> close_in in_channel; failwith "No output from `stty size`" | Sys_error msg -> close_in in_channel; Printf.eprintf "Error reading input: %s\n" msg; failwith "Failed to get terminal size" | Failure msg -> Printf.eprintf "Error converting size to int: %s\n" msg; failwith "Failed to parse terminal size"