- Channels: create, edit, delete, link and move (drag and drop for channels and people), right-click on free space in the channel list - Permission editor with inherited rules, allow/deny per permission, groups and members; menus grey out actions the server does not allow - Notification sounds for connection, channel, chat, mute and push-to-talk events; custom files and TeamSpeak 3 style sound packs (settings.ini), stored in IndexedDB - Color schemes: Dark, Light, Midnight, Frost and Windows 95 - Public server list with live UDP ping, fetched through Chromium's network stack - Kick, ban and connection-lost card with Reconnect - Right-click menus for text fields, links, images and message authors - Formatting toolbar in the message box, shared with the description editor - Resizable channel list, menu headers, registration from the self menu only - Fix: client-side pacing now mirrors Murmur's whole-second leaky bucket exactly, so quick actions are no longer dropped silently Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
35 lines
1.1 KiB
TypeScript
35 lines
1.1 KiB
TypeScript
export interface MenuItem {
|
|
label: string;
|
|
action?: () => void;
|
|
danger?: boolean;
|
|
checked?: boolean;
|
|
disabled?: boolean;
|
|
separator?: boolean;
|
|
// Non-interactive title at the top (e.g. the server's name)
|
|
header?: boolean;
|
|
// Inline slider instead of a clickable item; the menu stays open while dragging
|
|
slider?: { value: number; min: number; max: number; step: number; format: (v: number) => string; oninput: (v: number) => void };
|
|
}
|
|
|
|
// One context menu for the whole app, opened at the pointer position
|
|
class MenuStore {
|
|
// keepFocus: leave focus (and the text selection) where it is, for text editing menus
|
|
open = $state<{ x: number; y: number; items: MenuItem[]; keepFocus?: boolean } | null>(null);
|
|
|
|
show(e: MouseEvent, items: MenuItem[]): void {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.open = { x: e.clientX, y: e.clientY, items };
|
|
}
|
|
|
|
showAt(x: number, y: number, items: MenuItem[], keepFocus = false): void {
|
|
this.open = { x, y, items, keepFocus };
|
|
}
|
|
|
|
close(): void {
|
|
this.open = null;
|
|
}
|
|
}
|
|
|
|
export const menu = new MenuStore();
|