A pixel-art Claude Code desktop client.
Tauri-based fork of winfunc/opcode — same underlying engine, completely different vibe, plus a stack of upstream-bug fixes you can feel.
Features · Screenshots · Pixet vs opcode · Getting started · Repo layout · Acknowledgments
⚠️ Pixet is a community fork. It is not affiliated with or endorsed by the upstream opcode project or its authors. For the original, unmodified client, seewinfunc/opcode. All upstream copyrights and the AGPL-3.0 license are preserved — seeNOTICEandLICENSE.
![]() Main chat — pixel-art rail, mascot, sessions |
![]() In-rail file tree + modal code viewer |
![]() Running shells across every chat tab, with Stop |
![]() Agents & MCP — opcode features, pixel-styled |
| opcode (upstream) | Pixet | |
|---|---|---|
| Visual style | Modern smooth UI | Pixel-art overlay, Pixelify Sans, 12-color palette |
| Icon set | lucide-react |
pixelarticons (+ lucide fallback) |
| Chat sidebar | Full-page navigation between sessions | Dense in-place rail: mascot, sessions, file tree, running shells, stats |
| Session rename | Filesystem-only | Inline rename with localStorage persistence |
| File viewer | Open external | Modal overlay inside the rail |
| Running processes | Per-session Stop only | Cross-tab Running panel with per-row Stop |
| Streaming on macOS/WebKit | Drops events silently (#71, #26) | Disk watchdog merges JSONL history every 4 s — zero extra API tokens |
| Multi-tab streaming | Generic events leak between tabs | Session-scoped listeners; tabs stay isolated |
| Prompt queue | Drops sends after the first | Hoisted dispatch, queue clears reliably |
| Bash kill | Orphans descendants on cancel | pgrep -P sweep before killing claude |
| External links | Can hijack the Tauri window | Routed through plugin-opener |
| Analytics | Opt-in default, can re-enable silently | Opt-out default, never silently re-enables |
| Project paths | Encoded folder name only (#465) | Full JSONL scan + sidecar resolution |
| Prompt input | Runtime require() breaks in webview (#462) |
Static ESM import |
- Global pixel-art overlay (
src/styles/pixet.css) — stippled dot patterns, Pixelify Sans typography,step-endanimations across every animated surface. - Twelve-color palette anchored on a single orange accent. All hardcoded purple / violet / cyan / rose Tailwind classes were removed throughout the app — loaders, MCP widget, gradients, picker buttons.
- Pixel-art mascot diorama (
PixetMascot.tsx) — a creature at a CRT workstation, rendered as inline SVG, with hanging plant, bookshelf, lamp, steaming mug, and a mouse darting across the floor. - Custom Pixet wordmark + platform icons regenerated from the original mascot SVG for every Tauri target (
.icns,.ico, PNG set). - Defensive Radix overlay (
src/styles/pixet-radix.css) — keeps portaled content (popovers, dropdowns, tooltips) on-style. - Icon shim (
src/lib/icons.ts) — re-exportspixelarticonswhere a pixel equivalent exists, falls back tolucide-reactotherwise. SeeFALLBACK_ICONS.md. - Readable model + thinking pickers — full names instead of truncated buttons, larger hit targets.
A dense sidebar that lives inside every chat session:
- Mascot at the top.
- Project sessions list (
PixetProjectSessions.tsx) — inline rename (localStorage-backed viasessionNames.ts), one-click in-place session switching, no full-page navigation. - File tree of the active project (
PixetFileTree.tsx) — lazy-loaded, recursive, virtualized where it counts. - Modal file viewer (
PixetFileViewer.tsx) — click any file in the tree, get a code-styled overlay without leaving the chat. Backed by the newread_file_textTauri command. - Running-shells widget (
PixetRunningShells.tsx) — lists every Claude / Bash process the RustProcessRegistryhas tracked as alive, across all chat tabs. Per-row Stop uses the samecancelClaudeExecutionpath as the main Stop, plus apgrep -Psweep that picks up orphaned bash descendants. Hidden when nothing is running. - Usage stats (
PixetSidebarStats.tsx) — project / session counts at a glance.
- Streaming-output watchdog — works around the Tauri-v2 event-broadcast IPC issue (opcode #71, #26) where the live event channel silently drops messages on macOS / WebKit. Pixet polls the on-disk session JSONL every 4 s (1.5 s for the first poll) and merges (not replaces) new entries, eliminating the vanish-and-reappear flicker. Loading state clears as soon as the
resultJSONL row from the Claude CLI is seen. Pure local disk reads, zero extra Claude API tokens. - Debounced loading indicator —
visibleIsLoadingabsorbs micro-flicker between events and disk polls. - Stick-to-bottom auto-scroll with a programmatic-scroll suppression window — the disk watchdog can no longer flip
stickToBottommid-animation when it injects merged history. - Prompt queue unblock — hoisted the API dispatch out of the listener-init branch so prompts after the first don't drop silently. Removed the
isLoadinggate on send/cancel so long-running bash no longer traps the queue. - Bash descendant capture — before killing a Claude turn, Pixet runs
pgrep -Pand tracks orphaned bash children so they end up in the Running panel and can be stopped explicitly. - Multi-tab event isolation — generic Tauri events without a session id no longer leak between tabs. Each chat scopes its listeners by session id.
- Pre-registered session listeners on resume — closes the race where messages streamed before the listener was attached.
- Project-path resolution (opcode #465) — resolves a project's real on-disk path from a full JSONL scan plus the sidecar file, instead of guessing from the encoded folder name. Fixes paths containing hyphens.
- Prompt-input ESM fix (opcode #462) — replaced a runtime
require()with a static ESM import so the prompt input works in the Tauri webview. - External link hardening — printed
http://localhost:…URLs from the CLI are routed throughplugin-openerinstead of being followed inline, so a stray URL can't hijack the Tauri window.
- Analytics default to opt-out and never silently re-enable. If you turn it off, it stays off across upgrades.
read_file_textTauri command (src-tauri/src/commands/claude.rs) used by the in-rail file viewer.pixet-webbinary — separate Cargo bin for the optional web-server mode (src-tauri/src/web_main.rs).justfilerewrite (bun + Tauri CLI) for repeatable local dev —just dev,just build,just web.@tauri-apps/apialigned to match the Rusttauriminor (2.11 / 2.7 against 2.8 crates).default-run = "pixet"sotauri devdoesn't ask which binary to run.- Default window bumped to 1440 × 900, with
macOSPrivateApi, transparent decorations, and centered placement.
| Platform | Status | Notes |
|---|---|---|
| macOS · Apple Silicon · Darwin 25.x · WebKit | ✅ daily-driven | Tauri's broadcast event IPC is degraded here — the streaming watchdog is what keeps chat usable. |
| Linux / Windows | inherits upstream | Pixet hasn't been verified on these targets. PRs welcome. |
Prerequisites
- Rust toolchain (stable)
- Node.js + either
bunornpm - Tauri prerequisites for your OS — Tauri prerequisites guide
- Claude Code CLI installed and on
PATH
Install + run
git clone <this-repo>
cd pixet
bun install # or: npm install
bun run build # first build — populates dist/
bun run tauri dev # spawns the native windowbun run tauri dev compiles the Rust side (a few minutes on a cold cache) and then opens the Tauri window. The Vite dev server runs in the same process.
If bun isn't installed: brew install oven-sh/bun/bun on macOS, or use the upstream's npm scripts.
Just recipes (justfile)
just dev # tauri dev
just build # production build (dmg via build:dmg)
just web # run the pixet-web binaryType + Rust check before commit
bun run check # tsc --noEmit && cargo checkThe parts Pixet added or changed:
src/
styles/
pixet.css # global pixel-art overlay
pixet-radix.css # defensive overrides for Radix portal content
components/
PixetMascot.tsx # the diorama SVG
PixetLogo.tsx # wordmark
PixetSidebarStats.tsx # usage / project counts in the rail
PixetProjectSessions.tsx # session list with rename + switch
PixetFileTree.tsx # lazy-loaded recursive file tree
PixetFileViewer.tsx # modal overlay for file contents
PixetRunningShells.tsx # cross-tab running-process widget + Stop
ClaudeCodeSession.tsx # rebuilt streaming/queue/auto-scroll
claude-code-session/
useClaudeMessages.ts # merge-not-replace disk-watchdog hook
lib/
icons.ts # pixelarticons ⇄ lucide-react shim
sessionNames.ts # localStorage rename store + hook
analytics/consent.ts # opt-out-by-default consent gate
api.ts # + listRunningClaudeSessions, killTrackedSubprocess, readFileText
main.tsx # plugin-opener link routing, palette boot
src-tauri/
src/commands/claude.rs # + read_file_text, watchdog diagnostics, pgrep-P sweep
src/main.rs # default-run = "pixet", registry wiring, window size
src/web_main.rs # pixet-web binary entry
NOTICE # upstream attribution + summary of changes
FALLBACK_ICONS.md # icons that stayed on lucide
CONTRIBUTING.md # contribution guidelines
docs/screenshots/ # README screenshot drop-in directory
Pixet inherits the upstream's GNU Affero General Public License v3.0. All Pixet modifications are released under the same license. See NOTICE for the full attribution + change summary.
- opcode by
mufeedvh,123vviekr, and contributors — the parent project. Pixet is a thin reskin and bug-fix layer on top of their work; none of the pixel-art polish would matter without the substantial backend and chat-session engineering they built. - pixelarticons by
halfmage— the icon set that gives the icon shim real pixel-art icons. - Pixelify Sans (Google Fonts) — the pixel typeface used throughout. Bundled locally in
src/assets/fonts/pixelify-sans/. - Tauri, React, Vite, Bun — the platform underneath.
—
Made with pixels. Powered by Claude.



