{!isMemberSession && (
<>
@@ -513,14 +548,14 @@ export function ChatInput() {
className="flex w-full items-center gap-3 px-4 py-2.5 text-left transition-colors hover:bg-[var(--color-surface-hover)]"
>
attach_file
- {t('chat.addFiles')}
+ {addFilesLabel}
)}
diff --git a/desktop/src/components/layout/Sidebar.test.tsx b/desktop/src/components/layout/Sidebar.test.tsx
index 64c93a98..a44f04ae 100644
--- a/desktop/src/components/layout/Sidebar.test.tsx
+++ b/desktop/src/components/layout/Sidebar.test.tsx
@@ -39,14 +39,18 @@ import { useUIStore } from '../../stores/uiStore'
describe('Sidebar', () => {
const connectToSession = vi.fn()
+ const disconnectSession = vi.fn()
const fetchSessions = vi.fn()
const createSession = vi.fn()
+ const deleteSession = vi.fn()
const addToast = vi.fn()
beforeEach(() => {
connectToSession.mockReset()
+ disconnectSession.mockReset()
fetchSessions.mockReset()
createSession.mockReset()
+ deleteSession.mockReset()
addToast.mockReset()
useTabStore.setState({ tabs: [], activeTabId: null })
@@ -59,9 +63,11 @@ describe('Sidebar', () => {
availableProjects: [],
fetchSessions,
createSession,
+ deleteSession,
})
useChatStore.setState({
connectToSession,
+ disconnectSession,
} as Partial
>)
useUIStore.setState({
addToast,
@@ -111,4 +117,42 @@ describe('Sidebar', () => {
expect(useTabStore.getState().tabs).toEqual([])
})
+
+ it('removes the matching tab when deleting a session from the sidebar', async () => {
+ deleteSession.mockResolvedValue(undefined)
+ useSessionStore.setState({
+ sessions: [
+ {
+ id: 'session-1',
+ title: 'Open Session',
+ createdAt: new Date().toISOString(),
+ modifiedAt: new Date().toISOString(),
+ messageCount: 1,
+ projectPath: '/workspace/project',
+ workDir: '/workspace/project',
+ workDirExists: true,
+ },
+ ],
+ })
+ useTabStore.setState({
+ tabs: [{ sessionId: 'session-1', title: 'Open Session', type: 'session', status: 'idle' }],
+ activeTabId: 'session-1',
+ })
+
+ render()
+
+ fireEvent.contextMenu(screen.getByRole('button', { name: /Open Session/ }))
+
+ await act(async () => {
+ fireEvent.click(screen.getByRole('button', { name: 'Delete' }))
+ })
+
+ await waitFor(() => {
+ expect(deleteSession).toHaveBeenCalledWith('session-1')
+ expect(disconnectSession).toHaveBeenCalledWith('session-1')
+ })
+
+ expect(useTabStore.getState().tabs).toEqual([])
+ expect(useTabStore.getState().activeTabId).toBeNull()
+ })
})
diff --git a/desktop/src/components/layout/Sidebar.tsx b/desktop/src/components/layout/Sidebar.tsx
index af13259d..1eeef738 100644
--- a/desktop/src/components/layout/Sidebar.tsx
+++ b/desktop/src/components/layout/Sidebar.tsx
@@ -23,6 +23,8 @@ export function Sidebar() {
const renameSession = useSessionStore((s) => s.renameSession)
const addToast = useUIStore((s) => s.addToast)
const activeTabId = useTabStore((s) => s.activeTabId)
+ const closeTab = useTabStore((s) => s.closeTab)
+ const disconnectSession = useChatStore((s) => s.disconnectSession)
const [searchQuery, setSearchQuery] = useState('')
const [contextMenu, setContextMenu] = useState<{ id: string; x: number; y: number } | null>(null)
const [renamingId, setRenamingId] = useState(null)
@@ -64,7 +66,9 @@ export function Sidebar() {
const handleDelete = useCallback(async (id: string) => {
setContextMenu(null)
await deleteSession(id)
- }, [deleteSession])
+ disconnectSession(id)
+ closeTab(id)
+ }, [closeTab, deleteSession, disconnectSession])
const handleStartRename = useCallback((id: string, currentTitle: string) => {
setContextMenu(null)
diff --git a/desktop/src/components/skills/SkillList.tsx b/desktop/src/components/skills/SkillList.tsx
index 9fc62716..f34cecaa 100644
--- a/desktop/src/components/skills/SkillList.tsx
+++ b/desktop/src/components/skills/SkillList.tsx
@@ -1,5 +1,6 @@
import { useEffect, useMemo } from 'react'
import { useSkillStore } from '../../stores/skillStore'
+import { useSessionStore } from '../../stores/sessionStore'
import { useTranslation } from '../../i18n'
import type { SkillMeta, SkillSource } from '../../types/skill'
@@ -28,11 +29,15 @@ function estimateTokens(contentLength: number) {
export function SkillList() {
const { skills, isLoading, error, fetchSkills, fetchSkillDetail } =
useSkillStore()
+ const sessions = useSessionStore((s) => s.sessions)
+ const activeSessionId = useSessionStore((s) => s.activeSessionId)
const t = useTranslation()
+ const activeSession = sessions.find((session) => session.id === activeSessionId)
+ const currentWorkDir = activeSession?.workDir || undefined
useEffect(() => {
- fetchSkills()
- }, [fetchSkills])
+ fetchSkills(currentWorkDir)
+ }, [fetchSkills, currentWorkDir])
const grouped = useMemo(() => {
const result: Partial> = {}
@@ -175,7 +180,7 @@ export function SkillList() {
key={`${skill.source}-${skill.name}`}
onClick={() =>
skill.hasDirectory &&
- fetchSkillDetail(skill.source, skill.name)
+ fetchSkillDetail(skill.source, skill.name, currentWorkDir)
}
disabled={!skill.hasDirectory}
className="group rounded-xl border border-transparent px-3 py-3 text-left transition-all hover:border-[var(--color-border-focus)] hover:bg-[var(--color-surface-hover)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-surface)] disabled:opacity-60 disabled:cursor-default disabled:hover:bg-transparent disabled:hover:border-transparent"
diff --git a/desktop/src/pages/ActiveSession.tsx b/desktop/src/pages/ActiveSession.tsx
index a63724e2..61762165 100644
--- a/desktop/src/pages/ActiveSession.tsx
+++ b/desktop/src/pages/ActiveSession.tsx
@@ -204,7 +204,7 @@ export function ActiveSession() {
-
+
)
}
diff --git a/desktop/src/pages/EmptySession.tsx b/desktop/src/pages/EmptySession.tsx
index 653dbf2d..e54fe5a6 100644
--- a/desktop/src/pages/EmptySession.tsx
+++ b/desktop/src/pages/EmptySession.tsx
@@ -390,12 +390,12 @@ export function EmptySession() {
ref={(el) => { slashItemRefs.current[index] = el }}
onClick={() => selectSlashCommand(command.name)}
onMouseEnter={() => setSlashSelectedIndex(index)}
- className={`flex w-full items-center justify-between gap-3 px-4 py-2.5 text-left transition-colors ${
+ className={`flex w-full items-center gap-3 px-4 py-2.5 text-left transition-colors ${
index === slashSelectedIndex ? 'bg-[var(--color-surface-hover)]' : 'hover:bg-[var(--color-surface-hover)]'
}`}
>
-