mirror of
https://github.com/NanmiCoder/claude-code-haha.git
synced 2026-10-10 20:03:13 +08:00
fix(desktop): compact session sidebar and workbench divider
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'`
|
||||
|
||||
Reference in New Issue
Block a user