mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-03-04 02:20:19 +01:00
rename legacy -> full screen, add a badge component to be used in both tool pickers, disable right rail buttons when full screen tool picker is active to minamize confusion, change initial startup prompt wording, create themes for colors
This commit is contained in:
@@ -1,4 +1,21 @@
|
||||
{
|
||||
"toolPanel": {
|
||||
"modePrompt": {
|
||||
"title": "Choose how you browse tools",
|
||||
"description": "Preview both layouts and decide how you want to explore Stirling PDF tools.",
|
||||
"sidebarTitle": "Sidebar mode",
|
||||
"sidebarDescription": "Keep tools alongside your workspace for quick switching.",
|
||||
"recommended": "Recommended",
|
||||
"chooseSidebar": "Use sidebar mode",
|
||||
"fullscreenTitle": "Fullscreen mode - (legacy)",
|
||||
"fullscreenDescription": "Browse every tool in a catalogue that covers the workspace until you pick one.",
|
||||
"chooseFullscreen": "Use fullscreen mode",
|
||||
"dismiss": "Maybe later"
|
||||
},
|
||||
"fullscreen": {
|
||||
"showDetails": "Show Details"
|
||||
}
|
||||
},
|
||||
"unsavedChanges": "You have unsaved changes to your PDF.",
|
||||
"areYouSure": "Are you sure you want to leave?",
|
||||
"unsavedChangesTitle": "Unsaved Changes",
|
||||
|
||||
@@ -2,6 +2,23 @@
|
||||
"language": {
|
||||
"direction": "ltr"
|
||||
},
|
||||
"toolPanel": {
|
||||
"modePrompt": {
|
||||
"title": "Choose how you browse tools",
|
||||
"description": "Preview both layouts and decide how you want to explore Stirling PDF tools.",
|
||||
"sidebarTitle": "Sidebar mode",
|
||||
"sidebarDescription": "Keep tools alongside your workspace for quick switching.",
|
||||
"recommended": "Recommended",
|
||||
"chooseSidebar": "Use sidebar mode",
|
||||
"fullscreenTitle": "Fullscreen mode - (legacy)",
|
||||
"fullscreenDescription": "Browse every tool in a catalogue that covers the workspace until you pick one.",
|
||||
"chooseFullscreen": "Use fullscreen mode",
|
||||
"dismiss": "Maybe later"
|
||||
},
|
||||
"fullscreen": {
|
||||
"showDetails": "Show Details"
|
||||
}
|
||||
},
|
||||
"addPageNumbers": {
|
||||
"fontSize": "Font Size",
|
||||
"fontName": "Font Name",
|
||||
|
||||
101
frontend/src/components/shared/Badge.tsx
Normal file
101
frontend/src/components/shared/Badge.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@mantine/core';
|
||||
|
||||
interface BadgeProps {
|
||||
children: React.ReactNode;
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
variant?: 'default' | 'colored';
|
||||
color?: string;
|
||||
textColor?: string;
|
||||
backgroundColor?: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const Badge: React.FC<BadgeProps> = ({
|
||||
children,
|
||||
size = 'sm',
|
||||
variant = 'default',
|
||||
color,
|
||||
textColor,
|
||||
backgroundColor,
|
||||
className,
|
||||
style
|
||||
}) => {
|
||||
const getSizeStyles = () => {
|
||||
switch (size) {
|
||||
case 'sm':
|
||||
return {
|
||||
padding: '0.125rem 0.5rem',
|
||||
fontSize: '0.75rem',
|
||||
fontWeight: 700,
|
||||
borderRadius: '0.5rem',
|
||||
};
|
||||
case 'md':
|
||||
return {
|
||||
padding: '0.25rem 0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 700,
|
||||
borderRadius: '0.625rem',
|
||||
};
|
||||
case 'lg':
|
||||
return {
|
||||
padding: '0.375rem 1rem',
|
||||
fontSize: '1rem',
|
||||
fontWeight: 700,
|
||||
borderRadius: '0.75rem',
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const getVariantStyles = () => {
|
||||
// If explicit colors are provided, use them
|
||||
if (textColor && backgroundColor) {
|
||||
return {
|
||||
backgroundColor,
|
||||
color: textColor,
|
||||
};
|
||||
}
|
||||
|
||||
// If a single color is provided, use it for text and 20% opacity for background
|
||||
if (color) {
|
||||
return {
|
||||
backgroundColor: `color-mix(in srgb, ${color} 20%, transparent)`,
|
||||
color: color,
|
||||
};
|
||||
}
|
||||
|
||||
// If variant is colored but no color provided, use default colored styling
|
||||
if (variant === 'colored') {
|
||||
return {
|
||||
backgroundColor: `color-mix(in srgb, var(--category-color-default) 15%, transparent)`,
|
||||
color: 'var(--category-color-default)',
|
||||
borderColor: `color-mix(in srgb, var(--category-color-default) 30%, transparent)`,
|
||||
border: '1px solid',
|
||||
};
|
||||
}
|
||||
|
||||
// Default styling
|
||||
return {
|
||||
background: 'var(--tool-header-badge-bg)',
|
||||
color: 'var(--tool-header-badge-text)',
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
className={className}
|
||||
style={{
|
||||
...getSizeStyles(),
|
||||
...getVariantStyles(),
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Badge;
|
||||
@@ -209,6 +209,7 @@ const LanguageSelector: React.FC<LanguageSelectorProps> = ({ position = 'bottom-
|
||||
width={600}
|
||||
position={position}
|
||||
offset={offset}
|
||||
zIndex={1400}
|
||||
transitionProps={{
|
||||
transition: 'scale-y',
|
||||
duration: 200,
|
||||
@@ -264,6 +265,7 @@ const LanguageSelector: React.FC<LanguageSelectorProps> = ({ position = 'bottom-
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
|
||||
backgroundColor: 'light-dark(var(--mantine-color-white), var(--mantine-color-dark-6))',
|
||||
border: 'light-dark(1px solid var(--mantine-color-gray-3), 1px solid var(--mantine-color-dark-4))',
|
||||
zIndex: 1400,
|
||||
}}
|
||||
>
|
||||
<ScrollArea h={190} type="scroll">
|
||||
|
||||
@@ -98,24 +98,21 @@ const QuickAccessBar = forwardRef<HTMLDivElement>((_, ref) => {
|
||||
type: 'navigation',
|
||||
onClick: () => {
|
||||
setActiveButton('read');
|
||||
handleBackToTools();
|
||||
handleReaderToggle();
|
||||
setToolPanelMode('sidebar');
|
||||
}
|
||||
},
|
||||
// TODO: Add sign
|
||||
//{
|
||||
// id: 'sign',
|
||||
// name: t("quickAccess.sign", "Sign"),
|
||||
// icon: <LocalIcon icon="signature-rounded" width="1.25rem" height="1.25rem" />,
|
||||
// size: 'lg',
|
||||
// isRound: false,
|
||||
// type: 'navigation',
|
||||
// onClick: () => {
|
||||
// setActiveButton('sign');
|
||||
// handleToolSelect('sign');
|
||||
// }
|
||||
//},
|
||||
{
|
||||
id: 'sign',
|
||||
name: t("quickAccess.sign", "Sign"),
|
||||
icon: <LocalIcon icon="signature-rounded" width="1.25rem" height="1.25rem" />,
|
||||
size: 'lg',
|
||||
isRound: false,
|
||||
type: 'navigation',
|
||||
onClick: () => {
|
||||
setActiveButton('sign');
|
||||
handleToolSelect('sign');
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'automate',
|
||||
name: t("quickAccess.automate", "Automate"),
|
||||
|
||||
@@ -27,7 +27,8 @@ export default function RightRail() {
|
||||
// Viewer context for PDF controls - safely handle when not available
|
||||
const viewerContext = React.useContext(ViewerContext);
|
||||
const { toggleTheme } = useRainbowThemeContext();
|
||||
const { buttons, actions } = useRightRail();
|
||||
const { buttons, actions, allButtonsDisabled } = useRightRail();
|
||||
|
||||
const topButtons = useMemo(() => buttons.filter(b => (b.section || 'top') === 'top' && (b.visible ?? true)), [buttons]);
|
||||
|
||||
// Access PageEditor functions for page-editor-specific actions
|
||||
@@ -176,7 +177,7 @@ export default function RightRail() {
|
||||
}, [currentView]);
|
||||
|
||||
return (
|
||||
<div className="right-rail" data-sidebar="right-rail">
|
||||
<div className={`right-rail`} data-sidebar="right-rail">
|
||||
<div className="right-rail-inner">
|
||||
{topButtons.length > 0 && (
|
||||
<>
|
||||
@@ -188,7 +189,7 @@ export default function RightRail() {
|
||||
radius="md"
|
||||
className="right-rail-icon"
|
||||
onClick={() => actions[btn.id]?.()}
|
||||
disabled={btn.disabled}
|
||||
disabled={btn.disabled || allButtonsDisabled}
|
||||
>
|
||||
{btn.icon}
|
||||
</ActionIcon>
|
||||
@@ -214,7 +215,7 @@ export default function RightRail() {
|
||||
variant="subtle"
|
||||
radius="md"
|
||||
className="right-rail-icon"
|
||||
disabled={currentView !== 'viewer'}
|
||||
disabled={currentView !== 'viewer' || allButtonsDisabled}
|
||||
aria-label={typeof t === 'function' ? t('rightRail.search', 'Search PDF') : 'Search PDF'}
|
||||
>
|
||||
<LocalIcon icon="search" width="1.5rem" height="1.5rem" />
|
||||
@@ -244,7 +245,7 @@ export default function RightRail() {
|
||||
viewerContext?.panActions.togglePan();
|
||||
setIsPanning(!isPanning);
|
||||
}}
|
||||
disabled={currentView !== 'viewer'}
|
||||
disabled={currentView !== 'viewer' || allButtonsDisabled}
|
||||
>
|
||||
<LocalIcon icon="pan-tool-rounded" width="1.5rem" height="1.5rem" />
|
||||
</ActionIcon>
|
||||
@@ -259,7 +260,7 @@ export default function RightRail() {
|
||||
onClick={() => {
|
||||
viewerContext?.rotationActions.rotateBackward();
|
||||
}}
|
||||
disabled={currentView !== 'viewer'}
|
||||
disabled={currentView !== 'viewer' || allButtonsDisabled}
|
||||
>
|
||||
<LocalIcon icon="rotate-left" width="1.5rem" height="1.5rem" />
|
||||
</ActionIcon>
|
||||
@@ -274,7 +275,7 @@ export default function RightRail() {
|
||||
onClick={() => {
|
||||
viewerContext?.rotationActions.rotateForward();
|
||||
}}
|
||||
disabled={currentView !== 'viewer'}
|
||||
disabled={currentView !== 'viewer' || allButtonsDisabled}
|
||||
>
|
||||
<LocalIcon icon="rotate-right" width="1.5rem" height="1.5rem" />
|
||||
</ActionIcon>
|
||||
@@ -289,7 +290,7 @@ export default function RightRail() {
|
||||
onClick={() => {
|
||||
viewerContext?.toggleThumbnailSidebar();
|
||||
}}
|
||||
disabled={currentView !== 'viewer'}
|
||||
disabled={currentView !== 'viewer' || allButtonsDisabled}
|
||||
>
|
||||
<LocalIcon icon="view-list" width="1.5rem" height="1.5rem" />
|
||||
</ActionIcon>
|
||||
@@ -315,7 +316,7 @@ export default function RightRail() {
|
||||
radius="md"
|
||||
className="right-rail-icon"
|
||||
onClick={handleSelectAll}
|
||||
disabled={currentView === 'viewer' || totalItems === 0 || selectedCount === totalItems}
|
||||
disabled={currentView === 'viewer' || totalItems === 0 || selectedCount === totalItems || allButtonsDisabled}
|
||||
>
|
||||
<LocalIcon icon="select-all" width="1.5rem" height="1.5rem" />
|
||||
</ActionIcon>
|
||||
@@ -330,7 +331,7 @@ export default function RightRail() {
|
||||
radius="md"
|
||||
className="right-rail-icon"
|
||||
onClick={handleDeselectAll}
|
||||
disabled={currentView === 'viewer' || selectedCount === 0}
|
||||
disabled={currentView === 'viewer' || selectedCount === 0 || allButtonsDisabled}
|
||||
>
|
||||
<LocalIcon icon="crop-square-outline" width="1.5rem" height="1.5rem" />
|
||||
</ActionIcon>
|
||||
@@ -349,7 +350,7 @@ export default function RightRail() {
|
||||
variant="subtle"
|
||||
radius="md"
|
||||
className="right-rail-icon"
|
||||
disabled={!pageControlsVisible || totalItems === 0}
|
||||
disabled={!pageControlsVisible || totalItems === 0 || allButtonsDisabled}
|
||||
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" />
|
||||
@@ -385,7 +386,7 @@ export default function RightRail() {
|
||||
radius="md"
|
||||
className="right-rail-icon"
|
||||
onClick={() => { pageEditorFunctions?.handleDelete?.(); }}
|
||||
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0}
|
||||
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0 || allButtonsDisabled}
|
||||
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" />
|
||||
@@ -406,7 +407,7 @@ export default function RightRail() {
|
||||
radius="md"
|
||||
className="right-rail-icon"
|
||||
onClick={() => { pageEditorFunctions?.onExportSelected?.(); }}
|
||||
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0 || pageEditorFunctions?.exportLoading}
|
||||
disabled={!pageControlsVisible || (pageEditorFunctions?.selectedPageIds?.length || 0) === 0 || pageEditorFunctions?.exportLoading || allButtonsDisabled}
|
||||
aria-label={typeof t === 'function' ? t('rightRail.exportSelected', 'Export Selected Pages') : 'Export Selected Pages'}
|
||||
>
|
||||
<LocalIcon icon="download" width="1.5rem" height="1.5rem" />
|
||||
@@ -427,7 +428,8 @@ export default function RightRail() {
|
||||
disabled={
|
||||
currentView === 'viewer' ||
|
||||
(currentView === 'fileEditor' && selectedCount === 0) ||
|
||||
(currentView === 'pageEditor' && (activeFiles.length === 0 || !pageEditorFunctions?.closePdf))
|
||||
(currentView === 'pageEditor' && (activeFiles.length === 0 || !pageEditorFunctions?.closePdf)) ||
|
||||
allButtonsDisabled
|
||||
}
|
||||
>
|
||||
<LocalIcon icon="close-rounded" width="1.5rem" height="1.5rem" />
|
||||
@@ -441,7 +443,8 @@ export default function RightRail() {
|
||||
|
||||
{/* Theme toggle and Language dropdown */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1rem' }}>
|
||||
<Tooltip content={t('rightRail.toggleTheme', 'Toggle Theme')} position="left" offset={12} arrow>
|
||||
<Tooltip content={t('rightRail.toggleTheme', 'Toggle Theme')} position="left" offset={12} arrow portalTarget={document.body}
|
||||
>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
radius="md"
|
||||
@@ -452,7 +455,11 @@ export default function RightRail() {
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<LanguageSelector position="left-start" offset={6} compact />
|
||||
<Tooltip content={t('rightRail.language', 'Language')} position="left" offset={12} arrow portalTarget={document.body}>
|
||||
<div style={{ display: 'inline-flex' }}>
|
||||
<LanguageSelector position="left-start" offset={6} compact />
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content={
|
||||
currentView === 'pageEditor'
|
||||
@@ -466,7 +473,7 @@ export default function RightRail() {
|
||||
className="right-rail-icon"
|
||||
onClick={handleExportAll}
|
||||
disabled={
|
||||
currentView === 'viewer' ? !exportState?.canExport : totalItems === 0
|
||||
currentView === 'viewer' ? !exportState?.canExport : totalItems === 0 || allButtonsDisabled
|
||||
}
|
||||
>
|
||||
<LocalIcon icon="download" width="1.5rem" height="1.5rem" />
|
||||
|
||||
@@ -46,6 +46,13 @@
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* When all buttons are disabled via context */
|
||||
.right-rail--all-disabled .right-rail-icon {
|
||||
color: var(--right-rail-icon-disabled) !important;
|
||||
background-color: transparent !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.right-rail-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { ActionIcon, Badge, Text } from '@mantine/core';
|
||||
import { ActionIcon, Text } from '@mantine/core';
|
||||
import { Tooltip } from '../shared/Tooltip';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ToolRegistryEntry, getSubcategoryLabel, getSubcategoryColor, getSubcategoryIcon } from '../../data/toolsTaxonomy';
|
||||
@@ -12,9 +12,10 @@ 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 Badge from '../shared/Badge';
|
||||
import './ToolPanel.css';
|
||||
|
||||
interface LegacyToolListProps {
|
||||
interface FullscreenToolListProps {
|
||||
filteredTools: Array<{ item: [string, ToolRegistryEntry]; matchedText?: string }>;
|
||||
searchQuery: string;
|
||||
showDescriptions: boolean;
|
||||
@@ -23,17 +24,17 @@ interface LegacyToolListProps {
|
||||
onSelect: (id: ToolId) => void;
|
||||
}
|
||||
|
||||
const LegacyToolList = ({
|
||||
const FullscreenToolList = ({
|
||||
filteredTools,
|
||||
searchQuery,
|
||||
showDescriptions,
|
||||
selectedToolKey,
|
||||
matchedTextMap,
|
||||
onSelect,
|
||||
}: LegacyToolListProps) => {
|
||||
}: FullscreenToolListProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { hotkeys } = useHotkeys();
|
||||
const { toolRegistry, recentTools, favoriteTools, toggleFavorite, isFavorite, legacyToolSettings } = useToolWorkflow();
|
||||
const { toolRegistry, recentTools, favoriteTools, toggleFavorite, isFavorite, fullscreenToolSettings } = useToolWorkflow();
|
||||
|
||||
const { sections, searchGroups } = useToolSections(filteredTools, searchQuery);
|
||||
|
||||
@@ -72,32 +73,32 @@ const LegacyToolList = ({
|
||||
|
||||
if (subcategoryGroups.length === 0 && !showRecentFavorites) {
|
||||
return (
|
||||
<div className="tool-panel__legacy-empty">
|
||||
<div className="tool-panel__fullscreen-empty">
|
||||
<NoToolsFound />
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('toolPanel.legacy.noResults', 'Try adjusting your search or toggle descriptions to find what you need.')}
|
||||
{t('toolPanel.fullscreen.noResults', 'Try adjusting your search or toggle descriptions to find what you need.')}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const containerClass = showDescriptions
|
||||
? 'tool-panel__legacy-groups tool-panel__legacy-groups--detailed'
|
||||
: 'tool-panel__legacy-groups tool-panel__legacy-groups--compact';
|
||||
? 'tool-panel__fullscreen-groups tool-panel__fullscreen-groups--detailed'
|
||||
: 'tool-panel__fullscreen-groups tool-panel__fullscreen-groups--compact';
|
||||
|
||||
const getItemClasses = (isDetailed: boolean) => {
|
||||
const base = isDetailed ? 'tool-panel__legacy-item--detailed' : '';
|
||||
const border = legacyToolSettings.toolItemBorder === 'hidden' ? 'tool-panel__legacy-item--no-border' : '';
|
||||
const hover = `tool-panel__legacy-item--hover-${legacyToolSettings.hoverIntensity}`;
|
||||
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(' ');
|
||||
};
|
||||
|
||||
const getIconBackground = (categoryColor: string, isDetailed: boolean) => {
|
||||
if (legacyToolSettings.iconBackground === 'none' || legacyToolSettings.iconBackground === 'hover') {
|
||||
if (fullscreenToolSettings.iconBackground === 'none' || fullscreenToolSettings.iconBackground === 'hover') {
|
||||
return 'transparent';
|
||||
}
|
||||
|
||||
const baseColor = isDetailed ? 'var(--legacy-bg-icon-detailed)' : 'var(--legacy-bg-icon-compact)';
|
||||
const baseColor = isDetailed ? 'var(--fullscreen-bg-icon-detailed)' : 'var(--fullscreen-bg-icon-compact)';
|
||||
const blend1 = isDetailed ? '18%' : '15%';
|
||||
const blend2 = isDetailed ? '8%' : '6%';
|
||||
|
||||
@@ -108,10 +109,10 @@ const LegacyToolList = ({
|
||||
};
|
||||
|
||||
const getIconStyle = () => {
|
||||
if (legacyToolSettings.iconColorScheme === 'monochrome') {
|
||||
if (fullscreenToolSettings.iconColorScheme === 'monochrome') {
|
||||
return { filter: 'grayscale(1) opacity(0.8)' };
|
||||
}
|
||||
if (legacyToolSettings.iconColorScheme === 'vibrant') {
|
||||
if (fullscreenToolSettings.iconColorScheme === 'vibrant') {
|
||||
return { filter: 'saturate(1.5) brightness(1.1)' };
|
||||
}
|
||||
return {};
|
||||
@@ -156,29 +157,29 @@ const LegacyToolList = ({
|
||||
// Detailed view
|
||||
if (showDescriptions) {
|
||||
const iconBg = getIconBackground(categoryColor, true);
|
||||
const iconClasses = legacyToolSettings.iconBackground === 'hover'
|
||||
? 'tool-panel__legacy-icon tool-panel__legacy-icon--hover-bg'
|
||||
: 'tool-panel__legacy-icon';
|
||||
const iconClasses = fullscreenToolSettings.iconBackground === 'hover'
|
||||
? 'tool-panel__fullscreen-icon tool-panel__fullscreen-icon--hover-bg'
|
||||
: 'tool-panel__fullscreen-icon';
|
||||
|
||||
const hoverBgDetailed = legacyToolSettings.iconBackground === 'hover'
|
||||
const hoverBgDetailed = fullscreenToolSettings.iconBackground === 'hover'
|
||||
? `linear-gradient(135deg,
|
||||
color-mix(in srgb, ${categoryColor} 18%, var(--legacy-bg-icon-detailed)),
|
||||
color-mix(in srgb, ${categoryColor} 8%, var(--legacy-bg-icon-detailed))
|
||||
color-mix(in srgb, ${categoryColor} 18%, var(--fullscreen-bg-icon-detailed)),
|
||||
color-mix(in srgb, ${categoryColor} 8%, var(--fullscreen-bg-icon-detailed))
|
||||
)`
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={id}
|
||||
type="button"
|
||||
className={`tool-panel__legacy-item ${getItemClasses(true)} ${isSelected ? 'tool-panel__legacy-item--selected' : ''} tool-panel__legacy-item--with-star`}
|
||||
onClick={handleClick}
|
||||
aria-disabled={isDisabled}
|
||||
disabled={isDisabled}
|
||||
style={{
|
||||
['--legacy-icon-hover-bg' as any]: hoverBgDetailed,
|
||||
}}
|
||||
>
|
||||
return (
|
||||
<button
|
||||
key={id}
|
||||
type="button"
|
||||
className={`tool-panel__fullscreen-item ${getItemClasses(true)} ${isSelected ? 'tool-panel__fullscreen-item--selected' : ''} tool-panel__fullscreen-item--with-star`}
|
||||
onClick={handleClick}
|
||||
aria-disabled={isDisabled}
|
||||
disabled={isDisabled}
|
||||
style={{
|
||||
['--fullscreen-icon-hover-bg' as any]: hoverBgDetailed,
|
||||
}}
|
||||
>
|
||||
{tool.icon ? (
|
||||
<span
|
||||
className={iconClasses}
|
||||
@@ -191,15 +192,15 @@ const LegacyToolList = ({
|
||||
{iconNode}
|
||||
</span>
|
||||
) : null}
|
||||
<span className="tool-panel__legacy-body">
|
||||
<Text fw={600} size="sm" className="tool-panel__legacy-name">
|
||||
<span className="tool-panel__fullscreen-body">
|
||||
<Text fw={600} size="sm" className="tool-panel__fullscreen-name">
|
||||
{tool.name}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" className="tool-panel__legacy-description">
|
||||
<Text size="sm" c="dimmed" className="tool-panel__fullscreen-description">
|
||||
{tool.description}
|
||||
</Text>
|
||||
{binding && (
|
||||
<div className="tool-panel__legacy-shortcut">
|
||||
<div className="tool-panel__fullscreen-shortcut">
|
||||
<span style={{ color: 'var(--mantine-color-dimmed)', fontSize: '0.75rem' }}>
|
||||
{t('settings.hotkeys.shortcut', 'Shortcut')}
|
||||
</span>
|
||||
@@ -207,8 +208,8 @@ const LegacyToolList = ({
|
||||
</div>
|
||||
)}
|
||||
{matchedText && (
|
||||
<Text size="xs" c="dimmed" className="tool-panel__legacy-match">
|
||||
{t('toolPanel.legacy.matchedSynonym', 'Matches "{{text}}"', { text: matchedText })}
|
||||
<Text size="xs" c="dimmed" className="tool-panel__fullscreen-match">
|
||||
{t('toolPanel.fullscreen.matchedSynonym', 'Matches "{{text}}"', { text: matchedText })}
|
||||
</Text>
|
||||
)}
|
||||
</span>
|
||||
@@ -218,8 +219,8 @@ const LegacyToolList = ({
|
||||
radius="xl"
|
||||
size="sm"
|
||||
onClick={handleStarClick}
|
||||
className="tool-panel__legacy-star"
|
||||
aria-label={isFav ? t('toolPanel.legacy.unfavorite', 'Remove from favourites') : t('toolPanel.legacy.favorite', 'Add to favourites')}
|
||||
className="tool-panel__fullscreen-star"
|
||||
aria-label={isFav ? t('toolPanel.fullscreen.unfavorite', 'Remove from favourites') : t('toolPanel.fullscreen.favorite', 'Add to favourites')}
|
||||
>
|
||||
{isFav ? (
|
||||
<StarRoundedIcon fontSize="small" style={{ color: '#FFC107' }} />
|
||||
@@ -234,29 +235,29 @@ const LegacyToolList = ({
|
||||
|
||||
// Compact view
|
||||
const iconBg = getIconBackground(categoryColor, false);
|
||||
const iconClasses = legacyToolSettings.iconBackground === 'hover'
|
||||
? 'tool-panel__legacy-list-icon tool-panel__legacy-list-icon--hover-bg'
|
||||
: 'tool-panel__legacy-list-icon';
|
||||
const iconClasses = fullscreenToolSettings.iconBackground === 'hover'
|
||||
? 'tool-panel__fullscreen-list-icon tool-panel__fullscreen-list-icon--hover-bg'
|
||||
: 'tool-panel__fullscreen-list-icon';
|
||||
|
||||
const hoverBgCompact = legacyToolSettings.iconBackground === 'hover'
|
||||
const hoverBgCompact = fullscreenToolSettings.iconBackground === 'hover'
|
||||
? `linear-gradient(135deg,
|
||||
color-mix(in srgb, ${categoryColor} 15%, var(--legacy-bg-icon-compact)),
|
||||
color-mix(in srgb, ${categoryColor} 6%, var(--legacy-bg-icon-compact))
|
||||
color-mix(in srgb, ${categoryColor} 15%, var(--fullscreen-bg-icon-compact)),
|
||||
color-mix(in srgb, ${categoryColor} 6%, var(--fullscreen-bg-icon-compact))
|
||||
)`
|
||||
: undefined;
|
||||
|
||||
const compactButton = (
|
||||
<button
|
||||
key={id}
|
||||
type="button"
|
||||
className={`tool-panel__legacy-list-item ${getItemClasses(false)} ${isSelected ? 'tool-panel__legacy-list-item--selected' : ''} ${!isDisabled ? 'tool-panel__legacy-list-item--with-star' : ''}`}
|
||||
onClick={handleClick}
|
||||
aria-disabled={isDisabled}
|
||||
disabled={isDisabled}
|
||||
style={{
|
||||
['--legacy-icon-hover-bg' as any]: hoverBgCompact,
|
||||
}}
|
||||
>
|
||||
const compactButton = (
|
||||
<button
|
||||
key={id}
|
||||
type="button"
|
||||
className={`tool-panel__fullscreen-list-item ${getItemClasses(false)} ${isSelected ? 'tool-panel__fullscreen-list-item--selected' : ''} ${!isDisabled ? 'tool-panel__fullscreen-list-item--with-star' : ''}`}
|
||||
onClick={handleClick}
|
||||
aria-disabled={isDisabled}
|
||||
disabled={isDisabled}
|
||||
style={{
|
||||
['--fullscreen-icon-hover-bg' as any]: hoverBgCompact,
|
||||
}}
|
||||
>
|
||||
{tool.icon ? (
|
||||
<span
|
||||
className={iconClasses}
|
||||
@@ -269,13 +270,13 @@ const LegacyToolList = ({
|
||||
{iconNode}
|
||||
</span>
|
||||
) : null}
|
||||
<span className="tool-panel__legacy-list-body">
|
||||
<Text fw={600} size="sm" className="tool-panel__legacy-name">
|
||||
<span className="tool-panel__fullscreen-list-body">
|
||||
<Text fw={600} size="sm" className="tool-panel__fullscreen-name">
|
||||
{tool.name}
|
||||
</Text>
|
||||
{matchedText && (
|
||||
<Text size="xs" c="dimmed" className="tool-panel__legacy-match">
|
||||
{t('toolPanel.legacy.matchedSynonym', 'Matches "{{text}}"', { text: matchedText})}
|
||||
<Text size="xs" c="dimmed" className="tool-panel__fullscreen-match">
|
||||
{t('toolPanel.fullscreen.matchedSynonym', 'Matches "{{text}}"', { text: matchedText})}
|
||||
</Text>
|
||||
)}
|
||||
</span>
|
||||
@@ -285,8 +286,8 @@ const LegacyToolList = ({
|
||||
radius="xl"
|
||||
size="xs"
|
||||
onClick={handleStarClick}
|
||||
className="tool-panel__legacy-star-compact"
|
||||
aria-label={isFav ? t('toolPanel.legacy.unfavorite', 'Remove from favourites') : t('toolPanel.legacy.favorite', 'Add to favourites')}
|
||||
className="tool-panel__fullscreen-star-compact"
|
||||
aria-label={isFav ? t('toolPanel.fullscreen.unfavorite', 'Remove from favourites') : t('toolPanel.fullscreen.favorite', 'Add to favourites')}
|
||||
>
|
||||
{isFav ? (
|
||||
<StarRoundedIcon fontSize="inherit" style={{ color: '#FFC107', fontSize: '1rem' }} />
|
||||
@@ -331,13 +332,13 @@ const LegacyToolList = ({
|
||||
{showRecentFavorites && (
|
||||
<>
|
||||
{favoriteToolItems.length > 0 && (
|
||||
<section className="tool-panel__legacy-group tool-panel__legacy-group--special">
|
||||
<header className="tool-panel__legacy-section-header">
|
||||
<div className="tool-panel__legacy-section-title">
|
||||
<section className="tool-panel__fullscreen-group tool-panel__fullscreen-group--special">
|
||||
<header className="tool-panel__fullscreen-section-header">
|
||||
<div className="tool-panel__fullscreen-section-title">
|
||||
<span
|
||||
className="tool-panel__legacy-section-icon"
|
||||
className="tool-panel__fullscreen-section-icon"
|
||||
style={{
|
||||
color: legacyToolSettings.headerIconColor === 'colored' ? '#FFC107' : 'var(--mantine-color-dimmed)',
|
||||
color: fullscreenToolSettings.headerIconColor === 'colored' ? '#FFC107' : 'var(--mantine-color-dimmed)',
|
||||
...getIconStyle(),
|
||||
}}
|
||||
aria-hidden
|
||||
@@ -345,23 +346,23 @@ const LegacyToolList = ({
|
||||
<StarRoundedIcon />
|
||||
</span>
|
||||
<Text size="sm" fw={600} tt="uppercase" lts={0.5} c="dimmed">
|
||||
{t('toolPanel.legacy.favorites', 'Favourites')}
|
||||
{t('toolPanel.fullscreen.favorites', 'Favourites')}
|
||||
</Text>
|
||||
</div>
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="light"
|
||||
color={legacyToolSettings.headerBadgeColor === 'colored' ? 'yellow' : 'gray'}
|
||||
variant={fullscreenToolSettings.headerBadgeColor === 'colored' ? 'colored' : 'default'}
|
||||
color={fullscreenToolSettings.headerBadgeColor === 'colored' ? '#FFC107' : undefined}
|
||||
>
|
||||
{favoriteToolItems.length}
|
||||
</Badge>
|
||||
</header>
|
||||
{showDescriptions ? (
|
||||
<div className="tool-panel__legacy-grid tool-panel__legacy-grid--detailed">
|
||||
<div className="tool-panel__fullscreen-grid tool-panel__fullscreen-grid--detailed">
|
||||
{favoriteToolItems.map((item: any) => renderToolItem(item.id, item.tool))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="tool-panel__legacy-list">
|
||||
<div className="tool-panel__fullscreen-list">
|
||||
{favoriteToolItems.map((item: any) => renderToolItem(item.id, item.tool))}
|
||||
</div>
|
||||
)}
|
||||
@@ -369,13 +370,13 @@ const LegacyToolList = ({
|
||||
)}
|
||||
|
||||
{recentToolItems.length > 0 && (
|
||||
<section className="tool-panel__legacy-group tool-panel__legacy-group--special">
|
||||
<header className="tool-panel__legacy-section-header">
|
||||
<div className="tool-panel__legacy-section-title">
|
||||
<section className="tool-panel__fullscreen-group tool-panel__fullscreen-group--special">
|
||||
<header className="tool-panel__fullscreen-section-header">
|
||||
<div className="tool-panel__fullscreen-section-title">
|
||||
<span
|
||||
className="tool-panel__legacy-section-icon"
|
||||
className="tool-panel__fullscreen-section-icon"
|
||||
style={{
|
||||
color: legacyToolSettings.headerIconColor === 'colored' ? '#1BB1D4' : 'var(--mantine-color-dimmed)',
|
||||
color: fullscreenToolSettings.headerIconColor === 'colored' ? '#1BB1D4' : 'var(--mantine-color-dimmed)',
|
||||
...getIconStyle(),
|
||||
}}
|
||||
aria-hidden
|
||||
@@ -383,23 +384,23 @@ const LegacyToolList = ({
|
||||
<HistoryRoundedIcon />
|
||||
</span>
|
||||
<Text size="sm" fw={600} tt="uppercase" lts={0.5} c="dimmed">
|
||||
{t('toolPanel.legacy.recent', 'Recently used')}
|
||||
{t('toolPanel.fullscreen.recent', 'Recently used')}
|
||||
</Text>
|
||||
</div>
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="light"
|
||||
color={legacyToolSettings.headerBadgeColor === 'colored' ? 'cyan' : 'gray'}
|
||||
variant={fullscreenToolSettings.headerBadgeColor === 'colored' ? 'colored' : 'default'}
|
||||
color={fullscreenToolSettings.headerBadgeColor === 'colored' ? '#1BB1D4' : undefined}
|
||||
>
|
||||
{recentToolItems.length}
|
||||
</Badge>
|
||||
</header>
|
||||
{showDescriptions ? (
|
||||
<div className="tool-panel__legacy-grid tool-panel__legacy-grid--detailed">
|
||||
<div className="tool-panel__fullscreen-grid tool-panel__fullscreen-grid--detailed">
|
||||
{recentToolItems.map((item: any) => renderToolItem(item.id, item.tool))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="tool-panel__legacy-list">
|
||||
<div className="tool-panel__fullscreen-list">
|
||||
{recentToolItems.map((item: any) => renderToolItem(item.id, item.tool))}
|
||||
</div>
|
||||
)}
|
||||
@@ -414,17 +415,17 @@ const LegacyToolList = ({
|
||||
return (
|
||||
<section
|
||||
key={subcategoryId}
|
||||
className={`tool-panel__legacy-group ${showDescriptions ? 'tool-panel__legacy-group--detailed' : 'tool-panel__legacy-group--compact'}`}
|
||||
className={`tool-panel__fullscreen-group ${showDescriptions ? 'tool-panel__fullscreen-group--detailed' : 'tool-panel__fullscreen-group--compact'}`}
|
||||
style={{
|
||||
borderColor: `color-mix(in srgb, ${categoryColor} 25%, var(--legacy-border-subtle-65))`,
|
||||
borderColor: `color-mix(in srgb, ${categoryColor} 25%, var(--fullscreen-border-subtle-65))`,
|
||||
}}
|
||||
>
|
||||
<header className="tool-panel__legacy-section-header">
|
||||
<div className="tool-panel__legacy-section-title">
|
||||
<header className="tool-panel__fullscreen-section-header">
|
||||
<div className="tool-panel__fullscreen-section-title">
|
||||
<span
|
||||
className="tool-panel__legacy-section-icon"
|
||||
className="tool-panel__fullscreen-section-icon"
|
||||
style={{
|
||||
color: legacyToolSettings.sectionTitleColor === 'colored' ? categoryColor : 'var(--mantine-color-dimmed)',
|
||||
color: fullscreenToolSettings.sectionTitleColor === 'colored' ? categoryColor : 'var(--mantine-color-dimmed)',
|
||||
...getIconStyle(),
|
||||
}}
|
||||
aria-hidden
|
||||
@@ -437,33 +438,28 @@ const LegacyToolList = ({
|
||||
tt="uppercase"
|
||||
lts={0.5}
|
||||
style={{
|
||||
color: legacyToolSettings.sectionTitleColor === 'colored' ? categoryColor : undefined,
|
||||
color: fullscreenToolSettings.sectionTitleColor === 'colored' ? categoryColor : undefined,
|
||||
}}
|
||||
c={legacyToolSettings.sectionTitleColor === 'neutral' ? 'dimmed' : undefined}
|
||||
c={fullscreenToolSettings.sectionTitleColor === 'neutral' ? 'dimmed' : undefined}
|
||||
>
|
||||
{getSubcategoryLabel(t, subcategoryId)}
|
||||
</Text>
|
||||
</div>
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="light"
|
||||
style={legacyToolSettings.sectionTitleColor === 'colored' ? {
|
||||
backgroundColor: `color-mix(in srgb, ${categoryColor} 15%, transparent)`,
|
||||
color: categoryColor,
|
||||
borderColor: `color-mix(in srgb, ${categoryColor} 30%, transparent)`
|
||||
} : undefined}
|
||||
color={legacyToolSettings.sectionTitleColor === 'neutral' ? 'gray' : undefined}
|
||||
variant={fullscreenToolSettings.sectionTitleColor === 'colored' ? 'colored' : 'default'}
|
||||
color={fullscreenToolSettings.sectionTitleColor === 'colored' ? categoryColor : undefined}
|
||||
>
|
||||
{tools.length}
|
||||
</Badge>
|
||||
</header>
|
||||
|
||||
{showDescriptions ? (
|
||||
<div className="tool-panel__legacy-grid tool-panel__legacy-grid--detailed">
|
||||
<div className="tool-panel__fullscreen-grid tool-panel__fullscreen-grid--detailed">
|
||||
{tools.map(({ id, tool }) => renderToolItem(id, tool))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="tool-panel__legacy-list">
|
||||
<div className="tool-panel__fullscreen-list">
|
||||
{tools.map(({ id, tool }) => renderToolItem(id, tool))}
|
||||
</div>
|
||||
)}
|
||||
@@ -474,4 +470,6 @@ const LegacyToolList = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default LegacyToolList;
|
||||
export default FullscreenToolList;
|
||||
|
||||
|
||||
204
frontend/src/components/tools/FullscreenToolSettings.tsx
Normal file
204
frontend/src/components/tools/FullscreenToolSettings.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
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;
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import { ActionIcon, ScrollArea, Switch, Tooltip, useMantineColorScheme } from '
|
||||
import ViewSidebarRoundedIcon from '@mui/icons-material/ViewSidebarRounded';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ToolSearch from './toolPicker/ToolSearch';
|
||||
import LegacyToolList from './LegacyToolList';
|
||||
import LegacyToolSettings from './LegacyToolSettings';
|
||||
import FullscreenToolList from './FullscreenToolList';
|
||||
import FullscreenToolSettings from './FullscreenToolSettings';
|
||||
import { ToolRegistryEntry } from '../../data/toolsTaxonomy';
|
||||
import { ToolId } from '../../types/toolId';
|
||||
import { useFocusTrap } from '../../hooks/tools/useFocusTrap';
|
||||
@@ -12,7 +12,7 @@ import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
|
||||
import { BASE_PATH } from '../../constants/app';
|
||||
import './ToolPanel.css';
|
||||
|
||||
interface LegacyToolSurfaceProps {
|
||||
interface FullscreenToolSurfaceProps {
|
||||
searchQuery: string;
|
||||
toolRegistry: Record<string, ToolRegistryEntry>;
|
||||
filteredTools: Array<{ item: [string, ToolRegistryEntry]; matchedText?: string }>;
|
||||
@@ -22,7 +22,7 @@ interface LegacyToolSurfaceProps {
|
||||
onSearchChange: (value: string) => void;
|
||||
onSelect: (id: ToolId) => void;
|
||||
onToggleDescriptions: () => void;
|
||||
onExitLegacyMode: () => void;
|
||||
onExitFullscreenMode: () => void;
|
||||
toggleLabel: string;
|
||||
geometry: {
|
||||
left: number;
|
||||
@@ -32,7 +32,7 @@ interface LegacyToolSurfaceProps {
|
||||
} | null;
|
||||
}
|
||||
|
||||
const LegacyToolSurface = ({
|
||||
const FullscreenToolSurface = ({
|
||||
searchQuery,
|
||||
toolRegistry,
|
||||
filteredTools,
|
||||
@@ -42,13 +42,13 @@ const LegacyToolSurface = ({
|
||||
onSearchChange,
|
||||
onSelect,
|
||||
onToggleDescriptions,
|
||||
onExitLegacyMode,
|
||||
onExitFullscreenMode,
|
||||
toggleLabel,
|
||||
geometry,
|
||||
}: LegacyToolSurfaceProps) => {
|
||||
}: FullscreenToolSurfaceProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const { legacyToolSettings, setLegacyToolSettings } = useToolWorkflow();
|
||||
const { fullscreenToolSettings, setFullscreenToolSettings } = useToolWorkflow();
|
||||
const [isExiting, setIsExiting] = useState(false);
|
||||
const surfaceRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -67,13 +67,13 @@ const LegacyToolSurface = ({
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
if (prefersReducedMotion) {
|
||||
onExitLegacyMode();
|
||||
onExitFullscreenMode();
|
||||
return;
|
||||
}
|
||||
|
||||
setIsExiting(true);
|
||||
setTimeout(() => {
|
||||
onExitLegacyMode();
|
||||
onExitFullscreenMode();
|
||||
}, 220); // Match animation duration (0.22s)
|
||||
};
|
||||
|
||||
@@ -99,24 +99,24 @@ const LegacyToolSurface = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="tool-panel__legacy-surface"
|
||||
className="tool-panel__fullscreen-surface"
|
||||
style={style}
|
||||
role="region"
|
||||
aria-label={t('toolPanel.legacy.heading', 'All tools (legacy view)')}
|
||||
aria-label={t('toolPanel.fullscreen.heading', 'All tools (fullscreen view)')}
|
||||
>
|
||||
<div
|
||||
ref={surfaceRef}
|
||||
className={`tool-panel__legacy-surface-inner ${isExiting ? 'tool-panel__legacy-surface-inner--exiting' : ''}`}
|
||||
className={`tool-panel__fullscreen-surface-inner ${isExiting ? 'tool-panel__fullscreen-surface-inner--exiting' : ''}`}
|
||||
>
|
||||
<header className="tool-panel__legacy-header">
|
||||
<div className="tool-panel__legacy-brand">
|
||||
<img src={brandIconSrc} alt="" className="tool-panel__legacy-brand-icon" />
|
||||
<img src={brandTextSrc} alt={brandAltText} className="tool-panel__legacy-brand-text" />
|
||||
<header className="tool-panel__fullscreen-header">
|
||||
<div className="tool-panel__fullscreen-brand">
|
||||
<img src={brandIconSrc} alt="" className="tool-panel__fullscreen-brand-icon" />
|
||||
<img src={brandTextSrc} alt={brandAltText} className="tool-panel__fullscreen-brand-text" />
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
|
||||
<LegacyToolSettings
|
||||
settings={legacyToolSettings}
|
||||
onChange={setLegacyToolSettings}
|
||||
<FullscreenToolSettings
|
||||
settings={fullscreenToolSettings}
|
||||
onChange={setFullscreenToolSettings}
|
||||
/>
|
||||
<Tooltip label={toggleLabel} position="bottom" withArrow>
|
||||
<ActionIcon
|
||||
@@ -125,6 +125,7 @@ const LegacyToolSurface = ({
|
||||
size="md"
|
||||
onClick={handleExit}
|
||||
aria-label={toggleLabel}
|
||||
style={{ color: 'var(--right-rail-icon)' }}
|
||||
>
|
||||
<ViewSidebarRoundedIcon fontSize="small" />
|
||||
</ActionIcon>
|
||||
@@ -132,7 +133,7 @@ const LegacyToolSurface = ({
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="tool-panel__legacy-controls">
|
||||
<div className="tool-panel__fullscreen-controls">
|
||||
<ToolSearch
|
||||
value={searchQuery}
|
||||
onChange={onSearchChange}
|
||||
@@ -145,13 +146,13 @@ const LegacyToolSurface = ({
|
||||
onChange={() => onToggleDescriptions()}
|
||||
size="md"
|
||||
labelPosition="left"
|
||||
label={showDescriptions ? t('toolPanel.legacy.descriptionsOn', 'Showing descriptions') : t('toolPanel.legacy.descriptionsOff', 'Descriptions hidden')}
|
||||
label={t('toolPanel.fullscreen.showDetails', 'Show Details')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="tool-panel__legacy-body">
|
||||
<ScrollArea className="tool-panel__legacy-scroll" offsetScrollbars>
|
||||
<LegacyToolList
|
||||
<div className="tool-panel__fullscreen-body">
|
||||
<ScrollArea className="tool-panel__fullscreen-scroll" offsetScrollbars>
|
||||
<FullscreenToolList
|
||||
filteredTools={filteredTools}
|
||||
searchQuery={searchQuery}
|
||||
showDescriptions={showDescriptions}
|
||||
@@ -166,4 +167,6 @@ const LegacyToolSurface = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default LegacyToolSurface;
|
||||
export default FullscreenToolSurface;
|
||||
|
||||
|
||||
@@ -1,252 +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 LegacyToolStyleSettings {
|
||||
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 defaultLegacyToolSettings: LegacyToolStyleSettings = {
|
||||
iconBackground: 'always',
|
||||
iconColorScheme: 'colored',
|
||||
sectionTitleColor: 'colored',
|
||||
headerIconColor: 'colored',
|
||||
headerBadgeColor: 'colored',
|
||||
toolItemBorder: 'visible',
|
||||
hoverIntensity: 'moderate',
|
||||
};
|
||||
|
||||
interface LegacyToolSettingsProps {
|
||||
settings: LegacyToolStyleSettings;
|
||||
onChange: (settings: LegacyToolStyleSettings) => void;
|
||||
}
|
||||
|
||||
const LegacyToolSettings = ({ settings, onChange }: LegacyToolSettingsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
const updateSetting = <K extends keyof LegacyToolStyleSettings>(
|
||||
key: K,
|
||||
value: LegacyToolStyleSettings[K]
|
||||
) => {
|
||||
onChange({ ...settings, [key]: value });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
radius="xl"
|
||||
size="md"
|
||||
onClick={() => setOpened(true)}
|
||||
aria-label={t('toolPanel.legacy.settings.title', 'Customize appearance')}
|
||||
>
|
||||
<TuneRoundedIcon fontSize="small" />
|
||||
</ActionIcon>
|
||||
|
||||
<Drawer
|
||||
opened={opened}
|
||||
onClose={() => setOpened(false)}
|
||||
title={t('toolPanel.legacy.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.legacy.settings.iconBackground.label', 'Tool icon background')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mb="sm">
|
||||
{t('toolPanel.legacy.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.legacy.settings.iconBackground.none', 'None'),
|
||||
value: 'none',
|
||||
},
|
||||
{
|
||||
label: t('toolPanel.legacy.settings.iconBackground.hover', 'On hover'),
|
||||
value: 'hover',
|
||||
},
|
||||
{
|
||||
label: t('toolPanel.legacy.settings.iconBackground.always', 'Always'),
|
||||
value: 'always',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text size="sm" fw={600} mb="xs">
|
||||
{t('toolPanel.legacy.settings.iconColor.label', 'Tool icon color')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mb="sm">
|
||||
{t('toolPanel.legacy.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.legacy.settings.iconColor.colored', 'Colored'),
|
||||
value: 'colored',
|
||||
},
|
||||
{
|
||||
label: t('toolPanel.legacy.settings.iconColor.vibrant', 'Vibrant'),
|
||||
value: 'vibrant',
|
||||
},
|
||||
{
|
||||
label: t('toolPanel.legacy.settings.iconColor.monochrome', 'Monochrome'),
|
||||
value: 'monochrome',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text size="sm" fw={600} mb="xs">
|
||||
{t('toolPanel.legacy.settings.sectionTitle.label', 'Section titles')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mb="sm">
|
||||
{t('toolPanel.legacy.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.legacy.settings.sectionTitle.colored', 'Colored'),
|
||||
value: 'colored',
|
||||
},
|
||||
{
|
||||
label: t('toolPanel.legacy.settings.sectionTitle.neutral', 'Neutral'),
|
||||
value: 'neutral',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text size="sm" fw={600} mb="xs">
|
||||
{t('toolPanel.legacy.settings.headerIcon.label', 'Section header icons')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mb="sm">
|
||||
{t('toolPanel.legacy.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.legacy.settings.headerIcon.colored', 'Colored'),
|
||||
value: 'colored',
|
||||
},
|
||||
{
|
||||
label: t('toolPanel.legacy.settings.headerIcon.monochrome', 'Monochrome'),
|
||||
value: 'monochrome',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text size="sm" fw={600} mb="xs">
|
||||
{t('toolPanel.legacy.settings.headerBadge.label', 'Section header badges')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mb="sm">
|
||||
{t('toolPanel.legacy.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.legacy.settings.headerBadge.colored', 'Colored'),
|
||||
value: 'colored',
|
||||
},
|
||||
{
|
||||
label: t('toolPanel.legacy.settings.headerBadge.neutral', 'Neutral'),
|
||||
value: 'neutral',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text size="sm" fw={600} mb="xs">
|
||||
{t('toolPanel.legacy.settings.border.label', 'Tool item borders')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mb="sm">
|
||||
{t('toolPanel.legacy.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.legacy.settings.border.visible', 'Visible'),
|
||||
value: 'visible',
|
||||
},
|
||||
{
|
||||
label: t('toolPanel.legacy.settings.border.hidden', 'Hidden'),
|
||||
value: 'hidden',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text size="sm" fw={600} mb="xs">
|
||||
{t('toolPanel.legacy.settings.hover.label', 'Hover effect intensity')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mb="sm">
|
||||
{t('toolPanel.legacy.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.legacy.settings.hover.subtle', 'Subtle')}
|
||||
/>
|
||||
<Radio
|
||||
value="moderate"
|
||||
label={t('toolPanel.legacy.settings.hover.moderate', 'Moderate')}
|
||||
/>
|
||||
<Radio
|
||||
value="prominent"
|
||||
label={t('toolPanel.legacy.settings.hover.prominent', 'Prominent')}
|
||||
/>
|
||||
</Stack>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
</Stack>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LegacyToolSettings;
|
||||
@@ -48,14 +48,14 @@ const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect,
|
||||
) : undefined;
|
||||
|
||||
return (
|
||||
<ToolButton
|
||||
key={id}
|
||||
id={id}
|
||||
tool={tool}
|
||||
isSelected={false}
|
||||
onSelect={onSelect}
|
||||
matchedSynonym={matchedSynonym}
|
||||
/>
|
||||
<ToolButton
|
||||
key={id}
|
||||
id={id}
|
||||
tool={tool}
|
||||
isSelected={false}
|
||||
onSelect={onSelect}
|
||||
matchedSynonym={matchedSynonym}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
/* CSS Custom Properties for Legacy Mode */
|
||||
.tool-panel__legacy-surface-inner {
|
||||
--legacy-bg-surface-1: color-mix(in srgb, var(--bg-toolbar) 96%, transparent);
|
||||
--legacy-bg-surface-2: color-mix(in srgb, var(--bg-background) 90%, transparent);
|
||||
--legacy-bg-header: var(--bg-toolbar);
|
||||
--legacy-bg-controls-1: var(--bg-toolbar);
|
||||
--legacy-bg-controls-2: color-mix(in srgb, var(--bg-toolbar) 95%, var(--bg-background));
|
||||
--legacy-bg-body-1: color-mix(in srgb, var(--bg-background) 86%, transparent);
|
||||
--legacy-bg-body-2: color-mix(in srgb, var(--bg-toolbar) 78%, transparent);
|
||||
--legacy-bg-group: color-mix(in srgb, var(--bg-toolbar) 82%, transparent);
|
||||
--legacy-bg-item: color-mix(in srgb, var(--bg-toolbar) 88%, transparent);
|
||||
--legacy-bg-list-item: color-mix(in srgb, var(--bg-toolbar) 86%, transparent);
|
||||
--legacy-bg-icon-detailed: color-mix(in srgb, var(--bg-muted) 75%, transparent);
|
||||
--legacy-bg-icon-compact: color-mix(in srgb, var(--bg-muted) 70%, transparent);
|
||||
--legacy-border-subtle-75: color-mix(in srgb, var(--border-subtle) 75%, transparent);
|
||||
--legacy-border-subtle-70: color-mix(in srgb, var(--border-subtle) 70%, transparent);
|
||||
--legacy-border-subtle-65: color-mix(in srgb, var(--border-subtle) 65%, transparent);
|
||||
--legacy-shadow-primary: color-mix(in srgb, var(--shadow-color, rgba(15, 23, 42, 0.55)) 25%, transparent);
|
||||
--legacy-shadow-secondary: color-mix(in srgb, var(--shadow-color, rgba(15, 23, 42, 0.35)) 30%, transparent);
|
||||
--legacy-shadow-group: color-mix(in srgb, var(--shadow-color, rgba(15, 23, 42, 0.45)) 18%, transparent);
|
||||
--legacy-accent-hover: color-mix(in srgb, var(--text-primary) 20%, var(--border-subtle));
|
||||
--legacy-accent-selected: color-mix(in srgb, var(--text-primary) 30%, var(--border-subtle));
|
||||
--legacy-accent-ring: color-mix(in srgb, var(--text-primary) 15%, transparent);
|
||||
--legacy-accent-list-bg: color-mix(in srgb, var(--text-primary) 8%, var(--bg-toolbar));
|
||||
--legacy-accent-list-border: color-mix(in srgb, var(--text-primary) 20%, var(--border-subtle));
|
||||
--legacy-text-icon: color-mix(in srgb, var(--text-primary) 90%, var(--text-muted));
|
||||
--legacy-text-icon-compact: color-mix(in srgb, var(--text-primary) 88%, var(--text-muted));
|
||||
/* CSS Custom Properties for Fullscreen Mode */
|
||||
.tool-panel__fullscreen-surface-inner {
|
||||
--fullscreen-bg-surface-1: color-mix(in srgb, var(--bg-toolbar) 96%, transparent);
|
||||
--fullscreen-bg-surface-2: color-mix(in srgb, var(--bg-background) 90%, transparent);
|
||||
--fullscreen-bg-header: var(--bg-toolbar);
|
||||
--fullscreen-bg-controls-1: var(--bg-toolbar);
|
||||
--fullscreen-bg-controls-2: color-mix(in srgb, var(--bg-toolbar) 95%, var(--bg-background));
|
||||
--fullscreen-bg-body-1: color-mix(in srgb, var(--bg-background) 86%, transparent);
|
||||
--fullscreen-bg-body-2: color-mix(in srgb, var(--bg-toolbar) 78%, transparent);
|
||||
--fullscreen-bg-group: color-mix(in srgb, var(--bg-toolbar) 82%, transparent);
|
||||
--fullscreen-bg-item: color-mix(in srgb, var(--bg-toolbar) 88%, transparent);
|
||||
--fullscreen-bg-list-item: color-mix(in srgb, var(--bg-toolbar) 86%, transparent);
|
||||
--fullscreen-bg-icon-detailed: color-mix(in srgb, var(--bg-muted) 75%, transparent);
|
||||
--fullscreen-bg-icon-compact: color-mix(in srgb, var(--bg-muted) 70%, transparent);
|
||||
--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-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);
|
||||
--fullscreen-accent-hover: color-mix(in srgb, var(--text-primary) 20%, var(--border-subtle));
|
||||
--fullscreen-accent-selected: color-mix(in srgb, var(--text-primary) 30%, var(--border-subtle));
|
||||
--fullscreen-accent-ring: color-mix(in srgb, var(--text-primary) 15%, transparent);
|
||||
--fullscreen-accent-list-bg: color-mix(in srgb, var(--text-primary) 8%, var(--bg-toolbar));
|
||||
--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 {
|
||||
@@ -32,7 +36,7 @@
|
||||
transition: width 0.3s ease, max-width 0.3s ease;
|
||||
}
|
||||
|
||||
.tool-panel--legacy-active {
|
||||
.tool-panel--fullscreen-active {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
@@ -55,7 +59,7 @@
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
.tool-panel--legacy {
|
||||
.tool-panel--fullscreen {
|
||||
background: var(--bg-toolbar);
|
||||
}
|
||||
|
||||
@@ -70,7 +74,7 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-surface {
|
||||
.tool-panel__fullscreen-surface {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
@@ -81,7 +85,7 @@
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-surface-inner {
|
||||
.tool-panel__fullscreen-surface-inner {
|
||||
pointer-events: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -91,24 +95,22 @@
|
||||
background:
|
||||
linear-gradient(
|
||||
140deg,
|
||||
var(--legacy-bg-surface-1),
|
||||
var(--legacy-bg-surface-2)
|
||||
var(--fullscreen-bg-surface-1),
|
||||
var(--fullscreen-bg-surface-2)
|
||||
)
|
||||
padding-box;
|
||||
border: 1px solid var(--legacy-border-subtle-75);
|
||||
box-shadow:
|
||||
0 24px 64px var(--legacy-shadow-primary),
|
||||
0 6px 18px var(--legacy-shadow-secondary);
|
||||
border: 1px solid var(--fullscreen-border-subtle-75);
|
||||
box-shadow: none;
|
||||
backdrop-filter: blur(18px);
|
||||
overflow: hidden;
|
||||
animation: tool-panel-legacy-slide-in 0.28s ease forwards;
|
||||
animation: tool-panel-fullscreen-slide-in 0.28s ease forwards;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-surface-inner--exiting {
|
||||
animation: tool-panel-legacy-slide-out 0.22s ease forwards;
|
||||
.tool-panel__fullscreen-surface-inner--exiting {
|
||||
animation: tool-panel-fullscreen-slide-out 0.22s ease forwards;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-header {
|
||||
.tool-panel__fullscreen-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -118,23 +120,23 @@
|
||||
background: var(--bg-toolbar);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-brand {
|
||||
.tool-panel__fullscreen-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-brand-icon {
|
||||
.tool-panel__fullscreen-brand-icon {
|
||||
height: 1.75rem;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-brand-text {
|
||||
.tool-panel__fullscreen-brand-text {
|
||||
height: 1.25rem;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-controls {
|
||||
.tool-panel__fullscreen-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
@@ -143,36 +145,36 @@
|
||||
background: var(--tool-panel-search-bg);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-controls .search-input-container {
|
||||
.tool-panel__fullscreen-controls .search-input-container {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-body {
|
||||
.tool-panel__fullscreen-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-scroll {
|
||||
.tool-panel__fullscreen-scroll {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* legacy group layout */
|
||||
.tool-panel__legacy-groups {
|
||||
/* fullscreen group layout */
|
||||
.tool-panel__fullscreen-groups {
|
||||
padding: 1.5rem 1.75rem;
|
||||
column-width: 18rem;
|
||||
column-gap: 1.5rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-groups--compact {
|
||||
.tool-panel__fullscreen-groups--compact {
|
||||
column-width: 17rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-groups--detailed {
|
||||
.tool-panel__fullscreen-groups--detailed {
|
||||
column-width: auto;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-group {
|
||||
.tool-panel__fullscreen-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
@@ -180,14 +182,14 @@
|
||||
margin: 0 0 1.5rem;
|
||||
padding: 0.65rem 0.75rem 1rem;
|
||||
border-radius: 1rem;
|
||||
background: var(--legacy-bg-group);
|
||||
border: 1px solid var(--legacy-border-subtle-65);
|
||||
box-shadow: 0 14px 32px var(--legacy-shadow-group);
|
||||
background: var(--fullscreen-bg-group);
|
||||
border: 1px solid var(--fullscreen-border-subtle-65);
|
||||
box-shadow: 0 14px 32px var(--fullscreen-shadow-group);
|
||||
break-inside: avoid;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-section-header {
|
||||
.tool-panel__fullscreen-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -195,38 +197,38 @@
|
||||
padding: 0.1rem 0.15rem 0.35rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-section-title {
|
||||
.tool-panel__fullscreen-section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-section-icon {
|
||||
.tool-panel__fullscreen-section-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-grid {
|
||||
.tool-panel__fullscreen-grid {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-grid--detailed {
|
||||
.tool-panel__fullscreen-grid--detailed {
|
||||
grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item {
|
||||
.tool-panel__fullscreen-item {
|
||||
all: unset;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.75rem;
|
||||
align-items: flex-start;
|
||||
padding: 0.85rem 0.95rem;
|
||||
border: 1px solid var(--legacy-border-subtle-70);
|
||||
border: 1px solid var(--fullscreen-border-subtle-70);
|
||||
border-radius: 0.95rem;
|
||||
background: var(--legacy-bg-item);
|
||||
background: var(--fullscreen-bg-item);
|
||||
backdrop-filter: blur(6px);
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
@@ -234,37 +236,37 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item:focus-visible {
|
||||
outline: 2px solid var(--legacy-accent-selected);
|
||||
.tool-panel__fullscreen-item:focus-visible {
|
||||
outline: 2px solid var(--fullscreen-accent-selected);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item[aria-disabled="true"],
|
||||
.tool-panel__legacy-item:disabled {
|
||||
.tool-panel__fullscreen-item[aria-disabled="true"],
|
||||
.tool-panel__fullscreen-item:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item:hover:not([aria-disabled="true"]):not(:disabled) {
|
||||
.tool-panel__fullscreen-item:hover:not([aria-disabled="true"]):not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
border-color: var(--legacy-accent-hover);
|
||||
border-color: var(--fullscreen-accent-hover);
|
||||
box-shadow: var(--shadow-xl, 0 18px 34px rgba(15, 23, 42, 0.14));
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item--selected {
|
||||
border-color: var(--legacy-accent-selected);
|
||||
box-shadow: 0 0 0 2px var(--legacy-accent-ring);
|
||||
.tool-panel__fullscreen-item--selected {
|
||||
border-color: var(--fullscreen-accent-selected);
|
||||
box-shadow: 0 0 0 2px var(--fullscreen-accent-ring);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item--detailed {
|
||||
.tool-panel__fullscreen-item--detailed {
|
||||
min-height: 7.5rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item--with-star {
|
||||
.tool-panel__fullscreen-item--with-star {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-star {
|
||||
.tool-panel__fullscreen-star {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
@@ -273,16 +275,16 @@
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item:hover .tool-panel__legacy-star,
|
||||
.tool-panel__legacy-star:focus {
|
||||
.tool-panel__fullscreen-item:hover .tool-panel__fullscreen-star,
|
||||
.tool-panel__fullscreen-star:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-item--with-star {
|
||||
.tool-panel__fullscreen-list-item--with-star {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-star-compact {
|
||||
.tool-panel__fullscreen-star-compact {
|
||||
position: absolute;
|
||||
top: 0.35rem;
|
||||
right: 0.35rem;
|
||||
@@ -291,36 +293,36 @@
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-item:hover .tool-panel__legacy-star-compact,
|
||||
.tool-panel__legacy-star-compact:focus {
|
||||
.tool-panel__fullscreen-list-item:hover .tool-panel__fullscreen-star-compact,
|
||||
.tool-panel__fullscreen-star-compact:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-group--special {
|
||||
.tool-panel__fullscreen-group--special {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
color-mix(in srgb, var(--legacy-bg-group) 95%, var(--text-primary) 5%),
|
||||
var(--legacy-bg-group)
|
||||
color-mix(in srgb, var(--fullscreen-bg-group) 95%, var(--text-primary) 5%),
|
||||
var(--fullscreen-bg-group)
|
||||
);
|
||||
border-width: 1.5px;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-icon {
|
||||
.tool-panel__fullscreen-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
border-radius: 0.75rem;
|
||||
background: var(--legacy-bg-icon-detailed);
|
||||
color: var(--legacy-text-icon);
|
||||
background: var(--fullscreen-bg-icon-detailed);
|
||||
color: var(--fullscreen-text-icon);
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-icon::before {
|
||||
.tool-panel__fullscreen-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -333,54 +335,54 @@
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item:hover:not([aria-disabled="true"]) .tool-panel__legacy-icon {
|
||||
.tool-panel__fullscreen-item:hover:not([aria-disabled="true"]) .tool-panel__fullscreen-icon {
|
||||
transform: scale(1.08);
|
||||
box-shadow: 0 4px 12px color-mix(in srgb, var(--text-primary) 15%, transparent);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item:hover:not([aria-disabled="true"]) .tool-panel__legacy-icon::before {
|
||||
.tool-panel__fullscreen-item:hover:not([aria-disabled="true"]) .tool-panel__fullscreen-icon::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-icon svg {
|
||||
.tool-panel__fullscreen-icon svg {
|
||||
font-size: 1.65rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-body {
|
||||
.tool-panel__fullscreen-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-name {
|
||||
.tool-panel__fullscreen-name {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-description {
|
||||
.tool-panel__fullscreen-description {
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-match {
|
||||
.tool-panel__fullscreen-match {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-shortcut {
|
||||
.tool-panel__fullscreen-shortcut {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list {
|
||||
.tool-panel__fullscreen-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-item {
|
||||
.tool-panel__fullscreen-list-item {
|
||||
all: unset;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -389,45 +391,45 @@
|
||||
border-radius: 0.65rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease, transform 0.2s ease;
|
||||
background: var(--legacy-bg-list-item);
|
||||
background: var(--fullscreen-bg-list-item);
|
||||
border: 1px solid transparent;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-item[aria-disabled="true"],
|
||||
.tool-panel__legacy-list-item:disabled {
|
||||
.tool-panel__fullscreen-list-item[aria-disabled="true"],
|
||||
.tool-panel__fullscreen-list-item:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-item:hover:not([aria-disabled="true"]):not(:disabled),
|
||||
.tool-panel__legacy-list-item--selected {
|
||||
background: var(--legacy-accent-list-bg);
|
||||
border-color: var(--legacy-accent-list-border);
|
||||
.tool-panel__fullscreen-list-item:hover:not([aria-disabled="true"]):not(:disabled),
|
||||
.tool-panel__fullscreen-list-item--selected {
|
||||
background: var(--fullscreen-accent-list-bg);
|
||||
border-color: var(--fullscreen-accent-list-border);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-item--selected {
|
||||
.tool-panel__fullscreen-list-item--selected {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-icon {
|
||||
.tool-panel__fullscreen-list-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.1rem;
|
||||
height: 2.1rem;
|
||||
border-radius: 0.6rem;
|
||||
background: var(--legacy-bg-icon-compact);
|
||||
color: var(--legacy-text-icon-compact);
|
||||
background: var(--fullscreen-bg-icon-compact);
|
||||
color: var(--fullscreen-text-icon-compact);
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-icon::before {
|
||||
.tool-panel__fullscreen-list-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -440,21 +442,21 @@
|
||||
border-radius: 0.6rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-item:hover:not([aria-disabled="true"]) .tool-panel__legacy-list-icon {
|
||||
.tool-panel__fullscreen-list-item:hover:not([aria-disabled="true"]) .tool-panel__fullscreen-list-icon {
|
||||
transform: scale(1.06);
|
||||
box-shadow: 0 2px 8px color-mix(in srgb, var(--text-primary) 12%, transparent);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-item:hover:not([aria-disabled="true"]) .tool-panel__legacy-list-icon::before {
|
||||
.tool-panel__fullscreen-list-item:hover:not([aria-disabled="true"]) .tool-panel__fullscreen-list-icon::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-icon svg {
|
||||
.tool-panel__fullscreen-list-icon svg {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-list-body {
|
||||
.tool-panel__fullscreen-list-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
@@ -462,14 +464,14 @@
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-empty {
|
||||
.tool-panel__fullscreen-empty {
|
||||
padding: 2rem 1.75rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
@keyframes tool-panel-legacy-slide-in {
|
||||
@keyframes tool-panel-fullscreen-slide-in {
|
||||
from {
|
||||
transform: translateX(-6%) scaleX(0.85);
|
||||
opacity: 0;
|
||||
@@ -480,7 +482,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes tool-panel-legacy-slide-out {
|
||||
@keyframes tool-panel-fullscreen-slide-out {
|
||||
from {
|
||||
transform: translateX(0) scaleX(1);
|
||||
opacity: 1;
|
||||
@@ -492,7 +494,7 @@
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.tool-panel__legacy-surface-inner {
|
||||
.tool-panel__fullscreen-surface-inner {
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
@@ -500,33 +502,33 @@
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item,
|
||||
.tool-panel__legacy-list-item {
|
||||
.tool-panel__fullscreen-item,
|
||||
.tool-panel__fullscreen-list-item {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1440px) {
|
||||
.tool-panel__legacy-content {
|
||||
.tool-panel__fullscreen-content {
|
||||
padding-inline: 1.5rem;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-grid--compact {
|
||||
.tool-panel__fullscreen-grid--compact {
|
||||
column-width: 15rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.tool-panel__legacy-controls {
|
||||
.tool-panel__fullscreen-controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-controls .mantine-Switch-root {
|
||||
.tool-panel__fullscreen-controls .mantine-Switch-root {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-grid--compact {
|
||||
.tool-panel__fullscreen-grid--compact {
|
||||
column-width: 14rem;
|
||||
}
|
||||
}
|
||||
@@ -534,38 +536,39 @@
|
||||
/* Dynamic settings support */
|
||||
|
||||
/* No border variant */
|
||||
.tool-panel__legacy-item--no-border,
|
||||
.tool-panel__legacy-list-item--no-border {
|
||||
.tool-panel__fullscreen-item--no-border,
|
||||
.tool-panel__fullscreen-list-item--no-border {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* Hover intensity variants */
|
||||
.tool-panel__legacy-item--hover-subtle:hover,
|
||||
.tool-panel__legacy-list-item--hover-subtle:hover {
|
||||
background: var(--legacy-hover-subtle);
|
||||
.tool-panel__fullscreen-item--hover-subtle:hover,
|
||||
.tool-panel__fullscreen-list-item--hover-subtle:hover {
|
||||
background: var(--fullscreen-hover-subtle);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item--hover-moderate:hover,
|
||||
.tool-panel__legacy-list-item--hover-moderate:hover {
|
||||
background: var(--legacy-hover);
|
||||
.tool-panel__fullscreen-item--hover-moderate:hover,
|
||||
.tool-panel__fullscreen-list-item--hover-moderate:hover {
|
||||
background: var(--fullscreen-hover);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item--hover-prominent:hover,
|
||||
.tool-panel__legacy-list-item--hover-prominent:hover {
|
||||
background: var(--legacy-hover-prominent);
|
||||
.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__legacy-icon--hover-bg,
|
||||
.tool-panel__legacy-list-icon--hover-bg {
|
||||
.tool-panel__fullscreen-icon--hover-bg,
|
||||
.tool-panel__fullscreen-list-icon--hover-bg {
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.tool-panel__legacy-item:hover .tool-panel__legacy-icon--hover-bg,
|
||||
.tool-panel__legacy-list-item:hover .tool-panel__legacy-list-icon--hover-bg {
|
||||
background: var(--legacy-icon-hover-bg) !important;
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useRainbowThemeContext } from '../shared/RainbowThemeProvider';
|
||||
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
|
||||
import ToolPicker from './ToolPicker';
|
||||
@@ -13,9 +13,10 @@ import { useMediaQuery } from '@mantine/hooks';
|
||||
import ViewSidebarRoundedIcon from '@mui/icons-material/ViewSidebarRounded';
|
||||
import DashboardCustomizeRoundedIcon from '@mui/icons-material/DashboardCustomizeRounded';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import LegacyToolSurface from './LegacyToolSurface';
|
||||
import FullscreenToolSurface from './FullscreenToolSurface';
|
||||
import { useToolPanelGeometry } from '../../hooks/tools/useToolPanelGeometry';
|
||||
import { useLocalStorageState } from '../../hooks/tools/useLocalStorageState';
|
||||
import { useRightRail } from '../../contexts/RightRailContext';
|
||||
import './ToolPanel.css';
|
||||
|
||||
// No props needed - component uses context
|
||||
@@ -40,28 +41,49 @@ export default function ToolPanel() {
|
||||
toolPanelMode,
|
||||
setToolPanelMode,
|
||||
setLeftPanelView,
|
||||
readerMode,
|
||||
} = useToolWorkflow();
|
||||
|
||||
const isLegacyMode = toolPanelMode === 'legacy';
|
||||
const legacyExpanded = isLegacyMode && leftPanelView === 'toolPicker' && !isMobile;
|
||||
const { setAllRightRailButtonsDisabled } = useRightRail();
|
||||
|
||||
const isFullscreenMode = toolPanelMode === 'fullscreen';
|
||||
const toolPickerVisible = !readerMode;
|
||||
const fullscreenExpanded = isFullscreenMode && leftPanelView === 'toolPicker' && !isMobile && toolPickerVisible;
|
||||
|
||||
|
||||
// Debug logging for troubleshooting
|
||||
React.useEffect(() => {
|
||||
console.log('ToolPanel debug:', {
|
||||
isFullscreenMode,
|
||||
leftPanelView,
|
||||
isMobile,
|
||||
toolPickerVisible,
|
||||
fullscreenExpanded
|
||||
});
|
||||
}, [isFullscreenMode, leftPanelView, isMobile, toolPickerVisible, fullscreenExpanded]);
|
||||
|
||||
// Disable right rail buttons when fullscreen mode is active
|
||||
React.useEffect(() => {
|
||||
setAllRightRailButtonsDisabled(fullscreenExpanded);
|
||||
}, [fullscreenExpanded, setAllRightRailButtonsDisabled]);
|
||||
|
||||
// Use custom hooks for state management
|
||||
const [showLegacyDescriptions, setShowLegacyDescriptions] = useLocalStorageState('legacyToolDescriptions', false);
|
||||
const legacyGeometry = useToolPanelGeometry({
|
||||
enabled: legacyExpanded,
|
||||
const fullscreenGeometry = useToolPanelGeometry({
|
||||
enabled: fullscreenExpanded,
|
||||
toolPanelRef,
|
||||
quickAccessRef,
|
||||
});
|
||||
|
||||
const toggleLabel = isLegacyMode
|
||||
const toggleLabel = isFullscreenMode
|
||||
? t('toolPanel.toggle.sidebar', 'Switch to sidebar mode')
|
||||
: t('toolPanel.toggle.legacy', 'Switch to legacy mode');
|
||||
: t('toolPanel.toggle.fullscreen', 'Switch to fullscreen mode');
|
||||
|
||||
const handleModeToggle = () => {
|
||||
const nextMode = isLegacyMode ? 'sidebar' : 'legacy';
|
||||
const nextMode = isFullscreenMode ? 'sidebar' : 'fullscreen';
|
||||
setToolPanelMode(nextMode);
|
||||
|
||||
if (nextMode === 'legacy' && leftPanelView !== 'toolPicker') {
|
||||
if (nextMode === 'fullscreen' && leftPanelView !== 'toolPicker') {
|
||||
setLeftPanelView('toolPicker');
|
||||
}
|
||||
};
|
||||
@@ -92,15 +114,15 @@ export default function ToolPanel() {
|
||||
<div
|
||||
ref={toolPanelRef}
|
||||
data-sidebar="tool-panel"
|
||||
className={`tool-panel flex flex-col ${legacyExpanded ? 'tool-panel--legacy-active' : 'overflow-hidden'} bg-[var(--bg-toolbar)] border-r border-[var(--border-subtle)] transition-all duration-300 ease-out ${
|
||||
className={`tool-panel flex flex-col ${fullscreenExpanded ? 'tool-panel--fullscreen-active' : 'overflow-hidden'} bg-[var(--bg-toolbar)] border-r border-[var(--border-subtle)] transition-all duration-300 ease-out ${
|
||||
isRainbowMode ? rainbowStyles.rainbowPaper : ''
|
||||
} ${isMobile ? 'h-full border-r-0' : 'h-screen'} ${legacyExpanded ? 'tool-panel--legacy' : ''}`}
|
||||
} ${isMobile ? 'h-full border-r-0' : 'h-screen'} ${fullscreenExpanded ? 'tool-panel--fullscreen' : ''}`}
|
||||
style={{
|
||||
width: computedWidth(),
|
||||
padding: '0'
|
||||
}}
|
||||
>
|
||||
{!legacyExpanded && (
|
||||
{!fullscreenExpanded && (
|
||||
<div
|
||||
style={{
|
||||
opacity: isMobile || isPanelVisible ? 1 : 0,
|
||||
@@ -123,17 +145,26 @@ export default function ToolPanel() {
|
||||
toolRegistry={toolRegistry}
|
||||
mode="filter"
|
||||
/>
|
||||
{!isMobile && (
|
||||
<Tooltip label={toggleLabel} position="bottom" withArrow>
|
||||
{!isMobile && leftPanelView === 'toolPicker' && (
|
||||
<Tooltip
|
||||
label={toggleLabel}
|
||||
position="bottom"
|
||||
withArrow
|
||||
styles={{
|
||||
tooltip: {
|
||||
zIndex: 1400, // Higher than fullscreen surface
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
radius="xl"
|
||||
color="gray"
|
||||
style={{ color: 'var(--right-rail-icon)' }}
|
||||
onClick={handleModeToggle}
|
||||
aria-label={toggleLabel}
|
||||
className="tool-panel__mode-toggle"
|
||||
>
|
||||
{isLegacyMode ? (
|
||||
{isFullscreenMode ? (
|
||||
<ViewSidebarRoundedIcon fontSize="small" />
|
||||
) : (
|
||||
<DashboardCustomizeRoundedIcon fontSize="small" />
|
||||
@@ -143,23 +174,23 @@ export default function ToolPanel() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{searchQuery.trim().length > 0 ? (
|
||||
<div className="flex-1 flex flex-col overflow-y-auto">
|
||||
<SearchResults
|
||||
filteredTools={filteredTools}
|
||||
onSelect={(id) => handleToolSelect(id as ToolId)}
|
||||
searchQuery={searchQuery}
|
||||
/>
|
||||
</div>
|
||||
) : leftPanelView === 'toolPicker' ? (
|
||||
<div className="flex-1 flex flex-col overflow-auto">
|
||||
<ToolPicker
|
||||
selectedToolKey={selectedToolKey}
|
||||
onSelect={(id) => handleToolSelect(id as ToolId)}
|
||||
filteredTools={filteredTools}
|
||||
isSearching={Boolean(searchQuery && searchQuery.trim().length > 0)}
|
||||
/>
|
||||
</div>
|
||||
{searchQuery.trim().length > 0 ? (
|
||||
<div className="flex-1 flex flex-col overflow-y-auto">
|
||||
<SearchResults
|
||||
filteredTools={filteredTools}
|
||||
onSelect={(id) => handleToolSelect(id as ToolId)}
|
||||
searchQuery={searchQuery}
|
||||
/>
|
||||
</div>
|
||||
) : leftPanelView === 'toolPicker' ? (
|
||||
<div className="flex-1 flex flex-col overflow-auto">
|
||||
<ToolPicker
|
||||
selectedToolKey={selectedToolKey}
|
||||
onSelect={(id) => handleToolSelect(id as ToolId)}
|
||||
filteredTools={filteredTools}
|
||||
isSearching={Boolean(searchQuery && searchQuery.trim().length > 0)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
@@ -181,8 +212,8 @@ export default function ToolPanel() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{legacyExpanded && (
|
||||
<LegacyToolSurface
|
||||
{fullscreenExpanded && (
|
||||
<FullscreenToolSurface
|
||||
searchQuery={searchQuery}
|
||||
toolRegistry={toolRegistry}
|
||||
filteredTools={filteredTools}
|
||||
@@ -190,11 +221,11 @@ export default function ToolPanel() {
|
||||
showDescriptions={showLegacyDescriptions}
|
||||
matchedTextMap={matchedTextMap}
|
||||
onSearchChange={setSearchQuery}
|
||||
onSelect={(id) => handleToolSelect(id as ToolId)}
|
||||
onSelect={(id: ToolId) => handleToolSelect(id)}
|
||||
onToggleDescriptions={() => setShowLegacyDescriptions((prev) => !prev)}
|
||||
onExitLegacyMode={() => setToolPanelMode('sidebar')}
|
||||
onExitFullscreenMode={() => setToolPanelMode('sidebar')}
|
||||
toggleLabel={toggleLabel}
|
||||
geometry={legacyGeometry}
|
||||
geometry={fullscreenGeometry}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -136,6 +136,39 @@
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.tool-panel-mode-prompt__preview--fullscreen {
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 0.65rem 0.6rem;
|
||||
}
|
||||
|
||||
.tool-panel-mode-prompt__fullscreen-columns {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 0.45rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.tool-panel-mode-prompt__fullscreen-column {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.tool-panel-mode-prompt__fullscreen-card {
|
||||
border-radius: 0.45rem;
|
||||
border: 1px solid color-mix(in srgb, var(--border-subtle) 55%, transparent);
|
||||
background: linear-gradient(150deg,
|
||||
color-mix(in srgb, var(--bg-muted) 88%, transparent),
|
||||
color-mix(in srgb, var(--bg-background) 76%, transparent)
|
||||
);
|
||||
height: 1.2rem;
|
||||
}
|
||||
|
||||
.tool-panel-mode-prompt__fullscreen-card--muted {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.tool-panel-mode-prompt__card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useToolWorkflow, TOOL_PANEL_MODE_STORAGE_KEY } from '../../contexts/ToolWorkflowContext';
|
||||
import './ToolPanelModePrompt.css';
|
||||
|
||||
type ToolPanelModeOption = 'sidebar' | 'legacy';
|
||||
type ToolPanelModeOption = 'sidebar' | 'fullscreen';
|
||||
|
||||
const PROMPT_SEEN_KEY = 'toolPanelModePromptSeen';
|
||||
|
||||
@@ -72,10 +72,10 @@ const ToolPanelModePrompt = () => {
|
||||
<Stack gap="md" className="tool-panel-mode-prompt__card-content">
|
||||
<Group justify="space-between">
|
||||
<Stack gap={2}>
|
||||
<Text fw={600}>{t('toolPanel.modePrompt.sidebarTitle', 'Advanced sidebar')}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('toolPanel.modePrompt.sidebarDescription', 'Keep tools alongside your workspace for quick switching.')}
|
||||
</Text>
|
||||
<Text fw={600}>{t('toolPanel.modePrompt.sidebarTitle', 'Sidebar mode')}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('toolPanel.modePrompt.sidebarDescription', 'Keep tools alongside your workspace for quick switching.')}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Badge color="blue" variant="filled">
|
||||
{t('toolPanel.modePrompt.recommended', 'Recommended')}
|
||||
@@ -101,45 +101,45 @@ const ToolPanelModePrompt = () => {
|
||||
className="tool-panel-mode-prompt__action"
|
||||
onClick={() => handleSelect('sidebar')}
|
||||
>
|
||||
{t('toolPanel.modePrompt.chooseSidebar', 'Use advanced sidebar')}
|
||||
{t('toolPanel.modePrompt.chooseSidebar', 'Use sidebar mode')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
<Card withBorder radius="lg" shadow="xs" padding="lg" className="tool-panel-mode-prompt__card">
|
||||
<Stack gap="md" className="tool-panel-mode-prompt__card-content">
|
||||
<Stack gap={2}>
|
||||
<Text fw={600}>{t('toolPanel.modePrompt.legacyTitle', 'Legacy fullscreen')}</Text>
|
||||
<Text fw={600}>{t('toolPanel.modePrompt.fullscreenTitle', 'Fullscreen mode - (legacy)')}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('toolPanel.modePrompt.legacyDescription', 'Browse every tool in a catalogue that covers the workspace until you pick one.')}
|
||||
{t('toolPanel.modePrompt.fullscreenDescription', 'Browse every tool in a catalogue that covers the workspace until you pick one.')}
|
||||
</Text>
|
||||
</Stack>
|
||||
<div className="tool-panel-mode-prompt__preview tool-panel-mode-prompt__preview--legacy" aria-hidden>
|
||||
<div className="tool-panel-mode-prompt__legacy-columns">
|
||||
<div className="tool-panel-mode-prompt__legacy-column">
|
||||
<span className="tool-panel-mode-prompt__legacy-card" />
|
||||
<span className="tool-panel-mode-prompt__legacy-card" />
|
||||
<span className="tool-panel-mode-prompt__legacy-card tool-panel-mode-prompt__legacy-card--muted" />
|
||||
<div className="tool-panel-mode-prompt__preview tool-panel-mode-prompt__preview--fullscreen" aria-hidden>
|
||||
<div className="tool-panel-mode-prompt__fullscreen-columns">
|
||||
<div className="tool-panel-mode-prompt__fullscreen-column">
|
||||
<span className="tool-panel-mode-prompt__fullscreen-card" />
|
||||
<span className="tool-panel-mode-prompt__fullscreen-card" />
|
||||
<span className="tool-panel-mode-prompt__fullscreen-card tool-panel-mode-prompt__fullscreen-card--muted" />
|
||||
</div>
|
||||
<div className="tool-panel-mode-prompt__legacy-column">
|
||||
<span className="tool-panel-mode-prompt__legacy-card" />
|
||||
<span className="tool-panel-mode-prompt__legacy-card" />
|
||||
<span className="tool-panel-mode-prompt__legacy-card tool-panel-mode-prompt__legacy-card--muted" />
|
||||
<div className="tool-panel-mode-prompt__fullscreen-column">
|
||||
<span className="tool-panel-mode-prompt__fullscreen-card" />
|
||||
<span className="tool-panel-mode-prompt__fullscreen-card" />
|
||||
<span className="tool-panel-mode-prompt__fullscreen-card tool-panel-mode-prompt__fullscreen-card--muted" />
|
||||
</div>
|
||||
<div className="tool-panel-mode-prompt__legacy-column">
|
||||
<span className="tool-panel-mode-prompt__legacy-card" />
|
||||
<span className="tool-panel-mode-prompt__legacy-card" />
|
||||
<span className="tool-panel-mode-prompt__legacy-card tool-panel-mode-prompt__legacy-card--muted" />
|
||||
<div className="tool-panel-mode-prompt__fullscreen-column">
|
||||
<span className="tool-panel-mode-prompt__fullscreen-card" />
|
||||
<span className="tool-panel-mode-prompt__fullscreen-card" />
|
||||
<span className="tool-panel-mode-prompt__fullscreen-card tool-panel-mode-prompt__fullscreen-card--muted" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant={toolPanelMode === 'legacy' ? 'filled' : 'outline'}
|
||||
variant={toolPanelMode === 'fullscreen' ? 'filled' : 'outline'}
|
||||
color="blue"
|
||||
radius="md"
|
||||
className="tool-panel-mode-prompt__action"
|
||||
onClick={() => handleSelect('legacy')}
|
||||
onClick={() => handleSelect('fullscreen')}
|
||||
>
|
||||
{t('toolPanel.modePrompt.chooseLegacy', 'Use legacy fullscreen')}
|
||||
{t('toolPanel.modePrompt.chooseFullscreen', 'Use fullscreen mode')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
@@ -6,6 +6,7 @@ import "./toolPicker/ToolPicker.css";
|
||||
import { useToolSections } from "../../hooks/useToolSections";
|
||||
import NoToolsFound from "./shared/NoToolsFound";
|
||||
import { renderToolButtons } from "./shared/renderToolButtons";
|
||||
import Badge from "../shared/Badge";
|
||||
|
||||
interface ToolPickerProps {
|
||||
selectedToolKey: string | null;
|
||||
@@ -115,7 +116,7 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
||||
{searchGroups.length === 0 ? (
|
||||
<NoToolsFound />
|
||||
) : (
|
||||
searchGroups.map(group => renderToolButtons(t, group, selectedToolKey, onSelect))
|
||||
searchGroups.map(group => renderToolButtons(t, group, selectedToolKey, onSelect, true, false, filteredTools))
|
||||
)}
|
||||
</Stack>
|
||||
) : (
|
||||
@@ -142,25 +143,16 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
||||
onClick={() => scrollTo(quickAccessRef)}
|
||||
>
|
||||
<span style={{ fontSize: "1rem" }}>{t("toolPicker.quickAccess", "QUICK ACCESS")}</span>
|
||||
<span
|
||||
style={{
|
||||
background: "var(--tool-header-badge-bg)",
|
||||
color: "var(--tool-header-badge-text)",
|
||||
borderRadius: ".5rem",
|
||||
padding: "0.125rem 0.5rem",
|
||||
fontSize: ".75rem",
|
||||
fontWeight: 700
|
||||
}}
|
||||
>
|
||||
<Badge>
|
||||
{quickSection?.subcategories.reduce((acc, sc) => acc + sc.tools.length, 0)}
|
||||
</span>
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<Box ref={quickAccessRef} w="100%" my="sm">
|
||||
<Stack p="sm" gap="xs">
|
||||
{quickSection?.subcategories.map(sc =>
|
||||
renderToolButtons(t, sc, selectedToolKey, onSelect, false)
|
||||
)}
|
||||
{quickSection?.subcategories.map(sc =>
|
||||
renderToolButtons(t, sc, selectedToolKey, onSelect, false, false)
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</>
|
||||
@@ -188,25 +180,16 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
||||
onClick={() => scrollTo(allToolsRef)}
|
||||
>
|
||||
<span style={{ fontSize: "1rem" }}>{t("toolPicker.allTools", "ALL TOOLS")}</span>
|
||||
<span
|
||||
style={{
|
||||
background: "var(--tool-header-badge-bg)",
|
||||
color: "var(--tool-header-badge-text)",
|
||||
borderRadius: ".5rem",
|
||||
padding: "0.125rem 0.5rem",
|
||||
fontSize: ".75rem",
|
||||
fontWeight: 700
|
||||
}}
|
||||
>
|
||||
<Badge>
|
||||
{allSection?.subcategories.reduce((acc, sc) => acc + sc.tools.length, 0)}
|
||||
</span>
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<Box ref={allToolsRef} w="100%">
|
||||
<Stack p="sm" gap="xs">
|
||||
{allSection?.subcategories.map(sc =>
|
||||
renderToolButtons(t, sc, selectedToolKey, onSelect, true)
|
||||
)}
|
||||
{allSection?.subcategories.map(sc =>
|
||||
renderToolButtons(t, sc, selectedToolKey, onSelect, true, false)
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</>
|
||||
|
||||
@@ -30,20 +30,20 @@ export const renderToolButtons = (
|
||||
<SubcategoryHeader label={getSubcategoryLabel(t, subcategory.subcategoryId)} />
|
||||
)}
|
||||
<div>
|
||||
{subcategory.tools.map(({ id, tool }) => {
|
||||
const matchedSynonym = matchedTextMap.get(id);
|
||||
|
||||
return (
|
||||
<ToolButton
|
||||
key={id}
|
||||
id={id}
|
||||
tool={tool}
|
||||
isSelected={selectedToolKey === id}
|
||||
onSelect={onSelect}
|
||||
disableNavigation={disableNavigation}
|
||||
matchedSynonym={matchedSynonym}
|
||||
/>
|
||||
);
|
||||
{subcategory.tools.map(({ id, tool }, index) => {
|
||||
const matchedSynonym = matchedTextMap.get(id);
|
||||
|
||||
return (
|
||||
<ToolButton
|
||||
key={id}
|
||||
id={id}
|
||||
tool={tool}
|
||||
isSelected={selectedToolKey === id}
|
||||
onSelect={onSelect}
|
||||
disableNavigation={disableNavigation}
|
||||
matchedSynonym={matchedSynonym}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
@@ -103,7 +103,11 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
justify="flex-start"
|
||||
className="tool-button"
|
||||
styles={{
|
||||
root: { borderRadius: 0, color: "var(--tools-text-and-icon-color)", overflow: 'visible' },
|
||||
root: {
|
||||
borderRadius: 0,
|
||||
color: "var(--tools-text-and-icon-color)",
|
||||
overflow: 'visible'
|
||||
},
|
||||
label: { overflow: 'visible' }
|
||||
}}
|
||||
>
|
||||
@@ -124,7 +128,11 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
justify="flex-start"
|
||||
className="tool-button"
|
||||
styles={{
|
||||
root: { borderRadius: 0, color: "var(--tools-text-and-icon-color)", overflow: 'visible' },
|
||||
root: {
|
||||
borderRadius: 0,
|
||||
color: "var(--tools-text-and-icon-color)",
|
||||
overflow: 'visible'
|
||||
},
|
||||
label: { overflow: 'visible' }
|
||||
}}
|
||||
>
|
||||
@@ -141,7 +149,15 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
|
||||
justify="flex-start"
|
||||
className="tool-button"
|
||||
aria-disabled={isUnavailable}
|
||||
styles={{ root: { borderRadius: 0, color: "var(--tools-text-and-icon-color)", cursor: isUnavailable ? 'not-allowed' : undefined, overflow: 'visible' }, label: { overflow: 'visible' } }}
|
||||
styles={{
|
||||
root: {
|
||||
borderRadius: 0,
|
||||
color: "var(--tools-text-and-icon-color)",
|
||||
cursor: isUnavailable ? 'not-allowed' : undefined,
|
||||
overflow: 'visible'
|
||||
},
|
||||
label: { overflow: 'visible' }
|
||||
}}
|
||||
>
|
||||
{buttonContent}
|
||||
</Button>
|
||||
|
||||
@@ -20,7 +20,7 @@ interface ToolSearchProps {
|
||||
autoFocus?: boolean;
|
||||
}
|
||||
|
||||
const ToolSearch = ({
|
||||
const ToolSearch: React.FC<ToolSearchProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
toolRegistry,
|
||||
@@ -31,7 +31,7 @@ const ToolSearch = ({
|
||||
hideIcon = false,
|
||||
onFocus,
|
||||
autoFocus = false,
|
||||
}: ToolSearchProps) => {
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const searchRef = useRef<HTMLInputElement>(null);
|
||||
@@ -81,15 +81,15 @@ const ToolSearch = ({
|
||||
}, [autoFocus]);
|
||||
|
||||
const searchInput = (
|
||||
<TextInput
|
||||
ref={searchRef}
|
||||
value={value}
|
||||
onChange={handleSearchChange}
|
||||
placeholder={placeholder || t("toolPicker.searchPlaceholder", "Search tools...")}
|
||||
icon={hideIcon ? undefined : <LocalIcon icon="search-rounded" width="1.5rem" height="1.5rem" />}
|
||||
autoComplete="off"
|
||||
onFocus={onFocus}
|
||||
/>
|
||||
<TextInput
|
||||
ref={searchRef}
|
||||
value={value}
|
||||
onChange={handleSearchChange}
|
||||
placeholder={placeholder || t("toolPicker.searchPlaceholder", "Search tools...")}
|
||||
icon={hideIcon ? undefined : <LocalIcon icon="search-rounded" width="1.5rem" height="1.5rem" />}
|
||||
autoComplete="off"
|
||||
onFocus={onFocus}
|
||||
/>
|
||||
);
|
||||
|
||||
if (mode === "filter") {
|
||||
|
||||
@@ -4,9 +4,11 @@ import { RightRailAction, RightRailButtonConfig } from '../types/rightRail';
|
||||
interface RightRailContextValue {
|
||||
buttons: RightRailButtonConfig[];
|
||||
actions: Record<string, RightRailAction>;
|
||||
allButtonsDisabled: boolean;
|
||||
registerButtons: (buttons: RightRailButtonConfig[]) => void;
|
||||
unregisterButtons: (ids: string[]) => void;
|
||||
setAction: (id: string, action: RightRailAction) => void;
|
||||
setAllRightRailButtonsDisabled: (disabled: boolean) => void;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
@@ -15,6 +17,7 @@ const RightRailContext = createContext<RightRailContextValue | undefined>(undefi
|
||||
export function RightRailProvider({ children }: { children: React.ReactNode }) {
|
||||
const [buttons, setButtons] = useState<RightRailButtonConfig[]>([]);
|
||||
const [actions, setActions] = useState<Record<string, RightRailAction>>({});
|
||||
const [allButtonsDisabled, setAllButtonsDisabled] = useState<boolean>(false);
|
||||
|
||||
const registerButtons = useCallback((newButtons: RightRailButtonConfig[]) => {
|
||||
setButtons(prev => {
|
||||
@@ -43,12 +46,25 @@ export function RightRailProvider({ children }: { children: React.ReactNode }) {
|
||||
setActions(prev => ({ ...prev, [id]: action }));
|
||||
}, []);
|
||||
|
||||
const setAllRightRailButtonsDisabled = useCallback((disabled: boolean) => {
|
||||
setAllButtonsDisabled(disabled);
|
||||
}, []);
|
||||
|
||||
const clear = useCallback(() => {
|
||||
setButtons([]);
|
||||
setActions({});
|
||||
}, []);
|
||||
|
||||
const value = useMemo<RightRailContextValue>(() => ({ buttons, actions, registerButtons, unregisterButtons, setAction, clear }), [buttons, actions, registerButtons, unregisterButtons, setAction, clear]);
|
||||
const value = useMemo<RightRailContextValue>(() => ({
|
||||
buttons,
|
||||
actions,
|
||||
allButtonsDisabled,
|
||||
registerButtons,
|
||||
unregisterButtons,
|
||||
setAction,
|
||||
setAllRightRailButtonsDisabled,
|
||||
clear
|
||||
}), [buttons, actions, allButtonsDisabled, registerButtons, unregisterButtons, setAction, setAllRightRailButtonsDisabled, clear]);
|
||||
|
||||
return (
|
||||
<RightRailContext.Provider value={value}>
|
||||
|
||||
@@ -13,10 +13,10 @@ import { useNavigationUrlSync } from '../hooks/useUrlSync';
|
||||
import { getDefaultWorkbench } from '../types/workbench';
|
||||
import { filterToolRegistryByQuery } from '../utils/toolSearch';
|
||||
import { useToolHistory } from '../hooks/tools/useToolHistory';
|
||||
import { LegacyToolStyleSettings, defaultLegacyToolSettings } from '../components/tools/LegacyToolSettings';
|
||||
import { FullscreenToolStyleSettings, defaultFullscreenToolSettings } from '../components/tools/FullscreenToolSettings';
|
||||
|
||||
// State interface
|
||||
type ToolPanelMode = 'sidebar' | 'legacy';
|
||||
type ToolPanelMode = 'sidebar' | 'fullscreen';
|
||||
|
||||
interface ToolWorkflowState {
|
||||
// UI State
|
||||
@@ -24,7 +24,7 @@ interface ToolWorkflowState {
|
||||
leftPanelView: 'toolPicker' | 'toolContent' | 'hidden';
|
||||
readerMode: boolean;
|
||||
toolPanelMode: ToolPanelMode;
|
||||
legacyToolSettings: LegacyToolStyleSettings;
|
||||
fullscreenToolSettings: FullscreenToolStyleSettings;
|
||||
|
||||
// File/Preview State
|
||||
previewFile: File | null;
|
||||
@@ -40,7 +40,7 @@ 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_LEGACY_TOOL_SETTINGS'; payload: LegacyToolStyleSettings }
|
||||
| { 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 }
|
||||
@@ -48,6 +48,7 @@ type ToolWorkflowAction =
|
||||
|
||||
// Initial state
|
||||
export const TOOL_PANEL_MODE_STORAGE_KEY = 'toolPanelModePreference';
|
||||
export const FULLSCREEN_TOOL_SETTINGS_STORAGE_KEY = 'fullscreenToolStyleSettings';
|
||||
export const LEGACY_TOOL_SETTINGS_STORAGE_KEY = 'legacyToolStyleSettings';
|
||||
|
||||
const getStoredToolPanelMode = (): ToolPanelMode => {
|
||||
@@ -57,30 +58,34 @@ const getStoredToolPanelMode = (): ToolPanelMode => {
|
||||
|
||||
const stored = window.localStorage.getItem(TOOL_PANEL_MODE_STORAGE_KEY);
|
||||
if (stored === 'legacy' || stored === 'fullscreen') {
|
||||
return 'legacy';
|
||||
return 'fullscreen';
|
||||
}
|
||||
|
||||
return 'sidebar';
|
||||
};
|
||||
|
||||
const getStoredLegacyToolSettings = (): LegacyToolStyleSettings => {
|
||||
const getStoredFullscreenToolSettings = (): FullscreenToolStyleSettings => {
|
||||
if (typeof window === 'undefined') {
|
||||
return defaultLegacyToolSettings;
|
||||
return defaultFullscreenToolSettings;
|
||||
}
|
||||
|
||||
try {
|
||||
const stored = window.localStorage.getItem(LEGACY_TOOL_SETTINGS_STORAGE_KEY);
|
||||
if (stored) {
|
||||
return { ...defaultLegacyToolSettings, ...JSON.parse(stored) };
|
||||
const storedNew = window.localStorage.getItem(FULLSCREEN_TOOL_SETTINGS_STORAGE_KEY);
|
||||
if (storedNew) {
|
||||
return { ...defaultFullscreenToolSettings, ...JSON.parse(storedNew) };
|
||||
}
|
||||
const storedLegacy = window.localStorage.getItem(LEGACY_TOOL_SETTINGS_STORAGE_KEY);
|
||||
if (storedLegacy) {
|
||||
return { ...defaultFullscreenToolSettings, ...JSON.parse(storedLegacy) };
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse legacy tool settings:', e);
|
||||
console.error('Failed to parse fullscreen tool settings:', e);
|
||||
}
|
||||
|
||||
return defaultLegacyToolSettings;
|
||||
return defaultFullscreenToolSettings;
|
||||
};
|
||||
|
||||
const baseState: Omit<ToolWorkflowState, 'toolPanelMode' | 'legacyToolSettings'> = {
|
||||
const baseState: Omit<ToolWorkflowState, 'toolPanelMode' | 'fullscreenToolSettings'> = {
|
||||
sidebarsVisible: true,
|
||||
leftPanelView: 'toolPicker',
|
||||
readerMode: false,
|
||||
@@ -92,7 +97,7 @@ const baseState: Omit<ToolWorkflowState, 'toolPanelMode' | 'legacyToolSettings'>
|
||||
const createInitialState = (): ToolWorkflowState => ({
|
||||
...baseState,
|
||||
toolPanelMode: getStoredToolPanelMode(),
|
||||
legacyToolSettings: getStoredLegacyToolSettings(),
|
||||
fullscreenToolSettings: getStoredFullscreenToolSettings(),
|
||||
});
|
||||
|
||||
// Reducer
|
||||
@@ -106,8 +111,8 @@ function toolWorkflowReducer(state: ToolWorkflowState, action: ToolWorkflowActio
|
||||
return { ...state, readerMode: action.payload };
|
||||
case 'SET_TOOL_PANEL_MODE':
|
||||
return { ...state, toolPanelMode: action.payload };
|
||||
case 'SET_LEGACY_TOOL_SETTINGS':
|
||||
return { ...state, legacyToolSettings: 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':
|
||||
@@ -118,7 +123,7 @@ function toolWorkflowReducer(state: ToolWorkflowState, action: ToolWorkflowActio
|
||||
return {
|
||||
...baseState,
|
||||
toolPanelMode: state.toolPanelMode,
|
||||
legacyToolSettings: state.legacyToolSettings,
|
||||
fullscreenToolSettings: state.fullscreenToolSettings,
|
||||
searchQuery: state.searchQuery,
|
||||
};
|
||||
default:
|
||||
@@ -139,12 +144,12 @@ interface ToolWorkflowContextValue extends ToolWorkflowState {
|
||||
setLeftPanelView: (view: 'toolPicker' | 'toolContent' | 'hidden') => void;
|
||||
setReaderMode: (mode: boolean) => void;
|
||||
setToolPanelMode: (mode: ToolPanelMode) => void;
|
||||
setLegacyToolSettings: (settings: LegacyToolStyleSettings) => void;
|
||||
setFullscreenToolSettings: (settings: FullscreenToolStyleSettings) => void;
|
||||
setPreviewFile: (file: File | null) => void;
|
||||
setPageEditorFunctions: (functions: PageEditorFunctions | null) => void;
|
||||
setSearchQuery: (query: string) => void;
|
||||
|
||||
// Tool Actions
|
||||
|
||||
selectTool: (toolId: ToolId | null) => void;
|
||||
clearToolSelection: () => void;
|
||||
|
||||
@@ -226,8 +231,9 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
||||
dispatch({ type: 'SET_TOOL_PANEL_MODE', payload: mode });
|
||||
}, []);
|
||||
|
||||
const setLegacyToolSettings = useCallback((settings: LegacyToolStyleSettings) => {
|
||||
dispatch({ type: 'SET_LEGACY_TOOL_SETTINGS', payload: settings });
|
||||
|
||||
const setFullscreenToolSettings = useCallback((settings: FullscreenToolStyleSettings) => {
|
||||
dispatch({ type: 'SET_FULLSCREEN_TOOL_SETTINGS', payload: settings });
|
||||
}, []);
|
||||
|
||||
const setPreviewFile = useCallback((file: File | null) => {
|
||||
@@ -258,8 +264,10 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.localStorage.setItem(LEGACY_TOOL_SETTINGS_STORAGE_KEY, JSON.stringify(state.legacyToolSettings));
|
||||
}, [state.legacyToolSettings]);
|
||||
const serialized = JSON.stringify(state.fullscreenToolSettings);
|
||||
window.localStorage.setItem(FULLSCREEN_TOOL_SETTINGS_STORAGE_KEY, serialized);
|
||||
window.localStorage.setItem(LEGACY_TOOL_SETTINGS_STORAGE_KEY, serialized);
|
||||
}, [state.fullscreenToolSettings]);
|
||||
|
||||
// Tool reset methods
|
||||
const registerToolReset = useCallback((toolId: string, resetFunction: () => void) => {
|
||||
@@ -288,8 +296,6 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
||||
actions.setSelectedTool('read');
|
||||
actions.setWorkbench('viewer');
|
||||
setSearchQuery('');
|
||||
setToolPanelMode('sidebar'); // Close legacy mode when switching to reader
|
||||
setLeftPanelView('toolPicker'); // Show tool picker when navigating back to tools
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -365,7 +371,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
||||
setLeftPanelView,
|
||||
setReaderMode,
|
||||
setToolPanelMode,
|
||||
setLegacyToolSettings,
|
||||
setFullscreenToolSettings,
|
||||
setPreviewFile,
|
||||
setPageEditorFunctions,
|
||||
setSearchQuery,
|
||||
@@ -402,7 +408,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
||||
setLeftPanelView,
|
||||
setReaderMode,
|
||||
setToolPanelMode,
|
||||
setLegacyToolSettings,
|
||||
setFullscreenToolSettings,
|
||||
setPreviewFile,
|
||||
setPageEditorFunctions,
|
||||
setSearchQuery,
|
||||
|
||||
@@ -77,17 +77,17 @@ export const SUBCATEGORY_ORDER: SubcategoryId[] = [
|
||||
];
|
||||
|
||||
export const SUBCATEGORY_COLOR_MAP: Record<SubcategoryId, string> = {
|
||||
[SubcategoryId.SIGNING]: '#E91E63', // Rose Pink
|
||||
[SubcategoryId.DOCUMENT_SECURITY]: '#D32F2F', // Deep Red
|
||||
[SubcategoryId.VERIFICATION]: '#1976D2', // Medium Blue
|
||||
[SubcategoryId.DOCUMENT_REVIEW]: '#388E3C', // Forest Green
|
||||
[SubcategoryId.PAGE_FORMATTING]: '#5E35B1', // Deep Purple
|
||||
[SubcategoryId.EXTRACTION]: '#F57C00', // Amber/Gold
|
||||
[SubcategoryId.REMOVAL]: '#E64A19', // Rust Orange
|
||||
[SubcategoryId.AUTOMATION]: '#00897B', // Teal
|
||||
[SubcategoryId.GENERAL]: '#689F38', // Olive Green
|
||||
[SubcategoryId.ADVANCED_FORMATTING]: '#8E24AA', // Deep Magenta
|
||||
[SubcategoryId.DEVELOPER_TOOLS]: '#455A64', // Slate Grey
|
||||
[SubcategoryId.SIGNING]: 'var(--category-color-signing)', // Green
|
||||
[SubcategoryId.DOCUMENT_SECURITY]: 'var(--category-color-security)', // Orange
|
||||
[SubcategoryId.VERIFICATION]: 'var(--category-color-verification)', // Orange
|
||||
[SubcategoryId.DOCUMENT_REVIEW]: 'var(--category-color-general)', // Blue
|
||||
[SubcategoryId.PAGE_FORMATTING]: 'var(--category-color-formatting)', // Purple
|
||||
[SubcategoryId.EXTRACTION]: 'var(--category-color-extraction)', // Cyan
|
||||
[SubcategoryId.REMOVAL]: 'var(--category-color-removal)', // Red
|
||||
[SubcategoryId.AUTOMATION]: 'var(--category-color-automation)', // Pink
|
||||
[SubcategoryId.GENERAL]: 'var(--category-color-general)', // Blue
|
||||
[SubcategoryId.ADVANCED_FORMATTING]: 'var(--category-color-formatting)', // Purple
|
||||
[SubcategoryId.DEVELOPER_TOOLS]: 'var(--category-color-developer)', // Gray
|
||||
};
|
||||
|
||||
export const getSubcategoryIcon = (subcategory: SubcategoryId): React.ReactNode => {
|
||||
|
||||
@@ -48,6 +48,18 @@
|
||||
--color-yellow-200: #fef08a;
|
||||
--color-yellow-300: #fde047;
|
||||
--color-yellow-400: #facc15;
|
||||
|
||||
/* Category colors - consistent across light and dark modes */
|
||||
--category-color-removal: #ef4444; /* Red for removal tools */
|
||||
--category-color-security: #f59e0b; /* Orange for security tools */
|
||||
--category-color-formatting: #8b5cf6; /* Purple for formatting tools */
|
||||
--category-color-extraction: #06b6d4; /* Cyan for extraction tools */
|
||||
--category-color-signing: #10b981; /* Green for signing tools */
|
||||
--category-color-general: #3b82f6; /* Blue for general tools */
|
||||
--category-color-verification: #f97316; /* Orange for verification tools */
|
||||
--category-color-automation: #ec4899; /* Pink for automation tools */
|
||||
--category-color-developer: #6b7280; /* Gray for developer tools */
|
||||
--category-color-default: #6b7280; /* Default gray */
|
||||
--color-yellow-500: #eab308;
|
||||
--color-yellow-600: #ca8a04;
|
||||
--color-yellow-700: #a16207;
|
||||
@@ -277,6 +289,18 @@
|
||||
--color-gray-800: #e5e7eb;
|
||||
--color-gray-900: #f3f4f6;
|
||||
|
||||
/* Category colors - same as light mode for consistency */
|
||||
--category-color-removal: #ef4444; /* Red for removal tools */
|
||||
--category-color-security: #f59e0b; /* Orange for security tools */
|
||||
--category-color-formatting: #8b5cf6; /* Purple for formatting tools */
|
||||
--category-color-extraction: #06b6d4; /* Cyan for extraction tools */
|
||||
--category-color-signing: #10b981; /* Green for signing tools */
|
||||
--category-color-general: #3b82f6; /* Blue for general tools */
|
||||
--category-color-verification: #f97316; /* Orange for verification tools */
|
||||
--category-color-automation: #ec4899; /* Pink for automation tools */
|
||||
--category-color-developer: #6b7280; /* Gray for developer tools */
|
||||
--category-color-default: #6b7280; /* Default gray */
|
||||
|
||||
/* Success (green) - dark */
|
||||
--color-green-50: #052e16;
|
||||
--color-green-100: #064e3b;
|
||||
|
||||
Reference in New Issue
Block a user