feat(ui): recording player + live per-item sync progress bars

This commit is contained in:
iamdoubz committed 2026-07-06 17:55:05 -05:00
1 parent 3b0f27f5d9
commit d08d03077c
1 file changed
+55
+55
View File
@@ -1,6 +1,7 @@
<script lang="ts">
// Right pane: LLM summary, action items, participants (Phase 5/6, FR-LLM-*/FR-CAL-*).
import { onMount } from "svelte";
import { convertFileSrc } from "@tauri-apps/api/core";
import { meetings } from "../stores/meetings.svelte";
import { calendar } from "../stores/calendar.svelte";
import { settings } from "../stores/settings.svelte";
@@ -26,6 +27,26 @@
onMount(() => calendar.load());
// ---- Recording playback (FR-REC-4) ----
let audioSrc = $state<string | null>(null);
let audioError = $state<string | null>(null);
$effect(() => {
const m = meetings.selected;
audioSrc = null;
audioError = null;
if (m?.recorded) {
const id = m.id;
api
.recordingPlaybackPath(id)
.then((p) => {
if (meetings.selected?.id === id) audioSrc = convertFileSrc(p);
})
.catch((e) => {
if (meetings.selected?.id === id) audioError = errorMessage(e);
});
}
});
// ---- Summary + action items (T5.4/T5.5/T5.6, FR-LLM-2/3/4) ----
let llmStatus = $state<LlmStatus | null>(null);
onMount(async () => {
@@ -233,6 +254,18 @@
</script>
<div class="wrap">
{#if meetings.selected?.recorded}
<h3><Mic2 size={14} aria-hidden="true" /> Recording</h3>
{#if audioSrc}
<!-- svelte-ignore a11y_media_has_caption -- a meeting recording has no caption track -->
<audio class="player" controls src={audioSrc}></audio>
{:else if audioError}
<p class="muted">{audioError}</p>
{:else}
<p class="muted">Loading recording…</p>
{/if}
{/if}
{#if meetings.selected && settings.settings.sync_enabled}
<h3><UploadCloud size={14} aria-hidden="true" /> Sync</h3>
<button
@@ -252,6 +285,7 @@
<span class="artifact">{job.artifact}</span>
<span class="job-status {job.status}">{job.status}</span>
{#if job.status === "uploading" && job.bytesTotal}
<progress class="job-bar" max={job.bytesTotal} value={job.bytesSent}></progress>
<span class="muted">{Math.round((100 * job.bytesSent) / job.bytesTotal)}%</span>
{/if}
{#if job.status === "failed"}
@@ -683,6 +717,27 @@
.artifact {
min-width: 5rem;
}
.job-bar {
flex: 1;
height: 0.4rem;
min-width: 3rem;
border: none;
border-radius: var(--radius-full);
overflow: hidden;
accent-color: var(--accent, #2563eb);
}
.job-bar::-webkit-progress-bar {
background: var(--bg-hover);
border-radius: var(--radius-full);
}
.job-bar::-webkit-progress-value {
background: var(--accent, #2563eb);
border-radius: var(--radius-full);
}
.player {
width: 100%;
margin: 0.2rem 0 0.6rem;
}
.job-status {
text-transform: capitalize;
color: var(--muted);