Skip to content

Commit fbb3861

Browse files
chernistryclaude
andauthored
feat: wire retro-demoscene widgets into premium dashboard (#291)
Integrate the 4 retro-demoscene widget modules (CRT shader, plasma canvas, tracker view, oscilloscope) into the BernsteinApp dashboard with keybindings, visibility toggles, and live agent data flow. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0dc1793 commit fbb3861

2 files changed

Lines changed: 117 additions & 0 deletions

File tree

src/bernstein/cli/dashboard.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939

4040
from bernstein.cli.icons import get_agent_icon, get_icons, get_status_icon
4141
from bernstein.cli.visual_theme import PALETTE, budget_color, model_color, role_color, sample_gradient, status_color
42+
from bernstein.tui.crt_shader import CRTMode, CRTShader
43+
from bernstein.tui.oscilloscope import OscilloscopeWidget
44+
from bernstein.tui.plasma import PlasmaCanvas
45+
from bernstein.tui.tracker_view import TrackerView
4246

4347
logger = logging.getLogger(__name__)
4448

@@ -1367,6 +1371,23 @@ class BernsteinApp(App[None]):
13671371
overflow-y: auto;
13681372
border-right: heavy $border;
13691373
}
1374+
1375+
#retro-bar {
1376+
height: auto;
1377+
max-height: 20;
1378+
}
1379+
1380+
#plasma-canvas {
1381+
height: 12;
1382+
}
1383+
1384+
#tracker-view {
1385+
height: 16;
1386+
}
1387+
1388+
#oscilloscope {
1389+
height: 16;
1390+
}
13701391
"""
13711392

13721393
#: Resize debounce delay in seconds (TUI-001).
@@ -1385,6 +1406,10 @@ class BernsteinApp(App[None]):
13851406
Binding("d", "compare_task", "Diff"),
13861407
Binding("v", "compare_task", "Diff", show=False),
13871408
Binding("i", "inspect_task", "Open", show=False),
1409+
Binding("C", "toggle_crt", "CRT"),
1410+
Binding("P", "toggle_plasma", "Plasma"),
1411+
Binding("T", "toggle_tracker", "Tracker"),
1412+
Binding("O", "toggle_scope", "Scope"),
13881413
]
13891414

13901415
def __init__(self, **kw: Any) -> None:
@@ -1403,6 +1428,7 @@ def __init__(self, **kw: Any) -> None:
14031428
self._last_activity: list[str] = []
14041429
self._compare_mark: str | None = None # first task ID for compare
14051430
self._resize_timer: object | None = None # debounce timer handle (TUI-001)
1431+
self._crt_shader = CRTShader(CRTMode.OFF)
14061432

14071433
def compose(self) -> ComposeResult:
14081434
yield DashboardHeader(id="header-bar")
@@ -1416,6 +1442,10 @@ def compose(self) -> ComposeResult:
14161442
with Vertical(id="activity-bar"):
14171443
yield Static("ACTIVITY", classes="col-header")
14181444
yield RichLog(id="activity-log", wrap=True, markup=True, auto_scroll=True)
1445+
with Vertical(id="retro-bar"):
1446+
yield PlasmaCanvas(id="plasma-canvas")
1447+
yield TrackerView(id="tracker-view")
1448+
yield OscilloscopeWidget(id="oscilloscope")
14191449
with Horizontal(id="expert-row"):
14201450
yield ExpertCostPanel(id="expert-cost")
14211451
yield ExpertBanditPanel(id="expert-bandit")
@@ -1477,6 +1507,9 @@ def on_mount(self) -> None:
14771507
except Exception as exc:
14781508
logger.warning("Failed to read evolve.json: %s", exc)
14791509

1510+
# Retro-demoscene widgets hidden by default (toggled via keybindings)
1511+
self.query_one("#retro-bar").display = False
1512+
14801513
# Write startup messages to activity log
14811514
log = self.query_one("#activity-log", RichLog)
14821515
log.write(_format_activity_line("system", "Bernstein starting..."))
@@ -1529,6 +1562,7 @@ def _check_agents_file(self) -> None:
15291562
costs: dict[str, Any] = {}
15301563
self._update_agents(agents, costs)
15311564
self._update_activity(agents)
1565+
self._update_retro_widgets(agents)
15321566
except Exception:
15331567
pass
15341568

@@ -1644,6 +1678,7 @@ def _apply_data(self, data: dict[str, Any]) -> None:
16441678
}
16451679
self._update_stats(data.get("status"), tasks, data.get("agents", []), costs, monitoring)
16461680
self._update_activity(data.get("agents", []))
1681+
self._update_retro_widgets(data.get("agents", []))
16471682

16481683
# -- Agents --
16491684

@@ -2223,6 +2258,66 @@ def action_toggle_expert(self) -> None:
22232258
else:
22242259
self.notify("Novice mode [e] to toggle on", severity="information", timeout=3)
22252260

2261+
def action_toggle_crt(self) -> None:
2262+
"""Cycle CRT phosphor shader mode."""
2263+
mode = self._crt_shader.cycle_mode()
2264+
self.notify(f"CRT: {mode.value}", timeout=2)
2265+
2266+
def action_toggle_plasma(self) -> None:
2267+
"""Show/hide the demoscene plasma canvas."""
2268+
retro = self.query_one("#retro-bar")
2269+
plasma = self.query_one("#plasma-canvas", PlasmaCanvas)
2270+
plasma.display = not plasma.display
2271+
# Show retro bar if any retro widget is visible
2272+
retro.display = (
2273+
plasma.display
2274+
or self.query_one("#tracker-view", TrackerView).display
2275+
or self.query_one("#oscilloscope", OscilloscopeWidget).display
2276+
)
2277+
2278+
def action_toggle_tracker(self) -> None:
2279+
"""Show/hide the tracker-style task monitor."""
2280+
retro = self.query_one("#retro-bar")
2281+
tracker = self.query_one("#tracker-view", TrackerView)
2282+
tracker.display = not tracker.display
2283+
retro.display = (
2284+
self.query_one("#plasma-canvas", PlasmaCanvas).display
2285+
or tracker.display
2286+
or self.query_one("#oscilloscope", OscilloscopeWidget).display
2287+
)
2288+
2289+
def action_toggle_scope(self) -> None:
2290+
"""Show/hide the braille oscilloscope."""
2291+
retro = self.query_one("#retro-bar")
2292+
scope = self.query_one("#oscilloscope", OscilloscopeWidget)
2293+
scope.display = not scope.display
2294+
retro.display = (
2295+
self.query_one("#plasma-canvas", PlasmaCanvas).display
2296+
or self.query_one("#tracker-view", TrackerView).display
2297+
or scope.display
2298+
)
2299+
2300+
def _update_retro_widgets(self, agents: list[dict[str, Any]]) -> None:
2301+
"""Feed agent data to retro-demoscene widgets."""
2302+
plasma = self.query_one("#plasma-canvas", PlasmaCanvas)
2303+
if plasma.display:
2304+
plasma.update_activity(len(agents))
2305+
2306+
tracker = self.query_one("#tracker-view", TrackerView)
2307+
if tracker.display:
2308+
tracker.update_agents(agents)
2309+
2310+
scope = self.query_one("#oscilloscope", OscilloscopeWidget)
2311+
if scope.display:
2312+
scope.update_agents(agents)
2313+
samples: dict[str, float] = {}
2314+
for a in agents:
2315+
sid = a.get("session_id", a.get("id", ""))
2316+
if sid:
2317+
samples[sid] = a.get("tokens_per_sec", 0.0)
2318+
if samples:
2319+
scope.add_samples(samples)
2320+
22262321
def action_stop_bernstein(self) -> None:
22272322
"""Backward-compatible stop -- delegates to drain."""
22282323
self.action_graceful_quit()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""Tests for retro-demoscene widget integration in the dashboard."""
2+
3+
from __future__ import annotations
4+
5+
from bernstein.tui.crt_shader import CRTMode, CRTShader
6+
7+
8+
def test_crt_shader_integration():
9+
shader = CRTShader(CRTMode.OFF)
10+
mode = shader.cycle_mode()
11+
assert mode == CRTMode.AMBER
12+
assert shader.active
13+
14+
15+
def test_retro_widgets_importable():
16+
from bernstein.tui.oscilloscope import OscilloscopeWidget
17+
from bernstein.tui.plasma import PlasmaCanvas
18+
from bernstein.tui.tracker_view import TrackerView
19+
20+
assert PlasmaCanvas is not None
21+
assert TrackerView is not None
22+
assert OscilloscopeWidget is not None

0 commit comments

Comments
 (0)