feat(desktop): polish desktop shell and shared WebUI surfaces (#4195)
* feat(desktop): add native host scaffold * feat(webui): track turns and usage in gateway * feat(webui): polish desktop chat experience * feat(apps): add ArcGIS and Joplin logos * feat(desktop): polish shell and shared surfaces * fix(webui): avoid preview chips for glob references * test: align CI expectations for token fallback * feat(webui): preview prompt rail entries * feat(webui): add prompt navigator drawer * style(webui): refine prompt navigator placement * style(webui): align prompt navigator with header actions * style(webui): simplify prompt navigator header * refactor(webui): clean thread resource refresh * feat(desktop): add native reply notifications * fix(webui): preserve desktop restart and replay state * fix(desktop): harden gateway proxy startup * fix(web): fall back when readability is unavailable * fix(desktop): hide window instead of closing on macos * fix(webui): unify desktop header actions * fix(webui): simplify prompt history rows * fix(desktop): log notification delivery failures * chore(desktop): clean source package artifacts * fix(cron): support one-time relative reminders * fix(webui): reveal scroll button in place * Revert "fix(cron): support one-time relative reminders" This reverts commit 4c4661da120a3c7283e0768412bae48604e7390b. * refactor(webui): extract token usage heatmap * docs(desktop): clarify contributor guides --------- Co-authored-by: chengyongru <2755839590@qq.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# nanobot Desktop
|
||||
|
||||
Mac-first desktop app for running nanobot locally with the same product UI as
|
||||
the browser WebUI.
|
||||
|
||||
For users, the desktop app is a local wrapper around nanobot: it starts the
|
||||
engine for you, keeps config and chat state in the platform app data directory,
|
||||
and uses the shared WebUI for chat, settings, apps, skills, and workspace
|
||||
selection.
|
||||
|
||||
For contributors, this folder is a native host shell. It reuses the root WebUI
|
||||
build at `nanobot/web/dist`; it does not copy or fork `webui/src`. Electron owns
|
||||
the local engine lifecycle, exposes `window.nanobotHost` to the renderer, serves
|
||||
the `nanobot-app://` app protocol, and proxies `/api/*` plus `/webui/bootstrap`
|
||||
to a private Unix socket `nanobot desktop-gateway` process.
|
||||
|
||||
## What To Read
|
||||
|
||||
- Using or trying the app from source: start with the development commands below.
|
||||
- Changing desktop behavior: read [`docs/development.md`](docs/development.md).
|
||||
- Adding native host capabilities: read [`docs/host-contract.md`](docs/host-contract.md).
|
||||
- Keeping browser WebUI and desktop aligned: read [`docs/webui-sync.md`](docs/webui-sync.md).
|
||||
|
||||
## Development
|
||||
|
||||
This section is for contributors working from a source checkout.
|
||||
|
||||
```sh
|
||||
cd desktop
|
||||
bun run dev:webui
|
||||
```
|
||||
|
||||
In another terminal:
|
||||
|
||||
```sh
|
||||
cd desktop
|
||||
bun run dev:app
|
||||
```
|
||||
|
||||
`dev:app` points Electron at the Vite dev server so WebUI changes hot reload.
|
||||
For source checkouts, the app uses `python3` by default and injects the repo
|
||||
root into `PYTHONPATH`. Packaged builds look for a bundled interpreter at
|
||||
`Resources/nanobot-engine/bin/python3`.
|
||||
|
||||
## Engine Bundle
|
||||
|
||||
Release builds prepare `resources/nanobot-engine/` from a macOS
|
||||
`python-build-standalone` archive before running `electron-builder`.
|
||||
By default the script discovers the latest `astral-sh/python-build-standalone`
|
||||
CPython 3.12 `install_only` asset for the requested architecture.
|
||||
|
||||
```sh
|
||||
cd desktop
|
||||
bun run make:mac:arm64
|
||||
bun run make:mac:x64
|
||||
```
|
||||
|
||||
Useful overrides:
|
||||
|
||||
- `NANOBOT_DESKTOP_ARCH=arm64|x64`
|
||||
- `NANOBOT_DESKTOP_PYTHON_VERSION=3.12`
|
||||
- `PYTHON_STANDALONE_RELEASE=20260510`
|
||||
- `PYTHON_STANDALONE_TARBALL=/path/to/archive.tar.gz`
|
||||
- `PYTHON_STANDALONE_URL=https://.../cpython-...tar.gz`
|
||||
- `NANOBOT_WHEELHOUSE=/path/to/wheels` to install from a locked wheelhouse
|
||||
|
||||
The script installs the current checkout's `nanobot-ai[api]` into the bundled
|
||||
runtime and writes `nanobot-engine.json` for diagnostics.
|
||||
|
||||
## Updating Builds
|
||||
|
||||
The native host does not copy the WebUI source or fork the Python agent code. A
|
||||
release bundle is assembled from the current repository state:
|
||||
|
||||
1. Build the shared WebUI:
|
||||
|
||||
```sh
|
||||
bun run build --prefix webui
|
||||
```
|
||||
|
||||
`electron-builder` packages the resulting `nanobot/web/dist` directory as
|
||||
`Resources/nanobot-webui`.
|
||||
|
||||
2. Prepare the bundled Python engine:
|
||||
|
||||
```sh
|
||||
cd desktop
|
||||
NANOBOT_DESKTOP_ARCH=arm64 bun run prepare-engine
|
||||
```
|
||||
|
||||
The script installs the current checkout's `nanobot-ai[api]` package into
|
||||
`resources/nanobot-engine/`, so agent, provider, tool, WebSocket, and config
|
||||
changes flow into the next desktop build automatically.
|
||||
|
||||
3. Build the desktop app and DMG:
|
||||
|
||||
```sh
|
||||
bun run make:mac:arm64
|
||||
bun run make:mac:x64
|
||||
```
|
||||
|
||||
User data is not stored in the app bundle. Config, sessions, logs, workspace
|
||||
state, and the default workspace remain under the platform app data directory,
|
||||
so updating the app replaces code without overwriting local user state.
|
||||
|
||||
## Runtime Contract
|
||||
|
||||
- User data lives under Electron's platform app data directory. In development
|
||||
this is usually `~/Library/Application Support/@nanobot/desktop/` on macOS;
|
||||
packaged builds use the packaged app name.
|
||||
- Fresh installs start the private engine directly. The Python desktop gateway
|
||||
creates the first `config.json` with defaults, then shared WebUI settings own
|
||||
provider, model, and credential setup.
|
||||
- The gateway listens on a per-user Unix socket in the app data directory and uses a transient secret.
|
||||
- The gateway starts with only the WebSocket local channel enabled and does not serve the WebUI static bundle.
|
||||
- The renderer loads assets through `nanobot-app://app/...`; browser users cannot open the native UI from a localhost port.
|
||||
- WebSocket traffic uses the generic `nanobot-host://engine/...` URL, is bridged over Electron IPC, and still uses the short-lived token minted by `/webui/bootstrap`.
|
||||
- Host IPC only accepts the trusted app origin, and socket bridging only accepts the `nanobot-host://engine/...` scheme.
|
||||
- Native WebUI responses include a restrictive Content Security Policy.
|
||||
- WebUI talks only to the generic `window.nanobotHost` contract. Product-specific native behavior stays in this folder.
|
||||
|
||||
Generated release artifacts, node modules, and bundled runtimes remain ignored
|
||||
so the tracked desktop package stays source-only.
|
||||
|
||||
See also:
|
||||
|
||||
- [`docs/development.md`](docs/development.md)
|
||||
- [`docs/host-contract.md`](docs/host-contract.md)
|
||||
- [`docs/webui-sync.md`](docs/webui-sync.md)
|
||||
Reference in New Issue
Block a user