mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-15 19:10:47 +02:00
co-authored by
a <a>
parent
28613caf8a
commit
bb43e9dcdf
@@ -140,6 +140,8 @@ const EmbedPdfViewerContent = ({
|
||||
printActions,
|
||||
setApplyChanges,
|
||||
applyChanges: viewerApplyChanges,
|
||||
pdfRenderMode,
|
||||
cyclePdfRenderMode,
|
||||
} = useViewer();
|
||||
|
||||
const scrollState = getScrollState();
|
||||
@@ -449,6 +451,7 @@ const EmbedPdfViewerContent = ({
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
case 'z':
|
||||
case 'Z':
|
||||
// Ctrl+Z: Undo; Ctrl+Shift+Z: Redo
|
||||
@@ -503,7 +506,7 @@ const EmbedPdfViewerContent = ({
|
||||
}, [
|
||||
isViewerHovered, isSearchInterfaceVisible, zoomActions, searchInterfaceActions,
|
||||
scrollActions, printActions, exportActions, rotationActions, historyApiRef,
|
||||
viewerApplyChanges,
|
||||
viewerApplyChanges, cyclePdfRenderMode,
|
||||
]);
|
||||
|
||||
// Watch the annotation history API to detect when the document becomes "dirty".
|
||||
@@ -964,6 +967,7 @@ const EmbedPdfViewerContent = ({
|
||||
}}>
|
||||
<LocalEmbedPDF
|
||||
key={currentFileId || 'no-file'}
|
||||
pdfRenderMode={pdfRenderMode}
|
||||
file={effectiveFile.file}
|
||||
url={effectiveFile.url}
|
||||
fileName={
|
||||
|
||||
@@ -88,9 +88,11 @@ interface LocalEmbedPDFProps {
|
||||
commentsSidebarRightOffset?: string;
|
||||
/** When true, blocks the general ink/pen annotation tool (sign tool context). */
|
||||
isSignMode?: boolean;
|
||||
/** Controls CSS filter applied only to rendered PDF canvas tiles */
|
||||
pdfRenderMode?: 'normal' | 'dark' | 'sepia';
|
||||
}
|
||||
|
||||
export function LocalEmbedPDF({ file, url, fileName, enableAnnotations = false, enableRedaction = false, enableFormFill = false, isManualRedactionMode = false, showBakedAnnotations = true, onSignatureAdded, signatureApiRef, annotationApiRef, historyApiRef, redactionTrackerRef, fileId, isCommentsSidebarVisible = false, commentsSidebarRightOffset = '0rem', isSignMode = false }: LocalEmbedPDFProps) {
|
||||
export function LocalEmbedPDF({ file, url, fileName, enableAnnotations = false, enableRedaction = false, enableFormFill = false, isManualRedactionMode = false, showBakedAnnotations = true, onSignatureAdded, signatureApiRef, annotationApiRef, historyApiRef, redactionTrackerRef, fileId, isCommentsSidebarVisible = false, commentsSidebarRightOffset = '0rem', isSignMode = false, pdfRenderMode = 'normal' }: LocalEmbedPDFProps) {
|
||||
const { t } = useTranslation();
|
||||
const [pdfUrl, setPdfUrl] = useState<string | null>(null);
|
||||
const [, setAnnotations] = useState<Array<{id: string, pageIndex: number, rect: Rect}>>([]);
|
||||
@@ -784,7 +786,19 @@ export function LocalEmbedPDF({ file, url, fileName, enableAnnotations = false,
|
||||
onDrop={(e) => e.preventDefault()}
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
>
|
||||
<TilingLayer documentId={documentId} pageIndex={pageIndex} />
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
transition: 'filter 0.25s ease',
|
||||
filter:
|
||||
pdfRenderMode === 'dark'
|
||||
? 'invert(1) hue-rotate(180deg)'
|
||||
: pdfRenderMode === 'sepia'
|
||||
? 'sepia(0.7) brightness(0.85)'
|
||||
: undefined,
|
||||
}}>
|
||||
<TilingLayer documentId={documentId} pageIndex={pageIndex} />
|
||||
</div>
|
||||
|
||||
<CustomSearchLayer documentId={documentId} pageIndex={pageIndex} />
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
||||
import LastPageIcon from '@mui/icons-material/LastPage';
|
||||
import DescriptionIcon from '@mui/icons-material/Description';
|
||||
import ViewWeekIcon from '@mui/icons-material/ViewWeek';
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import WbSunnyIcon from '@mui/icons-material/WbSunny';
|
||||
import WbTwilightIcon from '@mui/icons-material/WbTwilight';
|
||||
|
||||
interface PdfViewerToolbarProps {
|
||||
// Page navigation props (placeholders for now)
|
||||
@@ -33,6 +36,8 @@ export function PdfViewerToolbar({
|
||||
registerImmediateZoomUpdate,
|
||||
registerImmediateScrollUpdate,
|
||||
registerImmediateSpreadUpdate,
|
||||
pdfRenderMode,
|
||||
cyclePdfRenderMode,
|
||||
} = useViewer();
|
||||
|
||||
const scrollState = getScrollState();
|
||||
@@ -126,7 +131,7 @@ export function PdfViewerToolbar({
|
||||
borderBottomRightRadius: 0,
|
||||
boxShadow: "0 -2px 8px rgba(0,0,0,0.04)",
|
||||
pointerEvents: "auto",
|
||||
minWidth: '26.5rem',
|
||||
minWidth: '30rem',
|
||||
}}
|
||||
>
|
||||
{/* First Page Button */}
|
||||
@@ -234,6 +239,39 @@ export function PdfViewerToolbar({
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
{/* PDF Render Mode Toggle */}
|
||||
<Tooltip
|
||||
content={
|
||||
pdfRenderMode === 'normal'
|
||||
? t("viewer.enableDarkFilter", "Enable Dark Filter")
|
||||
: pdfRenderMode === 'dark'
|
||||
? t("viewer.enableSepiaFilter", "Enable Sepia Filter")
|
||||
: t("viewer.disableColorFilter", "Disable Color Filter")
|
||||
}
|
||||
position="top"
|
||||
arrow
|
||||
>
|
||||
<Button
|
||||
variant={pdfRenderMode !== 'normal' ? "filled" : "light"}
|
||||
color="blue"
|
||||
size="md"
|
||||
radius="xl"
|
||||
onClick={cyclePdfRenderMode}
|
||||
style={{ minWidth: '2.5rem' }}
|
||||
aria-label={
|
||||
pdfRenderMode === 'normal'
|
||||
? t("viewer.enableDarkFilter", "Enable Dark Filter")
|
||||
: pdfRenderMode === 'dark'
|
||||
? t("viewer.enableSepiaFilter", "Enable Sepia Filter")
|
||||
: t("viewer.disableColorFilter", "Disable Color Filter")
|
||||
}
|
||||
>
|
||||
{pdfRenderMode === 'normal' && <DarkModeIcon fontSize="small" />}
|
||||
{pdfRenderMode === 'dark' && <WbTwilightIcon fontSize="small" />}
|
||||
{pdfRenderMode === 'sepia' && <WbSunnyIcon fontSize="small" />}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
{/* Zoom Controls */}
|
||||
<Group gap={4} align="center" style={{ marginLeft: 16 }}>
|
||||
<Button
|
||||
|
||||
@@ -7,6 +7,7 @@ import React, {
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import { useNavigation } from '@app/contexts/NavigationContext';
|
||||
import { preferencesService, type PdfRenderMode } from '@app/services/preferencesService';
|
||||
import {
|
||||
createViewerActions,
|
||||
ScrollActions,
|
||||
@@ -170,6 +171,10 @@ export interface ViewerContextType {
|
||||
// Save changes function - registered by EmbedPdfViewer
|
||||
applyChanges: (() => Promise<void>) | null;
|
||||
setApplyChanges: (fn: (() => Promise<void>) | null) => void;
|
||||
|
||||
// PDF page color rendering mode (viewer-only, never modifies the PDF)
|
||||
pdfRenderMode: PdfRenderMode;
|
||||
cyclePdfRenderMode: () => void;
|
||||
}
|
||||
|
||||
export const ViewerContext = createContext<ViewerContextType | null>(null);
|
||||
@@ -194,6 +199,9 @@ export const ViewerProvider: React.FC<ViewerProviderProps> = ({ children }) => {
|
||||
const [isAnnotationsVisible, setIsAnnotationsVisible] = useState(true);
|
||||
const [isAnnotationMode, setIsAnnotationModeState] = useState(false);
|
||||
const [activeFileIndex, setActiveFileIndex] = useState(0);
|
||||
const [pdfRenderMode, setPdfRenderModeState] = useState<PdfRenderMode>(
|
||||
() => preferencesService.getPreference('pdfRenderMode')
|
||||
);
|
||||
|
||||
// Get current navigation state to check if we're in sign mode
|
||||
useNavigation();
|
||||
@@ -320,6 +328,15 @@ export const ViewerProvider: React.FC<ViewerProviderProps> = ({ children }) => {
|
||||
setIsAnnotationModeState(enabled);
|
||||
};
|
||||
|
||||
const cyclePdfRenderMode = useCallback(() => {
|
||||
setPdfRenderModeState(prev => {
|
||||
const next: PdfRenderMode =
|
||||
prev === 'normal' ? 'dark' : prev === 'dark' ? 'sepia' : 'normal';
|
||||
preferencesService.setPreference('pdfRenderMode', next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// State getters - read from bridge refs
|
||||
const getScrollState = (): ScrollState => {
|
||||
return bridgeRefs.current.scroll?.state || { currentPage: 1, totalPages: 0 };
|
||||
@@ -504,6 +521,10 @@ export const ViewerProvider: React.FC<ViewerProviderProps> = ({ children }) => {
|
||||
// Apply changes
|
||||
applyChanges,
|
||||
setApplyChanges,
|
||||
|
||||
// PDF page rendering mode
|
||||
pdfRenderMode,
|
||||
cyclePdfRenderMode,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,8 @@ import { type ThemeMode, getSystemTheme } from '@app/constants/theme';
|
||||
|
||||
export type LogoVariant = 'modern' | 'classic';
|
||||
|
||||
export type PdfRenderMode = 'normal' | 'dark' | 'sepia';
|
||||
|
||||
export interface UserPreferences {
|
||||
autoUnzip: boolean;
|
||||
autoUnzipFileLimit: number;
|
||||
@@ -17,6 +19,7 @@ export interface UserPreferences {
|
||||
hideUnavailableTools: boolean;
|
||||
hideUnavailableConversions: boolean;
|
||||
logoVariant: LogoVariant | null;
|
||||
pdfRenderMode: PdfRenderMode;
|
||||
}
|
||||
|
||||
export const DEFAULT_PREFERENCES: UserPreferences = {
|
||||
@@ -33,6 +36,7 @@ export const DEFAULT_PREFERENCES: UserPreferences = {
|
||||
hideUnavailableTools: false,
|
||||
hideUnavailableConversions: false,
|
||||
logoVariant: null,
|
||||
pdfRenderMode: 'normal',
|
||||
};
|
||||
|
||||
const STORAGE_KEY = 'stirlingpdf_preferences';
|
||||
|
||||
Reference in New Issue
Block a user