Colors no longer come from the terminal's palette, whose color 0 many color themes set to a grey that is not their background. A theme sets tview's default styles and the color names used in texts, all chosen from entries 16-255 of the 256-color table so terminals without 24-bit color show them exactly. "auto" picks light or dark from the background color the terminal reports. Settable in the Config view, applied at the next start (widgets copy their colors when created). Replaces using the terminal's own background and text colors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019reK4DUGYqDUjniWYJvwUW
71 lines
2.0 KiB
Go
71 lines
2.0 KiB
Go
package ui
|
|
|
|
import (
|
|
"bytes"
|
|
"image/color"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// terminalInfo is what the terminal said about itself at startup.
|
|
type terminalInfo struct {
|
|
graphics bool // the Kitty graphics protocol is supported
|
|
bg color.RGBA // its background color, if hasBG
|
|
hasBG bool
|
|
}
|
|
|
|
var (
|
|
terminalOnce sync.Once
|
|
terminalResult terminalInfo
|
|
)
|
|
|
|
// terminalAnswers asks the terminal once (see probeTerminal) and remembers the
|
|
// answers. The first call must happen before the screen is set up, which owns
|
|
// the terminal afterwards; New makes it. It is a variable so tests can replace it.
|
|
var terminalAnswers = func() terminalInfo {
|
|
terminalOnce.Do(func() { terminalResult = probeTerminal(300 * time.Millisecond) })
|
|
return terminalResult
|
|
}
|
|
|
|
// parseTerminalAnswers reads the answers to probeTerminal's questions.
|
|
func parseTerminalAnswers(got []byte) (info terminalInfo) {
|
|
info.graphics = bytes.Contains(got, []byte("_Gi=31;OK"))
|
|
info.bg, info.hasBG = parseColorAnswer(got, "11")
|
|
return info
|
|
}
|
|
|
|
// parseColorAnswer finds the answer to an OSC color query such as 11
|
|
// (background): ESC ] 11 ; rgb:RRRR/GGGG/BBBB, ended by ST or BEL. Each
|
|
// component has 1 to 4 hex digits.
|
|
func parseColorAnswer(got []byte, osc string) (color.RGBA, bool) {
|
|
prefix := []byte("\x1b]" + osc + ";rgb:")
|
|
i := bytes.Index(got, prefix)
|
|
if i < 0 {
|
|
return color.RGBA{}, false
|
|
}
|
|
rest := got[i+len(prefix):]
|
|
if end := bytes.IndexAny(rest, "\x1b\a"); end >= 0 {
|
|
rest = rest[:end]
|
|
}
|
|
parts := strings.Split(string(rest), "/")
|
|
if len(parts) != 3 {
|
|
return color.RGBA{}, false
|
|
}
|
|
var c [3]uint8
|
|
for j, p := range parts {
|
|
v, err := strconv.ParseUint(p, 16, 16)
|
|
if err != nil || len(p) < 1 || len(p) > 4 {
|
|
return color.RGBA{}, false
|
|
}
|
|
c[j] = uint8(v * 255 / (1<<(4*len(p)) - 1))
|
|
}
|
|
return color.RGBA{c[0], c[1], c[2], 255}, true
|
|
}
|
|
|
|
// luminance is the relative brightness of c, from 0 (black) to 1 (white).
|
|
func luminance(c color.RGBA) float64 {
|
|
return (0.2126*float64(c.R) + 0.7152*float64(c.G) + 0.0722*float64(c.B)) / 255
|
|
}
|