<template>
<div class="session-list-wrap">
<div
v-if="sessionsStore.loading && !sessionsStore.sessions.length"
class="sessions-loading"
>
<span class="sessions-spinner"></span>
<p>Loading conversations...</p>
</div>
<div
v-else-if="!sessionsStore.sessions.length"
class="empty-sessions"
>
<i class="ph ph-chat-dots"></i>
<p>No conversations yet</p>
</div>
<DynamicScroller
v-else
class="session-scroller"
:items="sessionsStore.sessions"
:min-item-size="64"
key-field="session_id"
@scroll.passive="onScroll"
>
<template #default="{ item, index, active }">
<DynamicScrollerItem :item="item" :active="active" :data-index="index">
<SessionItem
:session="item"
:active="item.session_id === chatStore.currentId"
@select="onSelect(item)"
@delete="onDelete(item)"
@pin="onPin(item)"
/>
</DynamicScrollerItem>
</template>
</DynamicScroller>
<div
v-if="sessionsStore.loadingMore"
class="sessions-loading sessions-loading-more"
>
<span class="sessions-spinner"></span>
</div>
</div>
</template>
<script setup>
import { watch } from 'vue'
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import { useSessionsStore } from '@/stores/sessions.js'
import { useChatStore } from '@/stores/chat.js'
import { useProfilesStore } from '@/stores/profiles.js'
import SessionItem from './SessionItem.vue'
const emit = defineEmits(['select'])
const sessionsStore = useSessionsStore()
const chatStore = useChatStore()
const profilesStore = useProfilesStore()
watch(
() => profilesStore.selectedProfileId,
(profileId) => {
if (profileId && profileId !== sessionsStore.currentProfileId) {
sessionsStore.fetchSessions(profileId)
}
}
)
async function onSelect(session) {
await chatStore.loadSession(session.session_id)
emit('select', session)
}
async function onDelete(session) {
const wasActive = session.session_id === chatStore.currentId
await sessionsStore.deleteSession(session.session_id)
if (wasActive) {
chatStore.clearSession()
}
}
async function onPin(session) {
await sessionsStore.pinSession(session.session_id, !session.pinned)
}
function onScroll(event) {
const el = event.target
const distanceToBottom = el.scrollHeight - el.scrollTop - el.clientHeight
if (distanceToBottom < 160) {
sessionsStore.fetchMoreSessions()
}
}
</script>
<style scoped>
.session-list-wrap {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
}
.session-scroller {
flex: 1;
overflow-y: auto;
}
.empty-sessions {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 32px 16px;
color: var(--color-text-dark, #787c99);
font-size: 13px;
i { font-size: 32px; opacity: 0.4; }
p { margin: 0; }
}
.sessions-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 28px 16px;
color: var(--color-text-dark, #787c99);
font-size: 12px;
}
.sessions-loading-more {
padding: 10px 16px 14px;
}
.sessions-spinner {
width: 16px;
height: 16px;
border: 2px solid var(--color-border, #3b4261);
border-top-color: var(--color-accent, #7aa2f7);
border-radius: 50%;
animation: sessions-spin 0.8s linear infinite;
}
@keyframes sessions-spin {
to { transform: rotate(360deg); }
}
</style>