Initial scaffold from M1 spike (tiletopia)

Tauri 2 + Svelte 5 + xterm.js + portable-pty. Single full-window
WSL terminal pane with clickable distro picker. M1 verified manually
on Windows: window opens, xterm.js renders, claude TUI works,
resize reflows cleanly.

Graduated from ~/claude/ideas/wsl-mux/ per the approved plan at
~/.claude/plans/imperative-coalescing-feigenbaum.md. See memory.md
for decisions, open TODOs, and the M2-M5 roadmap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-05-22 12:31:29 +01:00
commit b352f8f049
36 changed files with 11534 additions and 0 deletions

57
src-tauri/src/commands.rs Normal file
View file

@ -0,0 +1,57 @@
//! Tauri command surface. Every JS-callable function lives here.
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use tauri::AppHandle;
use crate::pty::{list_wsl_distros, PaneId, PtyManager};
#[tauri::command]
pub async fn list_distros() -> Result<Vec<String>, String> {
list_wsl_distros().map_err(|e| e.to_string())
}
#[tauri::command]
pub async fn spawn_pane(
app: AppHandle,
manager: tauri::State<'_, PtyManager>,
distro: Option<String>,
cwd: Option<String>,
cols: u16,
rows: u16,
) -> Result<PaneId, String> {
manager
.spawn_wsl(app, distro, cwd, cols, rows)
.map_err(|e| e.to_string())
}
/// `data_b64` is base64-encoded UTF-8 bytes (xterm.js's `onData` emits
/// strings; the frontend encodes before sending).
#[tauri::command]
pub async fn write_to_pane(
manager: tauri::State<'_, PtyManager>,
id: PaneId,
data_b64: String,
) -> Result<(), String> {
let bytes = B64
.decode(data_b64.as_bytes())
.map_err(|e| format!("base64 decode: {e}"))?;
manager.write(id, &bytes).map_err(|e| e.to_string())
}
#[tauri::command]
pub async fn resize_pane(
manager: tauri::State<'_, PtyManager>,
id: PaneId,
cols: u16,
rows: u16,
) -> Result<(), String> {
manager.resize(id, cols, rows).map_err(|e| e.to_string())
}
#[tauri::command]
pub async fn kill_pane(
manager: tauri::State<'_, PtyManager>,
id: PaneId,
) -> Result<(), String> {
manager.kill(id).map_err(|e| e.to_string())
}