69 lines
2.3 KiB
TypeScript
69 lines
2.3 KiB
TypeScript
import { useState, type ReactNode } from "react";
|
|
import { toast } from 'sonner';
|
|
import { sendToTerminal } from '@/lib/events';
|
|
import { useTerminals } from '@/hooks/useTerminals';
|
|
import {
|
|
ContextMenu,
|
|
ContextMenuContent,
|
|
ContextMenuItem,
|
|
ContextMenuSeparator,
|
|
ContextMenuSub,
|
|
ContextMenuSubContent,
|
|
ContextMenuSubTrigger,
|
|
ContextMenuTrigger,
|
|
} from '@/components/ui/context-menu';
|
|
|
|
// Wrap a message body with a right-click context menu offering Copy and
|
|
// "Send to terminal → <pane name>". Send is disabled when nothing is
|
|
// selected or no terminal panes are open; clicking a target emits a
|
|
// sendToTerminal event that TerminalPane subscribes to (filtered by pane_id).
|
|
export function SendToTerminalMenu({ children }: { children: ReactNode }) {
|
|
const [selection, setSelection] = useState('');
|
|
const terminals = useTerminals();
|
|
const hasSelection = selection.length > 0;
|
|
const canSend = hasSelection && terminals.length > 0;
|
|
|
|
return (
|
|
<ContextMenu
|
|
onOpenChange={(open) => {
|
|
if (open) {
|
|
const sel = typeof window !== 'undefined' ? window.getSelection()?.toString() ?? '' : '';
|
|
setSelection(sel);
|
|
}
|
|
}}
|
|
>
|
|
<ContextMenuTrigger asChild>{children}</ContextMenuTrigger>
|
|
<ContextMenuContent>
|
|
<ContextMenuItem
|
|
disabled={!hasSelection}
|
|
onSelect={() => {
|
|
void navigator.clipboard.writeText(selection).catch((err) => {
|
|
toast.error(err instanceof Error ? err.message : 'copy failed');
|
|
});
|
|
}}
|
|
>
|
|
Copy
|
|
</ContextMenuItem>
|
|
<ContextMenuSeparator />
|
|
<ContextMenuSub>
|
|
<ContextMenuSubTrigger disabled={!canSend}>Send to terminal</ContextMenuSubTrigger>
|
|
<ContextMenuSubContent>
|
|
{terminals.length === 0 ? (
|
|
<ContextMenuItem disabled>No terminal panes open</ContextMenuItem>
|
|
) : (
|
|
terminals.map((t) => (
|
|
<ContextMenuItem
|
|
key={t.paneId}
|
|
onSelect={() => sendToTerminal.emit({ pane_id: t.paneId, text: selection })}
|
|
>
|
|
{t.label}
|
|
</ContextMenuItem>
|
|
))
|
|
)}
|
|
</ContextMenuSubContent>
|
|
</ContextMenuSub>
|
|
</ContextMenuContent>
|
|
</ContextMenu>
|
|
);
|
|
}
|