fix(files): hide desktop-only reveal action and label download in browser clients

Reveal-in-file-manager was always offered whenever the server exposed
revealPath, including in a plain browser tab where there is no local
file manager to reveal into. Gate it behind a new isBrowserClientRuntime
check (web platform, no Electron shell) and relabel the save action to
"Download" for that case, since it triggers a browser-style file
download rather than an in-place save.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Serhii Dziupin
2026-08-03 17:11:41 +03:00
co-authored by Claude Sonnet 5
parent 166b89d8db
commit f02969548a
14 changed files with 63 additions and 13 deletions
+15
View File
@@ -0,0 +1,15 @@
import { describe, expect, test } from 'bun:test';
import { isBrowserClientRuntime } from './desktop';
describe('browser client runtime', () => {
test('uses browser file behavior only outside the Electron shell', () => {
expect(isBrowserClientRuntime('web', false)).toBe(true);
expect(isBrowserClientRuntime('web', true)).toBe(false);
});
test('keeps desktop and VS Code runtime behavior out of browser-only flows', () => {
expect(isBrowserClientRuntime('desktop', false)).toBe(false);
expect(isBrowserClientRuntime('vscode', false)).toBe(false);
});
});
+10 -1
View File
@@ -1,4 +1,4 @@
import type { ProjectEntry, TerminalShell } from '@/lib/api/types';
import type { ProjectEntry, RuntimeAPIs, TerminalShell } from '@/lib/api/types';
import { getInjectedBootOutcome } from '@/lib/desktopBoot';
import type { DraftStarterRef } from '@/lib/draftStarters';
import type { MobileKeyboardMode } from '@/lib/mobileKeyboardMode';
@@ -562,6 +562,15 @@ export const isWebRuntime = (): boolean => {
return !isVSCodeRuntime();
};
/**
* Electron reuses the web RuntimeAPIs implementation, so distinguish a browser
* client from an Electron renderer with both the runtime descriptor and shell.
*/
export const isBrowserClientRuntime = (
platform: RuntimeAPIs['runtime']['platform'],
desktopShell = isDesktopShell(),
): boolean => platform === 'web' && !desktopShell;
export const getDesktopHomeDirectory = async (): Promise<string | null> => {
if (typeof window !== 'undefined') {
const embedded = window.__OPENCHAMBER_HOME__;
+1
View File
@@ -1225,6 +1225,7 @@ export const dict = {
'sidebarFilesTree.menu.rename': 'Rename',
'sidebarFilesTree.menu.copyPath': 'Copy Path',
'sidebarFilesTree.menu.save': 'Save',
'sidebarFilesTree.menu.download': 'Download',
'sidebarFilesTree.menu.newFile': 'New File',
'sidebarFilesTree.menu.newFolder': 'New Folder',
'sidebarFilesTree.menu.delete': 'Delete',
+1
View File
@@ -1191,6 +1191,7 @@ export const dict: Record<I18nKey, string> = {
"sidebarFilesTree.menu.rename": "Cambiar nombre",
"sidebarFilesTree.menu.copyPath": "Copiar ruta",
"sidebarFilesTree.menu.save": "Guardar",
"sidebarFilesTree.menu.download": "Descargar",
"sidebarFilesTree.menu.newFile": "Nuevo archivo",
"sidebarFilesTree.menu.newFolder": "Nueva carpeta",
"sidebarFilesTree.menu.delete": "Eliminar",
+1
View File
@@ -1047,6 +1047,7 @@ export const dict = {
'sidebarFilesTree.menu.rename': 'Rebaptiser',
'sidebarFilesTree.menu.copyPath': 'Copier le chemin',
'sidebarFilesTree.menu.save': 'Sauvegarder',
'sidebarFilesTree.menu.download': 'Télécharger',
'sidebarFilesTree.menu.newFile': 'Nouveau fichier',
'sidebarFilesTree.menu.newFolder': 'Nouveau dossier',
'sidebarFilesTree.menu.delete': 'Supprimer',
+1
View File
@@ -1221,6 +1221,7 @@ export const dict: Record<I18nKey, string> = {
'sidebarFilesTree.menu.rename': '名前の変更',
'sidebarFilesTree.menu.copyPath': 'パスをコピー',
'sidebarFilesTree.menu.save': '保存',
'sidebarFilesTree.menu.download': 'ダウンロード',
'sidebarFilesTree.menu.newFile': '新しいファイル',
'sidebarFilesTree.menu.newFolder': '新しいフォルダ',
'sidebarFilesTree.menu.delete': '削除',
+1
View File
@@ -1228,6 +1228,7 @@ export const dict: Record<I18nKey, string> = {
'sidebarFilesTree.menu.rename': '이름 변경',
'sidebarFilesTree.menu.copyPath': '경로 복사',
'sidebarFilesTree.menu.save': '저장',
'sidebarFilesTree.menu.download': '다운로드',
'sidebarFilesTree.menu.newFile': '새 파일',
'sidebarFilesTree.menu.newFolder': '새 폴더',
'sidebarFilesTree.menu.delete': '삭제',
+1
View File
@@ -2708,6 +2708,7 @@ export const dict: Record<I18nKey, string> = {
'sidebarFilesTree.menu.newFolder': 'Nowy folder',
'sidebarFilesTree.menu.rename': 'Zmień nazwę',
'sidebarFilesTree.menu.save': 'Zapisz',
'sidebarFilesTree.menu.download': 'Pobierz',
'sidebarFilesTree.search.clearAria': 'Wyczyść wyszukiwanie',
'sidebarFilesTree.search.placeholder': 'Szukaj plików...',
'sidebarFilesTree.state.loading': 'Ładowanie...',
@@ -1191,6 +1191,7 @@ export const dict: Record<I18nKey, string> = {
"sidebarFilesTree.menu.rename": "Renomear",
"sidebarFilesTree.menu.copyPath": "Copiar caminho",
"sidebarFilesTree.menu.save": "Salvar",
"sidebarFilesTree.menu.download": "Baixar",
"sidebarFilesTree.menu.newFile": "Novo arquivo",
"sidebarFilesTree.menu.newFolder": "Nova pasta",
"sidebarFilesTree.menu.delete": "Excluir",
+1
View File
@@ -1191,6 +1191,7 @@ export const dict: Record<I18nKey, string> = {
"sidebarFilesTree.menu.rename": "Перейменувати",
"sidebarFilesTree.menu.copyPath": "Копіювати шлях",
"sidebarFilesTree.menu.save": "Зберегти",
"sidebarFilesTree.menu.download": "Завантажити",
"sidebarFilesTree.menu.newFile": "Новий файл",
"sidebarFilesTree.menu.newFolder": "Нова папка",
"sidebarFilesTree.menu.delete": "Видалити",
@@ -1191,6 +1191,7 @@ export const dict: Record<I18nKey, string> = {
'sidebarFilesTree.menu.rename': '重命名',
'sidebarFilesTree.menu.copyPath': '复制路径',
'sidebarFilesTree.menu.save': '保存',
'sidebarFilesTree.menu.download': '下载',
'sidebarFilesTree.menu.newFile': '新建文件',
'sidebarFilesTree.menu.newFolder': '新建文件夹',
'sidebarFilesTree.menu.delete': '删除',
@@ -1203,6 +1203,7 @@ export const dict: Record<I18nKey, string> = {
'sidebarFilesTree.menu.rename': '重新命名',
'sidebarFilesTree.menu.copyPath': '複製路徑',
'sidebarFilesTree.menu.save': '儲存',
'sidebarFilesTree.menu.download': '下載',
'sidebarFilesTree.menu.newFile': '新增檔案',
'sidebarFilesTree.menu.newFolder': '新增資料夾',
'sidebarFilesTree.menu.delete': '刪除',