auto cover style: use sextants when fontconfig has a font with the glyphs
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,8 +43,8 @@ type UI struct {
|
||||
// block, e.g. kitty, WezTerm, foot, Ghostty), "kitty" (real images through
|
||||
// the Kitty graphics protocol with Unicode placeholders: kitty and
|
||||
// Ghostty) or "auto" (default: the Kitty protocol where the terminal is
|
||||
// known to support it, else sextant on terminals known to draw them, else
|
||||
// quadrant). The block styles are also the fallback for "kitty".
|
||||
// known to support it, else sextant on terminals known to draw them or when
|
||||
// fontconfig finds a font with the glyphs, else quadrant). The block styles are also the fallback for "kitty".
|
||||
CoverStyle string `json:"cover_style,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"image/color"
|
||||
_ "image/jpeg" // poster formats
|
||||
_ "image/png"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -138,9 +139,29 @@ func coverStyleFor(setting string, env func(string) string) coverStyle {
|
||||
if env("KITTY_WINDOW_ID") != "" || env("WEZTERM_PANE") != "" {
|
||||
return sextantStyle
|
||||
}
|
||||
// Other terminals (GNOME Terminal, Konsole, Alacritty...) draw glyphs their
|
||||
// font lacks from a fallback font, so sextants work if some installed font
|
||||
// has them. Over SSH the installed fonts are the remote machine's, which
|
||||
// says nothing about the terminal, so keep the safe style there.
|
||||
if env("SSH_CONNECTION") == "" && env("SSH_TTY") == "" && sextantFontAvailable() {
|
||||
return sextantStyle
|
||||
}
|
||||
return quadrantStyle
|
||||
}
|
||||
|
||||
// sextantFontAvailable reports whether fontconfig knows a font with the
|
||||
// sextant characters. It is a variable so tests can replace it.
|
||||
var sextantFontAvailable = func() bool {
|
||||
path, err := exec.LookPath("fc-list")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
out, err := exec.CommandContext(ctx, path, ":charset=1fb00", "family").Output()
|
||||
return err == nil && len(bytes.TrimSpace(out)) > 0
|
||||
}
|
||||
|
||||
// resolveCovers decides how posters are drawn: as real images through the
|
||||
// Kitty graphics protocol (graphics) or with block characters in the given
|
||||
// style. "kitty" asks for the protocol explicitly; "auto" uses it when the
|
||||
|
||||
@@ -223,7 +223,39 @@ func TestSextantCell(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoverStyleForFontCheck(t *testing.T) {
|
||||
defer func(old func() bool) { sextantFontAvailable = old }(sextantFontAvailable)
|
||||
env := func(m map[string]string) func(string) string { return func(k string) string { return m[k] } }
|
||||
|
||||
sextantFontAvailable = func() bool { return true }
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
env map[string]string
|
||||
want string
|
||||
}{
|
||||
{"unknown terminal with a suitable font", map[string]string{"TERM": "xterm-256color"}, "sextant"},
|
||||
{"over ssh the local fonts are unknown", map[string]string{"TERM": "xterm-256color", "SSH_CONNECTION": "1 2 3 4"}, "quadrant"},
|
||||
{"over ssh with a tty", map[string]string{"SSH_TTY": "/dev/pts/1"}, "quadrant"},
|
||||
} {
|
||||
if got := coverStyleFor("auto", env(tc.env)).name; got != tc.want {
|
||||
t.Errorf("%s: %s, want %s", tc.name, got, tc.want)
|
||||
}
|
||||
}
|
||||
// An explicit setting never asks fontconfig.
|
||||
sextantFontAvailable = func() bool { t.Error("fontconfig consulted for an explicit style"); return true }
|
||||
if coverStyleFor("quadrant", env(nil)).name != "quadrant" || coverStyleFor("sextant", env(nil)).name != "sextant" {
|
||||
t.Error("explicit styles must win")
|
||||
}
|
||||
// No suitable font: quadrants.
|
||||
sextantFontAvailable = func() bool { return false }
|
||||
if got := coverStyleFor("auto", env(map[string]string{"TERM": "xterm-256color"})).name; got != "quadrant" {
|
||||
t.Errorf("no font: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoverStyleFor(t *testing.T) {
|
||||
defer func(old func() bool) { sextantFontAvailable = old }(sextantFontAvailable)
|
||||
sextantFontAvailable = func() bool { return false } // the table below is about the terminal checks
|
||||
env := func(m map[string]string) func(string) string { return func(k string) string { return m[k] } }
|
||||
for _, tc := range []struct {
|
||||
setting string
|
||||
|
||||
@@ -143,6 +143,8 @@ func TestPlaceholderCell(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestKittyTerminalDetection(t *testing.T) {
|
||||
defer func(old func() bool) { sextantFontAvailable = old }(sextantFontAvailable)
|
||||
sextantFontAvailable = func() bool { return false } // not what these cases are about
|
||||
env := func(m map[string]string) func(string) string { return func(k string) string { return m[k] } }
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
|
||||
Reference in New Issue
Block a user