Theme switch repaints every cell: cells are diffed by role name, so F4 left old-theme speckles on spaces and unchanged glyphs; fix a loop-less live stream test that never fired its timers

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 14:34:54 +02:00
co-authored by Claude Sonnet 5.5
parent b4bae3c34d
commit 1367373792
3 changed files with 27 additions and 4 deletions
+5 -2
View File
@@ -97,7 +97,7 @@ def _fromisoformat(text):
return parsed.replace(tzinfo=tzinfo)
raise ValueError("invalid datetime, use ISO like 2026-10-08T09:00")
VERSION = "1.33.0"
VERSION = "1.33.1"
WORKER_STEPS = 12
CREATE_SKILL_STEPS = 40
FORK_MAX_DEPTH = 2
@@ -13255,9 +13255,12 @@ class TuiApp:
self._need_paint = True
def set_theme(self, name):
"""Switch the global theme and redraw everything. Raises
"""Switch the global theme and repaint every cell. Cells are diffed
by style key (role names), which does not change with the theme, so
the compositor must forget what the terminal shows. Raises
ValueError naming the available themes when unknown."""
tui_set_theme(name)
self.compositor.invalidate()
self.dirty_paint()
return tui_theme()
+18
View File
@@ -5861,6 +5861,22 @@ class TuiThemeTests(unittest.TestCase):
with self.assertRaises(ValueError):
app.set_theme("nope")
def test_set_theme_repaints_every_cell_on_the_terminal(self):
"""Regression: after a theme switch only cells whose text changed got the
new colors, leaving old-theme speckles on spaces and unchanged glyphs."""
def screen(theme, switch_to=None):
tai.tui_set_theme(theme)
app = tai.TuiApp()
app.rows, app.cols = 10, 40
app.mount(tai.TuiText("hello world and more words", style=tai.TuiStyle(fg="role:fg", bg="role:bubble_in")))
vt = tai.TuiVtScreen(10, 40)
vt.feed(app.frame_bytes().encode("utf-8"))
if switch_to:
app.set_theme(switch_to)
vt.feed(app.frame_bytes().encode("utf-8"))
return vt.grid
self.assertEqual(screen("dark", "gruvbox"), screen("gruvbox"))
def test_cycle_order(self):
self.assertEqual(tai.tui_cycle_theme_name("dark"), "light")
self.assertEqual(tai.tui_cycle_theme_name("ansi"), "dark")
@@ -6207,8 +6223,10 @@ class TuiChatTests(unittest.TestCase):
app.offload(work, lambda value, error: done.append((value, error)))
for _ in range(300):
await asyncio.sleep(0.01)
app._fire_timers(time.time())
if done:
await asyncio.sleep(0.2)
app._fire_timers(time.time())
break
app.layout()
self.shown = app.render_text()
+4 -2
View File
@@ -242,8 +242,10 @@ render uncolored rather than crashing.
Selection precedence: `--theme` flag, `TAI_THEME` env var, `dark`
default; an unknown name prints the available list and exits 2.
`App.set_theme(name)` swaps the global theme and redraws everything;
F4 cycles registry order in the chat scene. To change a color, edit
`App.set_theme(name)` swaps the global theme and invalidates the compositor,
because cells are diffed by style key (role names), which a theme switch does
not change; without the invalidation only cells whose text changed got the
new colors and old-theme speckles stayed on spaces. F4 cycles registry order in the chat scene. To change a color, edit
one value in `_tui_builtin_themes()` or register a custom theme.
## Chat scene (`--tui`)