feat: tune session sidebar tooltip timing

Adds a shared sidebar tooltip provider with delayed open and instant close behavior
Removes per-button tooltip delays so sidebar actions feel more consistent
Aligns session sidebar hover interactions with the opencode-style tooltip experience
This commit is contained in:
Bohdan Triapitsyn
2026-08-30 18:03:29 +03:00
parent 654b3d2441
commit d0a5538bff
5 changed files with 19 additions and 20 deletions
@@ -592,13 +592,12 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
}, [mobileVariant, openNewSessionDraft, setSessionSwitcherOpen]);
return (
// One shared tooltip provider for the whole sidebar: session tooltips open
// instantly, and moving between rows hands the tooltip over (grouping)
// instead of replaying the exit/enter animation for each row.
// closeDelay bridges the small gap between rows: the tooltip survives the
// pointer crossing row margins, and the grouping timeout hands it over to
// the next row without an exit/enter cycle.
<TooltipProvider delay={0} closeDelay={150} timeout={600}>
// One shared tooltip provider for the whole sidebar, matching the opencode
// sidebar feel: 400ms before the first tooltip opens, instant close on
// leave, and grouping — moving between rows within 600ms hands the tooltip
// over to the next row without replaying the open delay or exit/enter
// animation.
<TooltipProvider delay={400} closeDelay={0} timeout={300}>
<div
ref={sessionSearchContainerRef}
className={cn(
@@ -1200,7 +1200,7 @@ function SessionGroupSectionBase(props: SessionGroupSectionProps): React.ReactNo
</div>
{group.isArchivedBucket && allGroupSessions.length > 0 ? (
<div className={cn('absolute right-0.5 top-1/2 -translate-y-1/2 z-10 transition-opacity', alwaysShowActions ? 'opacity-100' : 'opacity-0 group-hover/gh:opacity-100 group-focus-within/gh:opacity-100')}>
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -1223,7 +1223,7 @@ function SessionGroupSectionBase(props: SessionGroupSectionProps): React.ReactNo
) : null}
{group.directory && !group.isMain && group.worktree ? (
<div className={cn('absolute right-7 top-1/2 -translate-y-1/2 z-10 transition-opacity', alwaysShowActions ? 'opacity-100' : 'opacity-0 group-hover/gh:opacity-100 group-focus-within/gh:opacity-100')}>
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -1247,7 +1247,7 @@ function SessionGroupSectionBase(props: SessionGroupSectionProps): React.ReactNo
) : null}
{group.directory ? (
<div className={cn('absolute right-0.5 top-1/2 -translate-y-1/2 z-10 transition-opacity', alwaysShowActions ? 'opacity-100' : 'opacity-0 group-hover/gh:opacity-100 group-focus-within/gh:opacity-100')}>
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -342,7 +342,7 @@ export const SortableProjectItem: React.FC<SortableProjectItemProps> = ({
showCreateButtons ? 'right-7' : 'right-0.5',
)}>
{showCreateButtons && isRepo && !hideDirectoryControls && onNewWorktreeSession ? (
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -398,7 +398,7 @@ export const SortableProjectItem: React.FC<SortableProjectItemProps> = ({
{showCreateButtons && onNewSession ? (
<div className="absolute right-0.5 top-1/2 z-10 -translate-y-1/2">
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -233,7 +233,7 @@ const QuickSessionAction = React.memo(function QuickSessionAction({
};
return (
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -90,7 +90,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
icon inset inside the 24px buttons so the first glyph lines up
with the New-session icon above (16px from the sidebar edge). */}
<div className="ml-[3px] flex items-center gap-1.5">
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -104,7 +104,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.addProject')}</p></TooltipContent>
</Tooltip>
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -118,7 +118,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.scheduledTasks')}</p></TooltipContent>
</Tooltip>
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -133,7 +133,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.newMultiRun')}</p></TooltipContent>
</Tooltip>
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -149,7 +149,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
</div>
<div className="flex items-center gap-1.5">
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -164,7 +164,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.searchSessions')}</p></TooltipContent>
</Tooltip>
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
@@ -186,7 +186,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
</Tooltip>
<DropdownMenu>
<Tooltip delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<DropdownMenuTrigger asChild>
<button