| ... | @@ -31,6 +31,7 @@ import { | ... | @@ -31,6 +31,7 @@ import { |
| 31 | useState, | 31 | useState, |
| 32 | } from "react"; | 32 | } from "react"; |
| 33 | import { Popover, PopoverPopup, PopoverTrigger } from "~/components/ui/popover"; | 33 | import { Popover, PopoverPopup, PopoverTrigger } from "~/components/ui/popover"; |
| | 34 | import { writeTextToClipboard } from "~/hooks/useCopyToClipboard"; |
| 34 | import { cn } from "~/lib/utils"; | 35 | import { cn } from "~/lib/utils"; |
| 35 | import { type TerminalContextSelection } from "~/lib/terminalContext"; | 36 | import { type TerminalContextSelection } from "~/lib/terminalContext"; |
| 36 | import { useOpenInPreferredEditor } from "../editorPreferences"; | 37 | import { useOpenInPreferredEditor } from "../editorPreferences"; |
| ... | @@ -330,7 +331,7 @@ export function TerminalViewport({ | ... | @@ -330,7 +331,7 @@ export function TerminalViewport({ |
| 330 | const selectionPointerRef = useRef<{ x: number; y: number } | null>(null); | 331 | const selectionPointerRef = useRef<{ x: number; y: number } | null>(null); |
| 331 | const selectionGestureActiveRef = useRef(false); | 332 | const selectionGestureActiveRef = useRef(false); |
| 332 | const selectionActionRequestIdRef = useRef(0); | 333 | const selectionActionRequestIdRef = useRef(0); |
| 333 | const selectionActionOpenRef = useRef(false); | 334 | const selectionActionMenuOpenRef = useRef(false); |
| 334 | const selectionActionTimerRef = useRef<number | null>(null); | 335 | const selectionActionTimerRef = useRef<number | null>(null); |
| 335 | const keybindingsRef = useRef(keybindings); | 336 | const keybindingsRef = useRef(keybindings); |
| 336 | const runtimeEnvKey = useMemo(() => runtimeEnvSignature(runtimeEnv), [runtimeEnv]); | 337 | const runtimeEnvKey = useMemo(() => runtimeEnvSignature(runtimeEnv), [runtimeEnv]); |
| ... | @@ -417,6 +418,7 @@ export function TerminalViewport({ | ... | @@ -417,6 +418,7 @@ export function TerminalViewport({ |
| 417 | | 418 | |
| 418 | const readSelectionAction = (): { | 419 | const readSelectionAction = (): { |
| 419 | position: { x: number; y: number }; | 420 | position: { x: number; y: number }; |
| | 421 | clipboardText: string; |
| 420 | selection: TerminalContextSelection; | 422 | selection: TerminalContextSelection; |
| 421 | } | null => { | 423 | } | null => { |
| 422 | const activeTerminal = terminalRef.current; | 424 | const activeTerminal = terminalRef.current; |
| ... | @@ -445,6 +447,7 @@ export function TerminalViewport({ | ... | @@ -445,6 +447,7 @@ export function TerminalViewport({ |
| 445 | }); | 447 | }); |
| 446 | return { | 448 | return { |
| 447 | position, | 449 | position, |
| | 450 | clipboardText: selectionText, |
| 448 | selection: { | 451 | selection: { |
| 449 | terminalId, | 452 | terminalId, |
| 450 | terminalLabel: readTerminalLabel(), | 453 | terminalLabel: readTerminalLabel(), |
| ... | @@ -460,7 +463,7 @@ export function TerminalViewport({ | ... | @@ -460,7 +463,7 @@ export function TerminalViewport({ |
| 460 | clearSelectionAction(); | 463 | clearSelectionAction(); |
| 461 | return; | 464 | return; |
| 462 | } | 465 | } |
| 463 | if (selectionActionOpenRef.current) { | 466 | if (selectionActionMenuOpenRef.current) { |
| 464 | return; | 467 | return; |
| 465 | } | 468 | } |
| 466 | const nextAction = readSelectionAction(); | 469 | const nextAction = readSelectionAction(); |
| ... | @@ -469,20 +472,46 @@ export function TerminalViewport({ | ... | @@ -469,20 +472,46 @@ export function TerminalViewport({ |
| 469 | return; | 472 | return; |
| 470 | } | 473 | } |
| 471 | const requestId = ++selectionActionRequestIdRef.current; | 474 | const requestId = ++selectionActionRequestIdRef.current; |
| 472 | selectionActionOpenRef.current = true; | 475 | selectionActionMenuOpenRef.current = true; |
| 473 | try { | 476 | const clicked = await localApi.contextMenu |
| 474 | const clicked = await localApi.contextMenu.show( | 477 | .show( |
| 475 | [{ id: "add-to-chat", label: "Add to chat" }], | 478 | [ |
| | 479 | { id: "add-to-chat", label: "Add to chat" }, |
| | 480 | { id: "copy", label: "Copy" }, |
| | 481 | ], |
| 476 | nextAction.position, | 482 | nextAction.position, |
| 477 | ); | 483 | ) |
| 478 | if (requestId !== selectionActionRequestIdRef.current || clicked !== "add-to-chat") { | 484 | .finally(() => { |
| | 485 | selectionActionMenuOpenRef.current = false; |
| | 486 | }); |
| | 487 | if (requestId !== selectionActionRequestIdRef.current || clicked === null) { |
| | 488 | return; |
| | 489 | } |
| | 490 | switch (clicked) { |
| | 491 | case "add-to-chat": |
| | 492 | handleAddTerminalContext(nextAction.selection); |
| | 493 | terminalRef.current?.clearSelection(); |
| | 494 | terminalRef.current?.focus(); |
| | 495 | return; |
| | 496 | case "copy": |
| | 497 | try { |
| | 498 | await writeTextToClipboard(nextAction.clipboardText, "terminal selection"); |
| | 499 | } catch (error) { |
| | 500 | if (requestId !== selectionActionRequestIdRef.current) { |
| | 501 | return; |
| | 502 | } |
| | 503 | const activeTerminal = terminalRef.current; |
| | 504 | if (activeTerminal) { |
| | 505 | writeSystemMessage( |
| | 506 | activeTerminal, |
| | 507 | error instanceof Error ? error.message : "Unable to copy terminal selection", |
| | 508 | ); |
| | 509 | } |
| | 510 | } |
| | 511 | if (requestId === selectionActionRequestIdRef.current) { |
| | 512 | terminalRef.current?.focus(); |
| | 513 | } |
| 479 | return; | 514 | return; |
| 480 | } | | |
| 481 | handleAddTerminalContext(nextAction.selection); | | |
| 482 | terminalRef.current?.clearSelection(); | | |
| 483 | terminalRef.current?.focus(); | | |
| 484 | } finally { | | |
| 485 | selectionActionOpenRef.current = false; | | |
| 486 | } | 515 | } |
| 487 | }; | 516 | }; |
| 488 | | 517 | |