okami.mom/ultraviolet
git clone https://git.nkp.pet/okami.mom/ultraviolet
f72cc87
upstream
1package main 2 3import ( 4"context" 5"fmt" 6"image/color" 7"log" 8"math/rand" 9"time" 10 11 uv"github.com/charmbracelet/ultraviolet" 12"github.com/charmbracelet/ultraviolet/screen" 13) 14 15func setupColors (width ,height int ) [][]color.Color { 16height = height * 2 // double height for half blocks 17colors := make ([][]color.Color ,height ) 18 19for y := 0 ;y < height ;y ++ { 20colors [y ]= make ([]color.Color ,width ) 21randomnessFactor := float64 (height - y )/ float64 (height ) 22 23for x := 0 ;x < width ;x ++ { 24baseValue := randomnessFactor * (float64 (height - y )/ float64 (height )) 25randomOffset := (rand .Float64 ()* 0.2 )- 0.1 26value := clamp (baseValue + randomOffset ,0 ,1 ) 27 28// Convert value to grayscale color (0-255) 29gray := uint8 (value * 255 ) 30colors [y ][x ]= color.RGBA { 31R :gray ,G :gray ,B :gray ,A :255 , 32 } 33 } 34 } 35return colors 36} 37 38func clamp (value ,min ,max float64 )float64 { 39if value < min { 40return min 41 } 42if value > max { 43return max 44 } 45return value 46} 47 48type tickEvent struct {} 49 50func main () { 51t := uv .DefaultTerminal () 52scr := t .Screen () 53scr .EnterAltScreen () 54 55if err := t .Start ();err != nil { 56log .Fatalf ("failed to start terminal: %v" ,err ) 57 } 58 59defer t .Stop () 60 61ctx ,cancel := context .WithCancel (context .Background ()) 62defer cancel () 63 64var frameCount int 65var elapsed time.Duration 66now := time .Now () 67fps := 60.0 68fpsFrameCount := 0 69 70bounds := scr .Bounds () 71colors := setupColors (bounds .Dx (),bounds .Dy ()) 72 73go t .SendEvent (tickEvent {}) 74 75LOOP: 76for { 77select { 78case <- ctx .Done (): 79break LOOP 80case ev := <- t .Events (): 81switch ev := ev .(type ) { 82case uv.KeyPressEvent : 83switch ev .String () { 84case "q" ,"ctrl+c" : 85cancel () 86break LOOP 87 } 88 89case uv.WindowSizeEvent : 90width ,height := ev .Width ,ev .Height 91colors = setupColors (width ,height ) 92scr .Resize (width ,height ) 93case tickEvent : 94if len (colors )== 0 { 95continue 96 } 97 98frameCount ++ 99fpsFrameCount ++ 100screen .Clear (scr ) 101 102bounds := scr .Bounds () 103 104// Title 105uv .NewStyledString (fmt .Sprintf ("\x1b[1mSpace / FPS: %.1f\x1b[m" ,fps )). 106Draw (scr ,uv .Rect (0 ,0 ,bounds .Dx (),1 )) 107 108// Color display 109width ,height := bounds .Dx (),bounds .Dy () 110for y := 1 ;y < height ;y ++ { 111for x := 0 ;x < width ;x ++ { 112xi := (x + frameCount )% width 113fg := colors [y * 2 ][xi ] 114bg := colors [y * 2 + 1 ][xi ] 115st := uv.Style { 116Fg :fg , 117Bg :bg , 118 } 119scr .SetCell (x ,y ,& uv.Cell { 120Content :"▀" , 121Style :st , 122Width :1 , 123 }) 124 } 125 } 126 127scr .Render () 128scr .Flush () 129 130elapsed = time .Since (now ) 131if elapsed > time .Second && fpsFrameCount > 2 { 132fps = float64 (fpsFrameCount )/ elapsed .Seconds () 133now = time .Now () 134fpsFrameCount = 0 135 } 136 137go t .SendEvent (tickEvent {}) 138 } 139 } 140 } 141}