Stirling 2.0 (#3928)

# Description of Changes

<!--

File context for managing files between tools and views
Optimisation for large files
Updated Split to work with new file system and match Matts stepped
design closer

-->

---

## 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)

### 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.

---------

Co-authored-by: Anthony Stirling <[email protected]>
This commit is contained in:
Reece Browne
2025-07-16 17:53:50 +01:00
committed by GitHub
co-authored by Anthony Stirling
parent 584e2ecee7
commit 922bbc9076
66 changed files with 8728 additions and 2519 deletions
+178
View File
@@ -0,0 +1,178 @@
/**
* Types for global file context management across views and tools
*/
import { ProcessedFile } from './processing';
import { PDFDocument, PDFPage, PageOperation } from './pageEditor';
export type ModeType = 'viewer' | 'pageEditor' | 'fileEditor' | 'merge' | 'split' | 'compress';
// Legacy types for backward compatibility during transition
export type ViewType = 'viewer' | 'pageEditor' | 'fileEditor';
export type ToolType = 'merge' | 'split' | 'compress' | null;
export interface FileOperation {
id: string;
type: 'merge' | 'split' | 'compress' | 'add' | 'remove' | 'replace' | 'convert' | 'upload';
timestamp: number;
fileIds: string[];
status: 'pending' | 'applied' | 'failed';
data?: any;
metadata?: {
originalFileName?: string;
outputFileNames?: string[];
parameters?: Record<string, any>;
fileSize?: number;
pageCount?: number;
error?: string;
};
}
export interface FileOperationHistory {
fileId: string;
fileName: string;
operations: (FileOperation | PageOperation)[];
createdAt: number;
lastModified: number;
}
export interface ViewerConfig {
zoom: number;
currentPage: number;
viewMode: 'single' | 'continuous' | 'facing';
sidebarOpen: boolean;
}
export interface FileEditHistory {
fileId: string;
pageOperations: PageOperation[];
lastModified: number;
}
export interface FileContextState {
// Core file management
activeFiles: File[];
processedFiles: Map<File, ProcessedFile>;
// Current navigation state
currentMode: ModeType;
// Legacy fields for backward compatibility
currentView: ViewType;
currentTool: ToolType;
// Edit history and state
fileEditHistory: Map<string, FileEditHistory>;
globalFileOperations: FileOperation[];
// New comprehensive operation history
fileOperationHistory: Map<string, FileOperationHistory>;
// UI state that persists across views
selectedFileIds: string[];
selectedPageNumbers: number[];
viewerConfig: ViewerConfig;
// Processing state
isProcessing: boolean;
processingProgress: number;
// Export state
lastExportConfig?: {
filename: string;
selectedOnly: boolean;
splitDocuments: boolean;
};
// Navigation guard system
hasUnsavedChanges: boolean;
pendingNavigation: (() => void) | null;
showNavigationWarning: boolean;
}
export interface FileContextActions {
// File management
addFiles: (files: File[]) => Promise<void>;
removeFiles: (fileIds: string[], deleteFromStorage?: boolean) => void;
replaceFile: (oldFileId: string, newFile: File) => Promise<void>;
clearAllFiles: () => void;
// Navigation
setCurrentMode: (mode: ModeType) => void;
// Legacy navigation functions for backward compatibility
setCurrentView: (view: ViewType) => void;
setCurrentTool: (tool: ToolType) => void;
// Selection management
setSelectedFiles: (fileIds: string[]) => void;
setSelectedPages: (pageNumbers: number[]) => void;
updateProcessedFile: (file: File, processedFile: ProcessedFile) => void;
clearSelections: () => void;
// Edit operations
applyPageOperations: (fileId: string, operations: PageOperation[]) => void;
applyFileOperation: (operation: FileOperation) => void;
undoLastOperation: (fileId?: string) => void;
// Operation history management
recordOperation: (fileId: string, operation: FileOperation | PageOperation) => void;
markOperationApplied: (fileId: string, operationId: string) => void;
markOperationFailed: (fileId: string, operationId: string, error: string) => void;
getFileHistory: (fileId: string) => FileOperationHistory | undefined;
getAppliedOperations: (fileId: string) => (FileOperation | PageOperation)[];
clearFileHistory: (fileId: string) => void;
// Viewer state
updateViewerConfig: (config: Partial<ViewerConfig>) => void;
// Export configuration
setExportConfig: (config: FileContextState['lastExportConfig']) => void;
// Utility
getFileById: (fileId: string) => File | undefined;
getProcessedFileById: (fileId: string) => ProcessedFile | undefined;
getCurrentFile: () => File | undefined;
getCurrentProcessedFile: () => ProcessedFile | undefined;
// Context persistence
saveContext: () => Promise<void>;
loadContext: () => Promise<void>;
resetContext: () => void;
// Navigation guard system
setHasUnsavedChanges: (hasChanges: boolean) => void;
requestNavigation: (navigationFn: () => void) => boolean;
confirmNavigation: () => void;
cancelNavigation: () => void;
// Memory management
trackBlobUrl: (url: string) => void;
trackPdfDocument: (fileId: string, pdfDoc: any) => void;
cleanupFile: (fileId: string) => Promise<void>;
scheduleCleanup: (fileId: string, delay?: number) => void;
}
export interface FileContextValue extends FileContextState, FileContextActions {}
export interface FileContextProviderProps {
children: React.ReactNode;
enableUrlSync?: boolean;
enablePersistence?: boolean;
maxCacheSize?: number;
}
// Helper types for component props
export interface WithFileContext {
fileContext: FileContextValue;
}
// URL parameter types for deep linking
export interface FileContextUrlParams {
mode?: ModeType;
// Legacy parameters for backward compatibility
view?: ViewType;
tool?: ToolType;
fileIds?: string[];
pageIds?: string[];
zoom?: number;
page?: number;
}
+14 -3
View File
@@ -1,7 +1,7 @@
export interface PDFPage {
id: string;
pageNumber: number;
thumbnail: string;
thumbnail: string | null;
rotation: number;
selected: boolean;
splitBefore?: boolean;
@@ -16,12 +16,23 @@ export interface PDFDocument {
}
export interface PageOperation {
type: 'rotate' | 'delete' | 'move' | 'split' | 'insert';
id: string;
type: 'rotate' | 'delete' | 'move' | 'split' | 'insert' | 'reorder';
pageIds: string[];
timestamp: number;
status: 'pending' | 'applied' | 'failed';
data?: any;
metadata?: {
rotation?: number;
fromPosition?: number;
toPosition?: number;
splitType?: string;
insertAfterPage?: number;
error?: string;
};
}
export interface UndoRedoState {
operations: PageOperation[];
currentIndex: number;
}
}
+91
View File
@@ -0,0 +1,91 @@
export interface ProcessingError {
type: 'network' | 'parsing' | 'memory' | 'corruption' | 'timeout' | 'cancelled';
message: string;
recoverable: boolean;
retryCount: number;
maxRetries: number;
originalError?: Error;
}
export interface ProcessingState {
fileKey: string;
fileName: string;
status: 'pending' | 'processing' | 'completed' | 'error' | 'cancelled';
progress: number; // 0-100
strategy: ProcessingStrategy;
error?: ProcessingError;
startedAt: number;
completedAt?: number;
estimatedTimeRemaining?: number;
currentPage?: number;
cancellationToken?: AbortController;
}
export interface ProcessedFile {
id: string;
pages: PDFPage[];
totalPages: number;
metadata: {
title: string;
createdAt: string;
modifiedAt: string;
};
}
export interface PDFPage {
id: string;
pageNumber: number;
thumbnail: string | null;
rotation: number;
selected: boolean;
splitBefore?: boolean;
}
export interface CacheConfig {
maxFiles: number;
maxSizeBytes: number;
ttlMs: number;
}
export interface CacheEntry {
data: ProcessedFile;
size: number;
lastAccessed: number;
createdAt: number;
}
export interface CacheStats {
entries: number;
totalSizeBytes: number;
maxSizeBytes: number;
}
export type ProcessingStrategy = 'immediate_full' | 'progressive_chunked' | 'metadata_only' | 'priority_pages';
export interface ProcessingConfig {
strategy: ProcessingStrategy;
chunkSize: number; // Pages per chunk
thumbnailQuality: 'low' | 'medium' | 'high';
priorityPageCount: number; // Number of priority pages to process first
useWebWorker: boolean;
maxRetries: number;
timeoutMs: number;
}
export interface FileAnalysis {
fileSize: number;
estimatedPageCount?: number;
isEncrypted: boolean;
isCorrupted: boolean;
recommendedStrategy: ProcessingStrategy;
estimatedProcessingTime: number; // milliseconds
}
export interface ProcessingMetrics {
totalFiles: number;
completedFiles: number;
failedFiles: number;
averageProcessingTime: number;
cacheHitRate: number;
memoryUsage: number;
}