@@ -213,28 +213,107 @@ function convertRadialGradient(parts: string[]): {
213213 type : 'GRADIENT_RADIAL' ;
214214} {
215215 // Parse radial gradient syntax: radial-gradient([shape size] [at position], color-stops)
216- // For Figma, we'll use a basic radial transform centered at 0.5, 0.5
217- // More complex positioning and sizing could be added later
218-
219- // Skip shape/size/position parameters for now and focus on color stops
216+ let centerX = 0.5 ;
217+ let centerY = 0.5 ;
220218 let colorStopsStart = 0 ;
221- if ( parts . length > 0 && ! parts [ 0 ] . includes ( '#' ) && ! parts [ 0 ] . includes ( 'rgb' ) && ! parts [ 0 ] . includes ( 'hsl' ) ) {
222- // First part might be shape/size/position, skip it
219+
220+ const isPositionPart = ( part : string ) => {
221+ const lowerPart = part . toLowerCase ( ) ;
222+ return (
223+ lowerPart . includes ( 'at ' )
224+ || lowerPart . includes ( 'circle' )
225+ || lowerPart . includes ( 'ellipse' )
226+ || lowerPart . includes ( 'closest-' )
227+ || lowerPart . includes ( 'farthest-' )
228+ ) ;
229+ } ;
230+
231+ if ( parts . length > 0 && isPositionPart ( parts [ 0 ] ) ) {
232+ const positionPart = parts [ 0 ] ;
223233 colorStopsStart = 1 ;
234+
235+ const parseCoord = ( coord : string ) => {
236+ switch ( coord ) {
237+ case 'left' : return 0 ;
238+ case 'right' : return 1 ;
239+ case 'top' : return 0 ;
240+ case 'bottom' : return 1 ;
241+ case 'center' : return 0.5 ;
242+ default :
243+ if ( coord . endsWith ( '%' ) ) return parseFloat ( coord ) / 100 ;
244+ return 0.5 ;
245+ }
246+ } ;
247+
248+ const lowerPositionPart = positionPart . toLowerCase ( ) ;
249+ let positionString = '' ;
250+ if ( lowerPositionPart . includes ( ' at ' ) ) {
251+ [ , positionString ] = lowerPositionPart . split ( ' at ' ) ;
252+ } else if ( lowerPositionPart . startsWith ( 'at ' ) ) {
253+ positionString = lowerPositionPart . substring ( 3 ) ;
254+ }
255+
256+ if ( positionString ) {
257+ const posParts = positionString . trim ( ) . split ( / \s + / ) ;
258+ if ( posParts . length === 1 ) {
259+ const p = posParts [ 0 ] ;
260+ if ( [ 'left' , 'right' ] . includes ( p ) ) {
261+ centerX = parseCoord ( p ) ;
262+ centerY = 0.5 ;
263+ } else if ( [ 'top' , 'bottom' ] . includes ( p ) ) {
264+ centerX = 0.5 ;
265+ centerY = parseCoord ( p ) ;
266+ } else {
267+ centerX = parseCoord ( p ) ;
268+ centerY = 0.5 ;
269+ }
270+ } else if ( posParts . length >= 2 ) {
271+ const first = posParts [ 0 ] ;
272+ const second = posParts [ 1 ] ;
273+ const firstIsY = [ 'top' , 'bottom' ] . includes ( first ) ;
274+ const secondIsX = [ 'left' , 'right' ] . includes ( second ) ;
275+
276+ if ( firstIsY || secondIsX ) {
277+ centerY = parseCoord ( first ) ;
278+ centerX = parseCoord ( second ) ;
279+ } else {
280+ centerX = parseCoord ( first ) ;
281+ centerY = parseCoord ( second ) ;
282+ }
283+ }
284+ }
224285 }
225286
226287 const colorStopParts = parts . slice ( colorStopsStart ) ;
227- const gradientStops = parseColorStops ( colorStopParts ) ;
228288
229- // Create identity transform for basic radial gradient centered at 0.5, 0.5
230- const gradientTransform : Transform = [
231- [ 1 , 0 , 0 ] ,
232- [ 0 , 1 , 0 ] ,
289+ // a, e are the scale factors.
290+ // For the most common CSS keywords, we use verified matrices to match Figma's behavior.
291+ let matrix : Transform | null = null ;
292+ const posString = parts [ 0 ] ?. toLowerCase ( ) || '' ;
293+
294+ if ( posString . includes ( 'at top' ) && ! posString . includes ( 'left' ) && ! posString . includes ( 'right' ) ) {
295+ matrix = [ [ 1.0 , 0 , 0 ] , [ 0 , 1.0 , 0.5 ] ] ; // Top Edge center at 1.0
296+ } else if ( posString . includes ( 'at bottom' ) && ! posString . includes ( 'left' ) && ! posString . includes ( 'right' ) ) {
297+ matrix = [ [ 1.0 , 0 , 0 ] , [ 0 , 1.0 , - 0.5 ] ] ; // Bottom Edge center at 0.0
298+ } else if ( posString . includes ( 'at left' ) && ! posString . includes ( 'top' ) && ! posString . includes ( 'bottom' ) ) {
299+ matrix = [ [ 1.0 , 0 , - 0.5 ] , [ 0 , 1.0 , 0 ] ] ; // Left Edge center at 0.0
300+ } else if ( posString . includes ( 'at right' ) && ! posString . includes ( 'top' ) && ! posString . includes ( 'bottom' ) ) {
301+ matrix = [ [ 1.0 , 0 , 0.5 ] , [ 0 , 1.0 , 0 ] ] ; // Right Edge center at 1.0
302+ }
303+
304+ const figmaCenterY = 1 - centerY ;
305+ const figmaScaleY = 2 * Math . max ( figmaCenterY , 1 - figmaCenterY ) ;
306+ const correctedTy = figmaCenterY - 0.5 * figmaScaleY ;
307+ const correctedTx = centerX - 0.5 * ( 2 * Math . max ( centerX , 1 - centerX ) ) ;
308+
309+ const gradientTransform : Transform = matrix || [
310+ [ roundToPrecision ( 2 * Math . max ( centerX , 1 - centerX ) ) , 0 , roundToPrecision ( correctedTx ) ] ,
311+ [ 0 , roundToPrecision ( figmaScaleY ) , roundToPrecision ( correctedTy ) ] ,
233312 ] ;
234313
235314 return {
236- type : 'GRADIENT_RADIAL' as const ,
237- gradientStops,
315+ type : 'GRADIENT_RADIAL' ,
316+ gradientStops : parseColorStops ( colorStopParts ) ,
238317 gradientTransform,
239318 } ;
240319}
@@ -283,14 +362,14 @@ function convertConicGradient(parts: string[]): {
283362}
284363
285364// if node type check is needed due to bugs caused by obscure node types, use (value: string/*, node?: BaseNode | PaintStyle) and convertStringToFigmaGradient(value, target)
286- export function convertStringToFigmaGradient ( value : string ) : {
365+ export function convertStringToFigmaGradient ( gradientString : string ) : {
287366 gradientStops : ColorStop [ ] ;
288367 gradientTransform : Transform ;
289- type ? : 'GRADIENT_LINEAR' | 'GRADIENT_RADIAL' | 'GRADIENT_ANGULAR' ;
368+ type : 'GRADIENT_LINEAR' | 'GRADIENT_RADIAL' | 'GRADIENT_ANGULAR' | 'GRADIENT_DIAMOND ';
290369} {
291370 // Detect gradient type from the CSS function name
292- const gradientType = value . substring ( 0 , value . indexOf ( '(' ) ) ;
293- const innerContent = value . substring ( value . indexOf ( '(' ) + 1 , value . lastIndexOf ( ')' ) ) ;
371+ const gradientType = gradientString . substring ( 0 , gradientString . indexOf ( '(' ) ) . trim ( ) . toLowerCase ( ) ;
372+ const innerContent = gradientString . substring ( gradientString . indexOf ( '(' ) + 1 , gradientString . lastIndexOf ( ')' ) ) ;
294373 const parts = parseGradientParts ( innerContent ) ;
295374
296375 switch ( gradientType ) {
0 commit comments