A cross-platform desktop app for chatting with local small language models (SLMs) β private by default, everything runs on your machine.
Built with Electron, React 19, Vite and TypeScript, it manages a local inference
service called SLP (installed at ~/.smartloop/, bundled with the app) and
talks to it over HTTP on localhost using an OpenAI-compatible API.
| App ID | ai.smartloop.studio |
| Product name | Smartloop Studio |
| Version | see package.json (version) |
| SLP framework | see SLP_VERSION in Makefile / electron/slp-setup.js |
| Protocol | smartloop:// (OAuth callbacks + deep links) |
| Platforms | macOS (DMG/zip + Mac App Store), Windows (NSIS), Linux (AppImage/deb) |
- Local chat β streams completions from the local SLP service through the OpenAI SDK; markdown, code, math, and mermaid rendered with Streamdown.
- Projects β group conversations, documents, MCP servers, and skills; export and import a project as a zip.
- Documents β upload files into a project, watch indexing progress over SSE, browse them in the built-in document viewer.
- Skills β per-project
skills/<name>/SKILL.mdfiles, editable in-app. - MCP servers β configure Model Context Protocol servers per project.
- Model management β download, enable/disable, load/unload models; switch SLP framework versions; live RAM / TPS metrics.
- System tray β closing the window hides to tray instead of quitting.
- Auto-update β electron-updater against the Smartloop release feed.
- Node.js v20+
- npm
npm installFull Electron app with hot-reload (Vite + Electron together):
make electron-devOn Windows, use the PowerShell runner:
.\scripts\electron.ps1 electron-devThis starts the Vite dev server on http://localhost:5173, launches Electron
against it, and auto-reloads on change.
Renderer-only (no Electron shell β most features that need IPC won't work):
npm run devTail the main-process log while the app runs:
make logs-devSimulate the sandboxed Mac App Store code paths in dev:
MAS=1 make electron-devOn a clean machine the app health-checks SLP β resolves the framework (packaged
builds run it in place from the app bundle; a dev/unbundled run installs it under
~/.smartloop/<version>/) β starts the service, which writes its port to
~/.smartloop/server.port β runs bootstrap, downloading the default model with
SSE progress shown on the splash screen.
make test wipes ~/.smartloop and relaunches so you can walk that flow
end-to-end. There is no automated test suite.
make build-electron # current platform
make build-electron-mac # macOS DMG + zip (arm64), signed/notarized
make build-electron-windows # Windows NSIS installer
make build-electron-linux # Linux AppImage + deb (host arch: x64 or arm64)
make build-electron-mas # Mac App Store .pkg (sandboxed)
make push-to-mas BUILD=x.y.z # stage latest SLP, build MAS .pkg, upload to ASCOn Windows: npm run electron-build or .\scripts\electron.ps1 build-electron.
Supporting targets:
make stage-slp-latest # download the latest SLP framework into build/slp/
make check-slp-symbols # fail if staged SLP references deprecated Apple APIs
make rebuild-electron # quick rebuild, skips npm install
make clean-electron # remove dist/ and dist-web/
make stop # stop the running SLP service
make help-electron # list all Electron targetsThe SLP framework is bundled in the app on every platform (mac, Windows,
Linux) via extraResources β nothing is downloaded from a CDN at build time
except through stage-slp*.
.
βββ electron/ # Main process (Node.js side)
β βββ main.js # IPC handlers, windows, startup flow
β βββ preload.js # Context bridge β window.electronAPI
β βββ slp-service.js # Start/stop/health-check SLP, port management
β βββ slp-setup.js # Install/version the SLP binary
β βββ database.js # better-sqlite3 schema + queries
β βββ bootstrap-progress.js # Normalize SSE bootstrap progress
β βββ health-monitor.js # Background SLP health checks
β βββ release-checker.js # Update feed lookup β autoUpdater.setFeedURL
β βββ tray.js # System tray icon and menu
β βββ data/ # Bundled assets (project template)
βββ src/ # React renderer
β βββ app/pages/ # Home (chat), Settings, Login
β βββ app/layouts/ # AppLayout β sidebar + content
β βββ app/components/ # chat, sidebar, right-sidebar, document viewer, β¦
β βββ app/contexts/ # Auth, Project, AppStatus, Theme
β βββ app/services/ # Thin wrappers over window.electronAPI.db.*
βββ build/ # Icons, entitlements, NSIS scripts, staged SLP
βββ scripts/ # Signing hooks, release + staging scripts
βββ dist-web/ # Vite build output
βββ dist/ # electron-builder output
βββ electron-builder.yml
βββ Makefile
Two processes. Everything Node.js β filesystem, child processes, SQLite, SLP
lifecycle, OAuth β lives in the main process. The renderer is a plain React SPA
with no Node access; it reaches the main process only through
window.electronAPI, defined in electron/preload.js.
IPC channel naming:
| Prefix | Purpose | Examples |
|---|---|---|
db-* |
SQLite reads/writes | db-create-conversation, db-add-messages, db-get-mcp-servers-by-project |
slp-* |
Local service + models | slp-service-start, slp-list-models, slp-init-model, slp-add-document |
storage-* |
Encrypted key-value store | storage-get, storage-set, storage-usage |
updater-* / release-* |
Auto-update | updater-get-status, release-check-now |
| misc | OS integration | oauth-login, select-folder, open-external, get-machine-id |
Chat data flow: getLocalApiEndpoint() (src/app/constants.jsx) resolves the
local SLP URL β an OpenAI client is pointed at it β completions stream over SSE β
messages are persisted via window.electronAPI.db.addMessages().
Routing: React Router with two routes β / (Home/Chat) and /settings;
anything else redirects to /. AppLayout wraps both with a left sidebar
(projects/conversations) and a right sidebar rail (documents, MCP, skills).
- Binary:
<app resources>/slp/slpin packaged builds (run in place β the MAS sandbox will not exec a copied binary), otherwise~/.smartloop/<version>/slp - Writable state (
SLP_HOME):~/.smartloop/β or the app container in the MAS build - Port file:
~/.smartloop/server.port - Data:
~/.smartloop/studio.db, log:~/.smartloop/server.log - Endpoints: OpenAI-compatible
/v1/chat/completions, plus documents, projects, auth, models, and bootstrap
- electron-store β encrypted with the machine ID; tokens and preferences
- better-sqlite3 β projects, conversations, messages, settings, MCP servers, document folders/status, memory + TPS metrics
- Electron 39 Β· React 19 Β· TypeScript 5.6 Β· Vite 5
- UI: Material UI v7, Tailwind CSS 3, PrimeReact, React Router 6 (dark theme)
- Chat/markdown:
openaiSDK, Streamdown (+ code/math/mermaid/CJK plugins) - Data: better-sqlite3, electron-store, node-machine-id
- Ops: electron-updater, electron-log, electron-builder
See CLAUDE.md for UI conventions (Tailwind-first, theme variables, the outlined
label/badge style guide).
- Context isolation on, node integration off in the renderer
- All privileged work behind explicit IPC handlers in the preload bridge
- Local storage encrypted with the machine ID
- OAuth handled in the system browser, returning via
smartloop:// - macOS builds are hardened-runtime signed and notarized; the MAS build runs under the App Sandbox; Windows builds are Authenticode-signed in CI
Create .env.local:
VITE_BASE_URL=https://api.smartloop.ai # Cloud API (OAuth, releases)
VITE_APP_URL=https://app.smartloop.ai # Cloud frontend (OAuth redirect)
SLP_BASE_URL=https://dl.smartloop.ai # SLP framework download base
NODE_ENV=development
PORT=5173In renderer code read these via import.meta.env.VITE_* β not process.env.
Artifacts land in dist/:
- macOS:
studio-<version>-arm64.dmg/.zip;.pkgfor the App Store - Windows:
studio-Setup-<version>.exe(NSIS, assisted installer) - Linux:
.AppImageand.deb(x64, arm64)
For cutting a production release β the GitHub Actions flow, versioning, tagging, and publishing to the update feed β see RELEASING.md.
Contributions are welcome! Smartloop Studio is open source under the Apache License 2.0, and every contribution is accepted under the same license.
- Fork the repo and create a branch from
main. - Make your change; run
npm run lintandnpm run build. - Sign off your commits (
git commit -s) per the Developer Certificate of Origin. - Open a pull request against
maindescribing what changed and how you tested it.
See CONTRIBUTING.md for the full guide, including how to report bugs and security issues.
Copyright 2026 Smartloop Inc.
Licensed under the Apache License, Version 2.0. See LICENSE and NOTICE for details.
SLP is not covered by this license. The SLP inference framework that the app downloads or bundles is proprietary software from Smartloop Inc., distributed under its own terms. The Apache 2.0 license applies only to the source code in this repository.
Trademarks. "Smartloop", "Smartloop Studio" and the Smartloop logos are trademarks of Smartloop Inc. As section 6 of the Apache License states, the license does not grant permission to use them. If you distribute a modified build, please give it a different name and icon.
- Electron Β· Vite Β· React Β· electron-builder
Β© 2026 Smartloop Inc. Β· Licensed under Apache 2.0