drop the built-in Extract Text (OCR) entry from the context menu

OCR now exists as a task, with recursion, multi-select scoping and a choice of
inline or job. The file viewer's OCR button is untouched, and /file-browser/ocr
stays — it is what both that button and the task call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
brunorezio
2026-07-26 04:08:24 +01:00
co-authored by Claude Opus 5
parent 49b4773457
commit b6b9e21b72
3 changed files with 2 additions and 39 deletions
@@ -61,7 +61,6 @@ export const FileGrid = ({ fileBrowserManager }: FileGridProps) => {
renamingName, renamingName,
setRenamingName, setRenamingName,
handleReadAloud, handleReadAloud,
handleOcr,
handleExtract, handleExtract,
handlePlay, handlePlay,
getMatchingTaskGroups, getMatchingTaskGroups,
@@ -178,7 +177,6 @@ export const FileGrid = ({ fileBrowserManager }: FileGridProps) => {
forceRename={renamingName === entry.name} forceRename={renamingName === entry.name}
onRenamingChange={setRenamingName} onRenamingChange={setRenamingName}
onReadAloud={handleReadAloud} onReadAloud={handleReadAloud}
onOcr={handleOcr}
onExtract={handleExtract} onExtract={handleExtract}
onPlay={handlePlay} onPlay={handlePlay}
taskGroups={getMatchingTaskGroups(entry.name, entry.type)} taskGroups={getMatchingTaskGroups(entry.name, entry.type)}
@@ -12,7 +12,6 @@ import {
Download, Download,
LayoutGrid, LayoutGrid,
Volume2, Volume2,
ScanText,
FolderArchive, FolderArchive,
ClipboardCopy, ClipboardCopy,
Music, Music,
@@ -62,7 +61,6 @@ export type FileItemProps = {
forceRename: boolean; forceRename: boolean;
onRenamingChange: (name: string | null) => void; onRenamingChange: (name: string | null) => void;
onReadAloud: (entry: DirEntry) => void; onReadAloud: (entry: DirEntry) => void;
onOcr: (entry: DirEntry) => void;
onExtract: (entry: DirEntry) => void; onExtract: (entry: DirEntry) => void;
onPlay: (entry: DirEntry) => void; onPlay: (entry: DirEntry) => void;
taskGroups: TaskGroup[]; taskGroups: TaskGroup[];
@@ -93,7 +91,6 @@ type MenuItemsProps = {
onCopyPath: (e: DirEntry) => void; onCopyPath: (e: DirEntry) => void;
onDownload: (e: DirEntry) => void; onDownload: (e: DirEntry) => void;
onReadAloud: (e: DirEntry) => void; onReadAloud: (e: DirEntry) => void;
onOcr: (e: DirEntry) => void;
onExtract: (e: DirEntry) => void; onExtract: (e: DirEntry) => void;
onPlay: (e: DirEntry) => void; onPlay: (e: DirEntry) => void;
onCut: () => void; onCut: () => void;
@@ -112,7 +109,6 @@ const DropdownMenuItems = ({
onCopyPath, onCopyPath,
onDownload, onDownload,
onReadAloud, onReadAloud,
onOcr,
onExtract, onExtract,
onPlay, onPlay,
onCut, onCut,
@@ -123,12 +119,11 @@ const DropdownMenuItems = ({
}: MenuItemsProps) => { }: MenuItemsProps) => {
const fileType = entry.type === 'file' ? getFileType(entry.name) : null; const fileType = entry.type === 'file' ? getFileType(entry.name) : null;
const showReadAloud = fileType === 'markdown' || fileType === 'code' || fileType === 'text'; const showReadAloud = fileType === 'markdown' || fileType === 'code' || fileType === 'text';
const showOcr = fileType === 'image';
const showExtract = fileType === 'archive'; const showExtract = fileType === 'archive';
const showPlay = fileType === 'audio' || entry.type === 'directory'; const showPlay = fileType === 'audio' || entry.type === 'directory';
const hasTasks = taskGroups.length > 0; const hasTasks = taskGroups.length > 0;
const hasActions = showPlay || showReadAloud || showOcr || showExtract || hasTasks; const hasActions = showPlay || showReadAloud || showExtract || hasTasks;
// Only nest when more than one category matched — a file usually matches a single category, and // Only nest when more than one category matched — a file usually matches a single category, and
// Run Task > Video > Convert would just add a hop. // Run Task > Video > Convert would just add a hop.
const nestTasks = taskGroups.length > 1; const nestTasks = taskGroups.length > 1;
@@ -148,12 +143,6 @@ const DropdownMenuItems = ({
Read Aloud Read Aloud
</DropdownMenuItem> </DropdownMenuItem>
)} )}
{showOcr && (
<DropdownMenuItem onClick={() => onOcr(entry)} className="cursor-pointer">
<ScanText className="mr-2 h-4 w-4" />
Extract Text (OCR)
</DropdownMenuItem>
)}
{showExtract && ( {showExtract && (
<DropdownMenuItem onClick={() => onExtract(entry)} className="cursor-pointer"> <DropdownMenuItem onClick={() => onExtract(entry)} className="cursor-pointer">
<FolderArchive className="mr-2 h-4 w-4" /> <FolderArchive className="mr-2 h-4 w-4" />
@@ -248,7 +237,6 @@ const ContextMenuItems = ({
onCopyPath, onCopyPath,
onDownload, onDownload,
onReadAloud, onReadAloud,
onOcr,
onExtract, onExtract,
onPlay, onPlay,
onCut, onCut,
@@ -259,12 +247,11 @@ const ContextMenuItems = ({
}: MenuItemsProps) => { }: MenuItemsProps) => {
const fileType = entry.type === 'file' ? getFileType(entry.name) : null; const fileType = entry.type === 'file' ? getFileType(entry.name) : null;
const showReadAloud = fileType === 'markdown' || fileType === 'code' || fileType === 'text'; const showReadAloud = fileType === 'markdown' || fileType === 'code' || fileType === 'text';
const showOcr = fileType === 'image';
const showExtract = fileType === 'archive'; const showExtract = fileType === 'archive';
const showPlay = fileType === 'audio' || entry.type === 'directory'; const showPlay = fileType === 'audio' || entry.type === 'directory';
const hasTasks = taskGroups.length > 0; const hasTasks = taskGroups.length > 0;
const hasActions = showPlay || showReadAloud || showOcr || showExtract || hasTasks; const hasActions = showPlay || showReadAloud || showExtract || hasTasks;
// Only nest when more than one category matched — a file usually matches a single category, and // Only nest when more than one category matched — a file usually matches a single category, and
// Run Task > Video > Convert would just add a hop. // Run Task > Video > Convert would just add a hop.
const nestTasks = taskGroups.length > 1; const nestTasks = taskGroups.length > 1;
@@ -284,12 +271,6 @@ const ContextMenuItems = ({
Read Aloud Read Aloud
</ContextMenuItem> </ContextMenuItem>
)} )}
{showOcr && (
<ContextMenuItem onClick={() => onOcr(entry)} className="cursor-pointer">
<ScanText className="mr-2 h-4 w-4" />
Extract Text (OCR)
</ContextMenuItem>
)}
{showExtract && ( {showExtract && (
<ContextMenuItem onClick={() => onExtract(entry)} className="cursor-pointer"> <ContextMenuItem onClick={() => onExtract(entry)} className="cursor-pointer">
<FolderArchive className="mr-2 h-4 w-4" /> <FolderArchive className="mr-2 h-4 w-4" />
@@ -493,7 +474,6 @@ export const FileItem = ({
forceRename, forceRename,
onRenamingChange, onRenamingChange,
onReadAloud, onReadAloud,
onOcr,
onExtract, onExtract,
onPlay, onPlay,
taskGroups, taskGroups,
@@ -574,7 +554,6 @@ export const FileItem = ({
onCopyPath, onCopyPath,
onDownload, onDownload,
onReadAloud, onReadAloud,
onOcr,
onExtract, onExtract,
onPlay, onPlay,
onCut, onCut,
@@ -444,19 +444,6 @@ export const useFileBrowserApp = (
} }
}; };
const handleOcr = async (entry: DirEntry) => {
const filePath = entryPath(entry.name);
const toastId = toast.loading('Extracting text from image...');
try {
const { ocrPath } = await files.ocr(filePath, { saveNextTo: true });
toast.dismiss(toastId);
refresh();
setSearchParams({ view: filePath, ephemeral: ocrPath });
} catch {
toast.error('Failed to extract text from image', { id: toastId });
}
};
const handlePlay = (entry: DirEntry) => { const handlePlay = (entry: DirEntry) => {
const filePath = entryPath(entry.name); const filePath = entryPath(entry.name);
setSearchParams({ play: filePath }); setSearchParams({ play: filePath });
@@ -752,7 +739,6 @@ export const useFileBrowserApp = (
handleCreateDashboard, handleCreateDashboard,
handleCreateDashboardHere, handleCreateDashboardHere,
handleReadAloud, handleReadAloud,
handleOcr,
handleExtract, handleExtract,
handlePlay, handlePlay,
handleGitClone, handleGitClone,