Initial commit: mumh5, a modern Mumble client
Electron desktop app with a Svelte 5 interface for any Mumble server. - Mumble protocol core: TLS, handshake, channels, users, text, plugin data, client-side pacing of Murmur's rate limits - Voice: WebCodecs Opus over the TCP tunnel, voice activity, push to talk, always-on, devices, per-user volume and local mute - Several servers at once, voice on one; server rail with icons and ordering - Chat: channels, direct messages, side chat, file sharing through f0ckm, inline images without it, click-to-play YouTube - Profiles with rich descriptions, registration, rename, nicknames, connection information and moderation menus - Identity wizard, multiple identities, PKCS#12 import/export, desktop Mumble certificate import, certificate pinning and viewer - Tray icon with voice state - Unit, server and end-to-end tests against a real Murmur Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
type Listener = (connId: string, ...args: any[]) => void;
|
||||
const listeners = { secure: new Set<Listener>(), data: new Set<Listener>(), close: new Set<Listener>() };
|
||||
for (const key of Object.keys(listeners) as (keyof typeof listeners)[]) {
|
||||
ipcRenderer.on(`mumble:${key}`, (_e, connId: string, ...args: any[]) => {
|
||||
for (const fn of listeners[key]) fn(connId, ...args);
|
||||
});
|
||||
}
|
||||
|
||||
// The only native surface the renderer gets
|
||||
contextBridge.exposeInMainWorld('mumh5Native', {
|
||||
platformInfo: () => ipcRenderer.invoke('platform:info'),
|
||||
updateTray: (state: unknown) => ipcRenderer.send('tray:update', state),
|
||||
onTrayAction: (fn: (action: string) => void) => { ipcRenderer.on('tray:action', (_e, a: string) => fn(a)); },
|
||||
describeCerts: (ders: Uint8Array[]) => ipcRenderer.invoke('certs:describe', ders),
|
||||
identities: {
|
||||
list: () => ipcRenderer.invoke('identities:list'),
|
||||
create: (name: string, email: string) => ipcRenderer.invoke('identities:create', name, email),
|
||||
importP12: (bytes: Uint8Array, password: string, name: string) => ipcRenderer.invoke('identities:import', bytes, password, name),
|
||||
findMumble: () => ipcRenderer.invoke('identities:findMumble'),
|
||||
importMumble: (name: string) => ipcRenderer.invoke('identities:importMumble', name),
|
||||
exportP12: (id: string, password: string) => ipcRenderer.invoke('identities:export', id, password),
|
||||
setDefault: (id: string) => ipcRenderer.invoke('identities:setDefault', id),
|
||||
rename: (id: string, name: string) => ipcRenderer.invoke('identities:rename', id, name),
|
||||
remove: (id: string) => ipcRenderer.invoke('identities:remove', id),
|
||||
finishSetup: () => ipcRenderer.invoke('identities:finishSetup')
|
||||
},
|
||||
open: (connId: string, host: string, port: number, identityId?: string) => ipcRenderer.invoke('mumble:open', connId, host, port, identityId),
|
||||
send: (connId: string, bytes: Uint8Array) => ipcRenderer.send('mumble:send', connId, bytes),
|
||||
close: (connId: string) => ipcRenderer.send('mumble:close', connId),
|
||||
on: (event: keyof typeof listeners, fn: Listener) => {
|
||||
listeners[event].add(fn);
|
||||
return () => listeners[event].delete(fn);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user