anc
Проверка звука до записи и предупреждение, если дорожка молчит
Встреча, которая молча не записалась, — худшее для программы: человек
узнаёт об этом после встречи.
- «Новая запись»: проверка звука — микрофон и звук компьютера открываются
без записи в файл (audio_check_start/stop), две живые полоски
«слышно / тишина». Устройство не открылось — причина и где разрешить
(на Mac — «Запись экрана и системного звука»).
- Во время записи: дорожка минуту тише −60 dBFS — событие silent,
сообщение и строка на панели записи; звук вернулся — heard, строка
уходит. На паузе тишина не считается; звук компьютера, который в
тишине не приходит вовсе, тоже ловится.
- Начало записи гасит проверку: устройства не заняты дважды.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
57e443b
1 октября 2026 г.
state.ts
// Общее состояние интерфейса: идёт ли запись, есть ли ключ, короткие сообщения.
import { api, errorText, files, type RecordingView } from "./api";
type Listener = () => void;
const listeners = new Set<Listener>();
export const state = {
recording: null as RecordingView | null,
};
/** Файл, выбранный или перетащенный в окно, — новая запись. */
export async function addAudio(path: string | null): Promise<void> {
if (!path) return;
if (!files.isAudio(path)) {
toast("Это не аудиофайл — подойдут mp3, m4a, wav, aiff, flac, ogg");
return;
}
toast("Читаю файл…", "ok");
try {
const m = await api.addAudio(path);
location.hash = `#/m/${encodeURIComponent(m.id)}`;
} catch (e) {
toast(e);
}
}
export async function startRecording(): Promise<void> {
try {
(await import("./recording")).resetSilence();
const m = await api.recorderStart();
await refreshRecording();
location.hash = `#/m/${encodeURIComponent(m.id)}`;
} catch (e) {
toast(e);
}
}
export function onChange(cb: Listener): () => void {
listeners.add(cb);
return () => listeners.delete(cb);
}
export function changed(): void {
listeners.forEach((cb) => cb());
}
export async function refreshRecording(): Promise<void> {
state.recording = await api.recorderStatus();
changed();
}
/** Сообщение внизу экрана: ошибка или «Скопировано». Исчезает само. */
export function toast(message: unknown, kind: "error" | "ok" = "error"): void {
const el = document.getElementById("toast");
if (!el) return;
el.textContent = kind === "error" ? errorText(message) : String(message);
el.className = `toast is-${kind}`;
el.hidden = false;
window.clearTimeout(Number(el.dataset.timer));
el.dataset.timer = String(window.setTimeout(() => (el.hidden = true), kind === "error" ? 8000 : 2000));
}
export async function copy(text: string, what = "Скопировано"): Promise<void> {
try {
await navigator.clipboard.writeText(text);
toast(what, "ok");
} catch (e) {
toast(e);
}
}