- {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}
/>
diff --git a/frontend/editor/src/core/components/notifications/BellIcon.tsx b/frontend/editor/src/core/ui/BellIcon.tsx
similarity index 68%
rename from frontend/editor/src/core/components/notifications/BellIcon.tsx
rename to frontend/editor/src/core/ui/BellIcon.tsx
index 3cbd55519c..87908c4af6 100644
--- a/frontend/editor/src/core/components/notifications/BellIcon.tsx
+++ b/frontend/editor/src/core/ui/BellIcon.tsx
@@ -1,6 +1,6 @@
/**
- * The bundled Material Symbols set only carries the filled variant, which reads as permanently
- * ringing. Mirrors the portal's own icon rather than importing it: core cannot reach into portal.
+ * An outline bell. The bundled Material Symbols set only carries the filled variant,
+ * which reads as permanently ringing.
*/
export function BellIcon({ size = 18 }: { size?: number }) {
return (
diff --git a/frontend/editor/src/core/ui/index.ts b/frontend/editor/src/core/ui/index.ts
index dcf0e9265f..c621319f0e 100644
--- a/frontend/editor/src/core/ui/index.ts
+++ b/frontend/editor/src/core/ui/index.ts
@@ -1,5 +1,6 @@
export * from "@app/ui/Button";
export * from "@app/ui/ActionIcon";
+export * from "@app/ui/BellIcon";
export * from "@app/ui/Logo";
export * from "@app/ui/FilePicker";
export * from "@app/ui/SegmentedControl";
diff --git a/frontend/editor/src/proprietary/components/notifications/useNotificationsAvailable.ts b/frontend/editor/src/proprietary/components/notifications/useNotificationsAvailable.ts
new file mode 100644
index 0000000000..2e21f0f33e
--- /dev/null
+++ b/frontend/editor/src/proprietary/components/notifications/useNotificationsAvailable.ts
@@ -0,0 +1,7 @@
+/**
+ * This build ships the failure registry and the notification routes, so the bell has
+ * something to read and may mount.
+ */
+export function useNotificationsAvailable(): boolean {
+ return true;
+}