- SessionSidebar component listing past sessions sorted by recency - Session title preview and relative date display - Click to switch sessions, delete with confirmation - Added deleteSession method to session store - Integrated sidebar into chat page layout Closes #12 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
82 lines
2.6 KiB
Svelte
82 lines
2.6 KiB
Svelte
<script lang="ts">
|
|
import { sessionStore } from '$lib/stores/sessions.svelte';
|
|
|
|
let {
|
|
onSelectSession,
|
|
onNewChat
|
|
}: { onSelectSession: (id: string) => void; onNewChat: () => void } = $props();
|
|
|
|
let confirmDeleteId: string | null = $state(null);
|
|
|
|
const sessions = $derived(sessionStore.getAllSessions());
|
|
const activeId = $derived(sessionStore.activeSessionId);
|
|
|
|
function handleDelete(e: MouseEvent, id: string) {
|
|
e.stopPropagation();
|
|
if (confirmDeleteId === id) {
|
|
sessionStore.deleteSession(id);
|
|
confirmDeleteId = null;
|
|
if (sessionStore.activeSessionId) {
|
|
onSelectSession(sessionStore.activeSessionId);
|
|
}
|
|
} else {
|
|
confirmDeleteId = id;
|
|
}
|
|
}
|
|
|
|
function formatDate(date: Date): string {
|
|
const now = new Date();
|
|
const diff = now.getTime() - date.getTime();
|
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
if (days === 0) return 'Today';
|
|
if (days === 1) return 'Yesterday';
|
|
if (days < 7) return `${days} days ago`;
|
|
return date.toLocaleDateString();
|
|
}
|
|
</script>
|
|
|
|
<aside class="flex h-full w-64 flex-col border-r border-gray-200 bg-gray-50">
|
|
<div class="border-b border-gray-200 p-3">
|
|
<button
|
|
type="button"
|
|
onclick={onNewChat}
|
|
class="w-full rounded-lg bg-blue-600 px-3 py-2 text-sm font-medium text-white hover:bg-blue-700"
|
|
>
|
|
+ New Chat
|
|
</button>
|
|
</div>
|
|
|
|
<div class="flex-1 overflow-y-auto">
|
|
{#if sessions.length === 0}
|
|
<p class="p-4 text-center text-sm text-gray-400">No sessions yet</p>
|
|
{:else}
|
|
{#each sessions as session (session.id)}
|
|
<div
|
|
role="button"
|
|
tabindex="0"
|
|
onclick={() => onSelectSession(session.id)}
|
|
onkeydown={(e) => { if (e.key === 'Enter') onSelectSession(session.id); }}
|
|
class="group flex w-full cursor-pointer items-start gap-2 border-b border-gray-100 px-3 py-3 text-left hover:bg-gray-100
|
|
{activeId === session.id ? 'bg-blue-50 border-l-2 border-l-blue-500' : ''}"
|
|
>
|
|
<div class="min-w-0 flex-1">
|
|
<p class="truncate text-sm font-medium text-gray-900">{session.title}</p>
|
|
<p class="mt-0.5 text-xs text-gray-500">{formatDate(session.createdAt)}</p>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onclick={(e) => handleDelete(e, session.id)}
|
|
class="shrink-0 rounded p-1 text-xs opacity-0 group-hover:opacity-100
|
|
{confirmDeleteId === session.id
|
|
? 'bg-red-100 text-red-600'
|
|
: 'text-gray-400 hover:bg-gray-200 hover:text-gray-600'}"
|
|
title={confirmDeleteId === session.id ? 'Click again to confirm' : 'Delete session'}
|
|
>
|
|
{confirmDeleteId === session.id ? '✓' : '✕'}
|
|
</button>
|
|
</div>
|
|
{/each}
|
|
{/if}
|
|
</div>
|
|
</aside>
|