fix(tauri): transfer FIG exports over binary IPC (#509)

- Return native FIG archives as raw IPC responses instead of JSON byte arrays

- Decode the response as an ArrayBuffer and cover the binary contract

- Prevent large desktop exports from multiplying memory use during save
This commit is contained in:
Danila Poyarkov 2026-08-13 22:23:37 +03:00 committed by GitHub
parent 631fc25ee6
commit 5190a64017
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 5 additions and 4 deletions

View file

@ -15,6 +15,7 @@
### Fixed ### Fixed
- Transfer native `.fig` exports over binary Tauri IPC instead of JSON byte arrays, preventing large desktop saves from being truncated or exhausting WebView memory. (#484)
- Keep unsaved source-less documents recoverable after their editor tab is closed, matching Figma's retained offline-change behavior. - Keep unsaved source-less documents recoverable after their editor tab is closed, matching Figma's retained offline-change behavior.
- Decode zstd-compressed FIG containers, reject invalid compressed payloads, and preserve exact fixture byte ranges. (#397) - Decode zstd-compressed FIG containers, reject invalid compressed payloads, and preserve exact fixture byte ranges. (#397)
- Compose caller CSS with Tailwind defaults when importing DOM/CSS documents. (#397) - Compose caller CSS with Tailwind defaults when importing DOM/CSS documents. (#397)

View file

@ -15,7 +15,7 @@ pub fn build_fig_file(
meta_json: String, meta_json: String,
images: Option<Vec<ImageEntry>>, images: Option<Vec<ImageEntry>>,
fig_kiwi_version: Option<u32>, fig_kiwi_version: Option<u32>,
) -> Result<Vec<u8>, String> { ) -> Result<tauri::ipc::Response, String> {
let mut encoder = zstd::Encoder::new(Vec::new(), 3).map_err(|e| e.to_string())?; let mut encoder = zstd::Encoder::new(Vec::new(), 3).map_err(|e| e.to_string())?;
encoder encoder
.include_contentsize(true) .include_contentsize(true)
@ -63,5 +63,5 @@ pub fn build_fig_file(
} }
let result = zip.finish().map_err(|e| e.to_string())?; let result = zip.finish().map_err(|e| e.to_string())?;
Ok(result.into_inner()) Ok(tauri::ipc::Response::new(result.into_inner()))
} }

View file

@ -554,7 +554,7 @@ export async function exportFigFile(
if (IS_TAURI) { if (IS_TAURI) {
const { invoke } = await import('@tauri-apps/api/core') const { invoke } = await import('@tauri-apps/api/core')
return new Uint8Array( return new Uint8Array(
await invoke<number[]>('build_fig_file', { await invoke<ArrayBuffer>('build_fig_file', {
schemaDeflated: Array.from(schemaDeflated), schemaDeflated: Array.from(schemaDeflated),
kiwiData: Array.from(kiwiData), kiwiData: Array.from(kiwiData),
thumbnailPng: Array.from(thumbnailPNG), thumbnailPng: Array.from(thumbnailPNG),

View file

@ -20,7 +20,7 @@ mockIPC((cmd, args) => {
if (payload.thumbnailPng.length === 0) throw new Error('thumbnailPng is empty') if (payload.thumbnailPng.length === 0) throw new Error('thumbnailPng is empty')
if (payload.images.length !== 0) throw new Error('images should be empty') if (payload.images.length !== 0) throw new Error('images should be empty')
JSON.parse(payload.metaJson) JSON.parse(payload.metaJson)
return [7, 8, 9] return new Uint8Array([7, 8, 9]).buffer
}) })
const [{ exportFigFile }, { SceneGraph }] = await Promise.all([ const [{ exportFigFile }, { SceneGraph }] = await Promise.all([