From f8ec96b88b845cea723edce511a9724476ff7984 Mon Sep 17 00:00:00 2001 From: shranchi0 Date: Thu, 19 Feb 2026 16:21:56 -0800 Subject: [PATCH 1/2] Add cherry blossom theme A light pink theme inspired by cherry blossoms with a lavender blush background, rose-pink header/status bar, and soft pink sidebar accents. Co-Authored-By: Claude Opus 4.6 --- tame/ui/themes/builtin/cherry_blossom.tcss | 110 +++++++++++++++++++++ tame/ui/themes/manager.py | 8 ++ 2 files changed, 118 insertions(+) create mode 100644 tame/ui/themes/builtin/cherry_blossom.tcss diff --git a/tame/ui/themes/builtin/cherry_blossom.tcss b/tame/ui/themes/builtin/cherry_blossom.tcss new file mode 100644 index 0000000..6371424 --- /dev/null +++ b/tame/ui/themes/builtin/cherry_blossom.tcss @@ -0,0 +1,110 @@ +/* TAME Cherry Blossom Theme */ + +Screen { + background: #fff0f5; + color: #4a3040; +} + +#header-bar { + dock: top; + height: 1; + background: #d4678e; + color: #ffffff; + padding: 0 1; +} + +#sidebar { + width: 28; + background: #ffe4ec; + border-right: solid #f0b8cc; +} + +#sidebar-search { + margin: 0 1; + height: 1; +} + +#session-list { + background: #ffe4ec; +} + +.session-item { + padding: 0 1; + height: 1; +} + +.session-item:hover { + background: #ffd0dc; +} + +.session-item.--highlight { + background: #ffb6c8; +} + +.session-item.status-active { + color: #5a9e5a; +} + +.session-item.status-idle { + color: #b0909e; +} + +.session-item.status-waiting { + color: #d4a058; +} + +.session-item.status-error { + color: #c44569; +} + +.session-item.status-done { + color: #5a9e5a; +} + +.session-item.status-paused { + color: #b0909e; +} + +#new-session-btn { + margin: 0 1; + height: 1; + background: #f0c0d0; + color: #4a3040; +} + +#main-panel { + background: #fff0f5; +} + +#session-header { + height: 2; + background: #ffe4ec; + padding: 0 1; + color: #8a607a; + border-bottom: solid #f0b8cc; +} + +#session-viewer { + background: #fff0f5; + color: #4a3040; +} + +#input-area { + dock: bottom; + height: 3; + background: #ffe4ec; + border-top: solid #f0b8cc; + padding: 0 1; +} + +#status-bar { + dock: bottom; + height: 1; + background: #d4678e; + color: #ffffff; + padding: 0 1; +} + +#toast-overlay { + layer: overlay; +} diff --git a/tame/ui/themes/manager.py b/tame/ui/themes/manager.py index 44c4cbf..f8cfafa 100644 --- a/tame/ui/themes/manager.py +++ b/tame/ui/themes/manager.py @@ -16,6 +16,7 @@ "gruvbox", "solarized_dark", "solarized_light", + "cherry_blossom", ] # Colors for widgets that actually exist in the app. @@ -77,6 +78,13 @@ "viewer": ("#fdf6e3", "#657b83"), "status": ("#268bd2", "#fdf6e3"), }, + "cherry_blossom": { + "screen": ("#fff0f5", "#4a3040"), + "header": ("#d4678e", "#ffffff"), + "sidebar": ("#ffe4ec", "#4a3040"), + "viewer": ("#fff0f5", "#4a3040"), + "status": ("#d4678e", "#ffffff"), + }, } From 0930ad68829c7d74793ede88230b50dcf0723900 Mon Sep 17 00:00:00 2001 From: shranchi0 Date: Thu, 19 Feb 2026 16:41:53 -0800 Subject: [PATCH 2/2] Add cherry blossom theme with falling petal animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new "cherry_blossom" theme featuring: - Very light pink (lavender blush) background - Rose-pink header and status bar accents - Soft pink sidebar - Animated falling ASCII cherry blossom petals (*, ., ~, °) that drift downward across the screen in various shades of pink - Petal animation activates only when the cherry_blossom theme is selected and deactivates when switching to other themes New files: - tame/ui/themes/builtin/cherry_blossom.tcss - tame/ui/widgets/cherry_blossom_petals.py --- tame/app.py | 14 +++ tame/ui/widgets/__init__.py | 2 + tame/ui/widgets/cherry_blossom_petals.py | 103 +++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 tame/ui/widgets/cherry_blossom_petals.py diff --git a/tame/app.py b/tame/app.py index 4d4afe4..cc0e9d2 100644 --- a/tame/app.py +++ b/tame/app.py @@ -32,6 +32,7 @@ from tame.ui.keys.manager import KeybindManager from tame.ui.themes.manager import ThemeManager from tame.ui.widgets import ( + CherryBlossomPetals, CommandPalette, ConfirmDialog, DiffViewer, @@ -343,6 +344,7 @@ def compose(self) -> ComposeResult: yield SessionSearchBar() yield StatusBar() yield ToastOverlay() + yield CherryBlossomPetals() def on_mount(self) -> None: loop = asyncio.get_running_loop() @@ -350,6 +352,12 @@ def on_mount(self) -> None: self.call_later(self._restore_tmux_sessions_async) self._start_resource_poll() self._start_tmux_health_check() + # Activate cherry blossom petals if starting with that theme. + if self._theme_manager.current == "cherry_blossom": + try: + self.query_one(CherryBlossomPetals).display = True + except Exception: + pass log.info("TAME started") # ------------------------------------------------------------------ @@ -641,6 +649,12 @@ def action_toggle_theme(self) -> None: sidebar.styles.color = sfg except Exception: pass + # Toggle cherry blossom petal animation. + try: + petals = self.query_one(CherryBlossomPetals) + petals.display = new_theme == "cherry_blossom" + except Exception: + pass log.info("Switched theme to '%s'", new_theme) def action_delete_session(self) -> None: diff --git a/tame/ui/widgets/__init__.py b/tame/ui/widgets/__init__.py index 4dc2aac..25b895e 100644 --- a/tame/ui/widgets/__init__.py +++ b/tame/ui/widgets/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations +from .cherry_blossom_petals import CherryBlossomPetals from .command_palette import CommandPalette from .confirm_dialog import ConfirmDialog from .diff_viewer import DiffViewer @@ -18,6 +19,7 @@ from .toast_overlay import ToastOverlay __all__ = [ + "CherryBlossomPetals", "CommandPalette", "ConfirmDialog", "DiffViewer", diff --git a/tame/ui/widgets/cherry_blossom_petals.py b/tame/ui/widgets/cherry_blossom_petals.py new file mode 100644 index 0000000..6927d4e --- /dev/null +++ b/tame/ui/widgets/cherry_blossom_petals.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +import random + +from rich.segment import Segment +from rich.style import Style +from textual.strip import Strip +from textual.timer import Timer +from textual.widget import Widget + + +class CherryBlossomPetals(Widget): + """Animated falling cherry blossom petals overlay. + + Only visible when the cherry_blossom theme is active. + Renders sparse ASCII petals drifting downward across the screen. + """ + + DEFAULT_CSS = """ + CherryBlossomPetals { + layer: overlay; + width: 1fr; + height: 1fr; + display: none; + } + """ + + PETAL_CHARS = ("*", ".", "~", "*", ".", "°") + PETAL_COLORS = ("#d4678e", "#e891a8", "#ffb6d9", "#ffc9dd", "#f0a0b8") + + can_focus = False + + def __init__(self) -> None: + super().__init__(id="cherry-blossom-petals") + # Each petal: [row_float, col_int, char, color_hex, speed_float] + self._petals: list[list] = [] + self._animation_timer: Timer | None = None + + def on_mount(self) -> None: + self._animation_timer = self.set_interval(0.15, self._tick) + + def _tick(self) -> None: + if not self.display: + return + + height = self.size.height + width = self.size.width + if height <= 0 or width <= 0: + return + + # Move existing petals downward with slight horizontal drift. + surviving: list[list] = [] + for petal in self._petals: + row, col, char, color, speed = petal + new_row = row + speed + new_col = col + random.choice([-1, 0, 0, 0, 1]) + if new_row < height and 0 <= new_col < width: + surviving.append([new_row, new_col, char, color, speed]) + + # Spawn a new petal occasionally (keep density low). + if random.random() < 0.35: + col = random.randint(0, max(0, width - 1)) + char = random.choice(self.PETAL_CHARS) + color = random.choice(self.PETAL_COLORS) + speed = random.choice([0.5, 1.0, 1.0, 1.5]) + surviving.append([0.0, col, char, color, speed]) + + self._petals = surviving + self.refresh() + + def render_line(self, y: int) -> Strip: + """Render a single line of the petal overlay.""" + width = self.size.width + if width <= 0: + return Strip.blank(width) + + # Collect petals on this row. + row_petals: dict[int, tuple[str, str]] = {} + for petal in self._petals: + row, col, char, color, _speed = petal + if int(row) == y and 0 <= int(col) < width: + row_petals[int(col)] = (char, color) + + if not row_petals: + return Strip.blank(width) + + # Build segments: spaces for empty cells, styled chars for petals. + segments: list[Segment] = [] + blank = Style() + i = 0 + while i < width: + if i in row_petals: + char, color = row_petals[i] + segments.append(Segment(char, Style(color=color, bold=True))) + i += 1 + else: + # Accumulate consecutive blank spaces. + start = i + while i < width and i not in row_petals: + i += 1 + segments.append(Segment(" " * (i - start), blank)) + + return Strip(segments, width)