mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-24 04:26:14 +00:00
Compare commits
5 Commits
9c49cf9584
...
8fb5f54e9d
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8fb5f54e9d | ||
![]() |
b5ce46cc7a | ||
![]() |
7dad484aa7 | ||
![]() |
d7f698ead7 | ||
![]() |
bd6c9042d3 |
@ -2299,7 +2299,7 @@
|
|||||||
"noFileSelected": "No files selected",
|
"noFileSelected": "No files selected",
|
||||||
"showHistory": "Show History",
|
"showHistory": "Show History",
|
||||||
"hideHistory": "Hide History",
|
"hideHistory": "Hide History",
|
||||||
"fileHistory": "FileHistory",
|
"fileHistory": "File History",
|
||||||
"loadingHistory": "Loading History...",
|
"loadingHistory": "Loading History...",
|
||||||
"lastModified": "Last Modified",
|
"lastModified": "Last Modified",
|
||||||
"toolChain": "Tools Applied",
|
"toolChain": "Tools Applied",
|
||||||
|
@ -38,7 +38,7 @@ const FileHistoryGroup: React.FC<FileHistoryGroupProps> = ({
|
|||||||
<Collapse in={isExpanded}>
|
<Collapse in={isExpanded}>
|
||||||
<Box ml="md" mt="xs" mb="sm">
|
<Box ml="md" mt="xs" mb="sm">
|
||||||
<Group align="center" mb="sm">
|
<Group align="center" mb="sm">
|
||||||
<Text size="xs" fw={600} c="dimmed" tt="uppercase">
|
<Text size="xs" fw={600} c="dimmed">
|
||||||
{t('fileManager.fileHistory', 'File History')} ({sortedHistory.length})
|
{t('fileManager.fileHistory', 'File History')} ({sortedHistory.length})
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
|
@ -53,55 +53,6 @@ const FileInfoCard: React.FC<FileInfoCardProps> = ({
|
|||||||
</Group>
|
</Group>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
{/* Standard PDF Metadata */}
|
|
||||||
{currentFile?.pdfMetadata?.title && (
|
|
||||||
<>
|
|
||||||
<Group justify="space-between" py="xs">
|
|
||||||
<Text size="sm" c="dimmed">{t('fileManager.title', 'Title')}</Text>
|
|
||||||
<Text size="sm" fw={500} style={{ maxWidth: '60%', textAlign: 'right' }} truncate>
|
|
||||||
{currentFile.pdfMetadata.title}
|
|
||||||
</Text>
|
|
||||||
</Group>
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{currentFile?.pdfMetadata?.author && (
|
|
||||||
<>
|
|
||||||
<Group justify="space-between" py="xs">
|
|
||||||
<Text size="sm" c="dimmed">{t('fileManager.author', 'Author')}</Text>
|
|
||||||
<Text size="sm" fw={500} style={{ maxWidth: '60%', textAlign: 'right' }} truncate>
|
|
||||||
{currentFile.pdfMetadata.author}
|
|
||||||
</Text>
|
|
||||||
</Group>
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{currentFile?.pdfMetadata?.subject && (
|
|
||||||
<>
|
|
||||||
<Group justify="space-between" py="xs">
|
|
||||||
<Text size="sm" c="dimmed">{t('fileManager.subject', 'Subject')}</Text>
|
|
||||||
<Text size="sm" fw={500} style={{ maxWidth: '60%', textAlign: 'right' }} truncate>
|
|
||||||
{currentFile.pdfMetadata.subject}
|
|
||||||
</Text>
|
|
||||||
</Group>
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{currentFile?.pdfMetadata?.creationDate && (
|
|
||||||
<>
|
|
||||||
<Group justify="space-between" py="xs">
|
|
||||||
<Text size="sm" c="dimmed">{t('fileManager.created', 'Created')}</Text>
|
|
||||||
<Text size="sm" fw={500}>
|
|
||||||
{new Date(currentFile.pdfMetadata.creationDate).toLocaleDateString()}
|
|
||||||
</Text>
|
|
||||||
</Group>
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Group justify="space-between" py="xs">
|
<Group justify="space-between" py="xs">
|
||||||
<Text size="sm" c="dimmed">{t('fileManager.lastModified', 'Last Modified')}</Text>
|
<Text size="sm" c="dimmed">{t('fileManager.lastModified', 'Last Modified')}</Text>
|
||||||
<Text size="sm" fw={500}>
|
<Text size="sm" fw={500}>
|
||||||
|
@ -6,12 +6,12 @@ import { ToolOperationHook } from './useToolOperation';
|
|||||||
import { BaseParametersHook } from './useBaseParameters';
|
import { BaseParametersHook } from './useBaseParameters';
|
||||||
import { StirlingFile } from '../../../types/fileContext';
|
import { StirlingFile } from '../../../types/fileContext';
|
||||||
|
|
||||||
interface BaseToolReturn<TParams> {
|
interface BaseToolReturn<TParams, TParamsHook extends BaseParametersHook<TParams>> {
|
||||||
// File management
|
// File management
|
||||||
selectedFiles: StirlingFile[];
|
selectedFiles: StirlingFile[];
|
||||||
|
|
||||||
// Tool-specific hooks
|
// Tool-specific hooks
|
||||||
params: BaseParametersHook<TParams>;
|
params: TParamsHook;
|
||||||
operation: ToolOperationHook<TParams>;
|
operation: ToolOperationHook<TParams>;
|
||||||
|
|
||||||
// Endpoint validation
|
// Endpoint validation
|
||||||
@ -33,13 +33,13 @@ interface BaseToolReturn<TParams> {
|
|||||||
/**
|
/**
|
||||||
* Base tool hook for tool components. Manages standard behaviour for tools.
|
* Base tool hook for tool components. Manages standard behaviour for tools.
|
||||||
*/
|
*/
|
||||||
export function useBaseTool<TParams>(
|
export function useBaseTool<TParams, TParamsHook extends BaseParametersHook<TParams>>(
|
||||||
toolName: string,
|
toolName: string,
|
||||||
useParams: () => BaseParametersHook<TParams>,
|
useParams: () => TParamsHook,
|
||||||
useOperation: () => ToolOperationHook<TParams>,
|
useOperation: () => ToolOperationHook<TParams>,
|
||||||
props: BaseToolProps,
|
props: BaseToolProps,
|
||||||
options?: { minFiles?: number }
|
options?: { minFiles?: number }
|
||||||
): BaseToolReturn<TParams> {
|
): BaseToolReturn<TParams, TParamsHook> {
|
||||||
const minFiles = options?.minFiles ?? 1;
|
const minFiles = options?.minFiles ?? 1;
|
||||||
const { onPreviewFile, onComplete, onError } = props;
|
const { onPreviewFile, onComplete, onError } = props;
|
||||||
|
|
||||||
|
@ -87,45 +87,135 @@ class IndexedDBManager {
|
|||||||
request.onupgradeneeded = (event) => {
|
request.onupgradeneeded = (event) => {
|
||||||
const db = request.result;
|
const db = request.result;
|
||||||
const oldVersion = event.oldVersion;
|
const oldVersion = event.oldVersion;
|
||||||
|
const transaction = request.transaction;
|
||||||
|
|
||||||
console.log(`Upgrading ${config.name} from v${oldVersion} to v${config.version}`);
|
console.log(`Upgrading ${config.name} from v${oldVersion} to v${config.version}`);
|
||||||
|
|
||||||
// Create or update object stores
|
// Create or update object stores
|
||||||
config.stores.forEach(storeConfig => {
|
config.stores.forEach(storeConfig => {
|
||||||
|
let store: IDBObjectStore | undefined;
|
||||||
|
|
||||||
if (db.objectStoreNames.contains(storeConfig.name)) {
|
if (db.objectStoreNames.contains(storeConfig.name)) {
|
||||||
// Store exists - for now, just continue (could add migration logic here)
|
// Store exists - get reference for migration
|
||||||
console.log(`Object store '${storeConfig.name}' already exists`);
|
console.log(`Object store '${storeConfig.name}' already exists`);
|
||||||
return;
|
store = transaction?.objectStore(storeConfig.name);
|
||||||
|
|
||||||
|
// Add new indexes if they don't exist
|
||||||
|
if (storeConfig.indexes && store) {
|
||||||
|
storeConfig.indexes.forEach(indexConfig => {
|
||||||
|
if (!store?.indexNames.contains(indexConfig.name)) {
|
||||||
|
store?.createIndex(
|
||||||
|
indexConfig.name,
|
||||||
|
indexConfig.keyPath,
|
||||||
|
{ unique: indexConfig.unique }
|
||||||
|
);
|
||||||
|
console.log(`Created index '${indexConfig.name}' on '${storeConfig.name}'`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create new object store
|
||||||
|
const options: IDBObjectStoreParameters = {};
|
||||||
|
if (storeConfig.keyPath) {
|
||||||
|
options.keyPath = storeConfig.keyPath;
|
||||||
|
}
|
||||||
|
if (storeConfig.autoIncrement) {
|
||||||
|
options.autoIncrement = storeConfig.autoIncrement;
|
||||||
|
}
|
||||||
|
|
||||||
|
store = db.createObjectStore(storeConfig.name, options);
|
||||||
|
console.log(`Created object store '${storeConfig.name}'`);
|
||||||
|
|
||||||
|
// Create indexes
|
||||||
|
if (storeConfig.indexes) {
|
||||||
|
storeConfig.indexes.forEach(indexConfig => {
|
||||||
|
store?.createIndex(
|
||||||
|
indexConfig.name,
|
||||||
|
indexConfig.keyPath,
|
||||||
|
{ unique: indexConfig.unique }
|
||||||
|
);
|
||||||
|
console.log(`Created index '${indexConfig.name}' on '${storeConfig.name}'`);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new object store
|
// Perform data migration for files database
|
||||||
const options: IDBObjectStoreParameters = {};
|
if (config.name === 'stirling-pdf-files' && storeConfig.name === 'files' && store) {
|
||||||
if (storeConfig.keyPath) {
|
this.migrateFileHistoryFields(store, oldVersion);
|
||||||
options.keyPath = storeConfig.keyPath;
|
|
||||||
}
|
|
||||||
if (storeConfig.autoIncrement) {
|
|
||||||
options.autoIncrement = storeConfig.autoIncrement;
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = db.createObjectStore(storeConfig.name, options);
|
|
||||||
console.log(`Created object store '${storeConfig.name}'`);
|
|
||||||
|
|
||||||
// Create indexes
|
|
||||||
if (storeConfig.indexes) {
|
|
||||||
storeConfig.indexes.forEach(indexConfig => {
|
|
||||||
store.createIndex(
|
|
||||||
indexConfig.name,
|
|
||||||
indexConfig.keyPath,
|
|
||||||
{ unique: indexConfig.unique }
|
|
||||||
);
|
|
||||||
console.log(`Created index '${indexConfig.name}' on '${storeConfig.name}'`);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate existing file records to include new file history fields
|
||||||
|
*/
|
||||||
|
private migrateFileHistoryFields(store: IDBObjectStore, oldVersion: number): void {
|
||||||
|
// Only migrate if upgrading from a version before file history was added (version < 3)
|
||||||
|
if (oldVersion >= 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Starting file history migration for existing records...');
|
||||||
|
|
||||||
|
const cursor = store.openCursor();
|
||||||
|
let migratedCount = 0;
|
||||||
|
|
||||||
|
cursor.onsuccess = (event) => {
|
||||||
|
const cursor = (event.target as IDBRequest).result;
|
||||||
|
if (cursor) {
|
||||||
|
const record = cursor.value;
|
||||||
|
let needsUpdate = false;
|
||||||
|
|
||||||
|
// Add missing file history fields with sensible defaults
|
||||||
|
if (record.isLeaf === undefined) {
|
||||||
|
record.isLeaf = true; // Existing files are unprocessed, should appear in recent files
|
||||||
|
needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.versionNumber === undefined) {
|
||||||
|
record.versionNumber = 1; // Existing files are first version
|
||||||
|
needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.originalFileId === undefined) {
|
||||||
|
record.originalFileId = record.id; // Existing files are their own root
|
||||||
|
needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.parentFileId === undefined) {
|
||||||
|
record.parentFileId = undefined; // No parent for existing files
|
||||||
|
needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.toolHistory === undefined) {
|
||||||
|
record.toolHistory = []; // No history for existing files
|
||||||
|
needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the record if any fields were missing
|
||||||
|
if (needsUpdate) {
|
||||||
|
try {
|
||||||
|
cursor.update(record);
|
||||||
|
migratedCount++;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to migrate record:', record.id, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.continue();
|
||||||
|
} else {
|
||||||
|
// Migration complete
|
||||||
|
console.log(`File history migration completed. Migrated ${migratedCount} records.`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cursor.onerror = (event) => {
|
||||||
|
console.error('File history migration failed:', (event.target as IDBRequest).error);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get database connection (must be already opened)
|
* Get database connection (must be already opened)
|
||||||
*/
|
*/
|
||||||
|
@ -28,8 +28,8 @@ export interface BaseFileMetadata {
|
|||||||
createdAt?: number; // When file was added to system
|
createdAt?: number; // When file was added to system
|
||||||
|
|
||||||
// File history tracking
|
// File history tracking
|
||||||
isLeaf?: boolean; // True if this file hasn't been processed yet
|
isLeaf: boolean; // True if this file hasn't been processed yet
|
||||||
originalFileId?: string; // Root file ID for grouping versions
|
originalFileId: string; // Root file ID for grouping versions
|
||||||
versionNumber: number; // Version number in chain
|
versionNumber: number; // Version number in chain
|
||||||
parentFileId?: FileId; // Immediate parent file ID
|
parentFileId?: FileId; // Immediate parent file ID
|
||||||
toolHistory?: Array<{
|
toolHistory?: Array<{
|
||||||
@ -37,14 +37,4 @@ export interface BaseFileMetadata {
|
|||||||
timestamp: number;
|
timestamp: number;
|
||||||
}>; // Tool chain for history tracking
|
}>; // Tool chain for history tracking
|
||||||
|
|
||||||
// Standard PDF document metadata
|
|
||||||
pdfMetadata?: {
|
|
||||||
title?: string;
|
|
||||||
author?: string;
|
|
||||||
subject?: string;
|
|
||||||
creator?: string;
|
|
||||||
producer?: string;
|
|
||||||
creationDate?: Date;
|
|
||||||
modificationDate?: Date;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,7 @@ export function createNewStirlingFileStub(
|
|||||||
size: file.size,
|
size: file.size,
|
||||||
type: file.type,
|
type: file.type,
|
||||||
lastModified: file.lastModified,
|
lastModified: file.lastModified,
|
||||||
|
originalFileId: fileId,
|
||||||
quickKey: createQuickKey(file),
|
quickKey: createQuickKey(file),
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
isLeaf: true, // New files are leaf nodes by default
|
isLeaf: true, // New files are leaf nodes by default
|
||||||
|
Loading…
x
Reference in New Issue
Block a user