Merge branch 'V2' into add_eslint_plugins_20250928

This commit is contained in:
Ludy 2025-10-02 16:37:29 +02:00 committed by GitHub
commit 1b8f6d224e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 189 additions and 137 deletions

View File

@ -324,7 +324,7 @@ const FileEditorThumbnail = ({
marginTop: '0.5rem', marginTop: '0.5rem',
marginBottom: '0.5rem', marginBottom: '0.5rem',
}}> }}>
<Text size="lg" fw={700} className={styles.title} lineClamp={2}> <Text size="lg" fw={700} className={`${styles.title} ph-no-capture `} lineClamp={2}>
{file.name} {file.name}
</Text> </Text>
<Text <Text
@ -350,6 +350,7 @@ const FileEditorThumbnail = ({
<div className={styles.previewPaper}> <div className={styles.previewPaper}>
{file.thumbnailUrl && ( {file.thumbnailUrl && (
<img <img
className="ph-no-capture"
src={file.thumbnailUrl} src={file.thumbnailUrl}
alt={file.name} alt={file.name}
draggable={false} draggable={false}

View File

@ -26,7 +26,7 @@ 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 size="sm" c="dimmed">{t('fileManager.fileName', 'Name')}</Text> <Text className='ph-no-capture' size="sm" c="dimmed">{t('fileManager.fileName', 'Name')}</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

@ -93,7 +93,7 @@ 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} truncate style={{ flex: 1 }}>{file.name}</Text> <Text size="sm" fw={500} className='ph-no-capture' truncate style={{ flex: 1 }}>{file.name}</Text>
<Badge size="xs" variant="light" color={"blue"}> <Badge size="xs" variant="light" color={"blue"}>
v{currentVersion} v{currentVersion}
</Badge> </Badge>

View File

@ -317,6 +317,7 @@ const FileThumbnail = ({
> >
{file.thumbnail && ( {file.thumbnail && (
<img <img
className="ph-no-capture"
src={file.thumbnail} src={file.thumbnail}
alt={file.name} alt={file.name}
draggable={false} draggable={false}

View File

@ -371,6 +371,7 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
</div> </div>
) : thumbnailUrl ? ( ) : thumbnailUrl ? (
<img <img
className="ph-no-capture"
src={thumbnailUrl} src={thumbnailUrl}
alt={`Page ${page.pageNumber}`} alt={`Page ${page.pageNumber}`}
draggable={false} draggable={false}

View File

@ -0,0 +1,34 @@
import React from "react";
interface ToolIconProps {
icon: React.ReactNode;
opacity?: number;
color?: string;
marginRight?: string;
}
/**
* Shared icon component for consistent tool icon styling across the application.
* Uses the same visual pattern as ToolButton: scaled to 0.8, centered transform, consistent spacing.
*/
export const ToolIcon: React.FC<ToolIconProps> = ({
icon,
opacity = 1,
color = "var(--tools-text-and-icon-color)",
marginRight = "0.5rem"
}) => {
return (
<div
className="tool-button-icon"
style={{
color,
marginRight,
transform: "scale(0.8)",
transformOrigin: "center",
opacity
}}
>
{icon}
</div>
);
};

View File

@ -19,7 +19,7 @@ const DocumentThumbnail: React.FC<DocumentThumbnailProps> = ({
children children
}) => { }) => {
if (!file) return null; if (!file) return null;
const containerStyle = { const containerStyle = {
position: 'relative' as const, position: 'relative' as const,
cursor: onClick ? 'pointer' : 'default', cursor: onClick ? 'pointer' : 'default',
@ -36,6 +36,7 @@ const DocumentThumbnail: React.FC<DocumentThumbnailProps> = ({
return ( return (
<Box style={containerStyle} onClick={onClick}> <Box style={containerStyle} onClick={onClick}>
<Image <Image
className='ph-no-capture'
src={thumbnail} src={thumbnail}
alt={`Preview of ${file.name}`} alt={`Preview of ${file.name}`}
fit="contain" fit="contain"
@ -49,11 +50,12 @@ 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 <PictureAsPdfIcon
style={{ className='ph-no-capture'
fontSize: '2rem', style={{
color: 'var(--mantine-color-gray-6)' fontSize: '2rem',
}} color: 'var(--mantine-color-gray-6)'
}}
/> />
</Center> </Center>
{children} {children}
@ -61,4 +63,4 @@ const DocumentThumbnail: React.FC<DocumentThumbnailProps> = ({
); );
}; };
export default DocumentThumbnail; export default DocumentThumbnail;

View File

@ -200,7 +200,7 @@ export default function PageNumberPreview({ parameters, onParameterChange, file,
<img <img
src={pageThumbnail} src={pageThumbnail}
alt="page preview" alt="page preview"
className={styles.pageThumbnail} className={`${styles.pageThumbnail} ph-no-capture`}
draggable={false} draggable={false}
/> />
)} )}
@ -238,4 +238,4 @@ export default function PageNumberPreview({ parameters, onParameterChange, file,
</div> </div>
</div> </div>
); );
} }

View File

@ -1,11 +1,12 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Group, Text, ActionIcon, Menu, Box } from '@mantine/core'; import { Group, Text, ActionIcon, Menu, Button, Box } from '@mantine/core';
import MoreVertIcon from '@mui/icons-material/MoreVert'; import MoreVertIcon from '@mui/icons-material/MoreVert';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
import ContentCopyIcon from '@mui/icons-material/ContentCopy'; import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import { Tooltip } from '../../shared/Tooltip'; import { Tooltip } from '../../shared/Tooltip';
import { ToolIcon } from '../../shared/ToolIcon';
import { ToolRegistryEntry } from '../../../data/toolsTaxonomy'; import { ToolRegistryEntry } from '../../../data/toolsTaxonomy';
interface AutomationEntryProps { interface AutomationEntryProps {
@ -50,8 +51,7 @@ export default function AutomationEntry({
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false);
// Keep item in hovered state if menu is open const shouldShowMenu = isHovered || isMenuOpen;
const shouldShowHovered = isHovered || isMenuOpen;
// Helper function to resolve tool display names // Helper function to resolve tool display names
const getToolDisplayName = (operation: string): string => { const getToolDisplayName = (operation: string): string => {
@ -108,134 +108,135 @@ export default function AutomationEntry({
); );
}; };
const renderContent = () => { const buttonContent = (
if (title) { <>
// Custom automation with title {BadgeIcon && (
return ( <ToolIcon
<Group gap="md" align="center" justify="flex-start" style={{ width: '100%' }}> icon={<BadgeIcon />}
{BadgeIcon && ( {...(keepIconColor && { color: 'var(--mantine-primary-color-filled)' })}
<BadgeIcon />
style={{ )}
color: keepIconColor ? 'var(--mantine-primary-color-filled)' : 'var(--mantine-color-text)' <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', flex: 1, overflow: 'visible' }}>
}} {title ? (
/> // Custom automation with title
)} <Text size="sm" style={{ textAlign: 'left' }}>
<Text size="xs" style={{ flex: 1, textAlign: 'left', color: 'var(--mantine-color-text)' }}>
{title} {title}
</Text> </Text>
</Group> ) : (
); // Suggested automation showing tool chain
} else {
// Suggested automation showing tool chain
return (
<Group gap="md" align="center" justify="flex-start" style={{ width: '100%' }}>
{BadgeIcon && (
<BadgeIcon
style={{
color: keepIconColor ? 'var(--mantine-primary-color-filled)' : 'var(--mantine-color-text)'
}}
/>
)}
<Group gap="xs" justify="flex-start" style={{ flex: 1 }}> <Group gap="xs" justify="flex-start" style={{ flex: 1 }}>
{operations.map((op, index) => ( {operations.map((op, index) => (
<React.Fragment key={`${op}-${index}`}> <React.Fragment key={`${op}-${index}`}>
<Text size="xs" style={{ color: 'var(--mantine-color-text)' }}> <Text size="sm">
{getToolDisplayName(op)} {getToolDisplayName(op)}
</Text> </Text>
{index < operations.length - 1 && ( {index < operations.length - 1 && (
<Text size="xs" c="dimmed" style={{ color: 'var(--mantine-color-text)' }}> <Text size="sm" c="dimmed">
</Text> </Text>
)} )}
</React.Fragment> </React.Fragment>
))} ))}
</Group> </Group>
</Group> )}
); </div>
} </>
}; );
const boxContent = ( const wrapperContent = (
<Box <Box
style={{ style={{ position: 'relative', width: '100%' }}
backgroundColor: shouldShowHovered ? 'var(--mantine-color-gray-1)' : 'transparent',
borderRadius: 'var(--mantine-radius-md)',
transition: 'background-color 0.15s ease',
padding: '0.75rem 1rem',
cursor: 'pointer'
}}
onClick={onClick}
onMouseEnter={() => setIsHovered(true)} onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)} onMouseLeave={() => setIsHovered(false)}
> >
<Group gap="md" align="center" justify="space-between" style={{ width: '100%' }}> <Button
<div style={{ flex: 1, display: 'flex', justifyContent: 'flex-start' }}> variant="subtle"
{renderContent()} onClick={onClick}
</div> size="sm"
radius="md"
fullWidth
justify="flex-start"
className="tool-button"
styles={{
root: {
borderRadius: 0,
color: "var(--tools-text-and-icon-color)",
overflow: 'visible',
backgroundColor: shouldShowMenu ? 'var(--automation-entry-hover-bg)' : undefined,
'&:hover': {
backgroundColor: 'var(--automation-entry-hover-bg)'
}
},
label: { overflow: 'visible' }
}}
>
{buttonContent}
</Button>
{showMenu && (
<Menu
position="bottom-end"
withinPortal
onOpen={() => setIsMenuOpen(true)}
onClose={() => setIsMenuOpen(false)}
>
<Menu.Target>
<ActionIcon
variant="subtle"
c="dimmed"
size="md"
onClick={(e) => e.stopPropagation()}
style={{
position: 'absolute',
right: '0.5rem',
top: '50%',
transform: 'translateY(-50%)',
zIndex: 1,
opacity: shouldShowMenu ? 1 : 0,
transition: 'opacity 0.2s ease',
pointerEvents: shouldShowMenu ? 'auto' : 'none'
}}
>
<MoreVertIcon style={{ fontSize: 20 }} />
</ActionIcon>
</Menu.Target>
{showMenu && ( <Menu.Dropdown>
<Menu {onCopy && (
position="bottom-end" <Menu.Item
withinPortal leftSection={<ContentCopyIcon style={{ fontSize: 16 }} />}
onOpen={() => setIsMenuOpen(true)} onClick={(e) => {
onClose={() => setIsMenuOpen(false)} e.stopPropagation();
> onCopy();
<Menu.Target>
<ActionIcon
variant="subtle"
c="dimmed"
size="md"
onClick={(e) => e.stopPropagation()}
style={{
opacity: shouldShowHovered ? 1 : 0,
transform: shouldShowHovered ? 'scale(1)' : 'scale(0.8)',
transition: 'opacity 0.3s ease, transform 0.3s ease',
pointerEvents: shouldShowHovered ? 'auto' : 'none'
}} }}
> >
<MoreVertIcon style={{ fontSize: 20 }} /> {t('automate.copyToSaved', 'Copy to Saved')}
</ActionIcon> </Menu.Item>
</Menu.Target> )}
{onEdit && (
<Menu.Dropdown> <Menu.Item
{onCopy && ( leftSection={<EditIcon style={{ fontSize: 16 }} />}
<Menu.Item onClick={(e) => {
leftSection={<ContentCopyIcon style={{ fontSize: 16 }} />} e.stopPropagation();
onClick={(e) => { onEdit();
e.stopPropagation(); }}
onCopy(); >
}} {t('edit', 'Edit')}
> </Menu.Item>
{t('automate.copyToSaved', 'Copy to Saved')} )}
</Menu.Item> {onDelete && (
)} <Menu.Item
{onEdit && ( leftSection={<DeleteIcon style={{ fontSize: 16 }} />}
<Menu.Item onClick={(e) => {
leftSection={<EditIcon style={{ fontSize: 16 }} />} e.stopPropagation();
onClick={(e) => { onDelete();
e.stopPropagation(); }}
onEdit(); >
}} {t('delete', 'Delete')}
> </Menu.Item>
{t('edit', 'Edit')} )}
</Menu.Item> </Menu.Dropdown>
)} </Menu>
{onDelete && ( )}
<Menu.Item
leftSection={<DeleteIcon style={{ fontSize: 16 }} />}
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
>
{t('delete', 'Delete')}
</Menu.Item>
)}
</Menu.Dropdown>
</Menu>
)}
</Group>
</Box> </Box>
); );
@ -249,9 +250,9 @@ export default function AutomationEntry({
arrow={true} arrow={true}
delay={500} delay={500}
> >
{boxContent} {wrapperContent}
</Tooltip> </Tooltip>
) : ( ) : (
boxContent wrapperContent
); );
} }

View File

@ -62,6 +62,7 @@ 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={{

View File

@ -2,6 +2,7 @@ import React from 'react';
import { Stack, Text, Divider, Card, Group, Anchor } from '@mantine/core'; import { Stack, Text, Divider, Card, Group, Anchor } from '@mantine/core';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSuggestedTools } from '../../../hooks/useSuggestedTools'; import { useSuggestedTools } from '../../../hooks/useSuggestedTools';
import { ToolIcon } from '../../shared/ToolIcon';
export function SuggestedToolsSection(): React.ReactElement { export function SuggestedToolsSection(): React.ReactElement {
const { t } = useTranslation(); const { t } = useTranslation();
@ -31,7 +32,7 @@ export function SuggestedToolsSection(): React.ReactElement {
style={{ cursor: 'pointer' }} style={{ cursor: 'pointer' }}
> >
<Group gap="xs"> <Group gap="xs">
<IconComponent fontSize="small" /> <ToolIcon icon={<IconComponent />} />
<Text size="sm" fw={500}> <Text size="sm" fw={500}>
{tool.title} {tool.title}
</Text> </Text>

View File

@ -2,6 +2,7 @@ import React from "react";
import { Button } from "@mantine/core"; import { Button } from "@mantine/core";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Tooltip } from "../../shared/Tooltip"; import { Tooltip } from "../../shared/Tooltip";
import { ToolIcon } from "../../shared/ToolIcon";
import { ToolRegistryEntry } from "../../../data/toolsTaxonomy"; import { ToolRegistryEntry } from "../../../data/toolsTaxonomy";
import { useToolNavigation } from "../../../hooks/useToolNavigation"; import { useToolNavigation } from "../../../hooks/useToolNavigation";
import { handleUnlessSpecialClick } from "../../../utils/clickHandlers"; import { handleUnlessSpecialClick } from "../../../utils/clickHandlers";
@ -57,7 +58,10 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
const buttonContent = ( const buttonContent = (
<> <>
<div className="tool-button-icon" style={{ color: "var(--tools-text-and-icon-color)", marginRight: "0.5rem", transform: "scale(0.8)", transformOrigin: "center", opacity: isUnavailable ? 0.25 : 1 }}>{tool.icon}</div> <ToolIcon
icon={tool.icon}
opacity={isUnavailable ? 0.25 : 1}
/>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', flex: 1, overflow: 'visible' }}> <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', flex: 1, overflow: 'visible' }}>
<FitText <FitText
text={tool.name} text={tool.name}
@ -67,9 +71,9 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
style={{ display: 'inline-block', maxWidth: '100%', opacity: isUnavailable ? 0.25 : 1 }} style={{ display: 'inline-block', maxWidth: '100%', opacity: isUnavailable ? 0.25 : 1 }}
/> />
{matchedSynonym && ( {matchedSynonym && (
<span style={{ <span style={{
fontSize: '0.75rem', fontSize: '0.75rem',
color: 'var(--mantine-color-dimmed)', color: 'var(--mantine-color-dimmed)',
opacity: isUnavailable ? 0.25 : 1, opacity: isUnavailable ? 0.25 : 1,
marginTop: '1px', marginTop: '1px',
overflow: 'visible', overflow: 'visible',

View File

@ -182,14 +182,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 style={{ <div
height: '100%', className='ph-no-capture'
width: '100%',
position: 'relative', style={{
overflow: 'hidden', height: '100%',
flex: 1, width: '100%',
minHeight: 0, position: 'relative',
minWidth: 0 overflow: 'hidden',
flex: 1,
minHeight: 0,
minWidth: 0,
}}> }}>
<EmbedPDF <EmbedPDF
engine={engine} engine={engine}
@ -310,7 +313,7 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
<CustomSearchLayer pageIndex={pageIndex} scale={scale} /> <CustomSearchLayer pageIndex={pageIndex} scale={scale} />
{/* Selection layer for text interaction */} {/* Selection layer for text interaction */}
<SelectionLayer pageIndex={pageIndex} scale={scale} /> <SelectionLayer pageIndex={pageIndex} scale={scale} />
{/* Annotation layer for signatures (only when enabled) */} {/* Annotation layer for signatures (only when enabled) */}
{enableAnnotations && ( {enableAnnotations && (
<AnnotationLayer <AnnotationLayer

View File

@ -134,6 +134,7 @@ export function ThumbnailSidebar({ visible, onToggle: _onToggle }: ThumbnailSide
{/* Thumbnail Image */} {/* Thumbnail Image */}
{thumbnails[pageIndex] && thumbnails[pageIndex] !== 'error' ? ( {thumbnails[pageIndex] && thumbnails[pageIndex] !== 'error' ? (
<img <img
className='ph-no-capture'
src={thumbnails[pageIndex]} src={thumbnails[pageIndex]}
alt={`Page ${pageIndex + 1} thumbnail`} alt={`Page ${pageIndex + 1} thumbnail`}
style={{ style={{

View File

@ -120,6 +120,7 @@
--border-strong: #9ca3af; --border-strong: #9ca3af;
--hover-bg: #f9fafb; --hover-bg: #f9fafb;
--active-bg: #f3f4f6; --active-bg: #f3f4f6;
--automation-entry-hover-bg: var(--color-gray-100);
/* Icon colors for light mode */ /* Icon colors for light mode */
--icon-user-bg: #9CA3AF; --icon-user-bg: #9CA3AF;
@ -317,6 +318,7 @@
--border-strong: #4b5563; --border-strong: #4b5563;
--hover-bg: #374151; --hover-bg: #374151;
--active-bg: #4b5563; --active-bg: #4b5563;
--automation-entry-hover-bg: var(--color-gray-200);
/* Icon colors for dark mode */ /* Icon colors for dark mode */
--icon-user-bg: #2A2F36; --icon-user-bg: #2A2F36;