art

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Overview

Package art fetches cover art and draws it in the terminal: with the Kitty graphics protocol, iTerm2 inline images, or sixel when the terminal supports one of them, and with colored half-block characters otherwise.

Index

Constants

View Source
const QueryTimeout = 150 * time.Millisecond

QueryTimeout is how long Query waits for the terminal to answer.

View Source
const RemoteQueryTimeout = 600 * time.Millisecond

RemoteQueryTimeout is how long Query waits over SSH, where a reply can take a network round trip. A reply that came after the wait would be read as keystrokes once the view starts.

Variables

View Source
var CacheDir = func() string { return filepath.Join(paths.CacheDir(), "art") }

CacheDir is where fetched images are kept (CacheDir()/art by default).

View Source
var DefaultCell = CellSize{W: 10, H: 20}

DefaultCell is the cell size assumed when the terminal does not report one: about twice as tall as wide, like most terminal fonts.

View Source
var ErrNoArt = errors.New("no cover art for this result")

ErrNoArt means the result has no cover art source (no Apple Music artwork and a song_link that is not on lis.tn).

View Source
var HTTPClient = &http.Client{Timeout: 10 * time.Second}

HTTPClient fetches cover art. Tests replace it.

Functions

func Accent

func Accent(img image.Image, appleBG string) lipgloss.Color

Accent picks a color that matches the cover: Apple Music's artwork bgColor when known (a hex string such as "1a2b3c"), else the dominant saturated color of img. It returns "" (no color) when there is neither. Very dark or very light colors are pulled toward the middle so the accent stays readable on both dark and light terminals.

func DetectFullScreen

func DetectFullScreen(env func(string) string, isTTY bool, t Terminal) (Protocol, Probe)

DetectFullScreen picks the protocol for full-screen views, which redraw the screen as text and so need images that can be placed in cells:

  • Kitty and Ghostty: Kitty graphics with Unicode placeholders.
  • iTerm2 and WezTerm: iTerm2 inline images. WezTerm draws Kitty images but not Unicode placeholders.
  • VS Code: iTerm2 inline images when its terminal has images turned on, which it reports as sixel support in DA1.
  • Konsole, and any terminal whose DA1 reply lists sixel: sixel.
  • Everything else: half-blocks, in true color when available.

t is the terminal to query, or nil when stdin is not a terminal; it is asked for DA1 and its cell size unless AUDD_ART turns art off or picks half-blocks, or a multiplexer (tmux, screen) is in between, which would answer for itself. The wait is longer over SSH. AUDD_ART overrides the choice as in Detect.

func Fetch

func Fetch(ctx context.Context, songLink, appleArtworkURL string, size int) (image.Image, error)

Fetch downloads (or reads from the disk cache) the cover for a result and decodes it. size is the requested edge in pixels for Apple artwork.

func KittyDelete

func KittyDelete(id uint32) string

KittyDelete frees image id and its placements in the terminal.

func KittyPlaceholders

func KittyPlaceholders(id uint32, cols, rows int) string

KittyPlaceholders returns rows lines of cols Unicode placeholder cells (U+10EEEE) for image id. The foreground color carries the image id and two combining marks on each cell carry its row and column, so the cells show the image wherever they are drawn and can be moved, redrawn, or replaced like any other text.

func KittyTransmit

func KittyTransmit(img image.Image, id uint32, cols, rows int, cell CellSize) string

KittyTransmit sends img to the terminal under image id and creates a virtual placement of cols×rows cells (U=1), which KittyPlaceholders then shows wherever its cells are printed. q=2 silences the terminal's replies. id must be between 1 and 2^24-1.

func Render

func Render(img image.Image, cols, rows int, p Protocol) string

Render draws img in a box of cols×rows terminal cells. The half-block protocols return rows lines of colored "▀" cells separated by newlines; Kitty, iTerm2, and sixel return a single escape sequence that the terminal draws over cols×rows cells starting at the cursor. ProtoNone and an empty box return "".

Cover art is square and a cell is about twice as tall as it is wide, so cols = 2×rows keeps the picture square.

func RenderCell

func RenderCell(img image.Image, cols, rows int, p Protocol, cell CellSize) string

RenderCell is Render for a terminal whose cells are cell pixels in size, which sets the pixel size of images sent with Kitty, iTerm2, and sixel.

func SourceURL

func SourceURL(songLink, appleArtworkURL string, size int) string

SourceURL returns the image URL for a result: the Apple Music artwork template with {w}x{h} set to size, else song_link with ?thumb when the link is on lis.tn, else "".

func To256

func To256(r, g, b uint8) int

To256 maps a color to the nearest entry of the xterm 256-color palette (the 6×6×6 cube or the gray ramp).

Types

type CellSize

type CellSize struct{ W, H int }

CellSize is the size of one terminal character cell in pixels.

func CellSizeOf

func CellSizeOf(f *os.File) (CellSize, bool)

CellSizeOf returns the cell size of the terminal on f from the pixel fields of TIOCGWINSZ. Terminals that leave them at zero report false.

func (CellSize) ColsFor

func (c CellSize) ColsFor(rows int) int

ColsFor returns how many columns make a box of rows rows square on screen with this cell size. Half-blocks use it too: each half-block pixel is one cell wide and half a cell tall.

func (CellSize) Known

func (c CellSize) Known() bool

Known reports whether both sides are set.

func (CellSize) OrDefault

func (c CellSize) OrDefault() CellSize

OrDefault returns c, or DefaultCell when c is not known or implausible.

type Probe

type Probe struct {
	// Answered is set when the terminal replied to the device attributes
	// (DA1) query.
	Answered bool
	// Sixel is set when the DA1 reply lists sixel graphics (attribute 4).
	Sixel bool
	// Cell is the cell size from the CSI 16 t reply; zero when unknown.
	Cell CellSize
}

Probe is what a terminal said about itself.

func Query

func Query(t Terminal, timeout time.Duration) Probe

Query asks t for its cell size in pixels (CSI 16 t) and its device attributes (DA1, CSI c). Every terminal answers DA1, and answers in order, so the DA1 reply marks the end of the replies; Query stops there or after timeout.

type Protocol

type Protocol int

Protocol is a way of drawing an image in a terminal.

const (
	ProtoNone      Protocol = iota // no images (not a terminal, TERM=dumb, or turned off)
	ProtoKitty                     // Kitty graphics protocol (Kitty, Ghostty, WezTerm, Konsole)
	ProtoITerm2                    // iTerm2 inline images
	ProtoSixel                     // DEC sixel graphics
	ProtoHalfBlock                 // true-color "▀" cells
	// ProtoHalfBlock256 is the half-block fallback for terminals without
	// true color; colors are mapped to the 256-color palette.
	ProtoHalfBlock256
)

func Cells

func Cells(env func(string) string) Protocol

Cells returns the character-cell fallback for p: half-blocks in true color or 256 colors, following the terminal's color support.

func Detect

func Detect(env func(string) string, isTTY bool) Protocol

Detect picks the best protocol from the environment. Images are only drawn on a terminal. AUDD_ART (kitty, iterm2, sixel, blocks, blocks256, none) overrides the detection. Inside tmux or screen, which do not pass images through by default, it uses half-blocks.

Detect does not query the terminal. Sixel is found from TERM (names containing "sixel", foot, mlterm, contour); DetectFullScreen also asks the terminal (DA1) and so finds the others.

func (Protocol) IsGraphics

func (p Protocol) IsGraphics() bool

IsGraphics reports whether p draws real pixels (Kitty, iTerm2, sixel) rather than colored characters.

func (Protocol) String

func (p Protocol) String() string

String returns the name used by AUDD_ART.

type Terminal

type Terminal interface {
	io.Writer
	// ReadTimeout reads what the terminal sent, waiting at most d. It
	// returns 0 and a nil error when nothing arrived in time.
	ReadTimeout(p []byte, d time.Duration) (int, error)
}

Terminal is a terminal that can be asked questions: requests are written to it and replies read back with a timeout.

func OpenTTY

func OpenTTY(in, out *os.File) (Terminal, func())

OpenTTY returns a Terminal on in and out with in in raw mode, so replies are not echoed or held back until Enter, and a function that restores in. It returns nil when either is not a terminal.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL