- 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.
139 lines
3.9 KiB
TypeScript
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>
|
|
);
|
|
};
|