prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

about_render.go (2516B)


      1 package tui
      2 
      3 import (
      4 	"strings"
      5 	"time"
      6 
      7 	tea "github.com/charmbracelet/bubbletea"
      8 )
      9 
     10 const aboutLogoWidth = 32
     11 
     12 type aboutASCIIRenderedMsg struct {
     13 	width int
     14 	ascii string
     15 }
     16 
     17 func (m *portfolioModel) maybeRenderAboutASCIICmd() tea.Cmd {
     18 	if m.activeTab != tabAbout {
     19 		return nil
     20 	}
     21 
     22 	width := aboutLogoWidth
     23 	if ascii, cached := m.aboutASCIICache[width]; cached {
     24 		if strings.TrimSpace(ascii) != "" {
     25 			return nil
     26 		}
     27 	}
     28 	if m.aboutASCIILoads[width] {
     29 		return nil
     30 	}
     31 
     32 	m.aboutASCIILoads[width] = true
     33 	return renderAboutASCIICmd(width)
     34 }
     35 
     36 func renderAboutASCIICmd(width int) tea.Cmd {
     37 	return func() tea.Msg {
     38 		return aboutASCIIRenderedMsg{
     39 			width: width,
     40 			ascii: loadAboutASCII(width),
     41 		}
     42 	}
     43 }
     44 
     45 func (m *portfolioModel) applyAboutASCIIRendered(msg aboutASCIIRenderedMsg) {
     46 	delete(m.aboutASCIILoads, msg.width)
     47 	if strings.TrimSpace(msg.ascii) == "" {
     48 		delete(m.aboutASCIICache, msg.width)
     49 		return
     50 	}
     51 	m.aboutASCIICache[msg.width] = msg.ascii
     52 }
     53 
     54 // aboutLogoRowTick paces the portrait reveal: 16 rows in about 320ms, the
     55 // same budget the web gives its row-by-row draw.
     56 const aboutLogoRowTick = 20 * time.Millisecond
     57 
     58 type aboutLogoRowMsg struct{ gen int }
     59 
     60 func aboutLogoRowCmd(gen int) tea.Cmd {
     61 	return tea.Tick(aboutLogoRowTick, func(time.Time) tea.Msg { return aboutLogoRowMsg{gen: gen} })
     62 }
     63 
     64 // startAboutLogoReveal restarts the portrait from its first row. A newer
     65 // reveal bumps the generation so ticks from an older one are dropped.
     66 func (m *portfolioModel) startAboutLogoReveal() tea.Cmd {
     67 	if m.activeTab != tabAbout || m.bootActive {
     68 		return nil
     69 	}
     70 	if strings.TrimSpace(m.aboutASCIICache[aboutLogoWidth]) == "" {
     71 		return nil // starts when the render lands
     72 	}
     73 	m.aboutLogoGen++
     74 	m.aboutLogoRows = 1
     75 	return aboutLogoRowCmd(m.aboutLogoGen)
     76 }
     77 
     78 func (m *portfolioModel) stepAboutLogoReveal(msg aboutLogoRowMsg) tea.Cmd {
     79 	if msg.gen != m.aboutLogoGen {
     80 		return nil
     81 	}
     82 	total := strings.Count(m.aboutASCIICache[aboutLogoWidth], "\n") + 1
     83 	if m.aboutLogoRows >= total {
     84 		return nil
     85 	}
     86 	m.aboutLogoRows++
     87 	if m.aboutLogoRows >= total {
     88 		return nil
     89 	}
     90 	return aboutLogoRowCmd(m.aboutLogoGen)
     91 }
     92 
     93 // revealedLogo keeps the portrait's full size so the fetch lines never
     94 // shift, with the rows not drawn yet left blank.
     95 func revealedLogo(logo string, rows int) string {
     96 	lines := strings.Split(logo, "\n")
     97 	if rows >= len(lines) {
     98 		return logo
     99 	}
    100 	blank := strings.Repeat(" ", aboutLogoWidth)
    101 	for i := rows; i < len(lines); i++ {
    102 		lines[i] = blank
    103 	}
    104 	return strings.Join(lines, "\n")
    105 }