@@ -2,6 +2,7 @@ import { addWarning } from "../common/commonConversionWarnings";
22import { PluginSettings } from "types" ;
33import { variableToColorName } from "../tailwind/conversionTables" ;
44import { HasGeometryTrait , Node , Paint } from "../api_types" ;
5+ import { calculateRectangleFromBoundingBox } from "../common/commonPosition" ;
56
67// Performance tracking counters
78export let getNodeByIdAsyncTime = 0 ;
@@ -196,7 +197,11 @@ const processNodePair = async (
196197 const nodeType = jsonNode . type ;
197198
198199 // Store the cumulative rotation (parent's cumulative + node's own)
199- jsonNode . cumulativeRotation = parentCumulativeRotation ;
200+ if ( parentNode ) {
201+ // Only add cumulative when there is a parent. This is useful for the GROUP -> FRAME transformation, where
202+ // we want to move the rotation of the GROUP to children, but want to se FRAME to 0.
203+ jsonNode . cumulativeRotation = parentCumulativeRotation ;
204+ }
200205
201206 // Handle empty frames and convert to rectangles
202207 if (
@@ -358,11 +363,33 @@ const processNodePair = async (
358363 }
359364
360365 // Always copy size and position
361- if ( "width" in figmaNode ) {
362- jsonNode . width = figmaNode . width ;
363- jsonNode . height = figmaNode . height ;
364- jsonNode . x = figmaNode . x ;
365- jsonNode . y = figmaNode . y ;
366+ if ( "absoluteBoundingBox" in jsonNode && jsonNode . absoluteBoundingBox ) {
367+ if ( jsonNode . parent ) {
368+ // Extract width and height from bounding box and rotation. This is necessary because Figma JSON API doesn't have width and height.
369+ const rect = calculateRectangleFromBoundingBox (
370+ {
371+ width : jsonNode . absoluteBoundingBox . width ,
372+ height : jsonNode . absoluteBoundingBox . height ,
373+ x :
374+ jsonNode . absoluteBoundingBox . x -
375+ ( jsonNode . parent ?. absoluteBoundingBox . x || 0 ) ,
376+ y :
377+ jsonNode . absoluteBoundingBox . y -
378+ ( jsonNode . parent ?. absoluteBoundingBox . y || 0 ) ,
379+ } ,
380+ - ( ( jsonNode . rotation || 0 ) + ( jsonNode . cumulativeRotation || 0 ) ) ,
381+ ) ;
382+
383+ jsonNode . width = rect . width ;
384+ jsonNode . height = rect . height ;
385+ jsonNode . x = rect . left ;
386+ jsonNode . y = rect . top ;
387+ } else {
388+ jsonNode . width = jsonNode . absoluteBoundingBox . width ;
389+ jsonNode . height = jsonNode . absoluteBoundingBox . height ;
390+ jsonNode . x = 0 ;
391+ jsonNode . y = 0 ;
392+ }
366393 }
367394
368395 if ( "individualStrokeWeights" in jsonNode ) {
@@ -425,8 +452,6 @@ const processNodePair = async (
425452 "children" in figmaNode &&
426453 figmaNode . children . length === jsonNode . children . length
427454 ) {
428- console . log ( "cumulative" , parentCumulativeRotation ) ;
429-
430455 const cumulative =
431456 parentCumulativeRotation +
432457 ( jsonNode . type === "GROUP" ? jsonNode . rotation || 0 : 0 ) ;
@@ -489,21 +514,38 @@ export const nodesToJSON = async (
489514) : Promise < Node [ ] > => {
490515 // Reset name counters for each conversion
491516 nodeNameCounters . clear ( ) ;
492-
493517 const exportJsonStart = Date . now ( ) ;
494- // First get the JSON representation of nodes
495- const nodeJson = ( await Promise . all (
496- nodes . map (
497- async ( node ) =>
498- (
499- ( await node . exportAsync ( {
500- format : "JSON_REST_V1" ,
501- } ) ) as any
502- ) . document ,
503- ) ,
504- ) ) as Node [ ] ;
505-
506- console . log ( "[debug] initial nodeJson" , { ...nodeJson [ 0 ] } ) ;
518+ // First get the JSON representation of nodes with rotation handling
519+ const nodeResults = await Promise . all (
520+ nodes . map ( async ( node ) => {
521+ // Export node to JSON
522+ const nodeDoc = (
523+ ( await node . exportAsync ( {
524+ format : "JSON_REST_V1" ,
525+ } ) ) as any
526+ ) . document ;
527+
528+ let nodeCumulativeRotation = 0 ;
529+
530+ // Wire GROUPs into FRAME.
531+ if ( node . type === "GROUP" ) {
532+ nodeDoc . type = "FRAME" ;
533+
534+ // Fix rotation for children.
535+ if ( "rotation" in nodeDoc && nodeDoc . rotation ) {
536+ nodeCumulativeRotation = - nodeDoc . rotation * ( 180 / Math . PI ) ;
537+ nodeDoc . rotation = 0 ;
538+ }
539+ }
540+
541+ return {
542+ nodeDoc,
543+ nodeCumulativeRotation,
544+ } ;
545+ } ) ,
546+ ) ;
547+
548+ console . log ( "[debug] initial nodeJson" , { ...nodes [ 0 ] } ) ;
507549
508550 console . log (
509551 `[benchmark][inside nodesToJSON] JSON_REST_V1 export: ${ Date . now ( ) - exportJsonStart } ms` ,
@@ -515,9 +557,11 @@ export const nodesToJSON = async (
515557
516558 for ( let i = 0 ; i < nodes . length ; i ++ ) {
517559 const processedNode = await processNodePair (
518- nodeJson [ i ] ,
560+ nodeResults [ i ] . nodeDoc ,
519561 nodes [ i ] ,
520562 settings ,
563+ undefined ,
564+ nodeResults [ i ] . nodeCumulativeRotation ,
521565 ) ;
522566 if ( processedNode !== null ) {
523567 if ( Array . isArray ( processedNode ) ) {
0 commit comments