// Theme token ownership and ANSI background continuity are cross-screen
// visual invariants. Both fail silently in a real terminal — a screen that
// drifts from the palette, a selection tint cut short by a reset — and no
// daemon e2e observes terminal rendering.
package tui
import (
"os"
"regexp"
"strings"
"testing"
)
// Colors are chosen in the theme alone. A screen that picks its own drifts
// from the rest, which is how the session screen grew a second palette.
func TestColorsComeFromTheTheme(t *testing.T) {
owners := map[string]bool{"theme.go": true, "ink.go": true, "ink_unix.go": true, "ink_other.go": true}
raw := regexp.MustCompile(`lipgloss\.Color\(|AdaptiveColor|\\x1b\[(3\d|4\d|9\d|7|38;|48;)|Faint\(true\)|Reverse\(true\)|"#[0-9a-fA-F]{6}"|\.Foreground\(|\)\.Background\(`)
entries, err := os.ReadDir(".")
if err != nil {
t.Fatal(err)
}
for _, e := range entries {
name := e.Name()
if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") || owners[name] {
continue
}
src, err := os.ReadFile(name)
if err != nil {
t.Fatal(err)
}
for i, line := range strings.Split(string(src), "\n") {
if raw.MatchString(line) {
t.Errorf("%s:%d picks its own color, use a theme token: %s", name, i+1, strings.TrimSpace(line))
}
}
}
}
// A full reset comes as "ESC[m" from Lip Gloss and "ESC[0m" from the raw ink;
// the selection surface and a diff row's tint must survive both.
func TestBackgroundsOutliveStyledSpans(t *testing.T) {
span := DefaultStyles.Muted.Render("a")
marked := DefaultStyles.Selected.Render("\x00")
open := marked[:strings.IndexByte(marked, 0)]
if line := selectedLine(span+" b", 0); !strings.Contains(line, open+" b") {
t.Errorf("selection surface ends at the first span: %q", line)
}
if kept := keepBackground(span); strings.Contains(kept, "\x1b[m") || strings.Contains(kept, "\x1b[0m") {
t.Errorf("a span still resets the row tint: %q", kept)
}
}