From 96fa164cb1994f7ea737bd2fe4003799aebc94d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Mon, 13 Jul 2026 18:33:48 +0800 Subject: [PATCH] fix(desktop): streamline panel and effort controls --- .../controls/ReasoningEffortPopover.test.tsx | 16 +++++++-------- .../controls/ReasoningEffortPopover.tsx | 20 +++++++++---------- desktop/src/components/layout/TabBar.test.tsx | 9 ++++++++- desktop/src/components/layout/TabBar.tsx | 2 +- desktop/src/pages/ActiveSession.test.tsx | 12 ++++++++++- desktop/src/pages/ActiveSession.tsx | 7 ++++++- 6 files changed, 44 insertions(+), 22 deletions(-) diff --git a/desktop/src/components/controls/ReasoningEffortPopover.test.tsx b/desktop/src/components/controls/ReasoningEffortPopover.test.tsx index 09ef39c8..10cf4d13 100644 --- a/desktop/src/components/controls/ReasoningEffortPopover.test.tsx +++ b/desktop/src/components/controls/ReasoningEffortPopover.test.tsx @@ -44,16 +44,16 @@ describe('ReasoningEffortPopover', () => { renderPopover() const popover = screen.getByTestId('reasoning-effort-popover') - expect(popover).toHaveStyle({ width: '280px' }) - expect(popover).toHaveClass('px-4', 'pb-4', 'pt-3.5') + expect(popover).toHaveStyle({ width: '240px' }) + expect(popover).toHaveClass('px-3.5', 'pb-3.5', 'pt-3') expect(popover.querySelectorAll('svg')).toHaveLength(0) - expect(screen.getByTestId('reasoning-effort-header')).toHaveClass('mb-3', 'justify-between') - expect(screen.getByTestId('reasoning-effort-label')).toHaveClass('text-[15px]') - expect(screen.getByTestId('reasoning-effort-context-label')).toHaveClass('text-[11px]') + expect(screen.getByTestId('reasoning-effort-header')).toHaveClass('mb-2.5', 'justify-between') + expect(screen.getByTestId('reasoning-effort-label')).toHaveClass('text-sm') + expect(screen.getByTestId('reasoning-effort-context-label')).toHaveClass('text-[10px]') expect(screen.getByTestId('reasoning-effort-context-label')).toHaveTextContent('推理强度') - expect(screen.getByRole('slider', { name: '推理强度' })).toHaveClass('h-11') - expect(screen.getByTestId('reasoning-effort-track')).toHaveClass('h-[30px]') - expect(screen.getByTestId('reasoning-effort-thumb')).toHaveClass('h-10', 'w-10') + expect(screen.getByRole('slider', { name: '推理强度' })).toHaveClass('h-9') + expect(screen.getByTestId('reasoning-effort-track')).toHaveClass('h-6') + expect(screen.getByTestId('reasoning-effort-thumb')).toHaveClass('h-8', 'w-8') }) it('renders every model-supported stop and exposes the selected localized value', () => { diff --git a/desktop/src/components/controls/ReasoningEffortPopover.tsx b/desktop/src/components/controls/ReasoningEffortPopover.tsx index cfaf3cd9..c87666d0 100644 --- a/desktop/src/components/controls/ReasoningEffortPopover.tsx +++ b/desktop/src/components/controls/ReasoningEffortPopover.tsx @@ -20,7 +20,7 @@ type PopoverPosition = { width: number } -const POPOVER_WIDTH = 280 +const POPOVER_WIDTH = 240 const VIEWPORT_MARGIN = 16 const POPOVER_GAP = 10 @@ -107,22 +107,22 @@ export function ReasoningEffortPopover({
{labels[value]}
{ariaLabel}
@@ -137,7 +137,7 @@ export function ReasoningEffortPopover({ aria-valuemax={maxIndex} aria-valuenow={selectedIndex} aria-valuetext={labels[value]} - className="group relative flex h-11 touch-none cursor-pointer items-center outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)] focus-visible:ring-offset-4 focus-visible:ring-offset-[var(--color-surface-container-lowest)]" + className="group relative flex h-9 touch-none cursor-pointer items-center outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)] focus-visible:ring-offset-3 focus-visible:ring-offset-[var(--color-surface-container-lowest)]" onClick={(event) => selectFromClientX(event.clientX)} onPointerDown={(event) => { draggingRef.current = true @@ -182,7 +182,7 @@ export function ReasoningEffortPopover({ >
-
+
{options.map((option, index) => ( ))}
@@ -204,7 +204,7 @@ export function ReasoningEffortPopover({ diff --git a/desktop/src/components/layout/TabBar.test.tsx b/desktop/src/components/layout/TabBar.test.tsx index 99c3fde2..ba4e6078 100644 --- a/desktop/src/components/layout/TabBar.test.tsx +++ b/desktop/src/components/layout/TabBar.test.tsx @@ -295,11 +295,12 @@ describe('TabBar', () => { expect(screen.queryByTestId('session-activity-badge')).not.toBeInTheDocument() }) - it('shows the activity button for completed TodoWrite history without a badge', async () => { + it('shows the activity button for completed TodoWrite history and hides it while the workspace is open', async () => { const { TabBar } = await import('./TabBar') const { useTabStore } = await import('../../stores/tabStore') const { useChatStore } = await import('../../stores/chatStore') const { useSessionStore } = await import('../../stores/sessionStore') + const { useWorkspacePanelStore } = await import('../../stores/workspacePanelStore') const sessionId = 'session-1' const chatSession = makeChatSession('idle') chatSession.messages = [completedTodoWriteMessage()] @@ -324,6 +325,12 @@ describe('TabBar', () => { expect(screen.getByRole('button', { name: /activity/i })).toBeInTheDocument() expect(screen.queryByTestId('session-activity-badge')).not.toBeInTheDocument() + + act(() => { + useWorkspacePanelStore.getState().openPanel(sessionId) + }) + + expect(screen.queryByRole('button', { name: /activity/i })).not.toBeInTheDocument() }) it('shows the activity button without a numeric badge for running or failed activity', async () => { diff --git a/desktop/src/components/layout/TabBar.tsx b/desktop/src/components/layout/TabBar.tsx index 83c10d6d..4d3644c3 100644 --- a/desktop/src/components/layout/TabBar.tsx +++ b/desktop/src/components/layout/TabBar.tsx @@ -141,7 +141,7 @@ export function TabBar() { hasVisibleActivity: hasVisibleSessionActivity(model), } })) - const showActivityButton = activeTabId && activityState.hasVisibleActivity + const showActivityButton = activeTabId && activityState.hasVisibleActivity && !isWorkbenchOpen const moveTab = useTabStore((s) => s.moveTab) const scrollRef = useRef(null) diff --git a/desktop/src/pages/ActiveSession.test.tsx b/desktop/src/pages/ActiveSession.test.tsx index 9b2fb794..e01c8a9b 100644 --- a/desktop/src/pages/ActiveSession.test.tsx +++ b/desktop/src/pages/ActiveSession.test.tsx @@ -468,7 +468,7 @@ describe('ActiveSession task polling', () => { expect(screen.queryByTestId('background-tasks-button')).not.toBeInTheDocument() }) - it('renders the activity panel inside the chat column with session activity rows', () => { + it('renders the activity panel as a rail and hides it when the workspace opens', async () => { const sessionId = 'activity-panel-open-session' useCLITaskStore.setState({ @@ -577,6 +577,16 @@ describe('ActiveSession task polling', () => { expect(chatColumn).toContainElement(screen.getByTestId('chat-input')) expect(screen.queryByTestId('session-task-bar')).not.toBeInTheDocument() expect(screen.queryByTestId('background-tasks-button')).not.toBeInTheDocument() + + act(() => { + useWorkspacePanelStore.getState().openPanel(sessionId) + }) + + expect(screen.getByTestId('workbench-panel')).toBeInTheDocument() + expect(screen.queryByTestId('session-activity-panel')).not.toBeInTheDocument() + await waitFor(() => { + expect(useActivityPanelStore.getState().isOpen(sessionId)).toBe(false) + }) }) it('does not render the activity panel when the store is open without visible activity', async () => { diff --git a/desktop/src/pages/ActiveSession.tsx b/desktop/src/pages/ActiveSession.tsx index 4262e259..d73f9d79 100644 --- a/desktop/src/pages/ActiveSession.tsx +++ b/desktop/src/pages/ActiveSession.tsx @@ -477,6 +477,11 @@ export function ActiveSession() { closeActivityPanel(activeTabId) }, [activeTabId, closeActivityPanel, hasVisibleActivity, isActivityPanelOpen]) + useEffect(() => { + if (!activeTabId || !showWorkbench || !isActivityPanelOpen) return + closeActivityPanel(activeTabId) + }, [activeTabId, closeActivityPanel, isActivityPanelOpen, showWorkbench]) + const handleOpenSubagentRun = useCallback((payload: { sessionId: string; toolUseId: string; title: string }) => { useTabStore.getState().openSubagentTab(payload.sessionId, payload.toolUseId, payload.title) }, []) @@ -725,7 +730,7 @@ export function ActiveSession() { ) : null}
- {activityModel && hasVisibleActivity && !isMobileLayout && !isMemberSession && isSessionTabState(activeTabId, activeTabType) ? ( + {activityModel && hasVisibleActivity && !showWorkbench && !isMobileLayout && !isMemberSession && isSessionTabState(activeTabId, activeTabType) ? (