diff --git a/frontend/src/components/shared/QuickAccessBar.tsx b/frontend/src/components/shared/QuickAccessBar.tsx index c57e49c40..62edb311d 100644 --- a/frontend/src/components/shared/QuickAccessBar.tsx +++ b/frontend/src/components/shared/QuickAccessBar.tsx @@ -22,7 +22,7 @@ const QuickAccessBar = forwardRef(({ const { t } = useTranslation(); const { isRainbowMode } = useRainbowThemeContext(); const { openFilesModal, isFilesModalOpen } = useFilesModalContext(); - const { handleReaderToggle, handleBackToTools, handleToolSelect, selectedToolKey, leftPanelView, toolRegistry, readerMode } = useToolWorkflow(); + const { handleReaderToggle, handleBackToTools, handleToolSelect, selectedToolKey, leftPanelView, toolRegistry, readerMode, resetTool } = useToolWorkflow(); const [configModalOpen, setConfigModalOpen] = useState(false); const [activeButton, setActiveButton] = useState('tools'); const scrollableRef = useRef(null); @@ -74,7 +74,12 @@ const QuickAccessBar = forwardRef(({ type: 'navigation', onClick: () => { setActiveButton('automate'); - handleToolSelect('automate'); + // If already on automate tool, reset it directly + if (selectedToolKey === 'automate') { + resetTool('automate'); + } else { + handleToolSelect('automate'); + } } }, { diff --git a/frontend/src/contexts/ToolWorkflowContext.tsx b/frontend/src/contexts/ToolWorkflowContext.tsx index 2bbb3c9f4..2ab92572b 100644 --- a/frontend/src/contexts/ToolWorkflowContext.tsx +++ b/frontend/src/contexts/ToolWorkflowContext.tsx @@ -86,6 +86,10 @@ interface ToolWorkflowContextValue extends ToolWorkflowState { selectTool: (toolId: string) => void; clearToolSelection: () => void; + // Tool Reset Actions + registerToolReset: (toolId: string, resetFunction: () => void) => void; + resetTool: (toolId: string) => void; + // Workflow Actions (compound actions) handleToolSelect: (toolId: string) => void; handleBackToTools: () => void; @@ -106,16 +110,19 @@ interface ToolWorkflowProviderProps { export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { const [state, dispatch] = useReducer(toolWorkflowReducer, initialState); + // Store reset functions for tools + const [toolResetFunctions, setToolResetFunctions] = React.useState void>>({}); + // Navigation actions and state are available since we're inside NavigationProvider const { actions } = useNavigationActions(); const navigationState = useNavigationState(); - + // Tool management hook const { toolRegistry, getSelectedTool, } = useToolManagement(); - + // Get selected tool from navigation context const selectedTool = getSelectedTool(navigationState.selectedToolKey); @@ -147,13 +154,29 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { dispatch({ type: 'SET_SEARCH_QUERY', payload: query }); }, []); + // Tool reset methods + const registerToolReset = useCallback((toolId: string, resetFunction: () => void) => { + setToolResetFunctions(prev => ({ ...prev, [toolId]: resetFunction })); + }, []); + + const resetTool = useCallback((toolId: string) => { + // Use the current state directly instead of depending on the state in the closure + setToolResetFunctions(current => { + const resetFunction = current[toolId]; + if (resetFunction) { + resetFunction(); + } + return current; // Return the same state to avoid unnecessary updates + }); + }, []); // Empty dependency array makes this stable + // Workflow actions (compound actions that coordinate multiple state changes) const handleToolSelect = useCallback((toolId: string) => { actions.handleToolSelect(toolId); - + // Clear search query when selecting a tool setSearchQuery(''); - + // Handle view switching logic if (toolId === 'allTools' || toolId === 'read' || toolId === 'view-pdf') { setLeftPanelView('toolPicker'); @@ -212,6 +235,10 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { selectTool: actions.selectTool, clearToolSelection: actions.clearToolSelection, + // Tool Reset Actions + registerToolReset, + resetTool, + // Workflow Actions handleToolSelect, handleBackToTools, @@ -233,6 +260,8 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { setSearchQuery, actions.selectTool, actions.clearToolSelection, + registerToolReset, + resetTool, handleToolSelect, handleBackToTools, handleReaderToggle, @@ -251,36 +280,6 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) { 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 { - 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'); diff --git a/frontend/src/tools/Automate.tsx b/frontend/src/tools/Automate.tsx index 53040ffb0..d5cea69c8 100644 --- a/frontend/src/tools/Automate.tsx +++ b/frontend/src/tools/Automate.tsx @@ -1,9 +1,9 @@ -import React, { useState, useMemo } from "react"; +import React, { useState, useMemo, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { useFileContext } from "../contexts/FileContext"; import { useFileSelection } from "../contexts/FileContext"; import { useNavigation } from "../contexts/NavigationContext"; -import { CONVERT_SUPPORTED_FORMATS } from "../data/useTranslatedToolRegistry"; +import { useToolWorkflow } from "../contexts/ToolWorkflowContext"; import { createToolFlow } from "../components/tools/shared/createToolFlow"; import { createFilesToolStep } from "../components/tools/shared/FilesToolStep"; @@ -22,6 +22,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { const { t } = useTranslation(); const { selectedFiles } = useFileSelection(); const { setMode } = useNavigation(); + const { registerToolReset } = useToolWorkflow(); const [currentStep, setCurrentStep] = useState(AUTOMATION_STEPS.SELECTION); const [stepData, setStepData] = useState({ step: AUTOMATION_STEPS.SELECTION }); @@ -31,6 +32,28 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { const hasResults = automateOperation.files.length > 0 || automateOperation.downloadUrl !== null; const { savedAutomations, deleteAutomation, refreshAutomations, copyFromSuggested } = useSavedAutomations(); + // Use ref to store the latest reset function to avoid closure issues + const resetFunctionRef = React.useRef<() => void>(null); + + // Update ref with latest reset function + resetFunctionRef.current = () => { + automateOperation.resetResults(); + automateOperation.clearError(); + setCurrentStep(AUTOMATION_STEPS.SELECTION); + setStepData({ step: AUTOMATION_STEPS.SELECTION }); + }; + + // Register reset function with the tool workflow context - only once on mount + React.useEffect(() => { + const stableResetFunction = () => { + if (resetFunctionRef.current) { + resetFunctionRef.current(); + } + }; + + registerToolReset('automate', stableResetFunction); + }, [registerToolReset]); // Only depend on registerToolReset which should be stable + const handleStepChange = (data: AutomationStepData) => { // If navigating away from run step, reset automation results if (currentStep === AUTOMATION_STEPS.RUN && data.step !== AUTOMATION_STEPS.RUN) { @@ -139,7 +162,7 @@ const Automate = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { if (currentStep === AUTOMATION_STEPS.RUN && stepData.automation?.operations?.length) { const firstOperation = stepData.automation.operations[0]; const toolConfig = toolRegistry[firstOperation.operation]; - + // Check if the tool has supportedFormats that include non-PDF formats if (toolConfig?.supportedFormats && toolConfig.supportedFormats.length > 1) { return t('automate.files.placeholder.multiFormat', 'Select files to process (supports various formats)');