okami.mom/ultraviolet
git clone https://git.nkp.pet/okami.mom/ultraviolet
0322041
main
1package main 2 3import ( 4"fmt" 5"log" 6"slices" 7"strconv" 8"strings" 9"time" 10 11 uv"github.com/charmbracelet/ultraviolet" 12"github.com/charmbracelet/ultraviolet/layout" 13"github.com/charmbracelet/ultraviolet/screen" 14"github.com/charmbracelet/x/ansi" 15) 16 17func main () { 18t := uv .DefaultTerminal () 19scr := t .Screen () 20scr .EnterAltScreen () 21 22if err := t .Start ();err != nil { 23log .Fatalln ("failed to start terminal:" ,err ) 24 } 25 26defer t .Stop () 27 28var area uv.Rectangle 29 30ticker := time .NewTicker (time .Second / 60 ) 31defer ticker .Stop () 32 33for { 34select { 35case <- ticker .C : 36render (scr ,area ) 37 38scr .Render () 39scr .Flush () 40 41case ev := <- t .Events (): 42switch ev := ev .(type ) { 43case uv.WindowSizeEvent : 44area = ev .Bounds () 45scr .Resize (area .Dx (),area .Dy ()) 46screen .Clear (scr ) 47 48case uv.KeyPressEvent : 49switch { 50case ev .MatchString ("ctrl+c" ,"q" ): 51return 52 } 53 } 54 } 55 } 56} 57 58func render (sc uv.Screen ,area uv.Rectangle ) { 59var textArea uv.Rectangle 60 61layout .Vertical (layout .Len (7 ),layout .Min (0 )).Split (area ).Assign (& textArea ,& area ) 62 63lines := `Horizontal Layout Example. Press q to quit 64Each line has 2 constraints, plus Min(0) to fill the remaining space. 65E.g. the second line of the Len/Min box is [Len(2), Min(2), Min(0)] 66Note: constraint labels that don't fit are truncated` 67screen .NewContext (sc ).DrawStringWrapped (lines ,textArea .Min .X ,textArea .Min .Y ) 68 69rows := layout .Vertical ( 70layout .Len (9 ), 71layout .Len (9 ), 72layout .Len (9 ), 73layout .Len (9 ), 74layout .Len (9 ), 75layout .Min (0 ),// fills remaining space 76 ).Split (area ) 77 78var areas []uv.Rectangle 79 80for _ ,r := range rows { 81cols := layout .Horizontal ( 82layout .Len (14 ), 83layout .Len (14 ), 84layout .Len (14 ), 85layout .Len (14 ), 86layout .Len (14 ), 87layout .Min (0 ), 88 ).Split (r ) 89 90areas = append (areas ,cols [:5 ]... )// ignore Min(0) 91 } 92 93type Named struct { 94Name string 95Constraints []layout.Constraint 96 } 97 98examples := []Named { 99 { 100"Len" , 101 []layout.Constraint { 102layout .Len (0 ), 103layout .Len (2 ), 104layout .Len (3 ), 105layout .Len (6 ), 106layout .Len (10 ), 107layout .Len (15 ), 108 }, 109 }, 110 { 111"Min" , 112 []layout.Constraint { 113layout .Min (0 ), 114layout .Min (2 ), 115layout .Min (3 ), 116layout .Min (6 ), 117layout .Min (10 ), 118layout .Min (15 ), 119 }, 120 }, 121 { 122"Max" , 123 []layout.Constraint { 124layout .Max (0 ), 125layout .Max (2 ), 126layout .Max (3 ), 127layout .Max (6 ), 128layout .Max (10 ), 129layout .Max (15 ), 130 }, 131 }, 132 { 133"Perc" , 134 []layout.Constraint { 135layout .Percent (0 ), 136layout .Percent (25 ), 137layout .Percent (50 ), 138layout .Percent (75 ), 139layout .Percent (100 ), 140layout .Percent (150 ), 141 }, 142 }, 143 { 144"Ratio" , 145 []layout.Constraint { 146 layout.Ratio {0 ,4 }, 147 layout.Ratio {1 ,4 }, 148 layout.Ratio {2 ,4 }, 149 layout.Ratio {3 ,4 }, 150 layout.Ratio {4 ,4 }, 151 layout.Ratio {6 ,4 }, 152 }, 153 }, 154 } 155 156for i ,e := range cartesianProduct (examples ,examples ) { 157renderExampleCombinations ( 158sc , 159areas [i ], 160fmt .Sprintf ("%s | %s" ,e .Left .Name ,e .Right .Name ), 161zip (e .Left .Constraints ,e .Right .Constraints ), 162 ) 163 } 164} 165 166func renderExampleCombinations ( 167sc uv.Screen , 168area uv.Rectangle , 169title string , 170constraints []Pair [layout.Constraint , layout.Constraint ], 171) { 172rows := layout .Vertical ( 173slices .Repeat ( 174 []layout.Constraint {layout .Len (1 )}, 175len (constraints )+ 1 , 176 )... , 177 ). 178WithPadding (layout .Pad (1 )). 179Split (area ) 180 181screen .NewContext (sc ).DrawString (title ,rows [0 ].Min .X ,rows [0 ].Min .Y - 1 ) 182 183for _ ,p := range zip (constraints ,rows ) { 184renderExample (sc ,p .Right ,p .Left .Left ,p .Left .Right ,layout .Min (0 )) 185 } 186 187nums := "123456789012" 188row := rows [len (rows )- 1 ] 189 190screen .NewContext (sc ).DrawString ( 191nums [:min (len (nums ),row .Dx ())], 192row .Min .X , 193row .Min .Y , 194 ) 195} 196 197func renderExample (sc uv.Screen ,area uv.Rectangle ,constraints ... layout.Constraint ) { 198var r ,g ,b uv.Rectangle 199 200layout .Horizontal (constraints ... ).Split (area ).Assign (& r ,& b ,& g ) 201 202screen .FillArea (sc ,cell (ansi .Red ),r ) 203screen .FillArea (sc ,cell (ansi .Green ),g ) 204screen .FillArea (sc ,cell (ansi .Blue ),b ) 205 206draw := func (bg ansi.Color ,s string ,r uv.Rectangle ) { 207ctx := screen .NewContext (sc ).WithStyle (uv.Style {Bg :bg }) 208 209ctx .DrawString (s [:min (len (s ),r .Dx ())],r .Min .X ,r .Min .Y ) 210 } 211 212draw (ansi .Red ,constraintLabel (constraints [0 ]),r ) 213draw (ansi .Green ,strings .Repeat ("." ,g .Dx ()),g ) 214draw (ansi .Blue ,constraintLabel (constraints [1 ]),b ) 215} 216 217func constraintLabel (c layout.Constraint )string { 218switch c := c .(type ) { 219case layout.Fill : 220return strconv .Itoa (int (c )) 221 222case layout.Len : 223return strconv .Itoa (int (c )) 224 225case layout.Max : 226return strconv .Itoa (int (c )) 227 228case layout.Min : 229return strconv .Itoa (int (c )) 230 231case layout.Percent : 232return strconv .Itoa (int (c )) 233 234case layout.Ratio : 235 236return fmt .Sprintf ("%d/%d" ,c .Num ,c .Den ) 237default : 238return "" 239 } 240} 241 242func cell (bg ansi.Color )* uv.Cell { 243return & uv.Cell { 244Content :" " , 245Width :1 , 246Style : uv.Style { 247Bg :bg , 248 }, 249 } 250} 251 252type Pair [T ,U any ]struct { 253Left T 254Right U 255} 256 257func zip [T ,U any ](a []T ,b []U ) []Pair [T ,U ] { 258zipped := make ([]Pair [T ,U ],0 ,min (len (a ),len (b ))) 259 260for i := range min (len (a ),len (b )) { 261zipped = append (zipped ,Pair [T ,U ]{Left :a [i ],Right :b [i ]}) 262 } 263 264return zipped 265} 266 267func cartesianProduct [T ,U any ](as []T ,bs []U ) []Pair [T ,U ] { 268product := make ([]Pair [T ,U ],0 ,len (as )* len (bs )) 269 270for _ ,a := range as { 271for _ ,b := range bs { 272product = append (product ,Pair [T ,U ]{Left :a ,Right :b }) 273 } 274 } 275return product 276}