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,35 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
import Icon from './Icon.svelte';
|
||||
|
||||
let { title, onclose, children, footer, width = 460 }:
|
||||
{ title: string; onclose: () => void; children: Snippet; footer?: Snippet; width?: number } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={e => { if (e.key === 'Escape') onclose(); }} />
|
||||
|
||||
<div class="backdrop" role="presentation" onmousedown={e => { if (e.target === e.currentTarget) onclose(); }}>
|
||||
<div class="modal" role="dialog" aria-modal="true" aria-label={title} style:max-width="{width}px">
|
||||
<header>
|
||||
<h2>{title}</h2>
|
||||
<button class="icon-btn" onclick={onclose} aria-label="Close"><Icon name="x" /></button>
|
||||
</header>
|
||||
<div class="body">{@render children()}</div>
|
||||
{#if footer}<footer>{@render footer()}</footer>{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.backdrop {
|
||||
position: fixed; inset: 0; z-index: 100; background: rgb(0 0 0 / 0.6);
|
||||
display: flex; align-items: center; justify-content: center; padding: 16px;
|
||||
}
|
||||
.modal {
|
||||
width: 100%; max-height: calc(100vh - 32px); display: flex; flex-direction: column;
|
||||
background: var(--bg-1); border: 1px solid var(--line); box-shadow: 0 16px 48px rgb(0 0 0 / 0.5);
|
||||
}
|
||||
header { display: flex; align-items: center; justify-content: space-between; padding: 16px 16px 0 20px; }
|
||||
h2 { margin: 0; font-size: 18px; }
|
||||
.body { padding: 4px 20px 20px; overflow-y: auto; }
|
||||
footer { display: flex; justify-content: flex-end; gap: 8px; padding: 14px 20px; background: var(--bg-0); border-top: 1px solid var(--line); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user