File in context indication (#4990)

# Description of Changes

<!--
Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)
-->

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing)
for more details.
This commit is contained in:
Reece Browne
2025-11-25 20:28:24 +00:00
committed by GitHub
parent 53d167eda5
commit daf749e6be
6 changed files with 65 additions and 6 deletions

View File

@@ -4686,7 +4686,9 @@
"download": "Download",
"delete": "Delete",
"unsupported": "Unsupported",
"active": "Active",
"addToUpload": "Add to Upload",
"closeFile": "Close File",
"deleteAll": "Delete All",
"loadingFiles": "Loading files...",
"noFiles": "No files available",

View File

@@ -12,6 +12,7 @@ import { FileManagerProvider } from '@app/contexts/FileManagerContext';
import { Z_INDEX_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
import { isGoogleDriveConfigured } from '@app/services/googleDrivePickerService';
import { loadScript } from '@app/utils/scriptLoader';
import { useAllFiles } from '@app/contexts/FileContext';
interface FileManagerProps {
selectedTool?: Tool | null;
@@ -25,6 +26,9 @@ const FileManager: React.FC<FileManagerProps> = ({ selectedTool }) => {
const { loadRecentFiles, handleRemoveFile, loading } = useFileManager();
// Get active file IDs from FileContext to show which files are already loaded
const { fileIds: activeFileIds } = useAllFiles();
// File management handlers
const isFileSupported = useCallback((fileName: string) => {
if (!selectedTool?.supportedFormats) return true;
@@ -175,6 +179,7 @@ const FileManager: React.FC<FileManagerProps> = ({ selectedTool }) => {
modalHeight={modalHeight}
refreshRecentFiles={refreshRecentFiles}
isLoading={loading}
activeFileIds={activeFileIds}
>
{isMobile ? <MobileLayout /> : <DesktopLayout />}
</FileManagerProvider>

View File

@@ -30,6 +30,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
onDownloadSingle,
isFileSupported,
isLoading,
activeFileIds,
} = useFileManagerContext();
const { t } = useTranslation();
@@ -55,6 +56,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
// All files in filteredFiles are now leaf files only
const historyFiles = loadedHistoryFiles.get(file.id) || [];
const isExpanded = expandedFileIds.has(file.id);
const isActive = activeFileIds.includes(file.id);
return (
<React.Fragment key={file.id}>
@@ -68,6 +70,7 @@ const FileListArea: React.FC<FileListAreaProps> = ({
onDoubleClick={() => onFileDoubleClick(file)}
isHistoryFile={false} // All files here are leaf files
isLatestVersion={true} // All files here are the latest versions
isActive={isActive}
/>
<FileHistoryGroup

View File

@@ -6,6 +6,7 @@ import DownloadIcon from '@mui/icons-material/Download';
import HistoryIcon from '@mui/icons-material/History';
import RestoreIcon from '@mui/icons-material/Restore';
import UnarchiveIcon from '@mui/icons-material/Unarchive';
import CloseIcon from '@mui/icons-material/Close';
import { useTranslation } from 'react-i18next';
import { getFileSize, getFileDate } from '@app/utils/fileUtils';
import { FileId, StirlingFileStub } from '@app/types/fileContext';
@@ -14,6 +15,7 @@ import { zipFileService } from '@app/services/zipFileService';
import ToolChain from '@app/components/shared/ToolChain';
import { Z_INDEX_OVER_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
import { PrivateContent } from '@app/components/shared/PrivateContent';
import { useFileManagement } from '@app/contexts/FileContext';
interface FileListItemProps {
file: StirlingFileStub;
@@ -26,6 +28,7 @@ interface FileListItemProps {
isLast?: boolean;
isHistoryFile?: boolean; // Whether this is a history file (indented)
isLatestVersion?: boolean; // Whether this is the latest version (shows chevron)
isActive?: boolean; // Whether this file is currently loaded in FileContext
}
const FileListItem: React.FC<FileListItemProps> = ({
@@ -37,12 +40,14 @@ const FileListItem: React.FC<FileListItemProps> = ({
onDownload,
onDoubleClick,
isHistoryFile = false,
isLatestVersion = false
isLatestVersion = false,
isActive = false
}) => {
const [isHovered, setIsHovered] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const { t } = useTranslation();
const {expandedFileIds, onToggleExpansion, onUnzipFile } = useFileManagerContext();
const { removeFiles } = useFileManagement();
// Check if this is a ZIP file
const isZipFile = zipFileService.isZipFileStub(file);
@@ -61,8 +66,10 @@ const FileListItem: React.FC<FileListItemProps> = ({
<Box
p="sm"
style={{
cursor: isHistoryFile ? 'default' : 'pointer',
backgroundColor: isSelected
cursor: isHistoryFile || isActive ? 'default' : 'pointer',
backgroundColor: isActive
? 'var(--file-active-bg)'
: isSelected
? 'var(--mantine-color-gray-1)'
: (shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'var(--bg-file-list)'),
opacity: isSupported ? 1 : 0.5,
@@ -74,7 +81,7 @@ const FileListItem: React.FC<FileListItemProps> = ({
paddingLeft: isHistoryFile ? '2rem' : '0.75rem', // Indent history files
borderLeft: isHistoryFile ? '3px solid var(--mantine-color-blue-4)' : 'none' // Visual indicator for history
}}
onClick={isHistoryFile ? undefined : (e) => onSelect(e.shiftKey)}
onClick={isHistoryFile || isActive ? undefined : (e) => onSelect(e.shiftKey)}
onDoubleClick={onDoubleClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
@@ -84,14 +91,16 @@ const FileListItem: React.FC<FileListItemProps> = ({
<Box>
{/* Checkbox for regular files only */}
<Checkbox
checked={isSelected}
checked={isActive || isSelected}
onChange={() => {}} // Handled by parent onClick
size="sm"
pl="sm"
pr="xs"
disabled={isActive}
color={isActive ? "green" : undefined}
styles={{
input: {
cursor: 'pointer'
cursor: isActive ? 'not-allowed' : 'pointer'
}
}}
/>
@@ -103,6 +112,19 @@ const FileListItem: React.FC<FileListItemProps> = ({
<Text size="sm" fw={500} truncate style={{ flex: 1 }}>
<PrivateContent>{file.name}</PrivateContent>
</Text>
{isActive && (
<Badge
size="xs"
variant="light"
style={{
backgroundColor: 'var(--file-active-badge-bg)',
color: 'var(--file-active-badge-fg)',
border: '1px solid var(--file-active-badge-border)'
}}
>
{t('fileManager.active', 'Active')}
</Badge>
)}
<Badge size="xs" variant="light" color={"blue"}>
v{currentVersion}
</Badge>
@@ -151,6 +173,22 @@ const FileListItem: React.FC<FileListItemProps> = ({
</Menu.Target>
<Menu.Dropdown>
{/* Close file option for active files */}
{isActive && (
<>
<Menu.Item
leftSection={<CloseIcon style={{ fontSize: 16 }} />}
onClick={(e) => {
e.stopPropagation();
removeFiles([file.id]);
}}
>
{t('fileManager.closeFile', 'Close File')}
</Menu.Item>
<Menu.Divider />
</>
)}
{onDownload && (
<Menu.Item
leftSection={<DownloadIcon style={{ fontSize: 16 }} />}

View File

@@ -20,6 +20,7 @@ interface FileManagerContextValue {
fileGroups: Map<FileId, StirlingFileStub[]>;
loadedHistoryFiles: Map<FileId, StirlingFileStub[]>;
isLoading: boolean;
activeFileIds: FileId[];
// Handlers
onSourceChange: (source: 'recent' | 'local' | 'drive') => void;
@@ -63,6 +64,7 @@ interface FileManagerProviderProps {
modalHeight: string;
refreshRecentFiles: () => Promise<void>;
isLoading: boolean;
activeFileIds: FileId[];
}
export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
@@ -77,6 +79,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
modalHeight,
refreshRecentFiles,
isLoading,
activeFileIds,
}) => {
const [activeSource, setActiveSource] = useState<'recent' | 'local' | 'drive'>('recent');
const [selectedFileIds, setSelectedFileIds] = useState<FileId[]>([]);
@@ -618,6 +621,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
fileGroups,
loadedHistoryFiles,
isLoading,
activeFileIds,
// Handlers
onSourceChange: handleSourceChange,
@@ -654,6 +658,7 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
fileGroups,
loadedHistoryFiles,
isLoading,
activeFileIds,
handleSourceChange,
handleLocalFileClick,
handleFileSelect,

View File

@@ -12,6 +12,12 @@
/* Inline highlights in summary */
--spdf-compare-inline-removed-bg: rgba(255, 59, 48, 0.25);
--spdf-compare-inline-added-bg: rgba(52, 199, 89, 0.25);
/* File Manager active file colors */
--file-active-bg: rgba(59, 130, 246, 0.1); /* Transparent blue for active file background */
--file-active-badge-bg: rgba(34, 197, 94, 0.15); /* Transparent green for active badge */
--file-active-badge-fg: #15803d; /* Green text for active badge */
--file-active-badge-border: rgba(34, 197, 94, 0.3); /* Green border for active badge */
}
/* CSS variables for Tailwind + Mantine integration */