A three-pane desktop client for the Claude Code CLI and Codex CLI — Linux + macOS.
SalmonApp wraps a locally-logged-in
claudeorcodexand reuses its credentials — no second account to manage. See Releases for the changelog.Note: deliberately named SalmonApp (one word) to avoid colliding with the bioinformatics
salmonpackage on Linux distros.
中文 PRD · Mockup · Icon candidates
If you already use claude (Claude Code) or codex (OpenAI Codex CLI) in a terminal, you've hit the same speed bumps:
- Long transcripts are painful to read in a scroll-back buffer
- Multiple projects all share one shell history
- Tool-call diffs need a second
cat/lsto inspect - Switching between Claude and Codex means switching terminals
SalmonApp wraps the CLI you're already running and gives it a chat UI:
| Pane | What it shows |
|---|---|
| Left | Topic list, grouped by recency, with engine + workdir badges |
| Middle | Markdown-rendered chat, tool-call cards, permission prompts, code blocks with highlight.js |
| Right | Tabs: Files (workdir tree) · Diff · Preview (MD / HTML / pptx / docx / xlsx) · Logs · ⛶ fullscreen toggle |
A Topic is mentally a terminal tab pinned to a workdir — open many at once, each with its own engine + persistent CLI session. Closing a Topic SIGTERMs its child PTY but keeps the CLI's transcript in ~/.claude/... or ~/.codex/... exactly as the CLI itself stores it. Re-opening lazily re-spawns via claude --resume <session-id> (or the Codex equivalent). Detach / attach, basically.
SalmonApp does not speak to Anthropic or OpenAI directly. It owns no API key. Credentials and session storage live entirely with the CLI.
Grab the latest from Releases.
# .deb — installs to /usr/bin/salmonapp and adds an application entry
sudo apt install ./SalmonApp_*.deb
# OR AppImage — no install, double-click or chmod +x then run
chmod +x SalmonApp_*.AppImage
./SalmonApp_*.AppImageThe .deb declares its WebKit / GTK runtime deps; apt resolves them. The AppImage bundles them.
⚠ The Mac build is not notarized — this project has no Apple Developer account. The
.dmgis signed ad-hoc, which is enough to launch but not enough to satisfy Gatekeeper out of the box. You'll see "Apple could not verify SalmonApp is free of malware" on first launch.
# 1. Open the .dmg, drag SalmonApp.app into /Applications
# 2. Tell Gatekeeper to trust it. EITHER:
# (a) Right-click SalmonApp.app → Open → click "Open" in the dialog. macOS
# remembers the choice; subsequent launches are normal.
# OR (b) clear the quarantine bit from a terminal:
xattr -dr com.apple.quarantine /Applications/SalmonApp.app(b) is the smoother path if you trust this repo's release pipeline. The .dmg is universal (arm64 + x86_64), so the same file works on M-series and Intel Macs.
SalmonApp needs claude or codex discoverable on PATH. On macOS, GUI apps don't inherit your shell's PATH — SalmonApp walks $SHELL -ilc 'echo $PATH' at startup to import it, plus probes /opt/homebrew/bin, /usr/local/bin, ~/.npm-global/bin, ~/.bun/bin, etc. If npm i -g @anthropic-ai/claude-code worked in your terminal, it'll be found.
You need at least one of the CLIs already installed and logged in:
# Claude Code CLI
npm i -g @anthropic-ai/claude-code
claude # follow the auth flow once
# OR Codex CLI
npm i -g @openai/codex-cli
codex # auth flowSalmonApp detects whichever is on PATH and offers to use them per-Topic.
The Preview pane renders .pptx / .docx / .xlsx / .odp / .odt / .ods by shelling out to LibreOffice headless and slicing the resulting PDF with pdftoppm. Install once:
# Linux
sudo apt install libreoffice-impress libreoffice-writer libreoffice-calc poppler-utils
# macOS (either of these)
brew install --cask libreoffice && brew install poppler
# or download LibreOffice.app from libreoffice.org/download/ — SalmonApp
# probes /Applications/LibreOffice.app/Contents/MacOS/soffice automatically.Without these, Office files fall back to a friendly "binary file" placeholder instead of crashing the preview.
Common: Rust toolchain (rustup 1.77+) and Node 20+.
sudo apt install \
libwebkit2gtk-4.1-dev libssl-dev libayatana-appindicator3-dev \
librsvg2-dev build-essential curl wget file pkg-config
cd salmon
npm install
npm run tauri:build # → src-tauri/target/release/bundle/{deb,appimage}/xcode-select --install # if you don't have command-line tools yet
rustup target add aarch64-apple-darwin x86_64-apple-darwin
cd salmon
npm install
npm run tauri:build -- --target universal-apple-darwin
# → src-tauri/target/universal-apple-darwin/release/bundle/{macos,dmg}/For native-arch only (faster build, won't run on the other Mac arch):
npm run tauri:build # → src-tauri/target/release/bundle/{macos,dmg}/npm run tauri:devsalmon/
├── src/ React + TypeScript UI (Vite)
│ ├── App.tsx top-level layout, routing between Topics
│ ├── components/
│ │ ├── LeftSidebar.tsx Topic list, search, grouping
│ │ ├── ChatStream.tsx Markdown / tool-call rendering
│ │ ├── Composer.tsx Input box, /-command pass-through
│ │ ├── ToolCallCard.tsx Per-tool result rendering
│ │ ├── PermissionCard.tsx Approval prompts (allow / deny)
│ │ ├── RightPane.tsx Files / Diff / Preview / Logs tabs
│ │ ├── NewTopicDialog.tsx Create-topic flow
│ │ └── Onboarding.tsx First-run CLI detection
│ └── lib/ invoke() wrappers + types
└── src-tauri/ Rust backend
└── src/
├── lib.rs Tauri builder, plugin wiring
├── commands.rs Tauri commands invoked from React
├── engine.rs PTY child management, JSONL parse loop
├── db.rs SQLite schema + topic / message CRUD
└── types.rs Shared Rust ↔ TS types
Key choices:
- Tauri 2 — native window, system WebKit, ~3 MB app vs. an Electron equivalent
- Per-Topic PTY — each Topic owns one
tokio::process::Childrunningclaude(orcodex) in JSONL streaming mode. Stream events flow through an unbounded mpsc channel and out to the UI as Tauri events. - SQLite in
~/.local/share/app.salmonapp.desktop/salmon.db— Topics, messages, tool calls, permission decisions, token counts. Plain text. Export / clear available from the UI. - No API calls from SalmonApp itself — every model interaction is a child process invocation.
- Single window, single profile — no multi-account
- No cloud sync, no team workspace (out of scope per PRD)
- Windows build not yet wired
- macOS build is unsigned / unnotarized — first launch needs the
xattrworkaround above - Token-usage display only counts what the CLI emits in stream events
- Office preview blocks the UI thread for ~2-3 s on first render (LibreOffice cold-start); cached after
See PRD.md for the full design rationale and roadmap.