Capablio

360 Insights Studio — Desktop shell

The Electron app in desktop/ wraps the Python Studio backend (python -m feedback360 --app). It spawns (or attaches to) the local API server, then loads operator screens from desktop/ui/.

Updated

Architecture

desktop/main.js          Electron lifecycle, Python spawn, /health polling
desktop/preload.js       contextBridge → window.studio (IPC)
desktop/ui/index.html    Single-page shell (sidebar + screens)
feedback360/app_server.py Studio HTTP API (127.0.0.1 only)

On launch, main.js:

  1. Resolves Python (FEEDBACK360_PYTHON, else repo .venv, else python3).
  2. Picks port 8914 (increments up to +20 if busy).
  3. If /health already returns service: "feedback360-studio" on that port, attaches (no spawn).
  4. Otherwise spawns: python -m feedback360 --app --port <port> --out report with cwd = repo root.
  5. Polls GET /health for up to 30s, then loads ui/index.html.

On quit, only a spawned child process is terminated; attached backends are left running.

IPC surface (window.studio)

Method Description
pickPdf() Native open dialog (*.pdf); returns absolute path or null
pickFolder() Directory picker; returns path or null
backendUrl() http://127.0.0.1:<port> for the active backend
openExternal(url) Open URL in the system browser
showInFolder(path) Reveal file in file manager
appInfo() { version, platform } from package.json

Renderer: contextIsolation: true, nodeIntegration: false.

Screen map

Screen ID Backend routes
Welcome screen-welcome
New Report screen-new POST /api/generate, GET /api/jobs/<id>
Progress generate-progress Job polling (Parse / Analyze / Render / Verify)
Workspaces screen-workspaces GET /api/workspaces, POST /api/workspaces/select
Report screen-report iframe → GET /report; POST /api/ai/enrich, GET /api/ai/status
Live View screen-live iframe → GET /
Export screen-export POST /api/export (409 when verify blocks)
Settings screen-settings GET/PUT /api/settings, GET /api/ai/status, keystore IPC
About screen-about appInfo()

First-run wizard

On launch, the renderer reads GET /api/settings. When wizard_completed is not true, a modal wizard appears over the main shell:

  1. Reports folder (wizard-step-folder) — shows reports_root; Change… calls PUT /api/settings with a folder from pickFolder().
  2. AI narratives (wizard-step-ai) — shared renderAiSetup() cards for Cursor (when cursor_detected), Claude, and OpenAI. Keys are saved via window.studio.saveApiKey → encrypted keys.jsonPOST /api/ai/credentials. Skip stores ai.provider: null and continues.
  3. Try it now (wizard-step-demo) — optional sample run using bundled sources/*.pdf, or skip to Welcome.

Completing the wizard sets wizard_completed: true via PUT /api/settings. Run setup again in Settings re-opens the wizard without clearing settings.

API key storage (desktop/keystore.js)

  • createKeystore({ encryptor, storeDir }) — injectable for unit tests.
  • Production wiring in main.js uses Electron safeStorage when isEncryptionAvailable(); otherwise the user must confirm a warning dialog before keys are stored with a plain: prefix (base64 on disk — never the raw key string).
  • Encrypted blobs live in <userData>/keystore/keys.json.
  • On backend boot and after each save, main.js decrypts and posts POST /api/ai/credentials so the Python process holds keys in memory only (CredentialStore — never written to settings.json).
  • Forget key clears keystore + posts { api_key: null }.

IPC: saveApiKey, forgetApiKey, listApiKeyProviders, bundledSources.

Settings screen

screen-settings sections:

  • Reports folder — path, Change…, Show in folder.
  • AI narrativesrenderAiSetup(), status line, Test connection, Forget key, Advanced model selector (collapsed by default).
  • About — app version, backend version from /health, reader wiki link, Run setup again.

User-visible copy avoids technical jargon (no “endpoint”, “token”, “port”, etc.). Errors use backend message fields or hand-written sentences — never stack traces.

Dev run

./scripts/start-studio.sh

Or manually:

cd desktop && npm install && npm start

Smoke test

Used by orchestration/checks/check-130.py:

cd desktop
FEEDBACK360_PYTHON=../.venv/bin/python npx electron . --no-sandbox --smoke-test

Expects stdout line SMOKE-OK <port> and exit 0 (exit 2 on failure). No windows are created.