diff --git a/cmd/vitals/main.go b/cmd/vitals/main.go index 39f87f6..8ff0636 100644 --- a/cmd/vitals/main.go +++ b/cmd/vitals/main.go @@ -22,7 +22,6 @@ import ( "github.com/Jason-Adam/vitals/internal/preset" "github.com/Jason-Adam/vitals/internal/render" "github.com/Jason-Adam/vitals/internal/stdin" - "github.com/lucasb-eyer/go-colorful" ) // installPath is the go install target for the update command. @@ -408,13 +407,15 @@ func queryLightBackground() (light bool, ok bool) { } // isLightColor returns true when a color's HSL lightness is >= 0.5. -// Mirrors lipgloss's unexported isDarkColor with the inverse condition. +// Uses the standard RGB-to-HSL lightness formula: L = (max + min) / 2. func isLightColor(c color.Color) bool { - col, ok := colorful.MakeColor(c) - if !ok { - return false // can't determine — assume dark (safe default) - } - _, _, l := col.Hsl() + r, g, b, _ := c.RGBA() + rf := float64(r) / 0xffff + gf := float64(g) / 0xffff + bf := float64(b) / 0xffff + mx := max(rf, gf, bf) + mn := min(rf, gf, bf) + l := (mx + mn) / 2 return l >= 0.5 } diff --git a/cmd/vitals/main_test.go b/cmd/vitals/main_test.go index 2a47abf..090104a 100644 --- a/cmd/vitals/main_test.go +++ b/cmd/vitals/main_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "image/color" "os" "path/filepath" "strings" @@ -260,6 +261,30 @@ func TestWatchAndRender_DetectsFileChange(t *testing.T) { } } +func TestIsLightColor(t *testing.T) { + tests := []struct { + name string + c color.Color + want bool + }{ + {"black", color.Black, false}, + {"white", color.White, true}, + {"dark gray", color.RGBA{R: 64, G: 64, B: 64, A: 255}, false}, + {"light gray", color.RGBA{R: 192, G: 192, B: 192, A: 255}, true}, + {"mid gray below threshold", color.RGBA{R: 126, G: 126, B: 126, A: 255}, false}, + {"mid gray at threshold", color.RGBA{R: 128, G: 128, B: 128, A: 255}, true}, + {"pure red", color.RGBA{R: 255, G: 0, B: 0, A: 255}, true}, + {"dark blue", color.RGBA{R: 0, G: 0, B: 128, A: 255}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isLightColor(tt.c); got != tt.want { + t.Errorf("isLightColor(%v) = %v, want %v", tt.c, got, tt.want) + } + }) + } +} + func TestFindCurrentTranscript_ReturnsNewest(t *testing.T) { tmp := t.TempDir() t.Setenv("HOME", tmp) diff --git a/go.mod b/go.mod index 25a5882..48b9c43 100644 --- a/go.mod +++ b/go.mod @@ -7,16 +7,16 @@ require ( github.com/BurntSushi/toml v1.6.0 github.com/charmbracelet/colorprofile v0.4.2 github.com/charmbracelet/x/ansi v0.11.6 - github.com/charmbracelet/x/term v0.2.2 - github.com/lucasb-eyer/go-colorful v1.3.0 ) require ( github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 // indirect + github.com/charmbracelet/x/term v0.2.2 // indirect github.com/charmbracelet/x/termios v0.1.1 // indirect github.com/charmbracelet/x/windows v0.2.2 // indirect github.com/clipperhouse/displaywidth v0.11.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect + github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/mattn/go-runewidth v0.0.21 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/internal/gather/gather.go b/internal/gather/gather.go index 7ed9736..8948df8 100644 --- a/internal/gather/gather.go +++ b/internal/gather/gather.go @@ -19,7 +19,6 @@ import ( "github.com/Jason-Adam/vitals/internal/git" "github.com/Jason-Adam/vitals/internal/model" "github.com/Jason-Adam/vitals/internal/transcript" - "github.com/charmbracelet/x/term" ) // transcriptWidgets are the widget names that require transcript data. @@ -265,7 +264,7 @@ func sessionStart(path string) string { // (defaultTerminalWidth) when this returns 0. func terminalWidth() int { for _, fd := range []uintptr{os.Stdin.Fd(), os.Stderr.Fd(), os.Stdout.Fd()} { - if w, _, err := term.GetSize(fd); err == nil && w > 0 { + if w := getTermWidth(fd); w > 0 { return w } } diff --git a/internal/gather/termsize_other.go b/internal/gather/termsize_other.go new file mode 100644 index 0000000..22547e6 --- /dev/null +++ b/internal/gather/termsize_other.go @@ -0,0 +1,7 @@ +//go:build !unix + +package gather + +// getTermWidth is a no-op on non-Unix platforms; the caller falls back +// to the COLUMNS env var. +func getTermWidth(_ uintptr) int { return 0 } diff --git a/internal/gather/termsize_unix.go b/internal/gather/termsize_unix.go new file mode 100644 index 0000000..f9693db --- /dev/null +++ b/internal/gather/termsize_unix.go @@ -0,0 +1,26 @@ +//go:build unix + +package gather + +import ( + "syscall" + "unsafe" +) + +// getTermWidth returns the terminal width for the given file descriptor +// via the TIOCGWINSZ ioctl, or 0 if the fd is not a terminal. +func getTermWidth(fd uintptr) int { + var ws struct { + Row, Col uint16 + Xpixel, Ypixel uint16 + } + _, _, err := syscall.Syscall( + syscall.SYS_IOCTL, fd, + syscall.TIOCGWINSZ, + uintptr(unsafe.Pointer(&ws)), + ) + if err != 0 || ws.Col == 0 { + return 0 + } + return int(ws.Col) +}