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