agent-ecosystem/src/renderer/components/sidebar/SessionContextMenu.tsx
matt 12a5bf46a8 feat(sessions): implement session hiding and un-hiding functionality
- Added handlers for hiding and unhiding individual and multiple sessions in the configuration.
- Updated the ConfigManager to manage hidden sessions, including methods for bulk operations.
- Enhanced the IPC channels and preload scripts to support new session visibility features.
- Integrated UI components to allow users to toggle session visibility in the sidebar and context menus.
- Updated state management to reflect hidden sessions and support multi-select actions for bulk hiding/unhiding.
2026-02-16 21:22:42 +09:00

139 lines
3.9 KiB
TypeScript

/**
* SessionContextMenu - Right-click context menu for sidebar session items.
* Supports opening in current pane, new tab, and split right.
* Shows keyboard shortcut hints for actions that have them.
*/
import { useEffect, useRef } from 'react';
import { MAX_PANES } from '@renderer/types/panes';
import { Eye, EyeOff, Pin, PinOff } from 'lucide-react';
interface SessionContextMenuProps {
x: number;
y: number;
sessionId: string;
projectId: string;
sessionLabel: string;
paneCount: number;
isPinned: boolean;
isHidden: boolean;
onClose: () => void;
onOpenInCurrentPane: () => void;
onOpenInNewTab: () => void;
onSplitRightAndOpen: () => void;
onTogglePin: () => void;
onToggleHide: () => void;
}
export const SessionContextMenu = ({
x,
y,
paneCount,
isPinned,
isHidden,
onClose,
onOpenInCurrentPane,
onOpenInNewTab,
onSplitRightAndOpen,
onTogglePin,
onToggleHide,
}: SessionContextMenuProps): React.JSX.Element => {
const menuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleMouseDown = (e: MouseEvent): void => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
onClose();
}
};
const handleKeyDown = (e: KeyboardEvent): void => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('mousedown', handleMouseDown);
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('mousedown', handleMouseDown);
document.removeEventListener('keydown', handleKeyDown);
};
}, [onClose]);
const menuWidth = 240;
const menuHeight = 204;
const clampedX = Math.min(x, window.innerWidth - menuWidth - 8);
const clampedY = Math.min(y, window.innerHeight - menuHeight - 8);
const handleClick = (action: () => void) => () => {
action();
onClose();
};
const atMaxPanes = paneCount >= MAX_PANES;
return (
<div
ref={menuRef}
className="fixed z-50 min-w-[220px] overflow-hidden rounded-md border py-1 shadow-lg"
style={{
left: clampedX,
top: clampedY,
backgroundColor: 'var(--color-surface-overlay)',
borderColor: 'var(--color-border-emphasis)',
color: 'var(--color-text)',
}}
>
<MenuItem label="Open in Current Pane" onClick={handleClick(onOpenInCurrentPane)} />
<MenuItem label="Open in New Tab" shortcut="⌘ Click" onClick={handleClick(onOpenInNewTab)} />
<div className="mx-2 my-1 border-t" style={{ borderColor: 'var(--color-border)' }} />
<MenuItem
label="Split Right and Open"
onClick={handleClick(onSplitRightAndOpen)}
disabled={atMaxPanes}
/>
<div className="mx-2 my-1 border-t" style={{ borderColor: 'var(--color-border)' }} />
<MenuItem
label={isPinned ? 'Unpin Session' : 'Pin Session'}
icon={isPinned ? <PinOff className="size-4" /> : <Pin className="size-4" />}
onClick={handleClick(onTogglePin)}
/>
<MenuItem
label={isHidden ? 'Unhide Session' : 'Hide Session'}
icon={isHidden ? <Eye className="size-4" /> : <EyeOff className="size-4" />}
onClick={handleClick(onToggleHide)}
/>
</div>
);
};
const MenuItem = ({
label,
shortcut,
icon,
onClick,
disabled,
}: {
label: string;
shortcut?: string;
icon?: React.ReactNode;
onClick: () => void;
disabled?: boolean;
}): React.JSX.Element => {
return (
<button
className="flex w-full items-center justify-between px-3 py-1.5 text-left text-sm transition-colors hover:bg-[var(--color-surface-raised)]"
onClick={onClick}
disabled={disabled}
style={{ opacity: disabled ? 0.4 : 1 }}
>
<span className="flex items-center gap-2">
{icon}
{label}
</span>
{shortcut && (
<span className="ml-4 text-xs" style={{ color: 'var(--color-text-muted)' }}>
{shortcut}
</span>
)}
</button>
);
};