lemonade

Lemonade Desktop App (Tauri)

A native desktop GUI for interacting with the Lemonade Server.

Overview

This app provides a native desktop experience for managing models and chatting with LLMs running on lemond. It connects to the server via HTTP API and offers a modern, resizable panel-based interface.

It is built with Tauri v2, which embeds the operating system’s native webview — WebView2 on Windows, WKWebView on macOS, and webkit2gtk on Linux — instead of bundling Chromium. The renderer is a standard React 19 + TypeScript application served by webpack and shared with the browser-only src/web-app/ build.

Key Features:

Deployment Topology

The Tauri desktop app is a thin client for a separately-running lemond server. A single lemond can be driven by multiple clients at once, including clients running on other machines.

Consequences that callers of this code need to know about:

Code Structure

src/app/
├── package.json                   # Webpack + Tauri CLI devDependencies
├── webpack.config.js              # Bundler config (target: web)
├── tsconfig.json                  # TypeScript config
├── assets/                        # Icons, logos
│
├── src/
│   ├── global.d.ts                # window.api type declaration
│   └── renderer/                  # React UI (TypeScript)
│       ├── index.tsx              # Renderer entry (imports tauriShim first)
│       ├── tauriShim.ts           # Installs window.api → Tauri invoke() bridge
│       ├── App.tsx                # Root component, layout orchestration
│       ├── TitleBar.tsx           # Custom window controls
│       ├── ModelManager.tsx       # Model list and actions
│       ├── ChatWindow.tsx         # LLM chat interface
│       ├── LogsWindow.tsx         # Server log viewer
│       ├── SettingsPanel.tsx      # Inference parameters
│       └── utils/                 # API helpers and config
│
└── src-tauri/                     # Rust host (Tauri backend)
    ├── Cargo.toml                 # Rust dependencies
    ├── tauri.conf.json            # Window config, bundle settings, plugins
    ├── build.rs                   # tauri_build::build()
    ├── capabilities/default.json  # Tauri permissions
    ├── icons/                     # Generated app icons (32x32/128x128/ico/icns)
    └── src/
        ├── main.rs                # Entry point (binary)
        ├── lib.rs                 # Tauri builder, plugin wiring, deep-link routing
        ├── commands.rs            # #[tauri::command] handlers (window, settings, port)
        ├── events.rs              # Tauri event channel name constants
        ├── settings.rs            # app_settings.json read/write + sanitize
        ├── beacon.rs              # UDP beacon listener (single bound socket)
        ├── tray_launcher.rs       # macOS-only tray auto-start helper
        └── webview_shim.rs        # Per-platform webview hooks (mic permission, link interception)

/health, /system-stats, and /system-info are NOT proxied through Rust. The renderer fetches them directly via serverConfig.fetch(...); see StatusBar.tsx and AboutModal.tsx.

Architecture

┌────────────────────────────────────────────────┐
│  Tauri Rust Host (src-tauri/)                  │
│  Window mgmt, IPC commands, background tasks   │
│  UDP beacon listener, settings file I/O        │
├────────────────────────────────────────────────┤
│  tauriShim.ts (installs window.api in webview) │
│  Maps window.api.* → invoke() / listen()       │
├────────────────────────────────────────────────┤
│  React Renderer (TypeScript)                   │
│  Source lives in src/app/src/; the web-app     │
│  build (src/web-app/) reuses it via webpack    │
│  relative entry/template paths — no symlinks.  │
├────────────────────────────────────────────────┤
│  HTTP API → lemond (C++ server)                │
└────────────────────────────────────────────────┘

Prerequisites

Building

cd src/app

# Install webpack + Tauri CLI dependencies
npm ci

# Run in dev mode (opens a window, hot-reloads webpack)
npm run dev

# Production build (single binary, no OS bundles)
npm run build -- --no-bundle

# Production build with platform bundles (macOS .app, Linux .deb/.rpm, Windows MSI/NSIS)
npm run build

The preferred path for shipping is through CMake, which stages the Tauri output alongside the rest of the server:

cmake --build --preset default --target tauri-app      # Linux / macOS
cmake --build --preset windows --target tauri-app      # Windows

Development Scripts

npm run dev                    # Tauri dev mode (window + hot-reload)
npm run build                  # Tauri production build
npm run tauri icon <path>      # Regenerate icons from a source image
npm run build:renderer         # Build just the renderer (webpack, dev mode)
npm run build:renderer:prod    # Build just the renderer (webpack, production)
npm run watch:renderer         # Webpack watch mode for the renderer only

Testing the Rust host

Unit tests live alongside the Rust modules and cover settings sanitization, beacon parsing, and deep-link URL parsing:

cargo test --manifest-path src-tauri/Cargo.toml