mostly formatting, dependency fix

This commit is contained in:
2026-05-17 02:39:32 +02:00
parent a5dc70aabf
commit cf9a0dd0b7
32 changed files with 837 additions and 836 deletions

View File

@@ -1,9 +1,9 @@
import React, { useEffect } from "react";
import React, { useEffect } from 'react';
export interface ActionDialogAction {
label: string;
onClick: () => void | Promise<void>;
variant?: "primary" | "secondary" | "danger";
variant?: 'primary' | 'secondary' | 'danger';
disabled?: boolean;
autoFocus?: boolean;
title?: string;
@@ -18,21 +18,21 @@ interface ActionDialogProps {
}
const backgroundByVariant: Record<
NonNullable<ActionDialogAction["variant"]>,
NonNullable<ActionDialogAction['variant']>,
string
> = {
primary: "#2563eb",
secondary: "#e5e7eb",
danger: "#dc2626",
primary: '#2563eb',
secondary: '#e5e7eb',
danger: '#dc2626',
};
const colorByVariant: Record<
NonNullable<ActionDialogAction["variant"]>,
NonNullable<ActionDialogAction['variant']>,
string
> = {
primary: "white",
secondary: "#111827",
danger: "white",
primary: 'white',
secondary: '#111827',
danger: 'white',
};
const ActionDialog: React.FC<ActionDialogProps> = ({
@@ -46,16 +46,16 @@ const ActionDialog: React.FC<ActionDialogProps> = ({
if (!open) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") {
if (e.key === 'Escape') {
e.preventDefault();
onClose();
}
};
window.addEventListener("keydown", handleKeyDown);
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener('keydown', handleKeyDown);
};
}, [open, onClose]);
@@ -72,42 +72,42 @@ const ActionDialog: React.FC<ActionDialogProps> = ({
}
}}
style={{
position: "fixed",
position: 'fixed',
inset: 0,
zIndex: 70,
background: "rgba(15, 23, 42, 0.55)",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "1rem",
background: 'rgba(15, 23, 42, 0.55)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '1rem',
}}
>
<div
style={{
width: "100%",
maxWidth: "440px",
background: "white",
borderRadius: "0.75rem",
boxShadow: "0 20px 40px rgba(15, 23, 42, 0.35)",
padding: "1rem",
display: "flex",
flexDirection: "column",
gap: "0.75rem",
width: '100%',
maxWidth: '440px',
background: 'white',
borderRadius: '0.75rem',
boxShadow: '0 20px 40px rgba(15, 23, 42, 0.35)',
padding: '1rem',
display: 'flex',
flexDirection: 'column',
gap: '0.75rem',
}}
>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
gap: "0.75rem",
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: '0.75rem',
}}
>
<h2
id="action-dialog-title"
style={{
margin: 0,
fontSize: "1rem",
fontSize: '1rem',
}}
>
{title}
@@ -117,18 +117,18 @@ const ActionDialog: React.FC<ActionDialogProps> = ({
type="button"
onClick={onClose}
style={{
border: "none",
borderRadius: "999px",
width: "1.8rem",
height: "1.8rem",
background: "#e5e7eb",
color: "#111827",
cursor: "pointer",
fontSize: "1.1rem",
border: 'none',
borderRadius: '999px',
width: '1.8rem',
height: '1.8rem',
background: '#e5e7eb',
color: '#111827',
cursor: 'pointer',
fontSize: '1.1rem',
lineHeight: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
aria-label="Close dialog"
>
@@ -138,8 +138,8 @@ const ActionDialog: React.FC<ActionDialogProps> = ({
<div
style={{
fontSize: "0.9rem",
color: "#4b5563",
fontSize: '0.9rem',
color: '#4b5563',
lineHeight: 1.45,
}}
>
@@ -148,15 +148,15 @@ const ActionDialog: React.FC<ActionDialogProps> = ({
<div
style={{
display: "flex",
justifyContent: "flex-end",
gap: "0.5rem",
flexWrap: "wrap",
marginTop: "0.25rem",
display: 'flex',
justifyContent: 'flex-end',
gap: '0.5rem',
flexWrap: 'wrap',
marginTop: '0.25rem',
}}
>
{actions.map((action) => {
const variant = action.variant ?? "secondary";
const variant = action.variant ?? 'secondary';
return (
<button
@@ -169,15 +169,15 @@ const ActionDialog: React.FC<ActionDialogProps> = ({
autoFocus={action.autoFocus}
title={action.title}
style={{
border: "none",
borderRadius: "0.5rem",
padding: "0.45rem 0.8rem",
border: 'none',
borderRadius: '0.5rem',
padding: '0.45rem 0.8rem',
background: action.disabled
? "#e5e7eb"
? '#e5e7eb'
: backgroundByVariant[variant],
color: action.disabled ? "#6b7280" : colorByVariant[variant],
cursor: action.disabled ? "default" : "pointer",
fontSize: "0.9rem",
color: action.disabled ? '#6b7280' : colorByVariant[variant],
cursor: action.disabled ? 'default' : 'pointer',
fontSize: '0.9rem',
}}
>
{action.label}

View File

@@ -1,8 +1,8 @@
import React from "react";
import React from 'react';
import type {
PdfDownload,
SplitPdfDownload,
} from "../hooks/usePdfGeneratedOutputs";
} from '../hooks/usePdfGeneratedOutputs';
interface ActionsPanelProps {
hasPdf: boolean;
@@ -47,20 +47,20 @@ const ActionsPanel: React.FC<ActionsPanelProps> = ({
return (
<div className="card">
<h2>Tools</h2>
<p style={{ fontSize: "0.85rem", color: "#6b7280" }}>
<p style={{ fontSize: '0.85rem', color: '#6b7280' }}>
Use these tools on the current in-memory document (reordered, rotated,
with deletions). Nothing is uploaded to a server.
</p>
<div
className="button-row"
style={{ justifyContent: "space-between", flexWrap: "wrap" }}
style={{ justifyContent: 'space-between', flexWrap: 'wrap' }}
>
<button
className="secondary"
disabled={disabled}
onClick={onExportReordered}
style={{ flex: "1 1 45%" }}
style={{ flex: '1 1 45%' }}
>
🧾 Export new PDF
</button>
@@ -69,11 +69,11 @@ const ActionsPanel: React.FC<ActionsPanelProps> = ({
className="secondary"
disabled={disabled || selectedCount === 0}
onClick={handleExtractSelectedClick}
style={{ flex: "1 1 45%" }}
style={{ flex: '1 1 45%' }}
title={
selectedCount === 0
? "Select at least one page"
: "Create a PDF from selected pages"
? 'Select at least one page'
: 'Create a PDF from selected pages'
}
>
📤 Extract selected ({selectedCount})
@@ -97,15 +97,15 @@ const ActionsPanel: React.FC<ActionsPanelProps> = ({
className="secondary"
disabled={disabled}
onClick={onSplit}
style={{ flex: "1 1 45%" }}
style={{ flex: '1 1 45%' }}
>
📂 Split into single PDFs
</button>
</div>
{subsetDownload && (
<div style={{ marginTop: "0.5rem", fontSize: "0.9rem" }}>
<strong>Subset result:</strong>{" "}
<div style={{ marginTop: '0.5rem', fontSize: '0.9rem' }}>
<strong>Subset result:</strong>{' '}
<a
className="download-link"
href={subsetDownload.url}
@@ -117,8 +117,8 @@ const ActionsPanel: React.FC<ActionsPanelProps> = ({
)}
{exportDownload && (
<div style={{ marginTop: "0.5rem", fontSize: "0.9rem" }}>
<strong>Exported document:</strong>{" "}
<div style={{ marginTop: '0.5rem', fontSize: '0.9rem' }}>
<strong>Exported document:</strong>{' '}
<a
className="download-link"
href={exportDownload.url}
@@ -130,7 +130,7 @@ const ActionsPanel: React.FC<ActionsPanelProps> = ({
)}
{splitDownloads.length > 0 && (
<div style={{ marginTop: "0.75rem", fontSize: "0.9rem" }}>
<div style={{ marginTop: '0.75rem', fontSize: '0.9rem' }}>
<strong>Single-page PDFs:</strong>
<div>
{splitDownloads.map((download) => (

View File

@@ -1,5 +1,5 @@
import React from "react";
import type { PdfFile } from "../pdf/pdfTypes";
import React from 'react';
import type { PdfFile } from '../pdf/pdfTypes';
interface FileLoaderProps {
pdf: PdfFile | null;
@@ -11,7 +11,7 @@ const FileLoader: React.FC<FileLoaderProps> = ({ pdf, onFileLoaded }) => {
const file = e.target.files?.[0];
if (file) {
onFileLoaded(file);
e.target.value = "";
e.target.value = '';
}
};
@@ -22,7 +22,7 @@ const FileLoader: React.FC<FileLoaderProps> = ({ pdf, onFileLoaded }) => {
<input type="file" accept="application/pdf" onChange={handleChange} />
{pdf && (
<div style={{ marginTop: "0.75rem", fontSize: "0.9rem" }}>
<div style={{ marginTop: '0.75rem', fontSize: '0.9rem' }}>
<div>
<strong>Loaded:</strong> {pdf.name}
</div>

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React, { useEffect } from 'react';
interface HelpDialogProps {
open: boolean;
@@ -6,55 +6,55 @@ interface HelpDialogProps {
}
const shortcuts = [
{ keys: "F1 / ?", description: "Open this help and tutorial dialog" },
{ keys: 'F1 / ?', description: 'Open this help and tutorial dialog' },
{
keys: "Ctrl/⌘ + A",
description: "Select all pages in the current workspace",
keys: 'Ctrl/⌘ + A',
description: 'Select all pages in the current workspace',
},
{
keys: "Delete / Backspace",
description: "Delete the selected pages after confirmation",
keys: 'Delete / Backspace',
description: 'Delete the selected pages after confirmation',
},
{
keys: "Esc",
description: "Clear the page selection or close an open dialog",
keys: 'Esc',
description: 'Clear the page selection or close an open dialog',
},
{ keys: "Ctrl/⌘ + Z", description: "Undo the latest workspace command" },
{ keys: 'Ctrl/⌘ + Z', description: 'Undo the latest workspace command' },
{
keys: "Ctrl/⌘ + Shift + Z",
description: "Redo the next workspace command",
keys: 'Ctrl/⌘ + Shift + Z',
description: 'Redo the next workspace command',
},
{ keys: "Ctrl/⌘ + Y", description: "Redo the next workspace command" },
{ keys: 'Ctrl/⌘ + Y', description: 'Redo the next workspace command' },
{
keys: "← / → in preview",
description: "Move to the previous or next page in the preview overlay",
keys: '← / → in preview',
description: 'Move to the previous or next page in the preview overlay',
},
];
const tutorialSteps = [
{
title: "1. Open a PDF or load a workspace",
body: "Start by selecting a local PDF file. If you saved workspaces before, you can restore one from browser storage instead.",
title: '1. Open a PDF or load a workspace',
body: 'Start by selecting a local PDF file. If you saved workspaces before, you can restore one from browser storage instead.',
},
{
title: "2. Arrange pages visually",
body: "Drag page cards to reorder them. Rotate single pages, open the large preview with a click, or remove pages you do not want in the export.",
title: '2. Arrange pages visually',
body: 'Drag page cards to reorder them. Rotate single pages, open the large preview with a click, or remove pages you do not want in the export.',
},
{
title: "3. Select, copy, and delete pages",
body: "Use the checkbox on a page card to select it. Shift-click extends a range. Dragging a selected page moves the whole selection; the copy controls duplicate selected pages into a chosen slot.",
title: '3. Select, copy, and delete pages',
body: 'Use the checkbox on a page card to select it. Shift-click extends a range. Dragging a selected page moves the whole selection; the copy controls duplicate selected pages into a chosen slot.',
},
{
title: "4. Extract selected pages or branch into a new workspace",
body: "Extract selected pages when you only need a download. Open the selection as a new workspace when you want to continue working on that subset.",
title: '4. Extract selected pages or branch into a new workspace',
body: 'Extract selected pages when you only need a download. Open the selection as a new workspace when you want to continue working on that subset.',
},
{
title: "5. Save your workspace or export a PDF",
body: "Saving a workspace keeps the current working state in this browser. Exporting creates a new PDF file for download.",
title: '5. Save your workspace or export a PDF',
body: 'Saving a workspace keeps the current working state in this browser. Exporting creates a new PDF file for download.',
},
{
title: "6. Use history deliberately",
body: "Each workspace operation is stored as a command with label and timestamp. Undo and redo walk through that command history.",
title: '6. Use history deliberately',
body: 'Each workspace operation is stored as a command with label and timestamp. Undo and redo walk through that command history.',
},
];
@@ -63,7 +63,7 @@ const HelpDialog: React.FC<HelpDialogProps> = ({ open, onClose }) => {
if (!open) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key !== "Escape") return;
if (e.key !== 'Escape') return;
e.preventDefault();
e.stopPropagation();
@@ -71,10 +71,10 @@ const HelpDialog: React.FC<HelpDialogProps> = ({ open, onClose }) => {
onClose();
};
window.addEventListener("keydown", handleKeyDown, { capture: true });
window.addEventListener('keydown', handleKeyDown, { capture: true });
return () => {
window.removeEventListener("keydown", handleKeyDown, { capture: true });
window.removeEventListener('keydown', handleKeyDown, { capture: true });
};
}, [open, onClose]);

View File

@@ -1,5 +1,5 @@
import React from "react";
import { APP_VERSION } from "../version";
import React from 'react';
import { APP_VERSION } from '../version';
interface LayoutProps {
children: React.ReactNode;

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useRef } from "react";
import type { PdfFile } from "../pdf/pdfTypes";
import * as pdfjsLib from "pdfjs-dist";
import pdfjsWorker from "pdfjs-dist/build/pdf.worker?worker&url";
import React, { useEffect, useRef } from 'react';
import type { PdfFile } from '../pdf/pdfTypes';
import * as pdfjsLib from 'pdfjs-dist';
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker?worker&url';
// pdf.js worker setup
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -43,28 +43,28 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
if (!isOpen) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") {
if (e.key === 'Escape') {
e.preventDefault();
onClose();
return;
}
if (e.key === "ArrowLeft" && canGoPrevious) {
if (e.key === 'ArrowLeft' && canGoPrevious) {
e.preventDefault();
onPrevious();
return;
}
if (e.key === "ArrowRight" && canGoNext) {
if (e.key === 'ArrowRight' && canGoNext) {
e.preventDefault();
onNext();
}
};
window.addEventListener("keydown", handleKeyDown);
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener('keydown', handleKeyDown);
};
}, [isOpen, canGoPrevious, canGoNext, onPrevious, onNext, onClose]);
@@ -77,7 +77,7 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
try {
const canvas = canvasRef.current;
if (canvas) {
const ctx = canvas.getContext("2d");
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
@@ -102,7 +102,7 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
const scale = Math.min(
maxWidth / viewport.width,
maxHeight / viewport.height,
maxHeight / viewport.height
);
const scaledViewport = page.getViewport({ scale });
@@ -110,7 +110,7 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
const visibleCanvas = canvasRef.current;
if (!visibleCanvas) return;
const visibleCtx = visibleCanvas.getContext("2d");
const visibleCtx = visibleCanvas.getContext('2d');
if (!visibleCtx) return;
let canvasWidth = scaledViewport.width;
@@ -126,14 +126,15 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
visibleCanvas.width = canvasWidth;
visibleCanvas.height = canvasHeight;
const baseCanvas = document.createElement("canvas");
const baseCtx = baseCanvas.getContext("2d");
const baseCanvas = document.createElement('canvas');
const baseCtx = baseCanvas.getContext('2d');
if (!baseCtx) return;
baseCanvas.width = scaledViewport.width;
baseCanvas.height = scaledViewport.height;
const renderTask = page.render({
canvas: baseCanvas,
canvasContext: baseCtx,
viewport: scaledViewport,
});
@@ -161,7 +162,7 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
visibleCtx.drawImage(baseCanvas, 0, 0);
visibleCtx.restore();
} catch (e) {
console.error("Error rendering preview", e);
console.error('Error rendering preview', e);
}
})();
@@ -181,30 +182,30 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
<div
onClick={onClose}
style={{
position: "fixed",
position: 'fixed',
inset: 0,
background: "rgba(15, 23, 42, 0.8)",
background: 'rgba(15, 23, 42, 0.8)',
zIndex: 50,
display: "flex",
justifyContent: "center",
alignItems: "center",
padding: "1rem",
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: '1rem',
}}
>
<div
onClick={(e) => e.stopPropagation()}
style={{
position: "relative",
background: "#111827",
borderRadius: "0.75rem",
padding: "0.75rem",
maxWidth: "90vw",
maxHeight: "90vh",
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "0.5rem",
overflow: "visible",
position: 'relative',
background: '#111827',
borderRadius: '0.75rem',
padding: '0.75rem',
maxWidth: '90vw',
maxHeight: '90vh',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '0.5rem',
overflow: 'visible',
}}
>
{/* Previous page */}
@@ -216,22 +217,22 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
}}
disabled={!canGoPrevious}
style={{
position: "absolute",
position: 'absolute',
left: 0,
top: "50%",
transform: "translate(-50%, -50%)",
width: "2.5rem",
height: "2.5rem",
borderRadius: "999px",
border: "none",
background: canGoPrevious ? "#374151" : "#1f2937",
color: canGoPrevious ? "#e5e7eb" : "#6b7280",
cursor: canGoPrevious ? "pointer" : "default",
fontSize: "1.35rem",
top: '50%',
transform: 'translate(-50%, -50%)',
width: '2.5rem',
height: '2.5rem',
borderRadius: '999px',
border: 'none',
background: canGoPrevious ? '#374151' : '#1f2937',
color: canGoPrevious ? '#e5e7eb' : '#6b7280',
cursor: canGoPrevious ? 'pointer' : 'default',
fontSize: '1.35rem',
lineHeight: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 2,
}}
title="Previous page (←)"
@@ -249,22 +250,22 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
}}
disabled={!canGoNext}
style={{
position: "absolute",
position: 'absolute',
right: 0,
top: "50%",
transform: "translate(50%, -50%)",
width: "2.5rem",
height: "2.5rem",
borderRadius: "999px",
border: "none",
background: canGoNext ? "#374151" : "#1f2937",
color: canGoNext ? "#e5e7eb" : "#6b7280",
cursor: canGoNext ? "pointer" : "default",
fontSize: "1.35rem",
top: '50%',
transform: 'translate(50%, -50%)',
width: '2.5rem',
height: '2.5rem',
borderRadius: '999px',
border: 'none',
background: canGoNext ? '#374151' : '#1f2937',
color: canGoNext ? '#e5e7eb' : '#6b7280',
cursor: canGoNext ? 'pointer' : 'default',
fontSize: '1.35rem',
lineHeight: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 2,
}}
title="Next page (→)"
@@ -281,22 +282,22 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
onClose();
}}
style={{
position: "absolute",
position: 'absolute',
top: 0,
right: 0,
transform: "translate(50%, -50%)",
width: "2.25rem",
height: "2.25rem",
borderRadius: "999px",
border: "none",
background: "#374151",
color: "#e5e7eb",
cursor: "pointer",
fontSize: "1.2rem",
transform: 'translate(50%, -50%)',
width: '2.25rem',
height: '2.25rem',
borderRadius: '999px',
border: 'none',
background: '#374151',
color: '#e5e7eb',
cursor: 'pointer',
fontSize: '1.2rem',
lineHeight: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 3,
}}
title="Close preview (Esc)"
@@ -308,14 +309,14 @@ const PagePreviewModal: React.FC<PagePreviewModalProps> = ({
<canvas
ref={canvasRef}
style={{
maxWidth: "100%",
maxHeight: "75vh",
background: "white",
borderRadius: "0.5rem",
maxWidth: '100%',
maxHeight: '75vh',
background: 'white',
borderRadius: '0.5rem',
}}
/>
<div style={{ color: "#e5e7eb", fontSize: "0.85rem" }}>
<div style={{ color: '#e5e7eb', fontSize: '0.85rem' }}>
{positionLabel} · Original page {pageIndex + 1} · Rot {rotation}°
</div>
</div>

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React, { useEffect } from 'react';
interface CopyPagesDialogProps {
selectedCount: number;
@@ -21,16 +21,16 @@ const CopyPagesDialog: React.FC<CopyPagesDialogProps> = ({
}) => {
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") {
if (e.key === 'Escape') {
e.preventDefault();
onCancel();
}
};
window.addEventListener("keydown", handleKeyDown);
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener('keydown', handleKeyDown);
};
}, [onCancel]);
@@ -45,43 +45,43 @@ const CopyPagesDialog: React.FC<CopyPagesDialogProps> = ({
}
}}
style={{
position: "fixed",
position: 'fixed',
inset: 0,
zIndex: 60,
background: "rgba(15, 23, 42, 0.55)",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "1rem",
background: 'rgba(15, 23, 42, 0.55)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '1rem',
}}
>
<form
onSubmit={onConfirm}
style={{
width: "100%",
maxWidth: "420px",
background: "white",
borderRadius: "0.75rem",
boxShadow: "0 20px 40px rgba(15, 23, 42, 0.35)",
padding: "1rem",
display: "flex",
flexDirection: "column",
gap: "0.75rem",
width: '100%',
maxWidth: '420px',
background: 'white',
borderRadius: '0.75rem',
boxShadow: '0 20px 40px rgba(15, 23, 42, 0.35)',
padding: '1rem',
display: 'flex',
flexDirection: 'column',
gap: '0.75rem',
}}
>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
gap: "0.75rem",
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: '0.75rem',
}}
>
<h2
id="copy-pages-dialog-title"
style={{
margin: 0,
fontSize: "1rem",
fontSize: '1rem',
}}
>
Copy selected pages
@@ -91,18 +91,18 @@ const CopyPagesDialog: React.FC<CopyPagesDialogProps> = ({
type="button"
onClick={onCancel}
style={{
border: "none",
borderRadius: "999px",
width: "1.8rem",
height: "1.8rem",
background: "#e5e7eb",
color: "#111827",
cursor: "pointer",
fontSize: "1.1rem",
border: 'none',
borderRadius: '999px',
width: '1.8rem',
height: '1.8rem',
background: '#e5e7eb',
color: '#111827',
cursor: 'pointer',
fontSize: '1.1rem',
lineHeight: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
aria-label="Close copy dialog"
>
@@ -113,25 +113,25 @@ const CopyPagesDialog: React.FC<CopyPagesDialogProps> = ({
<p
style={{
margin: 0,
fontSize: "0.9rem",
color: "#4b5563",
fontSize: '0.9rem',
color: '#4b5563',
}}
>
Copy{" "}
Copy{' '}
<strong>
{selectedCount === 1
? "1 selected page"
? '1 selected page'
: `${selectedCount} selected pages`}
</strong>{" "}
</strong>{' '}
to a new position.
</p>
<label
style={{
display: "flex",
flexDirection: "column",
gap: "0.25rem",
fontSize: "0.9rem",
display: 'flex',
flexDirection: 'column',
gap: '0.25rem',
fontSize: '0.9rem',
}}
>
Insert before position
@@ -143,18 +143,18 @@ const CopyPagesDialog: React.FC<CopyPagesDialogProps> = ({
autoFocus
onChange={(e) => onTargetPositionChange(e.target.value)}
style={{
padding: "0.45rem 0.55rem",
borderRadius: "0.5rem",
border: "1px solid #d1d5db",
fontSize: "0.95rem",
padding: '0.45rem 0.55rem',
borderRadius: '0.5rem',
border: '1px solid #d1d5db',
fontSize: '0.95rem',
}}
/>
</label>
<div
style={{
fontSize: "0.8rem",
color: "#6b7280",
fontSize: '0.8rem',
color: '#6b7280',
lineHeight: 1.4,
}}
>
@@ -165,12 +165,12 @@ const CopyPagesDialog: React.FC<CopyPagesDialogProps> = ({
{error && (
<div
style={{
borderRadius: "0.5rem",
background: "#fef2f2",
border: "1px solid #fecaca",
color: "#b91c1c",
padding: "0.5rem",
fontSize: "0.85rem",
borderRadius: '0.5rem',
background: '#fef2f2',
border: '1px solid #fecaca',
color: '#b91c1c',
padding: '0.5rem',
fontSize: '0.85rem',
}}
>
{error}
@@ -179,23 +179,23 @@ const CopyPagesDialog: React.FC<CopyPagesDialogProps> = ({
<div
style={{
display: "flex",
justifyContent: "flex-end",
gap: "0.5rem",
marginTop: "0.25rem",
display: 'flex',
justifyContent: 'flex-end',
gap: '0.5rem',
marginTop: '0.25rem',
}}
>
<button
type="button"
onClick={onCancel}
style={{
border: "none",
borderRadius: "0.5rem",
padding: "0.45rem 0.8rem",
background: "#e5e7eb",
color: "#111827",
cursor: "pointer",
fontSize: "0.9rem",
border: 'none',
borderRadius: '0.5rem',
padding: '0.45rem 0.8rem',
background: '#e5e7eb',
color: '#111827',
cursor: 'pointer',
fontSize: '0.9rem',
}}
>
Cancel
@@ -204,13 +204,13 @@ const CopyPagesDialog: React.FC<CopyPagesDialogProps> = ({
<button
type="submit"
style={{
border: "none",
borderRadius: "0.5rem",
padding: "0.45rem 0.8rem",
background: "#16a34a",
color: "white",
cursor: "pointer",
fontSize: "0.9rem",
border: 'none',
borderRadius: '0.5rem',
padding: '0.45rem 0.8rem',
background: '#16a34a',
color: 'white',
cursor: 'pointer',
fontSize: '0.9rem',
}}
>
Copy pages

View File

@@ -1,23 +1,23 @@
import React from "react";
import React from 'react';
interface DropIndicatorProps {
side: "left" | "right" | "end";
side: 'left' | 'right' | 'end';
color: string;
}
const DropIndicator: React.FC<DropIndicatorProps> = ({ side, color }) => {
const isEnd = side === "end";
const isEnd = side === 'end';
return (
<div
style={{
position: "absolute",
left: side === "left" ? "-4px" : isEnd ? "8px" : undefined,
right: side === "right" ? "-4px" : undefined,
top: "4px",
bottom: "4px",
width: "3px",
borderRadius: "999px",
position: 'absolute',
left: side === 'left' ? '-4px' : isEnd ? '8px' : undefined,
right: side === 'right' ? '-4px' : undefined,
top: '4px',
bottom: '4px',
width: '3px',
borderRadius: '999px',
background: color,
}}
/>

View File

@@ -1,6 +1,6 @@
import React from "react";
import type { PageRef } from "../../pdf/pdfTypes";
import DropIndicator from "./DropIndicator";
import React from 'react';
import type { PageRef } from '../../pdf/pdfTypes';
import DropIndicator from './DropIndicator';
interface PageCardProps {
page: PageRef;
@@ -24,11 +24,11 @@ interface PageCardProps {
}
const pageActionButtonStyle: React.CSSProperties = {
border: "none",
borderRadius: "999px",
padding: "0.15rem 0.4rem",
fontSize: "0.75rem",
cursor: "pointer",
border: 'none',
borderRadius: '999px',
padding: '0.15rem 0.4rem',
fontSize: '0.75rem',
cursor: 'pointer',
};
const PageCard: React.FC<PageCardProps> = ({
@@ -53,11 +53,11 @@ const PageCard: React.FC<PageCardProps> = ({
}) => {
const background = isDraggingCard
? isCopyDragging
? "#dcfce7"
: "#dbeafe"
? '#dcfce7'
: '#dbeafe'
: selected
? "#eff6ff"
: "#f9fafb";
? '#eff6ff'
: '#f9fafb';
return (
<div
@@ -67,17 +67,17 @@ const PageCard: React.FC<PageCardProps> = ({
onDragOver={onDragOver}
onClick={onOpenPreview}
style={{
position: "relative",
width: "162px",
padding: "0.4rem",
borderRadius: "0.5rem",
border: "1px solid #e5e7eb",
position: 'relative',
width: '162px',
padding: '0.4rem',
borderRadius: '0.5rem',
border: '1px solid #e5e7eb',
background,
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "0.25rem",
cursor: isBusy ? "default" : isCopyDragging ? "copy" : "grab",
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '0.25rem',
cursor: isBusy ? 'default' : isCopyDragging ? 'copy' : 'grab',
opacity: isBusy ? 0.7 : 1,
}}
>
@@ -85,21 +85,21 @@ const PageCard: React.FC<PageCardProps> = ({
type="button"
onClick={onToggleSelect}
style={{
position: "absolute",
top: "4px",
left: "4px",
width: "20px",
height: "20px",
borderRadius: "0.4rem",
border: "1px solid #9ca3af",
background: selected ? "#2563eb" : "rgba(255,255,255,0.9)",
color: selected ? "white" : "transparent",
fontSize: "0.8rem",
display: "flex",
alignItems: "center",
justifyContent: "center",
position: 'absolute',
top: '4px',
left: '4px',
width: '20px',
height: '20px',
borderRadius: '0.4rem',
border: '1px solid #9ca3af',
background: selected ? '#2563eb' : 'rgba(255,255,255,0.9)',
color: selected ? 'white' : 'transparent',
fontSize: '0.8rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
cursor: "pointer",
cursor: 'pointer',
}}
title="Select page"
>
@@ -113,11 +113,11 @@ const PageCard: React.FC<PageCardProps> = ({
<div
style={{
width: "110px",
height: "90px",
display: "flex",
alignItems: "center",
justifyContent: "center",
width: '110px',
height: '90px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{thumbnail ? (
@@ -125,41 +125,41 @@ const PageCard: React.FC<PageCardProps> = ({
src={thumbnail}
alt={`Page ${page.sourcePageIndex + 1}`}
style={{
maxWidth: "100%",
maxHeight: "100%",
width: "auto",
height: "auto",
objectFit: "contain",
borderRadius: "0.25rem",
border: "1px solid #e5e7eb",
background: "white",
maxWidth: '100%',
maxHeight: '100%',
width: 'auto',
height: 'auto',
objectFit: 'contain',
borderRadius: '0.25rem',
border: '1px solid #e5e7eb',
background: 'white',
}}
/>
) : (
<div
style={{
width: "60px",
height: "80px",
borderRadius: "0.25rem",
border: "1px dashed #d1d5db",
background: "#f3f4f6",
width: '60px',
height: '80px',
borderRadius: '0.25rem',
border: '1px dashed #d1d5db',
background: '#f3f4f6',
}}
/>
)}
</div>
<span style={{ fontSize: "0.8rem" }}>
<span style={{ fontSize: '0.8rem' }}>
Page {page.sourcePageIndex + 1}
</span>
<span style={{ fontSize: "0.7rem", color: "#6b7280" }}>
<span style={{ fontSize: '0.7rem', color: '#6b7280' }}>
Pos {visualIndex + 1} · Rot {page.rotation}°
</span>
<div
style={{
display: "flex",
gap: "0.25rem",
marginTop: "0.25rem",
display: 'flex',
gap: '0.25rem',
marginTop: '0.25rem',
}}
>
<button
@@ -170,7 +170,7 @@ const PageCard: React.FC<PageCardProps> = ({
}}
style={{
...pageActionButtonStyle,
background: "#e5e7eb",
background: '#e5e7eb',
}}
>
90°
@@ -184,7 +184,7 @@ const PageCard: React.FC<PageCardProps> = ({
}}
style={{
...pageActionButtonStyle,
background: "#e5e7eb",
background: '#e5e7eb',
}}
>
90°
@@ -198,8 +198,8 @@ const PageCard: React.FC<PageCardProps> = ({
}}
style={{
...pageActionButtonStyle,
background: "#fecaca",
color: "#b91c1c",
background: '#fecaca',
color: '#b91c1c',
}}
title="Remove this page from the exported PDF"
>

View File

@@ -1,7 +1,7 @@
import React from "react";
import type { PageRef } from "../../pdf/pdfTypes";
import DropIndicator from "./DropIndicator";
import PageCard from "./PageCard";
import React from 'react';
import type { PageRef } from '../../pdf/pdfTypes';
import DropIndicator from './DropIndicator';
import PageCard from './PageCard';
interface PageGridProps {
pages: PageRef[];
@@ -16,14 +16,14 @@ interface PageGridProps {
onDragStart: (visualIndex: number) => React.DragEventHandler<HTMLDivElement>;
onDragEnd: React.DragEventHandler<HTMLDivElement>;
onCardDragOver: (
visualIndex: number,
visualIndex: number
) => React.DragEventHandler<HTMLDivElement>;
onEndSlotDragOver: React.DragEventHandler<HTMLDivElement>;
onDrop: React.DragEventHandler<HTMLDivElement>;
onOpenPreview: (pageId: string) => void;
onToggleSelect: (
pageId: string,
visualIndex: number,
visualIndex: number
) => React.MouseEventHandler<HTMLButtonElement>;
onRotateClockwise: (pageId: string) => void;
onRotateCounterclockwise: (pageId: string) => void;
@@ -67,11 +67,11 @@ const PageGrid: React.FC<PageGridProps> = ({
return (
<div
style={{
display: "flex",
flexWrap: "wrap",
gap: "0.5rem",
alignItems: "flex-start",
marginBottom: "0.75rem",
display: 'flex',
flexWrap: 'wrap',
gap: '0.5rem',
alignItems: 'flex-start',
marginBottom: '0.75rem',
}}
onDrop={onDrop}
>
@@ -112,10 +112,10 @@ const PageGrid: React.FC<PageGridProps> = ({
onDragOver={onEndSlotDragOver}
onDrop={onDrop}
style={{
width: "20px",
height: "120px",
position: "relative",
alignSelf: "stretch",
width: '20px',
height: '120px',
position: 'relative',
alignSelf: 'stretch',
}}
>
{showEndLine() && (

View File

@@ -1,4 +1,4 @@
import React from "react";
import React from 'react';
interface PageSelectionToolbarProps {
selectedCount: number;
@@ -9,10 +9,10 @@ interface PageSelectionToolbarProps {
}
const pillButtonStyle: React.CSSProperties = {
border: "none",
borderRadius: "999px",
padding: "0.15rem 0.6rem",
fontSize: "0.8rem",
border: 'none',
borderRadius: '999px',
padding: '0.15rem 0.6rem',
fontSize: '0.8rem',
};
const PageSelectionToolbar: React.FC<PageSelectionToolbarProps> = ({
@@ -27,11 +27,11 @@ const PageSelectionToolbar: React.FC<PageSelectionToolbarProps> = ({
return (
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "0.5rem",
fontSize: "0.85rem",
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '0.5rem',
fontSize: '0.85rem',
}}
>
<span>
@@ -40,10 +40,10 @@ const PageSelectionToolbar: React.FC<PageSelectionToolbarProps> = ({
<div
style={{
display: "flex",
gap: "0.4rem",
flexWrap: "wrap",
justifyContent: "flex-end",
display: 'flex',
gap: '0.4rem',
flexWrap: 'wrap',
justifyContent: 'flex-end',
}}
>
{hasSelection && (
@@ -53,9 +53,9 @@ const PageSelectionToolbar: React.FC<PageSelectionToolbarProps> = ({
disabled={!hasSelection}
style={{
...pillButtonStyle,
background: "#dcfce7",
color: "#166534",
cursor: "pointer",
background: '#dcfce7',
color: '#166534',
cursor: 'pointer',
}}
title="Copy selected pages to another position"
>
@@ -69,9 +69,9 @@ const PageSelectionToolbar: React.FC<PageSelectionToolbarProps> = ({
onClick={onDeleteSelected}
style={{
...pillButtonStyle,
background: "#fee2e2",
color: "#b91c1c",
cursor: "pointer",
background: '#fee2e2',
color: '#b91c1c',
cursor: 'pointer',
}}
>
Delete selected
@@ -83,9 +83,9 @@ const PageSelectionToolbar: React.FC<PageSelectionToolbarProps> = ({
onClick={onSelectAll}
style={{
...pillButtonStyle,
background: "#8dcd8d",
color: "#111827",
cursor: "pointer",
background: '#8dcd8d',
color: '#111827',
cursor: 'pointer',
}}
>
Select all
@@ -97,9 +97,9 @@ const PageSelectionToolbar: React.FC<PageSelectionToolbarProps> = ({
disabled={!hasSelection}
style={{
...pillButtonStyle,
background: "#e5e7eb",
color: hasSelection ? "#111827" : "#6b7280",
cursor: hasSelection ? "pointer" : "default",
background: '#e5e7eb',
color: hasSelection ? '#111827' : '#6b7280',
cursor: hasSelection ? 'pointer' : 'default',
}}
>
Clear selection

View File

@@ -1,8 +1,8 @@
import React, { useRef, useState } from "react";
import type { PageRef } from "../pdf/pdfTypes";
import CopyPagesDialog from "./PageWorkspace/CopyPagesDialog";
import PageGrid from "./PageWorkspace/PageGrid";
import PageSelectionToolbar from "./PageWorkspace/PageSelectionToolbar";
import React, { useRef, useState } from 'react';
import type { PageRef } from '../pdf/pdfTypes';
import CopyPagesDialog from './PageWorkspace/CopyPagesDialog';
import PageGrid from './PageWorkspace/PageGrid';
import PageSelectionToolbar from './PageWorkspace/PageSelectionToolbar';
interface ReorderPanelProps {
pages: PageRef[];
@@ -20,7 +20,7 @@ interface ReorderPanelProps {
onToggleSelect: (
pageId: string,
visualIndex: number,
e: React.MouseEvent<HTMLButtonElement>,
e: React.MouseEvent<HTMLButtonElement>
) => void;
onSelectAll: () => void;
@@ -51,7 +51,7 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
const [isCopyDragging, setIsCopyDragging] = useState(false);
const [copyDialogOpen, setCopyDialogOpen] = useState(false);
const [copyTargetPosition, setCopyTargetPosition] = useState("");
const [copyTargetPosition, setCopyTargetPosition] = useState('');
const [copyDialogError, setCopyDialogError] = useState<string | null>(null);
const dragGhostRef = useRef<HTMLDivElement | null>(null);
@@ -72,7 +72,7 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
if (!draggedPage) return [];
const selectedInVisualOrder = pages.filter((page) =>
selectedPageIds.includes(page.id),
selectedPageIds.includes(page.id)
);
const draggingIsSelected =
@@ -85,20 +85,20 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
const createDragGhost = (e: React.DragEvent, count: number) => {
cleanupDragGhost();
const ghost = document.createElement("div");
ghost.textContent = count === 1 ? "1 page" : `${count} pages`;
const ghost = document.createElement('div');
ghost.textContent = count === 1 ? '1 page' : `${count} pages`;
ghost.style.position = "fixed";
ghost.style.top = "0";
ghost.style.left = "0";
ghost.style.padding = "4px 8px";
ghost.style.borderRadius = "999px";
ghost.style.background = "#111827";
ghost.style.color = "#e5e7eb";
ghost.style.fontSize = "12px";
ghost.style.position = 'fixed';
ghost.style.top = '0';
ghost.style.left = '0';
ghost.style.padding = '4px 8px';
ghost.style.borderRadius = '999px';
ghost.style.background = '#111827';
ghost.style.color = '#e5e7eb';
ghost.style.fontSize = '12px';
ghost.style.fontFamily =
'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
ghost.style.zIndex = "9999";
ghost.style.zIndex = '9999';
document.body.appendChild(ghost);
dragGhostRef.current = ghost;
@@ -121,9 +121,9 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
const copying = isCopyModifierPressed(e);
setIsCopyDragging(copying);
e.dataTransfer.effectAllowed = "copyMove";
e.dataTransfer.dropEffect = copying ? "copy" : "move";
e.dataTransfer.setData("text/plain", String(visualIndex));
e.dataTransfer.effectAllowed = 'copyMove';
e.dataTransfer.dropEffect = copying ? 'copy' : 'move';
e.dataTransfer.setData('text/plain', String(visualIndex));
const draggedPages = getDraggedPages(visualIndex);
createDragGhost(e, draggedPages.length);
@@ -141,7 +141,7 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
const copying = isCopyModifierPressed(e);
setIsCopyDragging(copying);
e.dataTransfer.dropEffect = copying ? "copy" : "move";
e.dataTransfer.dropEffect = copying ? 'copy' : 'move';
const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();
const x = e.clientX - rect.left;
@@ -158,7 +158,7 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
const copying = isCopyModifierPressed(e);
setIsCopyDragging(copying);
e.dataTransfer.dropEffect = copying ? "copy" : "move";
e.dataTransfer.dropEffect = copying ? 'copy' : 'move';
setDropIndex(pages.length);
};
@@ -177,7 +177,7 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
if (shouldCopy) {
onCopyPagesToSlot(
draggedPages.map((page) => page.id),
dropIndex,
dropIndex
);
setDraggingIndex(null);
@@ -247,7 +247,7 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
e?.preventDefault();
if (selectedPageIds.length === 0) {
setCopyDialogError("No pages selected.");
setCopyDialogError('No pages selected.');
return;
}
@@ -279,13 +279,13 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
draggingPage != null &&
selectedPageIds.length > 0 &&
selectedPageIds.includes(draggingPage.id);
const dropIndicatorColor = isCopyDragging ? "#16a34a" : "#2563eb";
const dropIndicatorColor = isCopyDragging ? '#16a34a' : '#2563eb';
return (
<>
<div className="card">
<h2>Pages</h2>
<p style={{ fontSize: "0.85rem", color: "#6b7280" }}>
<p style={{ fontSize: '0.85rem', color: '#6b7280' }}>
Tap/click a page to preview it. Use the checkbox to select pages
(Shift for ranges). Drag to reorder; dragging a selected page moves
the whole selection. Hold Ctrl/ while dropping to copy instead of

View File

@@ -1,6 +1,6 @@
import React from "react";
import type { WorkspaceSummary } from "../workspace/workspaceTypes";
import type { WorkspaceCommandRecord } from "../workspace/workspaceCommands";
import React from 'react';
import type { WorkspaceSummary } from '../workspace/workspaceTypes';
import type { WorkspaceCommandRecord } from '../workspace/workspaceCommands';
interface WorkspacePanelProps {
hasPdf: boolean;
@@ -54,17 +54,17 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
<div className="card">
<h2>Workspace</h2>
<p style={{ fontSize: "0.85rem", color: "#6b7280" }}>
<p style={{ fontSize: '0.85rem', color: '#6b7280' }}>
Save named workspaces in this browser. PDF binaries are stored in
IndexedDB; nothing is uploaded.
</p>
<div
style={{
display: "flex",
gap: "0.5rem",
flexWrap: "wrap",
alignItems: "center",
display: 'flex',
gap: '0.5rem',
flexWrap: 'wrap',
alignItems: 'center',
}}
>
<input
@@ -74,12 +74,12 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
placeholder="Workspace name"
disabled={!hasPdf || isBusy}
style={{
flex: "1 1 220px",
flex: '1 1 220px',
minWidth: 0,
padding: "0.45rem 0.55rem",
borderRadius: "0.5rem",
border: "1px solid #d1d5db",
fontSize: "0.9rem",
padding: '0.45rem 0.55rem',
borderRadius: '0.5rem',
border: '1px solid #d1d5db',
fontSize: '0.9rem',
}}
/>
@@ -88,7 +88,7 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
className="secondary"
onClick={onUndo}
disabled={!hasPdf || isBusy || !canUndo}
title={latestUndo ? `Undo: ${latestUndo.label}` : "Nothing to undo"}
title={latestUndo ? `Undo: ${latestUndo.label}` : 'Nothing to undo'}
>
Undo
</button>
@@ -98,7 +98,7 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
className="secondary"
onClick={onRedo}
disabled={!hasPdf || isBusy || !canRedo}
title={latestRedo ? `Redo: ${latestRedo.label}` : "Nothing to redo"}
title={latestRedo ? `Redo: ${latestRedo.label}` : 'Nothing to redo'}
>
Redo
</button>
@@ -108,9 +108,9 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
className="secondary"
onClick={onSaveWorkspace}
disabled={!hasPdf || isBusy}
title={!hasPdf ? "Open a PDF first" : "Save workspace"}
title={!hasPdf ? 'Open a PDF first' : 'Save workspace'}
>
💾 {activeWorkspaceId ? "Save" : "Save as"}
💾 {activeWorkspaceId ? 'Save' : 'Save as'}
</button>
<button
@@ -119,7 +119,7 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
onClick={onResetWorkspace}
disabled={!hasPdf || isBusy}
title={
!hasPdf ? "No active workspace" : "Close the current workspace"
!hasPdf ? 'No active workspace' : 'Close the current workspace'
}
>
Reset workspace
@@ -138,9 +138,9 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
{workspaceDirty && hasPdf && (
<div
style={{
marginTop: "0.5rem",
fontSize: "0.8rem",
color: "#92400e",
marginTop: '0.5rem',
fontSize: '0.8rem',
color: '#92400e',
}}
>
Unsaved workspace changes.
@@ -150,9 +150,9 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
{workspaceMessage && (
<div
style={{
marginTop: "0.5rem",
fontSize: "0.85rem",
color: "#166534",
marginTop: '0.5rem',
fontSize: '0.85rem',
color: '#166534',
}}
>
{workspaceMessage}
@@ -160,15 +160,15 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
)}
{workspaces.length > 0 && (
<div style={{ marginTop: "0.75rem" }}>
<strong style={{ fontSize: "0.9rem" }}>Saved workspaces</strong>
<div style={{ marginTop: '0.75rem' }}>
<strong style={{ fontSize: '0.9rem' }}>Saved workspaces</strong>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "0.4rem",
marginTop: "0.4rem",
display: 'flex',
flexDirection: 'column',
gap: '0.4rem',
marginTop: '0.4rem',
}}
>
{workspaces.map((workspace) => {
@@ -178,29 +178,29 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
<div
key={workspace.id}
style={{
border: "1px solid #e5e7eb",
borderRadius: "0.5rem",
padding: "0.5rem",
background: active ? "#eff6ff" : "#f9fafb",
display: "flex",
justifyContent: "space-between",
gap: "0.75rem",
alignItems: "center",
flexWrap: "wrap",
border: '1px solid #e5e7eb',
borderRadius: '0.5rem',
padding: '0.5rem',
background: active ? '#eff6ff' : '#f9fafb',
display: 'flex',
justifyContent: 'space-between',
gap: '0.75rem',
alignItems: 'center',
flexWrap: 'wrap',
}}
>
<div style={{ minWidth: 0 }}>
<div style={{ fontSize: "0.9rem" }}>
<div style={{ fontSize: '0.9rem' }}>
<strong>{workspace.name}</strong>
{active && (
<span style={{ color: "#2563eb" }}> · active</span>
<span style={{ color: '#2563eb' }}> · active</span>
)}
</div>
<div style={{ fontSize: "0.75rem", color: "#6b7280" }}>
{workspace.pdfName} · source pages:{" "}
{workspace.sourcePageCount} · workspace pages:{" "}
{workspace.workspacePageCount} · undo:{" "}
<div style={{ fontSize: '0.75rem', color: '#6b7280' }}>
{workspace.pdfName} · source pages:{' '}
{workspace.sourcePageCount} · workspace pages:{' '}
{workspace.workspacePageCount} · undo:{' '}
{workspace.historyCount} · redo: {workspace.redoCount} ·
updated {new Date(workspace.updatedAt).toLocaleString()}
</div>
@@ -208,9 +208,9 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
<div
style={{
display: "flex",
gap: "0.35rem",
flexWrap: "wrap",
display: 'flex',
gap: '0.35rem',
flexWrap: 'wrap',
}}
>
<button
@@ -228,8 +228,8 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
disabled={isBusy}
onClick={() => onDeleteWorkspace(workspace.id)}
style={{
background: "#fee2e2",
color: "#991b1b",
background: '#fee2e2',
color: '#991b1b',
}}
>
Delete
@@ -243,36 +243,36 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
)}
{(history.length > 0 || redoHistory.length > 0) && (
<details style={{ marginTop: "0.75rem" }} open>
<summary style={{ cursor: "pointer", fontSize: "0.9rem" }}>
<details style={{ marginTop: '0.75rem' }} open>
<summary style={{ cursor: 'pointer', fontSize: '0.9rem' }}>
Command history ({history.length} undo / {redoHistory.length} redo)
</summary>
<div
style={{
marginTop: "0.5rem",
display: "flex",
flexDirection: "column",
gap: "0.25rem",
marginTop: '0.5rem',
display: 'flex',
flexDirection: 'column',
gap: '0.25rem',
}}
>
{history.map((entry, index) => (
<div
key={entry.id}
style={{
fontSize: "0.8rem",
color: "#374151",
borderLeft: "3px solid #2563eb",
paddingLeft: "0.45rem",
paddingTop: "0.2rem",
paddingBottom: "0.2rem",
fontSize: '0.8rem',
color: '#374151',
borderLeft: '3px solid #2563eb',
paddingLeft: '0.45rem',
paddingTop: '0.2rem',
paddingBottom: '0.2rem',
}}
>
<strong>
Undo {history.length - index}. {entry.label}
</strong>
<br />
<span style={{ color: "#6b7280" }}>
<span style={{ color: '#6b7280' }}>
{new Date(entry.timestamp).toLocaleString()}
</span>
</div>
@@ -280,15 +280,15 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
<div
style={{
margin: "0.25rem 0",
borderRadius: "999px",
background: "#ecfdf5",
color: "#166534",
fontSize: "0.8rem",
margin: '0.25rem 0',
borderRadius: '999px',
background: '#ecfdf5',
color: '#166534',
fontSize: '0.8rem',
fontWeight: 600,
alignSelf: "flex-start",
border: "2px solid #166534",
width: "100%",
alignSelf: 'flex-start',
border: '2px solid #166534',
width: '100%',
}}
></div>
@@ -299,12 +299,12 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
<div
key={entry.id}
style={{
fontSize: "0.8rem",
color: "#9ca3af",
borderLeft: "3px solid #d1d5db",
paddingLeft: "0.45rem",
paddingTop: "0.2rem",
paddingBottom: "0.2rem",
fontSize: '0.8rem',
color: '#9ca3af',
borderLeft: '3px solid #d1d5db',
paddingLeft: '0.45rem',
paddingTop: '0.2rem',
paddingBottom: '0.2rem',
opacity: 0.75,
}}
>
@@ -312,7 +312,7 @@ const WorkspacePanel: React.FC<WorkspacePanelProps> = ({
Redo {index + 1}. {entry.label}
</strong>
<br />
<span style={{ color: "#9ca3af" }}>
<span style={{ color: '#9ca3af' }}>
{new Date(entry.timestamp).toLocaleString()}
</span>
</div>