Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
493 changes: 359 additions & 134 deletions src/renderer/src/components/WindowSideBar.vue

Large diffs are not rendered by default.

444 changes: 407 additions & 37 deletions src/renderer/src/components/WindowSideBarSessionItem.vue

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/renderer/src/i18n/da-DK/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "Gruppér efter dato",
"groupByProject": "Gruppér efter projekt",
"pinned": "Fastgjorte",
"emptyTitle": "Ingen samtaler endnu",
"emptyDescription": "Start en ny samtale for at komme i gang",
"remoteControlDisabled": "Deaktiveret"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/en-US/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
},
"groupByDate": "Group by date",
"groupByProject": "Group by project",
"pinned": "Pinned",
"emptyTitle": "No conversations yet",
"emptyDescription": "Start a new chat to begin"
}
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/fa-IR/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "گروه‌بندی بر اساس تاریخ",
"groupByProject": "گروه‌بندی بر اساس پروژه",
"pinned": "سنجاق‌شده‌ها",
"emptyTitle": "هنوز گفت‌وگویی وجود ندارد",
"emptyDescription": "برای شروع، یک گفت‌وگوی جدید آغاز کنید",
"remoteControlDisabled": "غیرفعال"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/fr-FR/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "Grouper par date",
"groupByProject": "Grouper par projet",
"pinned": "Épinglés",
"emptyTitle": "Aucune conversation pour le moment",
"emptyDescription": "Commencez une nouvelle discussion pour démarrer",
"remoteControlDisabled": "Désactivé"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/he-IL/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "קבץ לפי תאריך",
"groupByProject": "קבץ לפי פרויקט",
"pinned": "נעוצים",
"emptyTitle": "עדיין אין שיחות",
"emptyDescription": "התחילו שיחה חדשה כדי להתחיל",
"remoteControlDisabled": "מושבת"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/ja-JP/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "日付でグループ化",
"groupByProject": "プロジェクトでグループ化",
"pinned": "ピン留め",
"emptyTitle": "まだ会話がありません",
"emptyDescription": "新しいチャットを始めましょう",
"remoteControlDisabled": "無効"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/ko-KR/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "날짜별 그룹화",
"groupByProject": "프로젝트별 그룹화",
"pinned": "고정됨",
"emptyTitle": "아직 대화가 없습니다",
"emptyDescription": "새 대화를 시작해 보세요",
"remoteControlDisabled": "사용 안 함"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/pt-BR/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "Agrupar por data",
"groupByProject": "Agrupar por projeto",
"pinned": "Fixados",
"emptyTitle": "Ainda não há conversas",
"emptyDescription": "Inicie um novo chat para começar",
"remoteControlDisabled": "Desativado"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/ru-RU/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "Группировать по дате",
"groupByProject": "Группировать по проекту",
"pinned": "Закрепленные",
"emptyTitle": "Пока нет диалогов",
"emptyDescription": "Начните новый чат, чтобы приступить",
"remoteControlDisabled": "Отключено"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/zh-CN/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
},
"groupByDate": "按时间分组",
"groupByProject": "按项目分组",
"pinned": "置顶会话",
"emptyTitle": "还没有会话",
"emptyDescription": "开始一个新会话吧"
}
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/zh-HK/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "按時間分組",
"groupByProject": "按專案分組",
"pinned": "置頂會話",
"emptyTitle": "還沒有對話",
"emptyDescription": "開始一個新對話吧",
"remoteControlDisabled": "未啟用"
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/i18n/zh-TW/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
},
"groupByDate": "依時間分組",
"groupByProject": "依專案分組",
"pinned": "置頂會話",
"emptyTitle": "還沒有對話",
"emptyDescription": "開始新的對話吧",
"remoteControlDisabled": "未啟用"
Expand Down
115 changes: 98 additions & 17 deletions test/renderer/components/WindowSideBar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { defineComponent, reactive } from 'vue'
import { flushPromises, mount } from '@vue/test-utils'

type SetupOptions = {
groupMode?: 'time' | 'project'
pinnedSessions?: Array<{ id: string; title: string; status: string; isPinned?: boolean }>
groups?: Array<{
label: string
Expand Down Expand Up @@ -40,7 +41,7 @@ const setup = async (options: SetupOptions = {}) => {
})

const sessionStore = reactive({
groupMode: 'time' as const,
groupMode: (options.groupMode ?? 'time') as 'time' | 'project',
activeSessionId: 'session-1' as string | null,
hasActiveSession: true,
selectSession: vi.fn(async (id: string) => {
Expand Down Expand Up @@ -238,10 +239,38 @@ describe('WindowSideBar agent switch', () => {
await wrapper.vm.$nextTick()

expect(wrapper.text()).toContain('Pinned Session')
expect(wrapper.text()).toContain('chat.sidebar.pinned')
expect(wrapper.text()).toContain('common.time.today')
expect(wrapper.text()).toContain('Normal Session')
}, 10000)

it('collapses and expands pinned sessions from the pinned folder header', async () => {
const { wrapper } = await setup({
pinnedSessions: [
{
id: 'pinned-1',
title: 'Pinned Session',
status: 'none'
}
]
})

await wrapper.vm.$nextTick()

expect(wrapper.text()).toContain('chat.sidebar.pinned')
expect(wrapper.text()).toContain('Pinned Session')

await wrapper.find('[data-group-id="__pinned__"]').trigger('click')
await wrapper.vm.$nextTick()

expect(wrapper.text()).not.toContain('Pinned Session')

await wrapper.find('[data-group-id="__pinned__"]').trigger('click')
await wrapper.vm.$nextTick()

expect(wrapper.text()).toContain('Pinned Session')
}, 10000)

it('toggles pinned state from a session item action', async () => {
const session = {
id: 'normal-1',
Expand All @@ -261,12 +290,78 @@ describe('WindowSideBar agent switch', () => {

const item = wrapper.findComponent({ name: 'WindowSideBarSessionItem' })
item.vm.$emit('toggle-pin', session)
await wrapper.vm.$nextTick()
await flushPromises()

expect(sessionStore.toggleSessionPinned).toHaveBeenCalledWith('normal-1', true)
}, 10000)

it('opens dialogs and dispatches rename, clear, and delete actions', async () => {
it('collapses and expands time groups from the folder header', async () => {
const { wrapper } = await setup({
groups: [
{
label: 'common.time.today',
labelKey: 'common.time.today',
sessions: [
{
id: 'time-1',
title: 'Today Session',
status: 'none'
}
]
}
]
})

await wrapper.vm.$nextTick()

expect(wrapper.text()).toContain('common.time.today')
expect(wrapper.text()).toContain('Today Session')

await wrapper.find('[data-group-id="common.time.today"]').trigger('click')
await wrapper.vm.$nextTick()

expect(wrapper.text()).not.toContain('Today Session')

await wrapper.find('[data-group-id="common.time.today"]').trigger('click')
await wrapper.vm.$nextTick()

expect(wrapper.text()).toContain('Today Session')
}, 10000)

it('collapses and expands project groups from the folder header', async () => {
const { wrapper } = await setup({
groupMode: 'project',
groups: [
{
label: 'DeepChat',
sessions: [
{
id: 'project-1',
title: 'Project Session',
status: 'none'
}
]
}
]
})

await wrapper.vm.$nextTick()

expect(wrapper.text()).toContain('DeepChat')
expect(wrapper.text()).toContain('Project Session')

await wrapper.find('[data-group-id="DeepChat"]').trigger('click')
await wrapper.vm.$nextTick()

expect(wrapper.text()).not.toContain('Project Session')

await wrapper.find('[data-group-id="DeepChat"]').trigger('click')
await wrapper.vm.$nextTick()

expect(wrapper.text()).toContain('Project Session')
}, 10000)

it('opens the delete dialog and dispatches delete actions', async () => {
const session = {
id: 'normal-1',
title: 'Normal Session',
Expand All @@ -285,20 +380,6 @@ describe('WindowSideBar agent switch', () => {

const item = wrapper.findComponent({ name: 'WindowSideBarSessionItem' })

item.vm.$emit('rename', session)
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('dialog.rename.title')
;(wrapper.vm as any).renameValue = 'Renamed Session'
await (wrapper.vm as any).handleRenameConfirm()
expect(sessionStore.renameSession).toHaveBeenCalledWith('normal-1', 'Renamed Session')

item.vm.$emit('clear', session)
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('dialog.cleanMessages.title')

await (wrapper.vm as any).handleClearConfirm()
expect(sessionStore.clearSessionMessages).toHaveBeenCalledWith('normal-1')

item.vm.$emit('delete', session)
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('dialog.delete.title')
Expand Down
Loading
Loading