file view container's context menus keyed (97 -> 80)
The folder right-click menu and the search-results menu: copy path, chat about this, paste, new file/folder, upload, clone, hidden-files toggle, create dashboard, download video, dictate, open folder, show location, and the drop-to-upload overlay. "Hide hidden files" / "Show hidden files" was a ternary inside JSX, which the scanner's own docs say it cannot see — found by reading the file rather than by the tool. Two keys, one per branch, since English and Portuguese verbs differ by more than a negation. files.menu.* is a new sub-namespace under the existing files.* group rather than a flat name, since a bare "download" would otherwise collide with nothing today but reads as ambiguous next to files.downloadFailed.
This commit is contained in:
+22
-18
@@ -29,12 +29,14 @@ import {
|
||||
import type { UseFileBrowserAppType } from '../useFileBrowserApp';
|
||||
import { NewEntryDialog } from './NewEntryDialog';
|
||||
import { FileGrid } from './FileGrid';
|
||||
import { useTranslation } from '../../../../locales';
|
||||
|
||||
type FileViewContainerProps = {
|
||||
fileBrowserManager: UseFileBrowserAppType;
|
||||
};
|
||||
|
||||
export const FileViewContainer = ({ fileBrowserManager }: FileViewContainerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
searchQuery,
|
||||
searching,
|
||||
@@ -85,11 +87,11 @@ export const FileViewContainer = ({ fileBrowserManager }: FileViewContainerProps
|
||||
<ContextMenuContent className="z-[600]">
|
||||
<ContextMenuItem onClick={handleCopyCurrentPath} className="cursor-pointer">
|
||||
<ClipboardCopy className="mr-2 h-4 w-4" />
|
||||
Copy path
|
||||
{t('files.menu.copyPath')}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem onClick={handleChatHere} className="cursor-pointer">
|
||||
<MessageSquare className="mr-2 h-4 w-4" />
|
||||
Chat about this
|
||||
{t('files.menu.chatAboutThis')}
|
||||
</ContextMenuItem>
|
||||
{/* Gated on the clipboard, as both toolbars already were. Ungated it fell through to the
|
||||
OS clipboard path, which on an insecure origin toasts an error and otherwise silently
|
||||
@@ -97,7 +99,7 @@ export const FileViewContainer = ({ fileBrowserManager }: FileViewContainerProps
|
||||
{clipboard !== null && (
|
||||
<ContextMenuItem onClick={handlePaste} className="cursor-pointer">
|
||||
<ClipboardPaste className="mr-2 h-4 w-4" />
|
||||
Paste
|
||||
{t('files.menu.paste')}
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
{/* Both open the same dialog. It validates as you type — against the names already on screen —
|
||||
@@ -105,43 +107,43 @@ export const FileViewContainer = ({ fileBrowserManager }: FileViewContainerProps
|
||||
rejected request. The server still refuses to clobber; this is the courtesy, that is the lock. */}
|
||||
<ContextMenuItem onClick={() => setNewEntry('file')} className="cursor-pointer">
|
||||
<FilePlus className="mr-2 h-4 w-4" />
|
||||
New file
|
||||
{t('files.menu.newFile')}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem onClick={() => setNewEntry('folder')} className="cursor-pointer">
|
||||
<FolderPlus className="mr-2 h-4 w-4" />
|
||||
New folder
|
||||
{t('files.menu.newFolder')}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem onClick={() => fileInputRef.current?.click()} className="cursor-pointer">
|
||||
<Upload className="mr-2 h-4 w-4" />
|
||||
Upload files
|
||||
{t('files.menu.uploadFiles')}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem onClick={() => folderInputRef.current?.click()} className="cursor-pointer">
|
||||
<FolderUp className="mr-2 h-4 w-4" />
|
||||
Upload folder
|
||||
{t('files.menu.uploadFolder')}
|
||||
</ContextMenuItem>
|
||||
{/* Was toolbar-only, and the toolbar hides it under `md:` — so on a phone there was no way
|
||||
to clone at all. */}
|
||||
<ContextMenuItem onClick={() => setNewEntry('clone')} className="cursor-pointer">
|
||||
<GitBranch className="mr-2 h-4 w-4" />
|
||||
Clone repository
|
||||
{t('files.menu.cloneRepository')}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem onClick={() => setShowHidden(!showHidden)} className="cursor-pointer">
|
||||
{showHidden ? <EyeOff className="mr-2 h-4 w-4" /> : <Eye className="mr-2 h-4 w-4" />}
|
||||
{showHidden ? 'Hide hidden files' : 'Show hidden files'}
|
||||
{showHidden ? t('files.menu.hideHiddenFiles') : t('files.menu.showHiddenFiles')}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem onClick={handleCreateDashboardHere} className="cursor-pointer">
|
||||
<LayoutGrid className="mr-2 h-4 w-4" />
|
||||
Create Dashboard here
|
||||
{t('files.menu.createDashboardHere')}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem onClick={() => setShowVideoDownload(true)} className="cursor-pointer">
|
||||
<Download className="mr-2 h-4 w-4" />
|
||||
Download video
|
||||
{t('files.menu.downloadVideo')}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem onClick={() => setShowDictate(true)} className="cursor-pointer">
|
||||
<Mic className="mr-2 h-4 w-4" />
|
||||
Dictate
|
||||
{t('files.menu.dictate')}
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
);
|
||||
@@ -158,7 +160,7 @@ export const FileViewContainer = ({ fileBrowserManager }: FileViewContainerProps
|
||||
<div className="absolute inset-0 z-50 flex items-center justify-center bg-duck-teal/10 border-2 border-dashed border-duck-teal rounded-lg m-2 pointer-events-none">
|
||||
<div className="flex flex-col items-center gap-2 text-duck-teal">
|
||||
<Upload className="h-8 w-8" />
|
||||
<span className="text-sm font-medium">Drop files to upload</span>
|
||||
<span className="text-sm font-medium">{t('files.menu.dropToUpload')}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -213,23 +215,23 @@ export const FileViewContainer = ({ fileBrowserManager }: FileViewContainerProps
|
||||
{isDir && (
|
||||
<ContextMenuItem onClick={() => handleSearchResultClick(entry)} className="cursor-pointer">
|
||||
<FolderOpen className="mr-2 h-4 w-4" />
|
||||
Open folder
|
||||
{t('files.menu.openFolder')}
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
<ContextMenuItem onClick={() => handleShowLocation(entry)} className="cursor-pointer">
|
||||
<FolderSearch className="mr-2 h-4 w-4" />
|
||||
Show location
|
||||
{t('files.menu.showLocation')}
|
||||
</ContextMenuItem>
|
||||
{absPath && (
|
||||
<ContextMenuItem onClick={() => handleCopyAbsPath(absPath)} className="cursor-pointer">
|
||||
<ClipboardCopy className="mr-2 h-4 w-4" />
|
||||
Copy path
|
||||
{t('files.menu.copyPath')}
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
{absPath && !isDir && (
|
||||
<ContextMenuItem onClick={() => handleDownloadPath(absPath)} className="cursor-pointer">
|
||||
<Download className="mr-2 h-4 w-4" />
|
||||
Download
|
||||
{t('files.menu.download')}
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
</ContextMenuContent>
|
||||
@@ -238,7 +240,9 @@ export const FileViewContainer = ({ fileBrowserManager }: FileViewContainerProps
|
||||
})}
|
||||
</div>
|
||||
) : searchResults ? (
|
||||
<div className="flex items-center justify-center py-20 text-duck-dark/40 text-sm">No results found</div>
|
||||
<div className="flex items-center justify-center py-20 text-duck-dark/40 text-sm">
|
||||
{t('files.menu.noResults')}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</ContextMenuTrigger>
|
||||
|
||||
@@ -484,7 +484,27 @@
|
||||
"duplicated": "Duplicated “{{name}}”",
|
||||
"pastedInto": "Pasted into “{{name}}”",
|
||||
"unpinned": "Unpinned “{{name}}”",
|
||||
"pinned": "Pinned “{{name}}”"
|
||||
"pinned": "Pinned “{{name}}”",
|
||||
"menu": {
|
||||
"copyPath": "Copy path",
|
||||
"chatAboutThis": "Chat about this",
|
||||
"paste": "Paste",
|
||||
"newFile": "New file",
|
||||
"newFolder": "New folder",
|
||||
"uploadFiles": "Upload files",
|
||||
"uploadFolder": "Upload folder",
|
||||
"cloneRepository": "Clone repository",
|
||||
"hideHiddenFiles": "Hide hidden files",
|
||||
"showHiddenFiles": "Show hidden files",
|
||||
"createDashboardHere": "Create Dashboard here",
|
||||
"downloadVideo": "Download video",
|
||||
"dictate": "Dictate",
|
||||
"dropToUpload": "Drop files to upload",
|
||||
"openFolder": "Open folder",
|
||||
"showLocation": "Show location",
|
||||
"download": "Download",
|
||||
"noResults": "No results found"
|
||||
}
|
||||
},
|
||||
"taskRunner": {
|
||||
"stop": "Stop",
|
||||
|
||||
@@ -484,7 +484,27 @@
|
||||
"duplicated": "“{{name}}” duplicado",
|
||||
"pastedInto": "Colado em “{{name}}”",
|
||||
"unpinned": "“{{name}}” removido dos fixados",
|
||||
"pinned": "“{{name}}” fixado"
|
||||
"pinned": "“{{name}}” fixado",
|
||||
"menu": {
|
||||
"copyPath": "Copiar caminho",
|
||||
"chatAboutThis": "Conversar sobre isto",
|
||||
"paste": "Colar",
|
||||
"newFile": "Novo ficheiro",
|
||||
"newFolder": "Nova pasta",
|
||||
"uploadFiles": "Carregar ficheiros",
|
||||
"uploadFolder": "Carregar pasta",
|
||||
"cloneRepository": "Clonar repositório",
|
||||
"hideHiddenFiles": "Ocultar ficheiros ocultos",
|
||||
"showHiddenFiles": "Mostrar ficheiros ocultos",
|
||||
"createDashboardHere": "Criar painel aqui",
|
||||
"downloadVideo": "Descarregar vídeo",
|
||||
"dictate": "Ditar",
|
||||
"dropToUpload": "Largue ficheiros para carregar",
|
||||
"openFolder": "Abrir pasta",
|
||||
"showLocation": "Mostrar localização",
|
||||
"download": "Descarregar",
|
||||
"noResults": "Nenhum resultado encontrado"
|
||||
}
|
||||
},
|
||||
"taskRunner": {
|
||||
"stop": "Parar",
|
||||
|
||||
Reference in New Issue
Block a user