fix(desktop): compact session sidebar and workbench divider

This commit is contained in:
程序员阿江(Relakkes)
2026-08-02 14:04:26 +08:00
parent a03fd8541e
commit a7df7f84fc
8 changed files with 23 additions and 11 deletions
@@ -328,7 +328,7 @@ describe('Sidebar', () => {
expect(screen.getByText('beta')).toBeInTheDocument()
expect(screen.getByRole('button', { name: /Alpha newest/ })).toBeInTheDocument()
expect(screen.queryByRole('button', { name: /Alpha hidden/ })).not.toBeInTheDocument()
expect(screen.getByTestId('sidebar-project-session-list-workspace-alpha').parentElement).toHaveClass('pl-6')
expect(screen.getByTestId('sidebar-project-session-list-workspace-alpha').parentElement).toHaveClass('pl-5')
expect(screen.getByRole('button', { name: 'Collapse alpha' })).toHaveAttribute('data-state', 'open')
expect(screen.getByTestId('sidebar-project-icon-workspace-alpha')).toHaveAttribute('data-icon-state', 'open')
@@ -995,6 +995,9 @@ describe('Sidebar', () => {
const idleRow = screen.getByRole('button', { name: /Idle Source/ })
expect(within(idleRow).queryByLabelText('Session running')).not.toBeInTheDocument()
expect(within(idleRow).getByText('20m ago')).toBeInTheDocument()
const idleMeta = within(idleRow).getByTitle('last updated 20m ago')
expect(idleMeta).toHaveClass('flex-shrink-0', 'whitespace-nowrap')
expect(idleMeta).not.toHaveClass('min-w-[78px]')
})
it('shows a toast when session creation fails', async () => {
+4 -4
View File
@@ -1047,7 +1047,7 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) {
</div>
</div>
{!projectCollapsed && (
<div className="mt-0.5 pl-6">
<div className="mt-0.5 pl-5">
<div
className={hasInternalScroll ? 'max-h-[420px] overflow-y-auto pr-1' : undefined}
data-testid={`sidebar-project-session-list-${domSafeProjectKey(project.key)}`}
@@ -1086,7 +1086,7 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) {
}}
onContextMenu={(e) => handleContextMenu(e, session.id)}
className={`
group/session w-full rounded-[var(--radius-md)] px-2.5 ${isMobile ? 'py-3' : 'py-1.5'} text-left text-[13px] transition-[background,filter,color,box-shadow] duration-200
group/session w-full rounded-[var(--radius-md)] px-2 ${isMobile ? 'py-3' : 'py-1.5'} text-left text-[13px] transition-[background,filter,color,box-shadow] duration-200
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-focus)] focus-visible:ring-offset-1 focus-visible:ring-offset-[var(--color-surface-sidebar)]
${selectedSessionIds.has(session.id)
? 'sidebar-session-row--selected bg-[var(--color-sidebar-item-active)] text-[var(--color-text-primary)] shadow-[var(--shadow-card)]'
@@ -1100,7 +1100,7 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) {
aria-pressed={isBatchMode ? selectedSessionIds.has(session.id) : undefined}
title={session.title || 'Untitled'}
>
<span className="flex min-w-0 items-center gap-2">
<span className="flex min-w-0 items-center gap-1.5">
{isBatchMode ? (
<span
className={`flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-[5px] border transition-colors ${
@@ -2000,7 +2000,7 @@ function SessionRowMeta({
return (
<span
className="ml-auto flex h-5 min-w-[78px] flex-shrink-0 items-center justify-end gap-1.5 text-[10px] font-medium tabular-nums text-[var(--color-text-tertiary)]"
className="ml-auto flex h-5 flex-shrink-0 items-center justify-end gap-1.5 whitespace-nowrap text-[10px] font-medium tabular-nums text-[var(--color-text-tertiary)]"
title={updatedLabel}
>
{isRunning && (
@@ -77,6 +77,7 @@ describe('useSidebarResize', () => {
it('publishes the stored width onto the shell', () => {
render(<Harness />)
expect(SIDEBAR_DEFAULT_WIDTH).toBe(280)
expect(shellWidth()).toBe(SIDEBAR_DEFAULT_WIDTH)
})
+4
View File
@@ -1518,6 +1518,10 @@ describe('ActiveSession task polling', () => {
expect(within(chatColumn).getByTestId('chat-input')).toHaveAttribute('data-compact', 'true')
expect(chatColumn).toHaveClass('flex-1')
expect(chatColumn).not.toHaveClass('shrink-0')
expect(chatColumn).not.toHaveClass('border-r')
expect(workbenchPanel).not.toHaveClass('border-l')
expect(resizeHandle).toHaveClass('w-px', 'bg-[var(--color-border)]')
expect(resizeHandle.firstElementChild).toHaveClass('-inset-x-1')
expect(contentRow.children[0]).toBe(chatColumn)
expect(contentRow.children[1]).toBe(resizeHandle)
expect(contentRow.children[2]).toBe(workbenchPanel)
+4 -4
View File
@@ -50,7 +50,7 @@ const ACTIVITY_AUTOCLOSE_GRACE_MS = 2000
const WORKSPACE_RESIZE_STEP = 32
const TERMINAL_RESIZE_STEP = 24
const CHAT_COLUMN_WITH_WORKSPACE_CLASS =
'min-w-[320px] flex-1 border-r border-[var(--color-border)] bg-[var(--color-surface)]'
'min-w-[320px] flex-1 bg-[var(--color-surface)]'
const EMPTY_DISMISSED_BACKGROUND_TASK_KEYS: readonly string[] = []
function isSessionTabState(activeTabId: string | null, activeTabType: TabType | null | undefined) {
@@ -201,9 +201,9 @@ function WorkspaceResizeHandle({ panelRef }: { panelRef: RefObject<HTMLElement>
setWidth(renderedWidth - WORKSPACE_RESIZE_STEP)
}
}}
className="group relative z-10 flex w-2 shrink-0 cursor-col-resize items-stretch justify-center bg-[var(--color-surface)] outline-none focus-visible:bg-[var(--color-surface-container)]"
className="relative z-10 w-px shrink-0 cursor-col-resize bg-[var(--color-border)] outline-none transition-colors hover:bg-[var(--color-border-focus)] focus-visible:bg-[var(--color-border-focus)]"
>
<div className="my-3 w-px rounded-full bg-[var(--color-border)] transition-colors group-hover:bg-[var(--color-border-focus)] group-focus-visible:bg-[var(--color-border-focus)]" />
<div aria-hidden="true" className="absolute -inset-x-1 inset-y-0" />
</div>
)
}
@@ -786,7 +786,7 @@ export function ActiveSession() {
<aside
ref={workbenchPanelRef}
data-testid="workbench-panel"
className="flex h-full shrink-0 flex-col border-l border-[var(--color-border)] bg-[var(--color-surface)]"
className="flex h-full shrink-0 flex-col bg-[var(--color-surface)]"
style={{ width: rightPanelWidth, maxWidth: '62%', minWidth: 'min(420px, 54%)' }}
>
<WorkbenchPanel sessionId={activeTabId} />
+1 -1
View File
@@ -29,7 +29,7 @@ const SIDEBAR_WIDTH_STORAGE_KEY = 'cc-haha-sidebar-width'
export const SIDEBAR_MIN_WIDTH = 240
export const SIDEBAR_MAX_WIDTH = 480
export const SIDEBAR_DEFAULT_WIDTH = 300
export const SIDEBAR_DEFAULT_WIDTH = 280
export function clampSidebarWidth(width: number): number {
return Math.min(SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_MIN_WIDTH, Math.round(width)))
+1 -1
View File
@@ -1012,7 +1012,7 @@
the third: it renders transparent, and only in that one theme. */
:root {
/* Layout dimensions */
--sidebar-width: 300px;
--sidebar-width: 280px;
--sidebar-rail-width: 72px;
--titlebar-height: 52px;
--statusbar-height: 36px;
+4
View File
@@ -152,6 +152,10 @@ describe('desktop theme tokens', () => {
}
})
it('keeps the startup sidebar width aligned with the compact store default', () => {
expect(getThemeBlock(':root')).toContain('--sidebar-width: 280px;')
})
it('gives each theme its own color-scheme so native controls match the ground', () => {
// Regression guard for the ink-blue palette: it is a dark ground but is not
// the theme literally named `dark`, so anything testing `theme === 'dark'`