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 { history, saveHistory, deleteFromHistory } from "$lib/stores/page.svelte.js";
|
||||||
import { convertFileSrc } from "@tauri-apps/api/core";
|
import { convertFileSrc } from "@tauri-apps/api/core";
|
||||||
import Card from "$lib/components/Card.svelte";
|
import Card from "$lib/components/Card.svelte";
|
||||||
|
import EmptyState from "$lib/components/EmptyState.svelte";
|
||||||
import { formatTime, formatDuration } from "$lib/utils/time.js";
|
import { formatTime, formatDuration } from "$lib/utils/time.js";
|
||||||
import { PLAYBACK_SPEEDS, FEEDBACK_TIMEOUT_MS } from "$lib/utils/constants.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 searchQuery = $state("");
|
||||||
let expandedId = $state(null);
|
let expandedId = $state(null);
|
||||||
@@ -157,9 +159,7 @@
|
|||||||
<div class="px-7 pb-4">
|
<div class="px-7 pb-4">
|
||||||
<Card>
|
<Card>
|
||||||
<div class="flex items-center gap-3 px-4 py-2.5">
|
<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">
|
<Search size={16} class="text-text-tertiary flex-shrink-0" aria-hidden="true" />
|
||||||
<circle cx="11" cy="11" r="8" /><path d="M21 21l-4.35-4.35" />
|
|
||||||
</svg>
|
|
||||||
<input
|
<input
|
||||||
class="flex-1 bg-transparent text-text text-[13px] placeholder:text-text-tertiary focus:outline-none"
|
class="flex-1 bg-transparent text-text text-[13px] placeholder:text-text-tertiary focus:outline-none"
|
||||||
placeholder="Search all transcripts..."
|
placeholder="Search all transcripts..."
|
||||||
@@ -181,25 +181,17 @@
|
|||||||
<div class="flex-1 px-7 pb-4 min-h-0">
|
<div class="flex-1 px-7 pb-4 min-h-0">
|
||||||
<Card classes="h-full flex flex-col overflow-hidden">
|
<Card classes="h-full flex flex-col overflow-hidden">
|
||||||
{#if filtered.length === 0}
|
{#if filtered.length === 0}
|
||||||
<div class="flex-1 flex flex-col items-center justify-center gap-3">
|
<EmptyState
|
||||||
<div class="w-12 h-12 rounded-full bg-bg-elevated flex items-center justify-center">
|
icon={Clock}
|
||||||
<svg class="w-6 h-6 text-text-tertiary" viewBox="0 0 24 24" fill="currentColor">
|
message={searchQuery ? "No matching transcripts" : "Your transcriptions will be saved here"}
|
||||||
<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>
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex-1 overflow-y-auto">
|
<div class="flex-1 overflow-y-auto">
|
||||||
{#each filtered as item}
|
{#each filtered as item}
|
||||||
<!-- Compact row -->
|
<!-- Compact row -->
|
||||||
<div
|
<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)}
|
onclick={() => toggleExpand(item.id)}
|
||||||
onkeydown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); toggleExpand(item.id); } }}
|
onkeydown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); toggleExpand(item.id); } }}
|
||||||
role="button"
|
role="button"
|
||||||
@@ -215,18 +207,12 @@
|
|||||||
aria-label={playingId === item.id && audioEl && !audioEl.paused ? "Pause" : "Play"}
|
aria-label={playingId === item.id && audioEl && !audioEl.paused ? "Pause" : "Play"}
|
||||||
>
|
>
|
||||||
{#if playingId === item.id && audioEl && !audioEl.paused}
|
{#if playingId === item.id && audioEl && !audioEl.paused}
|
||||||
<svg class="w-2.5 h-2.5" viewBox="0 0 24 24" fill="currentColor">
|
<Pause size={10} aria-hidden="true" />
|
||||||
<rect x="6" y="4" width="4" height="16" rx="1" />
|
|
||||||
<rect x="14" y="4" width="4" height="16" rx="1" />
|
|
||||||
</svg>
|
|
||||||
{:else}
|
{:else}
|
||||||
<svg class="w-2.5 h-2.5 ml-0.5" viewBox="0 0 24 24" fill="currentColor">
|
<Play size={10} class="ml-0.5" aria-hidden="true" />
|
||||||
<path d="M8 5v14l11-7z" />
|
|
||||||
</svg>
|
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Spacer to keep alignment when no audio -->
|
|
||||||
<div class="w-6 h-6 flex-shrink-0"></div>
|
<div class="w-6 h-6 flex-shrink-0"></div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@@ -245,19 +231,9 @@
|
|||||||
<!-- Source icon -->
|
<!-- Source icon -->
|
||||||
<span class="flex-shrink-0 text-text-tertiary" title={item.source}>
|
<span class="flex-shrink-0 text-text-tertiary" title={item.source}>
|
||||||
{#if item.source && item.source.toLowerCase().includes("file")}
|
{#if item.source && item.source.toLowerCase().includes("file")}
|
||||||
<!-- File icon -->
|
<FileText size={14} aria-hidden="true" />
|
||||||
<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>
|
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Microphone icon -->
|
<Mic size={14} aria-hidden="true" />
|
||||||
<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>
|
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -267,17 +243,17 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- Expand chevron -->
|
<!-- Expand chevron -->
|
||||||
<svg
|
<ChevronDown
|
||||||
class="w-3.5 h-3.5 text-text-tertiary flex-shrink-0 transition-transform duration-150 {expandedId === item.id ? 'rotate-180' : ''}"
|
size={14}
|
||||||
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
class="text-text-tertiary flex-shrink-0 {expandedId === item.id ? 'rotate-180' : ''}"
|
||||||
>
|
style="transition: transform var(--duration-ui)"
|
||||||
<polyline points="6 9 12 15 18 9" />
|
aria-hidden="true"
|
||||||
</svg>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Expanded detail -->
|
<!-- Expanded detail -->
|
||||||
{#if expandedId === item.id}
|
{#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) -->
|
<!-- Audio player (if playing this item) -->
|
||||||
{#if item.audioPath && playingId === item.id}
|
{#if item.audioPath && playingId === item.id}
|
||||||
<div class="flex items-center gap-3 mb-4 px-1">
|
<div class="flex items-center gap-3 mb-4 px-1">
|
||||||
@@ -315,22 +291,26 @@
|
|||||||
<!-- Action buttons -->
|
<!-- Action buttons -->
|
||||||
<div class="flex items-center gap-2 flex-wrap">
|
<div class="flex items-center gap-2 flex-wrap">
|
||||||
<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(); renameItem(item); }}
|
onclick={(e) => { e.stopPropagation(); renameItem(item); }}
|
||||||
>Rename</button>
|
>Rename</button>
|
||||||
<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); }}
|
onclick={(e) => { e.stopPropagation(); copyItem(item); }}
|
||||||
>Copy</button>
|
>Copy</button>
|
||||||
{#if item.audioPath && item.segments && item.segments.length > 0}
|
{#if item.audioPath && item.segments && item.segments.length > 0}
|
||||||
<button
|
<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); }}
|
onclick={(e) => { e.stopPropagation(); openViewer(item); }}
|
||||||
>Open viewer</button>
|
>Open viewer</button>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
<button
|
<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); }}
|
onclick={(e) => { e.stopPropagation(); removeItem(item); }}
|
||||||
>Delete</button>
|
>Delete</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user