Skip to content

Commit 92fc5d6

Browse files
committed
Enhance Compose support: add handling for Compose generation modes, improve text escaping, and refine border and padding functions
1 parent f6a5c75 commit 92fc5d6

File tree

13 files changed

+195
-87
lines changed

13 files changed

+195
-87
lines changed

apps/plugin/plugin-src/code.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import {
55
tailwindMain,
66
swiftuiMain,
77
htmlMain,
8+
composeMain,
89
postSettingsChanged,
910
} from "backend";
1011
import { nodesToJSON } from "backend/src/altNodes/jsonNodeConversion";
1112
import { retrieveGenericSolidUIColors } from "backend/src/common/retrieveUI/retrieveColors";
1213
import { flutterCodeGenTextStyles } from "backend/src/flutter/flutterMain";
1314
import { htmlCodeGenTextStyles } from "backend/src/html/htmlMain";
1415
import { swiftUICodeGenTextStyles } from "backend/src/swiftui/swiftuiMain";
16+
import { composeCodeGenTextStyles } from "backend/src/compose/composeMain";
1517
import { PluginSettings, SettingWillChangeMessage } from "types";
1618

1719
let userPluginSettings: PluginSettings;
@@ -394,6 +396,22 @@ const codegenMode = async () => {
394396
language: "SWIFT",
395397
},
396398
];
399+
// case "compose":
400+
// return [
401+
// {
402+
// title: "Jetpack Compose",
403+
// code: composeMain(convertedSelection, {
404+
// ...userPluginSettings,
405+
// composeGenerationMode: "snippet",
406+
// }),
407+
// language: "KOTLIN",
408+
// },
409+
// {
410+
// title: "Text Styles",
411+
// code: composeCodeGenTextStyles(),
412+
// language: "KOTLIN",
413+
// },
414+
// ];
397415
default:
398416
break;
399417
}

packages/backend/src/compose/builderImpl/composeAutoLayout.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export const getMainAxisAlignment = (
1111
return "Arrangement.End";
1212
case "SPACE_BETWEEN":
1313
return "Arrangement.SpaceBetween";
14+
default:
15+
return "Arrangement.Start";
1416
}
1517
};
1618

@@ -30,6 +32,8 @@ export const getCrossAxisAlignment = (
3032
return "Alignment.Bottom";
3133
case "BASELINE":
3234
return "Alignment.CenterVertically"; // Compose doesn't have baseline alignment for Row
35+
default:
36+
return "Alignment.Top";
3337
}
3438
} else {
3539
// VERTICAL layout mode
@@ -43,6 +47,8 @@ export const getCrossAxisAlignment = (
4347
return "Alignment.End";
4448
case "BASELINE":
4549
return "Alignment.CenterHorizontally"; // Baseline not applicable for Column
50+
default:
51+
return "Alignment.Start";
4652
}
4753
}
4854
};
@@ -60,6 +66,8 @@ export const getWrapAlignment = (
6066
return "Arrangement.End";
6167
case "SPACE_BETWEEN":
6268
return "Arrangement.SpaceBetween";
69+
default:
70+
return "Arrangement.Start";
6371
}
6472
};
6573

@@ -82,6 +90,8 @@ export const getWrapRunAlignment = (
8290
return "Arrangement.Center";
8391
case "MAX":
8492
return "Arrangement.Bottom";
93+
default:
94+
return "Arrangement.Top";
8595
}
8696
} else {
8797
// FlowColumn - horizontal alignment
@@ -94,6 +104,8 @@ export const getWrapRunAlignment = (
94104
return "Arrangement.Center";
95105
case "MAX":
96106
return "Arrangement.End";
107+
default:
108+
return "Arrangement.Start";
97109
}
98110
}
99111
};

packages/backend/src/compose/builderImpl/composeBorder.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const getStrokeAlignment = (node: SceneNode): "inside" | "center" | "outside" =>
3939
* @param node - The scene node with stroke properties
4040
* @returns Compose border modifier string
4141
*/
42-
export const composeBorder = (node: SceneNode): string => {
42+
export const composeBorder = (node: SceneNode, shape?: string | null): string => {
4343
if (!("strokes" in node)) {
4444
return "";
4545
}
@@ -62,7 +62,7 @@ export const composeBorder = (node: SceneNode): string => {
6262
return "";
6363
}
6464

65-
return generateBorderModifier(stroke.all, strokeFill, strokeAlignment);
65+
return generateBorderModifier(stroke.all, strokeFill, strokeAlignment, shape);
6666
} else {
6767
// Handle non-uniform borders
6868
// Compose doesn't have direct support for different border widths per side
@@ -80,7 +80,7 @@ export const composeBorder = (node: SceneNode): string => {
8080

8181
// For now, use uniform border with max width
8282
// TODO: Consider using Canvas or custom drawing for true non-uniform borders
83-
return generateBorderModifier(maxWidth, strokeFill, strokeAlignment);
83+
return generateBorderModifier(maxWidth, strokeFill, strokeAlignment, shape);
8484
}
8585
};
8686

@@ -90,7 +90,8 @@ export const composeBorder = (node: SceneNode): string => {
9090
const generateBorderModifier = (
9191
width: number,
9292
fill: Paint,
93-
alignment: "inside" | "center" | "outside"
93+
alignment: "inside" | "center" | "outside",
94+
shape?: string | null
9495
): string => {
9596
const widthDp = `${numberToFixedString(width)}.dp`;
9697

@@ -109,11 +110,12 @@ const generateBorderModifier = (
109110
// For alignment, we note that Compose doesn't have built-in stroke alignment
110111
// All borders are essentially "inside" by default
111112
// For outside borders, we might need custom drawing or padding adjustments
113+
const shapeParam = shape ? `, shape = ${shape}` : "";
112114
if (alignment === "outside") {
113115
// Add comment about limitation
114-
return `.border(width = ${widthDp}, color = ${colorValue}) // Note: Compose borders are always inside`;
116+
return `border(width = ${widthDp}, color = ${colorValue}${shapeParam}) // Note: Compose borders are always inside`;
115117
} else {
116-
return `.border(width = ${widthDp}, color = ${colorValue})`;
118+
return `border(width = ${widthDp}, color = ${colorValue}${shapeParam})`;
117119
}
118120
} else if (fill.type === "GRADIENT_LINEAR") {
119121
// Convert gradient to Compose Brush
@@ -133,13 +135,14 @@ const generateBorderModifier = (
133135
}).join(", ");
134136

135137
const brush = `Brush.linearGradient(
136-
colorStops = arrayOf(${stops})
138+
listOf(${stops})
137139
)`;
138140

141+
const shapeParam = shape ? `, shape = ${shape}` : "";
139142
if (alignment === "outside") {
140-
return `.border(width = ${widthDp}, brush = ${brush}) // Note: Compose borders are always inside`;
143+
return `border(width = ${widthDp}, brush = ${brush}${shapeParam}) // Note: Compose borders are always inside`;
141144
} else {
142-
return `.border(width = ${widthDp}, brush = ${brush})`;
145+
return `border(width = ${widthDp}, brush = ${brush}${shapeParam})`;
143146
}
144147
} else if (fill.type === "GRADIENT_RADIAL") {
145148
// Convert radial gradient to Compose Brush
@@ -159,13 +162,14 @@ const generateBorderModifier = (
159162
}).join(", ");
160163

161164
const brush = `Brush.radialGradient(
162-
colorStops = arrayOf(${stops})
165+
listOf(${stops})
163166
)`;
164167

168+
const shapeParam = shape ? `, shape = ${shape}` : "";
165169
if (alignment === "outside") {
166-
return `.border(width = ${widthDp}, brush = ${brush}) // Note: Compose borders are always inside`;
170+
return `border(width = ${widthDp}, brush = ${brush}${shapeParam}) // Note: Compose borders are always inside`;
167171
} else {
168-
return `.border(width = ${widthDp}, brush = ${brush})`;
172+
return `border(width = ${widthDp}, brush = ${brush}${shapeParam})`;
169173
}
170174
}
171175

packages/backend/src/compose/builderImpl/composeColor.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@ export const composeColor = (fill: Paint): string | null => {
44
if (fill.type === "SOLID") {
55
const color = rgbTo6hex(fill.color);
66
if (fill.opacity !== undefined && fill.opacity < 1) {
7-
const alpha = Math.round(fill.opacity * 255).toString(16).padStart(2, '0');
8-
return `background(Color(0x${alpha}${color.slice(1)}))`;
7+
const alpha = Math.round(fill.opacity * 255).toString(16).padStart(2, '0').toUpperCase();
8+
return `background(Color(0x${alpha}${color.toUpperCase()}))`;
99
}
10-
return `background(Color(0xFF${color.slice(1)}))`;
10+
return `background(Color(0xFF${color.toUpperCase()}))`;
1111
} else if (fill.type === "GRADIENT_LINEAR") {
1212
// Convert gradient to Compose Brush
1313
const stops = fill.gradientStops.map(stop => {
1414
const color = rgbTo6hex(stop.color);
15-
return `${stop.position}f to Color(0xFF${color.slice(1)})`;
15+
return `${stop.position}f to Color(0xFF${color.toUpperCase()})`;
1616
}).join(", ");
1717

1818
return `background(Brush.linearGradient(
19-
colorStops = arrayOf(${stops})
19+
listOf(${stops})
2020
))`;
2121
} else if (fill.type === "GRADIENT_RADIAL") {
2222
const stops = fill.gradientStops.map(stop => {
2323
const color = rgbTo6hex(stop.color);
24-
return `${stop.position}f to Color(0xFF${color.slice(1)})`;
24+
return `${stop.position}f to Color(0xFF${color.toUpperCase()})`;
2525
}).join(", ");
2626

2727
return `background(Brush.radialGradient(
28-
colorStops = arrayOf(${stops})
28+
listOf(${stops})
2929
))`;
3030
}
3131

packages/backend/src/compose/builderImpl/composePadding.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const composePadding = (node: InferredAutoLayoutResult): string => {
2323
if (padding.all === 0) {
2424
return "";
2525
}
26-
return `padding(all = ${numberToFixedString(padding.all)}.dp)`;
26+
return `padding(${numberToFixedString(padding.all)}.dp)`;
2727
}
2828

2929
if ("horizontal" in padding) {

packages/backend/src/compose/builderImpl/composeShadow.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const composeShadow = (effects: readonly Effect[]): string => {
3737
if (effect.spread === 0 && effect.radius <= 8 && effect.offset.x === 0) {
3838
const elevation = Math.abs(effect.offset.y);
3939
if (elevation > 0 && elevation <= 24) {
40-
shadowModifiers.push(`shadow(elevation = ${numberToFixedString(elevation)}.dp)`);
40+
shadowModifiers.push(`shadow(${numberToFixedString(elevation)}.dp)`);
4141
return;
4242
}
4343
}
@@ -57,11 +57,7 @@ export const composeShadow = (effects: readonly Effect[]): string => {
5757
}`);
5858
} else {
5959
// Simple shadow with custom color
60-
shadowModifiers.push(`shadow(
61-
elevation = ${blurRadius}.dp,
62-
ambientColor = Color(0x${color.toUpperCase()}),
63-
spotColor = Color(0x${color.toUpperCase()})
64-
)`);
60+
shadowModifiers.push(`shadow(${blurRadius}.dp, shape = RectangleShape)`);
6561
}
6662
} else if (effect.type === "INNER_SHADOW") {
6763
// Inner shadows in Compose require custom drawing

packages/backend/src/compose/composeContainer.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { numberToFixedString } from "../common/numToAutoFixed";
21
import { retrieveTopFill } from "../common/retrieveFill";
32
import { getCommonRadius } from "../common/commonRadius";
43
import { composeSize } from "./builderImpl/composeSize";
@@ -11,6 +10,13 @@ export const composeContainer = (
1110
node: SceneNode & MinimalBlendMixin,
1211
child: string,
1312
): string => {
13+
// Safety check for node dimensions
14+
if ("width" in node && "height" in node) {
15+
if ((node.width <= 0 || node.height <= 0) && !child) {
16+
return "// Invalid node dimensions";
17+
}
18+
}
19+
1420
const modifiers: string[] = [];
1521
let containerType = "Box";
1622

@@ -33,23 +39,26 @@ export const composeContainer = (
3339
}
3440

3541
// Border radius
42+
let shape = null;
3643
if ("cornerRadius" in node || "topLeftRadius" in node) {
3744
const radius = getCommonRadius(node);
3845
if ("all" in radius && radius.all > 0) {
39-
modifiers.push(`clip(RoundedCornerShape(${radius.all}.dp))`);
46+
shape = `RoundedCornerShape(${radius.all}.dp)`;
47+
modifiers.push(`clip(${shape})`);
4048
} else if ("topLeft" in radius) {
41-
modifiers.push(`clip(RoundedCornerShape(
49+
shape = `RoundedCornerShape(
4250
topStart = ${radius.topLeft}.dp,
4351
topEnd = ${radius.topRight}.dp,
4452
bottomEnd = ${radius.bottomRight}.dp,
4553
bottomStart = ${radius.bottomLeft}.dp
46-
))`);
54+
)`;
55+
modifiers.push(`clip(${shape})`);
4756
}
4857
}
4958

5059
// Border
5160
if ("strokes" in node && node.strokes.length > 0) {
52-
const borderModifier = composeBorder(node);
61+
const borderModifier = composeBorder(node, shape);
5362
if (borderModifier) {
5463
modifiers.push(borderModifier);
5564
}
@@ -73,7 +82,7 @@ export const composeContainer = (
7382

7483
// Build modifier chain
7584
const modifierChain = modifiers.length > 0
76-
? `modifier = Modifier.${modifiers.join(".")}`
85+
? `modifier = Modifier${modifiers.map(m => `.${m}`).join("")}`
7786
: "";
7887

7988
// Generate container

0 commit comments

Comments
 (0)