fix(kiwi): preserve more Figma metadata
This commit is contained in:
parent
e31234a0ed
commit
4cd187c766
|
|
@ -293,12 +293,18 @@ export interface NodeChange {
|
|||
cornerRadius?: number
|
||||
fillPaints?: Paint[]
|
||||
strokePaints?: Paint[]
|
||||
backgroundPaints?: Paint[]
|
||||
strokeWeight?: number
|
||||
strokeAlign?: string
|
||||
strokeCap?: string
|
||||
strokeJoin?: string
|
||||
dashPattern?: number[]
|
||||
effects?: Effect[]
|
||||
mask?: boolean
|
||||
maskType?: string
|
||||
maskIsOutline?: boolean
|
||||
exportSettings?: unknown[]
|
||||
layoutGrids?: unknown[]
|
||||
// Layout
|
||||
stackMode?: 'NONE' | 'HORIZONTAL' | 'VERTICAL'
|
||||
stackSpacing?: number
|
||||
|
|
@ -380,6 +386,12 @@ export interface NodeChange {
|
|||
textBidiVersion?: number
|
||||
textDecoration?: string
|
||||
textDecorationSkipInk?: boolean
|
||||
textDecorationFillPaints?: Paint[]
|
||||
textUnderlineOffset?: { value?: number; units?: string }
|
||||
textDecorationThickness?: { value?: number; units?: string }
|
||||
textDecorationStyle?: string
|
||||
toggledOnOTFeatures?: string[]
|
||||
toggledOffOTFeatures?: string[]
|
||||
fontVariations?: Array<{ axisTag?: number; axisName?: string; value?: number }>
|
||||
fontVariantCommonLigatures?: boolean
|
||||
fontVariantContextualLigatures?: boolean
|
||||
|
|
@ -404,6 +416,10 @@ export interface NodeChange {
|
|||
// Constraints
|
||||
horizontalConstraint?: string
|
||||
verticalConstraint?: string
|
||||
// Prototype
|
||||
prototypeStartNodeID?: GUID
|
||||
prototypeInteractions?: unknown[]
|
||||
transitionInfo?: unknown
|
||||
// Variables
|
||||
variableData?: VariableDataEntry
|
||||
variableConsumptionMap?: { entries?: VariableConsumptionEntry[] }
|
||||
|
|
|
|||
|
|
@ -760,6 +760,9 @@ export const FIGMA_RAW_NODE_FIELD_KEYS = [
|
|||
'styleIdForText',
|
||||
'styleIdForEffect',
|
||||
'styleIdForGrid',
|
||||
'backgroundPaints',
|
||||
'layoutGrids',
|
||||
'exportSettings',
|
||||
'componentPropDefs',
|
||||
'componentPropRefs',
|
||||
'variantPropSpecs',
|
||||
|
|
@ -784,6 +787,9 @@ export const FIGMA_RAW_NODE_FIELD_KEYS = [
|
|||
'handoffStatusMap',
|
||||
'annotationCategories',
|
||||
'miterLimit',
|
||||
'mask',
|
||||
'maskType',
|
||||
'maskIsOutline',
|
||||
'strokeWeight',
|
||||
'strokeJoin',
|
||||
'borderStrokeWeightsIndependent',
|
||||
|
|
@ -815,7 +821,12 @@ export const FIGMA_RAW_NODE_FIELD_KEYS = [
|
|||
'fontVariations',
|
||||
'fontVariantCommonLigatures',
|
||||
'fontVariantContextualLigatures',
|
||||
'toggledOnOTFeatures',
|
||||
'toggledOffOTFeatures',
|
||||
'leadingTrim',
|
||||
'textDecorationFillPaints',
|
||||
'textUnderlineOffset',
|
||||
'textDecorationThickness',
|
||||
'textDecorationStyle',
|
||||
'semanticWeight',
|
||||
'semanticItalic',
|
||||
|
|
@ -826,6 +837,9 @@ export const FIGMA_RAW_NODE_FIELD_KEYS = [
|
|||
'strokePaints',
|
||||
'effects',
|
||||
'sectionStatusInfo',
|
||||
'prototypeStartNodeID',
|
||||
'prototypeInteractions',
|
||||
'transitionInfo',
|
||||
'codeSyntax',
|
||||
'lockMode',
|
||||
'slideThemeMap',
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ const RAW_NODE_FIELD_KEYS = new Set([
|
|||
'vectorNetwork',
|
||||
'fillGeometry',
|
||||
'strokeGeometry',
|
||||
'isMask',
|
||||
'maskType',
|
||||
'clipsContent'
|
||||
])
|
||||
|
||||
|
|
|
|||
|
|
@ -195,9 +195,9 @@ OpenPencil deliberately preserves many Figma/Kiwi fields even when they are not
|
|||
| Version/sort/publish/library metadata | ↩ | — | ◐ | Assets UI shows a subset; publish/update workflow is missing. |
|
||||
| Variable and parameter consumption maps | ✅ | ◐ | ◐ | Filtered/preserved for safe round-trip; normalized bindings cover common cases. |
|
||||
| Page fields: background, page type, guides | ↩ | ◐ | — | Background/page type/guides mostly round-trip. Guides are not rendered/editable. |
|
||||
| Text internals: `textData`, layout versions, font version, derived data | ✅ | ✅ | — | Important for text fidelity; most internals are not editable. Imported derived text data, leading trim, decoration style, and semantic font metadata are preserved for round-trip when safe. |
|
||||
| Text internals: `textData`, layout versions, font version, derived data | ✅ | ✅ | — | Important for text fidelity; most internals are not editable. Imported derived text data, leading trim, decoration style, underline decoration paint/offset/thickness, semantic font metadata, and raw OpenType feature toggles are preserved for round-trip when safe. |
|
||||
| `fontVariations` | ✅ | ✅ | — | Variable font axes are imported, rendered, and exported for text nodes and style runs. |
|
||||
| Raw paint/effect/vector/geometry payloads | ✅ | ✅ | ◐ | Converted fields render; raw payloads preserve Figma import/export details. |
|
||||
| Raw paint/effect/vector/geometry payloads | ✅ | ✅ | ◐ | Converted fields render; raw payloads preserve Figma import/export details, including mask, background paint, layout grid, export setting, and prototype interaction metadata where safe. |
|
||||
|
||||
## Highest-priority visual gaps
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ const RAW_FIELD_COVERAGE = {
|
|||
'borderStrokeWeightsIndependent',
|
||||
'borderTopWeight',
|
||||
'derivedTextData',
|
||||
'backgroundPaints',
|
||||
'effects',
|
||||
'fillGeometry',
|
||||
'fillPaints',
|
||||
|
|
@ -19,26 +20,36 @@ const RAW_FIELD_COVERAGE = {
|
|||
'fontVariantCommonLigatures',
|
||||
'fontVariantContextualLigatures',
|
||||
'fontVariations',
|
||||
'layoutGrids',
|
||||
'leadingTrim',
|
||||
'letterSpacing',
|
||||
'lineHeight',
|
||||
'mask',
|
||||
'maskIsOutline',
|
||||
'maskType',
|
||||
'maxLines',
|
||||
'miterLimit',
|
||||
'semanticItalic',
|
||||
'semanticWeight',
|
||||
'strokeGeometry',
|
||||
'textDecorationFillPaints',
|
||||
'strokeJoin',
|
||||
'strokePaints',
|
||||
'strokeWeight',
|
||||
'textAutoResize',
|
||||
'textData',
|
||||
'textDecorationStyle',
|
||||
'textUnderlineOffset',
|
||||
'textDecorationThickness',
|
||||
'textPathStart',
|
||||
'textTracking',
|
||||
'toggledOffOTFeatures',
|
||||
'toggledOnOTFeatures',
|
||||
'vectorData'
|
||||
],
|
||||
uiEditable: [
|
||||
'componentPropDefs',
|
||||
'exportSettings',
|
||||
'fontSize',
|
||||
'letterSpacing',
|
||||
'lineHeight',
|
||||
|
|
@ -88,6 +99,8 @@ const RAW_FIELD_COVERAGE = {
|
|||
'minSize',
|
||||
'pageType',
|
||||
'parameterConsumptionMap',
|
||||
'prototypeInteractions',
|
||||
'prototypeStartNodeID',
|
||||
'scatterStrokeSettings',
|
||||
'sectionStatusInfo',
|
||||
'slideThemeMap',
|
||||
|
|
@ -102,6 +115,7 @@ const RAW_FIELD_COVERAGE = {
|
|||
'textExplicitLayoutVersion',
|
||||
'textUserLayoutVersion',
|
||||
'targetAspectRatio',
|
||||
'transitionInfo',
|
||||
'userFacingVersion',
|
||||
'variableConsumptionMap',
|
||||
'variableModeBySetMap',
|
||||
|
|
|
|||
|
|
@ -373,17 +373,17 @@ describe('Figma Kiwi schema coverage', () => {
|
|||
expect(
|
||||
Object.fromEntries([...buckets].map(([bucket, items]) => [bucket, items.length]))
|
||||
).toEqual({
|
||||
modeled: 89,
|
||||
modeled: 104,
|
||||
schemaTag: 60,
|
||||
internalBookkeeping: 18,
|
||||
rawPreserved: 53,
|
||||
internalBookkeeping: 17,
|
||||
rawPreserved: 52,
|
||||
styleLibraryMetadata: 39,
|
||||
componentInstanceMetadata: 42,
|
||||
textMetadata: 28,
|
||||
textMetadata: 23,
|
||||
slideFigjamMetadata: 39,
|
||||
visualGeometryMetadata: 42,
|
||||
layoutMetadata: 30,
|
||||
prototypeConnectorMetadata: 35,
|
||||
visualGeometryMetadata: 38,
|
||||
layoutMetadata: 29,
|
||||
prototypeConnectorMetadata: 32,
|
||||
variableDevMetadata: 14,
|
||||
widgetMetadata: 11,
|
||||
codeCmsAiMetadata: 67,
|
||||
|
|
@ -405,6 +405,15 @@ describe('Figma Kiwi schema coverage', () => {
|
|||
expect(covered('textDecorationStyle')).toBe(true)
|
||||
expect(covered('semanticWeight')).toBe(true)
|
||||
expect(covered('semanticItalic')).toBe(true)
|
||||
expect(covered('toggledOnOTFeatures')).toBe(true)
|
||||
expect(covered('toggledOffOTFeatures')).toBe(true)
|
||||
expect(covered('textDecorationFillPaints')).toBe(true)
|
||||
expect(covered('textUnderlineOffset')).toBe(true)
|
||||
expect(covered('textDecorationThickness')).toBe(true)
|
||||
expect(covered('mask')).toBe(true)
|
||||
expect(covered('maskType')).toBe(true)
|
||||
expect(covered('maskIsOutline')).toBe(true)
|
||||
expect(covered('layoutGrids')).toBe(true)
|
||||
expect(covered('gridChildVerticalAlign')).toBe(true)
|
||||
expect(covered('gridChildHorizontalAlign')).toBe(true)
|
||||
expect(covered('slideThemeMap')).toBe(true)
|
||||
|
|
|
|||
|
|
@ -137,6 +137,13 @@ describe('fig roundtrip source metadata', () => {
|
|||
text.source.id = '4:502'
|
||||
text.source.fig.rawNodeFields.leadingTrim = 'CAP_HEIGHT'
|
||||
text.source.fig.rawNodeFields.textDecorationStyle = 'WAVY'
|
||||
text.source.fig.rawNodeFields.textDecorationFillPaints = [
|
||||
{ type: 'SOLID', color: { r: 1, g: 0, b: 0, a: 1 }, opacity: 1 }
|
||||
]
|
||||
text.source.fig.rawNodeFields.textUnderlineOffset = { value: 2, units: 'PIXELS' }
|
||||
text.source.fig.rawNodeFields.textDecorationThickness = { value: 1.5, units: 'PIXELS' }
|
||||
text.source.fig.rawNodeFields.toggledOnOTFeatures = ['DLIG']
|
||||
text.source.fig.rawNodeFields.toggledOffOTFeatures = ['LIGA']
|
||||
text.source.fig.rawNodeFields.semanticWeight = 'BOLD'
|
||||
text.source.fig.rawNodeFields.semanticItalic = 'ITALIC'
|
||||
text.source.fig.rawNodeFields.derivedTextData = {
|
||||
|
|
@ -151,6 +158,11 @@ describe('fig roundtrip source metadata', () => {
|
|||
|
||||
expect(exported?.leadingTrim).toBe('CAP_HEIGHT')
|
||||
expect(exported?.textDecorationStyle).toBe('WAVY')
|
||||
expect(exported?.textDecorationFillPaints?.[0]?.type).toBe('SOLID')
|
||||
expect(exported?.textUnderlineOffset).toEqual({ value: 2, units: 'PIXELS' })
|
||||
expect(exported?.textDecorationThickness).toEqual({ value: 1.5, units: 'PIXELS' })
|
||||
expect(exported?.toggledOnOTFeatures).toEqual(['DLIG'])
|
||||
expect(exported?.toggledOffOTFeatures).toEqual(['LIGA'])
|
||||
expect(exported?.semanticWeight).toBe('BOLD')
|
||||
expect(exported?.semanticItalic).toBe('ITALIC')
|
||||
expect(exported?.derivedTextData?.layoutSize).toEqual({ x: 80, y: 20 })
|
||||
|
|
|
|||
Loading…
Reference in a new issue