diff --git a/packages/core/src/kiwi/fig/node-change/convert.ts b/packages/core/src/kiwi/fig/node-change/convert.ts index 9da425397..9c50397f1 100644 --- a/packages/core/src/kiwi/fig/node-change/convert.ts +++ b/packages/core/src/kiwi/fig/node-change/convert.ts @@ -3,7 +3,7 @@ import { DEFAULT_FONT_FAMILY, DEFAULT_STROKE_MITER_LIMIT } from '#core/constants import { parseVariantName } from '#core/scene-graph/variant-name' import { styleToWeight } from '#core/text/fonts' -import { guidToString } from './guid' +import { guidToString } from '@open-pencil/kiwi/fig/guid' import { convertEffects, convertFills, convertStrokes } from './paint' import { importStyleRuns } from './style-runs' export { importStyleRuns } from './style-runs' @@ -52,7 +52,7 @@ import type { } from '#core/scene-graph' import type { GUID } from '#core/types' -export { guidToString, stringToGuid } from './guid' +export { guidToString, stringToGuid } from '@open-pencil/kiwi/fig/guid' export const VARIABLE_BINDING_FIELDS: Record = { // Corner radius diff --git a/packages/core/src/kiwi/fig/node-change/export-node.ts b/packages/core/src/kiwi/fig/node-change/export-node.ts index acfa2d658..5f480a546 100644 --- a/packages/core/src/kiwi/fig/node-change/export-node.ts +++ b/packages/core/src/kiwi/fig/node-change/export-node.ts @@ -5,7 +5,7 @@ import { bytesToHex } from '#core/bytes/hex' import type { SceneGraph, SceneNode } from '#core/scene-graph' import type { Color, GUID, Matrix, Vector } from '#core/types' -import { stringToGuid } from './guid' +import { stringToGuid } from '@open-pencil/kiwi/fig/guid' import { applyExportSettingsPluginData, mergePluginData, diff --git a/packages/core/src/kiwi/fig/node-change/paint.ts b/packages/core/src/kiwi/fig/node-change/paint.ts index ed109da78..e1df2c97b 100644 --- a/packages/core/src/kiwi/fig/node-change/paint.ts +++ b/packages/core/src/kiwi/fig/node-change/paint.ts @@ -1,7 +1,7 @@ import type { Paint, Effect as KiwiEffect } from '@open-pencil/kiwi/fig/codec' +import { guidToString } from '@open-pencil/kiwi/fig/guid' import { normalizeColor } from '#core/color' -import { guidToString } from '#core/kiwi/fig/node-change/guid' import type { Fill, FillType, diff --git a/packages/core/src/kiwi/fig/node-change/style-refs.ts b/packages/core/src/kiwi/fig/node-change/style-refs.ts index a5a445e0e..5436f73ca 100644 --- a/packages/core/src/kiwi/fig/node-change/style-refs.ts +++ b/packages/core/src/kiwi/fig/node-change/style-refs.ts @@ -1,6 +1,5 @@ import type { GUID, NodeChange } from '@open-pencil/kiwi/fig/codec' - -import { guidToString } from '#core/kiwi/fig/node-change/guid' +import { guidToString } from '@open-pencil/kiwi/fig/guid' const TEXT_STYLE_FIELDS = [ 'fontSize', diff --git a/packages/kiwi/package.json b/packages/kiwi/package.json index b0bb2bc6c..c8a77f171 100644 --- a/packages/kiwi/package.json +++ b/packages/kiwi/package.json @@ -34,6 +34,12 @@ "import": "./dist/fig/container.js", "default": "./dist/fig/container.js" }, + "./fig/guid": { + "types": "./dist/src/fig/guid.d.ts", + "bun": "./src/fig/guid.ts", + "import": "./dist/fig/guid.js", + "default": "./dist/fig/guid.js" + }, "./fig/parse": { "types": "./dist/src/fig/parse.d.ts", "bun": "./src/fig/parse.ts", diff --git a/packages/kiwi/scripts/smoke-dist.ts b/packages/kiwi/scripts/smoke-dist.ts index bdd88b584..7c7f4fc71 100644 --- a/packages/kiwi/scripts/smoke-dist.ts +++ b/packages/kiwi/scripts/smoke-dist.ts @@ -1,5 +1,6 @@ import { getSchemaBytes, initCodec, isCodecReady } from '../dist/fig/codec.js' import { buildFigKiwi, parseFigKiwiChunks } from '../dist/fig/container.js' +import { guidToString } from '../dist/fig/guid.js' import { parseFigKiwiContainer } from '../dist/fig/parse.js' import { figmaSchema, getKiwiMessageType, parseSchema, validateSchema } from '../dist/index.js' @@ -33,3 +34,7 @@ const figKiwi = buildFigKiwi(new Uint8Array([1]), new Uint8Array([2])) if (parseFigKiwiChunks(figKiwi)?.length !== 2) { throw new Error('Failed to read built FIG Kiwi container chunks') } + +if (guidToString({ sessionID: 1, localID: 2 }) !== '1:2') { + throw new Error('Failed to format built FIG GUID helper') +} diff --git a/packages/core/src/kiwi/fig/node-change/guid.ts b/packages/kiwi/src/fig/guid.ts similarity index 86% rename from packages/core/src/kiwi/fig/node-change/guid.ts rename to packages/kiwi/src/fig/guid.ts index e86d1fcfc..73aa97342 100644 --- a/packages/core/src/kiwi/fig/node-change/guid.ts +++ b/packages/kiwi/src/fig/guid.ts @@ -1,4 +1,4 @@ -import type { GUID } from '@open-pencil/kiwi/fig/codec' +import type { GUID } from './types' export function guidToString(guid: GUID): string { return `${guid.sessionID}:${guid.localID}` @@ -6,8 +6,9 @@ export function guidToString(guid: GUID): string { export function stringToGuid(str: string): GUID { const match = str.match(/^(?:VariableID:|VariableCollectionId:)?(\d+):(\d+)$/) - if (match) + if (match) { return { sessionID: Number.parseInt(match[1], 10), localID: Number.parseInt(match[2], 10) } + } const [session, local] = str.split(':') return { sessionID: Number.parseInt(session, 10), localID: Number.parseInt(local, 10) } } diff --git a/packages/kiwi/tests/guid.test.ts b/packages/kiwi/tests/guid.test.ts new file mode 100644 index 000000000..d420cf564 --- /dev/null +++ b/packages/kiwi/tests/guid.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, test } from 'bun:test' + +import { guidToString, stringToGuid } from '../src/fig/guid' + +describe('Figma GUID helpers', () => { + test('formats GUID structs as Figma IDs', () => { + expect(guidToString({ sessionID: 12, localID: 34 })).toBe('12:34') + }) + + test('parses plain Figma IDs', () => { + expect(stringToGuid('12:34')).toEqual({ sessionID: 12, localID: 34 }) + }) + + test('parses variable-prefixed Figma IDs', () => { + expect(stringToGuid('VariableID:12:34')).toEqual({ sessionID: 12, localID: 34 }) + expect(stringToGuid('VariableCollectionId:56:78')).toEqual({ + sessionID: 56, + localID: 78 + }) + }) +}) diff --git a/packages/kiwi/tsdown.config.ts b/packages/kiwi/tsdown.config.ts index 7f32c29d5..5390a1db4 100644 --- a/packages/kiwi/tsdown.config.ts +++ b/packages/kiwi/tsdown.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ fig: './src/fig/index.ts', 'fig/codec': './src/fig/codec.ts', 'fig/container': './src/fig/container.ts', + 'fig/guid': './src/fig/guid.ts', 'fig/parse': './src/fig/parse.ts' }, plugins: [rawText()], diff --git a/tests/engine/io/subgraph.test.ts b/tests/engine/io/subgraph.test.ts index 0144270ed..ad80c7bbd 100644 --- a/tests/engine/io/subgraph.test.ts +++ b/tests/engine/io/subgraph.test.ts @@ -3,8 +3,8 @@ import { readFileSync } from 'node:fs' import { exportFigFile, extractExportGraph, parseFigFile } from '@open-pencil/core/io' import { initCodec } from '@open-pencil/core/kiwi' -import { guidToString } from '@open-pencil/core/kiwi/fig/node-change/guid' import { SceneGraph } from '@open-pencil/core/scene-graph' +import { guidToString } from '@open-pencil/kiwi/fig/guid' import { parseFigBuffer } from '@open-pencil/kiwi/fig/parse' describe('export subgraph extraction', () => {