fix(fig): preserve scene node defaults during overrides
This commit is contained in:
parent
955aa4d1e9
commit
eb7a663c1c
|
|
@ -209,7 +209,7 @@ export async function exportFigFile(
|
|||
if (page.internalOnly) canvasNc.internalOnly = true
|
||||
nodeChanges.push(canvasNc)
|
||||
|
||||
const children = graph.getChildren(page.id)
|
||||
const children = graph.getChildren(page.id).filter((child) => !child.internalOnly)
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
nodeChanges.push(
|
||||
...sceneNodeToKiwi(
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ function applyOverrideLayout(ov: Record<string, unknown>, updates: Partial<Scene
|
|||
}
|
||||
|
||||
function applyOverrideStrokes(ov: Record<string, unknown>, updates: Partial<SceneNode>): void {
|
||||
if (ov.strokeWeight != null && !ov.strokePaints) {
|
||||
updates.strokes = updates.strokes ?? []
|
||||
if (ov.strokeWeight != null && !ov.strokePaints && updates.strokes) {
|
||||
for (const stroke of updates.strokes) stroke.weight = ov.strokeWeight as number
|
||||
}
|
||||
if (ov.strokeAlign != null && updates.strokes) {
|
||||
let align: 'INSIDE' | 'OUTSIDE' | 'CENTER' = 'CENTER'
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ export function sceneNodeToKiwiWithContext(
|
|||
}
|
||||
|
||||
const result: KiwiNodeChange[] = [nc]
|
||||
const children = context.graph.getChildren(node.id)
|
||||
const children = context.graph.getChildren(node.id).filter((child) => !child.internalOnly)
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
result.push(...context.sceneNodeToKiwi(children[i], guid, i, localIdCounter, context))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,8 +190,8 @@ function exportTextData(node: SceneNode): NodeChange['textData'] {
|
|||
}
|
||||
}
|
||||
|
||||
export function safeColor(c: Color): Color {
|
||||
return { r: c.r, g: c.g, b: c.b, a: c.a }
|
||||
export function safeColor(c: Color | Omit<Color, 'a'>): Color {
|
||||
return { r: c.r, g: c.g, b: c.b, a: 'a' in c ? c.a : 1 }
|
||||
}
|
||||
|
||||
function fillToKiwiPaint(f: SceneNode['fills'][number]): Paint {
|
||||
|
|
@ -266,7 +266,7 @@ function serializeTextProps(
|
|||
nc.textAutoResize = autoResize
|
||||
nc.textAlignHorizontal = node.textAlignHorizontal
|
||||
nc.textAlignVertical = 'TOP'
|
||||
nc.textUserLayoutVersion = 5
|
||||
nc.textUserLayoutVersion = 4
|
||||
nc.textExplicitLayoutVersion = 1
|
||||
nc.textBidiVersion = 1
|
||||
nc.textDecorationSkipInk = true
|
||||
|
|
@ -275,7 +275,7 @@ function serializeTextProps(
|
|||
nc.fontVersion = ''
|
||||
nc.emojiImageSet = 'APPLE'
|
||||
if (fontDigestMap) nc.derivedTextData = buildDerivedTextData(node, fontDigestMap, blobs)
|
||||
nc.lineHeight = node.lineHeight != null ? { value: node.lineHeight, units: 'PIXELS' } : { value: 100, units: 'PERCENT' }
|
||||
if (node.lineHeight != null) nc.lineHeight = { value: node.lineHeight, units: 'PIXELS' }
|
||||
nc.letterSpacing = { value: node.letterSpacing, units: 'PIXELS' }
|
||||
if (node.textDecoration !== 'NONE') {
|
||||
nc.textDecoration = node.textDecoration === 'UNDERLINE' ? 'UNDERLINE' : 'STRIKETHROUGH'
|
||||
|
|
|
|||
|
|
@ -402,6 +402,11 @@ export class SceneGraph {
|
|||
if (node.textPicture && textChanged) node.textPicture = null
|
||||
if (node.figmaDerivedTextGlyphs && 'text' in changes) node.figmaDerivedTextGlyphs = null
|
||||
}
|
||||
changes = Object.fromEntries(
|
||||
(Object.entries(changes) as Array<[string, unknown]>).filter(([, value]) =>
|
||||
value !== undefined
|
||||
)
|
||||
) as Partial<SceneNode>
|
||||
if (changes.vectorNetwork) {
|
||||
changes = { ...changes, vectorNetwork: normalizeVectorNetwork(changes.vectorNetwork) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import { describe, expect, mock, test } from 'bun:test'
|
|||
import type { Canvas } from 'canvaskit-wasm'
|
||||
|
||||
import { renderShapeUncached } from '#core/canvas/scene'
|
||||
import type { SceneGraph, SceneNode } from '#core/scene-graph'
|
||||
import { createDefaultNode } from '#core/scene-graph/node-defaults'
|
||||
import type { SceneGraph } from '#core/scene-graph'
|
||||
|
||||
import { createMockCanvas, createMockRenderer } from './helpers'
|
||||
|
||||
|
|
@ -11,9 +12,7 @@ describe('Renderer effect ordering (Behavioral)', () => {
|
|||
test('drop shadow renders before fills', () => {
|
||||
const r = createMockRenderer()
|
||||
const canvas = createMockCanvas()
|
||||
const node: Partial<SceneNode> = {
|
||||
id: 'node1',
|
||||
type: 'RECTANGLE',
|
||||
const node = createDefaultNode(() => 'node1', 'RECTANGLE', {
|
||||
width: 100,
|
||||
height: 100,
|
||||
effects: [
|
||||
|
|
@ -27,11 +26,10 @@ describe('Renderer effect ordering (Behavioral)', () => {
|
|||
}
|
||||
],
|
||||
fills: [{ visible: true, type: 'SOLID', color: { r: 1, g: 0, b: 0, a: 1 }, opacity: 1 }],
|
||||
strokes: [],
|
||||
strokeGeometry: []
|
||||
}
|
||||
strokes: []
|
||||
})
|
||||
const graph: Partial<SceneGraph> = {
|
||||
getNode: mock(() => node as SceneNode)
|
||||
getNode: mock(() => node)
|
||||
}
|
||||
|
||||
const callOrder: string[] = []
|
||||
|
|
@ -42,7 +40,7 @@ describe('Renderer effect ordering (Behavioral)', () => {
|
|||
callOrder.push('drawNodeFill')
|
||||
})
|
||||
|
||||
renderShapeUncached(r, canvas as Canvas, node as SceneNode, graph as SceneGraph)
|
||||
renderShapeUncached(r, canvas as Canvas, node, graph as SceneGraph)
|
||||
|
||||
expect(callOrder).toEqual(['renderEffects:behind', 'drawNodeFill', 'renderEffects:front'])
|
||||
})
|
||||
|
|
@ -50,9 +48,7 @@ describe('Renderer effect ordering (Behavioral)', () => {
|
|||
test('inner shadow and blur render after strokes', () => {
|
||||
const r = createMockRenderer()
|
||||
const canvas = createMockCanvas()
|
||||
const node: Partial<SceneNode> = {
|
||||
id: 'node1',
|
||||
type: 'RECTANGLE',
|
||||
const node = createDefaultNode(() => 'node1', 'RECTANGLE', {
|
||||
width: 100,
|
||||
height: 100,
|
||||
fills: [],
|
||||
|
|
@ -67,11 +63,10 @@ describe('Renderer effect ordering (Behavioral)', () => {
|
|||
spread: 0
|
||||
}
|
||||
],
|
||||
strokes: [{ visible: true, weight: 1, opacity: 1 }],
|
||||
strokeGeometry: []
|
||||
}
|
||||
strokes: [{ visible: true, weight: 1, opacity: 1, color: { r: 0, g: 0, b: 0, a: 1 } }]
|
||||
})
|
||||
const graph: Partial<SceneGraph> = {
|
||||
getNode: mock(() => node as SceneNode)
|
||||
getNode: mock(() => node)
|
||||
}
|
||||
|
||||
const callOrder: string[] = []
|
||||
|
|
@ -82,9 +77,8 @@ describe('Renderer effect ordering (Behavioral)', () => {
|
|||
callOrder.push('drawStrokeWithAlign')
|
||||
})
|
||||
|
||||
renderShapeUncached(r, canvas as Canvas, node as SceneNode, graph as SceneGraph)
|
||||
renderShapeUncached(r, canvas as Canvas, node, graph as SceneGraph)
|
||||
|
||||
// Strokes are rendered between behind and front effects
|
||||
const strokeIdx = callOrder.indexOf('drawStrokeWithAlign')
|
||||
const frontIdx = callOrder.indexOf('renderEffects:front')
|
||||
expect(strokeIdx).toBeGreaterThan(-1)
|
||||
|
|
|
|||
Loading…
Reference in a new issue