perf(kiwi): lazily populate opened fig pages
This commit is contained in:
parent
04b425f64c
commit
4fbca976fd
|
|
@ -6,9 +6,13 @@ import { deserializeSceneGraph } from '../../../kiwi/graph-transfer'
|
|||
import type { SerializedSceneGraph } from '../../../kiwi/graph-transfer'
|
||||
import type { SceneGraph } from '../../../scene-graph'
|
||||
|
||||
function parseFigFileSync(buffer: ArrayBuffer): SceneGraph {
|
||||
export interface ParseFigFileOptions {
|
||||
populate?: 'all' | 'first-page'
|
||||
}
|
||||
|
||||
function parseFigFileSync(buffer: ArrayBuffer, options: ParseFigFileOptions = {}): SceneGraph {
|
||||
const { nodeChanges, blobs, images: imageEntries, figKiwiVersion } = parseFigBuffer(buffer)
|
||||
const graph = importNodeChanges(nodeChanges, blobs, new Map(imageEntries))
|
||||
const graph = importNodeChanges(nodeChanges, blobs, new Map(imageEntries), options)
|
||||
graph.figKiwiVersion = figKiwiVersion
|
||||
return graph
|
||||
}
|
||||
|
|
@ -18,7 +22,7 @@ interface WorkerParseResult {
|
|||
error?: string
|
||||
}
|
||||
|
||||
function parseViaWorker(buffer: ArrayBuffer): Promise<SceneGraph> {
|
||||
function parseViaWorker(buffer: ArrayBuffer, options: ParseFigFileOptions): Promise<SceneGraph> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const worker = new Worker(new URL('../../../kiwi/fig-parse-worker.ts', import.meta.url), {
|
||||
type: 'module'
|
||||
|
|
@ -38,23 +42,29 @@ function parseViaWorker(buffer: ArrayBuffer): Promise<SceneGraph> {
|
|||
reject(new Error(err.message || 'Worker failed to parse .fig file'))
|
||||
}
|
||||
|
||||
worker.postMessage(buffer, [buffer])
|
||||
worker.postMessage({ buffer, options }, [buffer])
|
||||
})
|
||||
}
|
||||
|
||||
export async function parseFigFile(buffer: ArrayBuffer): Promise<SceneGraph> {
|
||||
export async function parseFigFile(
|
||||
buffer: ArrayBuffer,
|
||||
options: ParseFigFileOptions = {}
|
||||
): Promise<SceneGraph> {
|
||||
if (typeof Worker !== 'undefined' && IS_BROWSER) {
|
||||
const copy = buffer.slice(0)
|
||||
try {
|
||||
return await parseViaWorker(buffer)
|
||||
return await parseViaWorker(buffer, options)
|
||||
} catch (error) {
|
||||
console.warn('Worker parsing failed, falling back to main thread:', error)
|
||||
return parseFigFileSync(copy)
|
||||
return parseFigFileSync(copy, options)
|
||||
}
|
||||
}
|
||||
return parseFigFileSync(buffer)
|
||||
return parseFigFileSync(buffer, options)
|
||||
}
|
||||
|
||||
export async function readFigFile(file: File): Promise<SceneGraph> {
|
||||
return parseFigFile(await file.arrayBuffer())
|
||||
export async function readFigFile(
|
||||
file: File,
|
||||
options: ParseFigFileOptions = {}
|
||||
): Promise<SceneGraph> {
|
||||
return parseFigFile(await file.arrayBuffer(), options)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -336,10 +336,15 @@ function parseDocumentColorSpace(nodeChanges: NodeChange[]): 'srgb' | 'display-p
|
|||
return documentNode?.documentColorProfile === 'DISPLAY_P3' ? 'display-p3' : 'srgb'
|
||||
}
|
||||
|
||||
export interface FigImportOptions {
|
||||
populate?: 'all' | 'first-page'
|
||||
}
|
||||
|
||||
export function importNodeChanges(
|
||||
nodeChanges: NodeChange[],
|
||||
blobs: Uint8Array[] = [],
|
||||
images?: Map<string, Uint8Array>
|
||||
images?: Map<string, Uint8Array>,
|
||||
options: FigImportOptions = {}
|
||||
): SceneGraph {
|
||||
const graph = new SceneGraph()
|
||||
graph.documentColorSpace = parseDocumentColorSpace(nodeChanges)
|
||||
|
|
@ -389,11 +394,25 @@ export function importNodeChanges(
|
|||
importVariableBindings(changeMap, guidToNodeId, graph)
|
||||
remapComponentIds(graph, guidToNodeId)
|
||||
|
||||
const firstPageId = graph.getPages()[0]?.id
|
||||
const componentPageIds = new Set<string>()
|
||||
for (const node of graph.getAllNodes()) {
|
||||
if (node.type !== 'COMPONENT' && node.type !== 'COMPONENT_SET') continue
|
||||
let current = node.parentId ? graph.getNode(node.parentId) : undefined
|
||||
while (current?.parentId && current.type !== 'CANVAS') current = graph.getNode(current.parentId)
|
||||
if (current?.type === 'CANVAS') componentPageIds.add(current.id)
|
||||
}
|
||||
const activeRootIds =
|
||||
options.populate === 'first-page'
|
||||
? [firstPageId, ...componentPageIds].filter(Boolean)
|
||||
: undefined
|
||||
|
||||
populateAndApplyOverrides(
|
||||
graph,
|
||||
changeMap as unknown as Map<string, InstanceNodeChange>,
|
||||
guidToNodeId,
|
||||
blobs
|
||||
blobs,
|
||||
activeRootIds
|
||||
)
|
||||
|
||||
setVariableColorResolver(null)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,16 @@ import { importNodeChanges } from './fig-import'
|
|||
import { parseFigBuffer } from './fig-parse-core'
|
||||
import { serializeSceneGraph } from './graph-transfer'
|
||||
|
||||
self.onmessage = (e: MessageEvent<ArrayBuffer>) => {
|
||||
interface WorkerParseRequest {
|
||||
buffer: ArrayBuffer
|
||||
options?: { populate?: 'all' | 'first-page' }
|
||||
}
|
||||
|
||||
self.onmessage = (e: MessageEvent<ArrayBuffer | WorkerParseRequest>) => {
|
||||
try {
|
||||
const { nodeChanges, blobs, images, figKiwiVersion } = parseFigBuffer(e.data)
|
||||
const graph = importNodeChanges(nodeChanges, blobs, new Map(images))
|
||||
const request = e.data instanceof ArrayBuffer ? { buffer: e.data } : e.data
|
||||
const { nodeChanges, blobs, images, figKiwiVersion } = parseFigBuffer(request.buffer)
|
||||
const graph = importNodeChanges(nodeChanges, blobs, new Map(images), request.options)
|
||||
graph.figKiwiVersion = figKiwiVersion
|
||||
self.postMessage({ graph: serializeSceneGraph(graph) })
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export function applyConstraintScaling(ctx: OverrideContext): void {
|
|||
const scaled = new Set<string>()
|
||||
|
||||
for (const node of graph.getAllNodes()) {
|
||||
if (ctx.activeNodeIds && !ctx.activeNodeIds.has(node.id)) continue
|
||||
if (node.type !== 'INSTANCE' || !node.componentId) continue
|
||||
const comp = graph.getNode(node.componentId)
|
||||
if (!comp || comp.width <= 0 || comp.height <= 0) continue
|
||||
|
|
@ -31,6 +32,7 @@ export function applyConstraintScaling(ctx: OverrideContext): void {
|
|||
}
|
||||
|
||||
if (scaled.size > 0) propagateScaling(ctx, scaled)
|
||||
normalizeOutOfBoundsSingleChildren(ctx)
|
||||
}
|
||||
|
||||
function resolveScaleBasis(
|
||||
|
|
@ -72,7 +74,11 @@ function scaleGeometryBlobs(geom: GeometryPath[], sx: number, sy: number): Geome
|
|||
})
|
||||
}
|
||||
|
||||
function scaleVectorNetwork(network: VectorNetwork | null, sx: number, sy: number): VectorNetwork | null {
|
||||
function scaleVectorNetwork(
|
||||
network: VectorNetwork | null,
|
||||
sx: number,
|
||||
sy: number
|
||||
): VectorNetwork | null {
|
||||
if (!network) return null
|
||||
return {
|
||||
vertices: network.vertices.map((vertex) => ({ ...vertex, x: vertex.x * sx, y: vertex.y * sy })),
|
||||
|
|
@ -142,9 +148,33 @@ function scaleChildren(
|
|||
}
|
||||
}
|
||||
|
||||
function normalizeOutOfBoundsSingleChildren(ctx: OverrideContext): void {
|
||||
const { graph } = ctx
|
||||
for (const parent of graph.getAllNodes()) {
|
||||
if (ctx.activeNodeIds && !ctx.activeNodeIds.has(parent.id)) continue
|
||||
if (parent.childIds.length !== 1) continue
|
||||
const child = graph.getNode(parent.childIds[0])
|
||||
if (!child?.visible || !child.componentId) continue
|
||||
if (ctx.geometryOverrideNodes.has(child.id) || child.figmaDerivedLayout?.x !== undefined)
|
||||
continue
|
||||
const outsideParent =
|
||||
child.x < -0.01 ||
|
||||
child.y < -0.01 ||
|
||||
child.x + child.width > parent.width + 0.01 ||
|
||||
child.y + child.height > parent.height + 0.01
|
||||
if (outsideParent) {
|
||||
graph.updateNode(child.id, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
figmaDerivedLayout: { ...child.figmaDerivedLayout, x: 0, y: 0 }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function propagateScaling(ctx: OverrideContext, scaled: Set<string>): void {
|
||||
const { graph } = ctx
|
||||
const clonesOf = buildClonesMap(graph)
|
||||
const clonesOf = buildClonesMap(graph, ctx.activeNodeIds)
|
||||
const queue = [...scaled]
|
||||
const visited = new Set<string>()
|
||||
|
||||
|
|
@ -165,7 +195,8 @@ function propagateScaling(ctx: OverrideContext, scaled: Set<string>): void {
|
|||
if (clone.y !== source.y) cu.y = source.y
|
||||
if (!ctx.geometryOverrideNodes.has(cloneId)) {
|
||||
if (source.fillGeometry.length > 0) cu.fillGeometry = copyGeometryPaths(source.fillGeometry)
|
||||
if (source.strokeGeometry.length > 0) cu.strokeGeometry = copyGeometryPaths(source.strokeGeometry)
|
||||
if (source.strokeGeometry.length > 0)
|
||||
cu.strokeGeometry = copyGeometryPaths(source.strokeGeometry)
|
||||
if (source.vectorNetwork) cu.vectorNetwork = structuredClone(source.vectorNetwork)
|
||||
}
|
||||
if (Object.keys(cu).length > 0) graph.updateNode(cloneId, cu)
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ function resolveDsdUpdates(ctx: OverrideContext): { modified: Set<string>; sizeS
|
|||
if (!derived?.length) continue
|
||||
|
||||
const nodeId = ctx.guidToNodeId.get(ncId)
|
||||
if (!nodeId) continue
|
||||
if (!nodeId || (ctx.activeNodeIds && !ctx.activeNodeIds.has(nodeId))) continue
|
||||
|
||||
for (const d of derived) {
|
||||
const guids = d.guidPath?.guids
|
||||
|
|
@ -188,7 +188,7 @@ function propagateDsdChanges(
|
|||
): void {
|
||||
if (modified.size === 0) return
|
||||
|
||||
const clonesOf = buildClonesMap(ctx.graph)
|
||||
const clonesOf = buildClonesMap(ctx.graph, ctx.activeNodeIds)
|
||||
const queue = [...modified]
|
||||
const visited = new Set<string>()
|
||||
|
||||
|
|
|
|||
|
|
@ -62,10 +62,15 @@ function buildKiwiGeometryNodes(
|
|||
return result
|
||||
}
|
||||
|
||||
function propagateResolvedFills(graph: SceneGraph, protectedNodes: Set<string>): void {
|
||||
function propagateResolvedFills(
|
||||
graph: SceneGraph,
|
||||
protectedNodes: Set<string>,
|
||||
activeNodeIds?: Set<string>
|
||||
): void {
|
||||
for (let pass = 0; pass < 10; pass++) {
|
||||
let changed = false
|
||||
for (const node of graph.getAllNodes()) {
|
||||
if (activeNodeIds && !activeNodeIds.has(node.id)) continue
|
||||
if (!node.componentId) continue
|
||||
const source = graph.getNode(node.componentId)
|
||||
if (!source || source.fills === node.fills) continue
|
||||
|
|
@ -81,7 +86,8 @@ function buildOverrideContext(
|
|||
graph: SceneGraph,
|
||||
changeMap: Map<string, InstanceNodeChange>,
|
||||
guidToNodeId: Map<string, string>,
|
||||
blobs: Uint8Array[]
|
||||
blobs: Uint8Array[],
|
||||
activeNodeIds?: Set<string>
|
||||
): OverrideContext {
|
||||
const overrideKeyToGuid = new Map<string, string>()
|
||||
for (const [id, nc] of changeMap) {
|
||||
|
|
@ -118,7 +124,8 @@ function buildOverrideContext(
|
|||
componentIdRoot: new Map(),
|
||||
swappedInstances: new Set(),
|
||||
kiwiPropertyNodes,
|
||||
geometryOverrideNodes
|
||||
geometryOverrideNodes,
|
||||
activeNodeIds
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -142,11 +149,12 @@ export function populateAndApplyOverrides(
|
|||
graph: SceneGraph,
|
||||
changeMap: Map<string, InstanceNodeChange>,
|
||||
guidToNodeId: Map<string, string>,
|
||||
blobs: Uint8Array[] = []
|
||||
blobs: Uint8Array[] = [],
|
||||
activeRootIds?: Iterable<string>
|
||||
): void {
|
||||
populateInstances(graph)
|
||||
const activeNodeIds = populateInstances(graph, activeRootIds)
|
||||
|
||||
const ctx = buildOverrideContext(graph, changeMap, guidToNodeId, blobs)
|
||||
const ctx = buildOverrideContext(graph, changeMap, guidToNodeId, blobs, activeNodeIds)
|
||||
preComputeRoots(ctx)
|
||||
|
||||
const overriddenNodes = applySymbolOverrides(ctx)
|
||||
|
|
@ -155,7 +163,14 @@ export function populateAndApplyOverrides(
|
|||
// synced with the correct values) AND protected (so sync doesn't overwrite
|
||||
// them with component defaults).
|
||||
for (const id of ctx.kiwiPropertyNodes) overriddenNodes.add(id)
|
||||
propagateOverridesTransitively(graph, overriddenNodes, ctx.swappedInstances, ctx.componentIdRoot)
|
||||
propagateOverridesTransitively(
|
||||
graph,
|
||||
overriddenNodes,
|
||||
ctx.swappedInstances,
|
||||
ctx.componentIdRoot,
|
||||
undefined,
|
||||
ctx.activeNodeIds
|
||||
)
|
||||
|
||||
const propModified = applyComponentProperties(ctx)
|
||||
if (propModified.size > 0) {
|
||||
|
|
@ -164,11 +179,32 @@ export function populateAndApplyOverrides(
|
|||
propModified,
|
||||
ctx.swappedInstances,
|
||||
ctx.componentIdRoot,
|
||||
overriddenNodes
|
||||
overriddenNodes,
|
||||
ctx.activeNodeIds
|
||||
)
|
||||
}
|
||||
|
||||
if (activeRootIds) {
|
||||
const populated = populateInstances(graph, activeRootIds)
|
||||
if (populated) ctx.activeNodeIds = populated
|
||||
const latePropModified = applyComponentProperties(ctx)
|
||||
if (latePropModified.size > 0) {
|
||||
propagateOverridesTransitively(
|
||||
graph,
|
||||
latePropModified,
|
||||
ctx.swappedInstances,
|
||||
ctx.componentIdRoot,
|
||||
overriddenNodes,
|
||||
ctx.activeNodeIds
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
applyDerivedSymbolData(ctx)
|
||||
propagateResolvedFills(graph, new Set([...ctx.kiwiPropertyNodes, ...overriddenNodes]))
|
||||
propagateResolvedFills(
|
||||
graph,
|
||||
new Set([...ctx.kiwiPropertyNodes, ...overriddenNodes]),
|
||||
ctx.activeNodeIds
|
||||
)
|
||||
applyConstraintScaling(ctx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,22 @@ import type { SceneGraph } from '../../scene-graph'
|
|||
* itself an unpopulated instance, populate the source first so cloned
|
||||
* children are complete.
|
||||
*/
|
||||
export function populateInstances(graph: SceneGraph): void {
|
||||
function collectSubtreeIds(graph: SceneGraph, rootIds: Iterable<string>): Set<string> {
|
||||
const result = new Set<string>()
|
||||
const queue = [...rootIds]
|
||||
for (let id = queue.shift(); id !== undefined; id = queue.shift()) {
|
||||
if (result.has(id)) continue
|
||||
result.add(id)
|
||||
const node = graph.getNode(id)
|
||||
if (node) queue.push(...node.childIds)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function populateInstances(
|
||||
graph: SceneGraph,
|
||||
rootIds?: Iterable<string>
|
||||
): Set<string> | undefined {
|
||||
const visiting = new Set<string>()
|
||||
|
||||
function ensurePopulated(nodeId: string): void {
|
||||
|
|
@ -34,9 +49,25 @@ export function populateInstances(graph: SceneGraph): void {
|
|||
}
|
||||
}
|
||||
|
||||
for (const node of graph.nodes.values()) {
|
||||
if (node.type === 'INSTANCE' && node.componentId && node.childIds.length === 0) {
|
||||
ensurePopulated(node.id)
|
||||
if (!rootIds) {
|
||||
for (const node of graph.nodes.values()) {
|
||||
if (node.type === 'INSTANCE' && node.componentId && node.childIds.length === 0) {
|
||||
ensurePopulated(node.id)
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
const queue = [...rootIds]
|
||||
const visited = new Set<string>()
|
||||
while (queue.length > 0) {
|
||||
const nodeId = queue.shift()
|
||||
if (!nodeId || visited.has(nodeId)) continue
|
||||
visited.add(nodeId)
|
||||
ensurePopulated(nodeId)
|
||||
const node = graph.getNode(nodeId)
|
||||
if (!node) continue
|
||||
queue.push(...node.childIds)
|
||||
}
|
||||
return collectSubtreeIds(graph, rootIds)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ function applyInstanceDirectAssignments(
|
|||
modified: Set<string>
|
||||
): void {
|
||||
for (const node of ctx.graph.getAllNodes()) {
|
||||
if (ctx.activeNodeIds && !ctx.activeNodeIds.has(node.id)) continue
|
||||
if (node.type !== 'INSTANCE') continue
|
||||
const ownFigmaId = ctx.nodeIdToGuid.get(node.id)
|
||||
if (!ownFigmaId) continue
|
||||
|
|
@ -154,7 +155,7 @@ function applyOverrideAssignments(
|
|||
): void {
|
||||
for (const [figmaId, nc] of ctx.changeMap) {
|
||||
const instanceNodeId = ctx.guidToNodeId.get(figmaId)
|
||||
if (!instanceNodeId) continue
|
||||
if (!instanceNodeId || (ctx.activeNodeIds && !ctx.activeNodeIds.has(instanceNodeId))) continue
|
||||
if (ctx.graph.getNode(instanceNodeId)?.type !== 'INSTANCE') continue
|
||||
|
||||
const overrides = nc.symbolData?.symbolOverrides
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import { resolveOverrideTarget, repopulateInstance } from './resolve'
|
|||
import type { SceneNode } from '../../scene-graph'
|
||||
import type { OverrideContext } from './types'
|
||||
|
||||
function isActiveInstance(ctx: OverrideContext, nodeId: string | undefined): nodeId is string {
|
||||
return nodeId !== undefined && (!ctx.activeNodeIds || ctx.activeNodeIds.has(nodeId))
|
||||
}
|
||||
|
||||
function preserveStrokeShapeProps(target: SceneNode, updates: Partial<SceneNode>): void {
|
||||
if (!updates.strokes) return
|
||||
updates.strokes = updates.strokes.map((stroke, index) => {
|
||||
|
|
@ -43,7 +47,7 @@ export function applySymbolOverrides(ctx: OverrideContext): Set<string> {
|
|||
if (!overrides?.length) continue
|
||||
|
||||
const nodeId = ctx.guidToNodeId.get(ncId)
|
||||
if (!nodeId) continue
|
||||
if (!isActiveInstance(ctx, nodeId)) continue
|
||||
|
||||
for (const ov of overrides) {
|
||||
const guids = ov.guidPath?.guids
|
||||
|
|
@ -52,9 +56,6 @@ export function applySymbolOverrides(ctx: OverrideContext): Set<string> {
|
|||
const targetId = resolveOverrideTarget(ctx, nodeId, guids)
|
||||
if (!targetId) continue
|
||||
|
||||
// When a symbolOverride resolves to the instance itself (self-reference
|
||||
// to the component shell), skip it if the instance has explicit kiwi NC
|
||||
// properties — the instance's own values take precedence.
|
||||
if (targetId === nodeId && ctx.kiwiPropertyNodes.has(nodeId)) continue
|
||||
|
||||
overriddenNodes.add(targetId)
|
||||
|
|
|
|||
|
|
@ -80,9 +80,13 @@ export function syncChildrenDeep(
|
|||
}
|
||||
|
||||
/** Build a map of componentId → list of clone node IDs. */
|
||||
export function buildClonesMap(graph: SceneGraph): Map<string, string[]> {
|
||||
export function buildClonesMap(
|
||||
graph: SceneGraph,
|
||||
activeNodeIds?: Set<string>
|
||||
): Map<string, string[]> {
|
||||
const clonesOf = new Map<string, string[]>()
|
||||
for (const node of graph.getAllNodes()) {
|
||||
if (activeNodeIds && !activeNodeIds.has(node.id)) continue
|
||||
if (!node.componentId) continue
|
||||
let arr = clonesOf.get(node.componentId)
|
||||
if (!arr) {
|
||||
|
|
@ -141,12 +145,13 @@ export function propagateOverridesTransitively(
|
|||
seeds: Set<string>,
|
||||
swappedInstances: Set<string>,
|
||||
componentIdRoot: Map<string, string>,
|
||||
protect?: Set<string>
|
||||
protect?: Set<string>,
|
||||
activeNodeIds?: Set<string>
|
||||
): void {
|
||||
if (seeds.size === 0) return
|
||||
|
||||
componentIdRoot.clear()
|
||||
const clonesOf = buildClonesMap(graph)
|
||||
const clonesOf = buildClonesMap(graph, activeNodeIds)
|
||||
const expandedSeeds = expandSeedsToParents(graph, seeds)
|
||||
const needsSync = buildNeedsSyncSet(expandedSeeds, clonesOf)
|
||||
|
||||
|
|
|
|||
|
|
@ -83,4 +83,6 @@ export interface OverrideContext {
|
|||
kiwiPropertyNodes: Set<string>
|
||||
/** Nodes whose Figma-derived geometry should not be overwritten by clone propagation. */
|
||||
geometryOverrideNodes: Set<string>
|
||||
/** When set, apply/populate expensive instance work only inside these already-imported nodes. */
|
||||
activeNodeIds?: Set<string>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -910,7 +910,7 @@ export function createEditorStore(initialGraph?: SceneGraph) {
|
|||
try {
|
||||
state.loading = true
|
||||
await yieldToUI()
|
||||
const imported = await readFigFile(file)
|
||||
const imported = await readFigFile(file, { populate: 'first-page' })
|
||||
await yieldToUI()
|
||||
editor.replaceGraph(imported)
|
||||
editor.undo.clear()
|
||||
|
|
@ -1022,11 +1022,11 @@ export function createEditorStore(initialGraph?: SceneGraph) {
|
|||
const bytes = await tauriRead(filePath)
|
||||
const blob = new Blob([bytes])
|
||||
const file = new File([blob], state.documentName + '.fig')
|
||||
const imported = await readFigFile(file)
|
||||
const imported = await readFigFile(file, { populate: 'first-page' })
|
||||
editor.replaceGraph(imported)
|
||||
} else if (fileHandle) {
|
||||
const file = await fileHandle.getFile()
|
||||
const imported = await readFigFile(file)
|
||||
const imported = await readFigFile(file, { populate: 'first-page' })
|
||||
editor.replaceGraph(imported)
|
||||
} else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowRef, computed, triggerRef } from 'vue'
|
||||
|
||||
import { BUILTIN_IO_FORMATS, IORegistry } from '@open-pencil/core'
|
||||
import { BUILTIN_IO_FORMATS, IORegistry, readFigFile } from '@open-pencil/core'
|
||||
|
||||
import { createEditorStore, setActiveEditorStore } from './editor'
|
||||
|
||||
|
|
@ -102,12 +102,14 @@ export async function openFileInNewTab(
|
|||
await yieldToUI()
|
||||
|
||||
try {
|
||||
const bytes = new Uint8Array(await file.arrayBuffer())
|
||||
const { graph: imported, sourceFormat } = await io.readDocument({
|
||||
name: file.name,
|
||||
mimeType: file.type || undefined,
|
||||
data: bytes
|
||||
})
|
||||
const isFig = file.name.toLowerCase().endsWith('.fig')
|
||||
const { graph: imported, sourceFormat } = isFig
|
||||
? { graph: await readFigFile(file, { populate: 'first-page' }), sourceFormat: 'fig' }
|
||||
: await io.readDocument({
|
||||
name: file.name,
|
||||
mimeType: file.type || undefined,
|
||||
data: new Uint8Array(await file.arrayBuffer())
|
||||
})
|
||||
|
||||
store.replaceGraph(imported)
|
||||
store.undo.clear()
|
||||
|
|
|
|||
Loading…
Reference in a new issue