anc
Интерфейс как у MacWhisper в стиле Skillok, только светлая тема
- Слева библиотека записей: «Записать», «Добавить аудиофайл», список,
«Настройки» внизу; пока идёт запись — метка с таймером вместо кнопки.
- Экран записи: сверху название и «Копировать», расшифровка по репликам
(время — кто — текст), под ней полоса «Дальше» с Конспектом, Задачами,
Ревью и своим запросом. Шаги-«лесенка» убраны.
- Ход расшифровки — крупный процент и полоса.
- Тёмная тема убрана, окно Tauri всегда светлое.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
43a49bd
30 сентября 2026 г.
home.ts
// Главная: записать встречу или добавить файл. Список записей — в боковой панели.
import { api, files, type SettingsView } from "../api";
import { bindRecording, recordingPanel } from "../recording";
import type { Scope } from "../scope";
import { addAudio, onChange, startRecording, state } from "../state";
function hero(view: SettingsView): string {
if (state.recording) {
return `<section class="hero is-live">${recordingPanel(view.settings.warning_text)}</section>`;
}
const pick = `<button class="link-btn" data-pick>выберите файл</button>`;
if (!view.capture_supported) {
return `
<section class="hero drop">
<h1>Добавьте запись встречи</h1>
<p class="hero-text">На этой системе запись не поддерживается. Перетащите сюда аудиофайл — mp3, m4a, wav и другие.</p>
<button class="ui-btn hero-btn" data-pick>Выбрать файл</button>
</section>`;
}
return `
<section class="hero">
<h1>Запишите встречу</h1>
<p class="hero-text">Zoom, Google Meet, Телемост — что угодно. Запишутся собеседники и вы, потом — расшифровка, конспект и задачи.</p>
<button class="ui-btn hero-btn" data-record>Записать</button>
<p class="small muted">Лучше в наушниках — так голоса собеседников не попадут в микрофон второй раз.</p>
</section>
<section class="hero drop">
<p>Уже есть запись? Перетащите аудиофайл сюда или ${pick}.</p>
<p class="small muted">mp3, m4a, wav, aiff, flac, ogg</p>
</section>`;
}
export async function renderHome(root: HTMLElement, scope: Scope): Promise<void> {
const view = await api.getSettings();
if (!scope.alive) return;
const draw = () => {
root.innerHTML = `
<div class="home">
${hero(view)}
${
view.openrouter_key
? ""
: `<p class="notice">Чтобы расшифровывать записи, подключите ИИ — <a href="#/settings">это минута</a>.</p>`
}
</div>`;
root.querySelector("[data-record]")?.addEventListener("click", startRecording);
root.querySelectorAll("[data-pick]").forEach((b) => b.addEventListener("click", async () => addAudio(await files.pickAudio())));
// После «Остановить» переход к записи делает подписка ниже.
bindRecording(root, scope, view.settings.warning_text, () => undefined);
};
draw();
// Запись остановили или поставили на паузу — карточка меняется.
let last = JSON.stringify(state.recording);
const off = onChange(async () => {
const now = JSON.stringify(state.recording);
if (now === last || !scope.alive) return;
const stoppedId = last && !state.recording ? (JSON.parse(last) as { meeting_id: string }).meeting_id : null;
last = now;
if (stoppedId) {
location.hash = `#/m/${encodeURIComponent(stoppedId)}`;
return;
}
draw();
});
scope.on(Promise.resolve(off));
}