This commit is contained in:
2026-03-30 11:04:04 +02:00
commit db9201e2ed
38 changed files with 1191 additions and 0 deletions

145
frontend/main.ts Normal file
View File

@@ -0,0 +1,145 @@
import { invoke } from "@tauri-apps/api/core";
const startAudioBtn = document.querySelector<HTMLButtonElement>("#start-audio-btn");
const stopAudioBtn = document.querySelector<HTMLButtonElement>("#stop-audio-btn");
const audioStatus = document.querySelector<HTMLElement>("#audio-status");
const startVideoBtn = document.querySelector<HTMLButtonElement>("#start-video-btn");
const stopVideoBtn = document.querySelector<HTMLButtonElement>("#stop-video-btn");
const videoStatus = document.querySelector<HTMLElement>("#video-status");
const videoPreview = document.querySelector<HTMLImageElement>("#video-preview");
if (
startAudioBtn === null ||
stopAudioBtn === null ||
audioStatus === null ||
startVideoBtn === null ||
stopVideoBtn === null ||
videoStatus === null ||
videoPreview === null
) {
throw new Error("missing UI elements");
}
let previewTimer: number | null = null;
let previewRequestInFlight = false;
function setAudioStatus(message: string): void {
if (audioStatus)
audioStatus.textContent = message;
}
function setAudioButtons(isRecording: boolean): void {
if (startAudioBtn)
startAudioBtn.disabled = isRecording;
if (stopAudioBtn)
stopAudioBtn.disabled = !isRecording;
}
function setVideoStatus(message: string): void {
if (videoStatus)
videoStatus.textContent = message;
}
function setVideoButtons(isRecording: boolean): void {
if (startVideoBtn)
startVideoBtn.disabled = isRecording;
if (stopVideoBtn)
stopVideoBtn.disabled = !isRecording;
}
async function refreshPreviewFrame(): Promise<void> {
if (previewRequestInFlight) {
return;
}
previewRequestInFlight = true;
try {
const encoded = await invoke<string | null>("get_video_preview_frame_base64");
if (encoded !== null && encoded.length > 0 && videoPreview) {
videoPreview.src = `data:image/jpeg;base64,${encoded}`;
}
} catch (error) {
console.error("preview refresh failed", error);
} finally {
previewRequestInFlight = false;
}
}
function startPreviewPolling(): void {
if (previewTimer !== null) {
return;
}
previewTimer = window.setInterval(() => {
void refreshPreviewFrame();
}, 200);
}
function stopPreviewPolling(): void {
if (previewTimer !== null) {
window.clearInterval(previewTimer);
previewTimer = null;
}
previewRequestInFlight = false;
if (videoPreview)
videoPreview.removeAttribute("src");
}
startAudioBtn.addEventListener("click", async () => {
startAudioBtn.disabled = true;
try {
const path = await invoke<string>("start_audio_recording");
setAudioStatus(`Audio recording started.\nOutput: ${path}`);
setAudioButtons(true);
} catch (error) {
setAudioStatus(`Start audio failed.\n${String(error)}`);
setAudioButtons(false);
}
});
stopAudioBtn.addEventListener("click", async () => {
stopAudioBtn.disabled = true;
try {
const path = await invoke<string>("stop_audio_recording");
setAudioStatus(`Audio recording stopped.\nSaved file: ${path}`);
setAudioButtons(false);
} catch (error) {
setAudioStatus(`Stop audio failed.\n${String(error)}`);
setAudioButtons(true);
}
});
startVideoBtn.addEventListener("click", async () => {
startVideoBtn.disabled = true;
try {
const path = await invoke<string>("start_video_recording");
setVideoStatus(`Video recording started.\nOutput: ${path}`);
setVideoButtons(true);
startPreviewPolling();
} catch (error) {
setVideoStatus(`Start video failed.\n${String(error)}`);
setVideoButtons(false);
stopPreviewPolling();
}
});
stopVideoBtn.addEventListener("click", async () => {
stopVideoBtn.disabled = true;
try {
const path = await invoke<string>("stop_video_recording");
setVideoStatus(`Video recording stopped.\nSaved file: ${path}`);
setVideoButtons(false);
stopPreviewPolling();
} catch (error) {
setVideoStatus(`Stop video failed.\n${String(error)}`);
setVideoButtons(true);
}
});