<script lang="ts">
import { onMount } from 'svelte';
import { sessionsStore } from '$lib/stores/sessions.svelte.js';
import { appStore } from '$lib/store.svelte.js';
import { deleteSession } from '$lib/api.js';
onMount(() => {
sessionsStore.load(appStore.authToken ?? undefined);
});
function formatTs(ts: string): string {
const d = new Date(ts);
const now = new Date();
const diffMs = now.getTime() - d.getTime();
const diffMin = Math.floor(diffMs / 60000);
if (diffMin < 1) return 'just now';
if (diffMin < 60) return `${diffMin}m ago`;
const diffH = Math.floor(diffMin / 60);
if (diffH < 24) return `${diffH}h ago`;
const diffD = Math.floor(diffH / 24);
if (diffD < 7) return `${diffD}d ago`;
return `${String(d.getMonth() + 1).padStart(2, '0')}/${String(d.getDate()).padStart(2, '0')}`;
}
function truncate(s: string | null, max: number): string {
if (!s) return 'New conversation';
return s.length > max ? s.slice(0, max) + '...' : s;
}
async function handleDelete(e: MouseEvent, id: string) {
e.stopPropagation();
await deleteSession(id, appStore.authToken ?? undefined);
await sessionsStore.load(appStore.authToken ?? undefined);
}
</script>
<div class="session-list">
{#if sessionsStore.loading}
<div class="empty">Loading...</div>
{:else if sessionsStore.sessions.length === 0}
<div class="empty">No previous sessions</div>
{:else}
{#each sessionsStore.sessions as session (session.id)}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="session-item"
class:selected={sessionsStore.selectedId === session.id}
onclick={() => sessionsStore.select(session.id, appStore.authToken ?? undefined)}
onkeydown={(e) => { if (e.key === 'Enter') sessionsStore.select(session.id, appStore.authToken ?? undefined); }}
tabindex="0"
role="option"
aria-selected={sessionsStore.selectedId === session.id}
>
<div class="session-row">
<span class="session-indicator">—</span>
<span class="session-preview">{truncate(session.task_preview, 50)}</span>
</div>
<div class="session-meta">
<span class="session-time">{formatTs(session.timestamp)}</span>
<button
class="session-del"
onclick={(e) => handleDelete(e, session.id)}
aria-label="Delete session"
>×</button>
</div>
</div>
{/each}
{/if}
</div>
<style>
.session-list {
padding: 0.25rem 0;
}
.empty {
padding: 1.5rem 1rem;
color: var(--fg-dim);
font-size: 0.875rem;
text-align: center;
}
.session-item {
display: flex;
flex-direction: column;
gap: 0.125rem;
padding: 0.625rem 1rem;
cursor: pointer;
border-left: 2px solid transparent;
transition: background 0.1s;
}
.session-item:hover {
background: var(--bg-elevated);
}
.session-item:hover .session-del {
opacity: 1;
}
.session-item.selected {
background: var(--bg-elevated);
border-left-color: var(--accent);
}
.session-item:focus-visible {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.session-row {
display: flex;
align-items: center;
gap: 0.5rem;
}
.session-indicator {
color: var(--fg-dim);
flex-shrink: 0;
}
.session-preview {
font-size: 0.875rem;
color: var(--fg);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.session-meta {
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 1.25rem;
}
.session-time {
font-size: 0.75rem;
color: var(--fg-dim);
}
.session-del {
all: unset;
cursor: pointer;
color: var(--fg-dim);
opacity: 0;
font-size: 0.75rem;
padding: 0.125rem 0.25rem;
border-radius: 3px;
transition: opacity 0.1s, color 0.1s;
}
.session-del:hover {
color: var(--red);
}
.session-del:focus-visible {
opacity: 1;
outline: 1px solid var(--red);
}
</style>