Files
mumh5/src/ui/Modal.svelte
T
kibiandClaude Opus 5.5 6d29ee1485 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>
2026-09-30 23:28:43 +02:00

36 lines
1.5 KiB
Svelte

<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>