Lossless Changelog
component

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.

Michael StatonMusic Editoraugmented with Claude Code on Opus 4.7

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 .dmg for 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:

ToolVersion foundRequired by Tauri 2
Xcode CLI Tools/Library/Developer/CommandLineToolsyes (macOS native build)
Rustrustc 1.85.1>= 1.77
Bun1.3.5yes (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:

  1. The GUI is a separate concern from the orchestrator — different release cadence, eventual private-repo isolation per firm, possible distribution as its own product.

  2. The memopop-ai monorepo was already scaffolded for exactly this (Astro site + SvelteKit web app + intended Python orchestrator), so the workspace just absorbs a third app.

  3. Shared design tokens via packages/shared-styles will eventually feed both desktop and (future hosted) web surfaces.

Phase 3 — Scaffold via bun create tauri-app

BASH
cd /Users/mpstaton/code/lossless-monorepo/ai-labs/memopop-ai/apps
bun create tauri-app

Interactive prompt answers:

PromptAnswer
Project namememopop-native
Identifiergroup.lossless.memopop.native
Frontend language categoryTypeScript / JavaScript
Package managerBun
UI templateSvelte
UI flavorTypeScript
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

BASH
cd /Users/mpstaton/code/lossless-monorepo/ai-labs/memopop-ai
bun install                      # workspace install at root
cd apps/memopop-native
bun run tauri dev

Rust 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:

PackageInstalledAvailable
svelte5.46.15.55.5
svelte-check4.3.54.4.6
@sveltejs/kit2.49.22.58.0
@sveltejs/vite-plugin-svelte5.1.17.0.0
vite6.4.18.0.10
typescript5.6.36.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:

BASH
bun update svelte svelte-check @sveltejs/kit
bun run tauri dev   # window still opens, IPC still works

Wave 2 — coupled majors, in one resolver pass:

BASH
bun add -d vite@latest @sveltejs/vite-plugin-svelte@latest typescript@latest
bun run tauri dev   # window still opens, IPC still works

Each 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.

PluginPurpose
shellSpawn the Python orchestrator's CLI processes (the core action mechanism).
dialogNative file/folder pickers — "browse for repo path", "choose deal".
storePersistent app settings — active firm, last-used parameters.
fsFilesystem 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 build works 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-memo once 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.