Prettier 2: Electric Boogaloo (#6113)

# Description of Changes
When I added Prettier formatting in #6052, my aim was to use just the
default settings in Prettier. Turns out, Prettier looks _really hard_
for any config files if it's not explicitly given one, which means that
if a developer has some sort of Prettier config file lying around on
their system, Prettier might find it and use it. Also, Prettier changes
its defaults based on stuff in `.editorconfig` without any good way of
disabling that behaviour explicitly in its config file.

To solve both of these issues, I've introduced a `.prettierrc` file
which sets Prettier's defaults explicitly, and then reformatted all our
code _again_ in Prettier's actual default settings. This should achieve
the aim of #6052 and remove the possibility for it breaking on different
dev computers.
This commit is contained in:
James Brunton
2026-04-17 09:50:16 +00:00
committed by GitHub
parent de8c483054
commit 8ab060a4be
1207 changed files with 52646 additions and 15352 deletions
@@ -1,4 +1,10 @@
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
import React, {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from "react";
import { Box, Center, Text, ActionIcon, Button, Stack } from "@mantine/core";
import CloseIcon from "@mui/icons-material/Close";
import LockIcon from "@mui/icons-material/Lock";
@@ -12,7 +18,10 @@ import { ThumbnailSidebar } from "@app/components/viewer/ThumbnailSidebar";
import { BookmarkSidebar } from "@app/components/viewer/BookmarkSidebar";
import { AttachmentSidebar } from "@app/components/viewer/AttachmentSidebar";
import { LayerSidebar } from "@app/components/viewer/LayerSidebar";
import { useNavigationGuard, useNavigationState } from "@app/contexts/NavigationContext";
import {
useNavigationGuard,
useNavigationState,
} from "@app/contexts/NavigationContext";
import { useSignature } from "@app/contexts/SignatureContext";
import { useRedaction } from "@app/contexts/RedactionContext";
import type { RedactionPendingTrackerAPI } from "@app/components/viewer/RedactionPendingTracker";
@@ -34,26 +43,45 @@ import { useViewerKeyCommand } from "@app/hooks/useViewerKeyCommand";
// ─── Measure dictionary extraction ────────────────────────────────────────────
async function extractPageMeasureScales(file: Blob): Promise<PageMeasureScales | null> {
async function extractPageMeasureScales(
file: Blob,
): Promise<PageMeasureScales | null> {
try {
const { PDFDocument, PDFDict, PDFName, PDFArray, PDFNumber, PDFString, PDFHexString } = await import("@cantoo/pdf-lib");
const pdfDoc = await PDFDocument.load(await file.arrayBuffer(), { ignoreEncryption: true });
const {
PDFDocument,
PDFDict,
PDFName,
PDFArray,
PDFNumber,
PDFString,
PDFHexString,
} = await import("@cantoo/pdf-lib");
const pdfDoc = await PDFDocument.load(await file.arrayBuffer(), {
ignoreEncryption: true,
});
// Parse a Measure dict into a MeasureScale, or return null if malformed.
const parseScale = (measureObj: unknown) => {
if (!(measureObj instanceof PDFDict)) return null;
const rObj = measureObj.lookup(PDFName.of("R"));
const ratioLabel = rObj instanceof PDFString || rObj instanceof PDFHexString ? rObj.decodeText() : "";
const ratioLabel =
rObj instanceof PDFString || rObj instanceof PDFHexString
? rObj.decodeText()
: "";
// D = distance array, X = x-axis fallback
let fmtArray = measureObj.lookup(PDFName.of("D"));
if (!(fmtArray instanceof PDFArray)) fmtArray = measureObj.lookup(PDFName.of("X"));
if (!(fmtArray instanceof PDFArray))
fmtArray = measureObj.lookup(PDFName.of("X"));
if (!(fmtArray instanceof PDFArray)) return null;
const firstFmt = fmtArray.lookup(0);
if (!(firstFmt instanceof PDFDict)) return null;
const cObj = firstFmt.lookup(PDFName.of("C"));
const uObj = firstFmt.lookup(PDFName.of("U"));
if (!(cObj instanceof PDFNumber) || cObj.asNumber() <= 0) return null;
const unit = uObj instanceof PDFString || uObj instanceof PDFHexString ? uObj.decodeText() : "units";
const unit =
uObj instanceof PDFString || uObj instanceof PDFHexString
? uObj.decodeText()
: "units";
return { factor: cObj.asNumber(), unit, ratioLabel };
};
@@ -93,7 +121,8 @@ async function extractPageMeasureScales(file: Blob): Promise<PageMeasureScales |
if (scale) viewports.push({ bbox: null, scale });
}
if (viewports.length > 0) result.set(i, { viewports, pageHeight } satisfies PageScaleInfo);
if (viewports.length > 0)
result.set(i, { viewports, pageHeight } satisfies PageScaleInfo);
}
return result.size > 0 ? result : null;
@@ -157,13 +186,22 @@ const EmbedPdfViewerContent = ({
// Track initial rotation to detect changes
const initialRotationRef = useRef<number | null>(null);
useEffect(() => {
if (initialRotationRef.current === null && rotationState.rotation !== undefined) {
if (
initialRotationRef.current === null &&
rotationState.rotation !== undefined
) {
initialRotationRef.current = rotationState.rotation;
}
}, [rotationState.rotation]);
// Get signature and annotation contexts
const { signatureApiRef, annotationApiRef, historyApiRef, signatureConfig, isPlacementMode } = useSignature();
const {
signatureApiRef,
annotationApiRef,
historyApiRef,
signatureConfig,
isPlacementMode,
} = useSignature();
// Track whether there are unsaved annotation changes in this viewer session.
// This is our source of truth for navigation guards; it is set when the
@@ -212,13 +250,17 @@ const EmbedPdfViewerContent = ({
const { fetchFields: fetchFormFields, setProviderMode } = useFormFill();
const isInAnnotationTool =
selectedTool === "sign" || selectedTool === "addText" || selectedTool === "addImage" || selectedTool === "annotate";
selectedTool === "sign" ||
selectedTool === "addText" ||
selectedTool === "addImage" ||
selectedTool === "annotate";
const isSignatureMode = isInAnnotationTool;
const isManualRedactMode = selectedTool === "redact";
// Enable annotations when annotation tool is selected OR when annotations are visible
// (so users can interact with existing comment annotations in reader/viewer mode)
const shouldEnableAnnotations = selectedTool === "annotate" || isSignatureMode || isAnnotationsVisible;
const shouldEnableAnnotations =
selectedTool === "annotate" || isSignatureMode || isAnnotationsVisible;
// Enable redaction only when redaction tool is selected
const shouldEnableRedaction = selectedTool === "redact";
@@ -251,8 +293,10 @@ const EmbedPdfViewerContent = ({
// Preserve scroll position when switching between annotation and redaction tools
// Using useLayoutEffect to capture synchronously before DOM updates
useLayoutEffect(() => {
const annotationsChanged = prevEnableAnnotationsRef.current !== shouldEnableAnnotations;
const redactionChanged = prevEnableRedactionRef.current !== shouldEnableRedaction;
const annotationsChanged =
prevEnableAnnotationsRef.current !== shouldEnableAnnotations;
const redactionChanged =
prevEnableRedactionRef.current !== shouldEnableRedaction;
if (annotationsChanged || redactionChanged) {
// Read scroll state directly AND use the tracked value - take whichever is valid
@@ -279,12 +323,15 @@ const EmbedPdfViewerContent = ({
setAnnotationMode(true);
}
}, [isInAnnotationTool, setAnnotationMode]);
const isPlacementOverlayActive = Boolean(isInAnnotationTool && isPlacementMode && signatureConfig);
const isPlacementOverlayActive = Boolean(
isInAnnotationTool && isPlacementMode && signatureConfig,
);
// Track which file tab is active
const [internalActiveFileIndex, setInternalActiveFileIndex] = useState(0);
const activeFileIndex = externalActiveFileIndex ?? internalActiveFileIndex;
const setActiveFileIndex = externalSetActiveFileIndex ?? setInternalActiveFileIndex;
const setActiveFileIndex =
externalSetActiveFileIndex ?? setInternalActiveFileIndex;
// activeFileId (from ViewerContext) is the stable source of truth.
// We derive activeFileIndex from it so reorders after tool operations don't lose the viewed file.
@@ -418,7 +465,9 @@ const EmbedPdfViewerContent = ({
if (mod) {
const target = event.target as Element;
const isInTextInput =
target.tagName === "INPUT" || target.tagName === "TEXTAREA" || (target as HTMLElement).isContentEditable;
target.tagName === "INPUT" ||
target.tagName === "TEXTAREA" ||
(target as HTMLElement).isContentEditable;
if (!isInTextInput) {
const wasOverridden = viewerKeyCommand(event);
@@ -584,12 +633,15 @@ const EmbedPdfViewerContent = ({
const hasAnnotationChanges = hasAnnotationChangesRef.current;
// Check for pending redactions
const hasPendingRedactions = (redactionTrackerRef.current?.getPendingCount() ?? 0) > 0;
const hasPendingRedactions =
(redactionTrackerRef.current?.getPendingCount() ?? 0) > 0;
// Always consider applied redactions as unsaved until export
const hasAppliedRedactions = redactionsApplied;
return hasAnnotationChanges || hasPendingRedactions || hasAppliedRedactions;
return (
hasAnnotationChanges || hasPendingRedactions || hasAppliedRedactions
);
};
registerUnsavedChangesChecker(checkForChanges);
@@ -611,7 +663,9 @@ const EmbedPdfViewerContent = ({
if (!currentFile || activeFileIds.length === 0) return;
try {
console.log("[Viewer] Applying changes - exporting PDF with annotations/redactions");
console.log(
"[Viewer] Applying changes - exporting PDF with annotations/redactions",
);
// Use the continuously tracked scroll position - more reliable than reading at this moment
const pageToRestore = lastKnownScrollPageRef.current;
@@ -620,7 +674,8 @@ const EmbedPdfViewerContent = ({
const currentRotation = rotationState.rotation ?? 0;
// Step 0: Commit any pending redactions before export
const hadPendingRedactions = (redactionTrackerRef.current?.getPendingCount() ?? 0) > 0;
const hadPendingRedactions =
(redactionTrackerRef.current?.getPendingCount() ?? 0) > 0;
// Mark redactions as applied BEFORE committing, so the button stays enabled during the save process
// This ensures the button doesn't become disabled when pendingCount becomes 0
@@ -654,7 +709,11 @@ const EmbedPdfViewerContent = ({
const parentStub = selectors.getStirlingFileStub(currentFileId);
if (!parentStub) throw new Error("Parent stub not found");
const { stirlingFiles, stubs } = await createStirlingFilesAndStubs([file], parentStub, selectedTool ?? "multiTool");
const { stirlingFiles, stubs } = await createStirlingFilesAndStubs(
[file],
parentStub,
selectedTool ?? "multiTool",
);
// Store the page to restore after file replacement triggers re-render
pendingScrollRestoreRef.current = pageToRestore;
@@ -697,7 +756,9 @@ const EmbedPdfViewerContent = ({
formApplyInProgressRef.current = true;
try {
console.log("[Viewer] Applying form fill changes - reloading filled PDF");
console.log(
"[Viewer] Applying form fill changes - reloading filled PDF",
);
// Use the continuously tracked scroll position
const pageToRestore = lastKnownScrollPageRef.current;
@@ -707,7 +768,9 @@ const EmbedPdfViewerContent = ({
// Convert Blob to File
const filename = currentFile.name || "document.pdf";
const file = new File([filledBlob], filename, { type: "application/pdf" });
const file = new File([filledBlob], filename, {
type: "application/pdf",
});
// Get current file info for creating the updated version
const currentFileId = activeFiles[activeFileIndex]?.fileId;
@@ -717,7 +780,11 @@ const EmbedPdfViewerContent = ({
if (!parentStub) throw new Error("Parent stub not found");
// Create StirlingFiles and stubs for version history
const { stirlingFiles, stubs } = await createStirlingFilesAndStubs([file], parentStub, selectedTool ?? "multiTool");
const { stirlingFiles, stubs } = await createStirlingFilesAndStubs(
[file],
parentStub,
selectedTool ?? "multiTool",
);
// Store the page to restore after file replacement
pendingScrollRestoreRef.current = pageToRestore;
@@ -741,7 +808,15 @@ const EmbedPdfViewerContent = ({
formApplyInProgressRef.current = false;
}
},
[currentFile, activeFiles, activeFileIndex, actions, selectors, activeFileIds.length, rotationState.rotation],
[
currentFile,
activeFiles,
activeFileIndex,
actions,
selectors,
activeFileIds.length,
rotationState.rotation,
],
);
useEffect(() => {
@@ -768,7 +843,9 @@ const EmbedPdfViewerContent = ({
const currentRotation = rotationState.rotation ?? 0;
const filename = currentFile.name || "document.pdf";
const file = new File([modifiedBlob], filename, { type: "application/pdf" });
const file = new File([modifiedBlob], filename, {
type: "application/pdf",
});
const currentFileId = activeFiles[activeFileIndex]?.fileId;
if (!currentFileId) throw new Error("Current file ID not found");
@@ -776,7 +853,11 @@ const EmbedPdfViewerContent = ({
const parentStub = selectors.getStirlingFileStub(currentFileId);
if (!parentStub) throw new Error("Parent stub not found");
const { stirlingFiles, stubs } = await createStirlingFilesAndStubs([file], parentStub, selectedTool ?? "multiTool");
const { stirlingFiles, stubs } = await createStirlingFilesAndStubs(
[file],
parentStub,
selectedTool ?? "multiTool",
);
pendingScrollRestoreRef.current = pageToRestore;
scrollRestoreAttemptsRef.current = 0;
@@ -793,7 +874,15 @@ const EmbedPdfViewerContent = ({
layerApplyInProgressRef.current = false;
}
},
[currentFile, activeFiles, activeFileIndex, actions, selectors, activeFileIds.length, rotationState.rotation],
[
currentFile,
activeFiles,
activeFileIndex,
actions,
selectors,
activeFileIds.length,
rotationState.rotation,
],
);
// Discard pending redactions but save already-applied ones
@@ -807,7 +896,9 @@ const EmbedPdfViewerContent = ({
}
try {
console.log("[Viewer] Discarding pending marks but saving applied redactions");
console.log(
"[Viewer] Discarding pending marks but saving applied redactions",
);
// Save current view state to restore after file replacement
const pageToRestore = lastKnownScrollPageRef.current;
@@ -831,7 +922,11 @@ const EmbedPdfViewerContent = ({
const parentStub = selectors.getStirlingFileStub(currentFileId);
if (!parentStub) throw new Error("Parent stub not found");
const { stirlingFiles, stubs } = await createStirlingFilesAndStubs([file], parentStub, selectedTool ?? "multiTool");
const { stirlingFiles, stubs } = await createStirlingFilesAndStubs(
[file],
parentStub,
selectedTool ?? "multiTool",
);
// Store view state to restore after file replacement
pendingScrollRestoreRef.current = pageToRestore;
@@ -911,7 +1006,10 @@ const EmbedPdfViewerContent = ({
// Check if scroll succeeded after a brief delay
setTimeout(() => {
const afterState = getScrollState();
if (afterState.currentPage === targetPage || scrollRestoreAttemptsRef.current >= maxAttempts) {
if (
afterState.currentPage === targetPage ||
scrollRestoreAttemptsRef.current >= maxAttempts
) {
// Success or max attempts reached - clear pending
pendingScrollRestoreRef.current = null;
scrollRestoreAttemptsRef.current = 0;
@@ -962,7 +1060,10 @@ const EmbedPdfViewerContent = ({
// Check if rotation succeeded after a brief delay
setTimeout(() => {
const currentRotation = rotationActions.getRotation();
if (currentRotation === rotationToRestore || rotationRestoreAttemptsRef.current >= maxAttempts) {
if (
currentRotation === rotationToRestore ||
rotationRestoreAttemptsRef.current >= maxAttempts
) {
// Success or max attempts reached - clear pending
pendingRotationRestoreRef.current = null;
rotationRestoreAttemptsRef.current = 0;
@@ -1004,7 +1105,8 @@ const EmbedPdfViewerContent = ({
// Ruler / measurement tool state
const [isRulerActive, setIsRulerActive] = useState(false);
const [pageMeasureScales, setPageMeasureScales] = useState<PageMeasureScales | null>(null);
const [pageMeasureScales, setPageMeasureScales] =
useState<PageMeasureScales | null>(null);
useEffect(() => {
const file = effectiveFile?.file;
@@ -1037,11 +1139,17 @@ const EmbedPdfViewerContent = ({
useEffect(() => {
const fileChanged = currentFileId !== formFillFileIdRef.current;
const providerChanged = formFillProviderRef.current !== isFormFillToolActive;
const providerChanged =
formFillProviderRef.current !== isFormFillToolActive;
formFillProviderRef.current = isFormFillToolActive;
if (fileChanged) {
console.log("[FormFill] File changed. Old:", formFillFileIdRef.current, "New:", currentFileId);
console.log(
"[FormFill] File changed. Old:",
formFillFileIdRef.current,
"New:",
currentFileId,
);
formFillFileIdRef.current = currentFileId;
// NOTE: Don't call resetFormFill() here — fetchFormFields() handles
// clearing old state internally. Calling reset() before fetch() would
@@ -1053,7 +1161,13 @@ const EmbedPdfViewerContent = ({
console.log("[FormFill] Fetching form fields for:", currentFileId);
fetchFormFields(currentFile, currentFileId ?? undefined);
}
}, [isFormFillToolActive, currentFile, currentFileId, fetchFormFields, isCurrentFileEncrypted]);
}, [
isFormFillToolActive,
currentFile,
currentFileId,
fetchFormFields,
isCurrentFileEncrypted,
]);
const sidebarWidthRem = 15;
const commentsSidebarWidthRem = 18;
@@ -1084,7 +1198,13 @@ const EmbedPdfViewerContent = ({
variant="filled"
color="gray"
size="lg"
style={{ position: "absolute", top: "1rem", right: "1rem", zIndex: 1000, borderRadius: "50%" }}
style={{
position: "absolute",
top: "1rem",
right: "1rem",
zIndex: 1000,
borderRadius: "50%",
}}
onClick={onClose}
>
<CloseIcon />
@@ -1150,7 +1270,9 @@ const EmbedPdfViewerContent = ({
signatureApiRef={signatureApiRef as React.RefObject<any>}
annotationApiRef={annotationApiRef as React.RefObject<any>}
historyApiRef={historyApiRef as React.RefObject<any>}
redactionTrackerRef={redactionTrackerRef as React.RefObject<RedactionPendingTrackerAPI>}
redactionTrackerRef={
redactionTrackerRef as React.RefObject<RedactionPendingTrackerAPI>
}
fileId={currentFileId}
isCommentsSidebarVisible={isCommentsSidebarVisible}
commentsSidebarRightOffset={`${(isThumbnailSidebarVisible ? sidebarWidthRem : 0) + (isBookmarkSidebarVisible ? sidebarWidthRem : 0) + (isAttachmentSidebarVisible ? sidebarWidthRem : 0) + (isLayerSidebarVisible ? sidebarWidthRem : 0)}rem`}
@@ -1160,13 +1282,21 @@ const EmbedPdfViewerContent = ({
}}
/>
{/* Floating save bar for form-filled PDFs (like Chrome/Firefox PDF viewers) */}
<FormSaveBar file={currentFile ?? null} isFormFillToolActive={isFormFillToolActive} onApply={handleFormApply} />
<FormSaveBar
file={currentFile ?? null}
isFormFillToolActive={isFormFillToolActive}
onApply={handleFormApply}
/>
<StampPlacementOverlay
containerRef={pdfContainerRef}
isActive={isPlacementOverlayActive}
signatureConfig={signatureConfig}
/>
<RulerOverlay containerRef={pdfContainerRef} isActive={isRulerActive} pageMeasureScales={pageMeasureScales} />
<RulerOverlay
containerRef={pdfContainerRef}
isActive={isRulerActive}
pageMeasureScales={pageMeasureScales}
/>
</Box>
</>
)}
@@ -1187,7 +1317,10 @@ const EmbedPdfViewerContent = ({
}}
>
<div style={{ pointerEvents: "auto" }}>
<PdfViewerToolbar currentPage={scrollState.currentPage} totalPages={scrollState.totalPages} />
<PdfViewerToolbar
currentPage={scrollState.currentPage}
totalPages={scrollState.totalPages}
/>
</div>
</div>
)}