Skip to content

VIDSOL-649: [WEB] Add Electron Desktop Application Support#420

Open
czoli1976 wants to merge 20 commits intodevelopfrom
electron-desktop-support
Open

VIDSOL-649: [WEB] Add Electron Desktop Application Support#420
czoli1976 wants to merge 20 commits intodevelopfrom
electron-desktop-support

Conversation

@czoli1976
Copy link
Copy Markdown

@czoli1976 czoli1976 commented Mar 22, 2026

What is this PR doing?

Adds full Electron desktop application support to the Vonage Video React Reference App, allowing the same codebase to run as both a web app and a native desktop application on macOS, Windows, and Linux.

  1. Electron main process — embeds the Express backend via require() and serves the React frontend over localhost, so BrowserRouter, localStorage, window.location, and all existing web logic works unchanged
  2. Screen sharing picker — custom branded picker UI with tabbed "Entire Screen" / "Window" views, live thumbnails, and proper desktopCapturer integration. Supports both the Vonage SDK's desktop-capturer-get-sources IPC path and Chromium's setDisplayMediaRequestHandler
  3. macOS TCC permission handling — camera, microphone, and screen recording permissions work correctly via Info.plist patching (NSCameraUsageDescription, NSMicrophoneUsageDescription), setPermissionCheckHandler/setPermissionRequestHandler, and systemPreferences.getMediaAccessStatus() checks at startup
  4. Clipboard support — routed through Electron's main-process clipboard module via IPC, replacing navigator.clipboard which requires secure context focus that Electron doesn't always provide
  5. Vonage-branded About dialog — custom About window using VERA design tokens showing app name, version, SDK version, Electron version, with Vonage icon and GitHub link
  6. System tray menu — menu bar icon with Show/Hide, About, and Quit options
  7. App icons — full macOS .icns icon set, Windows .ico (16–256px, 6 resolutions), and cross-platform PNG tray icons, all generated from the Vonage logo
  8. Content Security Policy — configured for Vonage/OpenTok/TokBox domains, WebRTC, WebSocket, blob/mediastream sources
  9. Dev mode script (yarn dev:electron) — builds frontend + backend + Electron TypeScript, patches Info.plist for dev, launches via open -n on macOS for correct TCC attribution
  10. Packaging script (yarn build:electron) — electron-builder config for macOS (DMG, arm64+x64), Windows (NSIS, x64+arm64), Linux (AppImage, x64+arm64) with hardened runtime and entitlements
  11. VERA design tokens — About dialog and screen picker use VERA design system colors and Inter font for consistency with the main app
  12. ELECTRON.md documentation — comprehensive architecture guide covering the localhost embedding strategy, permission handling, screen sharing, CSP, build pipeline, and cross-platform considerations
  13. Updated README.md — new "Electron Desktop App" section with quick-start instructions
  14. Localized InfoPlist.strings — permission descriptions in en, de, es, es_MX, it (matching existing app language support)
  15. Check for Updates — About dialog includes a "Check for Updates" button powered by electron-updater
  16. Version-adaptive code — runtime detection of Electron major version for API branching across Electron 36–41+; tested on v36.9.5, v39.8.3, and v41.0.3
  17. Security hardeningwill-navigate handler to block navigation outside localhost, setWindowOpenHandler to deny new windows, render-process-gone crash recovery, unresponsive handler, spellchecker disabled (per Electron Security Checklist)
  18. Modal screen picker — picker is modal to the parent window with alwaysOnTop: 'floating' level; About dialog floats above the picker
  19. Permission probe timingdesktopCapturer.getSources() probe fires after win.loadURL() completes so the macOS permission dialog appears in front of the app
  20. Developer toolingyarn test:electron-version <version> to test with any Electron version/channel (auto-restores original), yarn test:tcc to diagnose macOS TCC permissions
  21. TCC troubleshooting docs — ELECTRON.md includes troubleshooting table for all common macOS permission issues

Architecture

The Electron app uses a localhost embedding strategy: the main process starts the Express backend server, then loads http://localhost:<port> in a BrowserWindow. This means:

  • Zero frontend changes needed for routing, storage, or state management — the renderer is just a Chromium browser hitting localhost
  • The only frontend changes are:
    • useRoomShareUrl.tsx — detects Electron context to show a "copy link" button instead of relying on window.location.origin (which would be localhost in Electron)
    • clipboard.ts — utility that routes clipboard writes through Electron's IPC when available
    • ParticipantList.tsx / SmallViewportHeader.tsx — use the clipboard utility for share URL copying
    • electron.d.ts — TypeScript declarations for window.electron
┌─────────────────────────────────────────────┐
│           Electron Main Process             │
│                                             │
│  ┌──────────┐  ┌──────────────────────────┐ │
│  │  Express  │  │   BrowserWindow          │ │
│  │  Backend  │  │   (Chromium renderer)    │ │
│  │  :3345    │──│   loads localhost:3345   │ │
│  │           │  │                          │ │
│  │  bundle   │  │   React Frontend         │ │
│  │  .cjs     │  │   (unchanged)            │ │
│  └──────────┘  └──────────────────────────┘ │
│                                             │
│  ┌──────────┐  ┌──────────────────────────┐ │
│  │   Tray   │  │  Screen Picker Window    │ │
│  │   Menu   │  │  (custom branded UI)     │ │
│  └──────────┘  └──────────────────────────┘ │
└─────────────────────────────────────────────┘

Cross-Platform Support

The codebase is designed to work on macOS, Windows, and Linux from a single codebase with platform-specific handling where needed:

Feature macOS Windows Linux
Installer format DMG NSIS AppImage
Architectures arm64, x64 x64, arm64 x64, arm64
App icon .icns (16–512@2x) .ico (16–256px) .png (512px)
Tray icon Template image (auto dark/light) Colored PNG Colored PNG
Menu bar macOS app menu Per-window (hidden) Per-window (hidden)
Title bar Hidden inset (About) Default Default
Window close Hide to dock Quit app Quit app
Quit shortcut Cmd+Q Alt+F4 Alt+F4
Camera/Mic permissions TCC system prompts Auto-granted Auto-granted
Screen Recording TCC permission required Auto-granted Auto-granted
Tray click Right-click (context menu) Left-click (show/focus) Left-click (show/focus)
Code signing Hardened runtime + entitlements Certificate (optional) N/A

Note: This PR has been developed and tested on macOS. Windows and Linux builds are architecturally supported (all platform-specific code uses process.platform guards), but have not yet been tested on those platforms. Community testing on Windows and Linux is welcome.

Electron Version Compatibility

The code is version-adaptive — it detects the Electron major version at startup and adapts API calls accordingly. Tested and verified on:

Electron Version Chromium Status
36.9.5 128 ✅ All features working
39.8.3 134 ✅ All features working
41.0.3 (latest stable) 136 ✅ All features working

Key version adaptations:

  • Electron 39+: setDisplayMediaRequestHandler requires { useSystemPicker: false } to use the custom picker instead of the OS-level picker
  • Electron 39+: Permission handler callback patterns adjusted
  • macOS 14.2+ / Electron 39+: NSAudioCaptureUsageDescription added to Info.plist for CoreAudio Tap API
  • All versions: electron-updater dynamically imported (no hard dependency at startup)

New Files

File Purpose
electron/main.ts Electron main process — backend embedding, window management, permissions, CSP, tray, screen sharing
electron/preload.ts Context bridge — exposes window.electron API (clipboard, screen capture, external links)
electron/preload.d.ts TypeScript declarations for the preload API
electron/picker-preload.ts Preload script for the screen sharing picker window
electron/picker.html Screen sharing picker UI — tabbed layout with live thumbnails
electron/about.html About dialog — Vonage-branded with version info
electron/project.json Nx project descriptor with build, dev, package, ts-check targets
electron/tsconfig.json TypeScript config targeting CommonJS + Node
electron/electron-builder.json Packaging config for macOS/Windows/Linux
electron/entitlements.mac.plist macOS entitlements — camera, microphone, screen capture, JIT, network
electron/ELECTRON.md Architecture documentation and developer guide
electron/assets/ App icons (.icns, .ico, iconset PNGs, tray icons)
electron/build-resources/ Localized InfoPlist.strings for permission descriptions
scripts/electronDev.ts Dev mode launcher — build + plist patch + open -n launch
scripts/electronPackage.ts Production packaging script
scripts/generateElectronIcons.ts Icon generation from source PNG
frontend/src/utils/clipboard.ts Clipboard utility with Electron IPC fallback
frontend/src/types/electron.d.ts window.electron type declarations

Modified Files

File Change
package.json Added electron, electron-builder devDependencies; dev:electron, build:electron scripts
scripts/dev.ts Added devElectron() function and electron target in main switch
README.md Added "Electron Desktop App" section with quick-start instructions
frontend/src/hooks/useRoomShareUrl.tsx Detect Electron context for share URL handling
frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.tsx Use clipboard utility
frontend/src/components/MeetingRoom/SmallViewportHeader/SmallViewportHeader.tsx Use clipboard utility
.prettierignore Added .claude/ to ignore list
frontend/tsconfig.json Removed stale project reference added by nx sync

macOS TCC (Transparency, Consent, Control) Permissions

Camera, microphone, and screen recording permissions on macOS require special handling in Electron:

  • Info.plist patchingNSCameraUsageDescription and NSMicrophoneUsageDescription are required for macOS to show permission prompts. The dev script patches the Electron framework's Info.plist at startup
  • open -n launch — In dev mode, the app is launched via open -n Electron.app --args ... instead of spawning as a child process. This makes Electron the "responsible process" for TCC, so macOS correctly attributes permission requests to the Electron app rather than the parent terminal/IDE
  • Entitlements — The entitlements.mac.plist declares camera, microphone, screen capture, JIT, unsigned executable memory, and network entitlements for hardened runtime builds
  • Localized permission descriptionsInfoPlist.strings files provide localized descriptions for permission prompts in en, de, es, es_MX, and it

Screen Sharing

The screen sharing implementation supports two code paths:

  1. Vonage SDK path — The SDK calls window.electron.desktopCapturer.getSources() via IPC, which opens the custom picker and returns the selected source
  2. Chromium pathsetDisplayMediaRequestHandler intercepts getDisplayMedia() calls and routes them through the same picker

The picker UI features:

  • Tabbed interface: "Entire Screen" and "Window" views
  • Live thumbnails refreshed every 2 seconds
  • Vonage branding with VERA dark theme tokens and Inter font
  • Cancel/Share buttons with keyboard support (Escape to cancel)

How should this be manually tested?

Prerequisites:

  • Clone the repo and run yarn install
  • Ensure backend/.env has valid Vonage credentials (API key, App ID, private key)
  • Node.js >= 22.x required

Quick Start:

yarn dev:electron

A. App Launch and Landing Page

# Scenario Steps Expected outcome
A1 Dev mode launch Run yarn dev:electron App builds, Electron window opens, landing page visible with Vonage branding
A2 Create room Click "Create a new room" Room is created, navigates to waiting room
A3 Join existing room Enter a valid room name, click "Join waiting room" Navigates to waiting room for that room
A4 About dialog Click Vonage Video menu > About About dialog shows app name, version, SDK version, Electron version, Vonage icon
A5 Tray menu Check system tray / menu bar Vonage icon visible, menu has Show/Hide, About, Quit options
A6 External links Click the GitHub link in About dialog or footer Opens in default browser (not in Electron)

B. Camera and Microphone

# Scenario Steps Expected outcome
B1 Camera preview Navigate to waiting room Camera preview shows in the video container
B2 Permission prompt (first run) First launch on a fresh system macOS shows camera/microphone permission prompts for Electron
B3 Microphone level Speak while in waiting room Microphone level indicator responds
B4 Camera toggle Click camera on/off button Camera preview toggles
B5 Microphone toggle Click microphone on/off button Microphone toggles
B6 Device selection Open device settings dropdown Available cameras and microphones listed, selection works

C. Screen Sharing

# Scenario Steps Expected outcome
C1 Screen share picker In a meeting, click screen share button Custom picker opens with "Entire Screen" and "Window" tabs
C2 Screen tab View "Entire Screen" tab Shows available displays with thumbnails
C3 Window tab Click "Window" tab Shows available application windows with thumbnails
C4 Select and share Select a source, click "Share" Screen sharing starts, remote participants see shared content
C5 Cancel Open picker, press Escape or click "Cancel" Picker closes, no sharing started
C6 Stop sharing While sharing, click stop share button Screen sharing stops
C7 Screen Recording permission (first run) First screen share attempt on macOS System prompts to grant Screen Recording permission, or shows dialog to open System Settings

D. Clipboard and Share URL

# Scenario Steps Expected outcome
D1 Copy room link In meeting, open participant list, click copy link Room URL copied to clipboard (verifiable by pasting)
D2 Copy from header On small viewport, click share in header URL copied to clipboard

E. Meeting Functionality

# Scenario Steps Expected outcome
E1 Join meeting From waiting room, click join Enters meeting room, publisher stream visible
E2 Two participants Join same room from browser + Electron Both see each other's video and hear audio
E3 Leave meeting Click end call button Returns to goodbye page

F. Web App Regression

# Scenario Steps Expected outcome
F1 Web app unchanged Run yarn dev (regular web mode) Web app works normally — no regressions
F2 Web clipboard In web browser, copy room link Uses native navigator.clipboard (not Electron IPC)
F3 Web screen share In web browser, click screen share Uses native getDisplayMedia (not Electron picker)

G. Cross-Platform (Windows / Linux)

# Scenario Steps Expected outcome
G1 Windows dev launch Run yarn dev:electron on Windows App builds and launches, landing page visible
G2 Windows camera/mic Navigate to waiting room Camera and microphone work without OS permission prompts (auto-granted)
G3 Windows screen share Click screen share in meeting Custom picker shows available screens and windows
G4 Windows tray Check system tray Vonage icon visible with context menu
G5 Windows packaging Run yarn build:electron Produces NSIS installer in dist/electron/
G6 Linux dev launch Run yarn dev:electron on Linux App builds and launches, landing page visible
G7 Linux camera/mic Navigate to waiting room Camera and microphone work (may need PulseAudio/PipeWire)
G8 Linux screen share Click screen share in meeting Custom picker shows available screens and windows
G9 Linux packaging Run yarn build:electron Produces AppImage in dist/electron/

⚠️ Note: Tests G1–G9 have not been verified yet. The code handles all platforms via process.platform guards but needs community testing on Windows and Linux.


Screenshots

Waiting Room — Permission Prompts
Microphone Camera
Mic permission Camera permission
Waiting Room — Camera Working
Camera working
About Dialog
About dialog
Tray Icon
Tray icon
Screen Sharing Picker
Entire Screen Window
Picker - Entire screen Picker - Window

What are the relevant tickets?

VIDSOL-649 — [WEB] Add Electron Desktop Application Support

Documentation

  • electron/ELECTRON.md — comprehensive architecture and developer documentation added to the repo
  • README.md — updated with Electron quick-start section

Checklist

  • Branch is based on develop (not main)
  • Resolves a Known Issue
  • If yes, did you remove the item from the docs/KNOWN_ISSUES.md?
  • Resolves an item reported in Issues

Generated with Claude Code

Copilot AI review requested due to automatic review settings March 22, 2026 23:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an Electron desktop application (Nx project + build/dev/package tooling) around the existing React frontend by serving frontend/dist from a localhost HTTP server inside the Electron main process, with custom screen-share picker UI, permissions handling, clipboard IPC, and accompanying docs/assets.

Changes:

  • Introduces a new electron/ application (main + preload scripts, picker/about windows, packaging config, macOS entitlements/resources).
  • Adds Electron dev/package scripts (including macOS Info.plist patching) and updates workspace scripts/README.
  • Updates a small set of frontend utilities/components to support Electron clipboard + share URL origin via TUNNEL_DOMAIN.

Reviewed changes

Copilot reviewed 26 out of 47 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
scripts/generateElectronIcons.ts Generates Electron app/tray icons (currently macOS-tooling dependent).
scripts/electronPackage.ts Build + package flow for electron-builder.
scripts/electronDev.ts Dev build + macOS Info.plist patch + launch flow.
scripts/dev.ts Adds yarn dev electron target (backend + Electron).
package.json Adds Electron scripts and Electron-related devDependencies.
frontend/src/utils/clipboard.ts Clipboard utility with Electron IPC fallback.
frontend/src/types/electron.d.ts Renderer ambient typings for window.electron.
frontend/src/hooks/useRoomShareUrl.tsx Uses TUNNEL_DOMAIN for externally shareable URLs (Electron-friendly).
frontend/src/components/MeetingRoom/SmallViewportHeader/SmallViewportHeader.tsx Uses clipboard utility for copy-link.
frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.tsx Uses clipboard utility for copy-link.
electron/tsconfig.json TypeScript config for Electron main/preload compilation.
electron/project.json Nx project targets for Electron (build/dev/package/ts-check).
electron/preload.ts Exposes minimal window.electron API via contextBridge.
electron/preload.d.ts Electron preload ambient typings (currently incomplete vs exposed API).
electron/picker-preload.ts Context bridge for the screen-share picker window.
electron/picker.html Custom screen-share source picker UI.
electron/main.ts Electron main process: localhost server, permissions, CSP, picker IPC, tray, updates, windows.
electron/entitlements.mac.plist macOS hardened runtime entitlements for media/screen/network.
electron/electron-builder.json electron-builder packaging configuration.
electron/build-resources/en.lproj/InfoPlist.strings Localized macOS permission strings (en).
electron/build-resources/de.lproj/InfoPlist.strings Localized macOS permission strings (de).
electron/build-resources/es.lproj/InfoPlist.strings Localized macOS permission strings (es).
electron/build-resources/es_MX.lproj/InfoPlist.strings Localized macOS permission strings (es_MX).
electron/build-resources/it.lproj/InfoPlist.strings Localized macOS permission strings (it).
electron/assets/tray-template.png macOS tray template icon asset.
electron/assets/tray-template@2x.png macOS tray template icon asset (@2x).
electron/assets/tray-icon.png Windows/Linux tray icon asset.
electron/assets/tray-icon@2x.png Windows/Linux tray icon asset (@2x).
electron/assets/app-icon.png App/window icon asset.
electron/assets/AppIcon.iconset/icon_16x16.png macOS iconset raster asset.
electron/assets/AppIcon.iconset/icon_16x16@2x.png macOS iconset raster asset.
electron/assets/AppIcon.iconset/icon_32x32.png macOS iconset raster asset.
electron/assets/AppIcon.iconset/icon_32x32@2x.png macOS iconset raster asset.
electron/assets/AppIcon.iconset/icon_64x64.png macOS iconset raster asset.
electron/assets/AppIcon.iconset/icon_64x64@2x.png macOS iconset raster asset.
electron/assets/AppIcon.iconset/icon_128x128.png macOS iconset raster asset.
electron/assets/AppIcon.iconset/icon_128x128@2x.png macOS iconset raster asset.
electron/assets/AppIcon.iconset/icon_256x256.png macOS iconset raster asset.
electron/about.html Vonage-branded About dialog window.
electron/ELECTRON.md Architecture and developer documentation for Electron support.
README.md Adds Electron Desktop App docs/quick start section.
.prettierignore Ignores .claude/.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 47 changed files in this pull request and generated 6 comments.

@czoli1976
Copy link
Copy Markdown
Author

📸 Screenshots

Please reply to this comment with the screenshots by dragging and dropping them here. Label each one:

  1. Waiting room — Microphone permission prompt
  2. Waiting room — Camera permission prompt
  3. Waiting room — Camera working
  4. About dialog (with Check for Updates)
  5. Tray icon (menu bar)
  6. Screen picker — Entire screen tab
  7. Screen picker — Window tab

Once uploaded, the image URLs will be added to the PR description.

@czoli1976
Copy link
Copy Markdown
Author

📸 Screenshots

Please reply to this comment with the screenshots by dragging and dropping them here. Label each one:

  1. Waiting room — Microphone permission prompt
image
  1. Waiting room — Camera permission prompt
image > 3. **Waiting room — Camera working** image
  1. About dialog (with Check for Updates)
image
  1. Tray icon (menu bar)
image
  1. Screen picker — Entire screen tab
image
  1. Screen picker — Window tab
image

Once uploaded, the image URLs will be added to the PR description.

@czoli1976 czoli1976 changed the title Add Electron desktop application support VIDSOL-649: [WEB] Add Electron Desktop Application Support Mar 23, 2026
czoli1976 and others added 11 commits March 23, 2026 16:57
- Electron main process embedding Express backend via localhost
- Custom screen sharing picker with tabbed Entire Screen / Window UI
- macOS TCC permission handling (camera, mic, screen recording)
- Vonage-branded About dialog, tray menu, and app icons
- Clipboard support routed through Electron's main-process API
- Content Security Policy configured for Vonage/OpenTok domains
- Dev mode script (yarn dev:electron) and packaging script (yarn build:electron)
- electron-builder config for macOS (dmg), Windows (nsis), Linux (AppImage)
- ELECTRON.md documentation covering architecture and setup
- Updated README.md with Electron section
- Localized InfoPlist.strings for en, de, es, es_MX, it

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix path traversal vulnerability in static file server (resolve + prefix check)
- Scope permission handlers to trusted localhost origin only
- Use BrowserWindow.fromWebContents() instead of title-based picker window lookup
- Guard against concurrent screen picker requests (reuse existing window)
- Enable sandbox: true on all BrowserWindows for defense-in-depth
- Add platform guard to icon generation script (skip on non-macOS)
- Fix ELECTRON.md documentation inconsistency about backend embedding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded CSS colors with VERA design system tokens for consistency
with the main app. About dialog uses light theme tokens, Picker uses dark
theme tokens. Both now load Inter font from Google Fonts CDN.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Generate app-icon.ico with 6 resolutions (16–256px) for sharp Windows
  taskbar/Start menu icons
- Add arm64 architecture targets for Windows (NSIS) and Linux (AppImage)
- Point Windows icon config to .ico instead of .png
- Document .ico as a committed asset in generateElectronIcons.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Fix path traversal guard: use path.relative() instead of startsWith()
  to prevent prefix collision bypasses
- Wrap decodeURIComponent in try/catch, return 400 on malformed URLs
- Only SPA-fallback to index.html for extensionless routes; return 404
  for missing static assets so build issues surface immediately
- Fix plist idempotence check: match CFBundleDisplayName specifically
  instead of any occurrence of the product name string
- Add Escape key handler to picker for keyboard cancel
- Add periodic thumbnail refresh (2s interval) via new IPC channel
- Fix ELECTRON.md: clarify backend is not bundled in packaged builds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… management

- About dialog: add "Check for Updates" button (uses electron-updater in
  packaged builds, shows friendly message in dev mode)
- Picker window: increase size by 20% (864×624) for better visibility
- Picker: set alwaysOnTop with 'floating' level so it cannot lose focus
- About: also floats above picker when both are open
- Dismiss picker automatically if About is opened (context switch)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ELECTRON_MAJOR version detection at startup for runtime API branching
- setDisplayMediaRequestHandler: pass { useSystemPicker: false } so our
  custom picker is used on Electron 39+ (which defaults to system picker)
- Permission handlers: version-branched for Electron ≤38 vs 39+ patterns
- Screen recording pre-check: always attempt getSources() instead of
  bailing on getMediaAccessStatus('denied') — macOS TCC can report stale
  status after binary swaps
- Dynamic import electron-updater everywhere (no top-level import)
- Add NSAudioCaptureUsageDescription to plist for macOS 14.2+ / Electron 39+
- setDisplayMediaRequestHandler: runtime feature check with graceful fallback

Tested on Electron 36.9.5 and 39.8.3 — screen share, permissions, picker,
About dialog, and Check for Updates all working on both versions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add tested versions (36, 39, 41), version-specific adaptations, and
step-by-step instructions for upgrading Electron and resetting macOS
TCC permissions after binary swaps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the desktopCapturer.getSources() startup probe to fire after
win.loadURL() completes, so the macOS permission dialog appears in
front of the app window rather than behind it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 48 changed files in this pull request and generated 5 comments.

Comment on lines +100 to +102
"electron": "36.9.5",
"electron-builder": "^25.1.8",
"electron-updater": "^6.8.3",
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

electron-updater is listed under devDependencies, but it’s dynamically imported at runtime in the packaged app (see electron/main.ts). electron-builder typically bundles only production dependencies, so packaged builds may not include electron-updater, causing update checks to always fall back (silent no-op). Move electron-updater to dependencies (or explicitly include it in the packaged app) so auto-update works in release artifacts.

Copilot uses AI. Check for mistakes.
electron/main.ts Outdated
Comment on lines +326 to +333
const ALLOWED_REQUEST = [
'media',
'display-capture',
'mediaKeySystem',
'notifications',
'clipboard-write',
'clipboard-read',
];
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

configureMediaPermissions() allows clipboard-read (and notifications) for the renderer. The frontend changes in this PR only require clipboard write (and Electron already routes writes through IPC), and there’s no usage of clipboard-read in the web code. Consider removing clipboard-read (and any other unused permissions) from the allowed lists to reduce the impact of any renderer XSS to data-exfiltration.

Copilot uses AI. Check for mistakes.
Comment on lines 5 to +13
* Creates a shareable link to the waiting room for the current meeting room.
* When running inside Electron, window.location.origin is 'http://localhost:PORT'
* which is not externally reachable. If TUNNEL_DOMAIN is configured (e.g. an ngrok
* domain), it is used as the base so the link can be shared with web users.
* @returns {string} - The shareable link.
*/
const useRoomShareUrl = (): string => {
const roomName = useRoomName();
const { origin } = window.location;
const origin = env.TUNNEL_DOMAIN ? `https://${env.TUNNEL_DOMAIN}` : window.location.origin;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring says this origin override is specifically for Electron, but the implementation uses env.TUNNEL_DOMAIN unconditionally (it will also affect normal web builds whenever the env var is set). Either update the comment to reflect the real behavior, or gate the override behind an Electron check (e.g., window.electron?.isElectron) so the semantics match the documentation.

Copilot uses AI. Check for mistakes.
* The backend runs in the background; the Electron app is the foreground process.
*/
function devElectron(): void {
runCommand("concurrently --kill-others 'nx run backend:dev' 'npx tsx scripts/electronDev.ts'");
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concurrently command is wrapped in single quotes. On Windows shells (cmd/powershell), single quotes are not treated as string quotes in the same way, which can break yarn dev electron—especially important since this PR aims for cross-platform Electron support. Prefer double quotes for the subcommands (or use concurrently --raw / --command patterns) so it runs consistently across platforms.

Copilot uses AI. Check for mistakes.
electron/main.ts Outdated
Comment on lines +31 to +60
let APP_VERSION = 'unknown';
let SDK_VERSION = 'unknown';

try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
APP_VERSION = (require('../../package.json') as { version: string }).version;
} catch {
/* leave as "unknown" */
}

try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
SDK_VERSION = (require('@vonage/client-sdk-video/package.json') as { version: string }).version;
} catch {
/* leave as "unknown" */
}

// ─── Electron version detection ──────────────────────────────────────────────
// Parse the major version once at startup so we can adapt to API changes
// across Electron 36–41+ without hard-coding version checks everywhere.
const ELECTRON_MAJOR = parseInt(process.versions.electron?.split('.')[0] ?? '0', 10);
console.log(`[Electron] Running Electron ${process.versions.electron} (major: ${ELECTRON_MAJOR})`);

// ─── Paths ────────────────────────────────────────────────────────────────────

const IS_PACKAGED = app.isPackaged;

const ASSETS_DIR = IS_PACKAGED
? path.join(process.resourcesPath, 'electron-assets')
: path.join(__dirname, '../assets');
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several newly introduced identifiers use acronyms/initialisms (e.g., SDK_VERSION, APP_VERSION, ELECTRON_MAJOR, IS_PACKAGED, ASSETS_DIR). Repository guidelines ban acronyms in names; prefer fully descriptive names (e.g., videoSdkVersion, applicationVersion, electronMajorVersion, isPackaged, assetsDirectory) to keep naming consistent and readable.

Copilot generated this review using guidance from repository custom instructions.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the naming convention issue flagged in this comment. Renamed all constants to follow the repo's no-acronyms rule:

  • APP_VERSIONapplicationVersion
  • SDK_VERSIONvideoSdkVersion
  • ELECTRON_MAJORelectronMajorVersion
  • IS_PACKAGEDisPackaged
  • ASSETS_DIRassetsDirectory
  • STATIC_PORTstaticPort
  • MIMEmimeTypes
  • ALLOWED_REQUESTallowedRequestPermissions
  • ALLOWED_CHECKallowedCheckPermissions
  • configureCSP()configureContentSecurityPolicy()

czoli1976 and others added 3 commits March 26, 2026 12:02
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace SCREAMING_CASE acronym identifiers with descriptive camelCase
names per repository guidelines: APP_VERSION → applicationVersion,
SDK_VERSION → videoSdkVersion, ELECTRON_MAJOR → electronMajorVersion,
IS_PACKAGED → isPackaged, ASSETS_DIR → assetsDirectory,
STATIC_PORT → staticPort, MIME → mimeTypes, configureCSP →
configureContentSecurityPolicy, ALLOWED_REQUEST/CHECK →
allowedRequestPermissions/allowedCheckPermissions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ments

Security (per Electron security checklist):
- Add will-navigate handler to block navigation outside trusted localhost
- Add setWindowOpenHandler to deny new window creation
- Add render-process-gone handler with crash recovery dialog
- Add unresponsive handler with wait/restart dialog
- Disable spellchecker to prevent data sent to OS spell services

Developer experience:
- Add IPC channel reference documentation at top of main.ts
- Add version detection comments explaining why branching is needed
- Add descriptive error logging for static server failures
- Add CSP confirmation log on startup
- Add Escape/Cmd+W keyboard shortcuts to close About dialog
- Remove auto-opening DevTools (still accessible via View menu)

References:
- https://www.electronjs.org/docs/latest/tutorial/security
- Items #13 (limit navigation) and #14 (limit new windows)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@czoli1976
Copy link
Copy Markdown
Author

Latest commits: Security hardening + Developer experience

Security best practices (per Electron Security Checklist)

Practice Status Reference
contextIsolation: true ✅ Already done #3
nodeIntegration: false ✅ Already done #2
sandbox: true ✅ Already done #1
Content Security Policy ✅ Already done #6
Permission handlers ✅ Already done #5
Single instance lock ✅ Already done
Path traversal protection ✅ Already done
Limit navigation Added #13
Limit new windows Added #14
Crash recovery Added render-process-gone + unresponsive handlers
Spellchecker disabled Added Prevents data sent to OS spell services

Developer experience improvements

  • IPC channel documentation — centralized reference of all renderer ↔ main IPC channels at top of main.ts
  • Version detection comments — explains why version branching is needed (Electron 39+ API changes)
  • Static server error logging — logs port and error message on failure
  • CSP confirmation log — confirms Content Security Policy is active at startup
  • About dialog keyboard shortcuts — Escape and Cmd/Ctrl+W to close
  • DevTools no longer auto-open — accessible via View → Toggle Developer Tools (Cmd+Option+I)

Naming convention fix

Renamed all SCREAMING_CASE constants to camelCase per repo guidelines (no acronyms rule):
APP_VERSIONapplicationVersion, SDK_VERSIONvideoSdkVersion, ELECTRON_MAJORelectronMajorVersion, IS_PACKAGEDisPackaged, ASSETS_DIRassetsDirectory, STATIC_PORTstaticPort, MIMEmimeTypes, configureCSPconfigureContentSecurityPolicy

New helper script to test the app with any Electron version or channel
(stable, beta, alpha, nightly). Automatically installs the requested
version, resets macOS TCC permissions, runs TypeScript check, launches
the app, and restores the original version on exit.

Usage: yarn test:electron-version 41.0.3
       yarn test:electron-version beta

Also expanded ELECTRON.md with detailed version testing docs including
release channel table and manual swap instructions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
czoli1976 and others added 3 commits March 26, 2026 12:34
New script (yarn test:tcc) that checks macOS TCC permission status for
Camera, Microphone, and Screen Recording. Shows Info.plist patching
status and suggests fixes for common permission issues.

Added troubleshooting table to ELECTRON.md covering all TCC issues we
encountered during development: binary hash changes, permission dialog
ordering, responsible process attribution, and version swap resets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@czoli1976 czoli1976 force-pushed the electron-desktop-support branch from d8f3648 to 791f3cc Compare March 26, 2026 15:04
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
26 Security Hotspots
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

The previous commit excluded only 3 specific .ts files, but SonarCloud
also analyzes .html, .json, and other files in the electron/ directory.
Using electron/** catches all Electron main-process files that cannot
be covered by the existing Vitest browser test suite.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants