diff --git a/src/renderer/src/components/link-actions/LinkActionPopover.test.tsx b/src/renderer/src/components/link-actions/LinkActionPopover.test.tsx index 39b1f283a6a..475c05fdbc7 100644 --- a/src/renderer/src/components/link-actions/LinkActionPopover.test.tsx +++ b/src/renderer/src/components/link-actions/LinkActionPopover.test.tsx @@ -254,13 +254,13 @@ describe('LinkActionPopover', () => { await waitFor(() => expect(mocks.writeClipboardText).toHaveBeenCalledTimes(2)) }) - it('does not offer copy link for non-URL destinations', () => { + it('does not offer copy actions for non-copyable destinations', () => { vi.stubGlobal('navigator', { userAgent: 'Macintosh' }) const request: LinkActionRequest = { anchorX: 100, anchorY: 200, destination: '/tmp/example.ts', - kind: 'file', + kind: 'workspace', primary: { label: 'Open file', run: vi.fn() }, restoreFocus: vi.fn() } @@ -268,6 +268,31 @@ describe('LinkActionPopover', () => { render() expect(screen.queryByRole('button', { name: 'Copy link' })).toBeNull() + expect(screen.queryByRole('button', { name: 'Copy file path' })).toBeNull() + }) + + it('copies the resolved file path without closing the popover', async () => { + vi.stubGlobal('navigator', { userAgent: 'Macintosh' }) + Object.assign(window, { api: { ui: { writeClipboardText: mocks.writeClipboardText } } }) + mocks.writeClipboardText.mockResolvedValue(undefined) + const onClose = vi.fn() + const request: LinkActionRequest = { + anchorX: 100, + anchorY: 200, + destination: 'file:///repo/src/main.ts', + copyValue: '/repo/src/main.ts', + kind: 'file', + primary: { label: 'Open file', run: vi.fn() }, + restoreFocus: vi.fn() + } + + render() + fireEvent.click(screen.getByRole('button', { name: 'Copy file path' })) + + await waitFor(() => expect(mocks.writeClipboardText).toHaveBeenCalledWith('/repo/src/main.ts')) + await waitFor(() => expect(screen.getByRole('button', { name: 'Copied' })).toBeTruthy()) + expect(mocks.toastSuccess).toHaveBeenCalledWith('Copied file path') + expect(onClose).not.toHaveBeenCalled() }) it('opens the terminal link setting from the compact settings button', () => { diff --git a/src/renderer/src/components/link-actions/LinkActionPopover.tsx b/src/renderer/src/components/link-actions/LinkActionPopover.tsx index 29338b208f9..8b787c56e4b 100644 --- a/src/renderer/src/components/link-actions/LinkActionPopover.tsx +++ b/src/renderer/src/components/link-actions/LinkActionPopover.tsx @@ -61,7 +61,11 @@ export function LinkActionPopover({ }: LinkActionPopoverProps): React.JSX.Element { const openSettingsPage = useAppStore((state) => state.openSettingsPage) const openSettingsTarget = useAppStore((state) => state.openSettingsTarget) - const copyableDestination = request?.kind === 'url' ? request.destination : '' + const isFilePath = request?.kind === 'file' + const canCopyDestination = isFilePath || request?.kind === 'url' + const copyableDestination = canCopyDestination + ? (request?.copyValue ?? request?.destination ?? '') + : '' const { copyText, status: copyStatus } = useClipboardTextCopyFeedback(copyableDestination) const copyInFlightRef = useRef(false) const contentRef = useRef(null) @@ -103,7 +107,28 @@ export function LinkActionPopover({ const copyLabel = copyStatus === 'copied' ? translate('auto.components.terminal.pane.TerminalLinkActionPopover.copied', 'Copied') - : translate('auto.components.terminal.pane.TerminalLinkActionPopover.copyLink', 'Copy link') + : isFilePath + ? translate( + 'auto.components.terminal.pane.TerminalLinkActionPopover.copyFilePath', + 'Copy file path' + ) + : translate('auto.components.terminal.pane.TerminalLinkActionPopover.copyLink', 'Copy link') + + const copiedMessage = isFilePath + ? translate( + 'auto.components.terminal.pane.TerminalLinkActionPopover.copiedFilePath', + 'Copied file path' + ) + : translate('auto.components.terminal.pane.TerminalLinkActionPopover.copiedLink', 'Copied link') + const copyFailedMessage = isFilePath + ? translate( + 'auto.components.terminal.pane.TerminalLinkActionPopover.copyFilePathFailed', + 'Failed to copy file path' + ) + : translate( + 'auto.components.terminal.pane.TerminalLinkActionPopover.copyLinkFailed', + 'Failed to copy link' + ) const copyDestination = async (): Promise => { if (copyInFlightRef.current) { @@ -112,20 +137,10 @@ export function LinkActionPopover({ copyInFlightRef.current = true try { if (await copyText()) { - toast.success( - translate( - 'auto.components.terminal.pane.TerminalLinkActionPopover.copiedLink', - 'Copied link' - ) - ) + toast.success(copiedMessage) return } - toast.error( - translate( - 'auto.components.terminal.pane.TerminalLinkActionPopover.copyLinkFailed', - 'Failed to copy link' - ) - ) + toast.error(copyFailedMessage) } finally { copyInFlightRef.current = false } @@ -170,7 +185,7 @@ export function LinkActionPopover({ > {request.destination} - {request.kind === 'url' ? ( + {canCopyDestination ? (