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
+26
View File
@@ -0,0 +1,26 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createCodec, FrameReader } from '../src/core/proto.ts';
const codec = createCodec();
test('encode/decode roundtrip with framing split across chunks', () => {
const a = codec.encode('TextMessage', { channel_id: [0], message: 'hello <b>world</b>' });
const b = codec.encode('Ping', { timestamp: 1790796000000 });
const stream = new Uint8Array([...a, ...b]);
const reader = new FrameReader();
const out: any[] = [];
for (let i = 0; i < stream.length; i += 3) {
reader.push(stream.subarray(i, i + 3), (id, body) => out.push(codec.decode(id, body)));
}
assert.equal(out.length, 2);
assert.equal(out[0].name, 'TextMessage');
assert.equal(out[0].msg.message, 'hello <b>world</b>');
assert.deepEqual(out[0].msg.channel_id, [0]);
assert.equal(out[1].name, 'Ping');
assert.equal(out[1].msg.timestamp, 1790796000000);
});
test('encode rejects invalid payloads', () => {
assert.throws(() => codec.encode('ChannelRemove', { channel_id: 'nope' }));
});