8.1 KiB
Desktop (Pear) UI
The client shell is a Pear desktop application built with pear-electron + pear-bridge. The HTML UI runs inside a frameless-style window with in-content chrome.
Entrypoints
| File | Role |
|---|---|
index.js |
Pear process entry — starts pear-electron Runtime + pear-bridge |
index.html |
GUI main (pear.gui.main) — titlebar + panels |
app.js |
UI logic (connect, fleet, live charts, invites, Data Manager wiring) |
ui/dashboard.js |
Charts tab master metrics wall |
ui/data-manager.js |
Settings → Data (retention / prune / storage usage) |
ui/logs.js |
System Log tab (journal default for admins) |
ui/qvac/* |
QVAC local AI tab (onboarding + tools; needs @qvac/sdk in this project) |
shared/taxonomy.js |
Section grouping for the metrics wall |
shared/container-names.js |
Human Docker/cgroup labels for the wall |
shared/format.js |
Overview KPI unit scaling |
shared/retention.js |
Retention presets shared with the agent |
ui/styles.css |
Layout, theme, titlebar drag regions |
client/* |
HyperDHT connection stack used by the UI |
docs/DASHBOARD.md |
Master Charts / metrics wall plan |
user-guide/ |
End-user workflows (Correlate, Related, Logs, Containers, Settings, …) |
npm start # pear run -d .
npm run dev # same
pear run -d . # equivalent
Requires the Pear CLI installed and bootstrapped (pear once to fetch the runtime).
Window configuration (package.json → pear.gui)
| Field | Template default | Purpose |
|---|---|---|
main |
index.html |
HTML entry |
width / height |
1280 / 860 |
Initial size |
minWidth / minHeight |
900 / 560 |
Resize floor |
resizable |
true |
Edge/corner resize |
movable |
true |
Allow OS move (with drag region) |
minimizable / maximizable / closable |
true |
Window buttons |
hasShadow |
true |
Native shadow |
backgroundColor |
#0a0c10 |
Avoid white flash on boot |
pre |
pear-electron/pre |
Runtime bootstrap (required) |
Platform overrides are supported:
{
"pear": {
"gui": {
"darwin": { "resizable": true },
"linux": { "autoHideMenuBar": true },
"win32": { "autoHideMenuBar": true }
}
}
}
See pear-electron README for the full option list (center, alwaysOnTop, transparent, closeHides, etc.).
Titlebar + <pear-ctrl>
Pear provides a custom element <pear-ctrl> for platform window controls:
- macOS (
darwin) — layout for system traffic lights (hidden title bar chrome) - Windows / Linux — minimize, maximize, close controls rendered by the runtime
Required HTML shape
<div id="titlebar" role="banner">
<div class="titlebar-left">
<pear-ctrl></pear-ctrl>
<div class="app-brand">…</div>
</div>
<div class="titlebar-right">
<!-- status chips, non-drag interactive bits -->
</div>
</div>
Do not remove <pear-ctrl> unless you intentionally ship a different window frame model and understand OS differences.
Drag to move
#titlebar {
-webkit-app-region: drag;
height: var(--titlebar-h); /* 42px in this template */
}
/* Interactive controls must not start a drag */
#titlebar pear-ctrl,
#titlebar .chip,
#titlebar button,
#titlebar input,
#titlebar a {
-webkit-app-region: no-drag;
}
.no-drag {
-webkit-app-region: no-drag;
}
Resize
- Enabled by
pear.gui.resizable: true(default in this template). - Users resize via the OS window edges/corners.
- Content layout should flex with the viewport (
#appusesheight: calc(100vh - var(--titlebar-h))).
Platform spacing
Reserve space so brand/text never sits under traffic lights:
#titlebar pear-ctrl[data-platform='darwin'] { min-width: 78px; }
#titlebar pear-ctrl[data-platform='win32'],
#titlebar pear-ctrl[data-platform='linux'] { min-width: 110px; }
Client identity (desktop process)
When the UI dials servers it uses a persistent Ed25519 identity:
| Item | Value |
|---|---|
| Path | ~/.config/peardata/identity.json (or $PEARDATA_HOME/.config/peardata/… if you set PEARDATA_HOME as home root — see client/identity.js) |
| Mode | Directory 0700, file 0600 |
| Contents | { version, seedHex, createdAt } |
Same identity ⇒ same peerId across restarts (useful for peer-bound capabilities and revoke).
Override home with PEARDATA_HOME if you need isolation (CI, multi-profile).
UI modules
| Module | Responsibility |
|---|---|
client/paths.js |
Unified ~/.config/peardata home |
client/jsonCache.js |
Atomic JSON writes + LS mirror |
client/settings.js |
UI prefs → cache/settings.json |
client/peerCache.js |
Multi-host roster + last-active |
client/bookmarks.js |
Compat shim over peerCache |
client/identity.js |
Load/create keypair on disk |
client/connection.js |
Single HyperDHT + protomux-rpc session |
client/manager.js |
Multi-peer map, active selection, reconnect |
client/errors.js |
Unwrap / normalize RPC errors for UI |
app.js |
Wire DOM to manager + protocol methods |
Persistence (PearDock-style)
| Store | Path |
|---|---|
| Settings | ~/.config/peardata/cache/settings.json (+ localStorage peardata.settings.v1 FOUC) |
| Peers / last-active | ~/.config/peardata/cache/peers.json |
| Identity | ~/.config/peardata/identity.json |
Override root with PEARDATA_HOME. Writes are atomic (tmp → rename, mode 0600). Legacy bookmarks.json / ui-settings.json migrate on first load.
Multi-agent
- Many peers can be dialed at once; one active owns Overview / Charts RPC.
- Boot restores saved peers in parallel (
skipActivate), then prefers last-active. - Reconnect never steals the active host unless that host was active.
- Agents view + fleet strip: set active / connect / forget.
- Settings → Connections: auto-restore toggle, reconnect budget, forget-all.
Manager reconnect
- Default max tries: settings
reconnectMaxAttemptsorPEARDATA_MAX_RECONNECT/ 20 connect(input, { adminSeed, autoReconnect, skipActivate })- Input may be 64-hex public key or
pd1.invite
LocalStorage mirrors
| Key | Purpose |
|---|---|
peardata.settings.v1 |
Flat settings FOUC + offline mirror |
peardata-ui-boot |
Theme-only early paint |
peardata_active_peer_id |
Last active peer mirror |
peardata.cache.peers |
Peers envelope mirror |
Development tips
- Keep DevTools available via
pear run -d . - After HTML/CSS/JS edits, reload the Pear window (or restart
npm start) - Server changes require restarting
npm run start:server - If the window cannot be moved: check
#titlebarhasdragand children that cover the bar incorrectly are not allno-dragwithout a parent drag region - If controls don’t work: ensure
<pear-ctrl>is present and not covered by another element with a higher z-index and full-width hit target
Packaging beyond Pear
This template ships the Pear run path only. For Electron-forge / multi-arch standalone binaries, adapt packaging from a fuller product (e.g. peardock’s forge + bare-standalone scripts) once the app stabilizes.
Charts investigation (summary)
| Control | Doc |
|---|---|
| Metrics wall, time, board, gestures | user-guide/charts.md · DASHBOARD.md |
| Correlate (highlight → Find Correlations) | user-guide/metric-correlations.md |
| Related (⇢) (taxonomy + Pearson) | user-guide/related-metrics.md |
| Filters menu (search / TOC / group / sort) | user-guide/charts.md |
| Logs tab | user-guide/logs.md |
Implementation: ui/dashboard.js, ui/logs.js, server/services/weights.js, server/services/logs.js, shared/related-metrics.js.