memopop-native Bootstrapped — Tauri 2 + Svelte 5 Inside the Bun Workspace
Stood up apps/memopop-native — a Tauri 2 desktop shell wrapping the investment-memo-orchestrator's CLIs — inside the existing memopop-ai Bun workspace. Verified prerequisites (Xcode CLI, Rust, Bun), updated Rust to current stable, scaffolded via bun create tauri-app with TypeScript + Svelte, brought the frontend stack to current within two safe waves (minor + coupled-major), added the four Tauri 2 plugins we'll need (shell, dialog, store, fs). Window opens, IPC works, baseline is clean.
Changelog - 2026-04-27 (01)
memopop-native Bootstrapped — Tauri 2 + Svelte 5 Inside the Bun Workspace
Why Care?
For the firm (Mike): investment-memo-orchestrator works beautifully but has a hard ceiling — it's a CLI. The audience for the output (VCs) is not the audience for the input ("type python -m src.main and pass --firm hypernova --deal Avalanche --mode justify"). Wrapping it in a native app removes that wall without rewriting the orchestrator. This entry is the foundation: a Tauri 2 desktop app, scaffolded inside the existing memopop-ai Bun workspace, sharing the workspace with the future Astro marketing site and SvelteKit web app. Same monorepo, three surfaces, one design-token package. Nothing fancy landed in this entry — what landed is the capacity to build.
The strategic reason for Tauri specifically (vs. Electron, vs. Swift):
Cross-platform from a single Rust shell with the OS's native webview. macOS today, Windows/Linux are a build flag away.
Bundle size measured in MB, not hundreds of MB. The webview is the OS's, not Chromium-bundled.
Web frontend means our Svelte/Tailwind fluency carries straight in.
Sidecar pattern for shipping Python alongside the app is a real upgrade path — eventually a single signed
.dmgfor VCs who can't open a terminal.
For end users (VCs eventually): Nothing visible yet. The first launch shows the default "Welcome to Tauri + Svelte" demo screen — boilerplate, replaced in entry 02. What this entry guarantees is that every subsequent feature compiles, ships, and runs as a native app rather than as an instruction to "open a terminal and type this."
What landed
Phase 1 — Prerequisites verified
Three checks, all returning green on the first try:
| Tool | Version found | Required by Tauri 2 |
| Xcode CLI Tools | /Library/Developer/CommandLineTools | yes (macOS native build) |
| Rust | rustc 1.85.1 | >= 1.77 |
| Bun | 1.3.5 | yes (any recent) |
Rust was 13 months behind stable, so bumped it before scaffolding (rustup update stable) — single command, reversible, no downstream impact.
Phase 2 — Project location
Two paths considered:
(A) Sibling app inside
memopop-ai/apps/← chosen(B) Subfolder of
investment-memo-orchestrator
Picked (A) because:
The GUI is a separate concern from the orchestrator — different release cadence, eventual private-repo isolation per firm, possible distribution as its own product.
The
memopop-aimonorepo was already scaffolded for exactly this (Astro site + SvelteKit web app + intended Python orchestrator), so the workspace just absorbs a third app.Shared design tokens via
packages/shared-styleswill eventually feed both desktop and (future hosted) web surfaces.
Phase 3 — Scaffold via bun create tauri-app
cd /Users/mpstaton/code/lossless-monorepo/ai-labs/memopop-ai/apps
bun create tauri-appInteractive prompt answers:
| Prompt | Answer |
| Project name | memopop-native |
| Identifier | group.lossless.memopop.native |
| Frontend language category | TypeScript / JavaScript |
| Package manager | Bun |
| UI template | Svelte |
| UI flavor | TypeScript |
Run bun install after? | No (workspace installs from root) |
Worth noting: the "Svelte" template in current create-tauri-app actually scaffolds SvelteKit with @sveltejs/adapter-static. The build still produces a SPA the webview loads — no SSR, no server endpoints — but you get SvelteKit's file-based routing and +layout.ts conventions for free. The earlier "plain Svelte vs SvelteKit" debate is moot: the static-adapter SvelteKit IS the plain-Svelte experience plus routing, in this context. Kept.
The identifier group.lossless.memopop.native is the macOS bundle ID and the eventual Windows/Linux app ID. Reverse-DNS, durable, scoped under the Lossless Group org so future Tauri apps slot in next to it (group.lossless.someotherapp.native).
Phase 4 — First run
cd /Users/mpstaton/code/lossless-monorepo/ai-labs/memopop-ai
bun install # workspace install at root
cd apps/memopop-native
bun run tauri devRust compiled, the webview rendered, Svelte mounted, the demo greet button proved JS↔Rust IPC works. Baseline: window opens. Every subsequent change measured against this.
Phase 5 — Frontend dependency bumps in two waves
bun install reported these as out of date:
| Package | Installed | Available |
svelte | 5.46.1 | 5.55.5 |
svelte-check | 4.3.5 | 4.4.6 |
@sveltejs/kit | 2.49.2 | 2.58.0 |
@sveltejs/vite-plugin-svelte | 5.1.1 | 7.0.0 |
vite | 6.4.1 | 8.0.10 |
typescript | 5.6.3 | 6.0.3 |
Three of those were minor bumps within the same major (safe). Three were major bumps and coupled — vite-plugin-svelte v7 needs a specific Vite major; bumping one without the others breaks dev mode.
Two-wave strategy:
Wave 1 — minors only:
bun update svelte svelte-check @sveltejs/kit
bun run tauri dev # window still opens, IPC still worksWave 2 — coupled majors, in one resolver pass:
bun add -d vite@latest @sveltejs/vite-plugin-svelte@latest typescript@latest
bun run tauri dev # window still opens, IPC still worksEach wave is independently revertible — git checkout package.json bun.lock && bun install rolls back without touching the other. Both waves passed without a config-file change; the scaffold's vite.config.ts was already compatible with Vite 8.
Phase 6 — Tauri 2 plugins
Four plugins added via bun run tauri add <name>. Each command does three things automatically: adds the Rust crate to Cargo.toml, adds the JS package to package.json, and inserts the plugin's default permission into src-tauri/capabilities/default.json.
| Plugin | Purpose |
shell | Spawn the Python orchestrator's CLI processes (the core action mechanism). |
dialog | Native file/folder pickers — "browse for repo path", "choose deal". |
store | Persistent app settings — active firm, last-used parameters. |
fs | Filesystem access from JS where round-tripping through Rust would be awkward. |
After all four, bun run tauri dev recompiled (new Rust crates) and the window opened with no errors. Baseline still green.
Resulting structure
memopop-ai/ ← existing Bun workspace
├── apps/
│ ├── memopop-site/ ← existing (Astro marketing)
│ ├── memopop-web-app/ ← existing (SvelteKit web app, Vercel)
│ └── memopop-native/ ← NEW (Tauri 2 desktop)
│ ├── src/ ← Svelte 5 frontend
│ │ ├── app.html
│ │ └── routes/
│ │ ├── +layout.ts
│ │ └── +page.svelte ← demo page (replaced in entry 02)
│ ├── src-tauri/ ← Rust backend
│ │ ├── src/
│ │ │ ├── main.rs
│ │ │ └── lib.rs ← plugin init + invoke handler
│ │ ├── capabilities/default.json ← permission allowlist
│ │ ├── tauri.conf.json ← window, bundle, plugin config
│ │ └── Cargo.toml
│ ├── svelte.config.js ← adapter-static
│ ├── vite.config.ts
│ └── package.json
├── packages/
│ └── shared-styles/ ← Tailwind tokens (consumed later)
├── bun.lock ← workspace-shared
└── package.json ← workspaces: [apps/*, packages/*]apps/memopop-native slots into the existing apps/* workspace glob — no edit to the root package.json was needed. The workspace bun.lock absorbs every dependency at the root.
Discipline notes (Mac-first, port-friendly)
The Tauri spec deliberately scoped v0 to macOS but flagged that nothing in the implementation should be Mac-specific. This entry honors that:
No
xcode-select-only assumptions in code. The Xcode CLI tools are needed to compile Rust on Mac; nothing in the project hardcodes/Library/Developer/CommandLineTools.No
~/Library/...paths. Settings persistence (entry 02) goes through@tauri-apps/plugin-store, which writes to the OS-correct app data directory on each platform.No
NSOpenPanel, no AppleScript. File pickers will go through@tauri-apps/plugin-dialog.No platform-gated Rust crates. Only
tauri,tauri-plugin-*,serde,serde_json— all cross-platform.
Porting later = changing the Tauri build target and possibly icon assets, not rewriting code.
Skipped on purpose
PyInstaller sidecar bundling. v1 distribution path. Today the orchestrator runs from its repo via
uv— the GUI assumes that's set up. Bundling Python + LangChain into a signed binary is a separate spike.Code signing / notarization. Not relevant until distribution.
bun run tauri buildworks locally without it.Auto-update. Tauri's updater plugin lands when there's a v0.2 worth shipping.
CI. Single-developer, single-machine for now. Add when collaborators or releases force it.
Next
First feature: Settings + Firm Picker — covered in entry 02 of this date.
First action:
POST /actions/recompile-memoonce the Settings flow is in place. Tightest scope, fastest runtime, exposes the streaming/job-event model.Three-region layout: sidebar / main / log pane, replacing the single-page Settings view.
