elit 3.5.6 → 3.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/Cargo.toml +1 -1
  2. package/README.md +1 -1
  3. package/desktop/build.rs +83 -0
  4. package/desktop/icon.rs +106 -0
  5. package/desktop/lib.rs +2 -0
  6. package/desktop/main.rs +235 -0
  7. package/desktop/native_main.rs +128 -0
  8. package/desktop/native_renderer/action_widgets.rs +184 -0
  9. package/desktop/native_renderer/app_models.rs +171 -0
  10. package/desktop/native_renderer/app_runtime.rs +140 -0
  11. package/desktop/native_renderer/container_rendering.rs +610 -0
  12. package/desktop/native_renderer/content_widgets.rs +634 -0
  13. package/desktop/native_renderer/css_models.rs +371 -0
  14. package/desktop/native_renderer/embedded_surfaces.rs +414 -0
  15. package/desktop/native_renderer/form_controls.rs +516 -0
  16. package/desktop/native_renderer/interaction_dispatch.rs +89 -0
  17. package/desktop/native_renderer/runtime_support.rs +135 -0
  18. package/desktop/native_renderer/utilities.rs +495 -0
  19. package/desktop/native_renderer/vector_drawing.rs +491 -0
  20. package/desktop/native_renderer.rs +4122 -0
  21. package/desktop/runtime/external.rs +422 -0
  22. package/desktop/runtime/mod.rs +67 -0
  23. package/desktop/runtime/quickjs.rs +106 -0
  24. package/desktop/window.rs +383 -0
  25. package/dist/build.d.ts +1 -1
  26. package/dist/cli.cjs +16 -2
  27. package/dist/cli.mjs +16 -2
  28. package/dist/config.d.ts +1 -1
  29. package/dist/coverage.d.ts +1 -1
  30. package/dist/desktop-auto-render.cjs +2370 -0
  31. package/dist/desktop-auto-render.d.ts +13 -0
  32. package/dist/desktop-auto-render.js +2341 -0
  33. package/dist/desktop-auto-render.mjs +2344 -0
  34. package/dist/render-context.cjs +118 -0
  35. package/dist/render-context.d.ts +39 -0
  36. package/dist/render-context.js +77 -0
  37. package/dist/render-context.mjs +87 -0
  38. package/dist/{server-CNgDUgSZ.d.ts → server-FCdUqabc.d.ts} +1 -1
  39. package/dist/server.d.ts +1 -1
  40. package/package.json +26 -3
  41. package/dist/build.d.mts +0 -20
  42. package/dist/chokidar.d.mts +0 -134
  43. package/dist/cli.d.mts +0 -81
  44. package/dist/config.d.mts +0 -254
  45. package/dist/coverage.d.mts +0 -85
  46. package/dist/database.d.mts +0 -52
  47. package/dist/desktop.d.mts +0 -68
  48. package/dist/dom.d.mts +0 -87
  49. package/dist/el.d.mts +0 -208
  50. package/dist/fs.d.mts +0 -255
  51. package/dist/hmr.d.mts +0 -38
  52. package/dist/http.d.mts +0 -169
  53. package/dist/https.d.mts +0 -108
  54. package/dist/index.d.mts +0 -13
  55. package/dist/mime-types.d.mts +0 -48
  56. package/dist/native.d.mts +0 -136
  57. package/dist/path.d.mts +0 -163
  58. package/dist/router.d.mts +0 -49
  59. package/dist/runtime.d.mts +0 -97
  60. package/dist/server-D0Dp4R5z.d.mts +0 -449
  61. package/dist/server.d.mts +0 -7
  62. package/dist/state.d.mts +0 -117
  63. package/dist/style.d.mts +0 -232
  64. package/dist/test-reporter.d.mts +0 -77
  65. package/dist/test-runtime.d.mts +0 -122
  66. package/dist/test.d.mts +0 -39
  67. package/dist/types.d.mts +0 -586
  68. package/dist/universal.d.mts +0 -21
  69. package/dist/ws.d.mts +0 -200
  70. package/dist/wss.d.mts +0 -108
  71. package/src/build.ts +0 -362
  72. package/src/chokidar.ts +0 -427
  73. package/src/cli.ts +0 -1162
  74. package/src/config.ts +0 -509
  75. package/src/coverage.ts +0 -1479
  76. package/src/database.ts +0 -1410
  77. package/src/desktop-auto-render.ts +0 -317
  78. package/src/desktop-cli.ts +0 -1533
  79. package/src/desktop.ts +0 -99
  80. package/src/dev-build.ts +0 -340
  81. package/src/dom.ts +0 -901
  82. package/src/el.ts +0 -183
  83. package/src/fs.ts +0 -609
  84. package/src/hmr.ts +0 -149
  85. package/src/http.ts +0 -856
  86. package/src/https.ts +0 -411
  87. package/src/index.ts +0 -16
  88. package/src/mime-types.ts +0 -222
  89. package/src/mobile-cli.ts +0 -2313
  90. package/src/native-background.ts +0 -444
  91. package/src/native-border.ts +0 -343
  92. package/src/native-canvas.ts +0 -260
  93. package/src/native-cli.ts +0 -414
  94. package/src/native-color.ts +0 -904
  95. package/src/native-estimation.ts +0 -194
  96. package/src/native-grid.ts +0 -590
  97. package/src/native-interaction.ts +0 -1289
  98. package/src/native-layout.ts +0 -568
  99. package/src/native-link.ts +0 -76
  100. package/src/native-render-support.ts +0 -361
  101. package/src/native-spacing.ts +0 -231
  102. package/src/native-state.ts +0 -318
  103. package/src/native-strings.ts +0 -46
  104. package/src/native-transform.ts +0 -120
  105. package/src/native-types.ts +0 -439
  106. package/src/native-typography.ts +0 -254
  107. package/src/native-units.ts +0 -441
  108. package/src/native-vector.ts +0 -910
  109. package/src/native.ts +0 -5606
  110. package/src/path.ts +0 -493
  111. package/src/pm-cli.ts +0 -2498
  112. package/src/preview-build.ts +0 -294
  113. package/src/render-context.ts +0 -138
  114. package/src/router.ts +0 -260
  115. package/src/runtime.ts +0 -97
  116. package/src/server.ts +0 -2294
  117. package/src/state.ts +0 -556
  118. package/src/style.ts +0 -1790
  119. package/src/test-globals.d.ts +0 -184
  120. package/src/test-reporter.ts +0 -609
  121. package/src/test-runtime.ts +0 -1359
  122. package/src/test.ts +0 -368
  123. package/src/types.ts +0 -381
  124. package/src/universal.ts +0 -81
  125. package/src/wapk-cli.ts +0 -3213
  126. package/src/workspace-package.ts +0 -102
  127. package/src/ws.ts +0 -648
  128. package/src/wss.ts +0 -241
@@ -1,343 +0,0 @@
1
- import type {
2
- NativeBorderStyleKeyword,
3
- NativeBorderSideValue,
4
- NativeBorderValue,
5
- NativeColorValue,
6
- NativePropValue,
7
- } from './native-types';
8
- import { formatFloat } from './native-units';
9
- import {
10
- parseCssColor,
11
- resolveStyleCurrentColor,
12
- toComposeColorLiteral,
13
- toSwiftColorLiteral,
14
- } from './native-color';
15
-
16
- function parseBorderValue(
17
- value: NativePropValue | undefined,
18
- unitParser: (value: NativePropValue | undefined) => string | undefined,
19
- currentColor: NativeColorValue,
20
- ): { width?: string; color?: NativeColorValue; style?: NativeBorderStyleKeyword } | undefined {
21
- if (typeof value !== 'string') return undefined;
22
-
23
- const widthMatch = value.match(/-?\d+(?:\.\d+)?(?:px|dp|pt)?/i);
24
- const width = widthMatch ? unitParser(widthMatch[0]) : undefined;
25
- const color = parseCssColor(value, currentColor);
26
- const style = parseBorderStyleKeyword(value);
27
-
28
- if (!width && !color && !style) {
29
- return undefined;
30
- }
31
-
32
- return { width, color, style };
33
- }
34
-
35
- function parseBorderStyleKeyword(value: NativePropValue | undefined): NativeBorderStyleKeyword | undefined {
36
- if (typeof value !== 'string') {
37
- return undefined;
38
- }
39
-
40
- const normalized = value.trim().toLowerCase();
41
- if (!normalized) {
42
- return undefined;
43
- }
44
-
45
- if (/(^|\s)(none|hidden)(\s|$)/.test(normalized)) {
46
- return 'none';
47
- }
48
-
49
- if (/(^|\s)solid(\s|$)/.test(normalized)) {
50
- return 'solid';
51
- }
52
-
53
- if (/(^|\s)dashed(\s|$)/.test(normalized)) {
54
- return 'dashed';
55
- }
56
-
57
- if (/(^|\s)dotted(\s|$)/.test(normalized)) {
58
- return 'dotted';
59
- }
60
-
61
- if (/(^|\s)(double|groove|ridge|inset|outset)(\s|$)/.test(normalized)) {
62
- return 'unsupported';
63
- }
64
-
65
- return undefined;
66
- }
67
-
68
- function areNativeColorsEqual(left: NativeColorValue | undefined, right: NativeColorValue | undefined): boolean {
69
- if (!left || !right) {
70
- return left === right;
71
- }
72
-
73
- return left.red === right.red
74
- && left.green === right.green
75
- && left.blue === right.blue
76
- && Math.abs(left.alpha - right.alpha) < 0.0001;
77
- }
78
-
79
- function parseNativeBorderNumericWidth(width: string): number {
80
- const parsedWidth = Number.parseFloat(width);
81
- return Number.isFinite(parsedWidth) && parsedWidth > 0 ? parsedWidth : 1;
82
- }
83
-
84
- function buildComposeBorderLineCap(style: NativeBorderStyleKeyword | undefined): string {
85
- return style === 'dotted'
86
- ? 'androidx.compose.ui.graphics.StrokeCap.Round'
87
- : 'androidx.compose.ui.graphics.StrokeCap.Square';
88
- }
89
-
90
- function buildComposeBorderJoinInset(side: NativeBorderSideValue | undefined, strokeVariable: string): string {
91
- return side ? `${side.width}.toPx() / 2f` : `${strokeVariable} / 2f`;
92
- }
93
-
94
- function buildComposeBorderDashPattern(style: NativeBorderStyleKeyword | undefined, strokeVariable: string): string | undefined {
95
- if (style === 'dotted') {
96
- return `floatArrayOf(${strokeVariable}, ${strokeVariable} * 1.5f)`;
97
- }
98
-
99
- if (style === 'dashed') {
100
- return `floatArrayOf(${strokeVariable} * 3f, ${strokeVariable} * 2f)`;
101
- }
102
-
103
- return undefined;
104
- }
105
-
106
- function buildSwiftBorderDashPattern(style: NativeBorderStyleKeyword | undefined, widthValue: number): string | undefined {
107
- if (style === 'dotted') {
108
- return `[${formatFloat(widthValue)}, ${formatFloat(widthValue * 1.5)}]`;
109
- }
110
-
111
- if (style === 'dashed') {
112
- return `[${formatFloat(widthValue * 3)}, ${formatFloat(widthValue * 2)}]`;
113
- }
114
-
115
- return undefined;
116
- }
117
-
118
- function buildSwiftBorderLineCap(style: NativeBorderStyleKeyword | undefined): string {
119
- return style === 'dotted' ? '.round' : '.square';
120
- }
121
-
122
- function buildSwiftBorderJoinInset(side: NativeBorderSideValue | undefined, widthValue: number): string {
123
- return side ? `CGFloat(${side.width}) / 2` : `CGFloat(${formatFloat(widthValue)}) / 2`;
124
- }
125
-
126
- export function resolveNativeBorder(
127
- style: Record<string, NativePropValue>,
128
- unitParser: (value: NativePropValue | undefined) => string | undefined,
129
- ): NativeBorderValue | undefined {
130
- const currentColor = resolveStyleCurrentColor(style);
131
- const shorthandBorder = parseBorderValue(style.border, unitParser, currentColor);
132
- const globalWidth = unitParser(style.borderWidth) ?? shorthandBorder?.width;
133
- const globalColor = parseCssColor(style.borderColor, currentColor) ?? shorthandBorder?.color;
134
- const globalStyle = parseBorderStyleKeyword(style.borderStyle) ?? shorthandBorder?.style;
135
- const sideKeys = [
136
- { shorthand: 'borderTop', width: 'borderTopWidth', color: 'borderTopColor', style: 'borderTopStyle' },
137
- { shorthand: 'borderRight', width: 'borderRightWidth', color: 'borderRightColor', style: 'borderRightStyle' },
138
- { shorthand: 'borderBottom', width: 'borderBottomWidth', color: 'borderBottomColor', style: 'borderBottomStyle' },
139
- { shorthand: 'borderLeft', width: 'borderLeftWidth', color: 'borderLeftColor', style: 'borderLeftStyle' },
140
- ] as const;
141
- const hasSideSpecificBorder = sideKeys.some((keys) => style[keys.shorthand] !== undefined || style[keys.width] !== undefined || style[keys.color] !== undefined || style[keys.style] !== undefined);
142
-
143
- const isRenderableBorder = (
144
- width: string | undefined,
145
- color: NativeColorValue | undefined,
146
- borderStyle: NativeBorderStyleKeyword | undefined,
147
- ): boolean => Boolean(width && color && borderStyle !== 'none' && borderStyle !== 'unsupported');
148
-
149
- if (!hasSideSpecificBorder) {
150
- return isRenderableBorder(globalWidth, globalColor, globalStyle)
151
- ? { width: globalWidth, color: globalColor, style: globalStyle }
152
- : undefined;
153
- }
154
-
155
- const resolvedSides = sideKeys.map((keys) => {
156
- const sideBorder = parseBorderValue(style[keys.shorthand], unitParser, currentColor);
157
- return {
158
- width: unitParser(style[keys.width]) ?? sideBorder?.width ?? globalWidth,
159
- color: parseCssColor(style[keys.color], currentColor) ?? sideBorder?.color ?? globalColor,
160
- style: parseBorderStyleKeyword(style[keys.style]) ?? sideBorder?.style ?? globalStyle,
161
- };
162
- });
163
-
164
- const [firstSide] = resolvedSides;
165
- if (
166
- firstSide
167
- && isRenderableBorder(firstSide.width, firstSide.color, firstSide.style)
168
- && resolvedSides.every((side) => side.width === firstSide.width && areNativeColorsEqual(side.color, firstSide.color) && side.style === firstSide.style)
169
- ) {
170
- return {
171
- width: firstSide.width,
172
- color: firstSide.color,
173
- style: firstSide.style,
174
- };
175
- }
176
-
177
- const renderedBorder: NativeBorderValue = {};
178
- const sideNames = ['top', 'right', 'bottom', 'left'] as const;
179
-
180
- resolvedSides.forEach((side, index) => {
181
- if (isRenderableBorder(side.width, side.color, side.style)) {
182
- renderedBorder[sideNames[index]] = {
183
- width: side.width!,
184
- color: side.color!,
185
- style: side.style,
186
- };
187
- }
188
- });
189
-
190
- return renderedBorder.top || renderedBorder.right || renderedBorder.bottom || renderedBorder.left
191
- ? renderedBorder
192
- : undefined;
193
- }
194
-
195
- function hasNativeSideBorders(border: NativeBorderValue | undefined): boolean {
196
- return Boolean(border?.top || border?.right || border?.bottom || border?.left);
197
- }
198
-
199
- export function buildComposeSideBorderModifier(border: NativeBorderValue): string | undefined {
200
- if (!hasNativeSideBorders(border)) {
201
- return undefined;
202
- }
203
-
204
- const commands: string[] = [];
205
- if (border.top) {
206
- commands.push(`val topStroke = ${border.top.width}.toPx()`);
207
- const topStartX = buildComposeBorderJoinInset(border.left, 'topStroke');
208
- const topEndX = `size.width - (${buildComposeBorderJoinInset(border.right, 'topStroke')})`;
209
- const topCap = buildComposeBorderLineCap(border.top.style);
210
- const topDashPattern = buildComposeBorderDashPattern(border.top.style, 'topStroke');
211
- const topPathEffect = topDashPattern
212
- ? `, pathEffect = androidx.compose.ui.graphics.PathEffect.dashPathEffect(${topDashPattern})`
213
- : '';
214
- commands.push(`drawLine(color = ${toComposeColorLiteral(border.top.color)}, start = androidx.compose.ui.geometry.Offset(${topStartX}, topStroke / 2f), end = androidx.compose.ui.geometry.Offset(${topEndX}, topStroke / 2f), strokeWidth = topStroke, cap = ${topCap}${topPathEffect})`);
215
- }
216
- if (border.right) {
217
- commands.push(`val rightStroke = ${border.right.width}.toPx()`);
218
- const rightStartY = buildComposeBorderJoinInset(border.top, 'rightStroke');
219
- const rightEndY = `size.height - (${buildComposeBorderJoinInset(border.bottom, 'rightStroke')})`;
220
- const rightCap = buildComposeBorderLineCap(border.right.style);
221
- const rightDashPattern = buildComposeBorderDashPattern(border.right.style, 'rightStroke');
222
- const rightPathEffect = rightDashPattern
223
- ? `, pathEffect = androidx.compose.ui.graphics.PathEffect.dashPathEffect(${rightDashPattern})`
224
- : '';
225
- commands.push(`drawLine(color = ${toComposeColorLiteral(border.right.color)}, start = androidx.compose.ui.geometry.Offset(size.width - (rightStroke / 2f), ${rightStartY}), end = androidx.compose.ui.geometry.Offset(size.width - (rightStroke / 2f), ${rightEndY}), strokeWidth = rightStroke, cap = ${rightCap}${rightPathEffect})`);
226
- }
227
- if (border.bottom) {
228
- commands.push(`val bottomStroke = ${border.bottom.width}.toPx()`);
229
- const bottomStartX = buildComposeBorderJoinInset(border.left, 'bottomStroke');
230
- const bottomEndX = `size.width - (${buildComposeBorderJoinInset(border.right, 'bottomStroke')})`;
231
- const bottomCap = buildComposeBorderLineCap(border.bottom.style);
232
- const bottomDashPattern = buildComposeBorderDashPattern(border.bottom.style, 'bottomStroke');
233
- const bottomPathEffect = bottomDashPattern
234
- ? `, pathEffect = androidx.compose.ui.graphics.PathEffect.dashPathEffect(${bottomDashPattern})`
235
- : '';
236
- commands.push(`drawLine(color = ${toComposeColorLiteral(border.bottom.color)}, start = androidx.compose.ui.geometry.Offset(${bottomStartX}, size.height - (bottomStroke / 2f)), end = androidx.compose.ui.geometry.Offset(${bottomEndX}, size.height - (bottomStroke / 2f)), strokeWidth = bottomStroke, cap = ${bottomCap}${bottomPathEffect})`);
237
- }
238
- if (border.left) {
239
- commands.push(`val leftStroke = ${border.left.width}.toPx()`);
240
- const leftStartY = buildComposeBorderJoinInset(border.top, 'leftStroke');
241
- const leftEndY = `size.height - (${buildComposeBorderJoinInset(border.bottom, 'leftStroke')})`;
242
- const leftCap = buildComposeBorderLineCap(border.left.style);
243
- const leftDashPattern = buildComposeBorderDashPattern(border.left.style, 'leftStroke');
244
- const leftPathEffect = leftDashPattern
245
- ? `, pathEffect = androidx.compose.ui.graphics.PathEffect.dashPathEffect(${leftDashPattern})`
246
- : '';
247
- commands.push(`drawLine(color = ${toComposeColorLiteral(border.left.color)}, start = androidx.compose.ui.geometry.Offset(leftStroke / 2f, ${leftStartY}), end = androidx.compose.ui.geometry.Offset(leftStroke / 2f, ${leftEndY}), strokeWidth = leftStroke, cap = ${leftCap}${leftPathEffect})`);
248
- }
249
-
250
- return commands.length > 0 ? `drawBehind { ${commands.join('; ')} }` : undefined;
251
- }
252
-
253
- export function buildComposeUniformStyledBorderModifier(border: NativeBorderValue, radius?: string): string | undefined {
254
- if (!border.width || !border.color || (border.style !== 'dashed' && border.style !== 'dotted')) {
255
- return undefined;
256
- }
257
-
258
- const dashPattern = buildComposeBorderDashPattern(border.style, 'strokeWidth');
259
- if (!dashPattern) {
260
- return undefined;
261
- }
262
-
263
- if (radius) {
264
- return `drawBehind { val strokeWidth = ${border.width}.toPx(); val dashPattern = ${dashPattern}; val borderRadius = ${radius}.toPx(); drawRoundRect(color = ${toComposeColorLiteral(border.color)}, cornerRadius = androidx.compose.ui.geometry.CornerRadius(borderRadius, borderRadius), style = androidx.compose.ui.graphics.drawscope.Stroke(width = strokeWidth, pathEffect = androidx.compose.ui.graphics.PathEffect.dashPathEffect(dashPattern))) }`;
265
- }
266
-
267
- return `drawBehind { val strokeWidth = ${border.width}.toPx(); val dashPattern = ${dashPattern}; drawRect(color = ${toComposeColorLiteral(border.color)}, style = androidx.compose.ui.graphics.drawscope.Stroke(width = strokeWidth, pathEffect = androidx.compose.ui.graphics.PathEffect.dashPathEffect(dashPattern))) }`;
268
- }
269
-
270
- export function buildSwiftSideBorderOverlay(border: NativeBorderValue, radius?: string): string | undefined {
271
- if (!hasNativeSideBorders(border)) {
272
- return undefined;
273
- }
274
-
275
- const sideEntries = [
276
- ['top', border.top],
277
- ['right', border.right],
278
- ['bottom', border.bottom],
279
- ['left', border.left],
280
- ].filter((entry): entry is ['top' | 'right' | 'bottom' | 'left', NativeBorderSideValue] => Boolean(entry[1]));
281
-
282
- const hasStyledSides = sideEntries.some(([, side]) => side.style === 'dashed' || side.style === 'dotted');
283
- if (hasStyledSides) {
284
- const overlays = sideEntries.map(([sideName, side]) => {
285
- const widthValue = parseNativeBorderNumericWidth(side.width);
286
- const dashPattern = buildSwiftBorderDashPattern(side.style, widthValue);
287
- const lineCap = buildSwiftBorderLineCap(side.style);
288
- const strokeStyle = dashPattern
289
- ? `StrokeStyle(lineWidth: ${side.width}, lineCap: ${lineCap}, dash: ${dashPattern})`
290
- : `StrokeStyle(lineWidth: ${side.width}, lineCap: ${lineCap})`;
291
-
292
- switch (sideName) {
293
- case 'top':
294
- return `Path { path in path.move(to: CGPoint(x: ${buildSwiftBorderJoinInset(border.left, widthValue)}, y: CGFloat(${side.width}) / 2)); path.addLine(to: CGPoint(x: proxy.size.width - (${buildSwiftBorderJoinInset(border.right, widthValue)}), y: CGFloat(${side.width}) / 2)) }.stroke(${toSwiftColorLiteral(side.color)}, style: ${strokeStyle})`;
295
- case 'right':
296
- return `Path { path in path.move(to: CGPoint(x: proxy.size.width - (CGFloat(${side.width}) / 2), y: ${buildSwiftBorderJoinInset(border.top, widthValue)})); path.addLine(to: CGPoint(x: proxy.size.width - (CGFloat(${side.width}) / 2), y: proxy.size.height - (${buildSwiftBorderJoinInset(border.bottom, widthValue)}))) }.stroke(${toSwiftColorLiteral(side.color)}, style: ${strokeStyle})`;
297
- case 'bottom':
298
- return `Path { path in path.move(to: CGPoint(x: ${buildSwiftBorderJoinInset(border.left, widthValue)}, y: proxy.size.height - (CGFloat(${side.width}) / 2))); path.addLine(to: CGPoint(x: proxy.size.width - (${buildSwiftBorderJoinInset(border.right, widthValue)}), y: proxy.size.height - (CGFloat(${side.width}) / 2))) }.stroke(${toSwiftColorLiteral(side.color)}, style: ${strokeStyle})`;
299
- case 'left':
300
- default:
301
- return `Path { path in path.move(to: CGPoint(x: CGFloat(${side.width}) / 2, y: ${buildSwiftBorderJoinInset(border.top, widthValue)})); path.addLine(to: CGPoint(x: CGFloat(${side.width}) / 2, y: proxy.size.height - (${buildSwiftBorderJoinInset(border.bottom, widthValue)}))) }.stroke(${toSwiftColorLiteral(side.color)}, style: ${strokeStyle})`;
302
- }
303
- });
304
-
305
- const clipModifier = radius ? `.clipShape(RoundedRectangle(cornerRadius: ${radius}))` : '';
306
- return `.overlay { GeometryReader { proxy in ZStack { ${overlays.join('; ')} } }${clipModifier} }`;
307
- }
308
-
309
- const overlays: string[] = [];
310
- if (border.top) {
311
- overlays.push(`Rectangle().fill(${toSwiftColorLiteral(border.top.color)}).frame(height: ${border.top.width}).frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)`);
312
- }
313
- if (border.right) {
314
- overlays.push(`Rectangle().fill(${toSwiftColorLiteral(border.right.color)}).frame(width: ${border.right.width}).frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .trailing)`);
315
- }
316
- if (border.bottom) {
317
- overlays.push(`Rectangle().fill(${toSwiftColorLiteral(border.bottom.color)}).frame(height: ${border.bottom.width}).frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)`);
318
- }
319
- if (border.left) {
320
- overlays.push(`Rectangle().fill(${toSwiftColorLiteral(border.left.color)}).frame(width: ${border.left.width}).frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)`);
321
- }
322
-
323
- if (overlays.length === 0) {
324
- return undefined;
325
- }
326
-
327
- const clipModifier = radius ? `.clipShape(RoundedRectangle(cornerRadius: ${radius}))` : '';
328
- return `.overlay { ZStack { ${overlays.join('; ')} }${clipModifier} }`;
329
- }
330
-
331
- export function buildSwiftUniformStyledBorderModifier(border: NativeBorderValue, radius?: string): string | undefined {
332
- if (!border.width || !border.color || (border.style !== 'dashed' && border.style !== 'dotted')) {
333
- return undefined;
334
- }
335
-
336
- const widthValue = parseNativeBorderNumericWidth(border.width);
337
- const dashPattern = buildSwiftBorderDashPattern(border.style, widthValue);
338
- if (!dashPattern) {
339
- return undefined;
340
- }
341
- const shape = radius ? `RoundedRectangle(cornerRadius: ${radius})` : 'Rectangle()';
342
- return `.overlay(${shape}.stroke(${toSwiftColorLiteral(border.color)}, style: StrokeStyle(lineWidth: ${border.width}, dash: ${dashPattern})))`;
343
- }
@@ -1,260 +0,0 @@
1
- import type { NativeIntrinsicSizeSpec, NativeVectorSpec } from './native-types';
2
- import { toComposeColorLiteral, toSwiftColorLiteral } from './native-color';
3
- import { formatFloat } from './native-units';
4
-
5
- function indent(level: number): string {
6
- return ' '.repeat(level);
7
- }
8
-
9
- export function prependComposeModifierCall(modifier: string, call: string): string {
10
- return modifier === 'Modifier'
11
- ? `Modifier.${call}`
12
- : modifier.replace(/^Modifier/, `Modifier.${call}`);
13
- }
14
-
15
- export function appendComposeModifierCall(modifier: string, call: string): string {
16
- return modifier === 'Modifier'
17
- ? `Modifier.${call}`
18
- : `${modifier}.${call}`;
19
- }
20
-
21
- function buildComposeIntrinsicSurfaceModifier(
22
- modifier: string,
23
- spec: NativeIntrinsicSizeSpec,
24
- widthDefined: boolean,
25
- heightDefined: boolean,
26
- ): string {
27
- if (!widthDefined && !heightDefined) {
28
- return prependComposeModifierCall(modifier, `size(width = ${formatFloat(spec.intrinsicWidth)}.dp, height = ${formatFloat(spec.intrinsicHeight)}.dp)`);
29
- }
30
-
31
- let resolvedModifier = modifier;
32
- if (!widthDefined) {
33
- resolvedModifier = prependComposeModifierCall(resolvedModifier, `width(${formatFloat(spec.intrinsicWidth)}.dp)`);
34
- }
35
- if (!heightDefined) {
36
- resolvedModifier = prependComposeModifierCall(resolvedModifier, `height(${formatFloat(spec.intrinsicHeight)}.dp)`);
37
- }
38
-
39
- return resolvedModifier;
40
- }
41
-
42
- function buildComposeDrawingCanvasLines(
43
- spec: NativeVectorSpec,
44
- level: number,
45
- modifier: string,
46
- ): string[] {
47
- const viewport = spec.viewport;
48
- const lines = [`${indent(level)}androidx.compose.foundation.Canvas(modifier = ${modifier}) {`];
49
- lines.push(`${indent(level + 1)}val viewportWidth = ${formatFloat(viewport.width)}f`);
50
- lines.push(`${indent(level + 1)}val viewportHeight = ${formatFloat(viewport.height)}f`);
51
- lines.push(`${indent(level + 1)}val scaleX = size.width / viewportWidth`);
52
- lines.push(`${indent(level + 1)}val scaleY = size.height / viewportHeight`);
53
- lines.push(`${indent(level + 1)}val strokeScale = (scaleX + scaleY) / 2f`);
54
-
55
- let pathIndex = 0;
56
- for (const shape of spec.shapes) {
57
- if (shape.kind === 'circle') {
58
- const radiusExpression = `${formatFloat(shape.r)}f * kotlin.math.min(scaleX, scaleY)`;
59
- const centerExpression = `androidx.compose.ui.geometry.Offset(${formatFloat(shape.cx - viewport.minX)}f * scaleX, ${formatFloat(shape.cy - viewport.minY)}f * scaleY)`;
60
- if (shape.fill) {
61
- lines.push(`${indent(level + 1)}drawCircle(color = ${toComposeColorLiteral(shape.fill)}, radius = ${radiusExpression}, center = ${centerExpression})`);
62
- }
63
- if (shape.stroke) {
64
- lines.push(`${indent(level + 1)}drawCircle(color = ${toComposeColorLiteral(shape.stroke)}, radius = ${radiusExpression}, center = ${centerExpression}, style = androidx.compose.ui.graphics.drawscope.Stroke(width = ${(shape.strokeWidth ?? 1).toString()}f * strokeScale))`);
65
- }
66
- continue;
67
- }
68
-
69
- if (shape.kind === 'ellipse') {
70
- const topLeftExpression = `androidx.compose.ui.geometry.Offset(${formatFloat(shape.cx - shape.rx - viewport.minX)}f * scaleX, ${formatFloat(shape.cy - shape.ry - viewport.minY)}f * scaleY)`;
71
- const sizeExpression = `androidx.compose.ui.geometry.Size(${formatFloat(shape.rx * 2)}f * scaleX, ${formatFloat(shape.ry * 2)}f * scaleY)`;
72
- if (shape.fill) {
73
- lines.push(`${indent(level + 1)}drawOval(color = ${toComposeColorLiteral(shape.fill)}, topLeft = ${topLeftExpression}, size = ${sizeExpression})`);
74
- }
75
- if (shape.stroke) {
76
- lines.push(`${indent(level + 1)}drawOval(color = ${toComposeColorLiteral(shape.stroke)}, topLeft = ${topLeftExpression}, size = ${sizeExpression}, style = androidx.compose.ui.graphics.drawscope.Stroke(width = ${(shape.strokeWidth ?? 1).toString()}f * strokeScale))`);
77
- }
78
- continue;
79
- }
80
-
81
- if (shape.kind === 'rect') {
82
- const topLeftExpression = `androidx.compose.ui.geometry.Offset(${formatFloat(shape.x - viewport.minX)}f * scaleX, ${formatFloat(shape.y - viewport.minY)}f * scaleY)`;
83
- const sizeExpression = `androidx.compose.ui.geometry.Size(${formatFloat(shape.width)}f * scaleX, ${formatFloat(shape.height)}f * scaleY)`;
84
- const hasRadius = (shape.rx ?? 0) > 0 || (shape.ry ?? shape.rx ?? 0) > 0;
85
- const radiusExpression = `androidx.compose.ui.geometry.CornerRadius(${formatFloat(shape.rx ?? 0)}f * scaleX, ${formatFloat(shape.ry ?? shape.rx ?? 0)}f * scaleY)`;
86
- if (shape.fill) {
87
- lines.push(`${indent(level + 1)}${hasRadius
88
- ? `drawRoundRect(color = ${toComposeColorLiteral(shape.fill)}, topLeft = ${topLeftExpression}, size = ${sizeExpression}, cornerRadius = ${radiusExpression})`
89
- : `drawRect(color = ${toComposeColorLiteral(shape.fill)}, topLeft = ${topLeftExpression}, size = ${sizeExpression})`}`);
90
- }
91
- if (shape.stroke) {
92
- lines.push(`${indent(level + 1)}${hasRadius
93
- ? `drawRoundRect(color = ${toComposeColorLiteral(shape.stroke)}, topLeft = ${topLeftExpression}, size = ${sizeExpression}, cornerRadius = ${radiusExpression}, style = androidx.compose.ui.graphics.drawscope.Stroke(width = ${(shape.strokeWidth ?? 1).toString()}f * strokeScale))`
94
- : `drawRect(color = ${toComposeColorLiteral(shape.stroke)}, topLeft = ${topLeftExpression}, size = ${sizeExpression}, style = androidx.compose.ui.graphics.drawscope.Stroke(width = ${(shape.strokeWidth ?? 1).toString()}f * strokeScale))`}`);
95
- }
96
- continue;
97
- }
98
-
99
- const pathName = `vectorPath${pathIndex++}`;
100
- lines.push(`${indent(level + 1)}val ${pathName} = androidx.compose.ui.graphics.Path().apply {`);
101
- for (const command of shape.commands) {
102
- if (command.kind === 'close') {
103
- lines.push(`${indent(level + 2)}close()`);
104
- continue;
105
- }
106
-
107
- if (command.kind === 'cubicTo') {
108
- lines.push(`${indent(level + 2)}cubicTo(${formatFloat(command.control1X - viewport.minX)}f * scaleX, ${formatFloat(command.control1Y - viewport.minY)}f * scaleY, ${formatFloat(command.control2X - viewport.minX)}f * scaleX, ${formatFloat(command.control2Y - viewport.minY)}f * scaleY, ${formatFloat(command.x - viewport.minX)}f * scaleX, ${formatFloat(command.y - viewport.minY)}f * scaleY)`);
109
- continue;
110
- }
111
-
112
- lines.push(`${indent(level + 2)}${command.kind}(${formatFloat(command.x - viewport.minX)}f * scaleX, ${formatFloat(command.y - viewport.minY)}f * scaleY)`);
113
- }
114
- lines.push(`${indent(level + 1)}}`);
115
- if (shape.fill) {
116
- lines.push(`${indent(level + 1)}drawPath(path = ${pathName}, color = ${toComposeColorLiteral(shape.fill)})`);
117
- }
118
- if (shape.stroke) {
119
- lines.push(`${indent(level + 1)}drawPath(path = ${pathName}, color = ${toComposeColorLiteral(shape.stroke)}, style = androidx.compose.ui.graphics.drawscope.Stroke(width = ${(shape.strokeWidth ?? 1).toString()}f * strokeScale))`);
120
- }
121
- }
122
-
123
- lines.push(`${indent(level)}}`);
124
- return lines;
125
- }
126
-
127
- export function buildComposeCanvasSurfaceLines(
128
- spec: NativeIntrinsicSizeSpec,
129
- drawingSpec: NativeVectorSpec | undefined,
130
- level: number,
131
- modifier: string,
132
- widthDefined: boolean,
133
- heightDefined: boolean,
134
- ): string[] {
135
- const canvasModifier = buildComposeIntrinsicSurfaceModifier(modifier, spec, widthDefined, heightDefined);
136
- return drawingSpec
137
- ? buildComposeDrawingCanvasLines(drawingSpec, level, canvasModifier)
138
- : [
139
- `${indent(level)}androidx.compose.foundation.Canvas(modifier = ${canvasModifier}) {`,
140
- `${indent(level)}}`,
141
- ];
142
- }
143
-
144
- export function buildComposeVectorCanvasLines(
145
- spec: NativeVectorSpec,
146
- level: number,
147
- modifier: string,
148
- widthDefined: boolean,
149
- heightDefined: boolean,
150
- ): string[] {
151
- const vectorModifier = buildComposeIntrinsicSurfaceModifier(modifier, spec, widthDefined, heightDefined);
152
- return buildComposeDrawingCanvasLines(spec, level, vectorModifier);
153
- }
154
-
155
- function buildSwiftIntrinsicSurfaceModifiers(
156
- baseModifiers: string[],
157
- spec: NativeIntrinsicSizeSpec,
158
- widthDefined: boolean,
159
- heightDefined: boolean,
160
- ): string[] {
161
- const modifiers = [...baseModifiers];
162
- const frameArgs: string[] = [];
163
- if (!widthDefined) {
164
- frameArgs.push(`width: ${formatFloat(spec.intrinsicWidth)}`);
165
- }
166
- if (!heightDefined) {
167
- frameArgs.push(`height: ${formatFloat(spec.intrinsicHeight)}`);
168
- }
169
- if (frameArgs.length > 0) {
170
- modifiers.push(`.frame(${frameArgs.join(', ')})`);
171
- }
172
- return modifiers;
173
- }
174
-
175
- function buildSwiftDrawingCanvasLines(
176
- spec: NativeVectorSpec,
177
- level: number,
178
- ): string[] {
179
- const viewport = spec.viewport;
180
- const lines = [`${indent(level)}Canvas { context, size in`];
181
- lines.push(`${indent(level + 1)}let viewportWidth = CGFloat(${formatFloat(viewport.width)})`);
182
- lines.push(`${indent(level + 1)}let viewportHeight = CGFloat(${formatFloat(viewport.height)})`);
183
- lines.push(`${indent(level + 1)}let scaleX = size.width / viewportWidth`);
184
- lines.push(`${indent(level + 1)}let scaleY = size.height / viewportHeight`);
185
- lines.push(`${indent(level + 1)}let strokeScale = (scaleX + scaleY) / 2`);
186
-
187
- let pathIndex = 0;
188
- for (const shape of spec.shapes) {
189
- const pathName = `vectorPath${pathIndex++}`;
190
- lines.push(`${indent(level + 1)}var ${pathName} = Path()`);
191
-
192
- if (shape.kind === 'circle') {
193
- lines.push(`${indent(level + 1)}${pathName}.addEllipse(in: CGRect(x: CGFloat(${formatFloat(shape.cx - shape.r - viewport.minX)}) * scaleX, y: CGFloat(${formatFloat(shape.cy - shape.r - viewport.minY)}) * scaleY, width: CGFloat(${formatFloat(shape.r * 2)}) * scaleX, height: CGFloat(${formatFloat(shape.r * 2)}) * scaleY))`);
194
- } else if (shape.kind === 'ellipse') {
195
- lines.push(`${indent(level + 1)}${pathName}.addEllipse(in: CGRect(x: CGFloat(${formatFloat(shape.cx - shape.rx - viewport.minX)}) * scaleX, y: CGFloat(${formatFloat(shape.cy - shape.ry - viewport.minY)}) * scaleY, width: CGFloat(${formatFloat(shape.rx * 2)}) * scaleX, height: CGFloat(${formatFloat(shape.ry * 2)}) * scaleY))`);
196
- } else if (shape.kind === 'rect') {
197
- if ((shape.rx ?? 0) > 0 || (shape.ry ?? shape.rx ?? 0) > 0) {
198
- lines.push(`${indent(level + 1)}${pathName}.addRoundedRect(in: CGRect(x: CGFloat(${formatFloat(shape.x - viewport.minX)}) * scaleX, y: CGFloat(${formatFloat(shape.y - viewport.minY)}) * scaleY, width: CGFloat(${formatFloat(shape.width)}) * scaleX, height: CGFloat(${formatFloat(shape.height)}) * scaleY), cornerSize: CGSize(width: CGFloat(${formatFloat(shape.rx ?? 0)}) * scaleX, height: CGFloat(${formatFloat(shape.ry ?? shape.rx ?? 0)}) * scaleY))`);
199
- } else {
200
- lines.push(`${indent(level + 1)}${pathName}.addRect(CGRect(x: CGFloat(${formatFloat(shape.x - viewport.minX)}) * scaleX, y: CGFloat(${formatFloat(shape.y - viewport.minY)}) * scaleY, width: CGFloat(${formatFloat(shape.width)}) * scaleX, height: CGFloat(${formatFloat(shape.height)}) * scaleY))`);
201
- }
202
- } else {
203
- for (const command of shape.commands) {
204
- if (command.kind === 'close') {
205
- lines.push(`${indent(level + 1)}${pathName}.closeSubpath()`);
206
- continue;
207
- }
208
-
209
- if (command.kind === 'cubicTo') {
210
- lines.push(`${indent(level + 1)}${pathName}.addCurve(to: CGPoint(x: CGFloat(${formatFloat(command.x - viewport.minX)}) * scaleX, y: CGFloat(${formatFloat(command.y - viewport.minY)}) * scaleY), control1: CGPoint(x: CGFloat(${formatFloat(command.control1X - viewport.minX)}) * scaleX, y: CGFloat(${formatFloat(command.control1Y - viewport.minY)}) * scaleY), control2: CGPoint(x: CGFloat(${formatFloat(command.control2X - viewport.minX)}) * scaleX, y: CGFloat(${formatFloat(command.control2Y - viewport.minY)}) * scaleY))`);
211
- continue;
212
- }
213
-
214
- lines.push(`${indent(level + 1)}${pathName}.${command.kind === 'moveTo' ? 'move' : 'addLine'}(to: CGPoint(x: CGFloat(${formatFloat(command.x - viewport.minX)}) * scaleX, y: CGFloat(${formatFloat(command.y - viewport.minY)}) * scaleY))`);
215
- }
216
- }
217
-
218
- if (shape.fill) {
219
- lines.push(`${indent(level + 1)}context.fill(${pathName}, with: .color(${toSwiftColorLiteral(shape.fill)}))`);
220
- }
221
- if (shape.stroke) {
222
- lines.push(`${indent(level + 1)}context.stroke(${pathName}, with: .color(${toSwiftColorLiteral(shape.stroke)}), style: StrokeStyle(lineWidth: CGFloat(${formatFloat(shape.strokeWidth ?? 1)}) * strokeScale))`);
223
- }
224
- }
225
-
226
- lines.push(`${indent(level)}}`);
227
- return lines;
228
- }
229
-
230
- export function buildSwiftCanvasSurfaceLines(
231
- spec: NativeIntrinsicSizeSpec,
232
- drawingSpec: NativeVectorSpec | undefined,
233
- level: number,
234
- baseModifiers: string[],
235
- widthDefined: boolean,
236
- heightDefined: boolean,
237
- ): { lines: string[]; modifiers: string[] } {
238
- return {
239
- lines: drawingSpec
240
- ? buildSwiftDrawingCanvasLines(drawingSpec, level)
241
- : [
242
- `${indent(level)}Canvas { _, _ in`,
243
- `${indent(level)}}`,
244
- ],
245
- modifiers: buildSwiftIntrinsicSurfaceModifiers(baseModifiers, spec, widthDefined, heightDefined),
246
- };
247
- }
248
-
249
- export function buildSwiftVectorCanvasLines(
250
- spec: NativeVectorSpec,
251
- level: number,
252
- baseModifiers: string[],
253
- widthDefined: boolean,
254
- heightDefined: boolean,
255
- ): { lines: string[]; modifiers: string[] } {
256
- return {
257
- lines: buildSwiftDrawingCanvasLines(spec, level),
258
- modifiers: buildSwiftIntrinsicSurfaceModifiers(baseModifiers, spec, widthDefined, heightDefined),
259
- };
260
- }