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:
2026-09-30 23:28:43 +02:00
co-authored by Claude Opus 5.5
commit 6d29ee1485
85 changed files with 22997 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
:root {
--bg-0: #0d0e10;
--bg-1: #151619;
--bg-2: #1b1c20;
--bg-3: #24262b;
--bg-hover: #2a2c32;
--bg-active: #33363d;
--line: #2c2e34;
--text: #e3e4e8;
--text-dim: #9a9ca5;
--text-faint: #686a73;
--accent: #7c6cff;
--accent-hover: #8f81ff;
--accent-text: #ffffff;
--speaking: #3bd16f;
--danger: #f0504a;
--warn: #e8b23c;
--link: #6fb3ff;
--font: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, sans-serif;
--mono: ui-monospace, "Cascadia Code", "JetBrains Mono", Menlo, Consolas, monospace;
--fs: 15px;
color-scheme: dark;
}
*, *::before, *::after { box-sizing: border-box; border-radius: 0 !important; }
html, body, #app { height: 100%; margin: 0; }
body {
background: var(--bg-2);
color: var(--text);
font: var(--fs)/1.45 var(--font);
overflow: hidden;
-webkit-font-smoothing: antialiased;
}
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
button:disabled { cursor: default; opacity: 0.5; }
input, textarea, select {
font: inherit;
color: var(--text);
background: var(--bg-0);
border: 1px solid var(--line);
padding: 8px 10px;
outline: none;
width: 100%;
}
input:focus, textarea:focus, select:focus { border-color: var(--accent); }
label { display: block; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); margin: 14px 0 6px; }
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
padding: 8px 16px; font-weight: 600; background: var(--bg-3); border: 1px solid var(--line);
}
.btn:hover:not(:disabled) { background: var(--bg-hover); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
.btn.primary:hover:not(:disabled) { background: var(--accent-hover); }
.btn.danger { background: transparent; border-color: var(--danger); color: var(--danger); }
.icon-btn {
display: inline-flex; align-items: center; justify-content: center;
width: 32px; height: 32px; color: var(--text-dim);
}
.icon-btn:hover { background: var(--bg-hover); color: var(--text); }
.icon-btn.on { color: var(--danger); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--bg-3); border: 2px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background-color: var(--bg-active); }
::-webkit-scrollbar-track { background: transparent; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }