feat(palette): sixteen ANSI colour ladders, cycled with c

This commit is contained in:
beefcarver committed 2026-11-04 08:55:51 -05:00
1 parent 253b462df1
commit 43ddd8666b
14 files changed
+622 -111

No files matched your search

+27 -2
View File
@@ -22,7 +22,7 @@ from textual.strip import Strip
from textual.widget import Widget
from .. import artwork
from .common import art_lines
from .common import active_stops, art_lines
_NULL_STYLE = Style()
_NULL_CONTROL = ((ControlType.CURSOR_FORWARD, 0),)
@@ -308,6 +308,8 @@ class ArtPane(Widget):
self._last_data: bytes | None = None
self._last_size: tuple[int, int] = (0, 0)
self._mosaic_key = ""
#: True while a modal is up: the inline image would sit over the menu.
self._suspended = False
# ------------------------------------------------------------------ #
# mosaic background
@@ -320,7 +322,12 @@ class ArtPane(Widget):
return Text("")
width = max(1, self.size.width)
height = max(1, self.size.height)
return Group(*art_lines(_art_key(app), width, height))
if artwork.supported() and app.artwork_bytes is not None:
# A real image occupies this box (letterboxed, so not all of it).
# Leave the space blank: the patterned mosaic behind a scaled
# picture reads as noise around its edges.
return Group(*[Text(" " * width) for _ in range(height)])
return Group(*art_lines(_art_key(app), width, height, active_stops(app)))
# ------------------------------------------------------------------ #
# image overlay
@@ -337,9 +344,27 @@ class ArtPane(Widget):
def on_unmount(self) -> None:
self._drop_image_widget()
def suspend(self) -> None:
"""Take the inline image down so a modal is not drawn underneath it.
Kitty and iTerm2 paint images into cells the terminal owns; Textual
repainting the menu over them does not remove them, so the artwork has
to be deleted and re-emitted rather than covered.
"""
self._suspended = True
self._drop_image_widget()
self.refresh(layout=True)
def resume(self) -> None:
"""Put the artwork back after the modal closes."""
self._suspended = False
self.sync_image(force=True)
def sync_image(self, *, force: bool = False) -> None:
# Keep the mosaic behind the image in sync with the current track.
self.refresh()
if self._suspended:
return
app = cast("BeefyPlayerApp", self.app)
station = app.current