Stained Glass is a Tauri v2 desktop app for wrapping an interactive Claude session in a split-pane native GUI: live terminal on the left, rendered markdown preview on the right.
The MVP contains a Rust/Tauri backend scaffold, rmux session lifecycle helpers, PTY/event wiring, pane snapshot extraction, a static split-pane frontend, and the stain CLI workspace crate for shell-level session control.
stained-glass/
├── Cargo.toml # Cargo workspace root
├── package.json # npm scripts for scaffold and CLI shape checks
├── scripts/
│ ├── check-scaffold.mjs # verifies frontend/Tauri scaffold files/config
│ └── check-stain-cli.mjs # verifies the CLI crate shape without Cargo
├── stain/ # CLI companion binary crate
│ ├── Cargo.toml
│ └── src/main.rs # `stain` open/attach/list/kill/snap
├── src-tauri/
│ ├── Cargo.toml # Tauri app crate
│ ├── tauri.conf.json # Tauri v2 configuration
│ ├── build.rs
│ └── src/
│ ├── main.rs
│ ├── session.rs
│ ├── pty.rs
│ ├── snapshot.rs
│ └── ipc.rs
└── src/ # framework-free static frontend shell
├── index.html
├── style.css
├── ipc.js
├── terminal.js
├── preview.js
└── vendor/ # static MVP adapters for terminal/markdown/highlight APIs
Install these before running the full app locally:
- Rust toolchain with Cargo. Dependency validation in #4 found Rust
1.88+is the safer effective baseline for current Tauri v2 transitive dependencies. - Node.js
18+and npm. rmuxonPATHfor session lifecycle, attach, list, kill, and snapshot commands.claudeonPATHfor the default session command.- Tauri system dependencies for your OS:
- Linux: WebKitGTK and related packages from the Tauri Linux prerequisites.
- macOS: Xcode Command Line Tools.
Useful prerequisite probes:
node --version
npm --version
rustc --version
cargo --version
command -v rmux
command -v claudegit clone https://github.com/zpyoung/stained-glass.git
cd stained-glass
npm installIf you only need the lightweight static checks, npm install is not currently required because the check scripts use Node's standard library.
Lightweight checks that do not require Rust, Cargo, Tauri system dependencies, rmux, or claude:
npm run check:scaffold
npm run check:stain
node --check scripts/check-scaffold.mjs
node --check scripts/check-stain-cli.mjs
node --check src/ipc.js
node --check src/terminal.js
node --check src/preview.js
node --check src/vendor/xterm.js
node --check src/vendor/marked.js
node --check src/vendor/highlight.js
python3 -m json.tool src-tauri/tauri.conf.json >/dev/nullGitHub Actions runs these checks on pushes and pull requests, then validates the Rust/Tauri toolchain on Ubuntu by installing the Tauri Linux prerequisites, running workspace tests/builds, building the Tauri bundle, installing rmux, and smoke-testing stain against a temporary rmux session. The CI smoke uses a shell command instead of real claude credentials; it verifies session creation/listing, stain send input, stain snap output capture, and cleanup. Live Claude GUI validation still needs a developer machine with claude authenticated.
With Rust/Cargo available:
cargo test --workspace
cargo build --workspace
cargo build --release -p stainWith Tauri prerequisites available:
npm run tauri:dev
npm run tauri:buildEquivalent Cargo/Tauri commands, if cargo-tauri is installed directly instead of using the npm CLI, are:
cargo tauri dev
cargo tauri buildRun these after building in an environment with rmux and claude installed.
CLI session smoke:
cargo build --release -p stain
./target/release/stain --help
./target/release/stain open --session stained-glass-smoke --cmd claude --cwd "$PWD" --no-gui
./target/release/stain list
./target/release/stain send --session stained-glass-smoke --text "/mcp" --enter
./target/release/stain snap --session stained-glass-smoke
./target/release/stain kill --session stained-glass-smokeExpected results:
stain --helplistsopen,attach,list,kill,snap, andsend.open --no-guicreates or reuses the rmux session and exits0.listincludes the smoke session while it is alive.sendwrites input into the target rmux pane; with--enter, it submits the text as a command/message.snapprints a plain-text pane snapshot and does not include ANSI escape codes.killexits0, and the session disappears fromlist.
GUI smoke:
npm run tauri:devThen verify:
- The app opens at or above the configured
800x500minimum window size. - The terminal pane renders raw
pty-dataon the left. - Typing in the terminal sends input through
send_input. - The preview pane updates from
pane-snapshotevents. - Preview HTML is rendered from escaped markdown, not raw untrusted HTML.
- Toolbar controls for split/preview/auto-scroll/theme remain usable at minimum size.
If automatic GUI launch from stain open is being tested:
STAINED_GLASS_APP=/path/to/stained-glass ./target/release/stain open --session stained-glass-smokePlatform defaults:
- macOS:
stain openusesopen -a "Stained Glass". - Linux:
stain openfirst triesgtk-launch stained-glass, then falls back toxdg-open stained-glass://open. - Any platform:
STAINED_GLASS_APP=/path/to/launcheroverrides automatic GUI launch;--no-guionly ensures the rmux session exists.
Keystrokes:
user keystroke
→ src/terminal.js Terminal.onData()
→ src/ipc.js sendInput(data)
→ Tauri invoke('send_input')
→ src-tauri/src/ipc.rs send_input()
→ PTY input channel
→ rmux attach-session PTY stdin
→ claude
Terminal output:
claude output
→ rmux session PTY stdout
→ src-tauri/src/pty.rs read loop
→ Tauri emit('pty-data')
→ src/ipc.js onPtyData()
→ src/terminal.js Terminal.write()
→ terminal pane
Preview snapshots:
rmux pane buffer
→ rmux capture-pane -p -e -t <session>
→ src-tauri/src/snapshot.rs strip ANSI
→ markdown-oriented extraction
→ Tauri emit('pane-snapshot')
→ src/ipc.js onPaneSnapshot()
→ src/preview.js marked/highlight-compatible render
→ preview pane
CLI control:
stain open/send/list/kill/snap/attach
→ std::process::Command argv arrays
→ rmux CLI
→ session lifecycle or plain-text snapshot output
stain controls Stained Glass rmux sessions independently of whether the GUI is open:
stain --help
stain open --session claude --cmd claude --cwd "$PWD"
stain open --session claude --no-gui
stain send --session claude --text "/mcp" --enter
stain attach --session claude
stain list
stain snap --session claude
stain kill --session claudeCommand behavior:
open: creates the named rmux session when missing, using--cmdand--cwd, then attempts to launch the GUI unless--no-guiis set.send: sends text to the named rmux session withrmux send-keys;--enterappends an Enter keypress.attach: attaches the current terminal directly tormux attach-session -t <session>.list: prints active rmux session names.snap: prints an ANSI-stripped plain-textrmux capture-panesnapshot.kill: stops the named rmux session.
CLI failures print actionable errors to stderr and return a non-zero exit code.
Build local artifacts:
npm run tauri:build
cargo build --release -p stainExpected local outputs depend on platform and Tauri configuration. Common paths include:
- Tauri app bundles/installers under
src-tauri/target/release/bundle/ stainbinary attarget/release/stain
Do not treat a local package build as a release. Signing, notarization, uploading packages, creating GitHub releases, publishing installers, or changing production distribution settings require Zach's explicit approval.
For any PR, include the exact commands you ran and call out missing local prerequisites clearly.
Backend / Rust changes:
-
cargo test --workspace -
cargo build --workspace - Relevant unit tests cover command construction or state transitions.
- If
rmuxbehavior changed, run or document a livermuxsmoke test.
Frontend changes:
-
npm run check:scaffold -
node --check src/ipc.js -
node --check src/terminal.js -
node --check src/preview.js - Browser/Tauri smoke for terminal rendering, input, preview updates, and minimum-size usability when a GUI environment is available.
CLI changes:
-
npm run check:stain -
cargo test -p stain -
cargo build --release -p stain -
stain --helplists all MVP commands. -
stain snap --session <name>returns plain text in a live rmux environment. - Failure paths return non-zero exit codes with actionable stderr.
Packaging changes:
-
npm run tauri:build -
cargo build --release -p stain - Artifact paths are documented.
- Release/signing/publishing steps are not performed without Zach's explicit approval.
Security / quality:
- No secrets, local credentials, profile paths, or machine-specific maintainer notes are committed.
- Run the repository security scanner if available, e.g.
/opt/data/bin/tirith scan ..