From 22f85994088392fc7fb7a3a1a8ec4f1f958c2872 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Sun, 1 Mar 2026 15:31:56 +0300 Subject: [PATCH] Fix Save in Safari: append to DOM, defer revokeObjectURL Safari requires the element to be in the document for .click() downloads to work. Also defers URL.revokeObjectURL by 100ms so the browser has time to start the download. Extracted shared downloadBlob helper used by both file save and image export fallbacks. --- src/stores/editor.ts | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/stores/editor.ts b/src/stores/editor.ts index 2ba48ac69..4c75d86c6 100644 --- a/src/stores/editor.ts +++ b/src/stores/editor.ts @@ -125,6 +125,21 @@ export function createEditorStore() { prefetchFigmaSchema() + function downloadBlob(data: Uint8Array, filename: string, mime: string) { + const blob = new Blob([data.buffer as ArrayBuffer], { type: mime }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = filename + a.style.display = 'none' + document.body.appendChild(a) + a.click() + setTimeout(() => { + document.body.removeChild(a) + URL.revokeObjectURL(url) + }, 100) + } + const state = reactive({ activeTool: 'SELECT' as Tool, currentPageId: graph.getPages()[0].id, @@ -612,13 +627,7 @@ export function createEditorStore() { } } - const blob = new Blob([new Uint8Array(data)], { type: 'application/octet-stream' }) - const url = URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = url - a.download = 'Untitled.fig' - a.click() - URL.revokeObjectURL(url) + downloadBlob(new Uint8Array(data), 'Untitled.fig', 'application/octet-stream') } async function writeFile(data: Uint8Array) { @@ -717,13 +726,7 @@ export function createEditorStore() { } } - const blob = new Blob([new Uint8Array(data)], { type: exportImageMime(format) }) - const url = URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = url - a.download = fileName - a.click() - URL.revokeObjectURL(url) + downloadBlob(new Uint8Array(data), fileName, exportImageMime(format)) } function runLayoutForNode(id: string) {