mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 03:20:46 +02:00
Feature/v2/filehistory (#4370)
File History --------- Co-authored-by: Connor Yoh <[email protected]>
This commit is contained in:
+24
-45
@@ -7,55 +7,34 @@ declare const tag: unique symbol;
|
||||
export type FileId = string & { readonly [tag]: 'FileId' };
|
||||
|
||||
/**
|
||||
* File metadata for efficient operations without loading full file data
|
||||
* Used by IndexedDBContext and FileContext for lazy file loading
|
||||
* Tool operation metadata for history tracking
|
||||
* Note: Parameters removed for security - sensitive data like passwords should not be stored in history
|
||||
*/
|
||||
export interface FileMetadata {
|
||||
export interface ToolOperation {
|
||||
toolName: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base file metadata shared between storage and runtime layers
|
||||
* Contains all common file properties and history tracking
|
||||
*/
|
||||
export interface BaseFileMetadata {
|
||||
id: FileId;
|
||||
name: string;
|
||||
type: string;
|
||||
size: number;
|
||||
lastModified: number;
|
||||
thumbnail?: string;
|
||||
isDraft?: boolean; // Marks files as draft versions
|
||||
createdAt?: number; // When file was added to system
|
||||
|
||||
// File history tracking
|
||||
isLeaf: boolean; // True if this file hasn't been processed yet
|
||||
originalFileId: string; // Root file ID for grouping versions
|
||||
versionNumber: number; // Version number in chain
|
||||
parentFileId?: FileId; // Immediate parent file ID
|
||||
toolHistory?: Array<{
|
||||
toolName: string;
|
||||
timestamp: number;
|
||||
}>; // Tool chain for history tracking
|
||||
|
||||
}
|
||||
|
||||
export interface StorageConfig {
|
||||
useIndexedDB: boolean;
|
||||
maxFileSize: number; // Maximum size per file in bytes
|
||||
maxTotalStorage: number; // Maximum total storage in bytes
|
||||
warningThreshold: number; // Warning threshold (percentage 0-1)
|
||||
}
|
||||
|
||||
export const defaultStorageConfig: StorageConfig = {
|
||||
useIndexedDB: true,
|
||||
maxFileSize: 100 * 1024 * 1024, // 100MB per file
|
||||
maxTotalStorage: 1024 * 1024 * 1024, // 1GB default, will be updated dynamically
|
||||
warningThreshold: 0.8, // Warn at 80% capacity
|
||||
};
|
||||
|
||||
// Calculate and update storage limit: half of available storage or 10GB, whichever is smaller
|
||||
export const initializeStorageConfig = async (): Promise<StorageConfig> => {
|
||||
const tenGB = 10 * 1024 * 1024 * 1024; // 10GB in bytes
|
||||
const oneGB = 1024 * 1024 * 1024; // 1GB fallback
|
||||
|
||||
let maxTotalStorage = oneGB; // Default fallback
|
||||
|
||||
// Try to estimate available storage
|
||||
if ('storage' in navigator && 'estimate' in navigator.storage) {
|
||||
try {
|
||||
const estimate = await navigator.storage.estimate();
|
||||
if (estimate.quota) {
|
||||
const halfQuota = estimate.quota / 2;
|
||||
maxTotalStorage = Math.min(halfQuota, tenGB);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Could not estimate storage quota, using 1GB default:', error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...defaultStorageConfig,
|
||||
maxTotalStorage
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { PageOperation } from './pageEditor';
|
||||
import { FileId, FileMetadata } from './file';
|
||||
import { FileId, BaseFileMetadata } from './file';
|
||||
|
||||
// Re-export FileId for convenience
|
||||
export type { FileId };
|
||||
@@ -40,7 +40,6 @@ export interface ProcessedFilePage {
|
||||
export interface ProcessedFileMetadata {
|
||||
pages: ProcessedFilePage[];
|
||||
totalPages?: number;
|
||||
thumbnailUrl?: string;
|
||||
lastProcessed?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -52,16 +51,17 @@ export interface ProcessedFileMetadata {
|
||||
* separately in refs for memory efficiency. Supports multi-tool workflows
|
||||
* where files persist across tool operations.
|
||||
*/
|
||||
export interface StirlingFileStub {
|
||||
id: FileId; // UUID primary key for collision-free operations
|
||||
name: string; // Display name for UI
|
||||
size: number; // File size for progress indicators
|
||||
type: string; // MIME type for format validation
|
||||
lastModified: number; // Original timestamp for deduplication
|
||||
/**
|
||||
* StirlingFileStub - Runtime UI metadata for files in the active workbench session
|
||||
*
|
||||
* Contains UI display data and processing state. Actual File objects stored
|
||||
* separately in refs for memory efficiency. Supports multi-tool workflows
|
||||
* where files persist across tool operations.
|
||||
*/
|
||||
export interface StirlingFileStub extends BaseFileMetadata {
|
||||
quickKey?: string; // Fast deduplication key: name|size|lastModified
|
||||
thumbnailUrl?: string; // Generated thumbnail blob URL for visual display
|
||||
blobUrl?: string; // File access blob URL for downloads/processing
|
||||
createdAt?: number; // When added to workbench for sorting
|
||||
processedFile?: ProcessedFileMetadata; // PDF page data and processing results
|
||||
insertAfterPageId?: string; // Page ID after which this file should be inserted
|
||||
isPinned?: boolean; // Protected from tool consumption (replace/remove)
|
||||
@@ -107,6 +107,11 @@ export function isStirlingFile(file: File): file is StirlingFile {
|
||||
|
||||
// Create a StirlingFile from a regular File object
|
||||
export function createStirlingFile(file: File, id?: FileId): StirlingFile {
|
||||
// Check if file is already a StirlingFile to avoid property redefinition
|
||||
if (isStirlingFile(file)) {
|
||||
return file; // Already has fileId and quickKey properties
|
||||
}
|
||||
|
||||
const fileId = id || createFileId();
|
||||
const quickKey = createQuickKey(file);
|
||||
|
||||
@@ -151,9 +156,11 @@ export function isFileObject(obj: any): obj is File | StirlingFile {
|
||||
|
||||
|
||||
|
||||
export function toStirlingFileStub(
|
||||
export function createNewStirlingFileStub(
|
||||
file: File,
|
||||
id?: FileId
|
||||
id?: FileId,
|
||||
thumbnail?: string,
|
||||
processedFileMetadata?: ProcessedFileMetadata
|
||||
): StirlingFileStub {
|
||||
const fileId = id || createFileId();
|
||||
return {
|
||||
@@ -162,8 +169,13 @@ export function toStirlingFileStub(
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
lastModified: file.lastModified,
|
||||
originalFileId: fileId,
|
||||
quickKey: createQuickKey(file),
|
||||
createdAt: Date.now()
|
||||
createdAt: Date.now(),
|
||||
isLeaf: true, // New files are leaf nodes by default
|
||||
versionNumber: 1, // New files start at version 1
|
||||
thumbnailUrl: thumbnail,
|
||||
processedFile: processedFileMetadata
|
||||
};
|
||||
}
|
||||
|
||||
@@ -209,7 +221,6 @@ export interface FileOperation {
|
||||
metadata?: {
|
||||
originalFileName?: string;
|
||||
outputFileNames?: string[];
|
||||
parameters?: Record<string, any>;
|
||||
fileSize?: number;
|
||||
pageCount?: number;
|
||||
error?: string;
|
||||
@@ -286,8 +297,7 @@ export type FileContextAction =
|
||||
export interface FileContextActions {
|
||||
// File management - lightweight actions only
|
||||
addFiles: (files: File[], options?: { insertAfterPageId?: string; selectFiles?: boolean }) => Promise<StirlingFile[]>;
|
||||
addProcessedFiles: (filesWithThumbnails: Array<{ file: File; thumbnail?: string; pageCount?: number }>) => Promise<StirlingFile[]>;
|
||||
addStoredFiles: (filesWithMetadata: Array<{ file: File; originalId: FileId; metadata: FileMetadata }>, options?: { selectFiles?: boolean }) => Promise<StirlingFile[]>;
|
||||
addStirlingFileStubs: (stirlingFileStubs: StirlingFileStub[], options?: { insertAfterPageId?: string; selectFiles?: boolean }) => Promise<StirlingFile[]>;
|
||||
removeFiles: (fileIds: FileId[], deleteFromStorage?: boolean) => Promise<void>;
|
||||
updateStirlingFileStub: (id: FileId, updates: Partial<StirlingFileStub>) => void;
|
||||
reorderFiles: (orderedFileIds: FileId[]) => void;
|
||||
@@ -299,7 +309,7 @@ export interface FileContextActions {
|
||||
unpinFile: (file: StirlingFile) => void;
|
||||
|
||||
// File consumption (replace unpinned files with outputs)
|
||||
consumeFiles: (inputFileIds: FileId[], outputFiles: File[]) => Promise<FileId[]>;
|
||||
consumeFiles: (inputFileIds: FileId[], outputStirlingFiles: StirlingFile[], outputStirlingFileStubs: StirlingFileStub[]) => Promise<FileId[]>;
|
||||
undoConsumeFiles: (inputFiles: File[], inputStirlingFileStubs: StirlingFileStub[], outputFileIds: FileId[]) => Promise<void>;
|
||||
// Selection management
|
||||
setSelectedFiles: (fileIds: FileId[]) => void;
|
||||
|
||||
Reference in New Issue
Block a user