feat(hh): ratatui TUI client — chat, live roster, themes

- Connect subcommand: SRP auth then a ratatui UI over tokio + crossterm.
- Async ws (tokio-tungstenite); reader task decrypts/parses frames into events.
- Panes: top bar (e2e + house N/cap), chat scrollback, roster (self marked ⛧),
  input box. Undecryptable frames surface as a system line, not a silent drop.
- Themes (vestments) via TOML --theme; default occult-monochrome + neon.
- Verified live in tmux: render, chat round-trip, roster, join/leave.
- Adds fernet python->rust interop regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-05-30 13:57:07 -07:00
parent bb1d662ee1
commit 14aa369fb2
10 changed files with 1121 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
//! Loadable colour/layout themes ("vestments"). Default is the churchofmalware
//! occult-monochrome: black ground, white/grey ink, ⛧ accents. Override with a
//! TOML file via `--theme <path>`.
use ratatui::style::Color;
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct Theme {
pub name: String,
pub border: Color,
pub title: Color,
pub accent: Color,
pub dim: Color,
pub me: Color,
pub other: Color,
pub system: Color,
pub input: Color,
pub roster_me: Color,
/// Width of the roster column.
pub roster_width: u16,
}
impl Default for Theme {
fn default() -> Self {
Self {
name: "crypt".into(),
border: Color::DarkGray,
title: Color::White,
accent: Color::White,
dim: Color::DarkGray,
me: Color::White,
other: Color::Gray,
system: Color::DarkGray,
input: Color::White,
roster_me: Color::White,
roster_width: 22,
}
}
}
impl Theme {
pub fn load(path: &str) -> anyhow::Result<Self> {
let s = std::fs::read_to_string(path)?;
Ok(toml::from_str(&s)?)
}
}