128128 <!-- Session list -->
129129 <div class =" flex-1 overflow-y-auto px-1.5" >
130130 <div v-if =" pinnedSessions.length > 0" class =" pt-2 space-y-0.5" >
131- <button
131+ <WindowSideBarSessionItem
132132 v-for =" session in pinnedSessions"
133133 :key =" `pinned-${session.id}`"
134- class =" flex items-center gap-2 w-full px-2 py-1.5 rounded-md text-left transition-all duration-150"
135- :class ="
136- sessionStore.activeSessionId === session.id
137- ? 'bg-accent text-accent-foreground'
138- : 'text-foreground/80 hover:bg-accent/50'
139- "
140- @click =" handleSessionClick(session)"
141- >
142- <Icon icon =" lucide:pin" class =" w-3.5 h-3.5 text-yellow-500 shrink-0" />
143- <span class =" flex-1 text-sm truncate" >{{ session.title }}</span >
144- <span v-if =" session.status === 'working'" class =" shrink-0" >
145- <Icon icon =" lucide:loader-2" class =" w-3.5 h-3.5 text-primary animate-spin" />
146- </span >
147- <span v-else-if =" session.status === 'completed'" class =" shrink-0" >
148- <Icon icon =" lucide:check" class =" w-3.5 h-3.5 text-green-500" />
149- </span >
150- <span v-else-if =" session.status === 'error'" class =" shrink-0" >
151- <Icon icon =" lucide:alert-circle" class =" w-3.5 h-3.5 text-destructive" />
152- </span >
153- </button >
134+ :session =" session"
135+ :active =" sessionStore.activeSessionId === session.id"
136+ @select =" handleSessionClick"
137+ @toggle-pin =" handleTogglePin"
138+ @rename =" openRenameDialog"
139+ @clear =" openClearDialog"
140+ @delete =" openDeleteDialog"
141+ />
154142 </div >
155143
156144 <!-- Empty state -->
171159 group.labelKey ? t(group.labelKey) : group.label
172160 }}</span >
173161 </div >
174- <button
162+ <WindowSideBarSessionItem
175163 v-for =" session in group.sessions"
176164 :key =" session.id"
177- class =" flex items-center gap-2 w-full px-2 py-1.5 rounded-md text-left transition-all duration-150"
178- :class ="
179- sessionStore.activeSessionId === session.id
180- ? 'bg-accent text-accent-foreground'
181- : 'text-foreground/80 hover:bg-accent/50'
182- "
183- @click =" handleSessionClick(session)"
184- >
185- <span class =" flex-1 text-sm truncate" >{{ session.title }}</span >
186- <span v-if =" session.status === 'working'" class =" shrink-0" >
187- <Icon icon =" lucide:loader-2" class =" w-3.5 h-3.5 text-primary animate-spin" />
188- </span >
189- <span v-else-if =" session.status === 'completed'" class =" shrink-0" >
190- <Icon icon =" lucide:check" class =" w-3.5 h-3.5 text-green-500" />
191- </span >
192- <span v-else-if =" session.status === 'error'" class =" shrink-0" >
193- <Icon icon =" lucide:alert-circle" class =" w-3.5 h-3.5 text-destructive" />
194- </span >
195- </button >
165+ :session =" session"
166+ :active =" sessionStore.activeSessionId === session.id"
167+ @select =" handleSessionClick"
168+ @toggle-pin =" handleTogglePin"
169+ @rename =" openRenameDialog"
170+ @clear =" openClearDialog"
171+ @delete =" openDeleteDialog"
172+ />
196173 </template >
197174 </div >
198175 </div >
199176 </div >
200177 </TooltipProvider >
178+
179+ <Dialog v-model:open =" renameDialogOpen" >
180+ <DialogContent >
181+ <DialogHeader >
182+ <DialogTitle >{{ t('dialog.rename.title') }}</DialogTitle >
183+ <DialogDescription >{{ t('dialog.rename.description') }}</DialogDescription >
184+ </DialogHeader >
185+ <Input v-model =" renameValue" />
186+ <DialogFooter >
187+ <Button variant =" outline" @click =" renameDialogOpen = false" >{{
188+ t('dialog.cancel')
189+ }}</Button >
190+ <Button variant =" default" @click =" handleRenameConfirm" >{{ t('dialog.confirm') }}</Button >
191+ </DialogFooter >
192+ </DialogContent >
193+ </Dialog >
194+
195+ <Dialog v-model:open =" clearDialogOpen" >
196+ <DialogContent >
197+ <DialogHeader >
198+ <DialogTitle >{{ t('dialog.cleanMessages.title') }}</DialogTitle >
199+ <DialogDescription >{{ t('dialog.cleanMessages.description') }}</DialogDescription >
200+ </DialogHeader >
201+ <DialogFooter >
202+ <Button variant =" outline" @click =" clearDialogOpen = false" >{{ t('dialog.cancel') }}</Button >
203+ <Button variant =" destructive" @click =" handleClearConfirm" >{{
204+ t('dialog.cleanMessages.confirm')
205+ }}</Button >
206+ </DialogFooter >
207+ </DialogContent >
208+ </Dialog >
209+
210+ <Dialog v-model:open =" deleteDialogOpen" >
211+ <DialogContent >
212+ <DialogHeader >
213+ <DialogTitle >{{ t('dialog.delete.title') }}</DialogTitle >
214+ <DialogDescription >{{ t('dialog.delete.description') }}</DialogDescription >
215+ </DialogHeader >
216+ <DialogFooter >
217+ <Button variant =" outline" @click =" deleteDialogOpen = false" >{{
218+ t('dialog.cancel')
219+ }}</Button >
220+ <Button variant =" destructive" @click =" handleDeleteConfirm" >{{
221+ t('dialog.delete.confirm')
222+ }}</Button >
223+ </DialogFooter >
224+ </DialogContent >
225+ </Dialog >
201226</template >
202227
203228<script setup lang="ts">
204229import { ref , computed } from ' vue'
205230import { Icon } from ' @iconify/vue'
231+ import { Input } from ' @shadcn/components/ui/input'
206232import {
207233 Tooltip ,
208234 TooltipContent ,
209235 TooltipProvider ,
210236 TooltipTrigger
211237} from ' @shadcn/components/ui/tooltip'
212238import { Button } from ' @shadcn/components/ui/button'
239+ import {
240+ Dialog ,
241+ DialogContent ,
242+ DialogDescription ,
243+ DialogFooter ,
244+ DialogHeader ,
245+ DialogTitle
246+ } from ' @shadcn/components/ui/dialog'
213247import { usePresenter } from ' @/composables/usePresenter'
214248import { useThemeStore } from ' @/stores/theme'
215249import { useAgentStore } from ' @/stores/ui/agent'
216- import { useSessionStore } from ' @/stores/ui/session'
250+ import { useSessionStore , type UISession } from ' @/stores/ui/session'
217251import ModelIcon from ' ./icons/ModelIcon.vue'
252+ import WindowSideBarSessionItem from ' ./WindowSideBarSessionItem.vue'
218253import { useI18n } from ' vue-i18n'
219254
220255const windowPresenter = usePresenter (' windowPresenter' )
@@ -232,6 +267,37 @@ const selectedAgentName = computed(
232267
233268const pinnedSessions = computed (() => sessionStore .getPinnedSessions (agentStore .selectedAgentId ))
234269const filteredGroups = computed (() => sessionStore .getFilteredGroups (agentStore .selectedAgentId ))
270+ const renameTargetSession = ref <UISession | null >(null )
271+ const clearTargetSession = ref <UISession | null >(null )
272+ const deleteTargetSession = ref <UISession | null >(null )
273+ const renameValue = ref (' ' )
274+
275+ const renameDialogOpen = computed ({
276+ get : () => renameTargetSession .value !== null ,
277+ set : (open : boolean ) => {
278+ if (! open ) {
279+ renameTargetSession .value = null
280+ }
281+ }
282+ })
283+
284+ const clearDialogOpen = computed ({
285+ get : () => clearTargetSession .value !== null ,
286+ set : (open : boolean ) => {
287+ if (! open ) {
288+ clearTargetSession .value = null
289+ }
290+ }
291+ })
292+
293+ const deleteDialogOpen = computed ({
294+ get : () => deleteTargetSession .value !== null ,
295+ set : (open : boolean ) => {
296+ if (! open ) {
297+ deleteTargetSession .value = null
298+ }
299+ }
300+ })
235301
236302const openSettings = () => {
237303 const windowId = window .api .getWindowId ()
@@ -241,7 +307,7 @@ const openSettings = () => {
241307}
242308
243309const handleNewChat = () => {
244- sessionStore .closeSession ()
310+ void sessionStore .closeSession ()
245311}
246312
247313const handleAgentSelect = async (id : string | null ) => {
@@ -281,7 +347,82 @@ const handleAgentSelect = async (id: string | null) => {
281347}
282348
283349const handleSessionClick = (session : { id: string }) => {
284- sessionStore .selectSession (session .id )
350+ void sessionStore .selectSession (session .id )
351+ }
352+
353+ const closeAllSessionDialogs = () => {
354+ renameTargetSession .value = null
355+ clearTargetSession .value = null
356+ deleteTargetSession .value = null
357+ }
358+
359+ const openRenameDialog = (session : UISession ) => {
360+ closeAllSessionDialogs ()
361+ renameValue .value = session .title
362+ renameTargetSession .value = session
363+ }
364+
365+ const openClearDialog = (session : UISession ) => {
366+ closeAllSessionDialogs ()
367+ clearTargetSession .value = session
368+ }
369+
370+ const openDeleteDialog = (session : UISession ) => {
371+ closeAllSessionDialogs ()
372+ deleteTargetSession .value = session
373+ }
374+
375+ const handleTogglePin = async (session : UISession ) => {
376+ try {
377+ await sessionStore .toggleSessionPinned (session .id , ! session .isPinned )
378+ } catch (error ) {
379+ console .error (' Failed to toggle pin status:' , error )
380+ }
381+ }
382+
383+ const handleRenameConfirm = async () => {
384+ const targetSession = renameTargetSession .value
385+ if (! targetSession ) {
386+ return
387+ }
388+
389+ try {
390+ await sessionStore .renameSession (targetSession .id , renameValue .value )
391+ } catch (error ) {
392+ console .error (t (' common.error.renameChatFailed' ), error )
393+ }
394+
395+ renameTargetSession .value = null
396+ }
397+
398+ const handleClearConfirm = async () => {
399+ const targetSession = clearTargetSession .value
400+ if (! targetSession ) {
401+ return
402+ }
403+
404+ try {
405+ await sessionStore .clearSessionMessages (targetSession .id )
406+ } catch (error ) {
407+ console .error (t (' common.error.cleanMessagesFailed' ), error )
408+ }
409+
410+ clearTargetSession .value = null
411+ }
412+
413+ const handleDeleteConfirm = async () => {
414+ const targetSession = deleteTargetSession .value
415+ if (! targetSession ) {
416+ return
417+ }
418+
419+ try {
420+ await sessionStore .deleteSession (targetSession .id )
421+ } catch (error ) {
422+ console .error (t (' common.error.deleteChatFailed' ), error )
423+ }
424+
425+ deleteTargetSession .value = null
285426}
286427 </script >
287428
0 commit comments