agent: history — restyle with brand tokens and empty state
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,10 @@
|
||||
import { history, saveHistory, deleteFromHistory } from "$lib/stores/page.svelte.js";
|
||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
||||
import Card from "$lib/components/Card.svelte";
|
||||
import EmptyState from "$lib/components/EmptyState.svelte";
|
||||
import { formatTime, formatDuration } from "$lib/utils/time.js";
|
||||
import { PLAYBACK_SPEEDS, FEEDBACK_TIMEOUT_MS } from "$lib/utils/constants.js";
|
||||
import { Search, Clock, Play, Pause, FileText, Mic, ChevronDown } from 'lucide-svelte';
|
||||
|
||||
let searchQuery = $state("");
|
||||
let expandedId = $state(null);
|
||||
@@ -157,9 +159,7 @@
|
||||
<div class="px-7 pb-4">
|
||||
<Card>
|
||||
<div class="flex items-center gap-3 px-4 py-2.5">
|
||||
<svg class="w-4 h-4 text-text-tertiary flex-shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8" /><path d="M21 21l-4.35-4.35" />
|
||||
</svg>
|
||||
<Search size={16} class="text-text-tertiary flex-shrink-0" aria-hidden="true" />
|
||||
<input
|
||||
class="flex-1 bg-transparent text-text text-[13px] placeholder:text-text-tertiary focus:outline-none"
|
||||
placeholder="Search all transcripts..."
|
||||
@@ -181,25 +181,17 @@
|
||||
<div class="flex-1 px-7 pb-4 min-h-0">
|
||||
<Card classes="h-full flex flex-col overflow-hidden">
|
||||
{#if filtered.length === 0}
|
||||
<div class="flex-1 flex flex-col items-center justify-center gap-3">
|
||||
<div class="w-12 h-12 rounded-full bg-bg-elevated flex items-center justify-center">
|
||||
<svg class="w-6 h-6 text-text-tertiary" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm0 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16Zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-[13px] text-text-tertiary">
|
||||
{searchQuery ? "No matching transcripts" : "No history yet"}
|
||||
</p>
|
||||
<p class="text-[11px] text-text-tertiary">
|
||||
{searchQuery ? "Try a different search" : "Transcripts auto-save after recording"}
|
||||
</p>
|
||||
</div>
|
||||
<EmptyState
|
||||
icon={Clock}
|
||||
message={searchQuery ? "No matching transcripts" : "Your transcriptions will be saved here"}
|
||||
/>
|
||||
{:else}
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
{#each filtered as item}
|
||||
<!-- Compact row -->
|
||||
<div
|
||||
class="flex items-center gap-3 px-4 py-3 border-b border-border-subtle hover:bg-hover cursor-pointer transition-colors duration-150"
|
||||
class="flex items-center gap-3 px-4 py-3 border-b border-border-subtle bg-bg-card hover:bg-hover cursor-pointer"
|
||||
style="transition-duration: var(--duration-ui)"
|
||||
onclick={() => toggleExpand(item.id)}
|
||||
onkeydown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); toggleExpand(item.id); } }}
|
||||
role="button"
|
||||
@@ -215,18 +207,12 @@
|
||||
aria-label={playingId === item.id && audioEl && !audioEl.paused ? "Pause" : "Play"}
|
||||
>
|
||||
{#if playingId === item.id && audioEl && !audioEl.paused}
|
||||
<svg class="w-2.5 h-2.5" viewBox="0 0 24 24" fill="currentColor">
|
||||
<rect x="6" y="4" width="4" height="16" rx="1" />
|
||||
<rect x="14" y="4" width="4" height="16" rx="1" />
|
||||
</svg>
|
||||
<Pause size={10} aria-hidden="true" />
|
||||
{:else}
|
||||
<svg class="w-2.5 h-2.5 ml-0.5" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
<Play size={10} class="ml-0.5" aria-hidden="true" />
|
||||
{/if}
|
||||
</button>
|
||||
{:else}
|
||||
<!-- Spacer to keep alignment when no audio -->
|
||||
<div class="w-6 h-6 flex-shrink-0"></div>
|
||||
{/if}
|
||||
|
||||
@@ -245,19 +231,9 @@
|
||||
<!-- Source icon -->
|
||||
<span class="flex-shrink-0 text-text-tertiary" title={item.source}>
|
||||
{#if item.source && item.source.toLowerCase().includes("file")}
|
||||
<!-- File icon -->
|
||||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
</svg>
|
||||
<FileText size={14} aria-hidden="true" />
|
||||
{:else}
|
||||
<!-- Microphone icon -->
|
||||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
|
||||
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
||||
<line x1="12" y1="19" x2="12" y2="23" />
|
||||
<line x1="8" y1="23" x2="16" y2="23" />
|
||||
</svg>
|
||||
<Mic size={14} aria-hidden="true" />
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
@@ -267,17 +243,17 @@
|
||||
</span>
|
||||
|
||||
<!-- Expand chevron -->
|
||||
<svg
|
||||
class="w-3.5 h-3.5 text-text-tertiary flex-shrink-0 transition-transform duration-150 {expandedId === item.id ? 'rotate-180' : ''}"
|
||||
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
<ChevronDown
|
||||
size={14}
|
||||
class="text-text-tertiary flex-shrink-0 {expandedId === item.id ? 'rotate-180' : ''}"
|
||||
style="transition: transform var(--duration-ui)"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Expanded detail -->
|
||||
{#if expandedId === item.id}
|
||||
<div class="px-4 py-4 bg-bg-elevated border-b border-border animate-slide-up">
|
||||
<div class="px-4 py-4 bg-bg-elevated border-b border-border-subtle animate-slide-up">
|
||||
<!-- Audio player (if playing this item) -->
|
||||
{#if item.audioPath && playingId === item.id}
|
||||
<div class="flex items-center gap-3 mb-4 px-1">
|
||||
@@ -315,22 +291,26 @@
|
||||
<!-- Action buttons -->
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<button
|
||||
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text transition-colors"
|
||||
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text"
|
||||
style="transition-duration: var(--duration-ui)"
|
||||
onclick={(e) => { e.stopPropagation(); renameItem(item); }}
|
||||
>Rename</button>
|
||||
<button
|
||||
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text transition-colors"
|
||||
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text"
|
||||
style="transition-duration: var(--duration-ui)"
|
||||
onclick={(e) => { e.stopPropagation(); copyItem(item); }}
|
||||
>Copy</button>
|
||||
{#if item.audioPath && item.segments && item.segments.length > 0}
|
||||
<button
|
||||
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-accent hover:text-accent-hover transition-colors"
|
||||
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-accent hover:text-accent-hover"
|
||||
style="transition-duration: var(--duration-ui)"
|
||||
onclick={(e) => { e.stopPropagation(); openViewer(item); }}
|
||||
>Open viewer</button>
|
||||
{/if}
|
||||
<div class="flex-1"></div>
|
||||
<button
|
||||
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-danger transition-colors"
|
||||
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-danger"
|
||||
style="transition-duration: var(--duration-ui)"
|
||||
onclick={(e) => { e.stopPropagation(); removeItem(item); }}
|
||||
>Delete</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user