Improvement/V2/generic_obscure_component_wrapper (#4794)

Created PrivateContent Component to be used as wrapper. 

This way all tools that need to obscure contents can update this
wrapper.

---------

Co-authored-by: Connor Yoh <connor@stirlingpdf.com>
Co-authored-by: James Brunton <jbrunton96@gmail.com>
This commit is contained in:
ConnorYoh 2025-11-10 11:24:14 +00:00 committed by GitHub
parent 45389340ed
commit f4543d26cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 237 additions and 166 deletions

View File

@ -3,6 +3,7 @@ import { Paper, Button, Modal, Stack, Text, Popover, ColorPicker as MantineColor
import { ColorSwatchButton } from '@app/components/annotation/shared/ColorPicker'; import { ColorSwatchButton } from '@app/components/annotation/shared/ColorPicker';
import PenSizeSelector from '@app/components/tools/sign/PenSizeSelector'; import PenSizeSelector from '@app/components/tools/sign/PenSizeSelector';
import SignaturePad from 'signature_pad'; import SignaturePad from 'signature_pad';
import { PrivateContent } from '@app/components/shared/PrivateContent';
interface DrawingCanvasProps { interface DrawingCanvasProps {
selectedColor: string; selectedColor: string;
@ -177,19 +178,21 @@ export const DrawingCanvas: React.FC<DrawingCanvasProps> = ({
<Paper withBorder p="md"> <Paper withBorder p="md">
<Stack gap="sm"> <Stack gap="sm">
<Text fw={500}>Draw your signature</Text> <Text fw={500}>Draw your signature</Text>
<canvas <PrivateContent>
ref={previewCanvasRef} <canvas
width={width} ref={previewCanvasRef}
height={height} width={width}
style={{ height={height}
border: '1px solid #ccc', style={{
borderRadius: '4px', border: '1px solid #ccc',
cursor: disabled ? 'default' : 'pointer', borderRadius: '4px',
backgroundColor: '#ffffff', cursor: disabled ? 'default' : 'pointer',
width: '100%', backgroundColor: '#ffffff',
}} width: '100%',
onClick={disabled ? undefined : openModal} }}
/> onClick={disabled ? undefined : openModal}
/>
</PrivateContent>
<Text size="sm" c="dimmed" ta="center"> <Text size="sm" c="dimmed" ta="center">
Click to open drawing canvas Click to open drawing canvas
</Text> </Text>
@ -246,23 +249,25 @@ export const DrawingCanvas: React.FC<DrawingCanvasProps> = ({
</div> </div>
</div> </div>
<canvas <PrivateContent>
ref={(el) => { <canvas
modalCanvasRef.current = el; ref={(el) => {
if (el) initPad(el); modalCanvasRef.current = el;
}} if (el) initPad(el);
style={{ }}
border: '1px solid #ccc', style={{
borderRadius: '4px', border: '1px solid #ccc',
display: 'block', borderRadius: '4px',
touchAction: 'none', display: 'block',
backgroundColor: 'white', touchAction: 'none',
width: '100%', backgroundColor: 'white',
maxWidth: '800px', width: '100%',
height: '400px', maxWidth: '800px',
cursor: 'crosshair', height: '400px',
}} cursor: 'crosshair',
/> }}
/>
</PrivateContent>
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Button variant="subtle" color="red" onClick={clear}> <Button variant="subtle" color="red" onClick={clear}>

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { FileInput, Text, Stack } from '@mantine/core'; import { FileInput, Text, Stack } from '@mantine/core';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PrivateContent } from '@app/components/shared/PrivateContent';
interface ImageUploaderProps { interface ImageUploaderProps {
onImageChange: (file: File | null) => void; onImageChange: (file: File | null) => void;
@ -40,13 +41,15 @@ export const ImageUploader: React.FC<ImageUploaderProps> = ({
return ( return (
<Stack gap="sm"> <Stack gap="sm">
<FileInput <PrivateContent>
label={label || t('sign.image.label', 'Upload signature image')} <FileInput
placeholder={placeholder || t('sign.image.placeholder', 'Select image file')} label={label || t('sign.image.label', 'Upload signature image')}
accept="image/*" placeholder={placeholder || t('sign.image.placeholder', 'Select image file')}
onChange={handleImageChange} accept="image/*"
disabled={disabled} onChange={handleImageChange}
/> disabled={disabled}
/>
</PrivateContent>
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">
{hint || t('sign.image.hint', 'Upload an image of your signature')} {hint || t('sign.image.hint', 'Upload an image of your signature')}
</Text> </Text>

View File

@ -21,6 +21,7 @@ import { FileId } from '@app/types/file';
import { formatFileSize } from '@app/utils/fileUtils'; import { formatFileSize } from '@app/utils/fileUtils';
import ToolChain from '@app/components/shared/ToolChain'; import ToolChain from '@app/components/shared/ToolChain';
import HoverActionMenu, { HoverAction } from '@app/components/shared/HoverActionMenu'; import HoverActionMenu, { HoverAction } from '@app/components/shared/HoverActionMenu';
import { PrivateContent } from '@app/components/shared/PrivateContent';
@ -328,8 +329,8 @@ const FileEditorThumbnail = ({
marginTop: '0.5rem', marginTop: '0.5rem',
marginBottom: '0.5rem', marginBottom: '0.5rem',
}}> }}>
<Text size="lg" fw={700} className={`${styles.title} ph-no-capture `} lineClamp={2}> <Text size="lg" fw={700} className={styles.title} lineClamp={2}>
{file.name} <PrivateContent>{file.name}</PrivateContent>
</Text> </Text>
<Text <Text
size="sm" size="sm"
@ -353,20 +354,20 @@ const FileEditorThumbnail = ({
> >
<div className={styles.previewPaper}> <div className={styles.previewPaper}>
{file.thumbnailUrl && ( {file.thumbnailUrl && (
<img <PrivateContent>
className="ph-no-capture" <img
src={file.thumbnailUrl} src={file.thumbnailUrl}
alt={file.name} alt={file.name}
draggable={false} draggable={false}
loading="lazy" loading="lazy"
decoding="async" decoding="async"
onError={(e) => { onError={(e) => {
const img = e.currentTarget; const img = e.currentTarget;
img.style.display = 'none'; img.style.display = 'none';
img.parentElement?.setAttribute('data-thumb-missing', 'true'); img.parentElement?.setAttribute('data-thumb-missing', 'true');
}} }}
style={{ style={{
maxWidth: '80%', maxWidth: '80%',
maxHeight: '80%', maxHeight: '80%',
objectFit: 'contain', objectFit: 'contain',
borderRadius: 0, borderRadius: 0,
@ -378,6 +379,7 @@ const FileEditorThumbnail = ({
alignSelf: 'start' alignSelf: 'start'
}} }}
/> />
</PrivateContent>
)} )}
</div> </div>

View File

@ -6,6 +6,7 @@ import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { getFileSize } from '@app/utils/fileUtils'; import { getFileSize } from '@app/utils/fileUtils';
import { StirlingFileStub } from '@app/types/fileContext'; import { StirlingFileStub } from '@app/types/fileContext';
import { PrivateContent } from '@app/components/shared/PrivateContent';
interface CompactFileDetailsProps { interface CompactFileDetailsProps {
currentFile: StirlingFileStub | null; currentFile: StirlingFileStub | null;
@ -41,18 +42,19 @@ const CompactFileDetails: React.FC<CompactFileDetailsProps> = ({
{/* Small preview */} {/* Small preview */}
<Box style={{ width: '7.5rem', height: '9.375rem', flexShrink: 0, position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> <Box style={{ width: '7.5rem', height: '9.375rem', flexShrink: 0, position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{currentFile && thumbnail ? ( {currentFile && thumbnail ? (
<img <PrivateContent>
className='ph-no-capture' <img
src={thumbnail} src={thumbnail}
alt={currentFile.name} alt={currentFile.name}
style={{ style={{
maxWidth: '100%', maxWidth: '100%',
maxHeight: '100%', maxHeight: '100%',
objectFit: 'contain', objectFit: 'contain',
borderRadius: '0.25rem', borderRadius: '0.25rem',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)' boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)'
}} }}
/> />
</PrivateContent>
) : currentFile ? ( ) : currentFile ? (
<Center style={{ <Center style={{
width: '100%', width: '100%',
@ -67,8 +69,8 @@ const CompactFileDetails: React.FC<CompactFileDetailsProps> = ({
{/* File info */} {/* File info */}
<Box style={{ flex: 1, minWidth: 0 }}> <Box style={{ flex: 1, minWidth: 0 }}>
<Text className='ph-no-capture' size="sm" fw={500} truncate> <Text size="sm" fw={500} truncate>
{currentFile ? currentFile.name : 'No file selected'} <PrivateContent>{currentFile ? currentFile.name : 'No file selected'}</PrivateContent>
</Text> </Text>
<Text size="xs" c="dimmed"> <Text size="xs" c="dimmed">
{currentFile ? getFileSize(currentFile) : ''} {currentFile ? getFileSize(currentFile) : ''}

View File

@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { detectFileExtension, getFileSize } from '@app/utils/fileUtils'; import { detectFileExtension, getFileSize } from '@app/utils/fileUtils';
import { StirlingFileStub } from '@app/types/fileContext'; import { StirlingFileStub } from '@app/types/fileContext';
import ToolChain from '@app/components/shared/ToolChain'; import ToolChain from '@app/components/shared/ToolChain';
import { PrivateContent } from '@app/components/shared/PrivateContent';
interface FileInfoCardProps { interface FileInfoCardProps {
currentFile: StirlingFileStub | null; currentFile: StirlingFileStub | null;
@ -26,7 +27,9 @@ const FileInfoCard: React.FC<FileInfoCardProps> = ({
<ScrollArea style={{ flex: 1 }} p="md"> <ScrollArea style={{ flex: 1 }} p="md">
<Stack gap="sm"> <Stack gap="sm">
<Group justify="space-between" py="xs"> <Group justify="space-between" py="xs">
<Text className='ph-no-capture' size="sm" c="dimmed">{t('fileManager.fileName', 'Name')}</Text> <Text size="sm" c="dimmed">
<PrivateContent>{t('fileManager.fileName', 'Name')}</PrivateContent>
</Text>
<Text size="sm" fw={500} style={{ maxWidth: '60%', textAlign: 'right' }} truncate> <Text size="sm" fw={500} style={{ maxWidth: '60%', textAlign: 'right' }} truncate>
{currentFile ? currentFile.name : ''} {currentFile ? currentFile.name : ''}
</Text> </Text>

View File

@ -13,6 +13,7 @@ import { useFileManagerContext } from '@app/contexts/FileManagerContext';
import { zipFileService } from '@app/services/zipFileService'; import { zipFileService } from '@app/services/zipFileService';
import ToolChain from '@app/components/shared/ToolChain'; import ToolChain from '@app/components/shared/ToolChain';
import { Z_INDEX_OVER_FILE_MANAGER_MODAL } from '@app/styles/zIndex'; import { Z_INDEX_OVER_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
import { PrivateContent } from '@app/components/shared/PrivateContent';
interface FileListItemProps { interface FileListItemProps {
file: StirlingFileStub; file: StirlingFileStub;
@ -99,7 +100,9 @@ const FileListItem: React.FC<FileListItemProps> = ({
<Box style={{ flex: 1, minWidth: 0 }}> <Box style={{ flex: 1, minWidth: 0 }}>
<Group gap="xs" align="center"> <Group gap="xs" align="center">
<Text size="sm" fw={500} className='ph-no-capture' truncate style={{ flex: 1 }}>{file.name}</Text> <Text size="sm" fw={500} truncate style={{ flex: 1 }}>
<PrivateContent>{file.name}</PrivateContent>
</Text>
<Badge size="xs" variant="light" color={"blue"}> <Badge size="xs" variant="light" color={"blue"}>
v{currentVersion} v{currentVersion}
</Badge> </Badge>

View File

@ -12,6 +12,7 @@ import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-d
import styles from '@app/components/pageEditor/PageEditor.module.css'; import styles from '@app/components/pageEditor/PageEditor.module.css';
import { useFileContext } from '@app/contexts/FileContext'; import { useFileContext } from '@app/contexts/FileContext';
import { FileId } from '@app/types/file'; import { FileId } from '@app/types/file';
import { PrivateContent } from '@app/components/shared/PrivateContent';
interface FileItem { interface FileItem {
id: FileId; id: FileId;
@ -316,29 +317,30 @@ const FileThumbnail = ({
}} }}
> >
{file.thumbnail && ( {file.thumbnail && (
<img <PrivateContent>
className="ph-no-capture" <img
src={file.thumbnail} src={file.thumbnail}
alt={file.name} alt={file.name}
draggable={false} draggable={false}
onError={(e) => { onError={(e) => {
// Hide broken image if blob URL was revoked // Hide broken image if blob URL was revoked
const img = e.target as HTMLImageElement; const img = e.target as HTMLImageElement;
img.style.display = 'none'; img.style.display = 'none';
}}
style={{
maxWidth: '80%',
maxHeight: '80%',
objectFit: 'contain',
borderRadius: 0,
background: '#ffffff',
border: '1px solid var(--border-default)',
display: 'block',
marginLeft: 'auto',
marginRight: 'auto',
alignSelf: 'start'
}} }}
style={{ />
maxWidth: '80%', </PrivateContent>
maxHeight: '80%',
objectFit: 'contain',
borderRadius: 0,
background: '#ffffff',
border: '1px solid var(--border-default)',
display: 'block',
marginLeft: 'auto',
marginRight: 'auto',
alignSelf: 'start'
}}
/>
)} )}
</div> </div>

View File

@ -14,6 +14,7 @@ import { useThumbnailGeneration } from '@app/hooks/useThumbnailGeneration';
import { useFilesModalContext } from '@app/contexts/FilesModalContext'; import { useFilesModalContext } from '@app/contexts/FilesModalContext';
import styles from '@app/components/pageEditor/PageEditor.module.css'; import styles from '@app/components/pageEditor/PageEditor.module.css';
import HoverActionMenu, { HoverAction } from '@app/components/shared/HoverActionMenu'; import HoverActionMenu, { HoverAction } from '@app/components/shared/HoverActionMenu';
import { PrivateContent } from '@app/components/shared/PrivateContent';
interface PageThumbnailProps { interface PageThumbnailProps {
@ -442,21 +443,22 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
}}></div> }}></div>
</div> </div>
) : thumbnailUrl ? ( ) : thumbnailUrl ? (
<img <PrivateContent>
className="ph-no-capture" <img
src={thumbnailUrl} src={thumbnailUrl}
alt={`Page ${page.pageNumber}`} alt={`Page ${page.pageNumber}`}
draggable={false} draggable={false}
data-original-rotation={page.rotation} data-original-rotation={page.rotation}
style={{ style={{
width: '100%', width: '100%',
height: '100%', height: '100%',
objectFit: 'contain', objectFit: 'contain',
borderRadius: 2, borderRadius: 2,
transform: `rotate(${page.rotation}deg)`, transform: `rotate(${page.rotation}deg)`,
transition: 'transform 0.3s ease-in-out' transition: 'transform 0.3s ease-in-out'
}} }}
/> />
</PrivateContent>
) : ( ) : (
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<Text size="lg" c="dimmed">📄</Text> <Text size="lg" c="dimmed">📄</Text>

View File

@ -3,6 +3,7 @@ import { Menu, Loader, Group, Text } from '@mantine/core';
import VisibilityIcon from '@mui/icons-material/Visibility'; import VisibilityIcon from '@mui/icons-material/Visibility';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import FitText from '@app/components/shared/FitText'; import FitText from '@app/components/shared/FitText';
import { PrivateContent } from '@app/components/shared/PrivateContent';
interface FileDropdownMenuProps { interface FileDropdownMenuProps {
displayName: string; displayName: string;
@ -31,7 +32,9 @@ export const FileDropdownMenu: React.FC<FileDropdownMenuProps> = ({
) : ( ) : (
<VisibilityIcon fontSize="small" /> <VisibilityIcon fontSize="small" />
)} )}
<FitText text={displayName} fontSize={14} minimumFontScale={0.6} className="ph-no-capture" /> <PrivateContent>
<FitText text={displayName} fontSize={14} minimumFontScale={0.6} />
</PrivateContent>
<KeyboardArrowDownIcon fontSize="small" /> <KeyboardArrowDownIcon fontSize="small" />
</div> </div>
</Menu.Target> </Menu.Target>
@ -61,7 +64,9 @@ export const FileDropdownMenu: React.FC<FileDropdownMenuProps> = ({
> >
<Group gap="xs" style={{ width: '100%', justifyContent: 'space-between' }}> <Group gap="xs" style={{ width: '100%', justifyContent: 'space-between' }}>
<div style={{ flex: 1, textAlign: 'left', minWidth: 0 }}> <div style={{ flex: 1, textAlign: 'left', minWidth: 0 }}>
<FitText text={itemName} fontSize={14} minimumFontScale={0.7} className="ph-no-capture" /> <PrivateContent>
<FitText text={itemName} fontSize={14} minimumFontScale={0.7} />
</PrivateContent>
</div> </div>
{file.versionNumber && file.versionNumber > 1 && ( {file.versionNumber && file.versionNumber > 1 && (
<Text size="xs" c="dimmed"> <Text size="xs" c="dimmed">

View File

@ -0,0 +1,40 @@
import React from 'react';
interface PrivateContentProps extends React.HTMLAttributes<HTMLSpanElement> {
children: React.ReactNode;
}
/**
* Wrapper component for content that should not be captured by analytics tools.
* Currently applies the 'ph-no-capture' className to prevent PostHog capture.
*
* Uses `display: contents` to be layout-invisible - the wrapper exists in the DOM
* for analytics filtering, but doesn't affect layout, flexbox, grid, or styling.
*
* Use this component to wrap any content containing sensitive or private information
* that should be excluded from analytics tracking.
*
* @example
* <PrivateContent>
* <Text>Sensitive filename.pdf</Text>
* </PrivateContent>
*
* <PrivateContent>
* <img src={thumbnail} alt="preview" />
* </PrivateContent>
*/
export const PrivateContent: React.FC<PrivateContentProps> = ({
children,
className = '',
style,
...props
}) => {
const combinedClassName = `ph-no-capture${className ? ` ${className}` : ''}`;
const combinedStyle = { display: 'contents' as const, ...style };
return (
<span className={combinedClassName} style={combinedStyle} {...props}>
{children}
</span>
);
};

View File

@ -9,6 +9,7 @@ import PictureAsPdfIcon from "@mui/icons-material/PictureAsPdf";
import { WorkbenchType, isValidWorkbench } from '@app/types/workbench'; import { WorkbenchType, isValidWorkbench } from '@app/types/workbench';
import type { CustomWorkbenchViewInstance } from '@app/contexts/ToolWorkflowContext'; import type { CustomWorkbenchViewInstance } from '@app/contexts/ToolWorkflowContext';
import { FileDropdownMenu } from '@app/components/shared/FileDropdownMenu'; import { FileDropdownMenu } from '@app/components/shared/FileDropdownMenu';
import { PrivateContent } from '@app/components/shared/PrivateContent';
const viewOptionStyle: React.CSSProperties = { const viewOptionStyle: React.CSSProperties = {
@ -54,7 +55,7 @@ const createViewOptions = (
) : ( ) : (
<VisibilityIcon fontSize="small" /> <VisibilityIcon fontSize="small" />
)} )}
<span className="ph-no-capture">{displayName}</span> <PrivateContent>{displayName}</PrivateContent>
</div> </div>
), ),
value: "viewer", value: "viewer",

View File

@ -2,6 +2,7 @@ import React from 'react';
import { Box, Center, Image } from '@mantine/core'; import { Box, Center, Image } from '@mantine/core';
import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
import { StirlingFileStub } from '@app/types/fileContext'; import { StirlingFileStub } from '@app/types/fileContext';
import { PrivateContent } from '@app/components/shared/PrivateContent';
export interface DocumentThumbnailProps { export interface DocumentThumbnailProps {
file: File | StirlingFileStub | null; file: File | StirlingFileStub | null;
@ -35,13 +36,14 @@ const DocumentThumbnail: React.FC<DocumentThumbnailProps> = ({
if (thumbnail) { if (thumbnail) {
return ( return (
<Box style={containerStyle} onClick={onClick}> <Box style={containerStyle} onClick={onClick}>
<Image <PrivateContent>
className='ph-no-capture' <Image
src={thumbnail} src={thumbnail}
alt={`Preview of ${file.name}`} alt={`Preview of ${file.name}`}
fit="contain" fit="contain"
style={{ maxWidth: '100%', maxHeight: '100%' }} style={{ maxWidth: '100%', maxHeight: '100%' }}
/> />
</PrivateContent>
{children} {children}
</Box> </Box>
); );
@ -50,13 +52,14 @@ const DocumentThumbnail: React.FC<DocumentThumbnailProps> = ({
return ( return (
<Box style={containerStyle} onClick={onClick}> <Box style={containerStyle} onClick={onClick}>
<Center style={{ width: '100%', height: '100%', backgroundColor: 'var(--mantine-color-gray-1)', borderRadius: '0.25rem' }}> <Center style={{ width: '100%', height: '100%', backgroundColor: 'var(--mantine-color-gray-1)', borderRadius: '0.25rem' }}>
<PictureAsPdfIcon <PrivateContent>
className='ph-no-capture' <PictureAsPdfIcon
style={{ style={{
fontSize: '2rem', fontSize: '2rem',
color: 'var(--mantine-color-gray-6)' color: 'var(--mantine-color-gray-6)'
}} }}
/> />
</PrivateContent>
</Center> </Center>
{children} {children}
</Box> </Box>

View File

@ -4,6 +4,7 @@ import { AddPageNumbersParameters } from '@app/components/tools/addPageNumbers/u
import { pdfWorkerManager } from '@app/services/pdfWorkerManager'; import { pdfWorkerManager } from '@app/services/pdfWorkerManager';
import { useThumbnailGeneration } from '@app/hooks/useThumbnailGeneration'; import { useThumbnailGeneration } from '@app/hooks/useThumbnailGeneration';
import styles from '@app/components/tools/addPageNumbers/PageNumberPreview.module.css'; import styles from '@app/components/tools/addPageNumbers/PageNumberPreview.module.css';
import { PrivateContent } from '@app/components/shared/PrivateContent';
// Simple utilities for page numbers (adapted from stamp) // Simple utilities for page numbers (adapted from stamp)
const A4_ASPECT_RATIO = 0.707; const A4_ASPECT_RATIO = 0.707;
@ -197,12 +198,14 @@ export default function PageNumberPreview({ parameters, onParameterChange, file,
style={containerStyle} style={containerStyle}
> >
{pageThumbnail && ( {pageThumbnail && (
<img <PrivateContent>
src={pageThumbnail} <img
alt="page preview" src={pageThumbnail}
className={`${styles.pageThumbnail} ph-no-capture`} alt="page preview"
draggable={false} className={styles.pageThumbnail}
/> draggable={false}
/>
</PrivateContent>
)} )}
{/* Quick position overlay grid - EXACT copy from stamp */} {/* Quick position overlay grid - EXACT copy from stamp */}

View File

@ -7,6 +7,7 @@ import { useFilesModalContext } from "@app/contexts/FilesModalContext";
import { useAllFiles } from "@app/contexts/FileContext"; import { useAllFiles } from "@app/contexts/FileContext";
import { useFileManager } from "@app/hooks/useFileManager"; import { useFileManager } from "@app/hooks/useFileManager";
import { StirlingFile } from "@app/types/fileContext"; import { StirlingFile } from "@app/types/fileContext";
import { PrivateContent } from "@app/components/shared/PrivateContent"
export interface FileStatusIndicatorProps { export interface FileStatusIndicatorProps {
selectedFiles?: StirlingFile[]; selectedFiles?: StirlingFile[];
@ -134,7 +135,9 @@ const FileStatusIndicator = ({
return ( return (
<Text size="sm" c="dimmed" style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}> <Text size="sm" c="dimmed" style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}>
{selectedFiles.length === 1 ? t("fileSelected", "Selected: {{filename}}", { filename: selectedFiles[0]?.name }) : t("filesSelected", "{{count}} files selected", { count: selectedFiles.length })} {selectedFiles.length === 1
? <PrivateContent>{t("fileSelected", "Selected: {{filename}}", { filename: selectedFiles[0]?.name }) }</PrivateContent>
: t("filesSelected", "{{count}} files selected", { count: selectedFiles.length })}
</Text> </Text>
); );
}; };

View File

@ -3,6 +3,7 @@ import { Box, Text, Loader, Stack, Center, Flex } from '@mantine/core';
import FilePreview from '@app/components/shared/FilePreview'; import FilePreview from '@app/components/shared/FilePreview';
import FileMetadata from '@app/components/tools/shared/FileMetadata'; import FileMetadata from '@app/components/tools/shared/FileMetadata';
import NavigationControls from '@app/components/tools/shared/NavigationControls'; import NavigationControls from '@app/components/tools/shared/NavigationControls';
import { PrivateContent } from '@app/components/shared/PrivateContent';
export interface ReviewFile { export interface ReviewFile {
file: File; file: File;
@ -62,7 +63,6 @@ const ResultsPreview = ({
{/* File name at the top */} {/* File name at the top */}
<Box mb="sm" style={{ minHeight: '3rem', display: 'flex', alignItems: 'flex-start' }}> <Box mb="sm" style={{ minHeight: '3rem', display: 'flex', alignItems: 'flex-start' }}>
<Text <Text
className='ph-no-capture'
size="sm" size="sm"
fw={500} fw={500}
style={{ style={{
@ -71,7 +71,7 @@ const ResultsPreview = ({
}} }}
title={currentFile.file.name} title={currentFile.file.name}
> >
{currentFile.file.name} <PrivateContent>{currentFile.file.name}</PrivateContent>
</Text> </Text>
</Box> </Box>

View File

@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
import { createPluginRegistration } from '@embedpdf/core'; import { createPluginRegistration } from '@embedpdf/core';
import { EmbedPDF } from '@embedpdf/core/react'; import { EmbedPDF } from '@embedpdf/core/react';
import { usePdfiumEngine } from '@embedpdf/engines/react'; import { usePdfiumEngine } from '@embedpdf/engines/react';
import { PrivateContent } from '@app/components/shared/PrivateContent';
// Import the essential plugins // Import the essential plugins
import { Viewport, ViewportPluginPackage } from '@embedpdf/plugin-viewport/react'; import { Viewport, ViewportPluginPackage } from '@embedpdf/plugin-viewport/react';
@ -184,18 +185,17 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
// Wrap your UI with the <EmbedPDF> provider // Wrap your UI with the <EmbedPDF> provider
return ( return (
<div <PrivateContent>
className='ph-no-capture' <div
style={{
style={{ height: '100%',
height: '100%', width: '100%',
width: '100%', position: 'relative',
position: 'relative', overflow: 'hidden',
overflow: 'hidden', flex: 1,
flex: 1, minHeight: 0,
minHeight: 0, minWidth: 0,
minWidth: 0, }}>
}}>
<EmbedPDF <EmbedPDF
engine={engine} engine={engine}
plugins={plugins} plugins={plugins}
@ -338,6 +338,7 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
</Viewport> </Viewport>
</GlobalPointerProvider> </GlobalPointerProvider>
</EmbedPDF> </EmbedPDF>
</div> </div>
</PrivateContent>
); );
} }

View File

@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Box, ScrollArea } from '@mantine/core'; import { Box, ScrollArea } from '@mantine/core';
import { useViewer } from '@app/contexts/ViewerContext'; import { useViewer } from '@app/contexts/ViewerContext';
import { PrivateContent } from '@app/components/shared/PrivateContent';
interface ThumbnailSidebarProps { interface ThumbnailSidebarProps {
visible: boolean; visible: boolean;
@ -145,18 +146,19 @@ export function ThumbnailSidebar({ visible, onToggle: _onToggle, activeFileIndex
> >
{/* Thumbnail Image */} {/* Thumbnail Image */}
{thumbnails[pageIndex] && thumbnails[pageIndex] !== 'error' ? ( {thumbnails[pageIndex] && thumbnails[pageIndex] !== 'error' ? (
<img <PrivateContent>
className='ph-no-capture' <img
src={thumbnails[pageIndex]} src={thumbnails[pageIndex]}
alt={`Page ${pageIndex + 1} thumbnail`} alt={`Page ${pageIndex + 1} thumbnail`}
style={{ style={{
maxWidth: '100%', maxWidth: '100%',
height: 'auto', height: 'auto',
borderRadius: '4px', borderRadius: '4px',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)', boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
border: '1px solid var(--border-subtle)' border: '1px solid var(--border-subtle)'
}} }}
/> />
</PrivateContent>
) : thumbnails[pageIndex] === 'error' ? ( ) : thumbnails[pageIndex] === 'error' ? (
<div style={{ <div style={{
width: '11.5rem', width: '11.5rem',

View File

@ -1,9 +0,0 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
testTimeout: 5000,
include: ['src/utils/convertUtils.test.ts']
},
});