diff --git a/surfsense_web/components/editor/plate-editor.tsx b/surfsense_web/components/editor/plate-editor.tsx index 29eeb02f6..ee43b296c 100644 --- a/surfsense_web/components/editor/plate-editor.tsx +++ b/surfsense_web/components/editor/plate-editor.tsx @@ -1,8 +1,8 @@ "use client"; -import { useEffect, useRef } from "react"; +import { useEffect, useMemo, useRef } from "react"; import { MarkdownPlugin, remarkMdx } from "@platejs/markdown"; -import { Plate, usePlateEditor } from "platejs/react"; +import { createPlatePlugin, Key, Plate, usePlateEditor } from "platejs/react"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; @@ -69,6 +69,31 @@ export function PlateEditor({ }: PlateEditorProps) { const lastMarkdownRef = useRef(markdown); + // Keep a stable ref to the latest onSave callback so the plugin shortcut + // always calls the most recent version without re-creating the editor. + const onSaveRef = useRef(onSave); + useEffect(() => { + onSaveRef.current = onSave; + }, [onSave]); + + // Stable Plate plugin for ⌘+S / Ctrl+S save shortcut + const SaveShortcutPlugin = useMemo( + () => + createPlatePlugin({ + key: "save-shortcut", + shortcuts: { + save: { + keys: [[Key.Mod, "s"]], + handler: () => { + onSaveRef.current?.(); + }, + preventDefault: true, + }, + }, + }), + [] + ); + // When readOnly is forced, always start in readOnly. // Otherwise, respect defaultEditing to decide initial mode. // The user can still toggle between editing/viewing via ModeToolbarButton. @@ -89,6 +114,7 @@ export function PlateEditor({ ...FloatingToolbarKit, ...AutoformatKit, ...DndKit, + SaveShortcutPlugin, MarkdownPlugin.configure({ options: { remarkPlugins: [remarkGfm, remarkMath, remarkMdx], diff --git a/surfsense_web/components/layout/hooks/useSidebarState.ts b/surfsense_web/components/layout/hooks/useSidebarState.ts index 9caa0b451..78631694d 100644 --- a/surfsense_web/components/layout/hooks/useSidebarState.ts +++ b/surfsense_web/components/layout/hooks/useSidebarState.ts @@ -40,10 +40,10 @@ export function useSidebarState(defaultCollapsed = false): UseSidebarStateReturn setIsCollapsed(!isCollapsed); }, [isCollapsed, setIsCollapsed]); - // Keyboard shortcut: Cmd/Ctrl + B + // Keyboard shortcut: Cmd/Ctrl + \ useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === "b" && (event.metaKey || event.ctrlKey)) { + if (event.key === "\\" && (event.metaKey || event.ctrlKey)) { event.preventDefault(); toggleCollapsed(); } diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarCollapseButton.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarCollapseButton.tsx index 44f05249c..3985e93e0 100644 --- a/surfsense_web/components/layout/ui/sidebar/SidebarCollapseButton.tsx +++ b/surfsense_web/components/layout/ui/sidebar/SidebarCollapseButton.tsx @@ -4,6 +4,7 @@ import { PanelLeft, PanelLeftClose } from "lucide-react"; import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { usePlatformShortcut } from "@/hooks/use-platform-shortcut"; interface SidebarCollapseButtonProps { isCollapsed: boolean; @@ -17,6 +18,7 @@ export function SidebarCollapseButton({ disableTooltip = false, }: SidebarCollapseButtonProps) { const t = useTranslations("sidebar"); + const { shortcut } = usePlatformShortcut(); const button = (