mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-22 04:09:22 +00:00
remove the 'convenience exports' from ToolWorkflowContext and standardise toolButton for tool picker. + Other general improvements
This commit is contained in:
parent
f2b5a4a1f8
commit
681f0abfd8
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { Box } from '@mantine/core';
|
import { Box } from '@mantine/core';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRainbowThemeContext } from '../shared/RainbowThemeProvider';
|
import { useRainbowThemeContext } from '../shared/RainbowThemeProvider';
|
||||||
import { useWorkbenchState, useToolSelection } from '../../contexts/ToolWorkflowContext';
|
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
|
||||||
import { useFileHandler } from '../../hooks/useFileHandler';
|
import { useFileHandler } from '../../hooks/useFileHandler';
|
||||||
import { useFileContext } from '../../contexts/FileContext';
|
import { useFileContext } from '../../contexts/FileContext';
|
||||||
|
|
||||||
@ -28,9 +28,9 @@ export default function Workbench() {
|
|||||||
setPreviewFile,
|
setPreviewFile,
|
||||||
setPageEditorFunctions,
|
setPageEditorFunctions,
|
||||||
setSidebarsVisible
|
setSidebarsVisible
|
||||||
} = useWorkbenchState();
|
} = useToolWorkflow();
|
||||||
|
|
||||||
const { selectedToolKey, selectedTool, handleToolSelect } = useToolSelection();
|
const { selectedToolKey, selectedTool, handleToolSelect } = useToolWorkflow();
|
||||||
const { addToActiveFiles } = useFileHandler();
|
const { addToActiveFiles } = useFileHandler();
|
||||||
|
|
||||||
const handlePreviewClose = () => {
|
const handlePreviewClose = () => {
|
||||||
|
@ -5,6 +5,7 @@ import ToolButton from './toolPicker/ToolButton';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useToolSections } from '../../hooks/useToolSections';
|
import { useToolSections } from '../../hooks/useToolSections';
|
||||||
import SubcategoryHeader from './shared/SubcategoryHeader';
|
import SubcategoryHeader from './shared/SubcategoryHeader';
|
||||||
|
import NoToolsFound from './shared/NoToolsFound';
|
||||||
|
|
||||||
interface SearchResultsProps {
|
interface SearchResultsProps {
|
||||||
filteredTools: [string, ToolRegistryEntry][];
|
filteredTools: [string, ToolRegistryEntry][];
|
||||||
@ -16,11 +17,7 @@ const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }
|
|||||||
const { searchGroups } = useToolSections(filteredTools);
|
const { searchGroups } = useToolSections(filteredTools);
|
||||||
|
|
||||||
if (searchGroups.length === 0) {
|
if (searchGroups.length === 0) {
|
||||||
return (
|
return <NoToolsFound />;
|
||||||
<Text c="dimmed" size="sm" p="sm">
|
|
||||||
{t('toolPicker.noToolsFound', 'No tools found')}
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRainbowThemeContext } from '../shared/RainbowThemeProvider';
|
import { useRainbowThemeContext } from '../shared/RainbowThemeProvider';
|
||||||
import { useToolPanelState, useToolSelection, useWorkbenchState } from '../../contexts/ToolWorkflowContext';
|
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
|
||||||
import ToolPicker from './ToolPicker';
|
import ToolPicker from './ToolPicker';
|
||||||
import SearchResults from './SearchResults';
|
import SearchResults from './SearchResults';
|
||||||
import ToolRenderer from './ToolRenderer';
|
import ToolRenderer from './ToolRenderer';
|
||||||
@ -27,10 +27,10 @@ export default function ToolPanel() {
|
|||||||
toolRegistry,
|
toolRegistry,
|
||||||
setSearchQuery,
|
setSearchQuery,
|
||||||
handleBackToTools
|
handleBackToTools
|
||||||
} = useToolPanelState();
|
} = useToolWorkflow();
|
||||||
|
|
||||||
const { selectedToolKey, handleToolSelect } = useToolSelection();
|
const { selectedToolKey, handleToolSelect } = useToolWorkflow();
|
||||||
const { setPreviewFile } = useWorkbenchState();
|
const { setPreviewFile } = useToolWorkflow();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -6,6 +6,7 @@ import ToolButton from "./toolPicker/ToolButton";
|
|||||||
import "./toolPicker/ToolPicker.css";
|
import "./toolPicker/ToolPicker.css";
|
||||||
import { useToolSections } from "../../hooks/useToolSections";
|
import { useToolSections } from "../../hooks/useToolSections";
|
||||||
import SubcategoryHeader from "./shared/SubcategoryHeader";
|
import SubcategoryHeader from "./shared/SubcategoryHeader";
|
||||||
|
import NoToolsFound from "./shared/NoToolsFound";
|
||||||
|
|
||||||
interface ToolPickerProps {
|
interface ToolPickerProps {
|
||||||
selectedToolKey: string | null;
|
selectedToolKey: string | null;
|
||||||
@ -14,6 +15,31 @@ interface ToolPickerProps {
|
|||||||
isSearching?: boolean;
|
isSearching?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to render tool buttons for a subcategory
|
||||||
|
const renderToolButtons = (
|
||||||
|
subcategory: any,
|
||||||
|
selectedToolKey: string | null,
|
||||||
|
onSelect: (id: string) => void,
|
||||||
|
showSubcategoryHeader: boolean = true
|
||||||
|
) => (
|
||||||
|
<Box key={subcategory.subcategory} w="100%">
|
||||||
|
{showSubcategoryHeader && (
|
||||||
|
<SubcategoryHeader label={subcategory.subcategory} />
|
||||||
|
)}
|
||||||
|
<Stack gap="xs">
|
||||||
|
{subcategory.tools.map(({ id, tool }: { id: string; tool: any }) => (
|
||||||
|
<ToolButton
|
||||||
|
key={id}
|
||||||
|
id={id}
|
||||||
|
tool={tool}
|
||||||
|
isSelected={selectedToolKey === id}
|
||||||
|
onSelect={onSelect}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = false }: ToolPickerProps) => {
|
const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = false }: ToolPickerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [quickHeaderHeight, setQuickHeaderHeight] = useState(0);
|
const [quickHeaderHeight, setQuickHeaderHeight] = useState(0);
|
||||||
@ -92,26 +118,9 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
|||||||
{isSearching ? (
|
{isSearching ? (
|
||||||
<Stack p="sm" gap="xs">
|
<Stack p="sm" gap="xs">
|
||||||
{searchGroups.length === 0 ? (
|
{searchGroups.length === 0 ? (
|
||||||
<Text c="dimmed" size="sm" p="sm">
|
<NoToolsFound />
|
||||||
{t("toolPicker.noToolsFound", "No tools found")}
|
|
||||||
</Text>
|
|
||||||
) : (
|
) : (
|
||||||
searchGroups.map(group => (
|
searchGroups.map(group => renderToolButtons(group, selectedToolKey, onSelect))
|
||||||
<Box key={group.subcategory} w="100%">
|
|
||||||
<SubcategoryHeader label={group.subcategory} />
|
|
||||||
<Stack gap="xs">
|
|
||||||
{group.tools.map(({ id, tool }) => (
|
|
||||||
<ToolButton
|
|
||||||
key={id}
|
|
||||||
id={id}
|
|
||||||
tool={tool}
|
|
||||||
isSelected={selectedToolKey === id}
|
|
||||||
onSelect={onSelect}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
))
|
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
) : (
|
) : (
|
||||||
@ -124,8 +133,8 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
|||||||
position: "sticky",
|
position: "sticky",
|
||||||
top: 0,
|
top: 0,
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
borderTop: `1px solid var(--tool-header-border)`,
|
borderTop: `0.0625rem solid var(--tool-header-border)`,
|
||||||
borderBottom: `1px solid var(--tool-header-border)`,
|
borderBottom: `0.0625rem solid var(--tool-header-border)`,
|
||||||
marginBottom: -1,
|
marginBottom: -1,
|
||||||
padding: "0.5rem 1rem",
|
padding: "0.5rem 1rem",
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
@ -143,9 +152,9 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
|||||||
style={{
|
style={{
|
||||||
background: "var(--tool-header-badge-bg)",
|
background: "var(--tool-header-badge-bg)",
|
||||||
color: "var(--tool-header-badge-text)",
|
color: "var(--tool-header-badge-text)",
|
||||||
borderRadius: 8,
|
borderRadius: ".5rem",
|
||||||
padding: "2px 8px",
|
padding: "0.125rem 0.5rem",
|
||||||
fontSize: 12,
|
fontSize: ".75rem",
|
||||||
fontWeight: 700
|
fontWeight: 700
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -155,24 +164,9 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
|||||||
|
|
||||||
<Box ref={quickAccessRef} w="100%">
|
<Box ref={quickAccessRef} w="100%">
|
||||||
<Stack p="sm" gap="xs">
|
<Stack p="sm" gap="xs">
|
||||||
{quickSection?.subcategories.map(sc => (
|
{quickSection?.subcategories.map(sc =>
|
||||||
<Box key={sc.subcategory} w="100%">
|
renderToolButtons(sc, selectedToolKey, onSelect, quickSection?.subcategories.length === 1)
|
||||||
{quickSection?.subcategories.length > 1 && (
|
|
||||||
<SubcategoryHeader label={sc.subcategory} />
|
|
||||||
)}
|
)}
|
||||||
<Stack gap="xs">
|
|
||||||
{sc.tools.map(({ id, tool }) => (
|
|
||||||
<ToolButton
|
|
||||||
key={id}
|
|
||||||
id={id}
|
|
||||||
tool={tool}
|
|
||||||
isSelected={selectedToolKey === id}
|
|
||||||
onSelect={onSelect}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
@ -186,8 +180,8 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
|||||||
position: "sticky",
|
position: "sticky",
|
||||||
top: quickSection ? quickHeaderHeight - 1: 0,
|
top: quickSection ? quickHeaderHeight - 1: 0,
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
borderTop: `1px solid var(--tool-header-border)`,
|
borderTop: `0.0625rem solid var(--tool-header-border)`,
|
||||||
borderBottom: `1px solid var(--tool-header-border)`,
|
borderBottom: `0.0625rem solid var(--tool-header-border)`,
|
||||||
padding: "0.5rem 1rem",
|
padding: "0.5rem 1rem",
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
background: "var(--tool-header-bg)",
|
background: "var(--tool-header-bg)",
|
||||||
@ -204,9 +198,9 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
|||||||
style={{
|
style={{
|
||||||
background: "var(--tool-header-badge-bg)",
|
background: "var(--tool-header-badge-bg)",
|
||||||
color: "var(--tool-header-badge-text)",
|
color: "var(--tool-header-badge-text)",
|
||||||
borderRadius: 8,
|
borderRadius: ".5rem",
|
||||||
padding: "2px 8px",
|
padding: "0.125rem 0.5rem",
|
||||||
fontSize: 12,
|
fontSize: ".75rem",
|
||||||
fontWeight: 700
|
fontWeight: 700
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -216,34 +210,15 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
|
|||||||
|
|
||||||
<Box ref={allToolsRef} w="100%">
|
<Box ref={allToolsRef} w="100%">
|
||||||
<Stack p="sm" gap="xs">
|
<Stack p="sm" gap="xs">
|
||||||
{allSection?.subcategories.map(sc => (
|
{allSection?.subcategories.map(sc =>
|
||||||
<Box key={sc.subcategory} w="100%">
|
renderToolButtons(sc, selectedToolKey, onSelect, allSection?.subcategories.length === 1)
|
||||||
{allSection?.subcategories.length > 1 && (
|
|
||||||
<SubcategoryHeader label={sc.subcategory} />
|
|
||||||
)}
|
)}
|
||||||
<Stack gap="xs">
|
|
||||||
{sc.tools.map(({ id, tool }) => (
|
|
||||||
<ToolButton
|
|
||||||
key={id}
|
|
||||||
id={id}
|
|
||||||
tool={tool}
|
|
||||||
isSelected={selectedToolKey === id}
|
|
||||||
onSelect={onSelect}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!quickSection && !allSection && (
|
{!quickSection && !allSection && <NoToolsFound />}
|
||||||
<Text c="dimmed" size="sm" p="sm">
|
|
||||||
{t("toolPicker.noToolsFound", "No tools found")}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* bottom spacer to allow scrolling past the last row */}
|
{/* bottom spacer to allow scrolling past the last row */}
|
||||||
<div aria-hidden style={{ height: 200 }} />
|
<div aria-hidden style={{ height: 200 }} />
|
||||||
|
@ -4,6 +4,7 @@ import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useMultipleEndpointsEnabled } from "../../../hooks/useEndpointConfig";
|
import { useMultipleEndpointsEnabled } from "../../../hooks/useEndpointConfig";
|
||||||
import { isImageFormat, isWebFormat } from "../../../utils/convertUtils";
|
import { isImageFormat, isWebFormat } from "../../../utils/convertUtils";
|
||||||
|
import { getConversionEndpoints } from "../../../data/toolRegistry";
|
||||||
import { useFileSelectionActions } from "../../../contexts/FileSelectionContext";
|
import { useFileSelectionActions } from "../../../contexts/FileSelectionContext";
|
||||||
import { useFileContext } from "../../../contexts/FileContext";
|
import { useFileContext } from "../../../contexts/FileContext";
|
||||||
import { detectFileExtension } from "../../../utils/fileUtils";
|
import { detectFileExtension } from "../../../utils/fileUtils";
|
||||||
@ -43,15 +44,7 @@ const ConvertSettings = ({
|
|||||||
const { setSelectedFiles } = useFileSelectionActions();
|
const { setSelectedFiles } = useFileSelectionActions();
|
||||||
const { activeFiles, setSelectedFiles: setContextSelectedFiles } = useFileContext();
|
const { activeFiles, setSelectedFiles: setContextSelectedFiles } = useFileContext();
|
||||||
|
|
||||||
const allEndpoints = useMemo(() => {
|
const allEndpoints = useMemo(() => getConversionEndpoints(EXTENSION_TO_ENDPOINT), []);
|
||||||
const endpoints = new Set<string>();
|
|
||||||
Object.values(EXTENSION_TO_ENDPOINT).forEach(toEndpoints => {
|
|
||||||
Object.values(toEndpoints).forEach(endpoint => {
|
|
||||||
endpoints.add(endpoint);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return Array.from(endpoints);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const { endpointStatus } = useMultipleEndpointsEnabled(allEndpoints);
|
const { endpointStatus } = useMultipleEndpointsEnabled(allEndpoints);
|
||||||
|
|
||||||
|
15
frontend/src/components/tools/shared/NoToolsFound.tsx
Normal file
15
frontend/src/components/tools/shared/NoToolsFound.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Text } from '@mantine/core';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
const NoToolsFound: React.FC = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text c="dimmed" size="sm" p="sm">
|
||||||
|
{t("toolPicker.noToolsFound", "No tools found")}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NoToolsFound;
|
@ -6,7 +6,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tool-picker-scrollable::-webkit-scrollbar {
|
.tool-picker-scrollable::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 0.375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-picker-scrollable::-webkit-scrollbar-track {
|
.tool-picker-scrollable::-webkit-scrollbar-track {
|
||||||
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
.tool-picker-scrollable::-webkit-scrollbar-thumb {
|
.tool-picker-scrollable::-webkit-scrollbar-thumb {
|
||||||
background-color: var(--mantine-color-gray-4);
|
background-color: var(--mantine-color-gray-4);
|
||||||
border-radius: 3px;
|
border-radius: 0.1875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-picker-scrollable::-webkit-scrollbar-thumb:hover {
|
.tool-picker-scrollable::-webkit-scrollbar-thumb:hover {
|
||||||
|
@ -225,8 +225,3 @@ export function useToolWorkflow(): ToolWorkflowContextValue {
|
|||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience exports for specific use cases (optional - components can use useToolWorkflow directly)
|
|
||||||
export const useToolSelection = useToolWorkflow;
|
|
||||||
export const useToolPanelState = useToolWorkflow;
|
|
||||||
export const useWorkbenchState = useToolWorkflow;
|
|
@ -12,7 +12,7 @@ export type ToolRegistryEntry = {
|
|||||||
view: string;
|
view: string;
|
||||||
description: string;
|
description: string;
|
||||||
category: string;
|
category: string;
|
||||||
subcategory: string | null;
|
subcategory: string;
|
||||||
|
|
||||||
// Optional custom props for tools
|
// Optional custom props for tools
|
||||||
maxFiles?: number;
|
maxFiles?: number;
|
||||||
@ -77,8 +77,7 @@ export const SUBCATEGORY_COLOR_MAP: Record<string, string> = {
|
|||||||
'Developer Tools': '#F55454',
|
'Developer Tools': '#F55454',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSubcategoryColor = (subcategory?: string | null): string => {
|
export const getSubcategoryColor = (subcategory: string): string => {
|
||||||
if (!subcategory) return '#7882FF';
|
|
||||||
return SUBCATEGORY_COLOR_MAP[subcategory] || '#7882FF';
|
return SUBCATEGORY_COLOR_MAP[subcategory] || '#7882FF';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -575,7 +574,7 @@ export function useFlatToolRegistry(): ToolRegistry {
|
|||||||
view: "format",
|
view: "format",
|
||||||
description: t("home.compare.desc", "Compare two PDF documents and highlight differences"),
|
description: t("home.compare.desc", "Compare two PDF documents and highlight differences"),
|
||||||
category: "Recommended Tools",
|
category: "Recommended Tools",
|
||||||
subcategory: null
|
subcategory: "General"
|
||||||
},
|
},
|
||||||
"compressPdfs": {
|
"compressPdfs": {
|
||||||
icon: <span className="material-symbols-rounded">zoom_in_map</span>,
|
icon: <span className="material-symbols-rounded">zoom_in_map</span>,
|
||||||
@ -584,7 +583,7 @@ export function useFlatToolRegistry(): ToolRegistry {
|
|||||||
view: "compress",
|
view: "compress",
|
||||||
description: t("home.compressPdfs.desc", "Compress PDFs to reduce their file size."),
|
description: t("home.compressPdfs.desc", "Compress PDFs to reduce their file size."),
|
||||||
category: "Recommended Tools",
|
category: "Recommended Tools",
|
||||||
subcategory: null,
|
subcategory: "General",
|
||||||
maxFiles: -1
|
maxFiles: -1
|
||||||
},
|
},
|
||||||
"convert": {
|
"convert": {
|
||||||
@ -594,7 +593,7 @@ export function useFlatToolRegistry(): ToolRegistry {
|
|||||||
view: "convert",
|
view: "convert",
|
||||||
description: t("home.fileToPDF.desc", "Convert files to and from PDF format"),
|
description: t("home.fileToPDF.desc", "Convert files to and from PDF format"),
|
||||||
category: "Recommended Tools",
|
category: "Recommended Tools",
|
||||||
subcategory: null,
|
subcategory: "General",
|
||||||
maxFiles: -1,
|
maxFiles: -1,
|
||||||
endpoints: [
|
endpoints: [
|
||||||
"pdf-to-img",
|
"pdf-to-img",
|
||||||
@ -638,7 +637,7 @@ export function useFlatToolRegistry(): ToolRegistry {
|
|||||||
view: "merge",
|
view: "merge",
|
||||||
description: t("home.merge.desc", "Merge multiple PDFs into a single document"),
|
description: t("home.merge.desc", "Merge multiple PDFs into a single document"),
|
||||||
category: "Recommended Tools",
|
category: "Recommended Tools",
|
||||||
subcategory: null,
|
subcategory: "General",
|
||||||
maxFiles: -1
|
maxFiles: -1
|
||||||
},
|
},
|
||||||
"multi-tool": {
|
"multi-tool": {
|
||||||
@ -648,7 +647,7 @@ export function useFlatToolRegistry(): ToolRegistry {
|
|||||||
view: "pageEditor",
|
view: "pageEditor",
|
||||||
description: t("home.multiTool.desc", "Use multiple tools on a single PDF document"),
|
description: t("home.multiTool.desc", "Use multiple tools on a single PDF document"),
|
||||||
category: "Recommended Tools",
|
category: "Recommended Tools",
|
||||||
subcategory: null,
|
subcategory: "General",
|
||||||
maxFiles: -1
|
maxFiles: -1
|
||||||
},
|
},
|
||||||
"ocr": {
|
"ocr": {
|
||||||
@ -658,7 +657,7 @@ export function useFlatToolRegistry(): ToolRegistry {
|
|||||||
view: "convert",
|
view: "convert",
|
||||||
description: t("home.ocr.desc", "Extract text from scanned PDFs using Optical Character Recognition"),
|
description: t("home.ocr.desc", "Extract text from scanned PDFs using Optical Character Recognition"),
|
||||||
category: "Recommended Tools",
|
category: "Recommended Tools",
|
||||||
subcategory: null,
|
subcategory: "General",
|
||||||
maxFiles: -1
|
maxFiles: -1
|
||||||
},
|
},
|
||||||
"redact": {
|
"redact": {
|
||||||
@ -668,7 +667,7 @@ export function useFlatToolRegistry(): ToolRegistry {
|
|||||||
view: "redact",
|
view: "redact",
|
||||||
description: t("home.redact.desc", "Permanently remove sensitive information from PDF documents"),
|
description: t("home.redact.desc", "Permanently remove sensitive information from PDF documents"),
|
||||||
category: "Recommended Tools",
|
category: "Recommended Tools",
|
||||||
subcategory: null
|
subcategory: "General"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -682,3 +681,56 @@ export const toolEndpoints: Record<string, string[]> = {
|
|||||||
merge: ["merge-pdfs"],
|
merge: ["merge-pdfs"],
|
||||||
// Add more endpoint mappings as needed
|
// Add more endpoint mappings as needed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all endpoints from both registry entries and legacy toolEndpoints mapping
|
||||||
|
* This consolidates endpoint discovery logic in one place
|
||||||
|
*/
|
||||||
|
export const getAllEndpoints = (registry: ToolRegistry): string[] => {
|
||||||
|
const lists: string[][] = [];
|
||||||
|
|
||||||
|
// Get endpoints from registry entries
|
||||||
|
Object.values(registry).forEach(entry => {
|
||||||
|
if (entry.endpoints && entry.endpoints.length > 0) {
|
||||||
|
lists.push(entry.endpoints);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get endpoints from legacy toolEndpoints mapping
|
||||||
|
Object.entries(toolEndpoints).forEach(([key, list]) => {
|
||||||
|
// Only add if not already covered by registry entries
|
||||||
|
if (!registry[key]?.endpoints) {
|
||||||
|
lists.push(list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(new Set(lists.flat()));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all endpoints from a conversion matrix (like EXTENSION_TO_ENDPOINT)
|
||||||
|
* This is useful for convert-specific endpoint discovery
|
||||||
|
*/
|
||||||
|
export const getConversionEndpoints = (extensionToEndpoint: Record<string, Record<string, string>>): string[] => {
|
||||||
|
const endpoints = new Set<string>();
|
||||||
|
Object.values(extensionToEndpoint).forEach(toEndpoints => {
|
||||||
|
Object.values(toEndpoints).forEach(endpoint => {
|
||||||
|
endpoints.add(endpoint);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return Array.from(endpoints);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all endpoints from both tool registry and conversion matrix
|
||||||
|
* This provides comprehensive endpoint coverage for the entire application
|
||||||
|
*/
|
||||||
|
export const getAllApplicationEndpoints = (
|
||||||
|
registry: ToolRegistry,
|
||||||
|
extensionToEndpoint?: Record<string, Record<string, string>>
|
||||||
|
): string[] => {
|
||||||
|
const toolEndpoints = getAllEndpoints(registry);
|
||||||
|
const conversionEndpoints = extensionToEndpoint ? getConversionEndpoints(extensionToEndpoint) : [];
|
||||||
|
|
||||||
|
return Array.from(new Set([...toolEndpoints, ...conversionEndpoints]));
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useCallback, useMemo, useEffect } from 'react';
|
import React, { useState, useCallback, useMemo, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useFlatToolRegistry, toolEndpoints, type ToolRegistryEntry } from "../data/toolRegistry";
|
import { useFlatToolRegistry, toolEndpoints, getAllEndpoints, type ToolRegistryEntry } from "../data/toolRegistry";
|
||||||
import { useMultipleEndpointsEnabled } from "./useEndpointConfig";
|
import { useMultipleEndpointsEnabled } from "./useEndpointConfig";
|
||||||
|
|
||||||
interface ToolManagementResult {
|
interface ToolManagementResult {
|
||||||
@ -31,14 +31,7 @@ export const useToolManagement = (): ToolManagementResult => {
|
|||||||
return endpointsByTool;
|
return endpointsByTool;
|
||||||
}, [baseRegistry]);
|
}, [baseRegistry]);
|
||||||
|
|
||||||
const allEndpoints = useMemo(() => {
|
const allEndpoints = useMemo(() => getAllEndpoints(baseRegistry), [baseRegistry]);
|
||||||
const lists: string[][] = [];
|
|
||||||
Object.values(registryDerivedEndpoints).forEach(list => lists.push(list));
|
|
||||||
Object.entries(toolEndpoints).forEach(([key, list]) => {
|
|
||||||
if (!registryDerivedEndpoints[key]) lists.push(list);
|
|
||||||
});
|
|
||||||
return Array.from(new Set(lists.flat()));
|
|
||||||
}, [registryDerivedEndpoints]);
|
|
||||||
const { endpointStatus, loading: endpointsLoading } = useMultipleEndpointsEnabled(allEndpoints);
|
const { endpointStatus, loading: endpointsLoading } = useMultipleEndpointsEnabled(allEndpoints);
|
||||||
|
|
||||||
const isToolAvailable = useCallback((toolKey: string): boolean => {
|
const isToolAvailable = useCallback((toolKey: string): boolean => {
|
||||||
|
@ -14,8 +14,8 @@ export function useToolSections(filteredTools: [string, ToolRegistryEntry][]) {
|
|||||||
const groupedTools = useMemo(() => {
|
const groupedTools = useMemo(() => {
|
||||||
const grouped: GroupedTools = {};
|
const grouped: GroupedTools = {};
|
||||||
filteredTools.forEach(([id, tool]) => {
|
filteredTools.forEach(([id, tool]) => {
|
||||||
const category = tool?.category || 'OTHER';
|
const category = tool.category;
|
||||||
const subcategory = tool?.subcategory || 'General';
|
const subcategory = tool.subcategory;
|
||||||
if (!grouped[category]) grouped[category] = {};
|
if (!grouped[category]) grouped[category] = {};
|
||||||
if (!grouped[category][subcategory]) grouped[category][subcategory] = [];
|
if (!grouped[category][subcategory]) grouped[category][subcategory] = [];
|
||||||
grouped[category][subcategory].push({ id, tool });
|
grouped[category][subcategory].push({ id, tool });
|
||||||
@ -72,7 +72,7 @@ export function useToolSections(filteredTools: [string, ToolRegistryEntry][]) {
|
|||||||
filteredTools.forEach(([id, tool]) => {
|
filteredTools.forEach(([id, tool]) => {
|
||||||
if (seen.has(id)) return;
|
if (seen.has(id)) return;
|
||||||
seen.add(id);
|
seen.add(id);
|
||||||
const sub = tool?.subcategory || 'General';
|
const sub = tool.subcategory;
|
||||||
if (!subMap[sub]) subMap[sub] = [];
|
if (!subMap[sub]) subMap[sub] = [];
|
||||||
subMap[sub].push({ id, tool });
|
subMap[sub].push({ id, tool });
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useFileContext } from "../contexts/FileContext";
|
import { useFileContext } from "../contexts/FileContext";
|
||||||
import { FileSelectionProvider, useFileSelection } from "../contexts/FileSelectionContext";
|
import { FileSelectionProvider, useFileSelection } from "../contexts/FileSelectionContext";
|
||||||
import { ToolWorkflowProvider, useToolSelection } from "../contexts/ToolWorkflowContext";
|
import { ToolWorkflowProvider, useToolWorkflow } from "../contexts/ToolWorkflowContext";
|
||||||
import { Group } from "@mantine/core";
|
import { Group } from "@mantine/core";
|
||||||
import { SidebarProvider, useSidebarContext } from "../contexts/SidebarContext";
|
import { SidebarProvider, useSidebarContext } from "../contexts/SidebarContext";
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ function HomePageContent() {
|
|||||||
|
|
||||||
const { setMaxFiles, setIsToolMode, setSelectedFiles } = useFileSelection();
|
const { setMaxFiles, setIsToolMode, setSelectedFiles } = useFileSelection();
|
||||||
|
|
||||||
const { selectedTool } = useToolSelection();
|
const { selectedTool } = useToolWorkflow();
|
||||||
|
|
||||||
// Update file selection context when tool changes
|
// Update file selection context when tool changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user