From c114c0618755108e34503e9c68d243053b4116ff Mon Sep 17 00:00:00 2001 From: BlitzOS Upstream Prep Date: Wed, 2 Sep 2026 00:26:21 +0000 Subject: [PATCH] feat(components): add Copy file path to the desktop file viewer `MobileFileViewerDrawer` has carried this action since it landed, under `sessions.fileViewer.copyPath`, and `ui/diff-viewer/diff-file-header-actions.tsx` draws the same button with the same key. The desktop viewer has no way to get at the path it is showing. Add the button to the viewer toolbar, before the search control. The path is knowable for every file the viewer can show, binary previews included, so the control has no condition of its own beyond a non-empty path, and it joins `showViewerTopBar` so a file whose toolbar was otherwise empty still gets one. Adds `sessions.fileViewer.pathCopyFailed` beside the existing `pathCopied`. Model: claude-opus-5[1m] --- locales/en.json | 1 + locales/zh_CN.json | 1 + .../sessions/session-file-content-view.tsx | 29 ++++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 44b6924d7..e626ca477 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1411,6 +1411,7 @@ "sessions.fileViewer.openFile": "Open file", "sessions.fileViewer.path": "File path", "sessions.fileViewer.pathCopied": "File path copied", + "sessions.fileViewer.pathCopyFailed": "Failed to copy file path", "sessions.fileViewer.preview.show": "Preview", "sessions.fileViewer.preview.hide": "Hide preview", "sessions.fileViewer.refresh": "Refresh", diff --git a/locales/zh_CN.json b/locales/zh_CN.json index 34f61a95c..a29983c7b 100644 --- a/locales/zh_CN.json +++ b/locales/zh_CN.json @@ -1411,6 +1411,7 @@ "sessions.fileViewer.openFile": "打开文件", "sessions.fileViewer.path": "文件路径", "sessions.fileViewer.pathCopied": "文件路径已复制", + "sessions.fileViewer.pathCopyFailed": "复制文件路径失败", "sessions.fileViewer.preview.show": "预览", "sessions.fileViewer.preview.hide": "退出预览", "sessions.fileViewer.refresh": "刷新", diff --git a/packages/components/src/components/sessions/session-file-content-view.tsx b/packages/components/src/components/sessions/session-file-content-view.tsx index 559b2ad24..8429454b2 100644 --- a/packages/components/src/components/sessions/session-file-content-view.tsx +++ b/packages/components/src/components/sessions/session-file-content-view.tsx @@ -1,5 +1,6 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'; import { + ClipboardCopy, Copy, Eye, EyeClosed, @@ -892,6 +893,17 @@ function SessionFileContentViewImpl({ ); } }, [data, normalizedPath, tRef]); + // SP28: the desktop viewer had no way to get at the path it is showing. + // `MobileFileViewerDrawer` has carried this action since it landed, under the + // same `sessions.fileViewer.copyPath` key. + const handleCopyFilePath = useCallback(async () => { + const copied = await writeTextToClipboard(normalizedPath); + if (copied) { + toast.success(tRef.current('sessions.fileViewer.pathCopied', 'File path copied')); + } else { + toast.error(tRef.current('sessions.fileViewer.pathCopyFailed', 'Failed to copy file path')); + } + }, [normalizedPath, tRef]); const lastCopyMarkdownRequestSeqRef = useRef(copyMarkdownRequestSeq ?? 0); useEffect(() => { if ( @@ -1300,12 +1312,16 @@ function SessionFileContentViewImpl({ isTextFileReady && !showSvgRendered && !showMarkdownRendered && !showHtmlRendered; const showSaveButton = isProviderFileEditable && isTextFileReady; const showRefreshButton = shouldUseProviderFileContent && isTextFileReady; + // The path is knowable for every file the viewer can show, binary previews + // included, so this control has no condition of its own. + const showCopyPathButton = normalizedPath.length > 0; const showViewerTopBar = showPreviewToggle || isMarkdownTextFile || showSearchButton || showSaveButton || - showRefreshButton; + showRefreshButton || + showCopyPathButton; return (
@@ -1409,6 +1425,17 @@ function SessionFileContentViewImpl({