- 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>
25 lines
1.0 KiB
TypeScript
25 lines
1.0 KiB
TypeScript
// Small IndexedDB store for user files (custom sounds): localStorage is too small for audio.
|
|
const DB = 'mumh5';
|
|
const STORE = 'files';
|
|
|
|
function open(): Promise<IDBDatabase> {
|
|
return new Promise((resolve, reject) => {
|
|
const req = indexedDB.open(DB, 1);
|
|
req.onupgradeneeded = () => req.result.createObjectStore(STORE);
|
|
req.onsuccess = () => resolve(req.result);
|
|
req.onerror = () => reject(req.error);
|
|
});
|
|
}
|
|
|
|
function run<T>(mode: IDBTransactionMode, fn: (s: IDBObjectStore) => IDBRequest<T>): Promise<T> {
|
|
return open().then(db => new Promise<T>((resolve, reject) => {
|
|
const req = fn(db.transaction(STORE, mode).objectStore(STORE));
|
|
req.onsuccess = () => resolve(req.result);
|
|
req.onerror = () => reject(req.error);
|
|
}));
|
|
}
|
|
|
|
export const putBlob = (key: string, blob: Blob) => run('readwrite', s => s.put(blob, key));
|
|
export const getBlob = (key: string) => run<Blob | undefined>('readonly', s => s.get(key));
|
|
export const deleteBlob = (key: string) => run('readwrite', s => s.delete(key));
|