feat: Electron screen share picker, instance selector, and system audio loopback

- Custom screen share picker for Electron (ScreenSharePicker.tsx) with
  Aether Drift design: glass-bubble footer, adaptive grid, pill tabs,
  border-based selection (avoids overflow clipping), hover brightness
- Instance URL picker so Electron connects to any Backspace server
- System audio loopback on Windows/Linux via desktopCapturer callback
- macOS: video-only callback (OS blocks system audio capture)
- IPC bridge for screen source enumeration and selection
- Purge stale service worker caches on Electron launch
This commit is contained in:
Jannis Braun
2026-03-16 03:50:07 +01:00
parent 8ae3ffc912
commit 95ec3ff952
20 changed files with 1042 additions and 95 deletions
+53 -44
View File
@@ -5,7 +5,9 @@ import { RegisterPage } from './components/auth/RegisterPage';
import { AppLayout } from './components/layout/AppLayout';
import { JoinPage } from './components/JoinPage';
import { SwAutoUpdate } from './components/ui/SwUpdatePrompt';
import { ScreenSharePicker } from './components/voice/ScreenSharePicker';
import { useAuthStore } from './stores/authStore';
import { isElectronMac } from './platform/platform';
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const token = useAuthStore((s) => s.token);
@@ -27,50 +29,57 @@ function AuthRedirect({ children }: { children: React.ReactNode }) {
}
export function App() {
return (
<>
<SwAutoUpdate />
<Routes>
<Route
path="/login"
element={
<AuthRedirect>
<LoginPage />
</AuthRedirect>
}
/>
<Route
path="/register"
element={
<AuthRedirect>
<RegisterPage />
</AuthRedirect>
}
/>
<Route
path="/channels/:spaceId/:channelId?"
element={
<ProtectedRoute>
<AppLayout />
</ProtectedRoute>
}
/>
<Route
path="/join/:inviteCode"
element={<JoinPage />}
/>
<Route
path="/explore"
element={
<ProtectedRoute>
<AppLayout />
</ProtectedRoute>
}
/>
<Route path="/" element={<Navigate to="/channels/@me" replace />} />
<Route path="*" element={<Navigate to="/channels/@me" replace />} />
</Routes>
</>
const showTitleBar = isElectronMac();
return (
<div className={`flex flex-col ${showTitleBar ? 'h-screen' : 'contents'}`}>
{showTitleBar && (
<div className="h-8 flex-shrink-0 bg-surface-base titlebar-drag" />
)}
<div className={showTitleBar ? 'flex-1 min-h-0' : 'contents'}>
<SwAutoUpdate />
<ScreenSharePicker />
<Routes>
<Route
path="/login"
element={
<AuthRedirect>
<LoginPage />
</AuthRedirect>
}
/>
<Route
path="/register"
element={
<AuthRedirect>
<RegisterPage />
</AuthRedirect>
}
/>
<Route
path="/channels/:spaceId/:channelId?"
element={
<ProtectedRoute>
<AppLayout />
</ProtectedRoute>
}
/>
<Route
path="/join/:inviteCode"
element={<JoinPage />}
/>
<Route
path="/explore"
element={
<ProtectedRoute>
<AppLayout />
</ProtectedRoute>
}
/>
<Route path="/" element={<Navigate to="/channels/@me" replace />} />
<Route path="*" element={<Navigate to="/channels/@me" replace />} />
</Routes>
</div>
</div>
);
}