diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index d8adb5348..47d8767dc 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -3658,8 +3658,10 @@ "help": "Help", "account": "Account", "config": "Config", + "settings": "Settings", "adminSettings": "Admin Settings", - "allTools": "All Tools", + "allTools": "Tools", + "reader": "Reader", "helpMenu": { "toolsTour": "Tools Tour", "toolsTourDesc": "Learn what the tools can do", @@ -4800,7 +4802,7 @@ "maybeLater": "Maybe Later", "dontShowAgain": "Don't Show Again" }, - "allTools": "This is the All Tools panel, where you can browse and select from all available PDF tools.", + "allTools": "This is the Tools panel, where you can browse and select from all available PDF tools.", "selectCropTool": "Let's select the Crop tool to demonstrate how to use one of the tools.", "toolInterface": "This is the Crop tool interface. As you can see, there's not much there because we haven't added any PDF files to work with yet.", "filesButton": "The Files button on the Quick Access bar allows you to upload PDFs to use the tools on.", @@ -4860,7 +4862,8 @@ "admin": "Admin", "roleDescriptions": { "admin": "Can manage settings and invite members, with full administrative access.", - "member": "Can view and edit shared files, but cannot manage workspace settings or members." + "member": "Can view and edit shared files, but cannot manage workspace settings or members.", + "user": "User" }, "editRole": "Edit Role", "enable": "Enable", @@ -4941,6 +4944,7 @@ "copied": "Link copied to clipboard", "success": "Invite link generated successfully", "successWithEmail": "Invite link generated and sent via email", + "emailSent": "Invite link generated and sent via email", "emailFailed": "Invite link generated, but email failed", "emailFailedDetails": "Error: {0}. Please share the invite link manually.", "error": "Failed to generate invite link", diff --git a/frontend/public/locales/en-US/translation.json b/frontend/public/locales/en-US/translation.json index 10e923c90..76f89ff4a 100644 --- a/frontend/public/locales/en-US/translation.json +++ b/frontend/public/locales/en-US/translation.json @@ -3967,7 +3967,7 @@ "account": "Account", "config": "Config", "adminSettings": "Admin Settings", - "allTools": "All Tools" + "allTools": "Tools" }, "admin": { "error": "Error", diff --git a/frontend/src/core/components/AppProviders.tsx b/frontend/src/core/components/AppProviders.tsx index 24f793188..34d0e9e06 100644 --- a/frontend/src/core/components/AppProviders.tsx +++ b/frontend/src/core/components/AppProviders.tsx @@ -68,11 +68,11 @@ export function AppProviders({ children, appConfigRetryOptions, appConfigProvide - - - {children} - - + + + {children} + + diff --git a/frontend/src/core/components/onboarding/OnboardingTour.tsx b/frontend/src/core/components/onboarding/OnboardingTour.tsx index 8f7608b14..503a246dc 100644 --- a/frontend/src/core/components/onboarding/OnboardingTour.tsx +++ b/frontend/src/core/components/onboarding/OnboardingTour.tsx @@ -123,7 +123,7 @@ export default function OnboardingTour() { const stepsConfig: Record = { [TourStep.ALL_TOOLS]: { selector: '[data-tour="tool-panel"]', - content: t('onboarding.allTools', 'This is the All Tools panel, where you can browse and select from all available PDF tools.'), + content: t('onboarding.allTools', 'This is the Tools panel, where you can browse and select from all available PDF tools.'), position: 'center', padding: 0, action: () => { diff --git a/frontend/src/core/components/shared/AllToolsNavButton.tsx b/frontend/src/core/components/shared/AllToolsNavButton.tsx index 9a333878a..f5ba4a97c 100644 --- a/frontend/src/core/components/shared/AllToolsNavButton.tsx +++ b/frontend/src/core/components/shared/AllToolsNavButton.tsx @@ -35,20 +35,20 @@ const AllToolsNavButton: React.FC = ({ activeButton, set const iconNode = ( - + ); return ( - +
= ({ activeButton, set {iconNode} - {t("quickAccess.allTools", "All Tools")} + {t("quickAccess.allTools", "Tools")}
diff --git a/frontend/src/core/components/shared/LandingPage.tsx b/frontend/src/core/components/shared/LandingPage.tsx index 9ee11cbd2..88cbe73b6 100644 --- a/frontend/src/core/components/shared/LandingPage.tsx +++ b/frontend/src/core/components/shared/LandingPage.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { Container, Button, Group, useMantineColorScheme } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import LocalIcon from '@app/components/shared/LocalIcon'; @@ -7,6 +7,7 @@ import { useFileHandler } from '@app/hooks/useFileHandler'; import { useFilesModalContext } from '@app/contexts/FilesModalContext'; import { BASE_PATH } from '@app/constants/app'; import { useLogoPath } from '@app/hooks/useLogoPath'; +import { useFileManager } from '@app/hooks/useFileManager'; const LandingPage = () => { const { addFiles } = useFileHandler(); @@ -16,6 +17,8 @@ const LandingPage = () => { const { openFilesModal } = useFilesModalContext(); const [isUploadHover, setIsUploadHover] = React.useState(false); const logoPath = useLogoPath(); + const { loadRecentFiles } = useFileManager(); + const [hasRecents, setHasRecents] = React.useState(false); const handleFileDrop = async (files: File[]) => { await addFiles(files); @@ -38,6 +41,22 @@ const LandingPage = () => { event.target.value = ''; }; + // Determine if the user has any recent files (same source as File Manager) + useEffect(() => { + let isMounted = true; + (async () => { + try { + const files = await loadRecentFiles(); + if (isMounted) { + setHasRecents((files?.length || 0) > 0); + } + } catch (_err) { + if (isMounted) setHasRecents(false); + } + })(); + return () => { isMounted = false; }; + }, [loadRecentFiles]); + return ( {/* White PDF Page Background */} @@ -119,59 +138,89 @@ const LandingPage = () => { }} onMouseLeave={() => setIsUploadHover(false)} > - - + + + )} + {!hasRecents && ( + + + )} {/* Hidden file input for native file picker */} diff --git a/frontend/src/core/components/shared/QuickAccessBar.tsx b/frontend/src/core/components/shared/QuickAccessBar.tsx index 2f73132a8..f8a1d1649 100644 --- a/frontend/src/core/components/shared/QuickAccessBar.tsx +++ b/frontend/src/core/components/shared/QuickAccessBar.tsx @@ -16,6 +16,7 @@ import ActiveToolButton from "@app/components/shared/quickAccessBar/ActiveToolBu import AppConfigModal from '@app/components/shared/AppConfigModal'; import { useAppConfig } from '@app/contexts/AppConfigContext'; import { useOnboarding } from '@app/contexts/OnboardingContext'; + import { isNavButtonActive, getNavButtonStyle, @@ -88,7 +89,7 @@ const QuickAccessBar = forwardRef((_, ref) => { onClick: () => handleClick(), 'aria-label': config.name })} - size={isActive ? (config.size || 'lg') : 'lg'} + size={isActive ? 'lg' : 'md'} variant="subtle" style={getNavButtonStyle(config, activeButton, isFilesModalOpen, configModalOpen, selectedToolKey, leftPanelView)} className={isActive ? 'activeIconScale' : ''} @@ -108,9 +109,9 @@ const QuickAccessBar = forwardRef((_, ref) => { const mainButtons: ButtonConfig[] = [ { id: 'read', - name: t("quickAccess.read", "Read"), - icon: , - size: 'lg', + name: t("quickAccess.reader", "Reader"), + icon: , + size: 'md', isRound: false, type: 'navigation', onClick: () => { @@ -118,23 +119,11 @@ const QuickAccessBar = forwardRef((_, ref) => { handleReaderToggle(); } }, - // { - // id: 'sign', - // name: t("quickAccess.sign", "Sign"), - // icon: , - // size: 'lg', - // isRound: false, - // type: 'navigation', - // onClick: () => { - // setActiveButton('sign'); - // handleToolSelect('sign'); - // } - // }, { id: 'automate', name: t("quickAccess.automate", "Automate"), - icon: , - size: 'lg', + icon: , + size: 'md', isRound: false, type: 'navigation', onClick: () => { @@ -147,37 +136,36 @@ const QuickAccessBar = forwardRef((_, ref) => { } } }, - ]; - - const middleButtons: ButtonConfig[] = [ { id: 'files', name: t("quickAccess.files", "Files"), - icon: , + icon: , isRound: true, - size: 'lg', + size: 'md', type: 'modal', onClick: handleFilesButtonClick }, - //TODO: Activity - //{ - // id: 'activity', - // name: t("quickAccess.activity", "Activity"), - // icon: , - // isRound: true, - // size: 'lg', - // type: 'navigation', - // onClick: () => setActiveButton('activity') - //}, ]; + const middleButtons: ButtonConfig[] = []; + //TODO: Activity + //{ + // id: 'activity', + // name: t("quickAccess.activity", "Activity"), + // icon: , + // isRound: true, + // size: 'lg', + // type: 'navigation', + // onClick: () => setActiveButton('activity') + //}, + const bottomButtons: ButtonConfig[] = [ { id: 'help', name: t("quickAccess.help", "Help"), - icon: , + icon: , isRound: true, - size: 'lg', + size: 'md', type: 'action', onClick: () => { // This will be overridden by the wrapper logic @@ -185,9 +173,9 @@ const QuickAccessBar = forwardRef((_, ref) => { }, { id: 'config', - name: config?.enableLogin ? t("quickAccess.account", "Account") : t("quickAccess.config", "Config"), - icon: config?.enableLogin ? : , - size: 'lg', + name: t("quickAccess.settings", "Settings"), + icon: , + size: 'md', type: 'modal', onClick: () => { navigate('/settings/overview'); @@ -200,7 +188,7 @@ const QuickAccessBar = forwardRef((_, ref) => {
((_, ref) => { ))} - {/* Divider after main buttons */} - + {/* Divider after main buttons (creates gap) */} + {middleButtons.length === 0 && ( + + )} {/* Middle section */} - - {middleButtons.map((config, index) => ( - - {renderNavButton(config, index)} - - ))} - + {middleButtons.length > 0 && ( + <> + + + {middleButtons.map((config, index) => ( + + {renderNavButton(config, index)} + + ))} + + + )} {/* Spacer to push bottom buttons to bottom */}
diff --git a/frontend/src/core/components/shared/RightRail.tsx b/frontend/src/core/components/shared/RightRail.tsx index 8efe507a1..bec38b0fe 100644 --- a/frontend/src/core/components/shared/RightRail.tsx +++ b/frontend/src/core/components/shared/RightRail.tsx @@ -14,6 +14,8 @@ import { ViewerContext } from '@app/contexts/ViewerContext'; import { useSignature } from '@app/contexts/SignatureContext'; import LocalIcon from '@app/components/shared/LocalIcon'; import { RightRailFooterExtensions } from '@app/components/rightRail/RightRailFooterExtensions'; +import DarkModeIcon from '@mui/icons-material/DarkMode'; +import LightModeIcon from '@mui/icons-material/LightMode'; import { useSidebarContext } from '@app/contexts/SidebarContext'; import { RightRailButtonConfig, RightRailRenderContext, RightRailSection } from '@app/types/rightRail'; @@ -39,7 +41,7 @@ export default function RightRail() { const { sidebarRefs } = useSidebarContext(); const { t } = useTranslation(); const viewerContext = React.useContext(ViewerContext); - const { toggleTheme } = useRainbowThemeContext(); + const { toggleTheme, themeMode } = useRainbowThemeContext(); const { buttons, actions, allButtonsDisabled } = useRightRail(); const { pageEditorFunctions, toolPanelMode, leftPanelView } = useToolWorkflow(); @@ -195,7 +197,11 @@ export default function RightRail() { className="right-rail-icon" onClick={toggleTheme} > - + {themeMode === 'dark' ? ( + + ) : ( + + )} , t('rightRail.toggleTheme', 'Toggle Theme') )} diff --git a/frontend/src/core/components/shared/TopControls.tsx b/frontend/src/core/components/shared/TopControls.tsx index 089f5b769..f414cc91a 100644 --- a/frontend/src/core/components/shared/TopControls.tsx +++ b/frontend/src/core/components/shared/TopControls.tsx @@ -2,10 +2,10 @@ import React, { useState, useCallback, useMemo } from "react"; import { SegmentedControl, Loader } from "@mantine/core"; import { useRainbowThemeContext } from '@app/components/shared/RainbowThemeProvider'; import rainbowStyles from '@app/styles/rainbow.module.css'; -import VisibilityIcon from "@mui/icons-material/Visibility"; +import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; +import GridViewIcon from "@mui/icons-material/GridView"; import FolderIcon from "@mui/icons-material/Folder"; import PictureAsPdfIcon from "@mui/icons-material/PictureAsPdf"; -import { LocalIcon } from '@app/components/shared/LocalIcon'; import { WorkbenchType, isValidWorkbench } from '@app/types/workbench'; import { PageEditorFileDropdown } from '@app/components/shared/PageEditorFileDropdown'; import type { CustomWorkbenchViewInstance } from '@app/contexts/ToolWorkflowContext'; @@ -55,7 +55,7 @@ const createViewOptions = ( {switchingTo === "viewer" ? ( ) : ( - + )}
), @@ -84,7 +84,7 @@ const createViewOptions = ( {switchingTo === "pageEditor" ? ( ) : ( - + )}
), diff --git a/frontend/src/core/components/shared/quickAccessBar/ActiveToolButton.tsx b/frontend/src/core/components/shared/quickAccessBar/ActiveToolButton.tsx index 043cb09f9..81ed8f43a 100644 --- a/frontend/src/core/components/shared/quickAccessBar/ActiveToolButton.tsx +++ b/frontend/src/core/components/shared/quickAccessBar/ActiveToolButton.tsx @@ -1,13 +1,13 @@ /** * ActiveToolButton - Shows the currently selected tool at the top of the Quick Access Bar * - * When a user selects a tool from the All Tools list, this component displays the tool's + * When a user selects a tool from the Tools list, this component displays the tool's * icon and name at the top of the navigation bar. It provides a quick way to see which - * tool is currently active and offers a back button to return to the All Tools list. + * tool is currently active and offers a back button to return to the Tools list. * * Features: * - Shows tool icon and name when a tool is selected - * - Hover to reveal back arrow for returning to All Tools + * - Hover to reveal back arrow for returning to Tools * - Smooth slide-down/slide-up animations * - Only appears for tools that don't have dedicated nav buttons (read, sign, automate) */ @@ -149,7 +149,7 @@ const ActiveToolButton: React.FC = ({ setActiveButton }) handleBackToTools(); }); }} - size={'xl'} + size={'lg'} variant="subtle" onMouseEnter={() => setIsBackHover(true)} onMouseLeave={() => setIsBackHover(false)} @@ -165,7 +165,7 @@ const ActiveToolButton: React.FC = ({ setActiveButton }) > {isBackHover ? ( - + ) : ( indicatorTool.icon )} diff --git a/frontend/src/core/components/shared/quickAccessBar/QuickAccessBar.css b/frontend/src/core/components/shared/quickAccessBar/QuickAccessBar.css index e2c261b52..cbea90655 100644 --- a/frontend/src/core/components/shared/quickAccessBar/QuickAccessBar.css +++ b/frontend/src/core/components/shared/quickAccessBar/QuickAccessBar.css @@ -1,15 +1,29 @@ .activeIconScale { - transform: scale(1.3); + transform: scale(1); transition: transform 0.2s; z-index: 1; + width: calc(1.75rem + 10px) !important; + height: calc(1.75rem + 10px) !important; +} + +.activeIconScale .iconContainer { + width: calc(1.5rem + 10px); + height: calc(1.5rem + 10px); +} + +.activeIconScale .iconContainer svg, +.activeIconScale .iconContainer img { + width: calc(1.25rem + 10px) !important; + height: calc(1.25rem + 10px) !important; } .iconContainer { display: flex; align-items: center; justify-content: center; - width: 2rem; - height: 2rem; + width: 1.5rem; + height: 1.5rem; + transition: width 0.2s, height 0.2s; } /* Action icon styles */ @@ -24,9 +38,9 @@ /* Main container styles */ .quick-access-bar-main { background-color: var(--bg-muted); - width: 5rem; - min-width: 5rem; - max-width: 5rem; + width: 4rem; + min-width: 4rem; + max-width: 4rem; position: relative; z-index: 10; } @@ -34,9 +48,9 @@ /* Rainbow mode container */ .quick-access-bar-main.rainbow-mode { background-color: var(--bg-muted); - width: 5rem; - min-width: 5rem; - max-width: 5rem; + width: 4rem; + min-width: 4rem; + max-width: 4rem; position: relative; z-index: 10; } @@ -57,7 +71,7 @@ /* Nav header divider */ .nav-header-divider { - width: 3.75rem; + width: 3rem; border-color: var(--color-gray-300); margin-top: 0.5rem; margin-bottom: 1rem; @@ -85,7 +99,7 @@ /* Overflow divider */ .overflow-divider { - width: 3.75rem; + width: 3rem; border-color: var(--color-gray-300); margin: 0 0.5rem; } @@ -143,7 +157,7 @@ /* Content divider */ .content-divider { - width: 3.75rem; + width: 3rem; border-color: var(--color-gray-300); margin: 1rem 0; } @@ -241,7 +255,7 @@ /* Divider that animates growing from top */ .current-tool-divider { - width: 3.75rem; + width: 3rem; border-color: var(--color-gray-300); margin: 0.5rem auto 0.5rem auto; transform-origin: top; diff --git a/frontend/src/core/components/tools/ToolPicker.tsx b/frontend/src/core/components/tools/ToolPicker.tsx index 396f97ed6..b9da16689 100644 --- a/frontend/src/core/components/tools/ToolPicker.tsx +++ b/frontend/src/core/components/tools/ToolPicker.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useRef, useLayoutEffect, useState } from "react"; +import React, { useMemo, useRef } from "react"; import { Box, Stack } from "@mantine/core"; import { useTranslation } from "react-i18next"; import { ToolRegistryEntry } from "@app/data/toolsTaxonomy"; @@ -8,11 +8,10 @@ import type { SubcategoryGroup } from "@app/hooks/useToolSections"; import { useFavoriteToolItems } from "@app/hooks/tools/useFavoriteToolItems"; import NoToolsFound from "@app/components/tools/shared/NoToolsFound"; import { renderToolButtons } from "@app/components/tools/shared/renderToolButtons"; -import Badge from "@app/components/shared/Badge"; -import SubcategoryHeader from "@app/components/tools/shared/SubcategoryHeader"; import ToolButton from "@app/components/tools/toolPicker/ToolButton"; import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext"; import { ToolId } from "@app/types/toolId"; +import { getSubcategoryLabel } from "@app/data/toolsTaxonomy"; interface ToolPickerProps { selectedToolKey: string | null; @@ -23,49 +22,8 @@ interface ToolPickerProps { const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = false }: ToolPickerProps) => { const { t } = useTranslation(); - const [quickHeaderHeight, setQuickHeaderHeight] = useState(0); - const [allHeaderHeight, setAllHeaderHeight] = useState(0); const scrollableRef = useRef(null); - const quickHeaderRef = useRef(null); - const allHeaderRef = useRef(null); - const quickAccessRef = useRef(null); - const allToolsRef = useRef(null); - - // Keep header heights in sync with any dynamic size changes - useLayoutEffect(() => { - const update = () => { - if (quickHeaderRef.current) { - setQuickHeaderHeight(quickHeaderRef.current.offsetHeight || 0); - } - if (allHeaderRef.current) { - setAllHeaderHeight(allHeaderRef.current.offsetHeight || 0); - } - }; - - update(); - - // Update on window resize - window.addEventListener("resize", update); - - // Update on element resize (e.g., font load, badge count change, zoom) - const observers: ResizeObserver[] = []; - if (typeof ResizeObserver !== "undefined") { - const observe = (el: HTMLDivElement | null, cb: () => void) => { - if (!el) return; - const ro = new ResizeObserver(() => cb()); - ro.observe(el); - observers.push(ro); - }; - observe(quickHeaderRef.current, update); - observe(allHeaderRef.current, update); - } - - return () => { - window.removeEventListener("resize", update); - observers.forEach(o => o.disconnect()); - }; - }, []); const { sections: visibleSections } = useToolSections(filteredTools); const { favoriteTools, toolRegistry } = useToolWorkflow(); @@ -84,31 +42,25 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa return items; }, [quickSection]); - const recommendedCount = useMemo(() => favoriteToolItems.length + recommendedItems.length, [favoriteToolItems.length, recommendedItems.length]); const allSection = useMemo( () => visibleSections.find(s => s.key === 'all'), [visibleSections] ); - const scrollTo = (ref: React.RefObject) => { - const container = scrollableRef.current; - const target = ref.current; - if (container && target) { - const stackedOffset = ref === allToolsRef - ? (quickHeaderHeight + allHeaderHeight) - : quickHeaderHeight; - const top = target.offsetTop - container.offsetTop - (stackedOffset || 0); - container.scrollTo({ - top: Math.max(0, top), - behavior: "smooth" - }); - } - }; - // Build flat list by subcategory for search mode const emptyFilteredTools: ToolPickerProps['filteredTools'] = []; const effectiveFilteredForSearch: ToolPickerProps['filteredTools'] = isSearching ? filteredTools : emptyFilteredTools; const { searchGroups } = useToolSections(effectiveFilteredForSearch); + const headerTextStyle: React.CSSProperties = { + fontSize: "0.75rem", + fontWeight: 500, + padding: "0.5rem 0 0.25rem 0.5rem", + textTransform: "none", + color: "var(--text-secondary, rgba(0, 0, 0, 0.6))", + opacity: 0.7 + }; + const toTitleCase = (s: string) => + s.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase()); return ( ) : ( <> - {quickSection && ( - <> -
scrollTo(quickAccessRef)} - > - {t("toolPicker.quickAccess", "QUICK ACCESS")} - - {recommendedCount} - -
- - - - {favoriteToolItems.length > 0 && ( - - -
- {favoriteToolItems.map(({ id, tool }) => ( - - ))} -
-
- )} - {recommendedItems.length > 0 && ( - - -
- {recommendedItems.map(({ id, tool }) => ( - - ))} -
-
- )} -
+ {/* Flat list: favorites and recommended first, then all subcategories */} + + {favoriteToolItems.length > 0 && ( + +
+ {t('toolPanel.fullscreen.favorites', 'Favourites')} +
+
+ {favoriteToolItems.map(({ id, tool }) => ( + + ))} +
- - )} - - {allSection && ( - <> -
scrollTo(allToolsRef)} - > - {t("toolPicker.allTools", "ALL TOOLS")} - - {allSection?.subcategories.reduce((acc, sc) => acc + sc.tools.length, 0)} - -
- - - - {allSection?.subcategories.map((sc: SubcategoryGroup) => - renderToolButtons(t, sc, selectedToolKey, onSelect, true, false, undefined, true) - )} - + )} + {recommendedItems.length > 0 && ( + +
+ {t('toolPanel.fullscreen.recommended', 'Recommended')} +
+
+ {recommendedItems.map(({ id, tool }) => ( + + ))} +
- - )} + )} + {allSection && allSection.subcategories.map((sc: SubcategoryGroup) => ( + +
+ {toTitleCase(getSubcategoryLabel(t, sc.subcategoryId))} +
+ {renderToolButtons(t, sc, selectedToolKey, onSelect, false, false, undefined, true)} +
+ ))} +
{!quickSection && !allSection && } diff --git a/frontend/src/core/components/tools/automate/ToolSelector.tsx b/frontend/src/core/components/tools/automate/ToolSelector.tsx index 3fc6b0a2a..6413f59e7 100644 --- a/frontend/src/core/components/tools/automate/ToolSelector.tsx +++ b/frontend/src/core/components/tools/automate/ToolSelector.tsx @@ -80,7 +80,7 @@ export default function ToolSelector({ // If no sections, create a simple group from filtered tools if (baseFilteredTools.length > 0) { return [{ - name: 'All Tools', + name: 'Tools', subcategoryId: 'all' as any, tools: baseFilteredTools.map(([key, tool]) => ({ id: key, tool })) }]; diff --git a/frontend/src/core/contexts/TourOrchestrationContext.tsx b/frontend/src/core/contexts/TourOrchestrationContext.tsx index 3c55fd394..d8b7afdc0 100644 --- a/frontend/src/core/contexts/TourOrchestrationContext.tsx +++ b/frontend/src/core/contexts/TourOrchestrationContext.tsx @@ -68,7 +68,7 @@ export const TourOrchestrationProvider: React.FC<{ children: React.ReactNode }> const restoreWorkbenchState = useCallback(async () => { console.log('Restoring workbench state, saved files count:', savedFilesRef.current.length); - // Go back to All Tools + // Go back to Tools handleBackToTools(); // Clear all files (including tour sample) diff --git a/frontend/src/core/hooks/useToolSections.ts b/frontend/src/core/hooks/useToolSections.ts index 79850f559..a94e4c474 100644 --- a/frontend/src/core/hooks/useToolSections.ts +++ b/frontend/src/core/hooks/useToolSections.ts @@ -64,8 +64,12 @@ export function useToolSections( Object.entries(subs).forEach(([s, tools]) => { const subcategoryId = s as SubcategoryId; - if (!all[subcategoryId]) all[subcategoryId] = []; - all[subcategoryId].push(...tools); + // Build the 'all' collection without duplicating recommended tools + // Recommended tools are shown in the Quick section only + if (categoryId !== ToolCategoryId.RECOMMENDED_TOOLS) { + if (!all[subcategoryId]) all[subcategoryId] = []; + all[subcategoryId].push(...tools); + } }); if (categoryId === ToolCategoryId.RECOMMENDED_TOOLS) { diff --git a/frontend/src/core/pages/HomePage.tsx b/frontend/src/core/pages/HomePage.tsx index 2731ad100..4aee49272 100644 --- a/frontend/src/core/pages/HomePage.tsx +++ b/frontend/src/core/pages/HomePage.tsx @@ -218,7 +218,7 @@ export default function HomePage() {
+ + + + )} + + )} + + {/* Email Mode */} + {inviteMode === 'email' && config?.enableEmailInvites && ( + <> +