Cover style "halves": left and right half blocks only

Twice the horizontal detail of "cells", for terminals such as macOS
Terminal whose upper and lower half blocks do not meet at the middle of
the cell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019reK4DUGYqDUjniWYJvwUW
This commit is contained in:
2026-10-21 11:38:04 +02:00
co-authored by Claude Opus 5.5
parent 417e61da00
commit 8f162729ea
8 changed files with 30 additions and 9 deletions
+4 -3
View File
@@ -40,7 +40,8 @@ type UI struct {
// CoverStyle selects the block characters posters are drawn with:
// "quadrant" (2x2 pixels per cell, every font has the glyphs), "cells" (1
// pixel per cell, background colors only, for terminals that draw block
// glyphs unevenly such as macOS Terminal), "sextant"
// glyphs unevenly such as macOS Terminal), "halves" (2x1, left and right
// half blocks only, for the same terminals), "sextant"
// (2x3, sharper, needs a font with the Symbols for Legacy Computing
// block, e.g. kitty, WezTerm, foot, Ghostty), "kitty" (real images through
// the Kitty graphics protocol with Unicode placeholders: kitty and
@@ -266,9 +267,9 @@ func (c *Config) Validate() error {
}
}
switch c.UI.CoverStyle {
case "", "auto", "quadrant", "sextant", "cells", "kitty":
case "", "auto", "quadrant", "sextant", "halves", "cells", "kitty":
default:
errs = append(errs, fmt.Errorf(`ui.cover_style: %q must be "auto", "kitty", "sextant", "quadrant" or "cells"`, c.UI.CoverStyle))
errs = append(errs, fmt.Errorf(`ui.cover_style: %q must be "auto", "kitty", "sextant", "quadrant", "halves" or "cells"`, c.UI.CoverStyle))
}
return errors.Join(errs...)
}
+1 -1
View File
@@ -100,7 +100,7 @@ func TestShowCovers(t *testing.T) {
func TestCoverStyleValidation(t *testing.T) {
base := `{"radarr":{"url":"http://r","apikey":"k"},"sonarr":{"url":"http://s","apikey":"k"},"sabnzbd":{"url":"http://a","apikey":"k"},"ui":{"cover_style":`
for _, ok := range []string{`"auto"`, `"quadrant"`, `"sextant"`, `"cells"`, `"kitty"`, `""`} {
for _, ok := range []string{`"auto"`, `"quadrant"`, `"sextant"`, `"cells"`, `"halves"`, `"kitty"`, `""`} {
if _, err := Parse([]byte(base + ok + `}}`)); err != nil {
t.Errorf("%s rejected: %v", ok, err)
}
+6
View File
@@ -99,6 +99,10 @@ var (
// Coarse, but exact in terminals that draw block elements from the font
// with the halves off the cell's middle (macOS Terminal).
cellsStyle = coverStyle{"cells", 1, 1, func(int) rune { return ' ' }}
// halvesStyle (2×1 pixels): left and right half blocks only. Twice the
// detail of cells across, for terminals whose upper and lower halves do not
// meet at the middle but whose left and right ones do.
halvesStyle = coverStyle{"halves", 2, 1, func(mask int) rune { return [4]rune{' ', '▌', '▐', '█'}[mask] }}
)
// quadrants[mask] is the glyph that fills the quadrants set in mask
@@ -141,6 +145,8 @@ func coverStyleFor(setting string, env func(string) string) coverStyle {
return quadrantStyle
case "cells":
return cellsStyle
case "halves":
return halvesStyle
}
// Terminal.app draws block elements from the font, and their halves do
// not meet at the cell's middle: rows of a poster come out uneven.
+9
View File
@@ -277,6 +277,7 @@ func TestCoverStyleFor(t *testing.T) {
{"sextant", nil, "sextant"},
{"auto", map[string]string{"TERM": "xterm-256color", "TERM_PROGRAM": "Apple_Terminal"}, "cells"},
{"cells", map[string]string{"TERM": "xterm-kitty"}, "cells"},
{"halves", map[string]string{"TERM_PROGRAM": "Apple_Terminal"}, "halves"},
{"quadrant", map[string]string{"TERM_PROGRAM": "Apple_Terminal"}, "quadrant"},
} {
if got := coverStyleFor(tc.setting, env(tc.env)).name; got != tc.want {
@@ -358,3 +359,11 @@ func TestCellsStyleIsFlat(t *testing.T) {
t.Errorf("got %q %v %v", ch, fg, bg)
}
}
func TestHalvesStyle(t *testing.T) {
dark, light := color.RGBA{0, 0, 0, 255}, color.RGBA{250, 250, 250, 255}
ch, fg, bg := halvesStyle.bestCell([]color.RGBA{light, dark})
if ch != '▌' || fg != light || bg != dark {
t.Errorf("light left half: %q %v %v", ch, fg, bg)
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ type settingsValues struct {
notify, bell bool
}
var coverStyles = []string{"auto", "kitty", "sextant", "quadrant", "cells"}
var coverStyles = []string{"auto", "kitty", "sextant", "quadrant", "halves", "cells"}
// settingsFrom reads the settings in use.
func settingsFrom(c *config.Config) settingsValues {