diff --git a/packages/core/src/clipboard.ts b/packages/core/src/clipboard.ts
index 368601f84..554f39106 100644
--- a/packages/core/src/clipboard.ts
+++ b/packages/core/src/clipboard.ts
@@ -420,7 +420,7 @@ export function buildFigmaClipboardHTML(nodes: SceneNode[], graph: SceneGraph):
type: 'NODE_CHANGES',
sessionID: 0,
ackID: 0,
- pasteID: crypto.getRandomValues(new Uint32Array(1))[0],
+ pasteID: crypto.getRandomValues(new Int32Array(1))[0],
pasteFileKey: 'openpencil',
nodeChanges
}
diff --git a/src/components/AppToast.vue b/src/components/AppToast.vue
index 407e07a6f..87275f69e 100644
--- a/src/components/AppToast.vue
+++ b/src/components/AppToast.vue
@@ -1,15 +1,20 @@
-
+
{
@@ -17,9 +22,24 @@ import { toast } from '@/composables/use-toast'
}
"
>
-
-
- {{ t.message }}
+
+
+ {{ t.message }}
+
+
+
+
{
expect(figmaNodesBounds(nodes)).toBeNull()
})
})
+
+describe('buildFigmaClipboardHTML', () => {
+ beforeAll(async () => {
+ await initCodec()
+ })
+
+ it('encodes a simple frame without throwing', () => {
+ const graph = new SceneGraph()
+ const page = graph.getPages()[0]
+ const frame = graph.createNode('FRAME', page.id, {
+ name: 'Card',
+ x: 0, y: 0, width: 300, height: 200,
+ fills: [{ type: 'SOLID', color: { r: 1, g: 1, b: 1, a: 1 }, opacity: 1, visible: true }],
+ })
+
+ const html = buildFigmaClipboardHTML([frame], graph)
+ expect(html).toContain('figmeta')
+ expect(html).toContain('figma')
+ })
+
+ it('encodes text nodes with style runs', () => {
+ const graph = new SceneGraph()
+ const page = graph.getPages()[0]
+ const text = graph.createNode('TEXT', page.id, {
+ name: 'Styled',
+ x: 0, y: 0, width: 200, height: 24,
+ text: 'Hello World',
+ fontFamily: 'Inter',
+ fontWeight: 400,
+ fontSize: 16,
+ styleRuns: [
+ { start: 0, length: 5, style: { fontWeight: 700 } },
+ { start: 6, length: 5, style: { fontWeight: 400, italic: true } },
+ ],
+ })
+
+ const html = buildFigmaClipboardHTML([text], graph)
+ expect(html).toContain('figmeta')
+ })
+
+ it('encodes auto-layout frames', () => {
+ const graph = new SceneGraph()
+ const page = graph.getPages()[0]
+ const frame = graph.createNode('FRAME', page.id, {
+ name: 'Row',
+ x: 0, y: 0, width: 400, height: 100,
+ layoutMode: 'HORIZONTAL',
+ itemSpacing: 16,
+ paddingTop: 12, paddingRight: 12, paddingBottom: 12, paddingLeft: 12,
+ primaryAxisSizing: 'HUG',
+ counterAxisSizing: 'FIXED',
+ })
+ graph.createNode('RECTANGLE', frame.id, {
+ name: 'Child',
+ x: 0, y: 0, width: 50, height: 50,
+ })
+
+ const html = buildFigmaClipboardHTML([frame], graph)
+ expect(html).toContain('figmeta')
+ })
+
+ it('roundtrips: encode then decode back', async () => {
+ const graph = new SceneGraph()
+ const page = graph.getPages()[0]
+ const frame = graph.createNode('FRAME', page.id, {
+ name: 'Analytics Overview',
+ x: 0, y: 0, width: 300, height: 200,
+ layoutMode: 'VERTICAL',
+ itemSpacing: 8,
+ paddingTop: 20, paddingRight: 20, paddingBottom: 20, paddingLeft: 20,
+ fills: [{ type: 'SOLID', color: { r: 1, g: 1, b: 1, a: 1 }, opacity: 1, visible: true }],
+ cornerRadius: 12,
+ })
+ graph.createNode('TEXT', frame.id, {
+ name: 'Title',
+ x: 0, y: 0, width: 260, height: 24,
+ text: 'Analytics Overview',
+ fontFamily: 'Inter',
+ fontWeight: 600,
+ fontSize: 18,
+ })
+ graph.createNode('TEXT', frame.id, {
+ name: 'Subtitle',
+ x: 0, y: 0, width: 260, height: 40,
+ text: 'Track your key metrics and performance indicators in real time.',
+ fontFamily: 'Inter',
+ fontWeight: 400,
+ fontSize: 14,
+ })
+
+ const html = buildFigmaClipboardHTML([frame], graph)
+ expect(html).not.toBeNull()
+
+ const parsed = await parseFigmaClipboard(html!)
+ expect(parsed).not.toBeNull()
+ expect(parsed!.nodes.length).toBeGreaterThan(0)
+
+ const graph2 = new SceneGraph()
+ const page2 = graph2.getPages()[0]
+ const created = importClipboardNodes(parsed!.nodes, graph2, page2.id)
+ expect(created).toHaveLength(1)
+
+ const imported = graph2.getNode(created[0])!
+ expect(imported.name).toBe('Analytics Overview')
+ expect(imported.cornerRadius).toBe(12)
+
+ const children = graph2.getChildren(imported.id)
+ expect(children).toHaveLength(2)
+ expect(children[0].text).toBe('Analytics Overview')
+ expect(children[1].text).toContain('Track your key metrics')
+ })
+})