Files
gh-commit/internal/ui/ui.go
T
t 9647c854c9 feat: rewrite gh-commit in Go with local Ollama backend
Replaces the Python/Crush/DuckDB implementation with a CGO-free Go binary:
cobra CLI, modernc.org/sqlite storage, huh/lipgloss UI, and speed-tuned raw
/api/generate calls (think:false, keep_alive, capped num_predict) against a
local qwen3.5:2b. Ships as a gh extension via cli/gh-extension-precompile.
2026-08-15 19:10:16 -04:00

114 lines
3.9 KiB
Go

// Package ui centralizes lipgloss styling, huh confirmations, and spinners so
// every command shares the same visual language as the original Python tool.
package ui
import (
"fmt"
"os"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/huh/spinner"
"github.com/charmbracelet/lipgloss"
"golang.org/x/term"
)
// AutoConfirm short-circuits every confirmation prompt (GH_COMMIT_AUTO / --auto).
var AutoConfirm bool
var (
red = lipgloss.NewStyle().Foreground(lipgloss.Color("1"))
green = lipgloss.NewStyle().Foreground(lipgloss.Color("2"))
greenBold = lipgloss.NewStyle().Foreground(lipgloss.Color("2")).Bold(true)
yellow = lipgloss.NewStyle().Foreground(lipgloss.Color("3"))
yellowBold = lipgloss.NewStyle().Foreground(lipgloss.Color("3")).Bold(true)
magenta = lipgloss.NewStyle().Foreground(lipgloss.Color("5"))
magentaBold = lipgloss.NewStyle().Foreground(lipgloss.Color("5")).Bold(true)
cyan = lipgloss.NewStyle().Foreground(lipgloss.Color("6"))
dim = lipgloss.NewStyle().Faint(true)
bold = lipgloss.NewStyle().Bold(true)
panelStyle = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("5")).
Padding(0, 1)
)
// Println prints a blank line.
func Println() { fmt.Println() }
// Printf prints unstyled text with a trailing newline.
func Printf(format string, a ...any) { fmt.Printf(format+"\n", a...) }
// Errorf prints red text.
func Errorf(format string, a ...any) { fmt.Println(red.Render(fmt.Sprintf(format, a...))) }
// Successf prints green text.
func Successf(format string, a ...any) { fmt.Println(green.Render(fmt.Sprintf(format, a...))) }
// SuccessBoldf prints bold green text.
func SuccessBoldf(format string, a ...any) {
fmt.Println(greenBold.Render(fmt.Sprintf(format, a...)))
}
// Warnf prints yellow text.
func Warnf(format string, a ...any) { fmt.Println(yellow.Render(fmt.Sprintf(format, a...))) }
// WarnBoldf prints bold yellow text.
func WarnBoldf(format string, a ...any) { fmt.Println(yellowBold.Render(fmt.Sprintf(format, a...))) }
// Infof prints cyan text.
func Infof(format string, a ...any) { fmt.Println(cyan.Render(fmt.Sprintf(format, a...))) }
// Magentaf prints magenta text.
func Magentaf(format string, a ...any) { fmt.Println(magenta.Render(fmt.Sprintf(format, a...))) }
// MagentaBoldf prints bold magenta text.
func MagentaBoldf(format string, a ...any) {
fmt.Println(magentaBold.Render(fmt.Sprintf(format, a...)))
}
// Dimf prints faint text.
func Dimf(format string, a ...any) { fmt.Println(dim.Render(fmt.Sprintf(format, a...))) }
// Bold returns s rendered bold, for inline composition.
func Bold(s string) string { return bold.Render(s) }
// Panel prints text inside a magenta rounded-border panel.
func Panel(text string) { fmt.Println(panelStyle.Render(text)) }
// Cyan returns s rendered cyan, for inline composition.
func Cyan(s string) string { return cyan.Render(s) }
// Green returns s rendered green, for inline composition.
func Green(s string) string { return green.Render(s) }
// Yellow returns s rendered yellow, for inline composition.
func Yellow(s string) string { return yellow.Render(s) }
// Red returns s rendered red, for inline composition.
func Red(s string) string { return red.Render(s) }
// Confirm asks a yes/no question, defaulting to yes. AutoConfirm returns true
// immediately; a prompt failure (e.g. no TTY, Ctrl-C) counts as no.
func Confirm(msg string) bool {
if AutoConfirm {
return true
}
v := true
if err := huh.NewConfirm().Title(msg).Value(&v).Run(); err != nil {
return false
}
return v
}
// Spin runs fn behind a spinner titled title. Without a TTY (or in auto mode)
// it prints the title once and runs fn directly.
func Spin(title string, fn func()) {
if AutoConfirm || !term.IsTerminal(int(os.Stdout.Fd())) || !term.IsTerminal(int(os.Stdin.Fd())) {
Dimf("%s", title)
fn()
return
}
_ = spinner.New().Title(title).Action(fn).Run()
}