okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
61c43ca
main
1package tui 2 3import ( 4"strings" 5 6"github.com/rivo/uniseg" 7 8"github.com/charmbracelet/x/ansi" 9) 10 11type agentCell struct { 12hue * rgb 13text string 14width int 15continuation bool 16} 17 18type agentCanvas struct { 19cells [][]agentCell 20w ,h int 21} 22 23func newCanvas (w ,h int )* agentCanvas { 24c := & agentCanvas {w :w ,h :h ,cells :make ([][]agentCell ,h )} 25for y := range c .cells { 26c .cells [y ]= make ([]agentCell ,w ) 27for x := range c .cells [y ] { 28c .cells [y ][x ]= agentCell {text :" " ,width :1 } 29 } 30 } 31return c 32} 33 34// clearAt removes a whole grapheme when another graph element overwrites it. 35func (c * agentCanvas )clearAt (x ,y int ) { 36start := x 37for start > 0 && c .cells [y ][start ].continuation { 38start -- 39 } 40width := max (1 ,c .cells [y ][start ].width ) 41for column := start ;column < min (c .w ,start + width );column ++ { 42c .cells [y ][column ]= agentCell {text :" " ,width :1 } 43 } 44} 45 46func (c * agentCanvas )put (x ,y int ,text string ,hue rgb ) { 47if y < 0 || y >= c .h { 48return 49 } 50graphemes := uniseg .NewGraphemes (text ) 51for graphemes .Next () { 52cluster := graphemes .Str () 53width := ansi .StringWidth (cluster ) 54if width == 0 { 55continue 56 } 57if x >= 0 && x + width <= c .w { 58for column := x ;column < x + width ;column ++ { 59c .clearAt (column ,y ) 60 } 61c .cells [y ][x ]= agentCell {text :cluster ,width :width ,hue :& hue } 62for column := x + 1 ;column < x + width ;column ++ { 63c .cells [y ][column ]= agentCell {continuation :true } 64 } 65 } 66x += width 67 } 68} 69 70var sgrCache = map [string ]string {} 71 72// sgrCached memoizes the escape sequence for one hex color. 73func sgrCached (hex string )string { 74seq ,ok := sgrCache [hex ] 75if ! ok { 76seq = sgr (hex ,"" ) 77sgrCache [hex ]= seq 78 } 79return seq 80} 81 82func (c * agentCanvas )line (y int )string { 83var b strings.Builder 84current := "" 85for _ ,cell := range c .cells [y ] { 86if cell .continuation { 87continue 88 } 89seq := "" 90if cell .hue != nil && cell .text != " " { 91seq = sgrCached (cell .hue .hex ()) 92 } 93if seq != current { 94if current != "" { 95b .WriteString (ansiReset ) 96 } 97b .WriteString (seq ) 98current = seq 99 } 100b .WriteString (cell .text ) 101 } 102if current != "" { 103b .WriteString (ansiReset ) 104 } 105return b .String () 106}