feat: Electron desktop app — hardening, IPC bridge, and dev launch fixes

- Dev/prod URL auto-detection (Vite 5173 in dev, server 3000 in prod)
- Typed IPC bridge via preload (notifications, badge, window controls, updates, deep links)
- Native OS notifications via NotificationController with window focus suppression
- Auto-update via electron-updater with UpdateToast UI
- Deep linking (backspace:// protocol) for macOS and Windows/Linux
- Window state persistence (position, size, maximize across restarts)
- Tray icon with graceful fallback when icon asset missing
- Suppress PWA service worker polling/reloads inside Electron
- Platform detection layer (isElectron, getElectronAPI)
- Root workspace scripts (dev:desktop, build:desktop)
- Document BACKSPACE_URL and BACKSPACE_UPDATE_URL env vars
This commit is contained in:
Jannis Braun
2026-03-16 00:10:47 +01:00
parent 56811b9333
commit 8ae3ffc912
17 changed files with 743 additions and 85 deletions
@@ -21,12 +21,15 @@ import { IncomingCallModal } from '../voice/IncomingCallModal';
import { PictureInPicture } from '../voice/PictureInPicture';
import { SoundController } from '../voice/SoundController';
import { GlobalAudioRenderer } from '../voice/GlobalAudioRenderer';
import { NotificationController } from '../NotificationController';
import { UserProfilePopout } from '../ui/UserProfilePopout';
import { ToastContainer } from '../ui/ToastContainer';
import { UpdateToast } from '../ui/UpdateToast';
import { useAuth } from '../../hooks/useAuth';
import { useWebSocket } from '../../hooks/useWebSocket';
import { useFederationToasts } from '../../hooks/useFederationToasts';
import { useLiveKit } from '../../hooks/useLiveKit';
import { useDeepLinkHandler } from '../../platform/deepLink';
import { useSpaceStore } from '../../stores/spaceStore';
import { useChatStore } from '../../stores/chatStore';
import { useUIStore } from '../../stores/uiStore';
@@ -115,6 +118,9 @@ export function AppLayout() {
// Federation toast notifications for remote instance connection state changes
useFederationToasts();
// Deep link handler for Electron (backspace:// protocol)
useDeepLinkHandler();
// Track the last channel we attempted to connect to, to prevent effect loops
const lastAttemptedRef = React.useRef<string | null>(null);
@@ -292,6 +298,8 @@ export function AppLayout() {
<PictureInPicture />
<SoundController />
<GlobalAudioRenderer />
<NotificationController />
<UpdateToast />
{/* User Profile Popout */}
{userProfilePopout.user && userProfilePopout.position && (