fix(gui): keep sidebar controls when collapsed

This commit is contained in:
Yichen Jiang
2026-07-22 20:28:40 +08:00
parent b51d2b3d67
commit 93f1eaa972
18 changed files with 240 additions and 76 deletions
@@ -15,6 +15,28 @@
font-size: 14px;
}
/* Closed state is a persistent rail: the layout reserves exactly the root's
horizontal padding plus one icon control. */
.root.collapsed {
gap: 0;
}
.collapsed .headerBlock {
padding-bottom: 0;
}
.collapsed .logoRow {
justify-content: center;
padding-inline: 0;
}
.collapsed .foot {
justify-content: center;
width: 28px;
margin-top: auto;
padding: 0;
}
/* Header block (figma 133:7630): logo row + New Session, gap 16, padBottom 12. */
.headerBlock {
flex: none;
@@ -24,12 +24,10 @@ const GROUP_BY_ITEMS = [
{ id: 'status', label: 'Status', disabled: true },
]
/**
* Render the sidebar column.
* @param props - composed slot props (owner share + injected surface, contract/slots.ts).
* @returns the sidebar element tree.
*/
export function SidebarRoot({ useTree, useCurrent, actions, tree }: SidebarRootComponentProps) {
type SidebarBodyProps = Pick<SidebarRootComponentProps, 'useTree' | 'useCurrent' | 'actions' | 'tree'>
/** Expanded-only content; unmounting drops tree/current subscriptions while the rail is collapsed. */
function SidebarBody({ useTree, useCurrent, actions, tree }: SidebarBodyProps) {
const rows = useTree((s) => s.rows)
const query = useTree((s) => s.query)
const groupBy = useTree((s) => s.groupBy)
@@ -47,32 +45,7 @@ export function SidebarRoot({ useTree, useCurrent, actions, tree }: SidebarRootC
}
return (
<div className={css.root}>
<div className={css.headerBlock}>
<div className={css.logoRow}>
<span className={css.brand}>
{/* Wordmark svg not extracted yet (figma 88:8932) — text stands in at the same ink. */}
<FishLogo size={23} />
<span className={css.wordmark}>deepseek</span>
<span className={css.badge}>HARNESS</span>
</span>
<button
type="button"
className={css.iconButton}
aria-label="Collapse sidebar"
onClick={() => { actions.toggleSidebar() }}
>
<IconPanelLeftOutline16 />
</button>
</div>
<button type="button" className={css.newSession} onClick={() => { actions.create() }}>
<IconNewChatOutline16 size={14} />
New Session
</button>
</div>
<div className={css.listArea}>
<div className={css.listArea}>
<div className={css.sectionHeader}>
<span className={css.sectionLabel}>WorkSpace</span>
<Menu
@@ -153,11 +126,66 @@ export function SidebarRoot({ useTree, useCurrent, actions, tree }: SidebarRootC
))}
</div>
<span className={css.fade} />
</div>
)
}
/**
* Render the sidebar column.
* @param props - composed slot props (owner share + injected surface, contract/slots.ts).
* @returns the sidebar element tree.
*/
export function SidebarRoot(props: SidebarRootComponentProps) {
const open = props.useSidebarOpen()
return (
<div className={clsx(css.root, !open && css.collapsed)}>
<div className={css.headerBlock}>
<div className={css.logoRow}>
{open
? (
<span className={css.brand}>
{/* Wordmark svg not extracted yet (figma 88:8932) — text stands in at the same ink. */}
<FishLogo size={23} />
<span className={css.wordmark}>deepseek</span>
<span className={css.badge}>HARNESS</span>
</span>
)
: null}
<button
type="button"
className={css.iconButton}
aria-label={open ? 'Collapse sidebar' : 'Expand sidebar'}
onClick={() => { props.actions.toggleSidebar() }}
>
<IconPanelLeftOutline16 />
</button>
</div>
{open
? (
<button type="button" className={css.newSession} onClick={() => { props.actions.create() }}>
<IconNewChatOutline16 size={14} />
New Session
</button>
)
: null}
</div>
<div className={clsx(css.foot)} role="button" tabIndex={0} aria-label="Settings">
{open
? (
<SidebarBody
useTree={props.useTree}
useCurrent={props.useCurrent}
actions={props.actions}
tree={props.tree}
/>
)
: null}
<div className={css.foot} role="button" tabIndex={0} aria-label="Settings">
<IconSettingsOutline14 />
Settings
{open ? <span>Settings</span> : null}
</div>
</div>
)
@@ -37,6 +37,8 @@ export type SidebarRootInjected = {
useTree: SnapshotSelectorHook<SidebarTreeState>
/** Current session selector (row highlight); undefined selects nothing. */
useCurrent: () => SessionId | undefined
/** Sidebar open selector; the collapsed render keeps only persistent rail controls. */
useSidebarOpen: () => boolean
actions: SidebarActions
tree: SidebarTreeActions
}
@@ -45,6 +45,7 @@ export function apply(ctx: ClientContext): void {
return {
useTree: tree.store.useSelector,
useCurrent: () => layout.current.useSelector(s => s.sessionId),
useSidebarOpen: () => layout.sidebar.useSelector(s => s.open),
actions: {
open: (id) => { layout.open(id) },
create: (cwd) => {