first commit
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/smallnest/pigo/internal/provider"
|
||||
"github.com/smallnest/pigo/internal/selfupdate"
|
||||
)
|
||||
|
||||
// This file builds the startup splash shown at the top of the transcript: the
|
||||
// pigo braille logo painted in a vertical rainbow gradient, with the session's
|
||||
// basic configuration (model, provider, protocol, thinking effort, directory)
|
||||
// laid out beside it. It is seeded once by withSession so it scrolls up as the
|
||||
// conversation grows, like a shell's login banner.
|
||||
|
||||
// logoLines is the pigo braille-art logo, one string per row.
|
||||
var logoLines = []string{
|
||||
"⣿⣿⣿⣿⡿⠟⠛⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠙⣿",
|
||||
"⣿⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼",
|
||||
"⣿⠋⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⢠⣤⣤⣤⠀⠀⠀⠀⠀⣤⣤⣤⣤⣤⣤⣾⣿",
|
||||
"⣧⠀⠀⠀⠀⣠⣾⣿⡇⠀⠀⠀⠀⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿",
|
||||
"⣿⣶⣤⣤⣾⣿⣿⣿⡇⠀⠀⠀⠀⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿",
|
||||
"⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⢀⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿",
|
||||
"⣿⣿⣿⣿⣿⣿⣿⡟⠀⠀⠀⠀⢸⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿",
|
||||
"⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠀⠀⣾⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿",
|
||||
"⣿⣿⣿⣿⣿⣿⡟⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⡿⠛⠛⢿⣿",
|
||||
"⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⡟⠀⠀⠀⠀⢻",
|
||||
"⣿⣿⣿⣿⡟⠁⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠛⠛⠁⠀⠀⠀⠀⣾",
|
||||
"⣿⣿⣿⡏⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⣿⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣿",
|
||||
"⣿⣿⣿⣿⣄⣀⣀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣄⣀⣀⣀⣀⣠⣴⣿⣿⣿",
|
||||
}
|
||||
|
||||
// logoColors is the top-to-bottom rainbow ramp painted across the logo rows
|
||||
// (ANSI 256-color cube): red → orange → yellow → green → cyan → blue.
|
||||
var logoColors = []string{
|
||||
"196", "202", "208", "214", "220", "190", "118",
|
||||
"46", "48", "50", "45", "39", "33",
|
||||
}
|
||||
|
||||
// renderBanner paints the logo gradient and joins it with a config panel showing
|
||||
// the session basics. Its only I/O is a single cheap read of the local
|
||||
// update-check cache (no network — CachedLatest); it never panics, so it is safe
|
||||
// to build eagerly at startup.
|
||||
func renderBanner(theme Theme, opts Options, cwd string) string {
|
||||
var logo strings.Builder
|
||||
for i, line := range logoLines {
|
||||
if i > 0 {
|
||||
logo.WriteByte('\n')
|
||||
}
|
||||
c := logoColors[i%len(logoColors)]
|
||||
logo.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color(c)).Render(line))
|
||||
}
|
||||
|
||||
title := lipgloss.NewStyle().Foreground(lipgloss.Color(colorAccent)).Bold(true)
|
||||
label := lipgloss.NewStyle().Foreground(lipgloss.Color(colorGray))
|
||||
value := lipgloss.NewStyle().Foreground(lipgloss.Color(colorUser)).Bold(true)
|
||||
|
||||
rows := [][2]string{
|
||||
{"Version", firstNonEmpty(opts.Version, "dev")},
|
||||
{"Model", firstNonEmpty(opts.Model, "—")},
|
||||
{"Provider", firstNonEmpty(opts.ProviderName, "—")},
|
||||
{"Protocol", firstNonEmpty(provider.ProtocolLabel(opts.Protocol), "—")},
|
||||
{"Thinking", firstNonEmpty(string(opts.ThinkingLevel), "off")},
|
||||
{"Directory", firstNonEmpty(cwd, "—")},
|
||||
}
|
||||
|
||||
// When the cached latest-release check says a newer version exists, append a
|
||||
// highlighted "→ vX.Y.Z" and an upgrade hint to the Version row. The check is
|
||||
// read from the local cache only (no network here); a background refresh keeps
|
||||
// it current for the next launch. dev/unparseable versions never trigger this.
|
||||
upgradeHint := ""
|
||||
if latest, _ := selfupdate.CachedLatest(); latest != "" {
|
||||
if avail, comparable := selfupdate.UpdateAvailable(opts.Version, latest); comparable && avail {
|
||||
newVer := lipgloss.NewStyle().Foreground(lipgloss.Color("214")).Bold(true).Render(latest)
|
||||
rows[0][1] = rows[0][1] + " → " + newVer
|
||||
upgradeHint = label.Render(strings.Repeat(" ", 11)) +
|
||||
lipgloss.NewStyle().Foreground(lipgloss.Color("214")).Render("Run pigo update to upgrade")
|
||||
}
|
||||
}
|
||||
|
||||
var info strings.Builder
|
||||
info.WriteString(title.Render("pigo") + " " + theme.System.Render("Terminal AI coding assistant") + "\n\n")
|
||||
for i, r := range rows {
|
||||
if i > 0 {
|
||||
info.WriteByte('\n')
|
||||
}
|
||||
info.WriteString(label.Render(fmt.Sprintf("%-10s ", r[0])) + value.Render(r[1]))
|
||||
}
|
||||
if upgradeHint != "" {
|
||||
info.WriteString("\n" + upgradeHint)
|
||||
}
|
||||
|
||||
return lipgloss.JoinHorizontal(lipgloss.Center, logo.String(), " ", info.String())
|
||||
}
|
||||
|
||||
// firstNonEmpty returns s when it is non-empty, otherwise the fallback.
|
||||
func firstNonEmpty(s, fallback string) string {
|
||||
if strings.TrimSpace(s) == "" {
|
||||
return fallback
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user