Show posters as real images in kitty and Ghostty (Kitty graphics with Unicode placeholders)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+49
-1
@@ -52,6 +52,8 @@ type App struct {
|
||||
bell bool
|
||||
covers bool // poster thumbnails enabled
|
||||
coverStyle coverStyle
|
||||
gfx *kittyGraphics // real-image posters, nil when not used
|
||||
cellAspect float64 // height of a terminal cell divided by its width
|
||||
coverCache *coverCache
|
||||
}
|
||||
|
||||
@@ -99,10 +101,16 @@ func New(cfg *config.Config) *App {
|
||||
notifyDone: cfg.UI.ShouldNotifyDownloads(),
|
||||
bell: cfg.UI.Bell,
|
||||
covers: cfg.UI.ShowCovers(),
|
||||
coverStyle: coverStyleFor(cfg.UI.CoverStyle, os.Getenv),
|
||||
cellAspect: 2,
|
||||
coverCache: newCoverCache(),
|
||||
}
|
||||
|
||||
blocks, graphics := resolveCovers(cfg.UI.CoverStyle, os.Getenv)
|
||||
a.coverStyle = blocks
|
||||
if graphics {
|
||||
a.gfx = newKittyGraphics(a.writeTerminal)
|
||||
}
|
||||
|
||||
radarrClient := radarr.NewClient(cfg.Radarr.URL, cfg.Radarr.APIKey)
|
||||
sonarrClient := sonarr.NewClient(cfg.Sonarr.URL, cfg.Sonarr.APIKey)
|
||||
a.logs = newLogView(a)
|
||||
@@ -149,6 +157,7 @@ func New(cfg *config.Config) *App {
|
||||
|
||||
a.app.SetBeforeDrawFunc(func(s tcell.Screen) bool {
|
||||
a.screen = s
|
||||
a.cellAspect = cellAspectOf(s)
|
||||
return false
|
||||
})
|
||||
a.app.SetRoot(&toastRoot{Flex: layout, app: a}, true).EnableMouse(false)
|
||||
@@ -158,6 +167,40 @@ func New(cfg *config.Config) *App {
|
||||
return a
|
||||
}
|
||||
|
||||
// writeTerminal sends raw bytes (graphics commands) to the terminal through the
|
||||
// screen's own connection. UI goroutine only, so it stays in step with drawing.
|
||||
func (a *App) writeTerminal(b []byte) error {
|
||||
if a.screen == nil {
|
||||
return fmt.Errorf("no screen yet")
|
||||
}
|
||||
tty, ok := a.screen.Tty()
|
||||
if !ok {
|
||||
return fmt.Errorf("the screen has no terminal")
|
||||
}
|
||||
_, err := tty.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// cellAspectOf measures the height of a terminal cell divided by its width in
|
||||
// pixels; 2 when the terminal does not report pixel sizes.
|
||||
func cellAspectOf(s tcell.Screen) float64 {
|
||||
if tty, ok := s.Tty(); ok {
|
||||
if ws, err := tty.WindowSize(); err == nil && ws.Width > 0 && ws.Height > 0 && ws.PixelWidth > 0 && ws.PixelHeight > 0 {
|
||||
return (float64(ws.PixelHeight) / float64(ws.Height)) / (float64(ws.PixelWidth) / float64(ws.Width))
|
||||
}
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
// coverMaxHeight is how tall the stored poster copies are: real images keep
|
||||
// more detail than block characters need.
|
||||
func (a *App) coverMaxHeight() int {
|
||||
if a.gfx != nil {
|
||||
return graphicsCoverHeight
|
||||
}
|
||||
return storedCoverHeight
|
||||
}
|
||||
|
||||
// SetService records the outcome of a request to a service and updates the
|
||||
// connection indicators. UI goroutine only. A nil err means reachable.
|
||||
func (a *App) SetService(name, version string, err error) {
|
||||
@@ -246,6 +289,11 @@ func (a *App) post(f func()) {
|
||||
func (a *App) Run() error {
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
defer func() {
|
||||
if a.gfx != nil {
|
||||
a.gfx.deleteAll()
|
||||
}
|
||||
}()
|
||||
go a.watchServices(stop)
|
||||
if a.notifyDone {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
Reference in New Issue
Block a user