GUI3 is the React 19 + TypeScript + webpack frontend integrated under src/app/. It runs real-server-first against lemond at http://localhost:13305 by default and powers both the browser-delivered web app and the Tauri desktop application from one React codebase.
Beta status: GUI3 is frozen for beta stabilization on GUI3_merging. New work should be limited to release-blocking fixes and integration updates. See docs/PRESETS_REDESIGN.md for the capability-keyed preset architecture.
- Node.js 20+ (check
package.jsonenginesfield for exact requirement) - npm 10+
- Optional for UI-only review, recommended for functional testing: a running
lemondinstance athttp://localhost:13305or a custom URL entered in Connect
npm installnpm run devOpens at http://localhost:8080 with hot-module reloading via webpack-dev-server.
npm run buildOutputs optimized bundles to dist/.
npm run watchIncrementally rebuilds on file changes.
npm testPlaywright starts an isolated, non-HMR test server on 127.0.0.1:4173 by default. This intentionally avoids reusing a manual development server on port 8080. Set PLAYWRIGHT_BASE_URL only when you explicitly want to test an already running server.
Note
Direct execution via npx playwright test runs the accessibility (a11y) and functional test suites concurrently, which can cause state collisions in local storage/server state. For isolated, sequential execution of the suites, always use npm test or npm run test:headed.
Runs all UI-safe Playwright tests headless via Chromium. Real-server smoke tests are opt-in so they fail fast instead of silently passing without a running server or loaded model:
LEMONADE_REAL_SERVER=1 npm testArtifacts are saved under distinct output folders per project (test-results/a11y and test-results/functional), preventing results from being overwritten when running the sequential test suites.
npm run test:headedOpens the browser so you can watch the tests run step-by-step.
On first run, Playwright may ask to install browser binaries:
npx playwright installThis prototype showcases the redesigned UI with capability-keyed presets (v1.4):
- Chat — multi-turn conversation with streaming support, scoped user/guest history, omni-capable composer routing, preset selector, sampling controls
- Models — model registry with load/unload, custom model/custom omni registration, categorized view (Loaded / Downloaded / Registry / HuggingFace)
- Backends — device-first capability matrix, backend versions and status
- Connect / Discover — integration showcase and curated model feed
- Presets — capability-keyed preset system with chat/omni (Balanced, Quality, Fast, Creative, Long Context, Code) and image (Sharp, Quick) starters
- Omni capability detection recognizes loaded models marked as
omni,multimodal,vision, VLM, LLaVA, Pixtral, Qwen-VL, MiniCPM-V, Mllama, GPT-4o-style, and similar names or labels. - Omni composer mode keeps those models in chat instead of misrouting them as plain LLMs or image/audio utility models. Text, image attachments, and one audio attachment are sent through
/api/v1/chat/completionsusing OpenAI-style multimodal content parts. - Specialized modes remain explicit: image models route to
/api/v1/images/generations, Whisper/Moonshine transcription models to/api/v1/audio/transcriptions, and TTS models to/api/v1/audio/speech.
- Custom model form on the Models page lets a user register a local/HuggingFace checkpoint or path, recipe/backend, labels, and capability.
- Custom Omni models are first-class: choose
Omniin the capability dropdown and the composer treats the model as multimodal chat even if the server health response later lacks perfect capability metadata. - Scoped per user: custom definitions are saved under the active guest/user storage scope. Guest custom models are shared on the browser; signed-in users get private custom definitions.
- Load path: custom models register/pull with the current Lemonade payload (
model_name,checkpoint,recipe, capability booleans, optionalmmproj). Custom Omni collections register asrecipe: "collection.omni"with acomponentsarray, then load bymodel_name.
- Guest mode is shared: users can chat without signing in. If guest history is enabled, it is visible to anyone using the same browser profile.
- Named local users: users can create an account with name + password. Passwords are salted and hashed with PBKDF2 in browser storage; raw passwords are never stored.
- Scoped data: conversations, active chat, tools setting, user presets, and custom model definitions are namespaced under
lemonade:<storageScope>:.... Signed-in users see only their own local profile data. - Deletion rules: guests can delete shared guest data, signed-in users can delete their own scoped data/account, and the first local account is admin with an all-local-user-data reset. The account UI lives in
src/features/accounts/so it can be extracted/replaced by server-backed auth for production.
- Capability-keyed compatibility — presets declare
applies_to: [capability]and models declarelabels; runtime matches by label intersection - Staged bindings — when you adjust preset settings (temperature, top-p, etc.), they show "Will apply on next load" — no immediate server calls
- Sampling wired — temperature, top_p, top_k, repeat_penalty settings are forwarded to
/api/v1/chat/completions - Advanced disclosure — backend hint field behind an Advanced toggle for power users
- Distinct image presets — Steps and CFG scale controls for image generation, separate from chat sampling
src/
index.tsx # React entry point
index.html # HTML shell
App.tsx # Root component
api.ts # API client (health, models, chat/completions, etc.)
presetStore.ts # Scoped presets state & v1.4 capability-keyed data model
components/ # React components (Chat, Models, Backends, Presets, etc.)
features/accounts/ # Extractable local user/session prototype
features/customModels/# Extractable custom model + custom omni prototype
hooks/ # Custom React hooks
styles/ # CSS modules and global styles
tools/ # Utility functions
tests/
features.spec.ts # Playwright test suite
webpack.config.js # Webpack configuration (dev server, loaders, bundles)
playwright.config.ts # Playwright configuration (baseURL, browsers, output dirs)
tsconfig.json # TypeScript configuration
The prototype stores an explicitly chosen Lemonade server URL in local browser state. For the packaged Lemonade web app, if that state is missing or has been cleared, the API client also tries the current browser origin, so a custom Lemonade host/port is recovered when the UI itself is served by lemond. During local webpack development it still prefers the usual http://localhost:13305 server and retries the current origin if needed. Use the Connect screen to change the URL; the field validates http:// / https:// URLs before attempting a request and shows the exact endpoint plus HTTP/network error on failure. API keys can be kept session-only or explicitly persisted.
Core API paths are normalized to /api/v1/... in src/api.ts. Mocked responses are no longer the default runtime path; use Playwright route mocks in tests when a deterministic mocked scenario is needed.
npm run dev -- --port 9000Webpack dev server will bind to the next available port, or specify one explicitly with --port.
npx playwright install chromiumThis downloads the Chromium binary used by Playwright tests.
Check that webpack-dev-server is running (it should print the URL). If you edited a file and the page didn't update, try:
- Hard refresh (Ctrl+Shift+R or Cmd+Shift+R)
- Restart
npm run dev
Verify the server is running (lemond or lemonade launch), check the URL in the Connect screen, and ensure no firewall is blocking the port. Try curl http://localhost:13305/api/v1/health from the terminal, or replace the port with your configured server URL.
Playwright waits up to 60 seconds by default (see playwright.config.ts). npm test starts its own non-HMR server on 127.0.0.1:4173; do not start the normal port-8080 dev server for the test run. If tests time out:
- Check that port 4173 is free (
lsof -i :4173) and terminate a stale test server if necessary - Unset an accidental
PLAYWRIGHT_BASE_URLoverride unless you intentionally test an external server - Verify network connectivity only for opt-in real-server tests
- Run
npm run test:headedorDEBUG=pw:webserver npm testto see the browser and web-server startup diagnostics
- Single codebase, dual delivery: The same React source powers both web-served and desktop (Tauri) builds. Platform-specific code uses feature detection, not separate branches.
- Real-server-first development: Runtime calls go to Lemonade-compatible
/api/v1/...endpoints. Tests that need deterministic data should mock those network routes explicitly. - Local client state: Conversation history is opt-in and now scoped to either the shared guest space or a signed-in local user. The account menu controls profile deletion; admin can clear every local profile.
- Client-only auth caveat: The account prototype protects data by browser-storage namespace and password-hash login, but production must enforce users, sessions, and authorization on the backend.
- Custom model caveat: Custom model records are prototype metadata; production should validate checkpoint paths, allowed recipes, and permissions server-side before loading.
- Presets are client-side: Presets are not persisted to the server; they're computed locally based on the model registry and user adjustments, and user-created presets are scoped per local user/guest space.
For beta stabilization and final integration:
- Keep changes scoped to release-blocking fixes.
- Merge current
mainintoGUI3_mergingwhen synchronization is required. - Keep API calls aligned with the finalized
/api/v1/...server contract. - Validate both npm-based desktop builds and distro system-module web builds.
See docs/PRESETS_REDESIGN.md, docs/UPDATE_PRESET_CONTRACT.md, and docs/CLIENT_MCP.md for the current design and runtime contracts.
- Omni registry/custom collection models stay selectable as the Omni wrapper even when lemond reports the loaded runtime as individual vLLM/llama/vision/audio components.
- Model typing is deliberately conservative: vLLM and plain LLM recipes stay in LLM mode; only explicit Omni/multimodal/VL metadata or collection recipes become Omni.
- Model downloads are started with server-owned persistence (
subscribe: false) and the Models page polls/api/v1/downloads, so active downloads reappear after refresh/new tab on servers that expose the downloads API. - Custom Omni collections can be created from named text, vision, image, transcription, and speech components and are sent to Lemonade as
collection.omnipluscomponents, not as pseudo-checkpoint registrations. - Account popovers use an opaque raised surface so labels remain readable over the model grid.
- Backend summary now falls back from
/system-infolemonade_versionto/healthversion, so Linux builds no longer showLemonade unknown ...when system-info omits the version.