From 1bd3b019dcdb82e7a696a6d6cbccffea0f74d547 Mon Sep 17 00:00:00 2001 From: Connor Yoh Date: Thu, 7 Aug 2025 14:37:04 +0100 Subject: [PATCH] Restructure homepage --- frontend/src/components/layout/Workbench.tsx | 203 +++++++++++++++++ frontend/src/components/tools/ToolPanel.tsx | 105 +++++++++ frontend/src/components/tools/ToolPicker.tsx | 25 +-- frontend/src/pages/HomePage.tsx | 215 +++---------------- 4 files changed, 345 insertions(+), 203 deletions(-) create mode 100644 frontend/src/components/layout/Workbench.tsx create mode 100644 frontend/src/components/tools/ToolPanel.tsx diff --git a/frontend/src/components/layout/Workbench.tsx b/frontend/src/components/layout/Workbench.tsx new file mode 100644 index 000000000..229da12ff --- /dev/null +++ b/frontend/src/components/layout/Workbench.tsx @@ -0,0 +1,203 @@ +import React from 'react'; +import { Box } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import { useRainbowThemeContext } from '../shared/RainbowThemeProvider'; +import { ToolConfiguration } from '../../types/tool'; +import { PageEditorFunctions } from '../../types/pageEditor'; + +import TopControls from '../shared/TopControls'; +import FileEditor from '../fileEditor/FileEditor'; +import PageEditor from '../pageEditor/PageEditor'; +import PageEditorControls from '../pageEditor/PageEditorControls'; +import Viewer from '../viewer/Viewer'; +import ToolRenderer from '../tools/ToolRenderer'; +import LandingPage from '../shared/LandingPage'; + +interface WorkbenchProps { + /** Currently active files */ + activeFiles: File[]; + /** Current view mode */ + currentView: string; + /** Currently selected tool key */ + selectedToolKey: string | null; + /** Selected tool configuration */ + selectedTool: ToolConfiguration | null; + /** Whether sidebars are visible */ + sidebarsVisible: boolean; + /** Function to set sidebars visibility */ + setSidebarsVisible: (visible: boolean) => void; + /** File to preview */ + previewFile: File | null; + /** Function to clear preview file */ + setPreviewFile: (file: File | null) => void; + /** Page editor functions */ + pageEditorFunctions: PageEditorFunctions | null; + /** Function to set page editor functions */ + setPageEditorFunctions: (functions: PageEditorFunctions | null) => void; + /** Handler for view changes */ + onViewChange: (view: string) => void; + /** Handler for tool selection */ + onToolSelect: (toolId: string) => void; + /** Handler for setting left panel view */ + onSetLeftPanelView: (view: 'toolPicker' | 'toolContent') => void; + /** Handler for adding files to active files */ + onAddToActiveFiles: (file: File) => void; +} + +export default function Workbench({ + activeFiles, + currentView, + selectedToolKey, + selectedTool, + sidebarsVisible, + setSidebarsVisible, + previewFile, + setPreviewFile, + pageEditorFunctions, + setPageEditorFunctions, + onViewChange, + onToolSelect, + onSetLeftPanelView, + onAddToActiveFiles +}: WorkbenchProps) { + const { t } = useTranslation(); + const { isRainbowMode } = useRainbowThemeContext(); + + const handlePreviewClose = () => { + setPreviewFile(null); + const previousMode = sessionStorage.getItem('previousMode'); + if (previousMode === 'split') { + onToolSelect('split'); + onViewChange('split'); + onSetLeftPanelView('toolContent'); + sessionStorage.removeItem('previousMode'); + } else if (previousMode === 'compress') { + onToolSelect('compress'); + onViewChange('compress'); + onSetLeftPanelView('toolContent'); + sessionStorage.removeItem('previousMode'); + } else if (previousMode === 'convert') { + onToolSelect('convert'); + onViewChange('convert'); + onSetLeftPanelView('toolContent'); + sessionStorage.removeItem('previousMode'); + } else { + onViewChange('fileEditor'); + } + }; + + const renderMainContent = () => { + if (!activeFiles[0]) { + return ( + + ); + } + + switch (currentView) { + case "fileEditor": + return ( + { + onViewChange("pageEditor"); + }, + onMergeFiles: (filesToMerge) => { + filesToMerge.forEach(onAddToActiveFiles); + onViewChange("viewer"); + } + })} + /> + ); + + case "viewer": + return ( + + ); + + case "pageEditor": + return ( + <> + + {pageEditorFunctions && ( + + )} + + ); + + default: + // Check if it's a tool view + if (selectedToolKey && selectedTool) { + return ( + + ); + } + return ( + + ); + } + }; + + return ( + + {/* Top Controls */} + + + {/* Main content area */} + + {renderMainContent()} + + + ); +} \ No newline at end of file diff --git a/frontend/src/components/tools/ToolPanel.tsx b/frontend/src/components/tools/ToolPanel.tsx new file mode 100644 index 000000000..a8005f2d5 --- /dev/null +++ b/frontend/src/components/tools/ToolPanel.tsx @@ -0,0 +1,105 @@ +import React, { useState } from 'react'; +import { Button, TextInput } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import { useRainbowThemeContext } from '../shared/RainbowThemeProvider'; +import { ToolRegistry, ToolConfiguration } from '../../types/tool'; +import ToolPicker from './ToolPicker'; +import ToolRenderer from './ToolRenderer'; +import rainbowStyles from '../../styles/rainbow.module.css'; + +interface ToolPanelProps { + /** Whether the tool panel is visible */ + visible: boolean; + /** Whether reader mode is active (hides the panel) */ + readerMode: boolean; + /** Current view mode: 'toolPicker' or 'toolContent' */ + leftPanelView: 'toolPicker' | 'toolContent'; + /** Currently selected tool key */ + selectedToolKey: string | null; + /** Selected tool configuration */ + selectedTool: ToolConfiguration | null; + /** Tool registry with all available tools */ + toolRegistry: ToolRegistry; + /** Handler for tool selection */ + onToolSelect: (toolId: string) => void; + /** Handler for back to tools navigation */ + onBackToTools: () => void; + /** Handler for file preview */ + onPreviewFile?: (file: File | null) => void; +} + +export default function ToolPanel({ + visible, + readerMode, + leftPanelView, + selectedToolKey, + selectedTool, + toolRegistry, + onToolSelect, + onBackToTools, + onPreviewFile +}: ToolPanelProps) { + const { t } = useTranslation(); + const { isRainbowMode } = useRainbowThemeContext(); + const [search, setSearch] = useState(""); + + // Filter tools based on search + const filteredTools = Object.entries(toolRegistry).filter(([_, { name }]) => + name.toLowerCase().includes(search.toLowerCase()) + ); + + return ( +
+
+ {/* Search Bar - Always visible at the top */} +
+ setSearch(e.currentTarget.value)} + autoComplete="off" + size="sm" + /> +
+ + {leftPanelView === 'toolPicker' ? ( + // Tool Picker View +
+ +
+ ) : ( + // Selected Tool Content View +
+ {/* Tool content */} +
+ +
+
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/tools/ToolPicker.tsx b/frontend/src/components/tools/ToolPicker.tsx index 7b678de98..d392f21b6 100644 --- a/frontend/src/components/tools/ToolPicker.tsx +++ b/frontend/src/components/tools/ToolPicker.tsx @@ -1,32 +1,21 @@ -import React, { useState } from "react"; -import { Box, Text, Stack, Button, TextInput, Group } from "@mantine/core"; +import React from "react"; +import { Box, Text, Stack, Button } from "@mantine/core"; import { useTranslation } from "react-i18next"; import { ToolRegistry } from "../../types/tool"; interface ToolPickerProps { selectedToolKey: string | null; onSelect: (id: string) => void; - toolRegistry: ToolRegistry; + /** Pre-filtered tools to display */ + filteredTools: [string, ToolRegistry[string]][]; } -const ToolPicker = ({ selectedToolKey, onSelect, toolRegistry }: ToolPickerProps) => { +const ToolPicker = ({ selectedToolKey, onSelect, filteredTools }: ToolPickerProps) => { const { t } = useTranslation(); - const [search, setSearch] = useState(""); - - const filteredTools = Object.entries(toolRegistry).filter(([_, { name }]) => - name.toLowerCase().includes(search.toLowerCase()) - ); return ( - - setSearch(e.currentTarget.value)} - mb="md" - autoComplete="off" - /> - + + {filteredTools.length === 0 ? ( {t("toolPicker.noToolsFound", "No tools found")} diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index cccce7667..6df32611f 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -4,25 +4,16 @@ import { useFileContext } from "../contexts/FileContext"; import { FileSelectionProvider, useFileSelection } from "../contexts/FileSelectionContext"; import { useToolManagement } from "../hooks/useToolManagement"; import { useFileHandler } from "../hooks/useFileHandler"; -import { Group, Box, Button } from "@mantine/core"; -import { useRainbowThemeContext } from "../components/shared/RainbowThemeProvider"; +import { Group } from "@mantine/core"; import { PageEditorFunctions } from "../types/pageEditor"; -import rainbowStyles from '../styles/rainbow.module.css'; -import ToolPicker from "../components/tools/ToolPicker"; -import TopControls from "../components/shared/TopControls"; -import FileEditor from "../components/fileEditor/FileEditor"; -import PageEditor from "../components/pageEditor/PageEditor"; -import PageEditorControls from "../components/pageEditor/PageEditorControls"; -import Viewer from "../components/viewer/Viewer"; -import ToolRenderer from "../components/tools/ToolRenderer"; +import ToolPanel from "../components/tools/ToolPanel"; +import Workbench from "../components/layout/Workbench"; import QuickAccessBar from "../components/shared/QuickAccessBar"; -import LandingPage from "../components/shared/LandingPage"; import FileUploadModal from "../components/shared/FileUploadModal"; function HomePageContent() { const { t } = useTranslation(); - const { isRainbowMode } = useRainbowThemeContext(); const fileContext = useFileContext(); const { activeFiles, currentView, setCurrentView } = fileContext; @@ -55,8 +46,6 @@ function HomePageContent() { } }, [selectedTool, setMaxFiles, setIsToolMode, setSelectedFiles]); - - const handleToolSelect = useCallback( (id: string) => { selectTool(id); @@ -81,16 +70,12 @@ function HomePageContent() { setCurrentView(view as any); }, [setCurrentView]); - - - return ( - {/* Quick Access Bar */} - {/* Left: Tool Picker or Selected Tool Panel */} -
-
- {leftPanelView === 'toolPicker' ? ( - // Tool Picker View -
- -
- ) : ( - // Selected Tool Content View -
- {/* Back button */} -
- -
+ - {/* Tool title */} -
-

{selectedTool?.name}

-
- - {/* Tool content */} -
- -
-
- )} -
-
- - {/* Main View */} - - {/* Top Controls */} - - {/* Main content area */} - - {!activeFiles[0] ? ( - - ) : currentView === "fileEditor" ? ( - { - handleViewChange("pageEditor"); - }, - onMergeFiles: (filesToMerge) => { - filesToMerge.forEach(addToActiveFiles); - handleViewChange("viewer"); - } - })} - /> - ) : currentView === "viewer" ? ( - { - setPreviewFile(null); // Clear preview file - const previousMode = sessionStorage.getItem('previousMode'); - if (previousMode === 'split') { - selectTool('split'); - setCurrentView('split'); - setLeftPanelView('toolContent'); - sessionStorage.removeItem('previousMode'); - } else if (previousMode === 'compress') { - selectTool('compress'); - setCurrentView('compress'); - setLeftPanelView('toolContent'); - sessionStorage.removeItem('previousMode'); - } else if (previousMode === 'convert') { - selectTool('convert'); - setCurrentView('convert'); - setLeftPanelView('toolContent'); - sessionStorage.removeItem('previousMode'); - } else { - setCurrentView('fileEditor'); - } - } - })} - /> - ) : currentView === "pageEditor" ? ( - <> - - {pageEditorFunctions && ( - - )} - - ) : selectedToolKey && selectedTool ? ( - // Fallback: if tool is selected but not in fileEditor view, show tool in main area - - ) : ( - - )} - - + {/* Global Modals */}