diff --git a/frontend/src/core/components/layout/Workbench.tsx b/frontend/src/core/components/layout/Workbench.tsx index 99d65e83f..0eabada86 100644 --- a/frontend/src/core/components/layout/Workbench.tsx +++ b/frontend/src/core/components/layout/Workbench.tsx @@ -3,11 +3,12 @@ import { Box } from '@mantine/core'; import { useRainbowThemeContext } from '@app/components/shared/RainbowThemeProvider'; import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext'; import { useFileHandler } from '@app/hooks/useFileHandler'; -import { useFileState } from '@app/contexts/FileContext'; +import { useFileState, useFileActions } from '@app/contexts/FileContext'; import { useNavigationState, useNavigationActions, useNavigationGuard } from '@app/contexts/NavigationContext'; import { isBaseWorkbench } from '@app/types/workbench'; import { useViewer } from '@app/contexts/ViewerContext'; import { useAppConfig } from '@app/contexts/AppConfigContext'; +import { FileId } from '@app/types/file'; import styles from '@app/components/layout/Workbench.module.css'; import TopControls from '@app/components/shared/TopControls'; @@ -26,6 +27,7 @@ export default function Workbench() { // Use context-based hooks to eliminate all prop drilling const { selectors } = useFileState(); + const { actions: fileActions } = useFileActions(); const { workbench: currentView } = useNavigationState(); const { actions: navActions } = useNavigationActions(); const setCurrentView = navActions.setWorkbench; @@ -61,13 +63,17 @@ export default function Workbench() { const handleFileSelect = useCallback((index: number) => { // Don't do anything if selecting the same file if (index === activeFileIndex) return; - + // requestNavigation handles the unsaved changes check internally requestNavigation(() => { setActiveFileIndex(index); }); }, [activeFileIndex, requestNavigation, setActiveFileIndex]); + const handleFileRemove = useCallback(async (fileId: FileId) => { + await fileActions.removeFiles([fileId], false); // false = don't delete from IndexedDB, just remove from context + }, [fileActions]); + const handlePreviewClose = () => { setPreviewFile(null); const previousMode = sessionStorage.getItem('previousMode'); @@ -201,6 +207,7 @@ export default function Workbench() { })} currentFileIndex={activeFileIndex} onFileSelect={handleFileSelect} + onFileRemove={handleFileRemove} /> )} diff --git a/frontend/src/core/components/shared/FileDropdownMenu.tsx b/frontend/src/core/components/shared/FileDropdownMenu.tsx index 24583ea77..71942c1bd 100644 --- a/frontend/src/core/components/shared/FileDropdownMenu.tsx +++ b/frontend/src/core/components/shared/FileDropdownMenu.tsx @@ -1,15 +1,28 @@ import React from 'react'; -import { Menu, Loader, Group, Text } from '@mantine/core'; -import VisibilityIcon from '@mui/icons-material/Visibility'; +import { Menu, Loader, Group, Text, ActionIcon, Tooltip } from '@mantine/core'; +import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import CloseIcon from '@mui/icons-material/Close'; import FitText from '@app/components/shared/FitText'; import { PrivateContent } from '@app/components/shared/PrivateContent'; +import { FileId } from '@app/types/file'; + +// Truncate text from the center: "very-long-filename.pdf" -> "very-lo...ame.pdf" +function truncateCenter(text: string, maxLength: number = 25): string { + if (text.length <= maxLength) return text; + const ellipsis = '...'; + const charsToShow = maxLength - ellipsis.length; + const frontChars = Math.ceil(charsToShow / 2); + const backChars = Math.floor(charsToShow / 2); + return text.substring(0, frontChars) + ellipsis + text.substring(text.length - backChars); +} interface FileDropdownMenuProps { displayName: string; activeFiles: Array<{ fileId: string; name: string; versionNumber?: number }>; currentFileIndex: number; onFileSelect?: (index: number) => void; + onFileRemove?: (fileId: FileId) => void; switchingTo?: string | null; viewOptionStyle: React.CSSProperties; pillRef?: React.RefObject; @@ -20,22 +33,27 @@ export const FileDropdownMenu: React.FC = ({ activeFiles, currentFileIndex, onFileSelect, + onFileRemove, switchingTo, viewOptionStyle, }) => { return ( -
+
{switchingTo === "viewer" ? ( ) : ( - + )} - + - +
= ({
- +
- {file.versionNumber && file.versionNumber > 1 && ( - - v{file.versionNumber} - - )} + + {file.versionNumber && file.versionNumber > 1 && ( + + v{file.versionNumber} + + )} + {onFileRemove && ( + + { + e.stopPropagation(); + onFileRemove(file.fileId as FileId); + }} + style={{ flexShrink: 0 }} + > + + + + )} +
); diff --git a/frontend/src/core/components/shared/PageEditorFileDropdown.tsx b/frontend/src/core/components/shared/PageEditorFileDropdown.tsx index 161f390df..11f9742c2 100644 --- a/frontend/src/core/components/shared/PageEditorFileDropdown.tsx +++ b/frontend/src/core/components/shared/PageEditorFileDropdown.tsx @@ -127,7 +127,7 @@ const FileMenuItem: React.FC = ({ />
- +
{file.versionNumber && file.versionNumber > 1 && ( diff --git a/frontend/src/core/components/shared/TopControls.tsx b/frontend/src/core/components/shared/TopControls.tsx index f414cc91a..e859d70d3 100644 --- a/frontend/src/core/components/shared/TopControls.tsx +++ b/frontend/src/core/components/shared/TopControls.tsx @@ -11,6 +11,7 @@ import { PageEditorFileDropdown } from '@app/components/shared/PageEditorFileDro import type { CustomWorkbenchViewInstance } from '@app/contexts/ToolWorkflowContext'; import { FileDropdownMenu } from '@app/components/shared/FileDropdownMenu'; import { usePageEditorDropdownState, PageEditorDropdownState } from '@app/components/pageEditor/hooks/usePageEditorDropdownState'; +import { FileId } from '@app/types/file'; const viewOptionStyle: React.CSSProperties = { @@ -29,6 +30,7 @@ const createViewOptions = ( activeFiles: Array<{ fileId: string; name: string; versionNumber?: number }>, currentFileIndex: number, onFileSelect?: (index: number) => void, + onFileRemove?: (fileId: FileId) => void, pageEditorState?: PageEditorDropdownState, customViews?: CustomWorkbenchViewInstance[] ) => { @@ -37,8 +39,7 @@ const createViewOptions = ( const isInViewer = currentView === 'viewer'; const fileName = currentFile?.name || ''; const viewerDisplayName = isInViewer && fileName ? fileName : 'Viewer'; - const hasMultipleFiles = activeFiles.length > 1; - const showViewerDropdown = isInViewer && hasMultipleFiles; + const showViewerDropdown = isInViewer; const viewerOption = { label: showViewerDropdown ? ( @@ -47,6 +48,7 @@ const createViewOptions = ( activeFiles={activeFiles} currentFileIndex={currentFileIndex} onFileSelect={onFileSelect} + onFileRemove={onFileRemove} switchingTo={switchingTo} viewOptionStyle={viewOptionStyle} /> @@ -132,6 +134,7 @@ interface TopControlsProps { activeFiles?: Array<{ fileId: string; name: string; versionNumber?: number }>; currentFileIndex?: number; onFileSelect?: (index: number) => void; + onFileRemove?: (fileId: FileId) => void; } const TopControls = ({ @@ -141,6 +144,7 @@ const TopControls = ({ activeFiles = [], currentFileIndex = 0, onFileSelect, + onFileRemove, }: TopControlsProps) => { const { isRainbowMode } = useRainbowThemeContext(); const [switchingTo, setSwitchingTo] = useState(null); @@ -176,9 +180,10 @@ const TopControls = ({ activeFiles, currentFileIndex, onFileSelect, + onFileRemove, pageEditorState, customViews - ), [currentView, switchingTo, activeFiles, currentFileIndex, onFileSelect, pageEditorState, customViews]); + ), [currentView, switchingTo, activeFiles, currentFileIndex, onFileSelect, onFileRemove, pageEditorState, customViews]); return (