From 593a4677b6eb737f0e785a6f805189d783cb62c4 Mon Sep 17 00:00:00 2001 From: sravan Date: Tue, 30 Jun 2026 17:49:41 +0530 Subject: [PATCH] feat(clients): scaffold mobile (Capacitor) + desktop (Electron) shells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plan + decisions in CLIENTS.md (parallel mobile+desktop; desktop = technician client + existing remote-control agent host; mobile = Capacitor wrap). - desktop/: Electron technician client — loads the live Connect UI, native screen capture via setDisplayMediaRequestHandler, persisted session, external links to browser; electron-builder config for Win/Mac/Linux installers. - mobile/: Capacitor project — server.url loads Connect UI, push/camera/status-bar plugins declared, www splash fallback; iOS/Android added via `cap add`. - Reuses the existing /api/v1 + Bearer auth backend; no web-code changes. - .gitignore: ignore generated mobile/android, mobile/ios platform dirs. Co-Authored-By: Claude Opus 4.8 --- .gitignore | 4 ++ CLIENTS.md | 107 +++++++++++++++++++++++++++++++++++ desktop/README.md | 26 +++++++++ desktop/main.js | 62 ++++++++++++++++++++ desktop/package.json | 22 +++++++ desktop/preload.js | 10 ++++ mobile/README.md | 39 +++++++++++++ mobile/capacitor.config.json | 15 +++++ mobile/package.json | 22 +++++++ mobile/www/index.html | 25 ++++++++ 10 files changed, 332 insertions(+) create mode 100644 CLIENTS.md create mode 100644 desktop/README.md create mode 100644 desktop/main.js create mode 100644 desktop/package.json create mode 100644 desktop/preload.js create mode 100644 mobile/README.md create mode 100644 mobile/capacitor.config.json create mode 100644 mobile/package.json create mode 100644 mobile/www/index.html diff --git a/.gitignore b/.gitignore index c8d4a07..00df4bd 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,10 @@ dist/ build/ out/ +# Native client generated/build artifacts (Capacitor adds platform dirs; Electron builds to dist) +mobile/android/ +mobile/ios/ + # Runtime media (created at startup by config.js) server/recordings/ server/transcripts/ diff --git a/CLIENTS.md b/CLIENTS.md new file mode 100644 index 0000000..2e99d26 --- /dev/null +++ b/CLIENTS.md @@ -0,0 +1,107 @@ +# Biz Connect — Mobile & Desktop clients + +Native clients for Biz Connect. **The web app is the single source of truth for the UI**; +each native client is a thin shell that loads that UI and adds the capabilities a browser +can't provide (background push, native screen capture, OS input injection, store presence). + +Decisions (set by the user 2026-06-30): +- **Build mobile and desktop in parallel.** +- **Desktop = both pieces:** the remote-control **host** (existing `agent/`) *and* a + **technician desktop client** (`desktop/` — Connect in a window). +- **Mobile = Capacitor wrap** of the existing web UI (one codebase), not a native rewrite. + +Backend is already client-ready (see [ARCHITECTURE.md](ARCHITECTURE.md) Phase 2): `/api/v1`, +`Authorization: Bearer` + refresh tokens, API keys, per-tenant webhooks. The one remaining +backend gap is **APNs/FCM push** for native mobile (needs Apple/Google credentials). + +--- + +## Components + +| Dir | Client | Tech | What it adds over the browser | +|-----|--------|------|-------------------------------| +| `mobile/` | iOS + Android app | **Capacitor** loading the Connect UI | Native push (FCM/APNs), camera/mic perms, store distribution, screen capture (ReplayKit / MediaProjection) | +| `desktop/` | Technician client | **Electron** loading the Connect UI | Native full-screen capture for screen-share; windowed app; later: tray, auto-update | +| `agent/` | Remote-control **host** (customer) | **Electron + nut-js** *(exists, v0.2.0)* | Screen capture + **OS input injection** so a technician can control the machine | + +All three authenticate through the same `/api/v1` access layer (Bearer token for mobile/desktop, +the existing consent/enroll flow for the agent). + +--- + +## Why "wrap the web UI" (not rewrite) + +The Connect UI is already an installable PWA built from server-rendered single-file HTML. +Pointing a native webview at the server origin means **every relative `/api` and `/ws` URL +keeps working unchanged** — zero web-code changes — while native plugins augment it through +the JS bridge. One codebase, three shells. + +Trade-off: a server-URL shell needs network at launch (fine for a remote-support tool) and +some stores scrutinise "just a website". We mitigate by shipping real native capabilities +(push, screen capture, deep links), and can later switch to **bundled** web assets + an +absolute API base if offline-launch or store policy requires it. + +--- + +## Phased plan + +### Phase A — Shells that load the live app ← start here +- [ ] `desktop/` Electron client: `loadURL(server)`, `setDisplayMediaRequestHandler` so + screen-share works natively, external links → browser, persisted session. +- [ ] `mobile/` Capacitor project: `server.url` → Connect, app icons/splash, status bar. +- [ ] Inject `window.__NATIVE__ = 'desktop' | 'ios' | 'android'` so the web UI can adapt + (e.g. hide the PWA "install" prompt, enable native push instead of Web Push). + +### Phase B — Native capabilities +- [ ] **Push:** server device-token registration (`/api/v1/devices`) + a pluggable + `push` sender (FCM/APNs), config-gated. Mobile uses the Capacitor push plugin; + desktop keeps Web Push / in-app. *Needs Apple/Google creds to test end-to-end.* +- [ ] **Mobile screen capture** for "Share Screen" from a phone (ReplayKit / MediaProjection plugin). +- [ ] **Deep links / universal links** so a session/meeting link opens the app. + +### Phase C — Packaging & distribution *(needs external accounts)* +- [ ] Desktop installers via **electron-builder** (Win NSIS + Mac dmg); **code-signing** + (Win EV cert, Apple Developer ID + notarization). +- [ ] Mobile store builds: **Apple Developer** ($99/yr) + **Google Play** ($25 once); + signing keys; store listings & privacy disclosures. +- [ ] Agent host installer (signed) for customers. +- [ ] Auto-update channels. + +--- + +## Build & run + +### Desktop (technician client) +```bash +cd desktop +npm install +SERVER_URL=https://remote.bizgaze.com npm start # or http://localhost:8090 in dev +npm run dist # build installers (needs electron-builder + certs) +``` + +### Mobile (Capacitor) +```bash +cd mobile +npm install +npx cap add android # needs Android Studio + SDK +npx cap add ios # needs macOS + Xcode +npx cap sync +npx cap open android # build/run from Android Studio +npx cap open ios # build/run from Xcode +``` +Set the server origin in `capacitor.config.json` (`server.url`). + +### Agent host (existing) +```bash +cd agent +npm install +SERVER_URL=https://remote.bizgaze.com AGENT_ENROLL_TOKEN= npm start +``` + +--- + +## What's gated on you (external, can't be done from code alone) +- **Apple Developer** + **Google Play** accounts (mobile store builds & push). +- **Code-signing certificates** (Windows EV, Apple Developer ID) for trusted installers. +- **FCM/APNs credentials** for native push. +Everything else — the shells, the device/push backend, native plugin wiring — is built here. diff --git a/desktop/README.md b/desktop/README.md new file mode 100644 index 0000000..5ee3f2d --- /dev/null +++ b/desktop/README.md @@ -0,0 +1,26 @@ +# Biz Connect — Desktop client + +Electron shell that loads the live Connect web UI and adds native screen capture. See the +overall plan in [../CLIENTS.md](../CLIENTS.md). + +## Run (dev) +```bash +npm install +SERVER_URL=http://localhost:8090 npm start # default is https://remote.bizgaze.com +``` + +## Build installers +```bash +npm run dist # electron-builder → Win NSIS / Mac dmg / Linux AppImage +``` +Signed, trusted installers need certificates: +- **Windows:** an EV (or OV) code-signing certificate. +- **macOS:** Apple Developer ID cert + notarization (`CSC_LINK`, `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`). + +## Notes +- The window loads `${SERVER_URL}/home`; relative `/api` and `/ws` URLs work because the + origin is the server itself — no web-code changes. +- `setDisplayMediaRequestHandler` in `main.js` is what makes "Share Screen" work in Electron; + it currently defaults to the primary display. Swap in a source-picker for production. +- The session is persisted (`persist:bizconnect`) so login survives restarts. +- `window.__NATIVE__ === 'desktop'` is exposed for the web UI to feature-detect. diff --git a/desktop/main.js b/desktop/main.js new file mode 100644 index 0000000..d4d1b9c --- /dev/null +++ b/desktop/main.js @@ -0,0 +1,62 @@ +// Biz Connect — technician desktop client (Electron main process). +// +// This is a thin shell: it loads the live Connect web UI from the server origin, so every +// relative /api and /ws URL in the web app keeps working unchanged. What it adds over a +// browser tab: +// - native full-screen capture for "Share Screen" (setDisplayMediaRequestHandler) +// - a real desktop window (no browser chrome), persisted login session +// - external links open in the user's browser, not inside the app +// +// Server origin is configurable so the same build works against prod or a dev server. +const { app, BrowserWindow, session, desktopCapturer, shell, Menu } = require('electron'); +const path = require('path'); + +const SERVER_URL = (process.env.SERVER_URL || 'https://remote.bizgaze.com').replace(/\/+$/, ''); + +let win; + +function createWindow() { + win = new BrowserWindow({ + width: 1200, + height: 800, + minWidth: 880, + minHeight: 600, + title: 'Biz Connect', + backgroundColor: '#0f1830', + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + contextIsolation: true, + nodeIntegration: false, + // Persist cookies/localStorage so the technician stays logged in between launches. + partition: 'persist:bizconnect', + }, + }); + + win.loadURL(SERVER_URL + '/home'); + + // Open target=_blank / external links in the system browser instead of a new Electron window. + win.webContents.setWindowOpenHandler(({ url }) => { + if (!url.startsWith(SERVER_URL)) { shell.openExternal(url); return { action: 'deny' }; } + return { action: 'allow' }; + }); +} + +// Electron requires an explicit handler for getDisplayMedia(); without it the web UI's +// "Share Screen" silently fails. Default to the primary display with loopback audio. +// A production build can swap this for a source-picker window. +function registerDisplayMediaHandler() { + session.fromPartition('persist:bizconnect').setDisplayMediaRequestHandler((request, callback) => { + desktopCapturer.getSources({ types: ['screen', 'window'] }).then((sources) => { + callback(sources.length ? { video: sources[0], audio: 'loopback' } : {}); + }).catch(() => callback({})); + }); +} + +app.whenReady().then(() => { + registerDisplayMediaHandler(); + createWindow(); + Menu.setApplicationMenu(null); // hide the default menu bar; the web UI is the chrome + app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); +}); + +app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); diff --git a/desktop/package.json b/desktop/package.json new file mode 100644 index 0000000..be16b6f --- /dev/null +++ b/desktop/package.json @@ -0,0 +1,22 @@ +{ + "name": "biz-connect-desktop", + "version": "0.1.0", + "description": "Biz Connect technician desktop client — loads the Connect web UI with native screen capture", + "main": "main.js", + "scripts": { + "start": "electron .", + "dist": "electron-builder" + }, + "devDependencies": { + "electron": "^31.0.0", + "electron-builder": "^24.13.3" + }, + "build": { + "appId": "com.bizgaze.connect.desktop", + "productName": "Biz Connect", + "files": ["main.js", "preload.js", "assets/**"], + "win": { "target": "nsis" }, + "mac": { "target": "dmg", "category": "public.app-category.business" }, + "linux": { "target": "AppImage" } + } +} diff --git a/desktop/preload.js b/desktop/preload.js new file mode 100644 index 0000000..1699922 --- /dev/null +++ b/desktop/preload.js @@ -0,0 +1,10 @@ +// Minimal, safe bridge into the web UI. Runs with contextIsolation, so it only exposes a +// frozen marker the web app can feature-detect against (e.g. to hide the PWA install prompt +// or prefer native push). No Node APIs are exposed to page JS. +const { contextBridge } = require('electron'); + +contextBridge.exposeInMainWorld('__NATIVE__', 'desktop'); +contextBridge.exposeInMainWorld('bizConnectNative', Object.freeze({ + platform: 'desktop', + version: process.env.npm_package_version || '0.1.0', +})); diff --git a/mobile/README.md b/mobile/README.md new file mode 100644 index 0000000..b94a88e --- /dev/null +++ b/mobile/README.md @@ -0,0 +1,39 @@ +# Biz Connect — Mobile app (Capacitor) + +A Capacitor shell that loads the live Connect web UI (`server.url` in +`capacitor.config.json`) and adds native push, camera/mic, and store distribution. See the +overall plan in [../CLIENTS.md](../CLIENTS.md). + +## Prerequisites +- Node + `npm install` here. +- **Android:** Android Studio + SDK. +- **iOS:** macOS + Xcode (+ an Apple Developer account to run on device / ship). + +## Setup +```bash +npm install +npx cap add android +npx cap add ios # macOS only +npx cap sync +``` + +## Run / build +```bash +npx cap open android # build & run from Android Studio +npx cap open ios # build & run from Xcode +``` + +## Server origin +The app loads `server.url` from `capacitor.config.json` (default +`https://remote.bizgaze.com`). For a local device test against a dev server, set it to your +machine's LAN URL (and allow cleartext for plain http). + +## Native push (next step) +Native push uses the Capacitor Push Notifications plugin (FCM on Android, APNs on iOS) and a +server endpoint to register device tokens — tracked in [../CLIENTS.md](../CLIENTS.md) Phase B. +This is separate from the existing Web Push (VAPID) the PWA already uses. Needs Google/Apple +credentials to test end-to-end. + +## Shipping (gated on accounts) +- **Google Play:** one-time $25; upload an AAB; signing key. +- **App Store:** Apple Developer $99/yr; archive via Xcode; App Store Connect listing. diff --git a/mobile/capacitor.config.json b/mobile/capacitor.config.json new file mode 100644 index 0000000..b1ba2fc --- /dev/null +++ b/mobile/capacitor.config.json @@ -0,0 +1,15 @@ +{ + "appId": "com.bizgaze.connect", + "appName": "Biz Connect", + "webDir": "www", + "server": { + "url": "https://remote.bizgaze.com", + "cleartext": false, + "androidScheme": "https" + }, + "plugins": { + "PushNotifications": { + "presentationOptions": ["badge", "sound", "alert"] + } + } +} diff --git a/mobile/package.json b/mobile/package.json new file mode 100644 index 0000000..4c051b8 --- /dev/null +++ b/mobile/package.json @@ -0,0 +1,22 @@ +{ + "name": "biz-connect-mobile", + "version": "0.1.0", + "description": "Biz Connect mobile app — Capacitor shell loading the Connect web UI", + "scripts": { + "sync": "cap sync", + "android": "cap open android", + "ios": "cap open ios" + }, + "dependencies": { + "@capacitor/android": "^6.1.0", + "@capacitor/app": "^6.0.0", + "@capacitor/camera": "^6.0.0", + "@capacitor/core": "^6.1.0", + "@capacitor/ios": "^6.1.0", + "@capacitor/push-notifications": "^6.0.0", + "@capacitor/status-bar": "^6.0.0" + }, + "devDependencies": { + "@capacitor/cli": "^6.1.0" + } +} diff --git a/mobile/www/index.html b/mobile/www/index.html new file mode 100644 index 0000000..73e326d --- /dev/null +++ b/mobile/www/index.html @@ -0,0 +1,25 @@ + + + + + + + Biz Connect + + + +
+
+

Biz Connect

+

Connecting…

+
+
+ +