Skip to content

Commit 7a94f45

Browse files
Kasper Jungeclaude
authored andcommitted
feat: redesign web UI with Dusk light theme and update CLI banner so the product feels warm, friendly, and cohesive
Replaces the dark GitHub-clone UI with a light, card-based design using the Dusk palette (violet primary, warm orange accent, mint highlights). Updates CLI banner gradient from Simpsons yellow/teal to violet-to-orange. Includes the full UI backend (FastAPI + WebSocket event streaming + SQLite persistence) and engine module that powers the web dashboard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent db48388 commit 7a94f45

23 files changed

Lines changed: 4400 additions & 221 deletions

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ classifiers = [
2020
]
2121
dependencies = ["typer>=0.9", "rich>=13.0"]
2222

23+
[project.optional-dependencies]
24+
ui = ["fastapi>=0.110", "uvicorn[standard]>=0.29", "aiosqlite>=0.20", "websockets>=12.0"]
25+
2326
[project.scripts]
2427
ralph = "ralphify:main"
2528

2629
[dependency-groups]
2730
dev = [
31+
"aiosqlite>=0.20",
2832
"mkdocs>=1.6.1",
2933
"mkdocs-material>=9.7.5",
3034
"pytest>=8.0",
@@ -41,6 +45,7 @@ build-backend = "hatchling.build"
4145

4246
[tool.hatch.build.targets.wheel]
4347
packages = ["src/ralphify"]
48+
exclude = ["src/ralphify/ui/frontend/"]
4449

4550
[tool.pytest.ini_options]
4651
testpaths = ["tests"]

src/ralphify/_events.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""Event types and emitter protocol for the run loop.
2+
3+
The run engine emits structured events during execution. CLI mode renders
4+
them to the terminal via ``ConsoleEmitter``. UI mode pushes them through
5+
a ``QueueEmitter`` into the web layer.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import queue
11+
from dataclasses import dataclass, field
12+
from datetime import datetime, timezone
13+
from enum import Enum
14+
from typing import Any, Protocol, runtime_checkable
15+
16+
17+
class EventType(Enum):
18+
"""All event types emitted by the run loop."""
19+
20+
RUN_STARTED = "run_started"
21+
RUN_STOPPED = "run_stopped"
22+
RUN_PAUSED = "run_paused"
23+
RUN_RESUMED = "run_resumed"
24+
ITERATION_STARTED = "iteration_started"
25+
ITERATION_COMPLETED = "iteration_completed"
26+
ITERATION_FAILED = "iteration_failed"
27+
ITERATION_TIMED_OUT = "iteration_timed_out"
28+
CHECKS_STARTED = "checks_started"
29+
CHECKS_COMPLETED = "checks_completed"
30+
CHECK_PASSED = "check_passed"
31+
CHECK_FAILED = "check_failed"
32+
CONTEXTS_RESOLVED = "contexts_resolved"
33+
PROMPT_ASSEMBLED = "prompt_assembled"
34+
PRIMITIVES_RELOADED = "primitives_reloaded"
35+
SETTINGS_CHANGED = "settings_changed"
36+
LOG_MESSAGE = "log_message"
37+
38+
39+
@dataclass
40+
class Event:
41+
"""A structured event emitted by the run loop."""
42+
43+
type: EventType
44+
run_id: str
45+
data: dict[str, Any] = field(default_factory=dict)
46+
timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
47+
48+
49+
@runtime_checkable
50+
class EventEmitter(Protocol):
51+
"""Protocol for objects that receive run-loop events."""
52+
53+
def emit(self, event: Event) -> None: ...
54+
55+
56+
class NullEmitter:
57+
"""Discards all events (useful for testing)."""
58+
59+
def emit(self, event: Event) -> None:
60+
pass
61+
62+
63+
class QueueEmitter:
64+
"""Pushes events into a :class:`queue.Queue` for async consumption."""
65+
66+
def __init__(self, q: queue.Queue[Event] | None = None) -> None:
67+
self.queue: queue.Queue[Event] = q or queue.Queue()
68+
69+
def emit(self, event: Event) -> None:
70+
self.queue.put(event)

0 commit comments

Comments
 (0)