Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1f0dc6e
Add Electron desktop application support
czoli1976 Mar 22, 2026
abc3191
Address Copilot review: security and quality fixes
czoli1976 Mar 22, 2026
48064af
Update About and Picker dialogs to use VERA design tokens and Inter font
czoli1976 Mar 22, 2026
48a6894
Add Windows .ico icon and ARM64 build targets for Windows/Linux
czoli1976 Mar 23, 2026
a6220dc
Update electron/main.ts
czoli1976 Mar 23, 2026
cbc507d
Update electron/main.ts
czoli1976 Mar 23, 2026
34defd6
Address Copilot review: harden static server, add picker Escape/refresh
czoli1976 Mar 23, 2026
6b45dfe
Add Check for Updates to About dialog, enlarge picker, improve window…
czoli1976 Mar 23, 2026
12a2124
Make Electron code version-adaptive for v36–41+ compatibility
czoli1976 Mar 23, 2026
9cbf512
Update ELECTRON.md with version compatibility table and upgrade guide
czoli1976 Mar 23, 2026
8bb92c7
Delay screen recording probe until after app window is fully loaded
czoli1976 Mar 23, 2026
7870169
chore: re-trigger CI checks
czoli1976 Mar 26, 2026
7510051
Rename constants to follow repo no-acronyms naming convention
czoli1976 Mar 26, 2026
c18b6a4
Add Electron security best practices and developer experience improve…
czoli1976 Mar 26, 2026
fe1f2ff
Add test:electron-version script for cross-version testing
czoli1976 Mar 26, 2026
d8402a0
Add TCC diagnostic script and troubleshooting docs
czoli1976 Mar 26, 2026
7604248
Exclude Electron bootstrap files from Sonar coverage
czoli1976 Mar 26, 2026
791f3cc
Fix Electron packaging build
czoli1976 Mar 26, 2026
b49a25b
Fix dependency resolution for CI checks
czoli1976 Mar 26, 2026
75da874
fix(sonar): exclude entire electron/ directory from coverage gate
czoli1976 Mar 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ frontend/docs/
# Documentation [todo]: We should be able to use prettier for this to ensure consistent formatting
*.md

# Claude Code local config
.claude/

# Build and dependency folders
dist/
node_modules/
Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ If you're new to Vonage, you can [sign up for a Vonage API account](https://dash
- [Getting Involved](#getting-involved)
- [Known Issues](#known-issues)
- [Report Issues](#report-issues)
- [Electron Desktop App](#electron-desktop-app)

<!-- /TOC -->
## What is it?
Expand Down Expand Up @@ -134,6 +135,7 @@ Looking to build on other platforms? The Vonage Video API Reference App is also

- *iOS*: [vonage-video-ios-app](https://github.com/Vonage/vonage-video-ios-app)
- *Android*: [vonage-video-android-app](https://github.com/Vonage/vonage-video-android-app)
- *Desktop (macOS, Windows, Linux)*: See [Electron Desktop App](#electron-desktop-app) below

These reference apps share the same backend infrastructure and demonstrate consistent best practices across all platforms, making it easy to build unified video experiences for your users.

Expand Down Expand Up @@ -453,3 +455,41 @@ We track known issues in [Known Issues](./docs/KNOWN_ISSUES.md). Please refer to
## Report Issues

If you have any issues, feel free to open an issue or reach out to support via [support@api.vonage.com](support@api.vonage.com).

## Electron Desktop App

The Reference App can also run as a native desktop application using [Electron](https://www.electronjs.org/). The same React frontend is served locally inside an Electron `BrowserWindow`, so all features (video calls, screen sharing, chat, reactions, etc.) work identically to the web version.

For a detailed explanation of the implementation, see [electron/ELECTRON.md](electron/ELECTRON.md).

### Quick Start

```bash
yarn dev electron
```

This single command starts the backend server and the Electron desktop app together. It builds the frontend, compiles the Electron main process, and launches the app.

Alternatively, you can run them separately:

```bash
# Terminal 1: start the backend
yarn dev backend

# Terminal 2: build and launch Electron only
yarn dev:electron
```

### macOS Permissions

On macOS, you will be prompted to grant **Camera** and **Microphone** access on first use. For **Screen Sharing**, you must manually enable Screen Recording in **System Settings > Privacy & Security > Screen & System Audio Recording** and restart the app.

### Packaging for Distribution

To build platform-specific installers (`.dmg` for macOS, `.exe` for Windows, `.AppImage` for Linux):

```bash
yarn build:electron
```

Output is written to the `dist/` directory. See `electron/electron-builder.json` for packaging configuration.
324 changes: 324 additions & 0 deletions electron/ELECTRON.md

Large diffs are not rendered by default.

278 changes: 278 additions & 0 deletions electron/about.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; style-src 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; script-src 'unsafe-inline';"
/>
<title>About Vonage Video</title>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap"
/>
<style>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}

:root {
--vera-primary: #9941ff;
--vera-surface: #ffffff;
--vera-background: #f5f0fd;
--vera-border: #e6e6e6;
--vera-text-primary: #000000;
--vera-text-tertiary: #757575;
}

html,
body {
height: 100%;
background: var(--vera-background);
font-family: Inter, sans-serif, system-ui, ui-sans-serif;
color: var(--vera-text-primary);
-webkit-font-smoothing: antialiased;
user-select: none;
-webkit-app-region: drag;
}

.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
gap: 8px;
/* Leave room for the hidden traffic-light buttons at the top */
padding: 40px 32px 28px;
}

/* Rounded-square icon container — macOS app icon style */
.icon {
width: 100px;
height: 100px;
border-radius: 22px;
background: var(--vera-surface);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 14px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.14);
flex-shrink: 0;
}
.icon svg {
width: 72px;
height: 72px;
}

.app-name {
font-size: 22px;
font-weight: 600;
letter-spacing: -0.3px;
color: var(--vera-text-primary);
}

.app-version {
font-size: 16px;
color: var(--vera-text-primary);
}

.build-info {
font-size: 14px;
color: var(--vera-text-tertiary);
}

.divider {
width: 260px;
height: 1px;
background: var(--vera-border);
margin: 10px 0;
}

.credits {
font-size: 14px;
color: var(--vera-text-tertiary);
text-align: center;
}

/* GitHub link button */
.github-btn {
display: inline-flex;
align-items: center;
gap: 8px;
margin-top: 4px;
padding: 6px 12px;
border-radius: 8px;
background: transparent;
border: none;
cursor: pointer;
color: var(--vera-primary);
font-size: 14px;
font-family: inherit;
-webkit-app-region: no-drag;
transition: background 0.12s;
}
.github-btn:hover {
background: rgba(153, 65, 255, 0.08);
}
.github-btn:active {
background: rgba(153, 65, 255, 0.16);
}
.github-btn svg {
width: 18px;
height: 18px;
flex-shrink: 0;
}

/* Check for Updates button */
.update-btn {
display: inline-flex;
align-items: center;
gap: 6px;
margin-top: 2px;
padding: 8px 18px;
border-radius: 8px;
background: var(--vera-primary);
border: none;
cursor: pointer;
color: #ffffff;
font-size: 13px;
font-weight: 500;
font-family: inherit;
-webkit-app-region: no-drag;
transition:
background 0.12s,
opacity 0.12s;
}
.update-btn:hover {
opacity: 0.88;
}
.update-btn:active {
opacity: 0.76;
}
.update-btn:disabled {
opacity: 0.5;
cursor: default;
}
.update-status {
font-size: 12px;
color: var(--vera-text-tertiary);
min-height: 16px;
}

.copyright {
font-size: 13px;
color: var(--vera-text-tertiary);
margin-top: 8px;
}
</style>
</head>
<body>
<div class="container">
<!-- Vonage V mark — actual brand paths from vonage-logo-desktop.svg -->
<div class="icon">
<svg viewBox="15 16 306 306" xmlns="http://www.w3.org/2000/svg">
<path
fill="#0A0203"
d="M101.8,74H58.7l61.5,139.7c0.5,1.1,2,1.1,2.4,0l20.4-48.1L101.8,74z"
/>
<path
fill="#0A0203"
d="M233,74c0,0-66,151.2-74.9,165.2c-10.3,16.2-17.1,22.4-29.7,24.4
c-0.1,0-0.2,0.1-0.2,0.2c0,0.1,0.1,0.2,0.2,0.2h39.5
c17.1,0,29.4-14.3,36.3-26.9C211.9,222.9,276.8,74,276.8,74H233z"
/>
</svg>
</div>

<div class="app-name">Vonage Video</div>
<div class="app-version" id="version">v–</div>
<div class="build-info" id="build">–</div>

<div class="divider"></div>

<div class="credits">Vonage Video React Reference Application</div>

<!-- GitHub link with official GitHub Mark icon -->
<button class="github-btn" id="github-btn" title="Open on GitHub">
<svg viewBox="0 0 98 96" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="currentColor"
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69
2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127
-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17
-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052
4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6
-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2
-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052
a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63
9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038
3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283
1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526
0 1.304.89 2.853 3.316 2.364C84.343 88.9 97.707 70.494 97.707 49.217
97.707 22 75.788 0 48.854 0z"
/>
</svg>
Vonage/vonage-video-react-app
</button>

<button class="update-btn" id="update-btn">Check for Updates…</button>
<div class="update-status" id="update-status"></div>

<div class="copyright" id="copyright">© 2026 Vonage</div>
</div>

<script>
const p = new URLSearchParams(window.location.search);
const appVersion = p.get('appVersion') || '–';
const sdkVersion = p.get('sdkVersion') || '–';
const electronVersion = p.get('electronVersion') || '–';

document.getElementById('version').textContent = 'v' + appVersion;
document.getElementById('build').textContent =
'SDK v' + sdkVersion + ' \u00b7 Electron v' + electronVersion;
document.getElementById('copyright').textContent =
'\u00a9 ' + new Date().getFullYear() + ' Vonage';

document.getElementById('github-btn').addEventListener('click', () => {
window.electron &&
window.electron.openExternal('https://github.com/Vonage/vonage-video-react-app');
});

// Keyboard shortcuts: Escape or Cmd/Ctrl+W to close
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' || ((e.metaKey || e.ctrlKey) && e.key === 'w')) {
window.close();
}
});

// Check for Updates
const updateBtn = document.getElementById('update-btn');
const updateStatus = document.getElementById('update-status');
updateBtn.addEventListener('click', async () => {
if (!window.electron || !window.electron.checkForUpdates) {
updateStatus.textContent = 'Updates not available in dev mode';
return;
}
updateBtn.disabled = true;
updateBtn.textContent = 'Checking…';
updateStatus.textContent = '';
try {
const result = await window.electron.checkForUpdates();
updateStatus.textContent = result;
} catch (err) {
updateStatus.textContent = 'Could not check for updates';
} finally {
updateBtn.disabled = false;
updateBtn.textContent = 'Check for Updates…';
}
});
</script>
</body>
</html>
Binary file added electron/assets/AppIcon.iconset/icon_128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/AppIcon.iconset/icon_16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/AppIcon.iconset/icon_16x16@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/AppIcon.iconset/icon_256x256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/AppIcon.iconset/icon_32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/AppIcon.iconset/icon_32x32@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/AppIcon.iconset/icon_512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/AppIcon.iconset/icon_64x64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/app-icon.icns
Binary file not shown.
Binary file added electron/assets/app-icon.ico
Binary file not shown.
Binary file added electron/assets/app-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/tray-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/tray-icon@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/tray-template.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added electron/assets/tray-template@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions electron/build-resources/de.lproj/InfoPlist.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* Deutsch — macOS Berechtigungsbeschreibungen */
"NSCameraUsageDescription" = "Vonage Video benötigt Zugriff auf die Kamera für Videoanrufe.";
"NSMicrophoneUsageDescription" = "Vonage Video benötigt Zugriff auf das Mikrofon für Audio in Anrufen.";
"NSScreenCaptureDescription" = "Vonage Video benötigt Zugriff auf die Bildschirmaufnahme für die Bildschirmübertragung.";
4 changes: 4 additions & 0 deletions electron/build-resources/en.lproj/InfoPlist.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* English — macOS permission usage descriptions */
"NSCameraUsageDescription" = "Vonage Video needs camera access for video calls.";
"NSMicrophoneUsageDescription" = "Vonage Video needs microphone access for audio in calls.";
"NSScreenCaptureDescription" = "Vonage Video needs screen recording access for screen sharing.";
4 changes: 4 additions & 0 deletions electron/build-resources/es.lproj/InfoPlist.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* Español — descripciones de uso para permisos de macOS */
"NSCameraUsageDescription" = "Vonage Video necesita acceso a la cámara para las videollamadas.";
"NSMicrophoneUsageDescription" = "Vonage Video necesita acceso al micrófono para el audio en las llamadas.";
"NSScreenCaptureDescription" = "Vonage Video necesita acceso a la grabación de pantalla para compartir pantalla.";
4 changes: 4 additions & 0 deletions electron/build-resources/es_MX.lproj/InfoPlist.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* Español (México) — descripciones de uso para permisos de macOS */
"NSCameraUsageDescription" = "Vonage Video necesita acceso a la cámara para las videollamadas.";
"NSMicrophoneUsageDescription" = "Vonage Video necesita acceso al micrófono para el audio en las llamadas.";
"NSScreenCaptureDescription" = "Vonage Video necesita acceso a la grabación de pantalla para compartir pantalla.";
4 changes: 4 additions & 0 deletions electron/build-resources/it.lproj/InfoPlist.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* Italiano — descrizioni di utilizzo per le autorizzazioni macOS */
"NSCameraUsageDescription" = "Vonage Video necessita dell'accesso alla fotocamera per le videochiamate.";
"NSMicrophoneUsageDescription" = "Vonage Video necessita dell'accesso al microfono per l'audio nelle chiamate.";
"NSScreenCaptureDescription" = "Vonage Video necessita dell'accesso alla registrazione dello schermo per la condivisione dello schermo.";
Loading
Loading