Skip to content

feat: InClave desktop app (Tauri + React + Tailwind + shadcn) - #18

Merged
emretheus merged 8 commits into
masterfrom
feat/desktop-app
Jun 8, 2026
Merged

feat: InClave desktop app (Tauri + React + Tailwind + shadcn)#18
emretheus merged 8 commits into
masterfrom
feat/desktop-app

Conversation

@emretheus

Copy link
Copy Markdown
Owner

Turns InClave into a native-feeling macOS desktop app without forking the engine. The privacy-first Python packages stay the single source of truth; the app drives them through a JSON-RPC sidecar. The CLI is unchanged in behavior.

Architecture

React UI ──Tauri invoke/events──▶ Rust shell ──JSON-RPC over stdio──▶ inclave-bridge (Python)
                                                                         └ reuses core / ollama / sandbox + chat_engine
  • No listening socket — the sidecar speaks over its own stdin/stdout. The only network connection remains the engine → local Ollama (127.0.0.1:11434).
  • One trust boundary — the frontend reaches the engine only via a single allow-listed Rust command (ipc_request); it cannot invoke arbitrary Rust or Python.
  • Sandbox untouched — model-written Python still runs through inclave_sandbox (Seatbelt, no network, rlimits).

What's in here (by commit)

  1. engine refactor — extract headless chat_engine (event-yielding turn loop) shared by the CLI and the bridge; chat.py becomes a thin renderer. Add delete_session; make the sandbox runtime bundle-aware via INCLAVE_SANDBOX_RUNTIME.
  2. inclave-bridge — new uv-workspace package: JSON-RPC server + handlers, protocol.py as the single contract source.
  3. monorepo — pnpm + turborepo alongside uv; @inclave/ipc-contract generates typed methods/events + zod validators from the bridge schema (drift-guarded in CI).
  4. desktop app — React 19 / Tailwind v4 / shadcn frontend + Tauri Rust shell. Streaming chat, shiki code blocks, sandbox output card, drag-and-drop, models manager with VRAM badges, onboarding, ⌘K palette. New InClave logo (enclave mark) + full icon set; sparks removed app-wide.
  5. ci + docs — desktop CI job (eslint/tsc/vitest/vite + cargo fmt/clippy), IPC-contract freshness check, extended privacy guard (JS + Rust), release workflow (PyInstaller sidecar + signed/notarized .dmg), README + DESKTOP_PLAN.

Verification

  • Python: ruff + format + mypy + 204 tests pass.
  • Desktop: eslint + tsc + 9 tests + vite build pass; Rust fmt + clippy -D warnings clean.
  • End-to-end: drag a CSV → model writes Python → Seatbelt sandbox auto-runs it → grounded answer, through the same contract the UI uses. The native app was launched and verified.

Notes for reviewers

  • Generated packages-js/ipc-contract/index.ts is gitignored and rebuilt by pnpm gen:ipc (CI regenerates + diffs).
  • Packaging (PyInstaller + notarization) is scripted and wired into release-desktop.yml; it produces real .dmgs only on a signing-capable runner.
  • The sandbox / outbound-HTTP boundary changes are intentionally minimal and additive — flagging per CONTRIBUTING.

emretheus added 8 commits June 7, 2026 16:12
…bundle-aware sandbox runtime

Pull the streaming + auto-run-sandbox orchestration out of the CLI's chat.py
into a headless, event-yielding chat_engine so a non-terminal front-end (the
desktop bridge) can drive the exact same behavior. chat.py becomes a thin Rich
renderer over the engine's events; existing test_chat.py still passes.

- chat_engine.run_turn yields Token/MessageDone/RunStart/RunOutput/Error/TurnDone
  events; stream_fn is injectable so the CLI keeps its patchable seam.
- add inclave_core.delete_session (UI needs delete; CLI never had it).
- runtime.runtime_root() honors INCLAVE_SANDBOX_RUNTIME so the Seatbelt runtime
  resolves when the engine is bundled inside a desktop app.
A new uv-workspace package that exposes the engine to the desktop app over
newline-delimited JSON-RPC 2.0 on stdin/stdout — no listening socket, so the
only network connection remains the engine's talk to local Ollama.

- protocol.py is the single source of truth (methods + events); export_schema.py
  serializes it to JSON for TypeScript generation.
- handlers for system/config/models/files/sessions/chat reuse the engine and
  chat_engine; chat.send streams chat.* notifications and runs the auto-run loop.
- server.py dispatches requests, maps the InClaveError hierarchy to JSON-RPC
  error codes, and forwards streamed events. 20 tests.
…tract

Stand up the JS toolchain alongside the existing uv workspace (one repo, two
ecosystems). @inclave/ipc-contract generates typed methods/events + zod
validators from the bridge's schema.json (pnpm gen:ipc); the generated index.ts
is gitignored and rebuilt in CI, with a freshness test guarding against drift.

gitignore covers node_modules, turbo cache, Rust target, dist, PyInstaller
output, and the bundled sidecar binaries.
The macOS desktop app. React 19/TS frontend (chat with token streaming, shiki
code blocks, the sandbox output card, drag-and-drop, models manager with VRAM
badges, onboarding, command palette) talks to the engine through a single
allow-listed Rust command that forwards to the bridge sidecar over stdio;
sidecar notifications are re-emitted as Tauri events. A browser mock backend
lets the UI run without Tauri for fast iteration.

Branding: a custom InClave mark (an inward arrow held inside a rounded
enclosure — data enters, stays on-device) replaces the generic spark across the
titlebar, onboarding, empty state, and assistant avatar. Full icon set
(.icns/.ico/PNG/iOS/Android) generated from icons/icon.svg.

Privacy: CSP locked to self, capability allow-list, no outbound network beyond
the engine's local Ollama. Frontend lint/typecheck/9 tests + Rust fmt/clippy.
- ci.yml: a desktop job (eslint/tsc/vitest/vite build + cargo fmt/clippy) and a
  step that regenerates the IPC contract and fails on drift. Privacy guard now
  also greps the frontend + Rust shell for non-localhost URLs.
- release-desktop.yml: PyInstaller sidecar build + universal Tauri bundle,
  optional Developer-ID signing/notarization, .dmg attached to the release.
- README + DESKTOP_PLAN document the architecture.
- pnpm/action-setup no longer passes version: 10 — it conflicted with
  package.json's packageManager field (ERR_PNPM_BAD_PM_VERSION / 'Multiple
  versions of pnpm specified'). Version is read from package.json now.
- privacy guard allow-lists www.w3.org: the only non-localhost URL the grep
  finds in desktop source is the SVG/XML namespace (xmlns="http://www.w3.org/
  2000/svg") in the inline logo — an identifier, not a network call.
…ise UI polish

CI fix: move bundle.resources (sidecar + sandbox runtime) out of tauri.conf.json
into a packaging-only overlay (tauri.bundle.conf.json) merged at release time.
The base config no longer references the gitignored PyInstaller artifacts, so
cargo clippy / dev builds stop failing with 'resource path doesn't exist'.

UI/UX: raise the design system to an enterprise bar —
- bundle Inter Variable (UI, tabular figures) + JetBrains Mono (code); no
  runtime font fetch, privacy-clean.
- deeper layered dark palette (4 elevation steps), hairline borders tuned per
  layer, a physical shadow scale, subtle-foreground token.
- refined titlebar (model chip), status bar (pulsing live dot, mono ⌘K), and
  composer (stronger elevation + focus ring). Upgraded dialog/dropdown shadows.
- titlebar badge bumped 18px→28px with tighter logo geometry (thicker frame,
  bigger arrow) so the enclave mark stays legible at small inline sizes instead
  of reading as a dot.
- user messages no longer dump the raw <<<FILE …>>> prompt block into the
  bubble. cleanUserText() strips embedded file blocks (covers live sends and
  resumed sessions where the engine stored the assembled prompt) and surfaces
  attachments as chips; show only the question. Tested.

@emre-kocyigit emre-kocyigit left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall.

Privacy promise still holds.

@emretheus
emretheus merged commit 60cd805 into master Jun 8, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants