diff --git a/packages/core/src/kiwi/fig/instance-overrides/symbol/patches.ts b/packages/core/src/kiwi/fig/instance-overrides/symbol/patches.ts index 5b0be4e5c..ecedf7a4d 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/symbol/patches.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/symbol/patches.ts @@ -1,10 +1,92 @@ +import type { GUID } from '#core/kiwi/fig/codec' import type { OverridePatch } from '#core/kiwi/fig/instance-overrides/patches' import type { OverrideContext, SymbolOverride } from '#core/kiwi/fig/instance-overrides/types' -import { guidToString } from '#core/kiwi/fig/node-change/convert' +import { guidToString, VARIABLE_BINDING_FIELDS_INVERSE } from '#core/kiwi/fig/node-change/convert' import { applyStyleRefsToFields } from '#core/kiwi/fig/node-change/style-refs' import { convertOverrideToProps } from './props' +interface AliasRef { + guid?: GUID + assetRef?: { key: string; version?: string } +} + +const VARIABLE_RADIUS_FIELDS = new Set([ + 'RECTANGLE_TOP_LEFT_CORNER_RADIUS', + 'RECTANGLE_TOP_RIGHT_CORNER_RADIUS', + 'RECTANGLE_BOTTOM_LEFT_CORNER_RADIUS', + 'RECTANGLE_BOTTOM_RIGHT_CORNER_RADIUS' +]) + +function assetRefKey(assetRef: { key: string; version?: string }): string { + return assetRef.version ? `${assetRef.key}@${assetRef.version}` : assetRef.key +} + +function buildAssetRefMap(ctx: OverrideContext): Map { + const refs = new Map() + for (const [id, nc] of ctx.changeMap) { + const key = typeof nc.key === 'string' ? nc.key : undefined + if (!key) continue + refs.set(key, id) + const version = typeof nc.version === 'string' ? nc.version : undefined + if (version) refs.set(assetRefKey({ key, version }), id) + } + return refs +} + +function resolveAliasId(alias: AliasRef, assetRefs: Map): string | undefined { + if (alias.guid) return guidToString(alias.guid) + const assetRef = alias.assetRef + if (!assetRef?.key) return undefined + return assetRefs.get(assetRefKey(assetRef)) ?? assetRefs.get(assetRef.key) +} + +function resolveFloatVariable( + ctx: OverrideContext, + id: string, + assetRefs: Map, + depth = 0 +): number | undefined { + if (depth > 10) return undefined + const nc = ctx.changeMap.get(id) + const entry = nc?.variableDataValues?.entries?.[0] + if (!entry) return undefined + const value = entry.variableData.value + if (!value) return undefined + if (typeof value.floatValue === 'number') return value.floatValue + const alias = value.alias as AliasRef | undefined + const aliasId = alias ? resolveAliasId(alias, assetRefs) : undefined + return aliasId ? resolveFloatVariable(ctx, aliasId, assetRefs, depth + 1) : undefined +} + +function applyVariableRadiusOverrides( + ctx: OverrideContext, + fields: Record, + props: ReturnType +): void { + const entries = (fields.variableConsumptionMap as { entries?: unknown[] } | undefined)?.entries + if (!entries?.length) return + const assetRefs = buildAssetRefMap(ctx) + for (const entry of entries) { + if (!entry || typeof entry !== 'object') continue + const variableEntry = entry as { + variableField?: string + variableData?: { value?: { alias?: AliasRef } } + } + const variableField = variableEntry.variableField + if (!variableField || !VARIABLE_RADIUS_FIELDS.has(variableField)) continue + const alias = variableEntry.variableData?.value?.alias + const id = alias ? resolveAliasId(alias, assetRefs) : undefined + const value = id ? resolveFloatVariable(ctx, id, assetRefs) : undefined + if (typeof value !== 'number') continue + const field = VARIABLE_BINDING_FIELDS_INVERSE[variableField] + if (field === 'topLeftRadius') props.topLeftRadius = value + else if (field === 'topRightRadius') props.topRightRadius = value + else if (field === 'bottomRightRadius') props.bottomRightRadius = value + else if (field === 'bottomLeftRadius') props.bottomLeftRadius = value + } +} + export function patchFromSymbolOverride( ctx: OverrideContext, targetId: string, @@ -20,6 +102,7 @@ export function patchFromSymbolOverride( if (Object.keys(fields).length > 0) { applyStyleRefsToFields(ctx.changeMap, fields) const props = convertOverrideToProps(fields as Record) + applyVariableRadiusOverrides(ctx, fields as Record, props) if (Object.keys(props).length > 0) patch.props = props } diff --git a/packages/core/src/kiwi/fig/instance-overrides/types.ts b/packages/core/src/kiwi/fig/instance-overrides/types.ts index 48098c3af..5a82d2938 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/types.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/types.ts @@ -80,6 +80,10 @@ export interface InstanceNodeChange { strokeGeometry?: Array<{ windingRule?: string; commandsBlob?: number }> strokeWeight?: number derivedSymbolData?: DerivedSymbolOverride[] + key?: string + version?: string + userFacingVersion?: string + variableDataValues?: NodeChange['variableDataValues'] } /** diff --git a/tests/engine/io/fig/roundtrip/exhaustive.test.ts b/tests/engine/io/fig/roundtrip/exhaustive.test.ts index 13956d00b..65cb89fe9 100644 --- a/tests/engine/io/fig/roundtrip/exhaustive.test.ts +++ b/tests/engine/io/fig/roundtrip/exhaustive.test.ts @@ -61,8 +61,8 @@ const SPECS: FixtureSpec[] = [ thumbnailHeight: 239, imageCount: 3, figKiwiVersion: 101, - g1ExportSize: 594522, - g2ExportSize: 594522 + g1ExportSize: 594517, + g2ExportSize: 594517 } ] @@ -584,10 +584,6 @@ function verifyFixture(spec: FixtureSpec): void { await ensureG2() compareRawNodeFields(spec, g1Graph, g2Graph, g1, g2, 'G1->G2') }) - - test.todo( - 'BUG: corner radius 999 sentinel lost for non-pill nodes on scene import (40 nodes, raw data preserved)' - ) }) } diff --git a/tests/engine/io/fig/roundtrip/helpers.ts b/tests/engine/io/fig/roundtrip/helpers.ts index e2692c512..b08d96cb4 100644 --- a/tests/engine/io/fig/roundtrip/helpers.ts +++ b/tests/engine/io/fig/roundtrip/helpers.ts @@ -92,11 +92,7 @@ export const SCENE_VERIFIERS = new Map([ if (!ctx.key.includes('componentPropertyDefinitions')) return false return ctx.a === 'VARIANT' && ctx.b === 'TEXT' } - ], - ['topLeftRadius', (ctx) => !isIdempotent(ctx) && ctx.a === 999 && ctx.b === 0], - ['topRightRadius', (ctx) => !isIdempotent(ctx) && ctx.a === 999 && ctx.b === 0], - ['bottomRightRadius', (ctx) => !isIdempotent(ctx) && ctx.a === 999 && ctx.b === 0], - ['bottomLeftRadius', (ctx) => !isIdempotent(ctx) && ctx.a === 999 && ctx.b === 0] + ] ]) function verifyAEntries(