skillok meeting
v1.0
Перейти к файлу
Код
meeting srcstate.ts
anc Простой путь: записать → расшифровать → получить результат - Главная: «Записать» (или аудиофайл перетаскиванием) и записи со статусом. Боковая панель и «Новая заметка» убраны. - Экран записи: шаги «Аудио → Расшифровка → Результат», на экране только текущий шаг и одна главная кнопка. - Результаты вкладками: «Расшифровка», «Конспект», «Задачи», «Ревью», «Свой запрос»; копировать, как текст, править, сделать заново, удалить. Повтор команды заменяет раздел, а не дописывает второй. - Задача #9: без ключа кнопки ведут к подключению ИИ; у расшифровки и ответа ИИ — прогресс и «Отменить» (CancellationToken в ядре); идущая запись видна меткой в шапке на любом экране. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> e75bd24 30 сентября 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 {
    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);
  }
}