feat: add export-as-image with format, scale, and background options

New ExportDialog component with scope (selection/all), filename,
format (PNG/JPEG/WebP), quality slider, scale multiplier, and
background color picker. Wired into Toolbar and Editor.
This commit is contained in:
Hiren Kangad
2026-03-10 19:16:05 +05:30
parent c126885e53
commit 51e53a323b
4 changed files with 402 additions and 0 deletions
+25
View File
@@ -9,6 +9,7 @@ import { SyncHandle } from '../canvas/sync';
import { UndoManager } from '../canvas/history';
import { shortcuts as shortcutDefs } from '../canvas/shortcut-definitions';
import { writeCanvasToClipboard as writeClipboard } from '../canvas/clipboard';
import { exportAsImage } from '../canvas/export';
import { buildContextMenuItems } from '../canvas/context-menu-items';
import { groupItems, ungroupItems } from '../canvas/grouping';
import { getSocket } from '../socket';
@@ -24,6 +25,7 @@ import ShortcutsHelp from '../components/ShortcutsHelp';
import MattermostImport from '../components/MattermostImport';
import Minimap from '../components/Minimap';
import UploadPanel from '../components/UploadPanel';
import ExportDialog from '../components/ExportDialog';
import { UploadManager } from '../stores/uploadManager';
import { InboxZone } from '../canvas/InboxZone';
import { getItemWorldBounds } from '../canvas/SceneManager';
@@ -87,6 +89,7 @@ export default function Editor({ isPublicView }: EditorProps) {
const [showGrid, setShowGrid] = useState(true);
const [showHelp, setShowHelp] = useState(false);
const [showMmImport, setShowMmImport] = useState(false);
const [showExport, setShowExport] = useState(false);
const [focusMode, setFocusMode] = useState(false);
const [layerList, setLayerList] = useState<any[]>([]);
const [selectedLayerIds, setSelectedLayerIds] = useState<string[]>([]);
@@ -486,6 +489,7 @@ export default function Editor({ isPublicView }: EditorProps) {
showLayers={showLayers}
onToggleHelp={() => setShowHelp((v) => !v)}
onMmImport={() => setShowMmImport(true)}
onExport={() => setShowExport(true)}
boardName={board?.name}
/>}
@@ -684,6 +688,27 @@ export default function Editor({ isPublicView }: EditorProps) {
}}
/>
)}
{/* Export dialog */}
{showExport && (
<ExportDialog
selectedItems={selectionRef.current?.getSelectedItems() ?? []}
allItems={canvasRef.current?.getScene()?.getAllItems() ?? []}
boardName={board?.name || 'export'}
onExport={async (items, options) => {
setShowExport(false);
try {
const app = canvasRef.current?.getApp() ?? null;
const viewport = canvasRef.current?.getViewport() ?? null;
await exportAsImage(app, viewport, items, options);
showToast('Exported successfully');
} catch (err: any) {
showToast('Export failed: ' + (err.message || 'unknown error'));
}
}}
onClose={() => setShowExport(false)}
/>
)}
</div>
);
}