);
diff --git a/frontend/src/core/components/tools/compare/CompareDocumentPane.tsx b/frontend/src/core/components/tools/compare/CompareDocumentPane.tsx
index 9155bde6b9..d3a13a6fdc 100644
--- a/frontend/src/core/components/tools/compare/CompareDocumentPane.tsx
+++ b/frontend/src/core/components/tools/compare/CompareDocumentPane.tsx
@@ -111,7 +111,12 @@ const CompareDocumentPane = ({
-
+
{title ? truncateCenter(title, COMPARE_PANE_TITLE_MAX_LEN) : title}
diff --git a/frontend/src/core/components/tools/compare/compareView.css b/frontend/src/core/components/tools/compare/compareView.css
index a16b85799e..120f22415f 100644
--- a/frontend/src/core/components/tools/compare/compareView.css
+++ b/frontend/src/core/components/tools/compare/compareView.css
@@ -564,8 +564,13 @@
justify-content: center;
gap: 0.35rem;
border-radius: var(--radius-md);
- border: 1px solid color-mix(in srgb, var(--mantine-color-blue-4) 28%, var(--border-default));
- background: color-mix(in srgb, var(--mantine-color-blue-0) 92%, var(--mantine-color-body));
+ border: 1px solid
+ color-mix(in srgb, var(--mantine-color-blue-4) 28%, var(--border-default));
+ background: color-mix(
+ in srgb,
+ var(--mantine-color-blue-0) 92%,
+ var(--mantine-color-body)
+ );
color: var(--mantine-color-blue-filled);
cursor: pointer;
font: inherit;
@@ -575,8 +580,16 @@
}
.compare-step-selection__swap:hover:not(:disabled) {
- background: color-mix(in srgb, var(--mantine-color-blue-1) 75%, var(--mantine-color-body));
- border-color: color-mix(in srgb, var(--mantine-color-blue-5) 35%, var(--border-default));
+ background: color-mix(
+ in srgb,
+ var(--mantine-color-blue-1) 75%,
+ var(--mantine-color-body)
+ );
+ border-color: color-mix(
+ in srgb,
+ var(--mantine-color-blue-5) 35%,
+ var(--border-default)
+ );
}
.compare-step-selection__swap:disabled {
@@ -598,18 +611,31 @@
writing-mode: horizontal-tb;
}
-[data-mantine-color-scheme=”dark”] .compare-step-selection__swap {
- background: color-mix(in srgb, var(--mantine-color-blue-9) 22%, var(--mantine-color-dark-6));
- border-color: color-mix(in srgb, var(--mantine-color-blue-4) 40%, var(--border-default));
+[data-mantine-color-scheme="”dark”"] .compare-step-selection__swap {
+ background: color-mix(
+ in srgb,
+ var(--mantine-color-blue-9) 22%,
+ var(--mantine-color-dark-6)
+ );
+ border-color: color-mix(
+ in srgb,
+ var(--mantine-color-blue-4) 40%,
+ var(--border-default)
+ );
color: var(--mantine-color-blue-3);
}
-[data-mantine-color-scheme=”dark”] .compare-step-selection__swap:hover:not(:disabled) {
- background: color-mix(in srgb, var(--mantine-color-blue-9) 32%, var(--mantine-color-dark-6));
+[data-mantine-color-scheme="”dark”"]
+ .compare-step-selection__swap:hover:not(:disabled) {
+ background: color-mix(
+ in srgb,
+ var(--mantine-color-blue-9) 32%,
+ var(--mantine-color-dark-6)
+ );
}
-[data-mantine-color-scheme=”dark”] .compare-step-selection__swap-icon,
-[data-mantine-color-scheme=”dark”] .compare-step-selection__swap-label {
+[data-mantine-color-scheme="”dark”"] .compare-step-selection__swap-icon,
+[data-mantine-color-scheme="”dark”"] .compare-step-selection__swap-label {
color: var(--mantine-color-blue-3);
}
diff --git a/frontend/src/core/components/tools/shared/FileStatusIndicator.tsx b/frontend/src/core/components/tools/shared/FileStatusIndicator.tsx
index 48f4772d1c..08a0d0ed91 100644
--- a/frontend/src/core/components/tools/shared/FileStatusIndicator.tsx
+++ b/frontend/src/core/components/tools/shared/FileStatusIndicator.tsx
@@ -165,7 +165,11 @@ const FileStatusIndicator = ({
>
✓{" "}
{selectedFiles.length === 1 ? (
- {t("fileSelected", "{{filename}}", { filename: selectedFiles[0]?.name })}
+
+ {t("fileSelected", "{{filename}}", {
+ filename: selectedFiles[0]?.name,
+ })}
+
) : (
t("filesSelected", "{{count}} files", { count: selectedFiles.length })
)}
diff --git a/frontend/src/core/components/tools/shared/OperationButton.tsx b/frontend/src/core/components/tools/shared/OperationButton.tsx
index 0a516f3259..3993953eab 100644
--- a/frontend/src/core/components/tools/shared/OperationButton.tsx
+++ b/frontend/src/core/components/tools/shared/OperationButton.tsx
@@ -58,7 +58,10 @@ const OperationButton = ({
"tool.endpointUnavailable",
"This tool is unavailable on your server.",
),
- filesLoading: t("tool.filesLoading", "Files are still loading, please wait."),
+ filesLoading: t(
+ "tool.filesLoading",
+ "Files are still loading, please wait.",
+ ),
noFiles: t("tool.noFiles", "Add a file to get started."),
invalidParams: t("tool.invalidParams", "Fill in the required settings."),
viewerMode: t(
diff --git a/frontend/src/core/components/tools/shared/ScopedOperationButton.tsx b/frontend/src/core/components/tools/shared/ScopedOperationButton.tsx
index 6283059f5a..1761c91e4b 100644
--- a/frontend/src/core/components/tools/shared/ScopedOperationButton.tsx
+++ b/frontend/src/core/components/tools/shared/ScopedOperationButton.tsx
@@ -34,7 +34,9 @@ export function ScopedOperationButton({
// Disable until all files are hydrated — running early would silently skip unloaded files.
const isFilesHydrating = fileIds.length > allFiles.length;
const effectiveDisabledReason =
- isFilesHydrating && props.disabledReason !== "endpointUnavailable" ? "filesLoading" : props.disabledReason;
+ isFilesHydrating && props.disabledReason !== "endpointUnavailable"
+ ? "filesLoading"
+ : props.disabledReason;
const isViewerMode = workbench === "viewer";
const isFileEditorMode = workbench === "fileEditor";
@@ -62,7 +64,9 @@ export function ScopedOperationButton({
const pendingCount = fileIds.length - allFiles.length;
const isBulkLoading = pendingCount > 1;
- const loadProgress = isBulkLoading ? (allFiles.length / fileIds.length) * 100 : 0;
+ const loadProgress = isBulkLoading
+ ? (allFiles.length / fileIds.length) * 100
+ : 0;
const showSelectFilesHint =
!disableScopeHints &&
isFileEditorMode &&
@@ -79,7 +83,12 @@ export function ScopedOperationButton({
loading={!isBulkLoading}
variant={props.variant ?? "filled"}
color={props.color ?? "blue"}
- style={{ position: "relative", overflow: "hidden", minHeight: "2.5rem", pointerEvents: "none" }}
+ style={{
+ position: "relative",
+ overflow: "hidden",
+ minHeight: "2.5rem",
+ pointerEvents: "none",
+ }}
>
{isBulkLoading && (
<>
@@ -94,17 +103,25 @@ export function ScopedOperationButton({
}}
/>
- {t("tool.filesLoadingProgress", "{{loaded}} / {{total}} files loading...", {
- loaded: allFiles.length,
- total: fileIds.length,
- })}
+ {t(
+ "tool.filesLoadingProgress",
+ "{{loaded}} / {{total}} files loading...",
+ {
+ loaded: allFiles.length,
+ total: fileIds.length,
+ },
+ )}
>
)}
) : (
-
+
)}
{viewerFileName && (
diff --git a/frontend/src/core/components/tools/toolPicker/ToolButton.tsx b/frontend/src/core/components/tools/toolPicker/ToolButton.tsx
index 7ca1c5ad32..d4021d794a 100644
--- a/frontend/src/core/components/tools/toolPicker/ToolButton.tsx
+++ b/frontend/src/core/components/tools/toolPicker/ToolButton.tsx
@@ -201,7 +201,9 @@ const ToolButton: React.FC = ({
handleUnlessSpecialClick(e, () => handleClick(id));
};
- const selectedStyles = isSelected ? { backgroundColor: "#EAEAEA", color: "var(--tools-text-and-icon-color)" } : {};
+ const selectedStyles = isSelected
+ ? { backgroundColor: "#EAEAEA", color: "var(--tools-text-and-icon-color)" }
+ : {};
const buttonElement = navProps ? (
// For internal tools with URLs, render Button as an anchor for proper link behavior
@@ -295,7 +297,12 @@ const ToolButton: React.FC = ({
return (
{star}
-
+
{buttonElement}
diff --git a/frontend/src/core/components/tools/toolPicker/ToolSearch.tsx b/frontend/src/core/components/tools/toolPicker/ToolSearch.tsx
index 3ba38bbe50..ae136c4333 100644
--- a/frontend/src/core/components/tools/toolPicker/ToolSearch.tsx
+++ b/frontend/src/core/components/tools/toolPicker/ToolSearch.tsx
@@ -94,8 +94,15 @@ const ToolSearch = ({
ref={searchRef}
value={value}
onChange={handleSearchChange}
- placeholder={placeholder || t("toolPicker.searchPlaceholder", "Search tools...")}
- icon={iconOverride ?? (hideIcon ? undefined : )}
+ placeholder={
+ placeholder || t("toolPicker.searchPlaceholder", "Search tools...")
+ }
+ icon={
+ iconOverride ??
+ (hideIcon ? undefined : (
+
+ ))
+ }
iconClickable={!!iconOverride}
autoComplete="off"
onFocus={onFocus}
diff --git a/frontend/src/core/components/viewer/EmbedPdfViewer.tsx b/frontend/src/core/components/viewer/EmbedPdfViewer.tsx
index 817c8075d0..ebb094d66a 100644
--- a/frontend/src/core/components/viewer/EmbedPdfViewer.tsx
+++ b/frontend/src/core/components/viewer/EmbedPdfViewer.tsx
@@ -333,14 +333,19 @@ const EmbedPdfViewerContent = ({
if (previewFile) {
return previewFile;
} else if (activeFiles.length > 0) {
- const byId = activeFileId ? activeFiles.find((f) => isStirlingFile(f) && f.fileId === activeFileId) : null;
+ const byId = activeFileId
+ ? activeFiles.find(
+ (f) => isStirlingFile(f) && f.fileId === activeFileId,
+ )
+ : null;
return byId || activeFiles[0];
}
return null;
}, [previewFile, activeFiles, activeFileId]);
// Stable id — avoids blob URL churn when FileContext recreates file objects each render.
- const currentFileStableId = currentFile && isStirlingFile(currentFile) ? currentFile.fileId : null;
+ const currentFileStableId =
+ currentFile && isStirlingFile(currentFile) ? currentFile.fileId : null;
const fileWithUrl = useFileWithUrl(currentFile, currentFileStableId);
// Determine the effective file to display
@@ -765,7 +770,14 @@ const EmbedPdfViewerContent = ({
formApplyInProgressRef.current = false;
}
},
- [currentFile, activeFiles, actions, selectors, activeFileIds.length, rotationState.rotation],
+ [
+ currentFile,
+ activeFiles,
+ actions,
+ selectors,
+ activeFileIds.length,
+ rotationState.rotation,
+ ],
);
useEffect(() => {
@@ -823,7 +835,14 @@ const EmbedPdfViewerContent = ({
layerApplyInProgressRef.current = false;
}
},
- [currentFile, activeFiles, actions, selectors, activeFileIds.length, rotationState.rotation],
+ [
+ currentFile,
+ activeFiles,
+ actions,
+ selectors,
+ activeFileIds.length,
+ rotationState.rotation,
+ ],
);
// Discard pending redactions but save already-applied ones
@@ -1097,7 +1116,11 @@ const EmbedPdfViewerContent = ({
// the effect re-fires before the async fetch completes.
}
- if (currentFile && !isCurrentFileEncrypted && (fileChanged || providerChanged)) {
+ if (
+ currentFile &&
+ !isCurrentFileEncrypted &&
+ (fileChanged || providerChanged)
+ ) {
console.log("[FormFill] Fetching form fields for:", currentFileId);
fetchFormFields(currentFile, currentFileId ?? undefined);
}
@@ -1159,7 +1182,12 @@ const EmbedPdfViewerContent = ({
- {t("encryptedPdfUnlock.viewerLocked", "This PDF is password-protected")}
+
+ {t(
+ "encryptedPdfUnlock.viewerLocked",
+ "This PDF is password-protected",
+ )}
+
{
@@ -1266,7 +1294,11 @@ const EmbedPdfViewerContent = ({
)}
{/* Thumbnail Sidebar */}
-
+
{
if (file) {
const objectUrl = URL.createObjectURL(file);
diff --git a/frontend/src/core/components/viewer/PdfViewerToolbar.tsx b/frontend/src/core/components/viewer/PdfViewerToolbar.tsx
index 8676691b15..ea71bb826b 100644
--- a/frontend/src/core/components/viewer/PdfViewerToolbar.tsx
+++ b/frontend/src/core/components/viewer/PdfViewerToolbar.tsx
@@ -1,5 +1,12 @@
import { useState, useEffect } from "react";
-import { ActionIcon, Button, Paper, Group, NumberInput, Slider } from "@mantine/core";
+import {
+ ActionIcon,
+ Button,
+ Paper,
+ Group,
+ NumberInput,
+ Slider,
+} from "@mantine/core";
import { useTranslation } from "react-i18next";
import { useViewer } from "@app/contexts/ViewerContext";
import { Tooltip } from "@app/components/shared/Tooltip";
@@ -325,7 +332,14 @@ export function PdfViewerToolbar({
>
-
+
{displayZoomPercent}%
diff --git a/frontend/src/core/components/viewer/ThumbnailSidebar.tsx b/frontend/src/core/components/viewer/ThumbnailSidebar.tsx
index 78c6c97256..06a3043363 100644
--- a/frontend/src/core/components/viewer/ThumbnailSidebar.tsx
+++ b/frontend/src/core/components/viewer/ThumbnailSidebar.tsx
@@ -9,7 +9,11 @@ interface ThumbnailSidebarProps {
activeFileId?: string | null;
}
-export function ThumbnailSidebar({ visible, onToggle: _onToggle, activeFileId }: ThumbnailSidebarProps) {
+export function ThumbnailSidebar({
+ visible,
+ onToggle: _onToggle,
+ activeFileId,
+}: ThumbnailSidebarProps) {
const { getScrollState, scrollActions, getThumbnailAPI } = useViewer();
const [thumbnails, setThumbnails] = useState<{ [key: number]: string }>({});
diff --git a/frontend/src/core/components/viewer/Viewer.tsx b/frontend/src/core/components/viewer/Viewer.tsx
index 0980f358d0..7052df4c67 100644
--- a/frontend/src/core/components/viewer/Viewer.tsx
+++ b/frontend/src/core/components/viewer/Viewer.tsx
@@ -19,7 +19,9 @@ const Viewer = (props: ViewerProps) => {
// Determine the active file — previewFile takes priority, then look up by stable ID
const activeFile = useMemo(() => {
if (props.previewFile) return props.previewFile;
- const byId = activeFileId ? activeFiles.find((f) => isStirlingFile(f) && f.fileId === activeFileId) : null;
+ const byId = activeFileId
+ ? activeFiles.find((f) => isStirlingFile(f) && f.fileId === activeFileId)
+ : null;
return byId ?? activeFiles[0] ?? null;
}, [props.previewFile, activeFiles, activeFileId]);
diff --git a/frontend/src/core/components/viewer/useViewerRightRailButtons.tsx b/frontend/src/core/components/viewer/useViewerRightRailButtons.tsx
index de4321b3e2..9bf431d8d2 100644
--- a/frontend/src/core/components/viewer/useViewerRightRailButtons.tsx
+++ b/frontend/src/core/components/viewer/useViewerRightRailButtons.tsx
@@ -201,9 +201,17 @@ export function useViewerRightRailButtons(
},
{
id: "viewer-pan-mode",
- icon: ,
- tooltip: !isPanning && pendingCount > 0 && redactionActiveType !== null ? applyRedactionsLabel : panLabel,
- ariaLabel: !isPanning && pendingCount > 0 && redactionActiveType !== null ? applyRedactionsLabel : panLabel,
+ icon: (
+
+ ),
+ tooltip:
+ !isPanning && pendingCount > 0 && redactionActiveType !== null
+ ? applyRedactionsLabel
+ : panLabel,
+ ariaLabel:
+ !isPanning && pendingCount > 0 && redactionActiveType !== null
+ ? applyRedactionsLabel
+ : panLabel,
section: "top" as const,
order: 20,
active: isPanning,
@@ -248,7 +256,9 @@ export function useViewerRightRailButtons(
},
{
id: "viewer-rotate-right",
- icon: ,
+ icon: (
+
+ ),
tooltip: rotateRightLabel,
ariaLabel: rotateRightLabel,
section: "top" as const,
@@ -271,7 +281,13 @@ export function useViewerRightRailButtons(
},
{
id: "viewer-toggle-bookmarks",
- icon: ,
+ icon: (
+
+ ),
tooltip: bookmarkLabel,
ariaLabel: bookmarkLabel,
section: "top" as const,
@@ -283,7 +299,13 @@ export function useViewerRightRailButtons(
},
{
id: "viewer-toggle-attachments",
- icon: ,
+ icon: (
+
+ ),
tooltip: attachmentLabel,
ariaLabel: attachmentLabel,
section: "top" as const,
@@ -351,7 +373,11 @@ export function useViewerRightRailButtons(
variant={isReadingAloud ? "filled" : "subtle"}
radius="md"
className="workbench-bar-action-icon"
- disabled={disabled || typeof window === "undefined" || !window.speechSynthesis}
+ disabled={
+ disabled ||
+ typeof window === "undefined" ||
+ !window.speechSynthesis
+ }
aria-label={readAloudLabel}
onClick={handleReadAloud}
color={isReadingAloud ? "blue" : undefined}
diff --git a/frontend/src/core/contexts/FileContext.tsx b/frontend/src/core/contexts/FileContext.tsx
index 9635ed0496..0bfeff476c 100644
--- a/frontend/src/core/contexts/FileContext.tsx
+++ b/frontend/src/core/contexts/FileContext.tsx
@@ -506,7 +506,9 @@ function FileContextInner({
}
if (failedIds.length > 0) {
- const failedNames = failedIds.map((id) => stateRef.current.files.byId[id]?.name ?? id);
+ const failedNames = failedIds.map(
+ (id) => stateRef.current.files.byId[id]?.name ?? id,
+ );
setUnlockError(
t("encryptedPdfUnlock.unlockAllPartialFail", {
defaultValue: "Wrong password for: {{names}}",
diff --git a/frontend/src/core/contexts/FileManagerContext.tsx b/frontend/src/core/contexts/FileManagerContext.tsx
index 8706ed7c4c..5e8bcf93ca 100644
--- a/frontend/src/core/contexts/FileManagerContext.tsx
+++ b/frontend/src/core/contexts/FileManagerContext.tsx
@@ -117,9 +117,15 @@ export const FileManagerProvider: React.FC = ({
activeFileIds,
maxSelectable = null,
}) => {
- const [activeSource, setActiveSource] = useState<"recent" | "local" | "drive">("recent");
- const [storageFilter, setStorageFilter] = useState<"all" | "local" | "sharedWithMe" | "sharedByMe">("all");
- const [selectedFileIds, setSelectedFileIds] = useState(() => activeFileIds);
+ const [activeSource, setActiveSource] = useState<
+ "recent" | "local" | "drive"
+ >("recent");
+ const [storageFilter, setStorageFilter] = useState<
+ "all" | "local" | "sharedWithMe" | "sharedByMe"
+ >("all");
+ const [selectedFileIds, setSelectedFileIds] = useState(
+ () => activeFileIds,
+ );
const [searchTerm, setSearchTerm] = useState("");
const [lastClickedIndex, setLastClickedIndex] = useState(null);
const [expandedFileIds, setExpandedFileIds] = useState>(
@@ -670,13 +676,23 @@ export const FileManagerProvider: React.FC = ({
}
// Add newly checked files (not already active)
- const newlySelected = selectedFiles.filter((f) => !activeFileIds.includes(f.id));
+ const newlySelected = selectedFiles.filter(
+ (f) => !activeFileIds.includes(f.id),
+ );
if (newlySelected.length > 0) {
onRecentFilesSelected(newlySelected);
}
onClose();
- }, [selectedFiles, selectedFilesSet, activeFileIds, filteredFiles, removeFiles, onRecentFilesSelected, onClose]);
+ }, [
+ selectedFiles,
+ selectedFilesSet,
+ activeFileIds,
+ filteredFiles,
+ removeFiles,
+ onRecentFilesSelected,
+ onClose,
+ ]);
const handleSearchChange = useCallback((value: string) => {
setSearchTerm(value);
@@ -710,8 +726,12 @@ export const FileManagerProvider: React.FC = ({
setLastClickedIndex(null);
} else {
// Select all filtered files, capped at maxSelectable
- const allIds = filteredFiles.map((file) => file.id).filter(Boolean) as FileId[];
- setSelectedFileIds(maxSelectable != null ? allIds.slice(0, maxSelectable) : allIds);
+ const allIds = filteredFiles
+ .map((file) => file.id)
+ .filter(Boolean) as FileId[];
+ setSelectedFileIds(
+ maxSelectable != null ? allIds.slice(0, maxSelectable) : allIds,
+ );
setLastClickedIndex(null);
}
}, [filteredFiles, selectedFileIds, maxSelectable]);
@@ -738,7 +758,10 @@ export const FileManagerProvider: React.FC = ({
if (localIds.length > 0) {
try {
const allStoredStubs = await fileStorage.getAllStirlingFileStubs();
- const safeIds = getSafeFilesToDelete(localIds, allStoredStubs) as FileId[];
+ const safeIds = getSafeFilesToDelete(
+ localIds,
+ allStoredStubs,
+ ) as FileId[];
const safeIdSet = new Set(safeIds);
// Clear UI state in one pass.
@@ -753,7 +776,8 @@ export const FileManagerProvider: React.FC = ({
safeIds.forEach((id) => next.delete(id));
for (const [mainId, histFiles] of next.entries()) {
const filtered = histFiles.filter((h) => !safeIdSet.has(h.id));
- if (filtered.length !== histFiles.length) next.set(mainId, filtered);
+ if (filtered.length !== histFiles.length)
+ next.set(mainId, filtered);
}
return next;
});
@@ -777,7 +801,15 @@ export const FileManagerProvider: React.FC = ({
await refreshRecentFiles();
}
}
- }, [selectedFileIds, filteredFiles, performFileDelete, getSafeFilesToDelete, removeFiles, refreshRecentFiles, onBulkRemove]);
+ }, [
+ selectedFileIds,
+ filteredFiles,
+ performFileDelete,
+ getSafeFilesToDelete,
+ removeFiles,
+ refreshRecentFiles,
+ onBulkRemove,
+ ]);
const handleDownloadSelected = useCallback(async () => {
if (selectedFileIds.length === 0) return;
diff --git a/frontend/src/core/contexts/FilesModalContext.tsx b/frontend/src/core/contexts/FilesModalContext.tsx
index 11ec5914f5..d1b50525d7 100644
--- a/frontend/src/core/contexts/FilesModalContext.tsx
+++ b/frontend/src/core/contexts/FilesModalContext.tsx
@@ -268,7 +268,15 @@ export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({
}
closeFilesModal();
},
- [addFiles, closeFilesModal, insertAfterPage, customHandler, actions, fileCtx, navActions],
+ [
+ addFiles,
+ closeFilesModal,
+ insertAfterPage,
+ customHandler,
+ actions,
+ fileCtx,
+ navActions,
+ ],
);
const handleRecentFileSelect = useCallback(
diff --git a/frontend/src/core/contexts/IndexedDBContext.tsx b/frontend/src/core/contexts/IndexedDBContext.tsx
index c73561d52d..6aabd7d47c 100644
--- a/frontend/src/core/contexts/IndexedDBContext.tsx
+++ b/frontend/src/core/contexts/IndexedDBContext.tsx
@@ -3,7 +3,13 @@
* Integrates with FileContext to provide transparent file persistence
*/
-import React, { createContext, useContext, useCallback, useRef, useState } from "react";
+import React, {
+ createContext,
+ useContext,
+ useCallback,
+ useRef,
+ useState,
+} from "react";
import { fileStorage } from "@app/services/fileStorage";
import { FileId } from "@app/types/file";
import {
diff --git a/frontend/src/core/contexts/ViewerContext.tsx b/frontend/src/core/contexts/ViewerContext.tsx
index 6e92778a4c..176b11271b 100644
--- a/frontend/src/core/contexts/ViewerContext.tsx
+++ b/frontend/src/core/contexts/ViewerContext.tsx
@@ -246,7 +246,9 @@ export const ViewerProvider: React.FC = ({ children }) => {
// Dep on state.files.ids so the effect re-runs on every add/remove.
useEffect(() => {
if (!activeFileId) return;
- const stillInWorkbench = state.files.ids.some((id) => (id as string) === activeFileId);
+ const stillInWorkbench = state.files.ids.some(
+ (id) => (id as string) === activeFileId,
+ );
if (!stillInWorkbench) setActiveFileId(null);
}, [activeFileId, state.files.ids]);
diff --git a/frontend/src/core/contexts/file/fileActions.ts b/frontend/src/core/contexts/file/fileActions.ts
index 8dcf8d3cfb..feafd15b76 100644
--- a/frontend/src/core/contexts/file/fileActions.ts
+++ b/frontend/src/core/contexts/file/fileActions.ts
@@ -484,7 +484,11 @@ export async function addFiles(
// Persist the thumbnail to IndexedDB so it's available in future sessions.
// The file was stored before hydration ran, so it had no thumbnail yet.
// Skip blob URLs — they're session-only and won't be valid after reload.
- if (primaryThumbnail && enablePersistence && !primaryThumbnail.startsWith("blob:")) {
+ if (
+ primaryThumbnail &&
+ enablePersistence &&
+ !primaryThumbnail.startsWith("blob:")
+ ) {
try {
await fileStorage.updateThumbnail(fileId, primaryThumbnail);
} catch {
@@ -715,7 +719,11 @@ export async function undoConsumeFiles(
if (indexedDB) {
outputFileIds.forEach((fileId) => {
indexedDB.deleteFile(fileId).catch((error) => {
- console.error("📄 undoConsumeFiles: Failed to delete output file from IDB:", fileId, error);
+ console.error(
+ "📄 undoConsumeFiles: Failed to delete output file from IDB:",
+ fileId,
+ error,
+ );
// Bump revision so the sidebar re-reads IDB and orphaned files reappear.
indexedDB.bumpRevision?.();
});
@@ -726,7 +734,10 @@ export async function undoConsumeFiles(
await Promise.all(
inputStirlingFileStubs.map((stub) =>
fileStorage.markFileAsLeaf(stub.id).catch((error) => {
- console.warn(`📄 undoConsumeFiles: Failed to restore isLeaf for ${stub.id}:`, error);
+ console.warn(
+ `📄 undoConsumeFiles: Failed to restore isLeaf for ${stub.id}:`,
+ error,
+ );
}),
),
);
diff --git a/frontend/src/core/data/useTranslatedToolRegistry.tsx b/frontend/src/core/data/useTranslatedToolRegistry.tsx
index 95cc5c3e30..e416f95944 100644
--- a/frontend/src/core/data/useTranslatedToolRegistry.tsx
+++ b/frontend/src/core/data/useTranslatedToolRegistry.tsx
@@ -179,7 +179,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
versionStatus: "alpha",
},
multiTool: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.multiTool.title", "Multi-Tool"),
component: null,
workbench: "pageEditor",
@@ -196,7 +202,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
automationSettings: null,
},
merge: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.merge.title", "Merge"),
component: Merge,
description: t(
@@ -213,7 +225,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
},
// Signing
certSign: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.certSign.title", "Certificate Sign"),
component: CertSign,
description: t(
@@ -229,7 +247,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
automationSettings: CertSignAutomationSettings,
},
timestampPdf: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.timestampPdf.title", "Timestamp PDF"),
component: TimestampPdf,
description: t(
@@ -285,7 +309,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
supportsAutomate: false,
},
addImage: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.addImage.title", "Add Image"),
component: AddImage,
description: t("home.addImage.desc", "Add images anywhere in your PDF"),
@@ -298,7 +328,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
supportsAutomate: false,
},
annotate: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.annotate.title", "Annotate"),
component: Annotate,
description: t(
@@ -358,7 +394,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
synonyms: getSynonyms(t, "watermark"),
},
addStamp: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.addStamp.title", "Add Stamp to PDF"),
component: AddStamp,
description: t(
@@ -396,7 +438,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
synonyms: getSynonyms(t, "sanitize"),
},
flatten: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.flatten.title", "Flatten"),
component: Flatten,
description: t(
@@ -472,7 +520,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
synonyms: getSynonyms(t, "changePermissions"),
},
getPdfInfo: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.getPdfInfo.title", "Get ALL Info on PDF"),
component: GetPdfInfo,
description: t(
@@ -488,7 +542,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
maxFiles: 1,
},
validateSignature: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.validateSignature.title", "Validate PDF Signature"),
component: ValidateSignature,
description: t(
@@ -506,7 +566,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
// Document Review
read: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.read.title", "Read"),
component: null,
workbench: "viewer",
@@ -522,7 +588,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
automationSettings: null,
},
changeMetadata: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.changeMetadata.title", "Change Metadata"),
component: ChangeMetadata,
description: t(
@@ -660,7 +732,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
synonyms: getSynonyms(t, "addPageNumbers"),
},
pageLayout: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.pageLayout.title", "Multi-Page Layout"),
component: PageLayout,
description: t(
@@ -675,7 +753,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
synonyms: getSynonyms(t, "pageLayout"),
},
bookletImposition: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.bookletImposition.title", "Booklet Imposition"),
component: BookletImposition,
operationConfig: bookletImpositionOperationConfig,
@@ -750,7 +834,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
endpoints: ["rearrange-pages"],
},
extractImages: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.extractImages.title", "Extract Images"),
component: ExtractImages,
description: t(
@@ -791,7 +881,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
automationSettings: RemovePagesSettings,
},
removeBlanks: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.removeBlanks.title", "Remove Blank Pages"),
component: RemoveBlanks,
description: t(
@@ -937,7 +1033,9 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
// Advanced Formatting
adjustContrast: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.adjustContrast.title", "Adjust Colors/Contrast"),
component: AdjustContrast,
description: t(
@@ -975,8 +1073,17 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
automationSettings: null,
},
scannerImageSplit: {
- icon: ,
- name: t("home.scannerImageSplit.title", "Detect & Split Scanned Photos"),
+ icon: (
+
+ ),
+ name: t(
+ "home.scannerImageSplit.title",
+ "Detect & Split Scanned Photos",
+ ),
component: ScannerImageSplit,
description: t(
"home.scannerImageSplit.desc",
@@ -991,7 +1098,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
synonyms: getSynonyms(t, "ScannerImageSplit"),
},
overlayPdfs: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.overlay-pdfs.title", "Overlay PDFs"),
component: OverlayPdfs,
description: t(
@@ -1028,7 +1141,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
synonyms: getSynonyms(t, "replaceColor"),
},
scannerEffect: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.scannerEffect.title", "Scanner Effect"),
component: null,
description: t(
@@ -1252,7 +1371,13 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
synonyms: getSynonyms(t, "ocr"),
},
redact: {
- icon: ,
+ icon: (
+
+ ),
name: t("home.redact.title", "Redact"),
component: Redact,
description: t(
diff --git a/frontend/src/core/hooks/tools/shared/toolOperationTypes.ts b/frontend/src/core/hooks/tools/shared/toolOperationTypes.ts
index 089de93849..27cc16b13a 100644
--- a/frontend/src/core/hooks/tools/shared/toolOperationTypes.ts
+++ b/frontend/src/core/hooks/tools/shared/toolOperationTypes.ts
@@ -15,7 +15,13 @@ export enum ToolType {
* Reason the execute button is disabled. Resolved to a translated tooltip by OperationButton.
* null means the button is enabled.
*/
-export type ExecuteDisabledReason = "endpointUnavailable" | "filesLoading" | "noFiles" | "invalidParams" | "viewerMode" | null;
+export type ExecuteDisabledReason =
+ | "endpointUnavailable"
+ | "filesLoading"
+ | "noFiles"
+ | "invalidParams"
+ | "viewerMode"
+ | null;
/**
* Result from custom processor with optional metadata about input consumption.
diff --git a/frontend/src/core/hooks/useRightRailTooltipSide.ts b/frontend/src/core/hooks/useRightRailTooltipSide.ts
index 4fcd407ab1..83e2dcfd98 100644
--- a/frontend/src/core/hooks/useRightRailTooltipSide.ts
+++ b/frontend/src/core/hooks/useRightRailTooltipSide.ts
@@ -5,7 +5,9 @@ export function useRightRailTooltipSide(
sidebarRefs?: SidebarRefs,
defaultOffset: number = 16,
): { position: "left" | "right" | "bottom"; offset: number } {
- const [position, setPosition] = useState<"left" | "right" | "bottom">("bottom");
+ const [position, setPosition] = useState<"left" | "right" | "bottom">(
+ "bottom",
+ );
useEffect(() => {
const computePosition = () => {
diff --git a/frontend/src/core/pages/HomePage.tsx b/frontend/src/core/pages/HomePage.tsx
index 5dff92679e..92906104c8 100644
--- a/frontend/src/core/pages/HomePage.tsx
+++ b/frontend/src/core/pages/HomePage.tsx
@@ -376,7 +376,10 @@ export default function HomePage() {
{!hideToolPanel && }
- setConfigModalOpen(false)} />
+ setConfigModalOpen(false)}
+ />
)}
diff --git a/frontend/src/core/services/fileStorage.ts b/frontend/src/core/services/fileStorage.ts
index f030f6e5ab..1676811f51 100644
--- a/frontend/src/core/services/fileStorage.ts
+++ b/frontend/src/core/services/fileStorage.ts
@@ -340,7 +340,12 @@ class FileStorageService {
if (cursor) {
const record = cursor.value as StoredStirlingFileRecord;
// Only include leaf files (default to true if undefined)
- if (record && record.name && typeof record.size === "number" && record.isLeaf !== false) {
+ if (
+ record &&
+ record.name &&
+ typeof record.size === "number" &&
+ record.isLeaf !== false
+ ) {
const fresh = this.isThumbnailFresh(record);
if (record.thumbnail) {
if (fresh) tobump.push(record.id);
@@ -408,7 +413,8 @@ class FileStorageService {
const store = transaction.objectStore(this.storeName);
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
- transaction.onabort = () => reject(transaction.error ?? new Error("Transaction aborted"));
+ transaction.onabort = () =>
+ reject(transaction.error ?? new Error("Transaction aborted"));
ids.forEach((id) => store.delete(id));
});
}
diff --git a/frontend/src/core/tools/Compare.tsx b/frontend/src/core/tools/Compare.tsx
index 28d25d7a2b..50ff935ddd 100644
--- a/frontend/src/core/tools/Compare.tsx
+++ b/frontend/src/core/tools/Compare.tsx
@@ -2,7 +2,15 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import CompareRoundedIcon from "@mui/icons-material/CompareRounded";
import CloseIcon from "@mui/icons-material/Close";
-import { Box, Group, Stack, Text, Button, Modal, ActionIcon } from "@mantine/core";
+import {
+ Box,
+ Group,
+ Stack,
+ Text,
+ Button,
+ Modal,
+ ActionIcon,
+} from "@mantine/core";
import SwapVertRoundedIcon from "@mui/icons-material/SwapVertRounded";
import { createToolFlow } from "@app/components/tools/shared/createToolFlow";
import { useBaseTool } from "@app/hooks/tools/shared/useBaseTool";
@@ -24,7 +32,10 @@ import DocumentThumbnail from "@app/components/shared/filePreview/DocumentThumbn
import type { CompareWorkbenchData } from "@app/types/compare";
import { getDefaultWorkbench } from "@app/types/workbench";
import { truncateCenter } from "@app/utils/textUtils";
-import { FileSelectorPicker, FileSelectorResult } from "@app/components/shared/FileSelectorPicker";
+import {
+ FileSelectorPicker,
+ FileSelectorResult,
+} from "@app/components/shared/FileSelectorPicker";
import "@app/components/tools/compare/compareView.css";
const CUSTOM_VIEW_ID = "compareWorkbenchView";
@@ -40,10 +51,16 @@ const Compare = (props: BaseToolProps) => {
clearCustomWorkbenchViewData,
} = useToolWorkflow();
- const base = useBaseTool("compare", useCompareParameters, useCompareOperation, props, {
- minFiles: 0,
- ignoreViewerScope: true,
- });
+ const base = useBaseTool(
+ "compare",
+ useCompareParameters,
+ useCompareOperation,
+ props,
+ {
+ minFiles: 0,
+ ignoreViewerScope: true,
+ },
+ );
const operation = base.operation as CompareOperationHook;
const params = base.params.parameters;
@@ -141,7 +158,12 @@ const Compare = (props: BaseToolProps) => {
);
const prepareWorkbenchForRun = useCallback(
- (baseId: FileId | null, compId: FileId | null, baseFile: StirlingFile | null, comparisonFile: StirlingFile | null) => {
+ (
+ baseId: FileId | null,
+ compId: FileId | null,
+ baseFile: StirlingFile | null,
+ comparisonFile: StirlingFile | null,
+ ) => {
if (!baseId || !compId) return;
updateWorkbenchData({
@@ -184,8 +206,10 @@ const Compare = (props: BaseToolProps) => {
result,
baseFileId,
comparisonFileId,
- baseLocalFile: baseSlot?.stirlingFile ?? previous?.baseLocalFile ?? null,
- comparisonLocalFile: compSlot?.stirlingFile ?? previous?.comparisonLocalFile ?? null,
+ baseLocalFile:
+ baseSlot?.stirlingFile ?? previous?.baseLocalFile ?? null,
+ comparisonLocalFile:
+ compSlot?.stirlingFile ?? previous?.comparisonLocalFile ?? null,
isLoading: false,
});
lastProcessedAtRef.current = processedAt;
@@ -198,8 +222,10 @@ const Compare = (props: BaseToolProps) => {
result: null,
baseFileId,
comparisonFileId,
- baseLocalFile: baseSlot?.stirlingFile ?? previous?.baseLocalFile ?? null,
- comparisonLocalFile: compSlot?.stirlingFile ?? previous?.comparisonLocalFile ?? null,
+ baseLocalFile:
+ baseSlot?.stirlingFile ?? previous?.baseLocalFile ?? null,
+ comparisonLocalFile:
+ compSlot?.stirlingFile ?? previous?.comparisonLocalFile ?? null,
isLoading: true,
});
return;
@@ -219,15 +245,33 @@ const Compare = (props: BaseToolProps) => {
if (!baseSlot || !compSlot) return;
const baseId = baseSlot.stirlingFile.fileId;
const compId = compSlot.stirlingFile.fileId;
- const selected: StirlingFile[] = [baseSlot.stirlingFile, compSlot.stirlingFile];
+ const selected: StirlingFile[] = [
+ baseSlot.stirlingFile,
+ compSlot.stirlingFile,
+ ];
- prepareWorkbenchForRun(baseId, compId, baseSlot.stirlingFile, compSlot.stirlingFile);
+ prepareWorkbenchForRun(
+ baseId,
+ compId,
+ baseSlot.stirlingFile,
+ compSlot.stirlingFile,
+ );
requestAnimationFrame(() => {
navigationActions.setWorkbench(CUSTOM_WORKBENCH_ID);
});
- await operation.executeOperation({ ...params, baseFileId: baseId, comparisonFileId: compId }, selected);
- }, [baseSlot, compSlot, navigationActions, operation, params, prepareWorkbenchForRun]);
+ await operation.executeOperation(
+ { ...params, baseFileId: baseId, comparisonFileId: compId },
+ selected,
+ );
+ }, [
+ baseSlot,
+ compSlot,
+ navigationActions,
+ operation,
+ params,
+ prepareWorkbenchForRun,
+ ]);
const performSwap = useCallback(() => {
if (!baseSlot || !compSlot) return;
@@ -238,14 +282,32 @@ const Compare = (props: BaseToolProps) => {
if (operation.result) {
const baseId = newBase.stirlingFile.fileId;
const compId = newComp.stirlingFile.fileId;
- const selected: StirlingFile[] = [newBase.stirlingFile, newComp.stirlingFile];
- prepareWorkbenchForRun(baseId, compId, newBase.stirlingFile, newComp.stirlingFile);
+ const selected: StirlingFile[] = [
+ newBase.stirlingFile,
+ newComp.stirlingFile,
+ ];
+ prepareWorkbenchForRun(
+ baseId,
+ compId,
+ newBase.stirlingFile,
+ newComp.stirlingFile,
+ );
requestAnimationFrame(() => {
navigationActions.setWorkbench(CUSTOM_WORKBENCH_ID);
});
- void operation.executeOperation({ ...params, baseFileId: baseId, comparisonFileId: compId }, selected);
+ void operation.executeOperation(
+ { ...params, baseFileId: baseId, comparisonFileId: compId },
+ selected,
+ );
}
- }, [baseSlot, compSlot, navigationActions, operation, params, prepareWorkbenchForRun]);
+ }, [
+ baseSlot,
+ compSlot,
+ navigationActions,
+ operation,
+ params,
+ prepareWorkbenchForRun,
+ ]);
const handleSwap = useCallback(() => {
if (!baseSlot || !compSlot) return;
@@ -270,7 +332,11 @@ const Compare = (props: BaseToolProps) => {
if (stub) {
const dateMs = (stub.lastModified || stub.createdAt) ?? null;
const dateText = dateMs
- ? new Date(dateMs).toLocaleDateString(undefined, { month: "short", day: "2-digit", year: "numeric" })
+ ? new Date(dateMs).toLocaleDateString(undefined, {
+ month: "short",
+ day: "2-digit",
+ year: "numeric",
+ })
: "";
const pageCount = stub.processedFile?.totalPages || null;
@@ -300,14 +366,25 @@ const Compare = (props: BaseToolProps) => {
-
+
{truncateCenter(stub.name || "", 50)}
{pageCount && dateText && (
-
+
{pageCount} {t("compare.pages", "pages")}
{dateText}
@@ -327,11 +404,17 @@ const Compare = (props: BaseToolProps) => {
isDisabled
? t("compare.edited.selectBaseFirst", "Select original PDF first")
: t(
- role === "base" ? "compare.original.placeholder" : "compare.edited.placeholder",
- role === "base" ? "Select the original PDF" : "Select the edited PDF",
+ role === "base"
+ ? "compare.original.placeholder"
+ : "compare.edited.placeholder",
+ role === "base"
+ ? "Select the original PDF"
+ : "Select the edited PDF",
)
}
- excludeIds={otherSlot ? [otherSlot.stirlingFile.fileId as string] : []}
+ excludeIds={
+ otherSlot ? [otherSlot.stirlingFile.fileId as string] : []
+ }
disabled={isDisabled}
onSelect={(result: FileSelectorResult) => {
if (role === "base") setBaseSlot(result);
@@ -376,7 +459,9 @@ const Compare = (props: BaseToolProps) => {
styles={{ root: { textDecoration: "underline" } }}
style={{
background: !hasAnySelected ? "transparent" : undefined,
- color: !hasAnySelected ? "var(--spdf-clear-disabled-text)" : undefined,
+ color: !hasAnySelected
+ ? "var(--spdf-clear-disabled-text)"
+ : undefined,
}}
>
{t("compare.clearSelected", "Clear selected")}
@@ -404,8 +489,13 @@ const Compare = (props: BaseToolProps) => {
disabled={base.operation.isLoading}
aria-label={t("compare.swap.label", "Swap")}
>
-
- {t("compare.swap.label", "Swap")}
+
+
+ {t("compare.swap.label", "Swap")}
+
)}
@@ -452,7 +542,12 @@ const Compare = (props: BaseToolProps) => {
size="sm"
>