mostly formatting, dependency fix
This commit is contained in:
281
src/App.tsx
281
src/App.tsx
@@ -1,19 +1,19 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import Layout from "./components/Layout";
|
||||
import FileLoader from "./components/FileLoader";
|
||||
import ReorderPanel from "./components/ReorderPanel";
|
||||
import ActionsPanel from "./components/ActionsPanel";
|
||||
import PagePreviewModal from "./components/PagePreviewModal";
|
||||
import WorkspacePanel from "./components/WorkspacePanel";
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import Layout from './components/Layout';
|
||||
import FileLoader from './components/FileLoader';
|
||||
import ReorderPanel from './components/ReorderPanel';
|
||||
import ActionsPanel from './components/ActionsPanel';
|
||||
import PagePreviewModal from './components/PagePreviewModal';
|
||||
import WorkspacePanel from './components/WorkspacePanel';
|
||||
import ActionDialog, {
|
||||
type ActionDialogAction,
|
||||
} from "./components/ActionDialog";
|
||||
import HelpDialog from "./components/HelpDialog";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
} from './components/ActionDialog';
|
||||
import HelpDialog from './components/HelpDialog';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import type {
|
||||
StoredWorkspace,
|
||||
WorkspaceSummary,
|
||||
} from "./workspace/workspaceTypes";
|
||||
} from './workspace/workspaceTypes';
|
||||
import {
|
||||
createInitialPageRefs,
|
||||
createPageRefId,
|
||||
@@ -22,22 +22,22 @@ import {
|
||||
defaultWorkspaceNameFromPdfName,
|
||||
normalizeRotation,
|
||||
useWorkspaceState,
|
||||
} from "./workspace/useWorkspaceState";
|
||||
} from './workspace/useWorkspaceState';
|
||||
import {
|
||||
deleteWorkspaceFromIndexedDb,
|
||||
listWorkspaces,
|
||||
loadWorkspaceFromIndexedDb,
|
||||
saveWorkspaceToIndexedDb,
|
||||
} from "./workspace/workspaceDb";
|
||||
import type { PageRef, PdfFile } from "./pdf/pdfTypes";
|
||||
} from './workspace/workspaceDb';
|
||||
import type { PageRef, PdfFile } from './pdf/pdfTypes';
|
||||
import {
|
||||
loadPdfFromFile,
|
||||
mergePdfFiles,
|
||||
splitIntoSinglePages,
|
||||
exportPages,
|
||||
} from "./pdf/pdfService";
|
||||
import { usePdfThumbnails } from "./pdf/usePdfThumbnails";
|
||||
import { usePdfGeneratedOutputs } from "./hooks/usePdfGeneratedOutputs";
|
||||
} from './pdf/pdfService';
|
||||
import { usePdfThumbnails } from './pdf/usePdfThumbnails';
|
||||
import { usePdfGeneratedOutputs } from './hooks/usePdfGeneratedOutputs';
|
||||
import {
|
||||
createSelectionPdfName,
|
||||
createSelectionWorkspaceName,
|
||||
@@ -50,9 +50,9 @@ function isEditableKeyboardTarget(target: EventTarget | null): boolean {
|
||||
const tagName = target.tagName.toLowerCase();
|
||||
return (
|
||||
target.isContentEditable ||
|
||||
tagName === "input" ||
|
||||
tagName === "textarea" ||
|
||||
tagName === "select"
|
||||
tagName === 'input' ||
|
||||
tagName === 'textarea' ||
|
||||
tagName === 'select'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,18 +70,18 @@ const App: React.FC = () => {
|
||||
|
||||
const [workspaces, setWorkspaces] = useState<WorkspaceSummary[]>([]);
|
||||
const [activeWorkspaceId, setActiveWorkspaceId] = useState<string | null>(
|
||||
null,
|
||||
null
|
||||
);
|
||||
const [workspaceName, setWorkspaceName] = useState("");
|
||||
const [workspaceName, setWorkspaceName] = useState('');
|
||||
|
||||
const [previewPageId, setPreviewPageId] = useState<string | null>(null);
|
||||
|
||||
const [pendingFile, setPendingFile] = useState<File | null>(null);
|
||||
const [showMergeOptions, setShowMergeOptions] = useState(false);
|
||||
const [mergeMode, setMergeMode] = useState<
|
||||
"overwrite" | "append" | "insertAt"
|
||||
>("append");
|
||||
const [mergeInsertAt, setMergeInsertAt] = useState<string>("");
|
||||
'overwrite' | 'append' | 'insertAt'
|
||||
>('append');
|
||||
const [mergeInsertAt, setMergeInsertAt] = useState<string>('');
|
||||
|
||||
const {
|
||||
splitDownloads,
|
||||
@@ -123,7 +123,7 @@ const App: React.FC = () => {
|
||||
console.error(thrown);
|
||||
setError(message);
|
||||
},
|
||||
[],
|
||||
[]
|
||||
);
|
||||
|
||||
const { thumbnails: reorderThumbnails, clearThumbnailCache } =
|
||||
@@ -154,7 +154,7 @@ const App: React.FC = () => {
|
||||
setWorkspaces(summaries);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError("Failed to read saved workspaces from browser storage.");
|
||||
setError('Failed to read saved workspaces from browser storage.');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -165,7 +165,7 @@ const App: React.FC = () => {
|
||||
const resetWorkspaceState = () => {
|
||||
setPdf(null);
|
||||
setActiveWorkspaceId(null);
|
||||
setWorkspaceName("");
|
||||
setWorkspaceName('');
|
||||
resetWorkspaceCommandState();
|
||||
clearGeneratedOutputs();
|
||||
clearThumbnailCache();
|
||||
@@ -183,7 +183,7 @@ const App: React.FC = () => {
|
||||
const workspaceId = activeWorkspaceId ?? createWorkspaceId();
|
||||
|
||||
const existing = workspaces.find(
|
||||
(workspace) => workspace.id === workspaceId,
|
||||
(workspace) => workspace.id === workspaceId
|
||||
);
|
||||
|
||||
const workspace: StoredWorkspace = {
|
||||
@@ -221,7 +221,7 @@ const App: React.FC = () => {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError(
|
||||
"Failed to save workspace. The browser storage quota may be full.",
|
||||
'Failed to save workspace. The browser storage quota may be full.'
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
@@ -242,7 +242,7 @@ const App: React.FC = () => {
|
||||
}
|
||||
|
||||
openActionDialog({
|
||||
title: "Reset workspace?",
|
||||
title: 'Reset workspace?',
|
||||
content: (
|
||||
<>
|
||||
<p style={{ marginTop: 0 }}>This workspace has unsaved changes.</p>
|
||||
@@ -253,21 +253,21 @@ const App: React.FC = () => {
|
||||
),
|
||||
actions: [
|
||||
{
|
||||
label: "Cancel",
|
||||
variant: "secondary",
|
||||
label: 'Cancel',
|
||||
variant: 'secondary',
|
||||
onClick: closeActionDialog,
|
||||
},
|
||||
{
|
||||
label: "Reset without saving",
|
||||
variant: "danger",
|
||||
label: 'Reset without saving',
|
||||
variant: 'danger',
|
||||
onClick: () => {
|
||||
closeActionDialog();
|
||||
performResetWorkspace();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Save and reset",
|
||||
variant: "primary",
|
||||
label: 'Save and reset',
|
||||
variant: 'primary',
|
||||
autoFocus: true,
|
||||
onClick: async () => {
|
||||
closeActionDialog();
|
||||
@@ -289,7 +289,7 @@ const App: React.FC = () => {
|
||||
const loaded = await loadWorkspaceFromIndexedDb(workspaceId);
|
||||
|
||||
if (!loaded) {
|
||||
setError("Workspace not found.");
|
||||
setError('Workspace not found.');
|
||||
await refreshWorkspaces();
|
||||
return;
|
||||
}
|
||||
@@ -324,7 +324,7 @@ const App: React.FC = () => {
|
||||
setWorkspaceName(loaded.workspace.name);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError("Failed to load workspace from browser storage.");
|
||||
setError('Failed to load workspace from browser storage.');
|
||||
} finally {
|
||||
setIsBusy(false);
|
||||
}
|
||||
@@ -332,10 +332,10 @@ const App: React.FC = () => {
|
||||
|
||||
const handleDeleteWorkspace = (workspaceId: string) => {
|
||||
const workspace = workspaces.find((item) => item.id === workspaceId);
|
||||
const name = workspace?.name ?? "this workspace";
|
||||
const name = workspace?.name ?? 'this workspace';
|
||||
|
||||
openActionDialog({
|
||||
title: "Delete workspace?",
|
||||
title: 'Delete workspace?',
|
||||
content: (
|
||||
<>
|
||||
<p style={{ marginTop: 0 }}>
|
||||
@@ -350,13 +350,13 @@ const App: React.FC = () => {
|
||||
),
|
||||
actions: [
|
||||
{
|
||||
label: "Cancel",
|
||||
variant: "secondary",
|
||||
label: 'Cancel',
|
||||
variant: 'secondary',
|
||||
onClick: closeActionDialog,
|
||||
},
|
||||
{
|
||||
label: "Delete workspace",
|
||||
variant: "danger",
|
||||
label: 'Delete workspace',
|
||||
variant: 'danger',
|
||||
autoFocus: true,
|
||||
onClick: () => {
|
||||
closeActionDialog();
|
||||
@@ -377,14 +377,14 @@ const App: React.FC = () => {
|
||||
setActiveWorkspaceId(null);
|
||||
setWorkspaceDirty(true);
|
||||
setWorkspaceMessage(
|
||||
"Saved workspace deleted. Current in-memory document remains open.",
|
||||
'Saved workspace deleted. Current in-memory document remains open.'
|
||||
);
|
||||
}
|
||||
|
||||
await refreshWorkspaces();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError("Failed to delete workspace.");
|
||||
setError('Failed to delete workspace.');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -411,7 +411,7 @@ const App: React.FC = () => {
|
||||
setWorkspaceName(defaultWorkspaceNameFromPdfName(loaded.name));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError("Failed to load PDF (see console).");
|
||||
setError('Failed to load PDF (see console).');
|
||||
} finally {
|
||||
setIsBusy(false);
|
||||
}
|
||||
@@ -423,7 +423,7 @@ const App: React.FC = () => {
|
||||
} else {
|
||||
setPendingFile(file);
|
||||
setShowMergeOptions(true);
|
||||
setMergeMode("append");
|
||||
setMergeMode('append');
|
||||
setMergeInsertAt(String(pages.length + 1));
|
||||
}
|
||||
};
|
||||
@@ -436,7 +436,7 @@ const App: React.FC = () => {
|
||||
const handleMergeConfirm = async () => {
|
||||
if (!pendingFile) return;
|
||||
|
||||
if (!pdf || mergeMode === "overwrite") {
|
||||
if (!pdf || mergeMode === 'overwrite') {
|
||||
await loadFileAsNew(pendingFile);
|
||||
setPendingFile(null);
|
||||
setShowMergeOptions(false);
|
||||
@@ -464,12 +464,12 @@ const App: React.FC = () => {
|
||||
|
||||
// 3) Determine insert position (0-based)
|
||||
let insertAt = pages.length; // default: append at end
|
||||
if (mergeMode === "insertAt") {
|
||||
if (mergeMode === 'insertAt') {
|
||||
const parsed = parseInt(mergeInsertAt, 10);
|
||||
if (Number.isFinite(parsed)) {
|
||||
insertAt = Math.min(Math.max(parsed - 1, 0), pages.length);
|
||||
}
|
||||
} else if (mergeMode === "append") {
|
||||
} else if (mergeMode === 'append') {
|
||||
insertAt = pages.length;
|
||||
}
|
||||
|
||||
@@ -495,7 +495,7 @@ const App: React.FC = () => {
|
||||
setActiveWorkspaceId(null);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError("Failed to merge PDF (see console).");
|
||||
setError('Failed to merge PDF (see console).');
|
||||
} finally {
|
||||
setIsBusy(false);
|
||||
setPendingFile(null);
|
||||
@@ -518,16 +518,16 @@ const App: React.FC = () => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (isEditableKeyboardTarget(e.target)) return;
|
||||
|
||||
if (e.key === "F1" || e.key === "?") {
|
||||
if (e.key === 'F1' || e.key === '?') {
|
||||
e.preventDefault();
|
||||
setHelpOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -537,13 +537,13 @@ const App: React.FC = () => {
|
||||
const afterPages = pages.map((page) =>
|
||||
page.id === pageId
|
||||
? { ...page, rotation: (normalizeRotation(page.rotation) + 90) % 360 }
|
||||
: page,
|
||||
: page
|
||||
);
|
||||
|
||||
executeWorkspaceCommand(
|
||||
createWorkspaceCommand({
|
||||
type: "page.rotate",
|
||||
label: "Rotated page clockwise",
|
||||
type: 'page.rotate',
|
||||
label: 'Rotated page clockwise',
|
||||
before,
|
||||
after: {
|
||||
...before,
|
||||
@@ -553,7 +553,7 @@ const App: React.FC = () => {
|
||||
pageId,
|
||||
degrees: 90,
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -562,13 +562,13 @@ const App: React.FC = () => {
|
||||
const afterPages = pages.map((page) =>
|
||||
page.id === pageId
|
||||
? { ...page, rotation: (normalizeRotation(page.rotation) + 270) % 360 }
|
||||
: page,
|
||||
: page
|
||||
);
|
||||
|
||||
executeWorkspaceCommand(
|
||||
createWorkspaceCommand({
|
||||
type: "page.rotate",
|
||||
label: "Rotated page counterclockwise",
|
||||
type: 'page.rotate',
|
||||
label: 'Rotated page counterclockwise',
|
||||
before,
|
||||
after: {
|
||||
...before,
|
||||
@@ -578,7 +578,7 @@ const App: React.FC = () => {
|
||||
pageId,
|
||||
degrees: -90,
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -586,10 +586,10 @@ const App: React.FC = () => {
|
||||
const page = pages.find((item) => item.id === pageId);
|
||||
const visualIndex = page ? pages.indexOf(page) : -1;
|
||||
const pageLabel =
|
||||
visualIndex >= 0 ? `page at position ${visualIndex + 1}` : "this page";
|
||||
visualIndex >= 0 ? `page at position ${visualIndex + 1}` : 'this page';
|
||||
|
||||
openActionDialog({
|
||||
title: "Delete page?",
|
||||
title: 'Delete page?',
|
||||
content: (
|
||||
<p style={{ margin: 0 }}>
|
||||
Delete <strong>{pageLabel}</strong> from the current workspace?
|
||||
@@ -597,13 +597,13 @@ const App: React.FC = () => {
|
||||
),
|
||||
actions: [
|
||||
{
|
||||
label: "Cancel",
|
||||
variant: "secondary",
|
||||
label: 'Cancel',
|
||||
variant: 'secondary',
|
||||
onClick: closeActionDialog,
|
||||
},
|
||||
{
|
||||
label: "Delete page",
|
||||
variant: "danger",
|
||||
label: 'Delete page',
|
||||
variant: 'danger',
|
||||
autoFocus: true,
|
||||
onClick: () => {
|
||||
closeActionDialog();
|
||||
@@ -619,8 +619,8 @@ const App: React.FC = () => {
|
||||
|
||||
executeWorkspaceCommand(
|
||||
createWorkspaceCommand({
|
||||
type: "page.delete",
|
||||
label: "Deleted page",
|
||||
type: 'page.delete',
|
||||
label: 'Deleted page',
|
||||
before,
|
||||
after: {
|
||||
pages: pages.filter((page) => page.id !== pageId),
|
||||
@@ -630,7 +630,7 @@ const App: React.FC = () => {
|
||||
details: {
|
||||
pageId,
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -639,8 +639,8 @@ const App: React.FC = () => {
|
||||
|
||||
executeWorkspaceCommand(
|
||||
createWorkspaceCommand({
|
||||
type: "pages.reorder",
|
||||
label: "Reordered pages",
|
||||
type: 'pages.reorder',
|
||||
label: 'Reordered pages',
|
||||
before,
|
||||
after: {
|
||||
...before,
|
||||
@@ -649,14 +649,14 @@ const App: React.FC = () => {
|
||||
details: {
|
||||
pageCount: newPages.length,
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleToggleSelect = (
|
||||
pageId: string,
|
||||
visualIndex: number,
|
||||
e: React.MouseEvent<HTMLButtonElement>,
|
||||
e: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
setSelectedPageIds((prev) => {
|
||||
if (e.shiftKey && lastSelectedVisualIndex !== null && pages.length > 0) {
|
||||
@@ -731,28 +731,28 @@ const App: React.FC = () => {
|
||||
openActionDialog({
|
||||
title:
|
||||
idsToDelete.length === 1
|
||||
? "Delete selected page?"
|
||||
: "Delete selected pages?",
|
||||
? 'Delete selected page?'
|
||||
: 'Delete selected pages?',
|
||||
content: (
|
||||
<p style={{ margin: 0 }}>
|
||||
Delete{" "}
|
||||
Delete{' '}
|
||||
<strong>
|
||||
{idsToDelete.length === 1
|
||||
? "1 selected page"
|
||||
? '1 selected page'
|
||||
: `${idsToDelete.length} selected pages`}
|
||||
</strong>{" "}
|
||||
</strong>{' '}
|
||||
from the current workspace?
|
||||
</p>
|
||||
),
|
||||
actions: [
|
||||
{
|
||||
label: "Cancel",
|
||||
variant: "secondary",
|
||||
label: 'Cancel',
|
||||
variant: 'secondary',
|
||||
onClick: closeActionDialog,
|
||||
},
|
||||
{
|
||||
label: idsToDelete.length === 1 ? "Delete page" : "Delete pages",
|
||||
variant: "danger",
|
||||
label: idsToDelete.length === 1 ? 'Delete page' : 'Delete pages',
|
||||
variant: 'danger',
|
||||
autoFocus: true,
|
||||
onClick: () => {
|
||||
closeActionDialog();
|
||||
@@ -795,10 +795,10 @@ const App: React.FC = () => {
|
||||
|
||||
executeWorkspaceCommand(
|
||||
createWorkspaceCommand({
|
||||
type: "pages.copy",
|
||||
type: 'pages.copy',
|
||||
label:
|
||||
copiedPages.length === 1
|
||||
? "Copied page"
|
||||
? 'Copied page'
|
||||
: `Copied ${copiedPages.length} pages`,
|
||||
before,
|
||||
after: {
|
||||
@@ -810,7 +810,7 @@ const App: React.FC = () => {
|
||||
count: copiedPages.length,
|
||||
insertSlot: clampedSlot,
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -852,7 +852,7 @@ const App: React.FC = () => {
|
||||
|
||||
const key = e.key.toLowerCase();
|
||||
|
||||
if ((e.ctrlKey || e.metaKey) && key === "z") {
|
||||
if ((e.ctrlKey || e.metaKey) && key === 'z') {
|
||||
e.preventDefault();
|
||||
if (e.shiftKey) {
|
||||
handleRedo();
|
||||
@@ -862,13 +862,13 @@ const App: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((e.ctrlKey || e.metaKey) && key === "y") {
|
||||
if ((e.ctrlKey || e.metaKey) && key === 'y') {
|
||||
e.preventDefault();
|
||||
handleRedo();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((e.ctrlKey || e.metaKey) && key === "a") {
|
||||
if ((e.ctrlKey || e.metaKey) && key === 'a') {
|
||||
e.preventDefault();
|
||||
setSelectedPageIds(pages.map((page) => page.id));
|
||||
setLastSelectedVisualIndex(null);
|
||||
@@ -876,7 +876,7 @@ const App: React.FC = () => {
|
||||
}
|
||||
|
||||
if (
|
||||
(e.key === "Delete" || e.key === "Backspace") &&
|
||||
(e.key === 'Delete' || e.key === 'Backspace') &&
|
||||
selectedPageIds.length > 0
|
||||
) {
|
||||
e.preventDefault();
|
||||
@@ -884,17 +884,17 @@ const App: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key === "Escape" && selectedPageIds.length > 0) {
|
||||
if (e.key === 'Escape' && selectedPageIds.length > 0) {
|
||||
e.preventDefault();
|
||||
setSelectedPageIds([]);
|
||||
setLastSelectedVisualIndex(null);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [
|
||||
hasPdf,
|
||||
@@ -919,7 +919,7 @@ const App: React.FC = () => {
|
||||
replaceSplitResults(result);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError("Error while splitting PDF (see console).");
|
||||
setError('Error while splitting PDF (see console).');
|
||||
} finally {
|
||||
setIsBusy(false);
|
||||
}
|
||||
@@ -939,18 +939,17 @@ const App: React.FC = () => {
|
||||
if (selectedPages.length === 0) return;
|
||||
|
||||
const blob = await exportPages(pdf, selectedPages);
|
||||
const base = pdf.name.replace(/\.pdf$/i, "");
|
||||
const base = pdf.name.replace(/\.pdf$/i, '');
|
||||
const filename = `${base}_selected.pdf`;
|
||||
replaceSubsetResult(blob, filename);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError("Error while extracting selected pages (see console).");
|
||||
setError('Error while extracting selected pages (see console).');
|
||||
} finally {
|
||||
setIsBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const performOpenSelectionAsWorkspace = async () => {
|
||||
if (!pdf || selectedPageIds.length === 0) return;
|
||||
|
||||
@@ -988,7 +987,7 @@ const App: React.FC = () => {
|
||||
redoHistory: [],
|
||||
dirty: true,
|
||||
message: `Created a new workspace from ${selectedPageCount} selected ${
|
||||
selectedPageCount === 1 ? "page" : "pages"
|
||||
selectedPageCount === 1 ? 'page' : 'pages'
|
||||
}.`,
|
||||
});
|
||||
|
||||
@@ -999,7 +998,7 @@ const App: React.FC = () => {
|
||||
clearThumbnailCache();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError("Error while opening selection as a new workspace.");
|
||||
setError('Error while opening selection as a new workspace.');
|
||||
} finally {
|
||||
setIsBusy(false);
|
||||
}
|
||||
@@ -1017,13 +1016,13 @@ const App: React.FC = () => {
|
||||
}
|
||||
|
||||
openActionDialog({
|
||||
title: "Open selection as new workspace?",
|
||||
title: 'Open selection as new workspace?',
|
||||
content: (
|
||||
<>
|
||||
<p style={{ marginTop: 0 }}>
|
||||
This will replace the current in-memory workspace with a new
|
||||
workspace built from {selectedPages.length}{' '}
|
||||
{selectedPages.length === 1 ? "selected page" : "selected pages"}.
|
||||
{selectedPages.length === 1 ? 'selected page' : 'selected pages'}.
|
||||
</p>
|
||||
<p style={{ marginBottom: 0 }}>
|
||||
The current workspace has unsaved changes. Do you want to save it
|
||||
@@ -1033,21 +1032,21 @@ const App: React.FC = () => {
|
||||
),
|
||||
actions: [
|
||||
{
|
||||
label: "Cancel",
|
||||
variant: "secondary",
|
||||
label: 'Cancel',
|
||||
variant: 'secondary',
|
||||
onClick: closeActionDialog,
|
||||
},
|
||||
{
|
||||
label: "Open without saving",
|
||||
variant: "danger",
|
||||
label: 'Open without saving',
|
||||
variant: 'danger',
|
||||
onClick: () => {
|
||||
closeActionDialog();
|
||||
void performOpenSelectionAsWorkspace();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Save and open",
|
||||
variant: "primary",
|
||||
label: 'Save and open',
|
||||
variant: 'primary',
|
||||
autoFocus: true,
|
||||
onClick: async () => {
|
||||
closeActionDialog();
|
||||
@@ -1068,12 +1067,12 @@ const App: React.FC = () => {
|
||||
|
||||
try {
|
||||
const blob = await exportPages(pdf, pages);
|
||||
const base = pdf.name.replace(/\.pdf$/i, "");
|
||||
const base = pdf.name.replace(/\.pdf$/i, '');
|
||||
const filename = `${base}_reordered.pdf`;
|
||||
replaceExportResult(blob, filename);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError("Error while exporting reordered PDF (see console).");
|
||||
setError('Error while exporting reordered PDF (see console).');
|
||||
} finally {
|
||||
setIsBusy(false);
|
||||
}
|
||||
@@ -1120,62 +1119,62 @@ const App: React.FC = () => {
|
||||
{showMergeOptions && pendingFile && pdf && pages.length > 0 && (
|
||||
<div
|
||||
className="card"
|
||||
style={{ border: "1px solid #bfdbfe", background: "#eff6ff" }}
|
||||
style={{ border: '1px solid #bfdbfe', background: '#eff6ff' }}
|
||||
>
|
||||
<h2>Open file: merge or replace?</h2>
|
||||
<p style={{ fontSize: "0.85rem", color: "#374151" }}>
|
||||
You already have <strong>{pdf.name}</strong> with {pages.length}{" "}
|
||||
pages open. What should happen with{" "}
|
||||
<p style={{ fontSize: '0.85rem', color: '#374151' }}>
|
||||
You already have <strong>{pdf.name}</strong> with {pages.length}{' '}
|
||||
pages open. What should happen with{' '}
|
||||
<strong>{pendingFile.name}</strong>?
|
||||
</p>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "0.5rem",
|
||||
marginTop: "0.5rem",
|
||||
fontSize: "0.9rem",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.5rem',
|
||||
marginTop: '0.5rem',
|
||||
fontSize: '0.9rem',
|
||||
}}
|
||||
>
|
||||
<label
|
||||
style={{ display: "flex", alignItems: "center", gap: "0.4rem" }}
|
||||
style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="mergeMode"
|
||||
value="overwrite"
|
||||
checked={mergeMode === "overwrite"}
|
||||
onChange={() => setMergeMode("overwrite")}
|
||||
checked={mergeMode === 'overwrite'}
|
||||
onChange={() => setMergeMode('overwrite')}
|
||||
/>
|
||||
<span>Replace current document</span>
|
||||
</label>
|
||||
|
||||
<label
|
||||
style={{ display: "flex", alignItems: "center", gap: "0.4rem" }}
|
||||
style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="mergeMode"
|
||||
value="append"
|
||||
checked={mergeMode === "append"}
|
||||
onChange={() => setMergeMode("append")}
|
||||
checked={mergeMode === 'append'}
|
||||
onChange={() => setMergeMode('append')}
|
||||
/>
|
||||
<span>Merge and append pages at the end</span>
|
||||
</label>
|
||||
|
||||
<label
|
||||
style={{ display: "flex", alignItems: "center", gap: "0.4rem" }}
|
||||
style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="mergeMode"
|
||||
value="insertAt"
|
||||
checked={mergeMode === "insertAt"}
|
||||
onChange={() => setMergeMode("insertAt")}
|
||||
checked={mergeMode === 'insertAt'}
|
||||
onChange={() => setMergeMode('insertAt')}
|
||||
/>
|
||||
<span>
|
||||
Merge and insert starting at position{" "}
|
||||
Merge and insert starting at position{' '}
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
@@ -1183,19 +1182,19 @@ const App: React.FC = () => {
|
||||
value={mergeInsertAt}
|
||||
onChange={(e) => setMergeInsertAt(e.target.value)}
|
||||
style={{
|
||||
width: "4rem",
|
||||
padding: "0.15rem 0.3rem",
|
||||
fontSize: "0.85rem",
|
||||
width: '4rem',
|
||||
padding: '0.15rem 0.3rem',
|
||||
fontSize: '0.85rem',
|
||||
}}
|
||||
/>{" "}
|
||||
<span style={{ color: "#6b7280" }}>
|
||||
/>{' '}
|
||||
<span style={{ color: '#6b7280' }}>
|
||||
(1 = before first page, {pages.length + 1} = after last page)
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="button-row" style={{ marginTop: "0.75rem" }}>
|
||||
<div className="button-row" style={{ marginTop: '0.75rem' }}>
|
||||
<button
|
||||
className="secondary"
|
||||
type="button"
|
||||
@@ -1210,7 +1209,7 @@ const App: React.FC = () => {
|
||||
onClick={handleMergeConfirm}
|
||||
disabled={isBusy}
|
||||
>
|
||||
{isBusy ? "Working…" : "Continue"}
|
||||
{isBusy ? 'Working…' : 'Continue'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1240,7 +1239,7 @@ const App: React.FC = () => {
|
||||
selectedCount={selectedPageIds.length}
|
||||
onSplit={handleSplit}
|
||||
onExtractSelected={handleExtractSelected}
|
||||
onOpenSelectionAsWorkspace={handleOpenSelectionAsWorkspace}
|
||||
onOpenSelectionAsWorkspace={handleOpenSelectionAsWorkspace}
|
||||
onExportReordered={handleExportReordered}
|
||||
splitDownloads={splitDownloads}
|
||||
subsetDownload={subsetDownload}
|
||||
@@ -1250,7 +1249,7 @@ const App: React.FC = () => {
|
||||
{error && (
|
||||
<div
|
||||
className="card"
|
||||
style={{ border: "1px solid #fecaca", background: "#fef2f2" }}
|
||||
style={{ border: '1px solid #fecaca', background: '#fef2f2' }}
|
||||
>
|
||||
<strong>Error:</strong> {error}
|
||||
</div>
|
||||
@@ -1272,7 +1271,7 @@ const App: React.FC = () => {
|
||||
|
||||
<ActionDialog
|
||||
open={actionDialog !== null}
|
||||
title={actionDialog?.title ?? ""}
|
||||
title={actionDialog?.title ?? ''}
|
||||
actions={actionDialog?.actions ?? []}
|
||||
onClose={closeActionDialog}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user