{
- if (open && narrow && event.key === 'Escape') {
+ if (open && overlay && event.key === 'Escape') {
event.preventDefault()
event.stopPropagation()
onOpenChange(false)
}
}}
>
- {narrow ?
+ {overlay ?
} size="xs" tone="muted" label={t('common.close')} data-testid="workspace-tree-overlay-close" onClick={() => onOpenChange(false)} />
: null}
-
setWidth(300)}
className="absolute inset-y-0 -left-1 w-2 cursor-col-resize outline-none focus-visible:bg-[var(--color-border-focus)]"
- />
+ /> : null}
{children}
)
diff --git a/desktop/src/components/workspace/WorkspaceFileOpenWith.test.tsx b/desktop/src/components/workspace/WorkspaceFileOpenWith.test.tsx
index 234f7c33..c966e8f8 100644
--- a/desktop/src/components/workspace/WorkspaceFileOpenWith.test.tsx
+++ b/desktop/src/components/workspace/WorkspaceFileOpenWith.test.tsx
@@ -211,4 +211,12 @@ describe('WorkspaceFileOpenWith', () => {
expect(openPreview).toHaveBeenCalledWith('s1', 'report.md')
})
+
+ it('omits file-only actions for directories, even with a file-like name', () => {
+ const { queryByRole, getByRole } = render(
)
+ expect(queryByRole('menuitem', { name: 'openWith.copyFileContent' })).toBeNull()
+ expect(queryByRole('menuitem', { name: 'openWith.workspacePreview' })).toBeNull()
+ expect(queryByRole('menuitem', { name: 'openWith.inAppBrowser' })).toBeNull()
+ expect(getByRole('menuitem', { name: 'openWith.copyPath' })).toBeInTheDocument()
+ })
})
diff --git a/desktop/src/components/workspace/WorkspaceFileOpenWith.tsx b/desktop/src/components/workspace/WorkspaceFileOpenWith.tsx
index 8cba9ddf..e8c54ba1 100644
--- a/desktop/src/components/workspace/WorkspaceFileOpenWith.tsx
+++ b/desktop/src/components/workspace/WorkspaceFileOpenWith.tsx
@@ -8,8 +8,10 @@ import { TargetIcon } from '@/components/composite/TargetIcon'
import type { OpenTarget } from '@/api/openTargets'
import { fileApplicationTargets, useWorkspaceFileOpenTargets } from '@/components/workspace/workspaceFileOpenTargets'
-export function WorkspaceFileOpenWith({ absolutePath, sessionId, workspacePath, onAfterSelect, targets: suppliedTargets, loading, error, onRefresh }: {
+export function WorkspaceFileOpenWith({ absolutePath, sessionId, workspacePath, onAfterSelect, targets: suppliedTargets, loading, error, onRefresh, isDirectory = false, onPreview }: {
absolutePath: string
+ isDirectory?: boolean
+ onPreview?: () => void
sessionId?: string
workspacePath?: string
onAfterSelect?: () => void
@@ -22,7 +24,7 @@ export function WorkspaceFileOpenWith({ absolutePath, sessionId, workspacePath,
const discovery = useWorkspaceFileOpenTargets(suppliedTargets === undefined ? absolutePath : null)
const targets = suppliedTargets ?? discovery.targets
const applications = fileApplicationTargets(absolutePath, targets)
- const context = sessionId && workspacePath
+ const context = !isDirectory && sessionId && workspacePath
? openWithContextForWorkspaceFile(workspacePath, absolutePath, { sessionId, serverBaseUrl: getServerBaseUrl() })
: { kind: 'file' as const, absolutePath, previewable: false }
const actions = buildOpenWithMenuItems(context, targets, {
@@ -30,7 +32,9 @@ export function WorkspaceFileOpenWith({ absolutePath, sessionId, workspacePath,
}).filter((item) => item.icon === 'copy' || item.id === 'in-app' || item.id === 'preview')
// Previewing again inside an already-open file has no effect. HTML browser
// and clipboard actions remain available because they have distinct results.
- const usefulActions = actions.filter((item) => item.id !== 'preview' || suppliedTargets === undefined)
+ const usefulActions = actions.filter((item) =>
+ (!isDirectory || item.id === 'copy-path') && (item.id !== 'preview' || suppliedTargets === undefined),
+ )
const folders = targets.filter((target) => target.kind === 'file_manager')
const selectTarget = (target: OpenTarget) => {
void useOpenTargetStore.getState().openTarget(target.id, absolutePath).catch(() => reportOpenFailure(absolutePath))
@@ -50,7 +54,7 @@ export function WorkspaceFileOpenWith({ absolutePath, sessionId, workspacePath,
))}
{applications.length > 0 && (folders.length > 0 || usefulActions.length > 0 || onRefresh) ?
: null}
{folders.map((target) =>
)}
- {usefulActions.map((item) =>
)}
+ {usefulActions.map((item) =>
)}
{onRefresh ?
: null}
>
)
diff --git a/desktop/src/i18n/locales/en.ts b/desktop/src/i18n/locales/en.ts
index e7e10656..85612e4f 100644
--- a/desktop/src/i18n/locales/en.ts
+++ b/desktop/src/i18n/locales/en.ts
@@ -703,6 +703,8 @@ export const en = {
'workspace.files.projectRoot': 'Project root',
'workspace.files.directoryMissing': 'Directory not found: {path}',
'workspace.files.directoryError': 'Unable to read directory: {path}',
+ 'workspace.files.hidePreview': 'Hide preview',
+ 'workspace.files.showPreview': 'Show preview',
'workspace.files.toggleTree': 'Toggle file tree',
'workspace.files.breadcrumb': 'File path',
'workspace.files.noSelection': 'No file selected',
diff --git a/desktop/src/i18n/locales/jp.ts b/desktop/src/i18n/locales/jp.ts
index 9086488c..3560d095 100644
--- a/desktop/src/i18n/locales/jp.ts
+++ b/desktop/src/i18n/locales/jp.ts
@@ -705,6 +705,8 @@ export const jp: Record
= {
'workspace.files.projectRoot': 'プロジェクトルート',
'workspace.files.directoryMissing': 'ディレクトリが見つかりません: {path}',
'workspace.files.directoryError': 'ディレクトリを読み込めません: {path}',
+ 'workspace.files.hidePreview': 'プレビューを非表示',
+ 'workspace.files.showPreview': 'プレビューを表示',
'workspace.files.toggleTree': 'ファイルツリーの表示切り替え',
'workspace.files.breadcrumb': 'ファイルパス',
'workspace.files.noSelection': 'ファイルが選択されていません',
diff --git a/desktop/src/i18n/locales/kr.ts b/desktop/src/i18n/locales/kr.ts
index 21b6ece2..4ca32604 100644
--- a/desktop/src/i18n/locales/kr.ts
+++ b/desktop/src/i18n/locales/kr.ts
@@ -707,6 +707,8 @@ export const kr: Record = {
'workspace.files.projectRoot': '프로젝트 루트',
'workspace.files.directoryMissing': '디렉터리를 찾을 수 없습니다: {path}',
'workspace.files.directoryError': '디렉터리를 읽을 수 없습니다: {path}',
+ 'workspace.files.hidePreview': '미리보기 숨기기',
+ 'workspace.files.showPreview': '미리보기 표시',
'workspace.files.toggleTree': '파일 트리 표시 전환',
'workspace.files.breadcrumb': '파일 경로',
'workspace.files.noSelection': '선택된 파일 없음',
diff --git a/desktop/src/i18n/locales/zh-TW.ts b/desktop/src/i18n/locales/zh-TW.ts
index 5c3fc548..b82eec6a 100644
--- a/desktop/src/i18n/locales/zh-TW.ts
+++ b/desktop/src/i18n/locales/zh-TW.ts
@@ -705,6 +705,8 @@ export const zh: Record = {
'workspace.files.projectRoot': '專案根目錄',
'workspace.files.directoryMissing': '目錄不存在:{path}',
'workspace.files.directoryError': '無法讀取目錄:{path}',
+ 'workspace.files.hidePreview': '隱藏預覽',
+ 'workspace.files.showPreview': '顯示預覽',
'workspace.files.toggleTree': '顯示/隱藏檔案樹',
'workspace.files.breadcrumb': '檔案路徑',
'workspace.files.noSelection': '未選擇檔案',
diff --git a/desktop/src/i18n/locales/zh.ts b/desktop/src/i18n/locales/zh.ts
index 4840cb8a..b0c114e0 100644
--- a/desktop/src/i18n/locales/zh.ts
+++ b/desktop/src/i18n/locales/zh.ts
@@ -704,6 +704,8 @@ export const zh: Record = {
'workspace.files.projectRoot': '项目根目录',
'workspace.files.directoryMissing': '目录不存在:{path}',
'workspace.files.directoryError': '无法读取目录:{path}',
+ 'workspace.files.hidePreview': '隐藏预览',
+ 'workspace.files.showPreview': '显示预览',
'workspace.files.toggleTree': '显示/隐藏文件树',
'workspace.files.breadcrumb': '文件路径',
'workspace.files.noSelection': '未选择文件',