diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 429b03308..e1f264c66 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -4,6 +4,7 @@ import { FileContextProvider } from "./contexts/FileContext";
import { FilesModalProvider } from "./contexts/FilesModalContext";
import { FileSelectionProvider } from "./contexts/FileSelectionContext";
import { ToolWorkflowProvider } from "./contexts/ToolWorkflowContext";
+import { ToolNavigationProvider } from "./contexts/ToolNavigationContext";
import { SidebarProvider } from "./contexts/SidebarContext";
import ErrorBoundary from "./components/shared/ErrorBoundary";
import HomePage from "./pages/HomePage";
@@ -36,11 +37,13 @@ export default function App() {
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/frontend/src/components/tools/automate/AutomationCreation.tsx b/frontend/src/components/tools/automate/AutomationCreation.tsx
index 8a51bc224..c72c66036 100644
--- a/frontend/src/components/tools/automate/AutomationCreation.tsx
+++ b/frontend/src/components/tools/automate/AutomationCreation.tsx
@@ -12,10 +12,9 @@ import {
} from '@mantine/core';
import DeleteIcon from '@mui/icons-material/Delete';
import SettingsIcon from '@mui/icons-material/Settings';
-import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';
-import { useFlatToolRegistry } from '../../../data/useTranslatedToolRegistry';
+import { ToolRegistryEntry } from '../../../data/toolsTaxonomy';
import ToolConfigurationModal from './ToolConfigurationModal';
import ToolSelector from './ToolSelector';
@@ -24,6 +23,7 @@ interface AutomationCreationProps {
existingAutomation?: any;
onBack: () => void;
onComplete: (automation: any) => void;
+ toolRegistry: Record; // Pass registry as prop to break circular dependency
}
interface AutomationTool {
@@ -34,9 +34,8 @@ interface AutomationTool {
parameters?: any;
}
-export default function AutomationCreation({ mode, existingAutomation, onBack, onComplete }: AutomationCreationProps) {
+export default function AutomationCreation({ mode, existingAutomation, onBack, onComplete, toolRegistry }: AutomationCreationProps) {
const { t } = useTranslation();
- const toolRegistry = useFlatToolRegistry();
const [automationName, setAutomationName] = useState('');
const [selectedTools, setSelectedTools] = useState([]);
@@ -157,6 +156,7 @@ export default function AutomationCreation({ mode, existingAutomation, onBack, o
{/* Selected Tools */}
diff --git a/frontend/src/components/tools/automate/ToolConfigurationModal.tsx b/frontend/src/components/tools/automate/ToolConfigurationModal.tsx
index 38754ca19..00ddb0cd6 100644
--- a/frontend/src/components/tools/automate/ToolConfigurationModal.tsx
+++ b/frontend/src/components/tools/automate/ToolConfigurationModal.tsx
@@ -14,8 +14,6 @@ import SettingsIcon from '@mui/icons-material/Settings';
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';
import WarningIcon from '@mui/icons-material/Warning';
-import { useToolWorkflow } from '../../../contexts/ToolWorkflowContext';
-
interface ToolConfigurationModalProps {
opened: boolean;
tool: {
@@ -30,7 +28,6 @@ interface ToolConfigurationModalProps {
export default function ToolConfigurationModal({ opened, tool, onSave, onCancel }: ToolConfigurationModalProps) {
const { t } = useTranslation();
- const { toolRegistry } = useToolWorkflow();
const [parameters, setParameters] = useState({});
const [isValid, setIsValid] = useState(true);
diff --git a/frontend/src/components/tools/automate/ToolSelector.tsx b/frontend/src/components/tools/automate/ToolSelector.tsx
index 7ba4b2e15..8d49e4649 100644
--- a/frontend/src/components/tools/automate/ToolSelector.tsx
+++ b/frontend/src/components/tools/automate/ToolSelector.tsx
@@ -5,18 +5,17 @@ import { ToolRegistryEntry } from '../../../data/toolsTaxonomy';
import { useToolSections } from '../../../hooks/useToolSections';
import { renderToolButtons } from '../shared/renderToolButtons';
import ToolSearch from '../toolPicker/ToolSearch';
-import { useFlatToolRegistry } from '../../../data/useTranslatedToolRegistry';
interface ToolSelectorProps {
onSelect: (toolKey: string) => void;
excludeTools?: string[];
+ toolRegistry: Record; // Pass registry as prop to break circular dependency
}
-export default function ToolSelector({ onSelect, excludeTools = [] }: ToolSelectorProps) {
+export default function ToolSelector({ onSelect, excludeTools = [], toolRegistry }: ToolSelectorProps) {
const { t } = useTranslation();
const [opened, setOpened] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
- const toolRegistry = useFlatToolRegistry();
// Filter out excluded tools (like 'automate' itself)
const baseFilteredTools = useMemo(() => {
diff --git a/frontend/src/components/tools/shared/SuggestedToolsSection.tsx b/frontend/src/components/tools/shared/SuggestedToolsSection.tsx
index b1f91fc39..18e7a0fc2 100644
--- a/frontend/src/components/tools/shared/SuggestedToolsSection.tsx
+++ b/frontend/src/components/tools/shared/SuggestedToolsSection.tsx
@@ -2,11 +2,14 @@ import React from 'react';
import { Stack, Text, Divider, Card, Group } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { useSuggestedTools } from '../../../hooks/useSuggestedTools';
+import { useToolNavigation } from '../../../contexts/ToolNavigationContext';
+
export interface SuggestedToolsSectionProps {}
export function SuggestedToolsSection(): React.ReactElement {
const { t } = useTranslation();
- const suggestedTools = useSuggestedTools();
+ const { handleToolSelect, selectedToolKey } = useToolNavigation();
+ const suggestedTools = useSuggestedTools(selectedToolKey, handleToolSelect);
return (
diff --git a/frontend/src/constants/addWatermarkConstants.ts b/frontend/src/constants/addWatermarkConstants.ts
index 65d26e5ca..1e84afb37 100644
--- a/frontend/src/constants/addWatermarkConstants.ts
+++ b/frontend/src/constants/addWatermarkConstants.ts
@@ -1,4 +1,16 @@
-import { AddWatermarkParameters } from "../hooks/tools/addWatermark/useAddWatermarkParameters";
+export interface AddWatermarkParameters {
+ watermarkType?: 'text' | 'image';
+ watermarkText: string;
+ watermarkImage?: File;
+ fontSize: number; // Used for both text size and image size
+ rotation: number;
+ opacity: number;
+ widthSpacer: number;
+ heightSpacer: number;
+ alphabet: string;
+ customColor: string;
+ convertPDFToImage: boolean;
+}
export interface AlphabetOption {
value: string;
diff --git a/frontend/src/contexts/ToolNavigationContext.tsx b/frontend/src/contexts/ToolNavigationContext.tsx
new file mode 100644
index 000000000..1e82b6c6f
--- /dev/null
+++ b/frontend/src/contexts/ToolNavigationContext.tsx
@@ -0,0 +1,85 @@
+/**
+ * ToolNavigationContext - Handles tool selection and navigation without tool registry
+ * This breaks the circular dependency by not importing the tool registry
+ */
+
+import React, { createContext, useContext, useState, useCallback } from 'react';
+import { useFileContext } from './FileContext';
+
+// Navigation state interface
+interface ToolNavigationState {
+ selectedToolKey: string | null;
+}
+
+// Context value interface
+interface ToolNavigationContextValue extends ToolNavigationState {
+ // Navigation Actions
+ selectTool: (toolKey: string) => void;
+ clearToolSelection: () => void;
+ handleToolSelect: (toolId: string) => void;
+}
+
+const ToolNavigationContext = createContext(undefined);
+
+// Provider component
+interface ToolNavigationProviderProps {
+ children: React.ReactNode;
+}
+
+export function ToolNavigationProvider({ children }: ToolNavigationProviderProps) {
+ const [selectedToolKey, setSelectedToolKey] = useState(null);
+ const { setCurrentView } = useFileContext();
+
+ const selectTool = useCallback((toolKey: string) => {
+ setSelectedToolKey(toolKey);
+ }, []);
+
+ const clearToolSelection = useCallback(() => {
+ setSelectedToolKey(null);
+ }, []);
+
+ const handleToolSelect = useCallback((toolId: string) => {
+ // Handle special cases
+ if (toolId === 'allTools') {
+ clearToolSelection();
+ return;
+ }
+
+ selectTool(toolId);
+ setCurrentView('fileEditor');
+ }, [selectTool, setCurrentView, clearToolSelection]);
+
+ const contextValue: ToolNavigationContextValue = {
+ selectedToolKey,
+ selectTool,
+ clearToolSelection,
+ handleToolSelect
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+// Custom hook to use the context
+export function useToolNavigation(): ToolNavigationContextValue {
+ const context = useContext(ToolNavigationContext);
+ if (!context) {
+ // During development hot reload, temporarily return a safe fallback
+ if (process.env.NODE_ENV === 'development') {
+ console.warn('ToolNavigationContext temporarily unavailable during hot reload, using fallback');
+
+ return {
+ selectedToolKey: null,
+ selectTool: () => {},
+ clearToolSelection: () => {},
+ handleToolSelect: () => {}
+ } as ToolNavigationContextValue;
+ }
+
+ throw new Error('useToolNavigation must be used within a ToolNavigationProvider');
+ }
+ return context;
+}
\ No newline at end of file
diff --git a/frontend/src/contexts/ToolWorkflowContext.tsx b/frontend/src/contexts/ToolWorkflowContext.tsx
index 637906a22..7fce31aae 100644
--- a/frontend/src/contexts/ToolWorkflowContext.tsx
+++ b/frontend/src/contexts/ToolWorkflowContext.tsx
@@ -7,6 +7,7 @@ import React, { createContext, useContext, useReducer, useCallback, useMemo } fr
import { useToolManagement } from '../hooks/useToolManagement';
import { PageEditorFunctions } from '../types/pageEditor';
import { ToolRegistryEntry } from '../data/toolsTaxonomy';
+import { useFileContext } from './FileContext';
// State interface
interface ToolWorkflowState {
@@ -71,7 +72,7 @@ interface ToolWorkflowContextValue extends ToolWorkflowState {
selectedToolKey: string | null;
selectedTool: ToolRegistryEntry | null;
toolRegistry: any; // From useToolManagement
-
+
// UI Actions
setSidebarsVisible: (visible: boolean) => void;
setLeftPanelView: (view: 'toolPicker' | 'toolContent') => void;
@@ -99,13 +100,14 @@ const ToolWorkflowContext = createContext(
// Provider component
interface ToolWorkflowProviderProps {
children: React.ReactNode;
- /** Handler for view changes (passed from parent) */
- onViewChange?: (view: string) => void;
}
-export function ToolWorkflowProvider({ children, onViewChange }: ToolWorkflowProviderProps) {
+export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
const [state, dispatch] = useReducer(toolWorkflowReducer, initialState);
+ // File context for view changes
+ const { setCurrentView } = useFileContext();
+
// Tool management hook
const {
selectedToolKey,
@@ -152,12 +154,12 @@ export function ToolWorkflowProvider({ children, onViewChange }: ToolWorkflowPro
}
selectTool(toolId);
- onViewChange?.('fileEditor');
+ setCurrentView('fileEditor');
setLeftPanelView('toolContent');
setReaderMode(false);
// Clear search so the tool content becomes visible immediately
setSearchQuery('');
- }, [selectTool, onViewChange, setLeftPanelView, setReaderMode, setSearchQuery, clearToolSelection]);
+ }, [selectTool, setCurrentView, setLeftPanelView, setReaderMode, setSearchQuery, clearToolSelection]);
const handleBackToTools = useCallback(() => {
setLeftPanelView('toolPicker');
@@ -183,7 +185,7 @@ export function ToolWorkflowProvider({ children, onViewChange }: ToolWorkflowPro
);
// Simple context value with basic memoization
- const contextValue = useMemo((): ToolWorkflowContextValue => ({
+ const contextValue : ToolWorkflowContextValue ={
// State
...state,
selectedToolKey,
@@ -208,7 +210,7 @@ export function ToolWorkflowProvider({ children, onViewChange }: ToolWorkflowPro
// Computed
filteredTools,
isPanelVisible,
- }), [state, selectedToolKey, selectedTool, toolRegistry, filteredTools, isPanelVisible]);
+ };
return (
@@ -221,7 +223,41 @@ export function ToolWorkflowProvider({ children, onViewChange }: ToolWorkflowPro
export function useToolWorkflow(): ToolWorkflowContextValue {
const context = useContext(ToolWorkflowContext);
if (!context) {
+ // During development hot reload, temporarily return a safe fallback
+ if (false && process.env.NODE_ENV === 'development') {
+ console.warn('ToolWorkflowContext temporarily unavailable during hot reload, using fallback');
+
+ // Return minimal safe fallback to prevent crashes
+ return {
+ state: {
+ sidebarsVisible: true,
+ leftPanelView: 'toolPicker',
+ readerMode: false,
+ previewFile: null,
+ pageEditorFunctions: null,
+ searchQuery: ''
+ },
+ selectedToolKey: null,
+ selectedTool: null,
+ toolRegistry: {},
+ filteredTools: [],
+ isPanelVisible: true,
+ setSidebarsVisible: () => {},
+ setLeftPanelView: () => {},
+ setReaderMode: () => {},
+ setPreviewFile: () => {},
+ setPageEditorFunctions: () => {},
+ setSearchQuery: () => {},
+ selectTool: () => {},
+ clearToolSelection: () => {},
+ handleToolSelect: () => {},
+ handleBackToTools: () => {},
+ handleReaderToggle: () => {}
+ } as ToolWorkflowContextValue;
+ }
+
+ console.error('ToolWorkflowContext not found. Current stack:', new Error().stack);
throw new Error('useToolWorkflow must be used within a ToolWorkflowProvider');
}
return context;
-}
\ No newline at end of file
+}
diff --git a/frontend/src/data/useTranslatedToolRegistry.tsx b/frontend/src/data/useTranslatedToolRegistry.tsx
index 7be8431df..4ceababe2 100644
--- a/frontend/src/data/useTranslatedToolRegistry.tsx
+++ b/frontend/src/data/useTranslatedToolRegistry.tsx
@@ -10,7 +10,6 @@ import ChangePermissions from '../tools/ChangePermissions';
import RemovePassword from '../tools/RemovePassword';
import { SubcategoryId, ToolCategory, ToolRegistry } from './toolsTaxonomy';
import AddWatermark from '../tools/AddWatermark';
-import Automate from '../tools/Automate';
// Hook to get the translated tool registry
export function useFlatToolRegistry(): ToolRegistry {
@@ -336,7 +335,7 @@ export function useFlatToolRegistry(): ToolRegistry {
"automate": {
icon: automation,
name: t("home.automate.title", "Automate"),
- component: Automate,
+ component: React.lazy(() => import('../tools/Automate')),
view: "format",
description: t("home.automate.desc", "Build multi-step workflows by chaining together PDF actions. Ideal for recurring tasks."),
category: ToolCategory.ADVANCED_TOOLS,
diff --git a/frontend/src/hooks/tools/addWatermark/useAddWatermarkParameters.ts b/frontend/src/hooks/tools/addWatermark/useAddWatermarkParameters.ts
index aa2f3bdda..574a22598 100644
--- a/frontend/src/hooks/tools/addWatermark/useAddWatermarkParameters.ts
+++ b/frontend/src/hooks/tools/addWatermark/useAddWatermarkParameters.ts
@@ -1,20 +1,5 @@
import { useState, useCallback } from 'react';
-import { defaultWatermarkParameters } from '../../../constants/addWatermarkConstants';
-
-export interface AddWatermarkParameters {
- watermarkType?: 'text' | 'image';
- watermarkText: string;
- watermarkImage?: File;
- fontSize: number; // Used for both text size and image size
- rotation: number;
- opacity: number;
- widthSpacer: number;
- heightSpacer: number;
- alphabet: string;
- customColor: string;
- convertPDFToImage: boolean;
-}
-
+import { defaultWatermarkParameters, AddWatermarkParameters } from '../../../constants/addWatermarkConstants';
export const useAddWatermarkParameters = () => {
const [parameters, setParameters] = useState(defaultWatermarkParameters);
diff --git a/frontend/src/hooks/useSuggestedTools.ts b/frontend/src/hooks/useSuggestedTools.ts
index ae6d77035..5f8891492 100644
--- a/frontend/src/hooks/useSuggestedTools.ts
+++ b/frontend/src/hooks/useSuggestedTools.ts
@@ -1,5 +1,4 @@
import { useMemo } from 'react';
-import { useToolWorkflow } from '../contexts/ToolWorkflowContext';
// Material UI Icons
import CompressIcon from '@mui/icons-material/Compress';
@@ -43,9 +42,7 @@ const ALL_SUGGESTED_TOOLS: Omit[] = [
}
];
-export function useSuggestedTools(): SuggestedTool[] {
- const { handleToolSelect, selectedToolKey } = useToolWorkflow();
-
+export function useSuggestedTools(selectedToolKey?: string | null, handleToolSelect?: (toolId: string) => void): SuggestedTool[] {
return useMemo(() => {
// Filter out the current tool
const filteredTools = ALL_SUGGESTED_TOOLS.filter(tool => tool.name !== selectedToolKey);
@@ -53,7 +50,7 @@ export function useSuggestedTools(): SuggestedTool[] {
// Add navigation function to each tool
return filteredTools.map(tool => ({
...tool,
- navigate: () => handleToolSelect(tool.name)
+ navigate: handleToolSelect ? () => handleToolSelect(tool.name) : () => {}
}));
}, [selectedToolKey, handleToolSelect]);
}
\ No newline at end of file
diff --git a/frontend/src/tools/Automate.tsx b/frontend/src/tools/Automate.tsx
index 7d5232b50..5c826c33b 100644
--- a/frontend/src/tools/Automate.tsx
+++ b/frontend/src/tools/Automate.tsx
@@ -10,6 +10,7 @@ import ToolSequence from "../components/tools/automate/ToolSequence";
import { useAutomateOperation } from "../hooks/tools/automate/useAutomateOperation";
import { BaseToolProps } from "../types/tool";
+import { useFlatToolRegistry } from "../data/useTranslatedToolRegistry";
const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
const { t } = useTranslation();
@@ -20,6 +21,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
const [stepData, setStepData] = useState({});
const automateOperation = useAutomateOperation();
+ const toolRegistry = useFlatToolRegistry();
const handleStepChange = (data: any) => {
setStepData(data);
@@ -51,6 +53,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
existingAutomation={stepData.automation}
onBack={() => handleStepChange({ step: 'selection' })}
onComplete={(automation: any) => handleStepChange({ step: 'sequence', automation })}
+ toolRegistry={toolRegistry}
/>
);