"""Kao TUI — developer test tool for firing events, faces, and sounds.""" import sys import requests from textual.app import App, ComposeResult from textual.binding import Binding from textual.widgets import Footer, Header, ListView, ListItem, Label, TabbedContent, TabPane BASE_URL = sys.argv[1].rstrip("/") if len(sys.argv) > 1 else "http://localhost:5100" SOUNDS = [ "chime", "alert", "warning", "critical", "success", "notify", "doorbell", "knock", "ding", "blip", "siren", "tada", "ping", "bubble", "fanfare", "alarm", "none", ] FACES = [ {"emote": "( ^_^)", "animation": "breathing", "color": "#00FF00", "desc": "calm"}, {"emote": "( ˙▿˙)", "animation": "floating", "color": "#00FF00", "desc": "content"}, {"emote": "(◕‿◕)", "animation": "bouncing", "color": "#00FF00", "desc": "cheerful"}, {"emote": "( ・ω・)", "animation": "swaying", "color": "#00FF00", "desc": "curious"}, {"emote": "( ˘▽˘)", "animation": "breathing", "color": "#00FF00", "desc": "cozy"}, {"emote": "( -_^)", "animation": "blink", "color": "#00FF00", "desc": "wink"}, {"emote": "( x_x)", "animation": "shaking", "color": "#FF0000", "desc": "critical"}, {"emote": "( o_o)", "animation": "breathing", "color": "#FFFF00", "desc": "warning"}, {"emote": "( 'o')", "animation": "popping", "color": "#0088FF", "desc": "notify"}, {"emote": r"\(^o^)/", "animation": "celebrating", "color": "#00FF00", "desc": "celebrate"}, {"emote": "( -_-)zzZ", "animation": "sleeping", "color": "#333333", "desc": "sleep"}, {"emote": "( ?.?)", "animation": "searching", "color": "#888888", "desc": "lost"}, ] EVENTS = [ {"label": "Critical (priority 1)", "id": "tui_1", "priority": 1, "message": "TUI Critical"}, {"label": "Warning (priority 2)", "id": "tui_2", "priority": 2, "message": "TUI Warning"}, {"label": "Notify (priority 3)", "id": "tui_3", "priority": 3, "message": "TUI Notify"}, {"label": "── Clear all ──", "id": None}, ] CONTROLS = [ {"label": "Sleep", "action": "sleep"}, {"label": "Wake", "action": "wake"}, {"label": "Clear all events", "action": "clear-all"}, ] def post(path: str, data: dict | None = None) -> bool: try: resp = requests.post(f"{BASE_URL}/{path}", json=data or {}, timeout=5) return resp.ok except Exception: return False class KaoTUI(App): TITLE = f"Kao TUI — {BASE_URL}" CSS = """ ListView { height: 1fr; border: none; } ListItem { padding: 0 2; } """ BINDINGS = [ Binding("q", "quit", "Quit"), ] def compose(self) -> ComposeResult: yield Header() with TabbedContent("Sounds", "Faces", "Events", "Controls"): with TabPane("Sounds", id="tab-sounds"): with ListView(id="list-sounds"): for name in SOUNDS: yield ListItem(Label(name), id=f"sound-{name}") with TabPane("Faces", id="tab-faces"): with ListView(id="list-faces"): for face in FACES: label = f"{face['emote']} {face['animation']} {face['desc']}" yield ListItem(Label(label), id=f"face-{face['desc']}") with TabPane("Events", id="tab-events"): with ListView(id="list-events"): for ev in EVENTS: yield ListItem(Label(ev["label"]), id=f"event-{ev['id'] or 'clearall'}") with TabPane("Controls", id="tab-controls"): with ListView(id="list-controls"): for ctrl in CONTROLS: yield ListItem(Label(ctrl["label"]), id=f"ctrl-{ctrl['action']}") yield Footer() def on_list_view_selected(self, event: ListView.Selected) -> None: item_id = event.item.id or "" if item_id.startswith("sound-"): name = item_id[len("sound-"):] ok = post("notify", {"message": f"sound: {name}", "sound": name, "duration": 5}) self.notify(f"♪ {name} {'sent' if ok else 'FAILED'}", severity="information" if ok else "error") elif item_id.startswith("face-"): desc = item_id[len("face-"):] face = next((f for f in FACES if f["desc"] == desc), None) if face: ok = post("notify", { "emote": face["emote"], "animation": face["animation"], "color": face["color"], "message": f"face: {face['desc']}", "duration": 5, }) self.notify(f"{face['emote']} {face['desc']} {'sent' if ok else 'FAILED'}", severity="information" if ok else "error") elif item_id.startswith("event-"): suffix = item_id[len("event-"):] if suffix == "clearall": ok = post("clear-all") self.notify(f"clear-all {'sent' if ok else 'FAILED'}", severity="information" if ok else "error") else: ev = next((e for e in EVENTS if e["id"] == suffix), None) if ev: ok = post("event", {"id": ev["id"], "priority": ev["priority"], "message": ev["message"], "ttl": 10}) self.notify(f"{ev['label'].strip()} {'sent' if ok else 'FAILED'}", severity="information" if ok else "error") elif item_id.startswith("ctrl-"): action = item_id[len("ctrl-"):] ok = post(action) self.notify(f"{action} {'sent' if ok else 'FAILED'}", severity="information" if ok else "error") if __name__ == "__main__": KaoTUI().run()