diff --git a/frontend/editor/src/core/components/shared/workbenchBar/WorkbenchBarMobileActions.tsx b/frontend/editor/src/core/components/shared/workbenchBar/WorkbenchBarMobileActions.tsx index 6d1cf0efdf..7675c37e33 100644 --- a/frontend/editor/src/core/components/shared/workbenchBar/WorkbenchBarMobileActions.tsx +++ b/frontend/editor/src/core/components/shared/workbenchBar/WorkbenchBarMobileActions.tsx @@ -25,6 +25,13 @@ export default function WorkbenchBarMobileActions({ }: WorkbenchBarActionsProps) { const { t } = useTranslation(); const exportDisabled = actionsDisabled || policyEnforcing; + const showPrint = currentView === "viewer"; + const showFileActions = !isCustomView; + + // Custom workbench views own their content, so none of these apply. The + // desktop cluster renders nothing at all in that case; without this the + // trigger would still be there, opening an empty dropdown. + if (!showPrint && !showFileActions) return null; return ( @@ -39,7 +46,7 @@ export default function WorkbenchBarMobileActions({ - {currentView === "viewer" && ( + {showPrint && ( } disabled={exportDisabled} @@ -48,7 +55,7 @@ export default function WorkbenchBarMobileActions({ {t("workbenchBar.print", "Print PDF")} )} - {!isCustomView && ( + {showFileActions && ( )} - {!isCustomView && saveAsIconName && ( + {showFileActions && saveAsIconName && ( @@ -74,7 +81,7 @@ export default function WorkbenchBarMobileActions({ {t("workbenchBar.saveAs", "Save As")} )} - {!isCustomView && ( + {showFileActions && ( <> diff --git a/frontend/editor/src/core/components/tools/ToolPanel.css b/frontend/editor/src/core/components/tools/ToolPanel.css index 427375db1a..3e3985103f 100644 --- a/frontend/editor/src/core/components/tools/ToolPanel.css +++ b/frontend/editor/src/core/components/tools/ToolPanel.css @@ -183,6 +183,17 @@ } } +/* In-panel tool filter. Aligned with .tool-picker__compact's inline padding so + the field lines up with the tool rows underneath it. */ +.tool-panel__search { + flex-shrink: 0; + padding: 0.5rem var(--mantine-spacing-sm) 0; +} + +.tool-panel__search .search-input-container { + margin: 0; +} + .tool-panel__compact-header-actions { display: flex; align-items: center; diff --git a/frontend/editor/src/core/components/tools/ToolPanel.tsx b/frontend/editor/src/core/components/tools/ToolPanel.tsx index 2d6006a8eb..d51ec9e0bf 100644 --- a/frontend/editor/src/core/components/tools/ToolPanel.tsx +++ b/frontend/editor/src/core/components/tools/ToolPanel.tsx @@ -4,6 +4,7 @@ import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext"; import ToolPicker from "@app/components/tools/ToolPicker"; import SearchResults from "@app/components/tools/SearchResults"; import ToolRenderer from "@app/components/tools/ToolRenderer"; +import ToolSearch from "@app/components/tools/toolPicker/ToolSearch"; import { ToolPanelViewerBar } from "@app/components/tools/ToolPanelViewerBar"; import { ToolId } from "@app/types/toolId"; @@ -20,6 +21,11 @@ interface ToolPanelProps { onToolSelect?: (id: ToolId) => void; /** Whether to render the compact (favourites + recommended only) view. */ compact?: boolean; + /** + * Render a tool filter at the head of the panel. Set where the workbench + * bar's super search is out of reach, so the list stays searchable in place. + */ + showSearch?: boolean; } /** Tool list and renderer for the right rail; rail chrome lives in RightSidebar. */ @@ -28,24 +34,44 @@ export default function ToolPanel({ onShowAllTools, onToolSelect, compact: compactProp, + showSearch = false, }: ToolPanelProps) { const { t } = useTranslation(); const { leftPanelView, searchQuery, + setSearchQuery, filteredTools, + toolRegistry, selectedToolKey, handleToolSelect, setPreviewFile, } = useToolWorkflow(); const selectTool = onToolSelect ?? handleToolSelect; + // Only offer the filter over the list itself; once a tool is open the panel + // belongs to that tool. Deriving the results branch from the same flag keeps + // the input and what it filters from drifting apart. + const panelSearch = showSearch && leftPanelView === "toolPicker"; + const searching = searchQuery.trim().length > 0; + return ( <> {/* Viewer mode tools — annotate, redact, form fill */} - {allToolsView && searchQuery.trim().length > 0 ? ( + {panelSearch && ( +
+ +
+ )} + + {searching && (allToolsView || panelSearch) ? (
selectTool(id as ToolId)} filteredTools={filteredTools} - isSearching={Boolean(searchQuery && searchQuery.trim().length > 0)} + isSearching={searching} compact={compactProp ?? !allToolsView} onShowAllTools={onShowAllTools} />