Merge pull request #504 from open-pencil/fig-export-memory
perf(fig): reduce export graph cloning
This commit is contained in:
commit
9676bfe31b
|
|
@ -8,6 +8,10 @@
|
|||
- Allow supported AI model profiles to set a provider-specific reasoning effort. (#454)
|
||||
- Show unavailable or substituted document fonts with affected-layer selection and retry actions, and expose font fidelity through the Figma API and MCP tooling. (#503)
|
||||
|
||||
### Performance
|
||||
|
||||
- Reduce peak memory during `.fig` export by sharing immutable binary resources with the isolated export graph.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Report exhausted provider credit, request failures, and output-token limits through localized chat toasts and copied diagnostics. (#451, #454)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import {
|
|||
makeDocumentNodeChange,
|
||||
makeCanvasNodeChange
|
||||
} from '#core/kiwi/fig/node-change/serialize'
|
||||
import { deserializeSceneGraph, serializeSceneGraph } from '#core/kiwi/fig/parse/transfer'
|
||||
import { cloneSceneGraphForFigExport } from '#core/kiwi/fig/parse/transfer'
|
||||
|
||||
const THUMBNAIL_1X1 = decodeBase64(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=='
|
||||
|
|
@ -388,7 +388,7 @@ export async function exportFigFile(
|
|||
): Promise<Uint8Array> {
|
||||
// Lazy population synchronizes component trees and therefore mutates its graph. Saving must not
|
||||
// rewrite the live editor document or restore component values over edits made by the user.
|
||||
const graph = deserializeSceneGraph(structuredClone(serializeSceneGraph(sourceGraph)))
|
||||
const graph = cloneSceneGraphForFigExport(sourceGraph)
|
||||
populateAllLazyFigImportRoots(graph)
|
||||
await initCodec()
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,40 @@ export function serializedSceneGraphTransferList(data: SerializedSceneGraph): Tr
|
|||
return [...buffers]
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone the graph state that lazy FIG population may mutate while retaining immutable imported
|
||||
* resources by reference. Population replaces node fields and mutates child ID arrays, but only
|
||||
* reads image bytes, variables, source changes, GUID mappings, blobs, and schema bytes.
|
||||
*/
|
||||
export function cloneSceneGraphForFigExport(graph: SceneGraph): SceneGraph {
|
||||
const cloned = new SceneGraph()
|
||||
cloned.rootId = graph.rootId
|
||||
cloned.nodes = new Map(
|
||||
[...graph.nodes].map(([id, node]) => [id, { ...node, childIds: [...node.childIds] }])
|
||||
)
|
||||
cloned.images = new Map(graph.images)
|
||||
cloned.variables = new Map(graph.variables)
|
||||
cloned.variableCollections = new Map(graph.variableCollections)
|
||||
cloned.activeMode = new Map(graph.activeMode)
|
||||
cloned.instanceIndex = new Map(
|
||||
[...graph.instanceIndex].map(([id, nodeIds]) => [id, new Set(nodeIds)])
|
||||
)
|
||||
cloned.figKiwiVersion = graph.figKiwiVersion
|
||||
cloned.figSchemaDeflated = graph.figSchemaDeflated
|
||||
cloned.documentColorSpace = graph.documentColorSpace
|
||||
|
||||
const lazyFigImport = getLazyFigImportContext(graph)
|
||||
if (lazyFigImport) {
|
||||
setLazyFigImportContext(cloned, {
|
||||
changeMap: lazyFigImport.changeMap,
|
||||
guidToNodeId: lazyFigImport.guidToNodeId,
|
||||
blobs: lazyFigImport.blobs,
|
||||
populatedRootIds: new Set(lazyFigImport.populatedRootIds)
|
||||
})
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
export function deserializeSceneGraph(data: SerializedSceneGraph): SceneGraph {
|
||||
const graph = new SceneGraph()
|
||||
graph.rootId = data.rootId
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { describe, expect, test } from 'bun:test'
|
||||
|
||||
import { exportFigFile, initCodec, parseFigFile } from '@open-pencil/core'
|
||||
import { setLazyFigImportContext } from '@open-pencil/core/kiwi/fig/lazy-import'
|
||||
import {
|
||||
getLazyFigImportContext,
|
||||
setLazyFigImportContext
|
||||
} from '@open-pencil/core/kiwi/fig/lazy-import'
|
||||
import { cloneSceneGraphForFigExport } from '@open-pencil/core/kiwi/fig/parse/transfer'
|
||||
import { SceneGraph } from '@open-pencil/scene-graph'
|
||||
|
||||
function lazyExportGraph() {
|
||||
|
|
@ -39,6 +43,27 @@ function createEditedInstance(
|
|||
}
|
||||
|
||||
describe('FIG population export lifecycle', () => {
|
||||
test('isolates mutable graph state while sharing immutable binary resources', () => {
|
||||
const { graph } = lazyExportGraph()
|
||||
const image = new Uint8Array([1, 2, 3])
|
||||
graph.images.set('image', image)
|
||||
const context = getLazyFigImportContext(graph)
|
||||
expect(context).toBeDefined()
|
||||
|
||||
const clone = cloneSceneGraphForFigExport(graph)
|
||||
const cloneContext = getLazyFigImportContext(clone)
|
||||
const firstPage = graph.getPages()[0]
|
||||
clone.getNode(firstPage.id)?.childIds.push('export-only')
|
||||
cloneContext?.populatedRootIds.add('export-only')
|
||||
|
||||
expect(graph.getNode(firstPage.id)?.childIds).not.toContain('export-only')
|
||||
expect(context?.populatedRootIds).not.toContain('export-only')
|
||||
expect(clone.images.get('image')).toBe(image)
|
||||
expect(cloneContext?.changeMap).toBe(context?.changeMap)
|
||||
expect(cloneContext?.guidToNodeId).toBe(context?.guidToNodeId)
|
||||
expect(cloneContext?.blobs).toBe(context?.blobs)
|
||||
})
|
||||
|
||||
test('exports all remaining lazy pages after a partial visit', async () => {
|
||||
await initCodec()
|
||||
const { graph, instance } = lazyExportGraph()
|
||||
|
|
|
|||
Loading…
Reference in a new issue