feat(desktop): port v0.11.4-v0.15.2 from Tauri to Electron + Discord-parity audio (v0.16.0)

Chronological port of every Tauri release commit (v0.11.4 -> v0.15.2)
into the Electron rebuild, plus Discord-style audio handling that goes
beyond the Tauri original.

Highlights:
  - All 17 Tauri release commits ported (audio fixes, custom notification
    sound, Discord-style chat UX, profile banner, changelog page,
    Discord-parity call UX, screen-share echo + re-watch UX, audio-loop
    fix).
  - Native napi-rs audio-loopback addon with WASAPI process-loopback:
      * EXCLUDE_TARGET_PROCESS_TREE for full-screen shares -> peers
        never hear themselves echoed back through the capture.
      * INCLUDE_TARGET_PROCESS_TREE for window shares -> only the
        picked window's audio is captured, not the whole OS mixer
        (Discord parity).
      * HWND -> PID resolution via Win32 GetWindowThreadProcessId.
  - Discord-style screen-source picker (thumbnail grid, screens vs
    apps tabs, live-refreshing thumbnails).
  - Hash routing fix for packaged builds (file:// can't resolve
    BrowserRouter paths).
  - Tauri sources removed (apps/desktop/src-tauri).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
byGalax
2026-05-06 23:35:01 +02:00
parent 72d02e385f
commit 825160ee46
504 changed files with 14217 additions and 14366 deletions
+16 -32
View File
@@ -1,29 +1,17 @@
import {
isPermissionGranted,
requestPermission,
sendNotification,
} from '@tauri-apps/plugin-notification';
// OS notifications via the Electron preload bridge. The exported API
// stays identical to the Tauri-era version so callers don't change.
//
// Permission under Electron is implicit ('granted' always). The legacy
// ASKED marker in localStorage is kept so we don't re-prompt after a
// previously-denied run; we just fast-succeed now.
import { isTauriRuntime } from './globalShortcut';
// Tracks whether permission has already been requested this session so we
// don't spam the OS prompt. Actual permission state lives in the OS, but we
// also persist a "we've asked" marker in localStorage so reloads don't
// re-request (OS would block anyway after denial, but calling it every reload
// triggers noisy plugin warnings on some platforms).
let permissionChecked = false;
let permissionGranted = false;
const ASKED_KEY = 'chatapp.notif.asked';
function readAskedMarker(): boolean {
try {
return window.localStorage.getItem(ASKED_KEY) === '1';
} catch {
return false;
}
}
function writeAskedMarker(): void {
try {
window.localStorage.setItem(ASKED_KEY, '1');
@@ -36,21 +24,13 @@ export async function ensureNotificationPermission(): Promise<boolean> {
if (permissionChecked) return permissionGranted;
permissionChecked = true;
if (!isTauriRuntime()) {
// Web preview / Chrome — Tauri notification plugin not available.
permissionGranted = false;
return false;
}
try {
let granted = await isPermissionGranted();
if (!granted && !readAskedMarker()) {
// First-install: prompt the user once. After this we remember via the
// marker and never re-prompt — the user can re-enable later via OS
// system settings if they change their mind.
const result = await requestPermission();
granted = result === 'granted';
writeAskedMarker();
}
permissionGranted = granted;
const state = await window.electronAPI.getNotificationPermission();
permissionGranted = state === 'granted';
writeAskedMarker();
} catch (err: unknown) {
permissionGranted = false;
console.warn('notification permission check failed', err);
@@ -65,7 +45,7 @@ export function isAppFocused(): boolean {
interface NotifyOpts {
title: string;
body?: string;
// Force notification even when app is focused. Default: suppress if focused.
/** Force notification even when app is focused. Default: suppress if focused. */
force?: boolean;
}
@@ -75,8 +55,12 @@ export async function notify({ title, body, force = false }: NotifyOpts): Promis
const granted = await ensureNotificationPermission();
if (!granted) return;
try {
sendNotification({ title, ...(body ? { body } : {}) });
await window.electronAPI.notify({
title,
body: body ?? '',
silent: true,
});
} catch (err: unknown) {
console.error('sendNotification failed', err);
console.error('notify failed', err);
}
}