feat(voice): саундборд, звуковая палитра, push-to-talk и статистика соединения

Клиентская часть Фазы 3 (AGENT.md 7.13, 7.14): звуки сервера, локальные
звуки интерфейса и телеметрия медиа.

- саундборд: `api/sounds.ts` (лимиты 512 КБ и 30 на вид, multipart-загрузка,
  play, `SOUND_EVENTS`), `lib/soundboard.ts` (blob-кэш `/files/{id}`,
  наложение до 8 звуков, громкость = `outputVolume`, тишина при дефене),
  попап в панели комнаты (`USE_SOUNDBOARD`), таймер на `rate_limited`,
  подпись «включил звук» и защита от двойного воспроизведения;
- звуковая палитра: `SOUNDBOARD_PLAY` и события сервера (вход/выход
  участника, упоминание, сообщение, вход/выход из голосовой, свои
  микрофон/камера/экран, серверный мьют) → `playEventSound`;
- секция «Звуки» в настройках сервера (`MANAGE_SOUNDS`): два вида со
  счётчиками, drag&drop, имя, эмодзи, событие палитры, переименование,
  удаление и клиентские проверки файла;
- push-to-talk: режим и клавиша (`event.code`), хук в `AppLayout`, вход в
  комнату с выключенным микрофоном, удержание включает публикацию, отпускание
  и потеря фокуса выключают, серверный мьют приоритетнее;
- статистика: поповер с разрешением, fps, кодеком, битрейтом, RTT, потерями
  и jitter (опрос раз в секунду, остановка при закрытии), выбор качества
  публикации low/medium/high, пресеты шаринга экрана 1080p60…480p30;
- данные: `guilds[].sounds` из READY, `GUILD_SOUNDS_UPDATE`, селектор
  `useGuildSounds`, тумблеры звуков в «Аудио-видео» (localStorage);
- тесты: `soundboard`, `soundSettings`, `pushToTalk`, `voiceStats` и
  расширенный фейковый LiveKit SDK.
This commit is contained in:
2026-09-20 19:30:43 +03:00
parent 2538e07ec3
commit 619bd91df4
40 changed files with 4269 additions and 48 deletions
+133 -4
View File
@@ -70,7 +70,17 @@
},
"voice.disabled": "Voice is not configured on this instance.",
"voice.channel_full": "No free slots in this room.",
"voice.token_refresh_failed": "Could not renew the voice session. Please join again."
"voice.token_refresh_failed": "Could not renew the voice session. Please join again.",
"sound": {
"invalid_type": "MP3, OGG, WAV, WebM and M4A are supported.",
"invalid_name": "Sound name: 2–32 characters, latin letters, digits and «_».",
"name_taken": "A sound with this name already exists."
},
"sounds": {
"limit_reached": "The limit of sounds of this kind is reached.",
"event_required": "Choose an event for a palette sound."
},
"voice.not_connected": "Join a voice channel first."
},
"theme": {
"switchToLight": "Light theme",
@@ -268,7 +278,14 @@
},
"screen": {
"on": "Start screen sharing",
"off": "Stop screen sharing"
"off": "Stop screen sharing",
"quality": "Screen share quality",
"presets": {
"1080p60": "1080p · 60 fps",
"1080p30": "1080p · 30 fps",
"720p30": "720p · 30 fps",
"480p30": "480p · 30 fps"
}
},
"quality": {
"excellent": "Excellent connection",
@@ -289,6 +306,44 @@
},
"channelMenu": {
"label": "Voice room {{name}}"
},
"soundboard": {
"title": "Soundboard",
"open": "Open the soundboard",
"searchLabel": "Search sounds",
"searchPlaceholder": "Sound name",
"empty": "This server has no soundboard sounds yet.",
"noResults": "Nothing found.",
"playLabel": "Play the sound {{name}}",
"rateLimited": "Too fast: try again in {{seconds}} s.",
"notConnected": "Join a voice channel first.",
"playedBy": "{{name}} played «{{sound}}»"
},
"stats": {
"title": "Statistics",
"open": "Open connection statistics",
"resolution": "Resolution",
"frameRate": "Frame rate",
"codec": "Codec",
"bitrate": "Bitrate",
"kbps": "kbps",
"ms": "ms",
"rtt": "Round-trip time",
"packetLoss": "Packet loss",
"jitter": "Jitter"
},
"publishQuality": {
"label": "Video quality",
"short": {
"low": "Low",
"medium": "Medium",
"high": "High"
},
"level": {
"low": "Low quality",
"medium": "Medium quality",
"high": "High quality"
}
}
},
"friends": {
@@ -490,7 +545,8 @@
"memberJoin": "{{name}} joined the server",
"memberKick": "{{name}} left the server (kicked)",
"channelCreated": "Room #{{name}} created",
"voiceChannelCreated": "Voice room {{name}} created"
"voiceChannelCreated": "Voice room {{name}} created",
"memberLeave": "{{name}} left the server"
}
},
"user": {
@@ -761,6 +817,59 @@
"fileSelected": "{{name}} · {{size}}",
"upload": "Upload",
"uploading": "Uploading…"
},
"sounds": {
"title": "Sounds",
"description": "Server soundboard and sound palette: up to {{limit}} sounds of each kind, 512 KB each.",
"counter": "{{count}} of {{limit}}",
"empty": "No sounds of this kind yet.",
"uploadTitle": "Upload a sound",
"uploadHint": "Drop a file below or choose it: MP3, OGG, WAV, WebM or M4A up to {{limit}}.",
"name": "Sound name",
"namePlaceholder": "for example, airhorn",
"nameHint": "2–32 characters: latin letters, digits and «_».",
"emoji": "Emoji",
"emojiPlaceholder": "for example, 📣",
"emojiHint": "Optional icon for the soundboard grid.",
"event": "Event",
"dropHint": "Drop the file here",
"file": "Sound file",
"chooseFile": "Choose a file",
"fileSelected": "{{name}} · {{size}}",
"upload": "Upload",
"uploading": "Uploading…",
"rename": "Rename",
"renameLabel": "Rename the sound {{name}}",
"renameTitle": "Rename the sound",
"delete": "Delete",
"deleteLabel": "Delete the sound {{name}}",
"deleteConfirmTitle": "Delete the sound?",
"deleteConfirmBody": "The sound {{name}} will be deleted permanently.",
"deleteConfirm": "Delete",
"kinds": {
"soundboard": "Soundboard",
"ui": "Sound palette"
},
"events": {
"member_join": "Member joined",
"member_leave": "Member left",
"mention": "Mention",
"message": "New message",
"message_send": "Message sent",
"voice_join": "Joined a voice channel",
"voice_leave": "Left a voice channel",
"mic_on": "Microphone on",
"mic_off": "Microphone off",
"camera_on": "Camera on",
"camera_off": "Camera off",
"screen_on": "Screen share started",
"screen_off": "Screen share stopped",
"kick": "Member kicked",
"ban": "Member banned",
"mute": "Member muted",
"role_grant": "Role granted",
"role_revoke": "Role revoked"
}
}
},
"navLabel": "Settings sections",
@@ -777,7 +886,27 @@
"output": "Sound",
"micVolume": "Microphone volume",
"outputVolume": "Sound volume",
"stub": "Changes apply immediately and are stored on this device."
"stub": "Changes apply immediately and are stored on this device.",
"micMode": "Microphone activation",
"micModes": {
"voice": "Voice activity",
"push_to_talk": "Push-to-talk"
},
"pttKey": "Key",
"pttHint": "Hold the key to talk. Works while the window is focused and the cursor is not in a text field.",
"pttKeys": {
"space": "Space",
"v": "V",
"b": "B",
"n": "N",
"alt": "Alt",
"ctrl": "Ctrl"
},
"sounds": "Sounds",
"eventSounds": "Server event sounds",
"eventSoundsHint": "Play the server palette sounds: member joins, mentions and other events.",
"soundboard": "Soundboard",
"soundboardHint": "Play soundboard sounds in a voice channel."
},
"language": {
"title": "Language & region",