From 321cbb60d5a96d84256bd7c3fd54c74c88d74d41 Mon Sep 17 00:00:00 2001 From: varun-pahuja Date: Thu, 15 Oct 2026 10:40:44 +0530 Subject: [PATCH] fix: register the editor export shortcuts once, via a shortcuts prop EditorApp renders an ExportBar in the top bar and a second one when the mobile Export tab is active. Each registered its own document keydown listener, so with the Export tab open Ctrl/Cmd+S downloaded the PNG twice. ExportBar takes a shortcuts prop, defaulting to true, and returns from the effect before registering when it is false. Only the mobile Export tab's bar passes false, so the always-mounted topbar bar is the single registration. The download and copy logic stays in ExportBar only, the execCommand fallback in copyToClipboard is untouched, and the shortcut still calls handleDownload and copyToClipboard, so the buttons keep updating as before. --- src/components/editor/EditorApp.tsx | 3 ++- src/components/editor/ExportBar.tsx | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/editor/EditorApp.tsx b/src/components/editor/EditorApp.tsx index 1d8bb7d..aa8f1e5 100644 --- a/src/components/editor/EditorApp.tsx +++ b/src/components/editor/EditorApp.tsx @@ -195,7 +195,8 @@ function EditorAppInner({ initialCategory }: EditorAppProps) {
- + {/* The topbar ExportBar always mounts and owns the shortcuts, so this second one opts out. */} + ; templateId: string; + /** + * Whether this bar owns the keyboard shortcuts. Pass false on a second + * ExportBar so Ctrl/Cmd+S and Ctrl/Cmd+Shift+C fire once, not once per bar. + */ + shortcuts?: boolean; } declare global { @@ -14,7 +19,13 @@ declare global { } } -export function ExportBar({ apiUrl, downloadUrl, params, templateId }: ExportBarProps) { +export function ExportBar({ + apiUrl, + downloadUrl, + params, + templateId, + shortcuts = true, +}: ExportBarProps) { const [downloading, setDownloading] = useState(false); const [copied, setCopied] = useState(null); const downloadRef = useRef(downloadUrl); @@ -68,6 +79,10 @@ export function ExportBar({ apiUrl, downloadUrl, params, templateId }: ExportBar // Keyboard shortcuts useEffect(() => { + // A second ExportBar can be mounted at the same time (the mobile Export + // tab). Only the first one registers, so a key press fires once. + if (!shortcuts) return; + function handleKeyDown(e: KeyboardEvent) { const mod = e.metaKey || e.ctrlKey; if (!mod) return; @@ -87,7 +102,7 @@ export function ExportBar({ apiUrl, downloadUrl, params, templateId }: ExportBar document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); - }, [handleDownload, copyToClipboard]); + }, [handleDownload, copyToClipboard, shortcuts]); const metaTags = generateMetaTags({ title: params.title ? String(params.title) : undefined,