change requests

This commit is contained in:
EthanHealy01 2025-10-08 14:42:27 +01:00
parent 3d30286b42
commit f5771d827f
10 changed files with 144 additions and 470 deletions

View File

@ -32,7 +32,8 @@ export default function RightRail() {
const topButtons = useMemo(() => buttons.filter(b => (b.section || 'top') === 'top' && (b.visible ?? true)), [buttons]);
// Access PageEditor functions for page-editor-specific actions
const { pageEditorFunctions } = useToolWorkflow();
const { pageEditorFunctions, toolPanelMode, leftPanelView } = useToolWorkflow();
const disableForFullscreen = toolPanelMode === 'fullscreen' && leftPanelView === 'toolPicker';
// CSV input state for page selection
const [csvInput, setCsvInput] = useState<string>("");
@ -183,13 +184,13 @@ export default function RightRail() {
<>
<div className="right-rail-section">
{topButtons.map(btn => (
<Tooltip key={btn.id} content={btn.tooltip} position="left" offset={12} arrow>
<Tooltip key={btn.id} content={btn.tooltip} position="left" offset={12} arrow portalTarget={document.body}>
<ActionIcon
variant="subtle"
radius="md"
className="right-rail-icon"
onClick={() => actions[btn.id]?.()}
disabled={btn.disabled || allButtonsDisabled}
disabled={btn.disabled || allButtonsDisabled || disableForFullscreen}
>
{btn.icon}
</ActionIcon>
@ -207,7 +208,7 @@ export default function RightRail() {
>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1rem' }}>
{/* Search */}
<Tooltip content={t('rightRail.search', 'Search PDF')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.search', 'Search PDF')} position="left" offset={12} arrow portalTarget={document.body}>
<Popover position="left" withArrow shadow="md" offset={8}>
<Popover.Target>
<div style={{ display: 'inline-flex' }}>
@ -215,7 +216,7 @@ export default function RightRail() {
variant="subtle"
radius="md"
className="right-rail-icon"
disabled={currentView !== 'viewer' || allButtonsDisabled}
disabled={currentView !== 'viewer' || allButtonsDisabled || disableForFullscreen}
aria-label={typeof t === 'function' ? t('rightRail.search', 'Search PDF') : 'Search PDF'}
>
<LocalIcon icon="search" width="1.5rem" height="1.5rem" />
@ -235,7 +236,7 @@ export default function RightRail() {
{/* Pan Mode */}
<Tooltip content={t('rightRail.panMode', 'Pan Mode')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.panMode', 'Pan Mode')} position="left" offset={12} arrow portalTarget={document.body}>
<ActionIcon
variant={isPanning ? "filled" : "subtle"}
color={isPanning ? "blue" : undefined}
@ -245,14 +246,14 @@ export default function RightRail() {
viewerContext?.panActions.togglePan();
setIsPanning(!isPanning);
}}
disabled={currentView !== 'viewer' || allButtonsDisabled}
disabled={currentView !== 'viewer' || allButtonsDisabled || disableForFullscreen}
>
<LocalIcon icon="pan-tool-rounded" width="1.5rem" height="1.5rem" />
</ActionIcon>
</Tooltip>
{/* Rotate Left */}
<Tooltip content={t('rightRail.rotateLeft', 'Rotate Left')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.rotateLeft', 'Rotate Left')} position="left" offset={12} arrow portalTarget={document.body}>
<ActionIcon
variant="subtle"
radius="md"
@ -260,14 +261,14 @@ export default function RightRail() {
onClick={() => {
viewerContext?.rotationActions.rotateBackward();
}}
disabled={currentView !== 'viewer' || allButtonsDisabled}
disabled={currentView !== 'viewer' || allButtonsDisabled || disableForFullscreen}
>
<LocalIcon icon="rotate-left" width="1.5rem" height="1.5rem" />
</ActionIcon>
</Tooltip>
{/* Rotate Right */}
<Tooltip content={t('rightRail.rotateRight', 'Rotate Right')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.rotateRight', 'Rotate Right')} position="left" offset={12} arrow portalTarget={document.body}>
<ActionIcon
variant="subtle"
radius="md"
@ -275,14 +276,14 @@ export default function RightRail() {
onClick={() => {
viewerContext?.rotationActions.rotateForward();
}}
disabled={currentView !== 'viewer' || allButtonsDisabled}
disabled={currentView !== 'viewer' || allButtonsDisabled || disableForFullscreen}
>
<LocalIcon icon="rotate-right" width="1.5rem" height="1.5rem" />
</ActionIcon>
</Tooltip>
{/* Sidebar Toggle */}
<Tooltip content={t('rightRail.toggleSidebar', 'Toggle Sidebar')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.toggleSidebar', 'Toggle Sidebar')} position="left" offset={12} arrow portalTarget={document.body}>
<ActionIcon
variant="subtle"
radius="md"
@ -290,7 +291,7 @@ export default function RightRail() {
onClick={() => {
viewerContext?.toggleThumbnailSidebar();
}}
disabled={currentView !== 'viewer' || allButtonsDisabled}
disabled={currentView !== 'viewer' || allButtonsDisabled || disableForFullscreen}
>
<LocalIcon icon="view-list" width="1.5rem" height="1.5rem" />
</ActionIcon>
@ -309,14 +310,14 @@ export default function RightRail() {
>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1rem' }}>
{/* Select All Button */}
<Tooltip content={t('rightRail.selectAll', 'Select All')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.selectAll', 'Select All')} position="left" offset={12} arrow portalTarget={document.body}>
<div>
<ActionIcon
variant="subtle"
radius="md"
className="right-rail-icon"
onClick={handleSelectAll}
disabled={currentView === 'viewer' || totalItems === 0 || selectedCount === totalItems || allButtonsDisabled}
disabled={currentView === 'viewer' || totalItems === 0 || selectedCount === totalItems || allButtonsDisabled || disableForFullscreen}
>
<LocalIcon icon="select-all" width="1.5rem" height="1.5rem" />
</ActionIcon>
@ -324,14 +325,14 @@ export default function RightRail() {
</Tooltip>
{/* Deselect All Button */}
<Tooltip content={t('rightRail.deselectAll', 'Deselect All')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.deselectAll', 'Deselect All')} position="left" offset={12} arrow portalTarget={document.body}>
<div>
<ActionIcon
variant="subtle"
radius="md"
className="right-rail-icon"
onClick={handleDeselectAll}
disabled={currentView === 'viewer' || selectedCount === 0 || allButtonsDisabled}
disabled={currentView === 'viewer' || selectedCount === 0 || allButtonsDisabled || disableForFullscreen}
>
<LocalIcon icon="crop-square-outline" width="1.5rem" height="1.5rem" />
</ActionIcon>
@ -340,7 +341,7 @@ export default function RightRail() {
{/* Select by Numbers - page editor only, with animated presence */}
{pageControlsMounted && (
<Tooltip content={t('rightRail.selectByNumber', 'Select by Page Numbers')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.selectByNumber', 'Select by Page Numbers')} position="left" offset={12} arrow portalTarget={document.body}>
<div className={`right-rail-fade ${pageControlsVisible ? 'enter' : 'exit'}`} aria-hidden={!pageControlsVisible}>
<Popover position="left" withArrow shadow="md" offset={8}>
@ -350,7 +351,7 @@ export default function RightRail() {
variant="subtle"
radius="md"
className="right-rail-icon"
disabled={!pageControlsVisible || totalItems === 0 || allButtonsDisabled}
disabled={!pageControlsVisible || totalItems === 0 || allButtonsDisabled || disableForFullscreen}
aria-label={typeof t === 'function' ? t('rightRail.selectByNumber', 'Select by Page Numbers') : 'Select by Page Numbers'}
>
<LocalIcon icon="pin-end" width="1.5rem" height="1.5rem" />
@ -377,7 +378,7 @@ export default function RightRail() {
{/* Delete Selected Pages - page editor only, with animated presence */}
{pageControlsMounted && (
<Tooltip content={t('rightRail.deleteSelected', 'Delete Selected Pages')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.deleteSelected', 'Delete Selected Pages')} position="left" offset={12} arrow portalTarget={document.body}>
<div className={`right-rail-fade ${pageControlsVisible ? 'enter' : 'exit'}`} aria-hidden={!pageControlsVisible}>
<div style={{ display: 'inline-flex' }}>
@ -386,7 +387,7 @@ export default function RightRail() {
radius="md"
className="right-rail-icon"
onClick={() => { pageEditorFunctions?.handleDelete?.(); }}
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0 || allButtonsDisabled}
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0 || allButtonsDisabled || disableForFullscreen}
aria-label={typeof t === 'function' ? t('rightRail.deleteSelected', 'Delete Selected Pages') : 'Delete Selected Pages'}
>
<LocalIcon icon="delete-outline-rounded" width="1.5rem" height="1.5rem" />
@ -399,7 +400,7 @@ export default function RightRail() {
{/* Export Selected Pages - page editor only */}
{pageControlsMounted && (
<Tooltip content={t('rightRail.exportSelected', 'Export Selected Pages')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.exportSelected', 'Export Selected Pages')} position="left" offset={12} arrow portalTarget={document.body}>
<div className={`right-rail-fade ${pageControlsVisible ? 'enter' : 'exit'}`} aria-hidden={!pageControlsVisible}>
<div style={{ display: 'inline-flex' }}>
<ActionIcon
@ -407,7 +408,7 @@ export default function RightRail() {
radius="md"
className="right-rail-icon"
onClick={() => { pageEditorFunctions?.onExportSelected?.(); }}
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0 || pageEditorFunctions?.exportLoading || allButtonsDisabled}
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0 || pageEditorFunctions?.exportLoading || allButtonsDisabled || disableForFullscreen}
aria-label={typeof t === 'function' ? t('rightRail.exportSelected', 'Export Selected Pages') : 'Export Selected Pages'}
>
<LocalIcon icon="download" width="1.5rem" height="1.5rem" />
@ -418,7 +419,7 @@ export default function RightRail() {
)}
{/* Close (File Editor: Close Selected | Page Editor: Close PDF) */}
<Tooltip content={currentView === 'pageEditor' ? t('rightRail.closePdf', 'Close PDF') : t('rightRail.closeSelected', 'Close Selected Files')} position="left" offset={12} arrow>
<Tooltip content={currentView === 'pageEditor' ? t('rightRail.closePdf', 'Close PDF') : t('rightRail.closeSelected', 'Close Selected Files')} position="left" offset={12} arrow portalTarget={document.body}>
<div>
<ActionIcon
variant="subtle"
@ -429,7 +430,7 @@ export default function RightRail() {
currentView === 'viewer' ||
(currentView === 'fileEditor' && selectedCount === 0) ||
(currentView === 'pageEditor' && (activeFiles.length === 0 || !pageEditorFunctions?.closePdf)) ||
allButtonsDisabled
allButtonsDisabled || disableForFullscreen
}
>
<LocalIcon icon="close-rounded" width="1.5rem" height="1.5rem" />
@ -465,7 +466,7 @@ export default function RightRail() {
currentView === 'pageEditor'
? t('rightRail.exportAll', 'Export PDF')
: (selectedCount > 0 ? t('rightRail.downloadSelected', 'Download Selected Files') : t('rightRail.downloadAll', 'Download All'))
} position="left" offset={12} arrow>
} position="left" offset={12} arrow portalTarget={document.body}>
<div>
<ActionIcon
variant="subtle"
@ -473,7 +474,7 @@ export default function RightRail() {
className="right-rail-icon"
onClick={handleExportAll}
disabled={
currentView === 'viewer' ? !exportState?.canExport : totalItems === 0 || allButtonsDisabled
disableForFullscreen || (currentView === 'viewer' ? !exportState?.canExport : totalItems === 0 || allButtonsDisabled)
}
>
<LocalIcon icon="download" width="1.5rem" height="1.5rem" />

View File

@ -10,6 +10,7 @@ import { useFileState, useFileContext } from '../../../contexts/FileContext';
import { generateThumbnailWithMetadata } from '../../../utils/thumbnailUtils';
import { createProcessedFile } from '../../../contexts/file/fileActions';
import { createStirlingFile, createNewStirlingFileStub } from '../../../types/fileContext';
import { useToolWorkflow } from '../../../contexts/ToolWorkflowContext';
interface ViewerAnnotationControlsProps {
currentView: string;
@ -17,6 +18,8 @@ interface ViewerAnnotationControlsProps {
export default function ViewerAnnotationControls({ currentView }: ViewerAnnotationControlsProps) {
const { t } = useTranslation();
const { toolPanelMode, leftPanelView } = useToolWorkflow();
const disableForFullscreen = toolPanelMode === 'fullscreen' && leftPanelView === 'toolPicker';
const [selectedColor, setSelectedColor] = useState('#000000');
const [isColorPickerOpen, setIsColorPickerOpen] = useState(false);
const [isHoverColorPickerOpen, setIsHoverColorPickerOpen] = useState(false);
@ -42,7 +45,7 @@ export default function ViewerAnnotationControls({ currentView }: ViewerAnnotati
return (
<>
{/* Annotation Visibility Toggle */}
<Tooltip content={t('rightRail.toggleAnnotations', 'Toggle Annotations Visibility')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.toggleAnnotations', 'Toggle Annotations Visibility')} position="left" offset={12} arrow portalTarget={document.body}>
<ActionIcon
variant="subtle"
radius="md"
@ -50,7 +53,7 @@ export default function ViewerAnnotationControls({ currentView }: ViewerAnnotati
onClick={() => {
viewerContext?.toggleAnnotationsVisibility();
}}
disabled={currentView !== 'viewer' || viewerContext?.isAnnotationMode}
disabled={currentView !== 'viewer' || viewerContext?.isAnnotationMode || disableForFullscreen}
>
<LocalIcon
icon={viewerContext?.isAnnotationsVisible ? "visibility" : "visibility-off-rounded"}
@ -94,7 +97,7 @@ export default function ViewerAnnotationControls({ currentView }: ViewerAnnotati
}
}
}}
disabled={currentView !== 'viewer'}
disabled={currentView !== 'viewer' || disableForFullscreen}
aria-label="Drawing mode active"
>
<LocalIcon icon="edit" width="1.5rem" height="1.5rem" />
@ -119,7 +122,7 @@ export default function ViewerAnnotationControls({ currentView }: ViewerAnnotati
</div>
) : (
// When inactive: Show "Draw" tooltip
<Tooltip content={t('rightRail.draw', 'Draw')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.draw', 'Draw')} position="left" offset={12} arrow portalTarget={document.body}>
<ActionIcon
variant="subtle"
radius="md"
@ -136,7 +139,7 @@ export default function ViewerAnnotationControls({ currentView }: ViewerAnnotati
}
}
}}
disabled={currentView !== 'viewer'}
disabled={currentView !== 'viewer' || disableForFullscreen}
aria-label={typeof t === 'function' ? t('rightRail.draw', 'Draw') : 'Draw'}
>
<LocalIcon icon="edit" width="1.5rem" height="1.5rem" />
@ -145,7 +148,7 @@ export default function ViewerAnnotationControls({ currentView }: ViewerAnnotati
)}
{/* Save PDF with Annotations */}
<Tooltip content={t('rightRail.save', 'Save')} position="left" offset={12} arrow>
<Tooltip content={t('rightRail.save', 'Save')} position="left" offset={12} arrow portalTarget={document.body}>
<ActionIcon
variant="subtle"
radius="md"
@ -193,7 +196,7 @@ export default function ViewerAnnotationControls({ currentView }: ViewerAnnotati
}
}
}}
disabled={currentView !== 'viewer'}
disabled={currentView !== 'viewer' || disableForFullscreen}
>
<LocalIcon icon="save" width="1.5rem" height="1.5rem" />
</ActionIcon>

View File

@ -11,7 +11,7 @@ import HotkeyDisplay from '../hotkeys/HotkeyDisplay';
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import StarRoundedIcon from '@mui/icons-material/StarRounded';
import StarBorderRoundedIcon from '@mui/icons-material/StarBorderRounded';
import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded';
import ThumbUpRoundedIcon from '@mui/icons-material/ThumbUpRounded';
import Badge from '../shared/Badge';
import './ToolPanel.css';
@ -34,22 +34,12 @@ const FullscreenToolList = ({
}: FullscreenToolListProps) => {
const { t } = useTranslation();
const { hotkeys } = useHotkeys();
const { toolRegistry, recentTools, favoriteTools, toggleFavorite, isFavorite, fullscreenToolSettings } = useToolWorkflow();
const { toolRegistry, favoriteTools, toggleFavorite, isFavorite } = useToolWorkflow();
const { sections, searchGroups } = useToolSections(filteredTools, searchQuery);
const tooltipPortalTarget = typeof document !== 'undefined' ? document.body : undefined;
// Prepare recent and favorite tool items
const recentToolItems = useMemo(() => {
return recentTools
.map((toolId) => {
const tool = toolRegistry[toolId];
return tool ? { id: toolId, tool } : null;
})
.filter(Boolean)
.slice(0, 6); // Show max 6 recent tools
}, [recentTools, toolRegistry]);
const favoriteToolItems = useMemo(() => {
return favoriteTools
@ -60,8 +50,16 @@ const FullscreenToolList = ({
.filter(Boolean);
}, [favoriteTools, toolRegistry]);
// Show recent/favorites section only when not searching
const showRecentFavorites = searchQuery.trim().length === 0 && (recentToolItems.length > 0 || favoriteToolItems.length > 0);
const quickSection = useMemo(() => sections.find(section => section.key === 'quick'), [sections]);
const recommendedItems = useMemo(() => {
if (!quickSection) return [] as Array<{ id: string, tool: ToolRegistryEntry }>;
const items: Array<{ id: string, tool: ToolRegistryEntry }> = [];
quickSection.subcategories.forEach(sc => sc.tools.forEach(t => items.push(t)));
return items.slice(0, 5);
}, [quickSection]);
// Show recommended/favorites section only when not searching
const showRecentFavorites = searchQuery.trim().length === 0 && ((recommendedItems.length > 0) || favoriteToolItems.length > 0);
const subcategoryGroups = useMemo(() => {
if (searchQuery.trim().length > 0) {
@ -88,16 +86,10 @@ const FullscreenToolList = ({
const getItemClasses = (isDetailed: boolean) => {
const base = isDetailed ? 'tool-panel__fullscreen-item--detailed' : '';
const border = fullscreenToolSettings.toolItemBorder === 'hidden' ? 'tool-panel__fullscreen-item--no-border' : '';
const hover = `tool-panel__fullscreen-item--hover-${fullscreenToolSettings.hoverIntensity}`;
return [base, border, hover].filter(Boolean).join(' ');
return base;
};
const getIconBackground = (categoryColor: string, isDetailed: boolean) => {
if (fullscreenToolSettings.iconBackground === 'none' || fullscreenToolSettings.iconBackground === 'hover') {
return 'transparent';
}
const baseColor = isDetailed ? 'var(--fullscreen-bg-icon-detailed)' : 'var(--fullscreen-bg-icon-compact)';
const blend1 = isDetailed ? '18%' : '15%';
const blend2 = isDetailed ? '8%' : '6%';
@ -109,12 +101,6 @@ const FullscreenToolList = ({
};
const getIconStyle = () => {
if (fullscreenToolSettings.iconColorScheme === 'monochrome') {
return { filter: 'grayscale(1) opacity(0.8)' };
}
if (fullscreenToolSettings.iconColorScheme === 'vibrant') {
return { filter: 'saturate(1.5) brightness(1.1)' };
}
return {};
};
@ -157,16 +143,7 @@ const FullscreenToolList = ({
// Detailed view
if (showDescriptions) {
const iconBg = getIconBackground(categoryColor, true);
const iconClasses = fullscreenToolSettings.iconBackground === 'hover'
? 'tool-panel__fullscreen-icon tool-panel__fullscreen-icon--hover-bg'
: 'tool-panel__fullscreen-icon';
const hoverBgDetailed = fullscreenToolSettings.iconBackground === 'hover'
? `linear-gradient(135deg,
color-mix(in srgb, ${categoryColor} 18%, var(--fullscreen-bg-icon-detailed)),
color-mix(in srgb, ${categoryColor} 8%, var(--fullscreen-bg-icon-detailed))
)`
: undefined;
const iconClasses = 'tool-panel__fullscreen-icon';
return (
<button
@ -176,9 +153,6 @@ const FullscreenToolList = ({
onClick={handleClick}
aria-disabled={isDisabled}
disabled={isDisabled}
style={{
['--fullscreen-icon-hover-bg' as any]: hoverBgDetailed,
}}
>
{tool.icon ? (
<span
@ -235,16 +209,7 @@ const FullscreenToolList = ({
// Compact view
const iconBg = getIconBackground(categoryColor, false);
const iconClasses = fullscreenToolSettings.iconBackground === 'hover'
? 'tool-panel__fullscreen-list-icon tool-panel__fullscreen-list-icon--hover-bg'
: 'tool-panel__fullscreen-list-icon';
const hoverBgCompact = fullscreenToolSettings.iconBackground === 'hover'
? `linear-gradient(135deg,
color-mix(in srgb, ${categoryColor} 15%, var(--fullscreen-bg-icon-compact)),
color-mix(in srgb, ${categoryColor} 6%, var(--fullscreen-bg-icon-compact))
)`
: undefined;
const iconClasses = 'tool-panel__fullscreen-list-icon';
const compactButton = (
<button
@ -254,9 +219,6 @@ const FullscreenToolList = ({
onClick={handleClick}
aria-disabled={isDisabled}
disabled={isDisabled}
style={{
['--fullscreen-icon-hover-bg' as any]: hoverBgCompact,
}}
>
{tool.icon ? (
<span
@ -299,19 +261,23 @@ const FullscreenToolList = ({
</button>
);
const tooltipContent = (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.35rem' }}>
<span>{tool.description}</span>
{binding && (
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.75rem' }}>
<span style={{ color: 'var(--mantine-color-dimmed)', fontWeight: 500 }}>
{t('settings.hotkeys.shortcut', 'Shortcut')}
</span>
<HotkeyDisplay binding={binding} />
</div>
)}
</div>
);
const tooltipContent = isDisabled
? (
<span><strong>{t('toolPanel.fullscreen.comingSoon', 'Coming soon:')}</strong> {tool.description}</span>
)
: (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.35rem' }}>
<span>{tool.description}</span>
{binding && (
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.75rem' }}>
<span style={{ color: 'var(--mantine-color-dimmed)', fontWeight: 500 }}>
{t('settings.hotkeys.shortcut', 'Shortcut')}
</span>
<HotkeyDisplay binding={binding} />
</div>
)}
</div>
);
return (
<Tooltip
@ -332,14 +298,18 @@ const FullscreenToolList = ({
{showRecentFavorites && (
<>
{favoriteToolItems.length > 0 && (
<section className="tool-panel__fullscreen-group tool-panel__fullscreen-group--special">
<section
className="tool-panel__fullscreen-group tool-panel__fullscreen-group--special"
style={{
borderColor: 'var(--fullscreen-border-favorites)',
}}
>
<header className="tool-panel__fullscreen-section-header">
<div className="tool-panel__fullscreen-section-title">
<span
className="tool-panel__fullscreen-section-icon"
style={{
color: fullscreenToolSettings.headerIconColor === 'colored' ? '#FFC107' : 'var(--mantine-color-dimmed)',
...getIconStyle(),
color: 'var(--special-color-favorites)',
}}
aria-hidden
>
@ -351,8 +321,8 @@ const FullscreenToolList = ({
</div>
<Badge
size="sm"
variant={fullscreenToolSettings.headerBadgeColor === 'colored' ? 'colored' : 'default'}
color={fullscreenToolSettings.headerBadgeColor === 'colored' ? '#FFC107' : undefined}
variant="colored"
color="var(--special-color-favorites)"
>
{favoriteToolItems.length}
</Badge>
@ -369,39 +339,43 @@ const FullscreenToolList = ({
</section>
)}
{recentToolItems.length > 0 && (
<section className="tool-panel__fullscreen-group tool-panel__fullscreen-group--special">
{recommendedItems.length > 0 && (
<section
className="tool-panel__fullscreen-group tool-panel__fullscreen-group--special"
style={{
borderColor: 'var(--fullscreen-border-recommended)',
}}
>
<header className="tool-panel__fullscreen-section-header">
<div className="tool-panel__fullscreen-section-title">
<span
className="tool-panel__fullscreen-section-icon"
style={{
color: fullscreenToolSettings.headerIconColor === 'colored' ? '#1BB1D4' : 'var(--mantine-color-dimmed)',
...getIconStyle(),
color: 'var(--special-color-recommended)',
}}
aria-hidden
>
<HistoryRoundedIcon />
<ThumbUpRoundedIcon />
</span>
<Text size="sm" fw={600} tt="uppercase" lts={0.5} c="dimmed">
{t('toolPanel.fullscreen.recent', 'Recently used')}
{t('toolPanel.fullscreen.recommended', 'Recommended')}
</Text>
</div>
<Badge
size="sm"
variant={fullscreenToolSettings.headerBadgeColor === 'colored' ? 'colored' : 'default'}
color={fullscreenToolSettings.headerBadgeColor === 'colored' ? '#1BB1D4' : undefined}
variant="colored"
color="var(--special-color-recommended)"
>
{recentToolItems.length}
{recommendedItems.length}
</Badge>
</header>
{showDescriptions ? (
<div className="tool-panel__fullscreen-grid tool-panel__fullscreen-grid--detailed">
{recentToolItems.map((item: any) => renderToolItem(item.id, item.tool))}
{recommendedItems.map((item: any) => renderToolItem(item.id, item.tool))}
</div>
) : (
<div className="tool-panel__fullscreen-list">
{recentToolItems.map((item: any) => renderToolItem(item.id, item.tool))}
{recommendedItems.map((item: any) => renderToolItem(item.id, item.tool))}
</div>
)}
</section>
@ -425,8 +399,7 @@ const FullscreenToolList = ({
<span
className="tool-panel__fullscreen-section-icon"
style={{
color: fullscreenToolSettings.sectionTitleColor === 'colored' ? categoryColor : 'var(--mantine-color-dimmed)',
...getIconStyle(),
color: categoryColor,
}}
aria-hidden
>
@ -438,17 +411,16 @@ const FullscreenToolList = ({
tt="uppercase"
lts={0.5}
style={{
color: fullscreenToolSettings.sectionTitleColor === 'colored' ? categoryColor : undefined,
color: categoryColor,
}}
c={fullscreenToolSettings.sectionTitleColor === 'neutral' ? 'dimmed' : undefined}
>
{getSubcategoryLabel(t, subcategoryId)}
</Text>
</div>
<Badge
size="sm"
variant={fullscreenToolSettings.sectionTitleColor === 'colored' ? 'colored' : 'default'}
color={fullscreenToolSettings.sectionTitleColor === 'colored' ? categoryColor : undefined}
variant="colored"
color={categoryColor}
>
{tools.length}
</Badge>

View File

@ -1,204 +0,0 @@
import { ActionIcon, Drawer, Radio, SegmentedControl, Stack, Text } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import TuneRoundedIcon from '@mui/icons-material/TuneRounded';
import { useState } from 'react';
export interface FullscreenToolStyleSettings {
iconBackground: 'none' | 'hover' | 'always';
iconColorScheme: 'colored' | 'vibrant' | 'monochrome';
sectionTitleColor: 'colored' | 'neutral';
headerIconColor: 'colored' | 'monochrome';
headerBadgeColor: 'colored' | 'neutral';
toolItemBorder: 'visible' | 'hidden';
hoverIntensity: 'subtle' | 'moderate' | 'prominent';
}
export const defaultFullscreenToolSettings: FullscreenToolStyleSettings = {
iconBackground: 'always',
iconColorScheme: 'colored',
sectionTitleColor: 'colored',
headerIconColor: 'colored',
headerBadgeColor: 'colored',
toolItemBorder: 'visible',
hoverIntensity: 'moderate',
};
interface FullscreenToolSettingsProps {
settings: FullscreenToolStyleSettings;
onChange: (settings: FullscreenToolStyleSettings) => void;
}
const FullscreenToolSettings = ({ settings, onChange }: FullscreenToolSettingsProps) => {
const { t } = useTranslation();
const [opened, setOpened] = useState(false);
const updateSetting = <K extends keyof FullscreenToolStyleSettings>(
key: K,
value: FullscreenToolStyleSettings[K]
) => {
onChange({ ...settings, [key]: value });
};
return (
<>
<ActionIcon
variant="subtle"
radius="xl"
size="md"
onClick={() => setOpened(true)}
aria-label={t('toolPanel.fullscreen.settings.title', 'Customize appearance')}
style={{ color: 'var(--right-rail-icon)' }}
>
<TuneRoundedIcon fontSize="small" />
</ActionIcon>
<Drawer
opened={opened}
onClose={() => setOpened(false)}
title={t('toolPanel.fullscreen.settings.title', 'Customize appearance')}
position="right"
size="md"
styles={{
root: { zIndex: 1300 },
overlay: { zIndex: 1300 },
inner: { zIndex: 1300 },
}}
>
<Stack gap="xl">
<div>
<Text size="sm" fw={600} mb="xs">
{t('toolPanel.fullscreen.settings.iconBackground.label', 'Tool icon background')}
</Text>
<Text size="xs" c="dimmed" mb="sm">
{t('toolPanel.fullscreen.settings.iconBackground.description', 'When to show colored backgrounds behind tool icons')}
</Text>
<SegmentedControl
fullWidth
value={settings.iconBackground}
onChange={(value) => updateSetting('iconBackground', value as any)}
data={[
{ label: t('toolPanel.fullscreen.settings.iconBackground.none', 'None'), value: 'none' },
{ label: t('toolPanel.fullscreen.settings.iconBackground.hover', 'On hover'), value: 'hover' },
{ label: t('toolPanel.fullscreen.settings.iconBackground.always', 'Always'), value: 'always' },
]}
/>
</div>
<div>
<Text size="sm" fw={600} mb="xs">
{t('toolPanel.fullscreen.settings.iconColor.label', 'Tool icon color')}
</Text>
<Text size="xs" c="dimmed" mb="sm">
{t('toolPanel.fullscreen.settings.iconColor.description', 'Color scheme for tool icons')}
</Text>
<SegmentedControl
fullWidth
value={settings.iconColorScheme}
onChange={(value) => updateSetting('iconColorScheme', value as any)}
data={[
{ label: t('toolPanel.fullscreen.settings.iconColor.colored', 'Colored'), value: 'colored' },
{ label: t('toolPanel.fullscreen.settings.iconColor.vibrant', 'Vibrant'), value: 'vibrant' },
{ label: t('toolPanel.fullscreen.settings.iconColor.monochrome', 'Monochrome'), value: 'monochrome' },
]}
/>
</div>
<div>
<Text size="sm" fw={600} mb="xs">
{t('toolPanel.fullscreen.settings.sectionTitle.label', 'Section titles')}
</Text>
<Text size="xs" c="dimmed" mb="sm">
{t('toolPanel.fullscreen.settings.sectionTitle.description', 'Color for category section titles')}
</Text>
<SegmentedControl
fullWidth
value={settings.sectionTitleColor}
onChange={(value) => updateSetting('sectionTitleColor', value as any)}
data={[
{ label: t('toolPanel.fullscreen.settings.sectionTitle.colored', 'Colored'), value: 'colored' },
{ label: t('toolPanel.fullscreen.settings.sectionTitle.neutral', 'Neutral'), value: 'neutral' },
]}
/>
</div>
<div>
<Text size="sm" fw={600} mb="xs">
{t('toolPanel.fullscreen.settings.headerIcon.label', 'Section header icons')}
</Text>
<Text size="xs" c="dimmed" mb="sm">
{t('toolPanel.fullscreen.settings.headerIcon.description', 'Color for Favorites/Recent icons')}
</Text>
<SegmentedControl
fullWidth
value={settings.headerIconColor}
onChange={(value) => updateSetting('headerIconColor', value as any)}
data={[
{ label: t('toolPanel.fullscreen.settings.headerIcon.colored', 'Colored'), value: 'colored' },
{ label: t('toolPanel.fullscreen.settings.headerIcon.monochrome', 'Monochrome'), value: 'monochrome' },
]}
/>
</div>
<div>
<Text size="sm" fw={600} mb="xs">
{t('toolPanel.fullscreen.settings.headerBadge.label', 'Section header badges')}
</Text>
<Text size="xs" c="dimmed" mb="sm">
{t('toolPanel.fullscreen.settings.headerBadge.description', 'Color for count badges in section headers')}
</Text>
<SegmentedControl
fullWidth
value={settings.headerBadgeColor}
onChange={(value) => updateSetting('headerBadgeColor', value as any)}
data={[
{ label: t('toolPanel.fullscreen.settings.headerBadge.colored', 'Colored'), value: 'colored' },
{ label: t('toolPanel.fullscreen.settings.headerBadge.neutral', 'Neutral'), value: 'neutral' },
]}
/>
</div>
<div>
<Text size="sm" fw={600} mb="xs">
{t('toolPanel.fullscreen.settings.border.label', 'Tool item borders')}
</Text>
<Text size="xs" c="dimmed" mb="sm">
{t('toolPanel.fullscreen.settings.border.description', 'Show borders around tool items')}
</Text>
<SegmentedControl
fullWidth
value={settings.toolItemBorder}
onChange={(value) => updateSetting('toolItemBorder', value as any)}
data={[
{ label: t('toolPanel.fullscreen.settings.border.visible', 'Visible'), value: 'visible' },
{ label: t('toolPanel.fullscreen.settings.border.hidden', 'Hidden'), value: 'hidden' },
]}
/>
</div>
<div>
<Text size="sm" fw={600} mb="xs">
{t('toolPanel.fullscreen.settings.hover.label', 'Hover effect intensity')}
</Text>
<Text size="xs" c="dimmed" mb="sm">
{t('toolPanel.fullscreen.settings.hover.description', 'How prominent the hover effect should be')}
</Text>
<Radio.Group
value={settings.hoverIntensity}
onChange={(value) => updateSetting('hoverIntensity', value as any)}
>
<Stack gap="xs">
<Radio value="subtle" label={t('toolPanel.fullscreen.settings.hover.subtle', 'Subtle')} />
<Radio value="moderate" label={t('toolPanel.fullscreen.settings.hover.moderate', 'Moderate')} />
<Radio value="prominent" label={t('toolPanel.fullscreen.settings.hover.prominent', 'Prominent')} />
</Stack>
</Radio.Group>
</div>
</Stack>
</Drawer>
</>
);
};
export default FullscreenToolSettings;

View File

@ -4,11 +4,9 @@ import ViewSidebarRoundedIcon from '@mui/icons-material/ViewSidebarRounded';
import { useTranslation } from 'react-i18next';
import ToolSearch from './toolPicker/ToolSearch';
import FullscreenToolList from './FullscreenToolList';
import FullscreenToolSettings from './FullscreenToolSettings';
import { ToolRegistryEntry } from '../../data/toolsTaxonomy';
import { ToolId } from '../../types/toolId';
import { useFocusTrap } from '../../hooks/tools/useFocusTrap';
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import { BASE_PATH } from '../../constants/app';
import './ToolPanel.css';
@ -48,7 +46,6 @@ const FullscreenToolSurface = ({
}: FullscreenToolSurfaceProps) => {
const { t } = useTranslation();
const { colorScheme } = useMantineColorScheme();
const { fullscreenToolSettings, setFullscreenToolSettings } = useToolWorkflow();
const [isExiting, setIsExiting] = useState(false);
const surfaceRef = useRef<HTMLDivElement>(null);
@ -104,10 +101,6 @@ const FullscreenToolSurface = ({
<img src={brandTextSrc} alt={brandAltText} className="tool-panel__fullscreen-brand-text" />
</div>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<FullscreenToolSettings
settings={fullscreenToolSettings}
onChange={setFullscreenToolSettings}
/>
<Tooltip label={toggleLabel} position="bottom" withArrow>
<ActionIcon
variant="subtle"

View File

@ -15,6 +15,8 @@
--fullscreen-border-subtle-75: color-mix(in srgb, var(--border-subtle) 75%, transparent);
--fullscreen-border-subtle-70: color-mix(in srgb, var(--border-subtle) 70%, transparent);
--fullscreen-border-subtle-65: color-mix(in srgb, var(--border-subtle) 65%, transparent);
--fullscreen-border-favorites: color-mix(in srgb, var(--special-color-favorites) 25%, var(--border-subtle));
--fullscreen-border-recommended: color-mix(in srgb, var(--special-color-recommended) 25%, var(--border-subtle));
--fullscreen-shadow-primary: color-mix(in srgb, var(--shadow-color, rgba(15, 23, 42, 0.55)) 25%, transparent);
--fullscreen-shadow-secondary: color-mix(in srgb, var(--shadow-color, rgba(15, 23, 42, 0.35)) 30%, transparent);
--fullscreen-shadow-group: color-mix(in srgb, var(--shadow-color, rgba(15, 23, 42, 0.45)) 18%, transparent);
@ -25,10 +27,6 @@
--fullscreen-accent-list-border: color-mix(in srgb, var(--text-primary) 20%, var(--border-subtle));
--fullscreen-text-icon: color-mix(in srgb, var(--text-primary) 90%, var(--text-muted));
--fullscreen-text-icon-compact: color-mix(in srgb, var(--text-primary) 88%, var(--text-muted));
--fullscreen-hover-subtle: color-mix(in srgb, var(--text-primary) 5%, var(--bg-toolbar));
--fullscreen-hover: color-mix(in srgb, var(--text-primary) 8%, var(--bg-toolbar));
--fullscreen-hover-prominent: color-mix(in srgb, var(--text-primary) 12%, var(--bg-toolbar));
--fullscreen-icon-hover-bg: color-mix(in srgb, var(--text-primary) 10%, var(--bg-muted));
}
.tool-panel {
@ -234,6 +232,8 @@
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
width: 100%;
box-sizing: border-box;
/* Allow flex children to shrink without forcing horizontal overflow */
min-width: 0;
}
.tool-panel__fullscreen-item:focus-visible {
@ -297,12 +297,7 @@
}
.tool-panel__fullscreen-group--special {
background: linear-gradient(
135deg,
color-mix(in srgb, var(--fullscreen-bg-group) 95%, var(--text-primary) 5%),
var(--fullscreen-bg-group)
);
border-width: 1.5px;
border-width: 1px;
}
.tool-panel__fullscreen-icon {
@ -353,6 +348,9 @@
flex-direction: column;
gap: 0.4rem;
text-align: left;
/* Prevent long content (names, descriptions, shortcuts) from overflowing */
flex: 1 1 auto;
min-width: 0;
}
.tool-panel__fullscreen-name {
@ -363,6 +361,15 @@
line-height: 1.45;
}
/* Ensure long words or unbroken strings wrap instead of overflowing */
.tool-panel__fullscreen-name,
.tool-panel__fullscreen-description,
.tool-panel__fullscreen-match {
overflow-wrap: anywhere;
word-break: break-word;
hyphens: auto;
}
.tool-panel__fullscreen-match {
font-style: italic;
}
@ -372,6 +379,9 @@
align-items: center;
gap: 0.5rem;
margin-top: 0.2rem;
/* Allow hotkey chips to wrap on small widths to avoid overflow */
flex-wrap: wrap;
row-gap: 0.25rem;
}
.tool-panel__fullscreen-list {
@ -531,42 +541,5 @@
}
}
/* Dynamic settings support */
/* No border variant */
.tool-panel__fullscreen-item--no-border,
.tool-panel__fullscreen-list-item--no-border {
border: none !important;
}
/* Hover intensity variants */
.tool-panel__fullscreen-item--hover-subtle:hover,
.tool-panel__fullscreen-list-item--hover-subtle:hover {
background: var(--fullscreen-hover-subtle);
transform: none;
}
.tool-panel__fullscreen-item--hover-moderate:hover,
.tool-panel__fullscreen-list-item--hover-moderate:hover {
background: var(--fullscreen-hover);
transform: translateY(-2px);
}
.tool-panel__fullscreen-item--hover-prominent:hover,
.tool-panel__fullscreen-list-item--hover-prominent:hover {
background: var(--fullscreen-hover-prominent);
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* Hover-only icon background */
.tool-panel__fullscreen-icon--hover-bg,
.tool-panel__fullscreen-list-icon--hover-bg {
transition: background 0.2s ease;
}
.tool-panel__fullscreen-item:hover .tool-panel__fullscreen-icon--hover-bg,
.tool-panel__fullscreen-list-item:hover .tool-panel__fullscreen-list-icon--hover-bg {
background: var(--fullscreen-icon-hover-bg) !important;
}

View File

@ -66,7 +66,7 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
}, []);
const { sections: visibleSections } = useToolSections(filteredTools);
const { favoriteTools, recentTools, toolRegistry } = useToolWorkflow();
const { favoriteTools, toolRegistry } = useToolWorkflow();
const favoriteToolItems = useMemo(() => {
return favoriteTools
@ -79,24 +79,19 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
.filter((item: any) => item && (item.tool.component || item.tool.link || item.id === 'read' || item.id === 'multiTool')) as Array<{ id: string; tool: ToolRegistryEntry }>;
}, [favoriteTools, toolRegistry]);
const recentToolItems = useMemo(() => {
return recentTools
.map((toolId) => {
const tool = (toolRegistry as any)[toolId as ToolId] as ToolRegistryEntry | undefined;
return tool ? { id: toolId as string, tool } : null;
})
.filter(Boolean)
.slice(0, 5) as Array<{ id: string; tool: ToolRegistryEntry }>; // cap to 5
}, [recentTools, toolRegistry]);
const recommendedCount = useMemo(() => {
return favoriteToolItems.length + recentToolItems.length;
}, [favoriteToolItems.length, recentToolItems.length]);
const quickSection = useMemo(
() => visibleSections.find(s => s.key === 'quick'),
[visibleSections]
);
const recommendedItems = useMemo(() => {
if (!quickSection) return [] as Array<{ id: string; tool: ToolRegistryEntry }>;
const items: Array<{ id: string; tool: ToolRegistryEntry }> = [];
quickSection.subcategories.forEach((sc: any) => sc.tools.forEach((toolEntry: any) => items.push(toolEntry)));
return items.slice(0, 5);
}, [quickSection]);
const recommendedCount = useMemo(() => favoriteToolItems.length + recommendedItems.length, [favoriteToolItems.length, recommendedItems.length]);
const allSection = useMemo(
() => visibleSections.find(s => s.key === 'all'),
[visibleSections]
@ -172,7 +167,7 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
}}
onClick={() => scrollTo(quickAccessRef)}
>
<span style={{ fontSize: "1rem" }}>{t("toolPicker.recommended", "RECOMMENDED")}</span>
<span style={{ fontSize: "1rem" }}>{t("toolPicker.quickAccess", "QUICK ACCESS")}</span>
<Badge>
{recommendedCount}
</Badge>
@ -196,13 +191,13 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
</div>
</Box>
)}
{recentToolItems.length > 0 && (
{recommendedItems.length > 0 && (
<Box w="100%">
<SubcategoryHeader label={t('toolPanel.fullscreen.recent', 'Recently used')} />
<SubcategoryHeader label={t('toolPanel.fullscreen.recommended', 'Recommended')} />
<div>
{recentToolItems.map(({ id, tool }) => (
{recommendedItems.map(({ id, tool }) => (
<ToolButton
key={`recent-${id}`}
key={`rec-${id}`}
id={id}
tool={tool}
isSelected={selectedToolKey === id}
@ -212,7 +207,6 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
</div>
</Box>
)}
{/* Temporarily hide the rest of Recommended tools; show only Favourites and Recently used */}
</Stack>
</Box>
</>
@ -247,7 +241,7 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
<Box ref={allToolsRef} w="100%">
<Stack p="sm" gap="xs">
{allSection?.subcategories.map(sc =>
{allSection?.subcategories.map((sc: any) =>
renderToolButtons(t, sc, selectedToolKey, onSelect, true, false)
)}
</Stack>

View File

@ -9,20 +9,16 @@ import { PageEditorFunctions } from '../types/pageEditor';
import { ToolRegistryEntry, ToolRegistry } from '../data/toolsTaxonomy';
import { useNavigationActions, useNavigationState } from './NavigationContext';
import { ToolId, isValidToolId } from '../types/toolId';
import { useNavigationUrlSync } from '../hooks/useUrlSync';
import { getDefaultWorkbench } from '../types/workbench';
import { filterToolRegistryByQuery } from '../utils/toolSearch';
import { useToolHistory } from '../hooks/tools/useToolHistory';
import { FullscreenToolStyleSettings } from '../components/tools/FullscreenToolSettings';
import {
ToolWorkflowState,
TOOL_PANEL_MODE_STORAGE_KEY,
FULLSCREEN_TOOL_SETTINGS_STORAGE_KEY,
createInitialState,
toolWorkflowReducer,
ToolPanelMode,
} from './toolWorkflow/state';
import { usePreferences } from '../contexts/PreferencesContext';
// State interface
// Types and reducer/state moved to './toolWorkflow/state'
@ -40,7 +36,6 @@ interface ToolWorkflowContextValue extends ToolWorkflowState {
setLeftPanelView: (view: 'toolPicker' | 'toolContent' | 'hidden') => void;
setReaderMode: (mode: boolean) => void;
setToolPanelMode: (mode: ToolPanelMode) => void;
setFullscreenToolSettings: (settings: FullscreenToolStyleSettings) => void;
setPreviewFile: (file: File | null) => void;
setPageEditorFunctions: (functions: PageEditorFunctions | null) => void;
setSearchQuery: (query: string) => void;
@ -80,7 +75,6 @@ interface ToolWorkflowProviderProps {
export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
const [state, dispatch] = useReducer(toolWorkflowReducer, undefined, createInitialState);
const { preferences } = usePreferences();
// Store reset functions for tools
const [toolResetFunctions, setToolResetFunctions] = React.useState<Record<string, () => void>>({});
@ -129,10 +123,6 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
}, []);
const setFullscreenToolSettings = useCallback((settings: FullscreenToolStyleSettings) => {
dispatch({ type: 'SET_FULLSCREEN_TOOL_SETTINGS', payload: settings });
}, []);
const setPreviewFile = useCallback((file: File | null) => {
dispatch({ type: 'SET_PREVIEW_FILE', payload: file });
if (file) {
@ -148,7 +138,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
dispatch({ type: 'SET_SEARCH_QUERY', payload: query });
}, []);
React.useEffect(() => {
useEffect(() => {
if (typeof window === 'undefined') {
return;
}
@ -156,24 +146,6 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
window.localStorage.setItem(TOOL_PANEL_MODE_STORAGE_KEY, state.toolPanelMode);
}, [state.toolPanelMode]);
// Initialize tool panel mode from user preferences if no explicit localStorage preference exists yet
useEffect(() => {
if (typeof window === 'undefined') return;
const stored = window.localStorage.getItem(TOOL_PANEL_MODE_STORAGE_KEY);
if (stored === null && preferences?.defaultToolPanelMode && state.toolPanelMode !== preferences.defaultToolPanelMode) {
dispatch({ type: 'SET_TOOL_PANEL_MODE', payload: preferences.defaultToolPanelMode });
}
}, [preferences?.defaultToolPanelMode]);
React.useEffect(() => {
if (typeof window === 'undefined') {
return;
}
const serialized = JSON.stringify(state.fullscreenToolSettings);
window.localStorage.setItem(FULLSCREEN_TOOL_SETTINGS_STORAGE_KEY, serialized);
}, [state.fullscreenToolSettings]);
// Tool reset methods
const registerToolReset = useCallback((toolId: string, resetFunction: () => void) => {
setToolResetFunctions(prev => ({ ...prev, [toolId]: resetFunction }));
@ -253,15 +225,6 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
[state.sidebarsVisible, state.readerMode, state.leftPanelView]
);
// URL sync for proper tool navigation
useNavigationUrlSync(
navigationState.selectedTool,
handleToolSelect,
handleBackToTools,
toolRegistry as ToolRegistry,
true
);
// Properly memoized context value
const contextValue = useMemo((): ToolWorkflowContextValue => ({
// State
@ -276,7 +239,6 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
setLeftPanelView,
setReaderMode,
setToolPanelMode,
setFullscreenToolSettings,
setPreviewFile,
setPageEditorFunctions,
setSearchQuery,
@ -313,7 +275,6 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
setLeftPanelView,
setReaderMode,
setToolPanelMode,
setFullscreenToolSettings,
setPreviewFile,
setPageEditorFunctions,
setSearchQuery,
@ -343,7 +304,6 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
export function useToolWorkflow(): ToolWorkflowContextValue {
const context = useContext(ToolWorkflowContext);
if (!context) {
console.error('ToolWorkflowContext not found. Current stack:', new Error().stack);
throw new Error('useToolWorkflow must be used within a ToolWorkflowProvider');
}

View File

@ -1,4 +1,3 @@
import { FullscreenToolStyleSettings, defaultFullscreenToolSettings } from '../../components/tools/FullscreenToolSettings';
import { PageEditorFunctions } from '../../types/pageEditor';
// State & Modes
@ -10,9 +9,7 @@ export interface ToolWorkflowState {
leftPanelView: 'toolPicker' | 'toolContent' | 'hidden';
readerMode: boolean;
toolPanelMode: ToolPanelMode;
fullscreenToolSettings: FullscreenToolStyleSettings;
// File/Preview State
previewFile: File | null;
pageEditorFunctions: PageEditorFunctions | null;
@ -26,7 +23,6 @@ export type ToolWorkflowAction =
| { type: 'SET_LEFT_PANEL_VIEW'; payload: 'toolPicker' | 'toolContent' | 'hidden' }
| { type: 'SET_READER_MODE'; payload: boolean }
| { type: 'SET_TOOL_PANEL_MODE'; payload: ToolPanelMode }
| { type: 'SET_FULLSCREEN_TOOL_SETTINGS'; payload: FullscreenToolStyleSettings }
| { type: 'SET_PREVIEW_FILE'; payload: File | null }
| { type: 'SET_PAGE_EDITOR_FUNCTIONS'; payload: PageEditorFunctions | null }
| { type: 'SET_SEARCH_QUERY'; payload: string }
@ -34,7 +30,6 @@ export type ToolWorkflowAction =
// Storage keys
export const TOOL_PANEL_MODE_STORAGE_KEY = 'toolPanelModePreference';
export const FULLSCREEN_TOOL_SETTINGS_STORAGE_KEY = 'fullscreenToolStyleSettings';
export const getStoredToolPanelMode = (): ToolPanelMode => {
if (typeof window === 'undefined') {
@ -49,24 +44,7 @@ export const getStoredToolPanelMode = (): ToolPanelMode => {
return 'sidebar';
};
export const getStoredFullscreenToolSettings = (): FullscreenToolStyleSettings => {
if (typeof window === 'undefined') {
return defaultFullscreenToolSettings;
}
try {
const storedNew = window.localStorage.getItem(FULLSCREEN_TOOL_SETTINGS_STORAGE_KEY);
if (storedNew) {
return { ...defaultFullscreenToolSettings, ...JSON.parse(storedNew) };
}
} catch (e) {
console.error('Failed to parse fullscreen tool settings:', e);
}
return defaultFullscreenToolSettings;
};
export const baseState: Omit<ToolWorkflowState, 'toolPanelMode' | 'fullscreenToolSettings'> = {
export const baseState: Omit<ToolWorkflowState, 'toolPanelMode'> = {
sidebarsVisible: true,
leftPanelView: 'toolPicker',
readerMode: false,
@ -78,7 +56,6 @@ export const baseState: Omit<ToolWorkflowState, 'toolPanelMode' | 'fullscreenToo
export const createInitialState = (): ToolWorkflowState => ({
...baseState,
toolPanelMode: getStoredToolPanelMode(),
fullscreenToolSettings: getStoredFullscreenToolSettings(),
});
export function toolWorkflowReducer(state: ToolWorkflowState, action: ToolWorkflowAction): ToolWorkflowState {
@ -91,8 +68,6 @@ export function toolWorkflowReducer(state: ToolWorkflowState, action: ToolWorkfl
return { ...state, readerMode: action.payload };
case 'SET_TOOL_PANEL_MODE':
return { ...state, toolPanelMode: action.payload };
case 'SET_FULLSCREEN_TOOL_SETTINGS':
return { ...state, fullscreenToolSettings: action.payload };
case 'SET_PREVIEW_FILE':
return { ...state, previewFile: action.payload };
case 'SET_PAGE_EDITOR_FUNCTIONS':
@ -103,7 +78,6 @@ export function toolWorkflowReducer(state: ToolWorkflowState, action: ToolWorkfl
return {
...baseState,
toolPanelMode: state.toolPanelMode,
fullscreenToolSettings: state.fullscreenToolSettings,
searchQuery: state.searchQuery,
};
default:

View File

@ -60,6 +60,10 @@
--category-color-automation: #ec4899; /* Pink for automation tools */
--category-color-developer: #6b7280; /* Gray for developer tools */
--category-color-default: #6b7280; /* Default gray */
/* Special section colors - consistent across light and dark modes */
--special-color-favorites: #FFC107; /* Yellow/gold for favorites */
--special-color-recommended: #1BB1D4; /* Cyan for recommended */
--color-yellow-500: #eab308;
--color-yellow-600: #ca8a04;
--color-yellow-700: #a16207;
@ -301,6 +305,10 @@
--category-color-developer: #6b7280; /* Gray for developer tools */
--category-color-default: #6b7280; /* Default gray */
/* Special section colors - same as light mode for consistency */
--special-color-favorites: #FFC107; /* Yellow/gold for favorites */
--special-color-recommended: #1BB1D4; /* Cyan for recommended */
/* Success (green) - dark */
--color-green-50: #052e16;
--color-green-100: #064e3b;