mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-13 09:01:54 +00:00
refactor(desktop): extract shared WorkspaceHeader for repo + worktree rows
The repo and worktree header rows were ~identical after the handle move. Fold them into one WorkspaceHeader (emphasis flag for the repo level) plus a small WorkspaceAddButton, so the toggle/handle/count/+ wiring lives in one place.
This commit is contained in:
parent
0595af0ad1
commit
dd12a5403d
1 changed files with 116 additions and 77 deletions
|
|
@ -1502,49 +1502,27 @@ function SidebarWorkspaceGroup({
|
|||
style={style}
|
||||
{...rest}
|
||||
>
|
||||
<div className="group/workspace flex min-h-6 items-center gap-1 px-2 pt-1 text-[0.6875rem] font-medium text-(--ui-text-tertiary)">
|
||||
<button
|
||||
className="flex min-w-0 flex-1 items-center gap-1.5 bg-transparent text-left hover:text-(--ui-text-secondary)"
|
||||
onClick={() => setOpen(value => !value)}
|
||||
type="button"
|
||||
>
|
||||
{reorderable ? (
|
||||
<WorkspaceReorderHandle
|
||||
dragging={dragging}
|
||||
dragHandleProps={dragHandleProps}
|
||||
icon={leadingIcon}
|
||||
label={s.reorderWorkspace(group.label)}
|
||||
/>
|
||||
) : (
|
||||
leadingIcon
|
||||
)}
|
||||
<span className="min-w-0 truncate">{group.label}</span>
|
||||
<span className="shrink-0">
|
||||
<SidebarCount>
|
||||
{isProfileGroup ? countLabel(visibleSessions.length, totalCount) : group.sessions.length}
|
||||
</SidebarCount>
|
||||
</span>
|
||||
<DisclosureCaret
|
||||
className="shrink-0 text-(--ui-text-tertiary) opacity-0 transition group-hover/workspace:opacity-100"
|
||||
open={open}
|
||||
/>
|
||||
</button>
|
||||
{(onNewSession || isProfileGroup) && (
|
||||
<Tip label={s.newSessionIn(group.label)}>
|
||||
<button
|
||||
aria-label={s.newSessionIn(group.label)}
|
||||
className="grid size-4 shrink-0 place-items-center rounded-sm bg-transparent text-(--ui-text-quaternary) opacity-0 transition-opacity hover:bg-(--ui-control-hover-background) hover:text-foreground group-hover/workspace:opacity-100"
|
||||
<WorkspaceHeader
|
||||
action={
|
||||
(onNewSession || isProfileGroup) && (
|
||||
<WorkspaceAddButton
|
||||
label={s.newSessionIn(group.label)}
|
||||
// Profile groups start a fresh session in that profile but keep the
|
||||
// all-profiles browse view (newSessionInProfile leaves the scope
|
||||
// alone); workspace groups seed the new session's cwd from the path.
|
||||
onClick={() => (isProfileGroup ? newSessionInProfile(group.id) : onNewSession?.(group.path))}
|
||||
type="button"
|
||||
>
|
||||
<Codicon name="add" size="0.75rem" />
|
||||
</button>
|
||||
</Tip>
|
||||
)}
|
||||
</div>
|
||||
/>
|
||||
)
|
||||
}
|
||||
count={isProfileGroup ? countLabel(visibleSessions.length, totalCount) : group.sessions.length}
|
||||
dragging={dragging}
|
||||
dragHandleProps={dragHandleProps}
|
||||
icon={leadingIcon}
|
||||
label={group.label}
|
||||
onToggle={() => setOpen(value => !value)}
|
||||
open={open}
|
||||
reorderable={reorderable}
|
||||
/>
|
||||
{open && (
|
||||
<>
|
||||
{renderRows(visibleSessions)}
|
||||
|
|
@ -1637,44 +1615,22 @@ function SidebarWorkspaceParent({
|
|||
style={style}
|
||||
{...rest}
|
||||
>
|
||||
<div className="group/workspace flex min-h-6 items-center gap-1 px-2 pt-1 text-[0.6875rem] font-semibold text-(--ui-text-secondary)">
|
||||
<button
|
||||
className="flex min-w-0 flex-1 items-center gap-1.5 bg-transparent text-left hover:text-foreground"
|
||||
onClick={() => setOpen(value => !value)}
|
||||
type="button"
|
||||
>
|
||||
{reorderable ? (
|
||||
<WorkspaceReorderHandle
|
||||
dragging={dragging}
|
||||
dragHandleProps={dragHandleProps}
|
||||
icon={<Codicon className="text-(--ui-text-tertiary)" name="repo" size="0.75rem" />}
|
||||
label={s.reorderWorkspace(parent.label)}
|
||||
/>
|
||||
) : (
|
||||
<Codicon className="shrink-0 text-(--ui-text-tertiary)" name="repo" size="0.75rem" />
|
||||
)}
|
||||
<span className="min-w-0 truncate">{parent.label}</span>
|
||||
<span className="shrink-0">
|
||||
<SidebarCount>{parent.sessionCount}</SidebarCount>
|
||||
</span>
|
||||
<DisclosureCaret
|
||||
className="shrink-0 text-(--ui-text-tertiary) opacity-0 transition group-hover/workspace:opacity-100"
|
||||
open={open}
|
||||
/>
|
||||
</button>
|
||||
{onNewSession && (newSessionPath || soleWorktree) && (
|
||||
<Tip label={s.newSessionIn(parent.label)}>
|
||||
<button
|
||||
aria-label={s.newSessionIn(parent.label)}
|
||||
className="grid size-4 shrink-0 place-items-center rounded-sm bg-transparent text-(--ui-text-quaternary) opacity-0 transition-opacity hover:bg-(--ui-control-hover-background) hover:text-foreground group-hover/workspace:opacity-100"
|
||||
onClick={() => onNewSession?.(newSessionPath)}
|
||||
type="button"
|
||||
>
|
||||
<Codicon name="add" size="0.75rem" />
|
||||
</button>
|
||||
</Tip>
|
||||
)}
|
||||
</div>
|
||||
<WorkspaceHeader
|
||||
action={
|
||||
onNewSession && (newSessionPath || soleWorktree) && (
|
||||
<WorkspaceAddButton label={s.newSessionIn(parent.label)} onClick={() => onNewSession?.(newSessionPath)} />
|
||||
)
|
||||
}
|
||||
count={parent.sessionCount}
|
||||
dragging={dragging}
|
||||
dragHandleProps={dragHandleProps}
|
||||
emphasis
|
||||
icon={<Codicon className="shrink-0 text-(--ui-text-tertiary)" name="repo" size="0.75rem" />}
|
||||
label={parent.label}
|
||||
onToggle={() => setOpen(value => !value)}
|
||||
open={open}
|
||||
reorderable={reorderable}
|
||||
/>
|
||||
{open &&
|
||||
(soleWorktree ? (
|
||||
// Collapsed: the repo's sessions hang straight off the header.
|
||||
|
|
@ -1784,6 +1740,89 @@ function WorkspaceReorderHandle({
|
|||
)
|
||||
}
|
||||
|
||||
// "+" affordance shared by repo and worktree headers — reveals on header hover.
|
||||
function WorkspaceAddButton({ label, onClick }: { label: string; onClick: () => void }) {
|
||||
return (
|
||||
<Tip label={label}>
|
||||
<button
|
||||
aria-label={label}
|
||||
className="grid size-4 shrink-0 place-items-center rounded-sm bg-transparent text-(--ui-text-quaternary) opacity-0 transition-opacity hover:bg-(--ui-control-hover-background) hover:text-foreground group-hover/workspace:opacity-100"
|
||||
onClick={onClick}
|
||||
type="button"
|
||||
>
|
||||
<Codicon name="add" size="0.75rem" />
|
||||
</button>
|
||||
</Tip>
|
||||
)
|
||||
}
|
||||
|
||||
// Collapsible header shared by the repo (emphasis) and worktree levels: a
|
||||
// toggle button whose leading glyph doubles as the reorder handle, plus an
|
||||
// optional trailing action (the +).
|
||||
function WorkspaceHeader({
|
||||
action,
|
||||
count,
|
||||
dragHandleProps,
|
||||
dragging = false,
|
||||
emphasis = false,
|
||||
icon,
|
||||
label,
|
||||
onToggle,
|
||||
open,
|
||||
reorderable = false
|
||||
}: {
|
||||
action?: React.ReactNode
|
||||
count: React.ReactNode
|
||||
dragHandleProps?: React.HTMLAttributes<HTMLElement>
|
||||
dragging?: boolean
|
||||
emphasis?: boolean
|
||||
icon: React.ReactNode
|
||||
label: string
|
||||
onToggle: () => void
|
||||
open: boolean
|
||||
reorderable?: boolean
|
||||
}) {
|
||||
const { t } = useI18n()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'group/workspace flex min-h-6 items-center gap-1 px-2 pt-1 text-[0.6875rem]',
|
||||
emphasis ? 'font-semibold text-(--ui-text-secondary)' : 'font-medium text-(--ui-text-tertiary)'
|
||||
)}
|
||||
>
|
||||
<button
|
||||
className={cn(
|
||||
'flex min-w-0 flex-1 items-center gap-1.5 bg-transparent text-left',
|
||||
emphasis ? 'hover:text-foreground' : 'hover:text-(--ui-text-secondary)'
|
||||
)}
|
||||
onClick={onToggle}
|
||||
type="button"
|
||||
>
|
||||
{reorderable ? (
|
||||
<WorkspaceReorderHandle
|
||||
dragging={dragging}
|
||||
dragHandleProps={dragHandleProps}
|
||||
icon={icon}
|
||||
label={t.sidebar.reorderWorkspace(label)}
|
||||
/>
|
||||
) : (
|
||||
icon
|
||||
)}
|
||||
<span className="min-w-0 truncate">{label}</span>
|
||||
<span className="shrink-0">
|
||||
<SidebarCount>{count}</SidebarCount>
|
||||
</span>
|
||||
<DisclosureCaret
|
||||
className="shrink-0 text-(--ui-text-tertiary) opacity-0 transition group-hover/workspace:opacity-100"
|
||||
open={open}
|
||||
/>
|
||||
</button>
|
||||
{action}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface SortableSessionRowProps {
|
||||
session: SessionInfo
|
||||
isPinned: boolean
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue