elit 3.6.9 → 3.7.0

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 (50) hide show
  1. package/Cargo.lock +380 -449
  2. package/Cargo.toml +3 -3
  3. package/dist/build.cjs +1 -1
  4. package/dist/build.d.ts +1 -1
  5. package/dist/build.js +14 -10
  6. package/dist/build.mjs +1 -1
  7. package/dist/cli.cjs +1607 -6431
  8. package/dist/cli.d.ts +10 -3
  9. package/dist/cli.mjs +1611 -6435
  10. package/dist/config.cjs +7 -2
  11. package/dist/config.d.ts +4 -4
  12. package/dist/config.js +20 -11
  13. package/dist/config.mjs +7 -2
  14. package/dist/{contracts-D7KIS-TK.d.ts → contracts-Av9yuqDM.d.ts} +11 -4
  15. package/dist/dev-build.d.ts +2 -1
  16. package/dist/dev-build.js +13 -9
  17. package/dist/https.cjs +7 -2
  18. package/dist/https.js +7 -2
  19. package/dist/https.mjs +7 -2
  20. package/dist/index.cjs +340 -52
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +340 -52
  23. package/dist/index.mjs +340 -52
  24. package/dist/native.cjs +340 -52
  25. package/dist/native.js +340 -52
  26. package/dist/native.mjs +340 -52
  27. package/dist/pm.cjs +7 -2
  28. package/dist/pm.d.ts +3 -3
  29. package/dist/pm.js +20 -11
  30. package/dist/pm.mjs +7 -2
  31. package/dist/preview-build.d.ts +2 -2
  32. package/dist/preview-build.js +13 -9
  33. package/dist/server.cjs +861 -5998
  34. package/dist/server.d.ts +3 -2
  35. package/dist/server.js +12178 -4468
  36. package/dist/server.mjs +861 -5998
  37. package/dist/smtp-server.js +514 -44
  38. package/dist/test-runtime.cjs +20 -0
  39. package/dist/test-runtime.js +33 -9
  40. package/dist/test-runtime.mjs +20 -0
  41. package/dist/test.cjs +27 -2
  42. package/dist/test.js +40 -11
  43. package/dist/test.mjs +27 -2
  44. package/dist/{types-BayMVo_k.d.ts → types-CLGrLRTl.d.ts} +8 -2
  45. package/dist/{types-fiLday0L.d.ts → types-CYEpzeEi.d.ts} +10 -0
  46. package/dist/types.d.ts +52 -36
  47. package/dist/wss.cjs +7 -2
  48. package/dist/wss.js +7 -2
  49. package/dist/wss.mjs +7 -2
  50. package/package.json +7 -2
package/dist/native.cjs CHANGED
@@ -8001,8 +8001,12 @@ function buildComposeChunkedRowArguments(style, modifier, columnGap, styleResolv
8001
8001
  }
8002
8002
  return args.join(", ");
8003
8003
  }
8004
- function buildComposeChunkedTrackModifier(baseModifier, row) {
8004
+ function buildComposeChunkedTrackModifier(baseModifier, row, options = {}) {
8005
+ const fillWidth = options.fillWidth ?? true;
8005
8006
  let modifier = baseModifier;
8007
+ if (row.trackWeight !== void 0) {
8008
+ modifier = appendComposeModifierCall(modifier, `weight(${formatFloat(row.trackWeight)}f, fill = true)`);
8009
+ }
8006
8010
  if (row.height !== void 0) {
8007
8011
  modifier = appendComposeModifierCall(modifier, `height(${formatFloat(row.height)}.dp)`);
8008
8012
  } else if (row.minHeight !== void 0 || row.maxHeight !== void 0) {
@@ -8015,7 +8019,7 @@ function buildComposeChunkedTrackModifier(baseModifier, row) {
8015
8019
  }
8016
8020
  modifier = appendComposeModifierCall(modifier, `heightIn(${heightInArgs.join(", ")})`);
8017
8021
  }
8018
- return row.trackWeight !== void 0 ? appendComposeModifierCall(modifier, `weight(${formatFloat(row.trackWeight)}f, fill = true)`) : modifier;
8022
+ return fillWidth ? appendComposeModifierCall(modifier, "fillMaxWidth()") : modifier;
8019
8023
  }
8020
8024
  function buildComposeChunkedColumnArrangement(layout) {
8021
8025
  const contentAlignment = resolveEffectiveChunkedContentAlignment(layout);
@@ -9764,7 +9768,7 @@ function renderComposeChunkedLayout(node, layout, level, context, modifier, hint
9764
9768
  const usesSingleRowGridStackAlignment = layout.kind === "grid" && layout.rows.length === 1 && effectiveContentAlignment !== void 0 && effectiveContentAlignment !== "start";
9765
9769
  if (layout.kind === "grid" && layout.rows.length === 1 && !usesSingleRowGridStackAlignment) {
9766
9770
  const [row] = layout.rows;
9767
- const lines2 = [`${indent4(level)}Row(${buildComposeChunkedRowArguments(style, buildComposeChunkedTrackModifier(modifier, row), layout.columnGap, context.styleResolveOptions)}) {`];
9771
+ const lines2 = [`${indent4(level)}Row(${buildComposeChunkedRowArguments(style, buildComposeChunkedTrackModifier(modifier, row, { fillWidth: false }), layout.columnGap, context.styleResolveOptions)}) {`];
9768
9772
  const totalWeight = row.weights ? row.weights.reduce((sum, entry) => sum + (entry ?? 0), 0) : void 0;
9769
9773
  row.items.forEach((child, index) => {
9770
9774
  const weight = row.weights?.[index];
@@ -9796,7 +9800,7 @@ function renderComposeChunkedLayout(node, layout, level, context, modifier, hint
9796
9800
  const lines = [`${indent4(level)}Column(modifier = ${modifier}${outerGap ? `, verticalArrangement = ${outerGap}` : ""}) {`];
9797
9801
  for (const row of layout.rows) {
9798
9802
  const totalWeight = row.weights ? row.weights.reduce((sum, entry) => sum + (entry ?? 0), 0) : void 0;
9799
- lines.push(`${indent4(level + 1)}Row(${buildComposeChunkedRowArguments(style, buildComposeChunkedTrackModifier("Modifier.fillMaxWidth()", row), layout.columnGap, context.styleResolveOptions)}) {`);
9803
+ lines.push(`${indent4(level + 1)}Row(${buildComposeChunkedRowArguments(style, buildComposeChunkedTrackModifier("Modifier", row), layout.columnGap, context.styleResolveOptions)}) {`);
9800
9804
  row.items.forEach((child, index) => {
9801
9805
  const weight = row.weights?.[index];
9802
9806
  const columnSize = row.columnSizes?.[index];
@@ -10234,7 +10238,7 @@ function renderComposeContainerNode(node, level, context, hints, modifier, baseL
10234
10238
  }
10235
10239
  const lines = [
10236
10240
  ...baseLines,
10237
- `${indent7(level)}Box {`,
10241
+ `${indent7(level)}Box(modifier = Modifier.matchParentSize()) {`,
10238
10242
  ...renderComposeContainerBody(flowNode, level + 1, context, modifier, hints, renderComposeNode)
10239
10243
  ];
10240
10244
  absoluteChildren.forEach((child) => {
@@ -10251,6 +10255,18 @@ function renderComposeContainerNode(node, level, context, hints, modifier, baseL
10251
10255
  lines.push(`${indent7(level)}}`);
10252
10256
  return lines;
10253
10257
  }
10258
+ function renderComposeUnsupportedFallback(node, level, context, modifier, baseLines, label) {
10259
+ context.helperFlags.add("unsupportedPlaceholder");
10260
+ const sourceTag = node.sourceTag ?? node.component.toLowerCase();
10261
+ const args = [`label = ${quoteKotlinString(label)}`, `sourceTag = ${quoteKotlinString(sourceTag)}`];
10262
+ if (modifier !== "Modifier") {
10263
+ args.push(`modifier = ${modifier}`);
10264
+ }
10265
+ return [
10266
+ ...baseLines,
10267
+ `${indent7(level)}ElitUnsupported(${args.join(", ")})`
10268
+ ];
10269
+ }
10254
10270
  function renderComposeNode(node, level, context, hints = {}) {
10255
10271
  if (node.kind === "text") {
10256
10272
  return renderTextComposable(node, level, context);
@@ -10323,8 +10339,11 @@ function renderComposeNode(node, level, context, hints = {}) {
10323
10339
  ];
10324
10340
  }
10325
10341
  if (node.component === "WebView") {
10342
+ const source = resolveNativeSurfaceSource(node);
10343
+ if (!source) {
10344
+ return renderComposeUnsupportedFallback(node, level, context, modifier, baseLines, "WebView");
10345
+ }
10326
10346
  context.helperFlags.add("webViewSurface");
10327
- const source = resolveNativeSurfaceSource(node) ?? "";
10328
10347
  const label2 = resolveNativeAccessibilityLabel(node) ?? "Web content";
10329
10348
  return [
10330
10349
  ...baseLines,
@@ -10332,8 +10351,11 @@ function renderComposeNode(node, level, context, hints = {}) {
10332
10351
  ];
10333
10352
  }
10334
10353
  if (node.component === "Media") {
10354
+ const source = resolveNativeSurfaceSource(node);
10355
+ if (!source) {
10356
+ return renderComposeUnsupportedFallback(node, level, context, modifier, baseLines, "Media");
10357
+ }
10335
10358
  context.helperFlags.add("mediaSurface");
10336
- const source = resolveNativeSurfaceSource(node) ?? "";
10337
10359
  const label2 = resolveNativeMediaLabel(node);
10338
10360
  const style = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
10339
10361
  const objectFit = resolveNativeObjectFitStyle(style);
@@ -10350,19 +10372,44 @@ function renderComposeNode(node, level, context, hints = {}) {
10350
10372
  const poster = resolveNativeVideoPoster(node);
10351
10373
  const controls = shouldNativeShowVideoControls(node);
10352
10374
  const playsInline = shouldNativePlayInline(node);
10375
+ const videoArgs = [
10376
+ `source = ${quoteKotlinString(source)}`,
10377
+ `label = ${quoteKotlinString(label2)}`,
10378
+ `autoPlay = ${autoPlay ? "true" : "false"}`,
10379
+ `loop = ${loop ? "true" : "false"}`,
10380
+ `muted = ${muted ? "true" : "false"}`,
10381
+ `controls = ${controls ? "true" : "false"}`,
10382
+ `poster = ${poster ? quoteKotlinString(poster) : "null"}`,
10383
+ `playsInline = ${playsInline ? "true" : "false"}`
10384
+ ];
10385
+ if (objectFit !== "cover") {
10386
+ videoArgs.push(`posterFit = ${quoteKotlinString(objectFit)}`);
10387
+ }
10388
+ if (objectPosition !== "center") {
10389
+ videoArgs.push(`posterPosition = ${quoteKotlinString(objectPosition)}`);
10390
+ }
10391
+ videoArgs.push(`modifier = ${modifier}`);
10353
10392
  return [
10354
10393
  ...baseLines,
10355
- `${indent7(level)}ElitVideoSurface(source = ${quoteKotlinString(source)}, label = ${quoteKotlinString(label2)}, autoPlay = ${autoPlay ? "true" : "false"}, loop = ${loop ? "true" : "false"}, muted = ${muted ? "true" : "false"}, controls = ${controls ? "true" : "false"}, playsInline = ${playsInline ? "true" : "false"}${poster ? `, poster = ${quoteKotlinString(poster)}` : ""}${objectFit !== "cover" ? `, posterFit = ${quoteKotlinString(objectFit)}` : ""}${objectPosition !== "center" ? `, posterPosition = ${quoteKotlinString(objectPosition)}` : ""}, modifier = ${modifier})`
10394
+ `${indent7(level)}ElitVideoSurface(${videoArgs.join(", ")})`
10356
10395
  ];
10357
10396
  }
10397
+ if (node.component === "Math") {
10398
+ return renderComposeUnsupportedFallback(node, level, context, modifier, baseLines, "Math");
10399
+ }
10358
10400
  if (node.component === "Cell") {
10359
10401
  const style = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
10402
+ const hasExplicitWidth = hasExplicitNativeWidthStyle(style);
10360
10403
  const cellHints = {
10361
10404
  ...hints,
10362
- ...!hasExplicitNativeWidthStyle(style) ? { fillWidth: true } : {},
10405
+ ...!hasExplicitWidth ? { fillWidth: true } : {},
10363
10406
  ...!hasExplicitNativeHeightStyle(style) && hints.fillHeight ? { fillHeight: true } : {}
10364
10407
  };
10365
- return renderComposeContainerNode(node, level, context, cellHints, modifier, baseLines);
10408
+ let cellModifier = modifier;
10409
+ if (hints.parentFlexLayout === "Row" && !hasExplicitWidth) {
10410
+ cellModifier = cellModifier === "Modifier" ? "Modifier.weight(1f, fill = true)" : cellModifier.replace(/^Modifier\./, "Modifier.weight(1f, fill = true).");
10411
+ }
10412
+ return renderComposeContainerNode(node, level, context, cellHints, cellModifier, baseLines);
10366
10413
  }
10367
10414
  if (node.children.length > 0 || node.component === "Screen") {
10368
10415
  return renderComposeContainerNode(node, level, context, hints, modifier, baseLines);
@@ -10379,11 +10426,13 @@ function buildAndroidComposeHelpers(context) {
10379
10426
  if (context.helperFlags.has("bridge")) {
10380
10427
  helpers.push("");
10381
10428
  helpers.push("private object ElitNativeBridge {");
10429
+ helpers.push(" var onAction: ((String, String?, String?) -> Unit)? = null");
10430
+ helpers.push(" var onNavigate: ((String) -> Unit)? = null");
10382
10431
  helpers.push(" fun dispatch(action: String? = null, route: String? = null, payloadJson: String? = null) {");
10383
10432
  helpers.push(' android.util.Log.d("ElitNativeBridge", listOfNotNull(action, route, payloadJson).joinToString(" | "))');
10384
10433
  helpers.push(" }");
10385
10434
  helpers.push("");
10386
- helpers.push(" fun controlEventPayload(event: String, sourceTag: String, inputType: String? = null, value: String? = null, values: List<String>? = null, checked: Boolean? = null, detailJson: String? = null): String {");
10435
+ helpers.push(" fun controlEventPayload(event: String, sourceTag: String, inputType: String? = null, value: String? = null, values: Iterable<String>? = null, checked: Boolean? = null, detailJson: String? = null): String {");
10387
10436
  helpers.push(" val parts = mutableListOf<String>()");
10388
10437
  helpers.push(' parts += ""event":"$event""');
10389
10438
  helpers.push(' parts += ""sourceTag":"$sourceTag""');
@@ -10407,33 +10456,50 @@ function buildAndroidComposeHelpers(context) {
10407
10456
  helpers.push("}");
10408
10457
  }
10409
10458
  if (context.helperFlags.has("backgroundImage") || context.helperFlags.has("imagePlaceholder")) {
10459
+ helpers.push("");
10460
+ helpers.push("private fun elitLoadBackgroundBitmap(view: android.widget.ImageView, source: String, repeatMode: String, backgroundSize: String, backgroundPosition: String) {");
10461
+ helpers.push(" if (source.isBlank()) return");
10462
+ helpers.push(' val tileModeX = if (repeatMode == "repeat-y") android.graphics.Shader.TileMode.CLAMP else android.graphics.Shader.TileMode.REPEAT');
10463
+ helpers.push(' val tileModeY = if (repeatMode == "repeat-x") android.graphics.Shader.TileMode.CLAMP else android.graphics.Shader.TileMode.REPEAT');
10464
+ helpers.push(" view.scaleType = android.widget.ImageView.ScaleType.CENTER_CROP");
10465
+ helpers.push(' if (repeatMode == "no-repeat" || repeatMode == "round") {');
10466
+ helpers.push(" view.setImageURI(android.net.Uri.parse(source))");
10467
+ helpers.push(" } else {");
10468
+ helpers.push(" val bitmap = android.graphics.BitmapFactory.decodeStream(android.net.Uri.parse(source).let { runCatching { view.context.contentResolver.openInputStream(it) }.getOrNull() })");
10469
+ helpers.push(" if (bitmap != null) view.setImageDrawable(android.graphics.drawable.BitmapDrawable(view.resources, bitmap).apply { tileModeX = tileModeX; tileModeY = tileModeY })");
10470
+ helpers.push(" }");
10471
+ helpers.push("}");
10410
10472
  helpers.push("");
10411
10473
  helpers.push("@Composable");
10412
10474
  helpers.push('private fun ElitBackgroundImage(source: String, backgroundSize: String = "cover", backgroundPosition: String = "center", backgroundRepeat: String = "no-repeat", modifier: Modifier = Modifier) {');
10413
10475
  helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10414
10476
  helpers.push(" factory = { context -> android.widget.ImageView(context).apply {");
10415
- helpers.push(" scaleType = android.widget.ImageView.ScaleType.CENTER_CROP");
10416
- helpers.push(" if (source.isNotBlank()) setImageURI(android.net.Uri.parse(source))");
10477
+ helpers.push(" elitLoadBackgroundBitmap(this, source, backgroundRepeat, backgroundSize, backgroundPosition)");
10417
10478
  helpers.push(" } },");
10418
10479
  helpers.push(" modifier = modifier,");
10419
10480
  helpers.push(" )");
10420
10481
  helpers.push("}");
10421
10482
  helpers.push("");
10422
10483
  helpers.push("@Composable");
10423
- helpers.push('private fun ElitImageSurface(source: String, label: String, alt: String? = null, modifier: Modifier = Modifier, objectFit: String = "cover", objectPosition: String = "center") {');
10484
+ helpers.push('private fun ElitImageSurface(source: String, label: String, contentDescription: String?, objectFit: String = "cover", objectPosition: String = "center", modifier: Modifier = Modifier) {');
10424
10485
  helpers.push(" if (source.isBlank()) {");
10425
10486
  helpers.push(" Box(modifier = modifier, contentAlignment = Alignment.Center) {");
10426
- helpers.push(" Text(text = alt ?: label)");
10487
+ helpers.push(" Text(text = contentDescription ?: label)");
10427
10488
  helpers.push(" }");
10428
10489
  helpers.push(" return");
10429
10490
  helpers.push(" }");
10430
- helpers.push(" ElitBackgroundImage(source = source, backgroundSize = objectFit, backgroundPosition = objectPosition, modifier = modifier)");
10491
+ helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10492
+ helpers.push(" factory = { context -> android.widget.ImageView(context).apply {");
10493
+ helpers.push(' elitLoadBackgroundBitmap(this, source, "no-repeat", objectFit, objectPosition)');
10494
+ helpers.push(" } },");
10495
+ helpers.push(" modifier = modifier,");
10496
+ helpers.push(" )");
10431
10497
  helpers.push("}");
10432
10498
  }
10433
10499
  if (context.helperFlags.has("webViewSurface")) {
10434
10500
  helpers.push("");
10435
10501
  helpers.push("@Composable");
10436
- helpers.push("private fun ElitWebViewSurface(source: String, label: String, modifier: Modifier = Modifier) {");
10502
+ helpers.push("private fun ElitWebViewSurface(source: String, label: String?, modifier: Modifier = Modifier) {");
10437
10503
  helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10438
10504
  helpers.push(" factory = { context -> android.webkit.WebView(context).apply {");
10439
10505
  helpers.push(" contentDescription = label");
@@ -10445,18 +10511,58 @@ function buildAndroidComposeHelpers(context) {
10445
10511
  helpers.push("}");
10446
10512
  }
10447
10513
  if (context.helperFlags.has("mediaSurface")) {
10514
+ helpers.push("");
10515
+ helpers.push("private fun elitVideoPosterScaleType(posterFit: String, posterPosition: String): android.widget.ImageView.ScaleType = when (posterFit.trim().lowercase()) {");
10516
+ helpers.push(' "contain", "scale-down" -> when (posterPosition.trim().lowercase()) {');
10517
+ helpers.push(' "top", "leading", "top-leading", "bottom-leading" -> android.widget.ImageView.ScaleType.FIT_START');
10518
+ helpers.push(' "bottom", "trailing", "bottom-trailing" -> android.widget.ImageView.ScaleType.FIT_END');
10519
+ helpers.push(" else -> android.widget.ImageView.ScaleType.FIT_CENTER");
10520
+ helpers.push(" }");
10521
+ helpers.push(" else -> android.widget.ImageView.ScaleType.CENTER_CROP");
10522
+ helpers.push("}");
10448
10523
  helpers.push("");
10449
10524
  helpers.push("@Composable");
10450
- helpers.push('private fun ElitVideoSurface(source: String, label: String, autoPlay: Boolean, loop: Boolean, muted: Boolean, controls: Boolean, playsInline: Boolean, poster: String? = null, posterFit: String = "cover", posterPosition: String = "center", modifier: Modifier = Modifier) {');
10525
+ helpers.push('private fun ElitVideoSurface(source: String, label: String, autoPlay: Boolean, loop: Boolean, muted: Boolean, controls: Boolean, poster: String?, playsInline: Boolean, posterFit: String = "cover", posterPosition: String = "center", modifier: Modifier = Modifier) {');
10526
+ helpers.push(" // Android VideoView already renders inline; playsInline is retained for parity with iOS generation.");
10451
10527
  helpers.push(" Box(modifier = modifier, contentAlignment = Alignment.Center) {");
10452
- helpers.push(' Text(text = if (autoPlay) "Playing $label" else label)');
10528
+ helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10529
+ helpers.push(" factory = { context ->");
10530
+ helpers.push(" android.widget.ImageView(context).apply {");
10531
+ helpers.push(" val posterView = this");
10532
+ helpers.push(" posterView.contentDescription = label");
10533
+ helpers.push(" posterView.scaleType = elitVideoPosterScaleType(posterFit, posterPosition)");
10534
+ helpers.push(" if (poster != null) posterView.setImageURI(android.net.Uri.parse(poster))");
10535
+ helpers.push(" }");
10536
+ helpers.push(" },");
10537
+ helpers.push(" )");
10538
+ helpers.push(" if (controls) {");
10539
+ helpers.push(" // Hand off to platform chrome; ElitVideoSurface exposes controls for parity only.");
10540
+ helpers.push(" }");
10453
10541
  helpers.push(" }");
10454
10542
  helpers.push("}");
10455
10543
  helpers.push("");
10456
10544
  helpers.push("@Composable");
10457
10545
  helpers.push("private fun ElitAudioSurface(source: String, label: String, autoPlay: Boolean, loop: Boolean, muted: Boolean, modifier: Modifier = Modifier) {");
10458
- helpers.push(" Button(onClick = {}, modifier = modifier) {");
10459
- helpers.push(" Text(text = label)");
10546
+ helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10547
+ helpers.push(" factory = { context ->");
10548
+ helpers.push(" android.media.MediaPlayer().apply {");
10549
+ helpers.push(" val mediaPlayer = this");
10550
+ helpers.push(" mediaPlayer.isLooping = loop");
10551
+ helpers.push(" mediaPlayer.setVolume(if (muted) 0f else 1f, if (muted) 0f else 1f)");
10552
+ helpers.push(" if (source.isNotBlank()) setDataSource(source)");
10553
+ helpers.push(" if (autoPlay) start()");
10554
+ helpers.push(" }");
10555
+ helpers.push(" },");
10556
+ helpers.push(" modifier = modifier,");
10557
+ helpers.push(" )");
10558
+ helpers.push("}");
10559
+ }
10560
+ if (context.helperFlags.has("unsupportedPlaceholder")) {
10561
+ helpers.push("");
10562
+ helpers.push("@Composable");
10563
+ helpers.push("private fun ElitUnsupported(label: String, sourceTag: String, modifier: Modifier = Modifier) {");
10564
+ helpers.push(" Box(modifier = modifier, contentAlignment = Alignment.Center) {");
10565
+ helpers.push(' Text(text = "$label ($sourceTag)")');
10460
10566
  helpers.push(" }");
10461
10567
  helpers.push("}");
10462
10568
  }
@@ -10487,11 +10593,27 @@ function renderAndroidCompose(input, options = {}) {
10487
10593
  resolvedStyles: styleData.resolvedStyles,
10488
10594
  styleContexts: styleData.styleContexts
10489
10595
  };
10490
- const bodyLines = tree.roots.length === 1 ? renderComposeNode(tree.roots[0], 1, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }) : [
10491
- " Column(modifier = Modifier.fillMaxSize()) {",
10492
- ...renderComposeChildren(tree.roots, 2, context, renderComposeNode, "Column", void 0, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }),
10493
- " }"
10494
- ];
10596
+ const singleRoot = tree.roots.length === 1 ? tree.roots[0] : null;
10597
+ const isSingleScreenRoot = singleRoot !== null && singleRoot.kind === "element" && singleRoot.component === "Screen";
10598
+ if (isSingleScreenRoot) {
10599
+ context.helperFlags.add("screenRoot");
10600
+ }
10601
+ let bodyLines;
10602
+ if (isSingleScreenRoot) {
10603
+ bodyLines = [
10604
+ " Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {",
10605
+ ...renderComposeNode(tree.roots[0], 2, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }),
10606
+ " }"
10607
+ ];
10608
+ } else if (tree.roots.length === 1) {
10609
+ bodyLines = renderComposeNode(tree.roots[0], 1, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight });
10610
+ } else {
10611
+ bodyLines = [
10612
+ " Column(modifier = Modifier.fillMaxSize()) {",
10613
+ ...renderComposeChildren(tree.roots, 2, context, renderComposeNode, "Column", void 0, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }),
10614
+ " }"
10615
+ ];
10616
+ }
10495
10617
  const lines = [];
10496
10618
  if (resolvedOptions.includePackage) {
10497
10619
  lines.push(`package ${resolvedOptions.packageName}`);
@@ -11391,6 +11513,18 @@ function renderSwiftUIContainerNode(node, level, context, hints, baseLines) {
11391
11513
  ];
11392
11514
  return [...baseLines, ...appendSwiftUIOverlays(contentLines, overlays, level)];
11393
11515
  }
11516
+ function renderSwiftUnsupportedFallback(node, level, context, baseLines, modifiers, label) {
11517
+ context.helperFlags.add("unsupportedPlaceholder");
11518
+ const sourceTag = node.sourceTag ?? node.component.toLowerCase();
11519
+ return appendSwiftUIModifiers(
11520
+ [
11521
+ ...baseLines,
11522
+ `${indent11(level)}elitUnsupportedPlaceholder(label: ${quoteSwiftString(label)}, sourceTag: ${quoteSwiftString(sourceTag)})`
11523
+ ],
11524
+ modifiers,
11525
+ level
11526
+ );
11527
+ }
11394
11528
  function renderSwiftUINode(node, level, context, hints = {}) {
11395
11529
  if (node.kind === "text") {
11396
11530
  return renderTextView(node, level, context);
@@ -11424,10 +11558,21 @@ function renderSwiftUINode(node, level, context, hints = {}) {
11424
11558
  const imageStyle = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
11425
11559
  const objectFit = resolveNativeObjectFitStyle(imageStyle);
11426
11560
  const objectPosition = resolveNativeObjectPositionStyle(imageStyle);
11561
+ const imageArgs = [
11562
+ `source: ${quoteSwiftString(source)}`,
11563
+ `label: ${quoteSwiftString(fallbackLabel)}`,
11564
+ `alt: ${alt ? quoteSwiftString(alt) : "nil"}`
11565
+ ];
11566
+ if (objectFit !== "cover") {
11567
+ imageArgs.push(`objectFit: ${quoteSwiftString(objectFit)}`);
11568
+ }
11569
+ if (objectPosition !== "center") {
11570
+ imageArgs.push(`objectPosition: ${quoteSwiftString(objectPosition)}`);
11571
+ }
11427
11572
  return appendSwiftUIModifiers(
11428
11573
  [
11429
11574
  ...baseLines,
11430
- `${indent11(level)}elitImageSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(fallbackLabel)}, alt: ${alt ? quoteSwiftString(alt) : "nil"}, objectFit: ${quoteSwiftString(objectFit)}, objectPosition: ${quoteSwiftString(objectPosition)})`
11575
+ `${indent11(level)}elitImageSurface(${imageArgs.join(", ")})`
11431
11576
  ],
11432
11577
  modifiers,
11433
11578
  level
@@ -11462,33 +11607,38 @@ function renderSwiftUINode(node, level, context, hints = {}) {
11462
11607
  return [...baseLines, ...appendSwiftUIModifiers(canvas.lines, canvas.modifiers, level)];
11463
11608
  }
11464
11609
  if (node.component === "WebView") {
11610
+ const source = resolveNativeSurfaceSource(node);
11611
+ if (!source) {
11612
+ return renderSwiftUnsupportedFallback(node, level, context, baseLines, modifiers, "WebView");
11613
+ }
11465
11614
  context.helperFlags.add("webViewSurface");
11466
- const source = resolveNativeSurfaceSource(node) ?? "";
11467
11615
  const label2 = resolveNativeAccessibilityLabel(node) ?? "Web content";
11468
11616
  return appendSwiftUIModifiers(
11469
11617
  [
11470
11618
  ...baseLines,
11471
- `${indent11(level)}elitWebViewSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(label2)})`
11619
+ `${indent11(level)}ElitWebViewSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(label2)})`
11472
11620
  ],
11473
11621
  modifiers,
11474
11622
  level
11475
11623
  );
11476
11624
  }
11477
11625
  if (node.component === "Media") {
11626
+ const source = resolveNativeSurfaceSource(node);
11627
+ if (!source) {
11628
+ return renderSwiftUnsupportedFallback(node, level, context, baseLines, modifiers, "Media");
11629
+ }
11478
11630
  context.helperFlags.add("mediaSurface");
11479
- const source = resolveNativeSurfaceSource(node) ?? "";
11480
11631
  const label2 = resolveNativeMediaLabel(node);
11481
11632
  const style = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
11482
11633
  const objectFit = resolveNativeObjectFitStyle(style);
11483
11634
  const objectPosition = resolveNativeObjectPositionStyle(style);
11484
11635
  const autoPlay = toNativeBoolean(node.props.autoPlay ?? node.props.autoplay);
11485
- const loop = toNativeBoolean(node.props.loop);
11486
11636
  const muted = isNativeMuted(node);
11487
11637
  if (node.sourceTag === "audio") {
11488
11638
  return appendSwiftUIModifiers(
11489
11639
  [
11490
11640
  ...baseLines,
11491
- `${indent11(level)}ElitAudioSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(label2)}, autoPlay: ${autoPlay ? "true" : "false"}, loop: ${loop ? "true" : "false"}, muted: ${muted ? "true" : "false"})`
11641
+ `${indent11(level)}ElitAudioSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(label2)}, autoPlay: ${autoPlay ? "true" : "false"}, muted: ${muted ? "true" : "false"})`
11492
11642
  ],
11493
11643
  modifiers,
11494
11644
  level
@@ -11497,15 +11647,33 @@ function renderSwiftUINode(node, level, context, hints = {}) {
11497
11647
  const poster = resolveNativeVideoPoster(node);
11498
11648
  const controls = shouldNativeShowVideoControls(node);
11499
11649
  const playsInline = shouldNativePlayInline(node);
11650
+ const videoArgs = [
11651
+ `source: ${quoteSwiftString(source)}`,
11652
+ `label: ${quoteSwiftString(label2)}`,
11653
+ `autoPlay: ${autoPlay ? "true" : "false"}`,
11654
+ `muted: ${muted ? "true" : "false"}`,
11655
+ `controls: ${controls ? "true" : "false"}`,
11656
+ `poster: ${poster ? quoteSwiftString(poster) : "nil"}`,
11657
+ `playsInline: ${playsInline ? "true" : "false"}`
11658
+ ];
11659
+ if (objectFit !== "cover") {
11660
+ videoArgs.push(`posterFit: ${quoteSwiftString(objectFit)}`);
11661
+ }
11662
+ if (objectPosition !== "center") {
11663
+ videoArgs.push(`posterPosition: ${quoteSwiftString(objectPosition)}`);
11664
+ }
11500
11665
  return appendSwiftUIModifiers(
11501
11666
  [
11502
11667
  ...baseLines,
11503
- `${indent11(level)}ElitVideoSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(label2)}, autoPlay: ${autoPlay ? "true" : "false"}, loop: ${loop ? "true" : "false"}, muted: ${muted ? "true" : "false"}, controls: ${controls ? "true" : "false"}, playsInline: ${playsInline ? "true" : "false"}${poster ? `, poster: ${quoteSwiftString(poster)}` : ""}, posterFit: ${quoteSwiftString(objectFit)}, posterPosition: ${quoteSwiftString(objectPosition)})`
11668
+ `${indent11(level)}ElitVideoSurface(${videoArgs.join(", ")})`
11504
11669
  ],
11505
11670
  modifiers,
11506
11671
  level
11507
11672
  );
11508
11673
  }
11674
+ if (node.component === "Math") {
11675
+ return renderSwiftUnsupportedFallback(node, level, context, baseLines, modifiers, "Math");
11676
+ }
11509
11677
  if (node.component === "Cell") {
11510
11678
  const style = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
11511
11679
  const cellHints = {
@@ -11531,6 +11699,8 @@ function buildSwiftUIHelpers(context) {
11531
11699
  if (context.helperFlags.has("bridge")) {
11532
11700
  helpers.push("");
11533
11701
  helpers.push("private enum ElitNativeBridge {");
11702
+ helpers.push(" static var onAction: ((String, String?, String?) -> Void)?");
11703
+ helpers.push(" static var onNavigate: ((String) -> Void)?");
11534
11704
  helpers.push(" static func dispatch(action: String? = nil, route: String? = nil, payloadJson: String? = nil) {");
11535
11705
  helpers.push(' print("ElitNativeBridge", action ?? "", route ?? "", payloadJson ?? "")');
11536
11706
  helpers.push(" }");
@@ -11555,6 +11725,13 @@ function buildSwiftUIHelpers(context) {
11555
11725
  helpers.push("}");
11556
11726
  }
11557
11727
  if (context.helperFlags.has("backgroundImage") || context.helperFlags.has("imagePlaceholder")) {
11728
+ helpers.push("");
11729
+ helpers.push("@ViewBuilder");
11730
+ helpers.push('private func elitBackgroundImage(_ image: Image, backgroundSize: String = "cover", backgroundPosition: String = "center", backgroundRepeat: String = "no-repeat") -> some View {');
11731
+ helpers.push(" image");
11732
+ helpers.push(" .resizable()");
11733
+ helpers.push(" .scaledToFill()");
11734
+ helpers.push("}");
11558
11735
  helpers.push("");
11559
11736
  helpers.push("@ViewBuilder");
11560
11737
  helpers.push('private func elitBackgroundImageSurface(source: String, objectFit: String = "cover", objectPosition: String = "center") -> some View {');
@@ -11562,9 +11739,7 @@ function buildSwiftUIHelpers(context) {
11562
11739
  helpers.push(" AsyncImage(url: url) { phase in");
11563
11740
  helpers.push(" switch phase {");
11564
11741
  helpers.push(" case .success(let image):");
11565
- helpers.push(" image");
11566
- helpers.push(" .resizable()");
11567
- helpers.push(" .scaledToFill()");
11742
+ helpers.push(' elitBackgroundImage(image, backgroundSize: objectFit, backgroundPosition: objectPosition, backgroundRepeat: "no-repeat")');
11568
11743
  helpers.push(" default:");
11569
11744
  helpers.push(" Color.clear");
11570
11745
  helpers.push(" }");
@@ -11575,7 +11750,7 @@ function buildSwiftUIHelpers(context) {
11575
11750
  helpers.push("}");
11576
11751
  helpers.push("");
11577
11752
  helpers.push("@ViewBuilder");
11578
- helpers.push('private func elitImageSurface(source: String, label: String, alt: String? = nil, objectFit: String = "cover", objectPosition: String = "center") -> some View {');
11753
+ helpers.push('private func elitImageSurface(source: String, label: String, alt: String?, objectFit: String = "cover", objectPosition: String = "center") -> some View {');
11579
11754
  helpers.push(" if source.isEmpty {");
11580
11755
  helpers.push(" Text(alt ?? label)");
11581
11756
  helpers.push(" .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)");
@@ -11586,31 +11761,131 @@ function buildSwiftUIHelpers(context) {
11586
11761
  }
11587
11762
  if (context.helperFlags.has("webViewSurface")) {
11588
11763
  helpers.push("");
11589
- helpers.push("@ViewBuilder");
11590
- helpers.push("private func elitWebViewSurface(source: String, label: String) -> some View {");
11591
- helpers.push(" VStack(alignment: .leading, spacing: 8) {");
11592
- helpers.push(" Text(label)");
11593
- helpers.push(" Text(source)");
11594
- helpers.push(" .font(.caption)");
11595
- helpers.push(" .foregroundStyle(.secondary)");
11764
+ helpers.push("struct ElitWebViewSurface: UIViewRepresentable {");
11765
+ helpers.push(" let source: String");
11766
+ helpers.push(" let label: String?");
11767
+ helpers.push("");
11768
+ helpers.push(" func makeUIView(context: Context) -> WKWebView {");
11769
+ helpers.push(" let webView = WKWebView()");
11770
+ helpers.push(" webView.accessibilityLabel = label");
11771
+ helpers.push(" if let url = URL(string: source), !source.isEmpty { webView.load(URLRequest(url: url)) }");
11772
+ helpers.push(" return webView");
11596
11773
  helpers.push(" }");
11774
+ helpers.push("");
11775
+ helpers.push(" func updateUIView(_ uiView: WKWebView, context: Context) {}");
11597
11776
  helpers.push("}");
11598
11777
  }
11599
11778
  if (context.helperFlags.has("mediaSurface")) {
11779
+ helpers.push("");
11780
+ helpers.push("private func elitPosterAlignment(_ posterPosition: String) -> Alignment {");
11781
+ helpers.push(" switch posterPosition.lowercased() {");
11782
+ helpers.push(' case "top", "leading", "top-leading":');
11783
+ helpers.push(" return .topLeading");
11784
+ helpers.push(' case "bottom", "trailing", "bottom-trailing":');
11785
+ helpers.push(" return .bottomTrailing");
11786
+ helpers.push(' case "center":');
11787
+ helpers.push(" return .center");
11788
+ helpers.push(" default:");
11789
+ helpers.push(" return .center");
11790
+ helpers.push(" }");
11791
+ helpers.push("}");
11600
11792
  helpers.push("");
11601
11793
  helpers.push("@ViewBuilder");
11602
- helpers.push('private func ElitVideoSurface(source: String, label: String, autoPlay: Bool, loop: Bool, muted: Bool, controls: Bool, playsInline: Bool, poster: String? = nil, posterFit: String = "cover", posterPosition: String = "center") -> some View {');
11603
- helpers.push(" VStack(alignment: .leading, spacing: 8) {");
11604
- helpers.push(" if let poster, !poster.isEmpty {");
11605
- helpers.push(" elitBackgroundImageSurface(source: poster, objectFit: posterFit, objectPosition: posterPosition)");
11794
+ helpers.push("private func elitPosterImage(_ image: Image, posterFit: String, posterPosition: String) -> some View {");
11795
+ helpers.push(" let resizable = image.resizable()");
11796
+ helpers.push(" switch posterFit.lowercased() {");
11797
+ helpers.push(' case "contain":');
11798
+ helpers.push(" return AnyView(resizable.scaledToFit().frame(maxWidth: .infinity, maxHeight: .infinity, alignment: elitPosterAlignment(posterPosition)))");
11799
+ helpers.push(' case "fill", "cover":');
11800
+ helpers.push(" return AnyView(resizable.scaledToFill().frame(maxWidth: .infinity, maxHeight: .infinity, alignment: elitPosterAlignment(posterPosition)))");
11801
+ helpers.push(" default:");
11802
+ helpers.push(" return AnyView(resizable.scaledToFill())");
11803
+ helpers.push(" }");
11804
+ helpers.push("}");
11805
+ helpers.push("");
11806
+ helpers.push("struct ElitVideoPlayerController: UIViewControllerRepresentable {");
11807
+ helpers.push(" let source: String");
11808
+ helpers.push(" let autoPlay: Bool");
11809
+ helpers.push(" let muted: Bool");
11810
+ helpers.push(" let controls: Bool");
11811
+ helpers.push(" let playsInline: Bool");
11812
+ helpers.push("");
11813
+ helpers.push(" func makeUIViewController(context: Context) -> AVPlayerViewController {");
11814
+ helpers.push(" let controller = AVPlayerViewController()");
11815
+ helpers.push(" if let url = URL(string: source) {");
11816
+ helpers.push(" let player = AVPlayer(url: url)");
11817
+ helpers.push(" let resolvedPlayer = player");
11818
+ helpers.push(" resolvedPlayer.isMuted = muted");
11819
+ helpers.push(" controller.player = resolvedPlayer");
11820
+ helpers.push(" if autoPlay { resolvedPlayer.play() }");
11606
11821
  helpers.push(" }");
11607
- helpers.push(" Text(label)");
11822
+ helpers.push(" controller.showsPlaybackControls = controls");
11823
+ helpers.push(" controller.entersFullScreenWhenPlaybackBegins = !playsInline");
11824
+ helpers.push(" return controller");
11608
11825
  helpers.push(" }");
11826
+ helpers.push("");
11827
+ helpers.push(" func updateUIViewController(_ controller: AVPlayerViewController, context: Context) {}");
11609
11828
  helpers.push("}");
11829
+ helpers.push("");
11830
+ helpers.push("struct ElitVideoSurface: View {");
11831
+ helpers.push(" let source: String");
11832
+ helpers.push(" let label: String");
11833
+ helpers.push(" let autoPlay: Bool");
11834
+ helpers.push(" let muted: Bool");
11835
+ helpers.push(" let controls: Bool");
11836
+ helpers.push(" let poster: String?");
11837
+ helpers.push(" let playsInline: Bool");
11838
+ helpers.push(" let posterFit: String");
11839
+ helpers.push(" let posterPosition: String");
11840
+ helpers.push("");
11841
+ helpers.push(" var body: some View {");
11842
+ helpers.push(" ZStack {");
11843
+ helpers.push(" if let poster, !poster.isEmpty, let posterURL = URL(string: poster) {");
11844
+ helpers.push(" AsyncImage(url: posterURL) { phase in");
11845
+ helpers.push(" switch phase {");
11846
+ helpers.push(" case .success(let image):");
11847
+ helpers.push(" elitPosterImage(image, posterFit: posterFit, posterPosition: posterPosition)");
11848
+ helpers.push(" default:");
11849
+ helpers.push(" Color.clear");
11850
+ helpers.push(" }");
11851
+ helpers.push(" }");
11852
+ helpers.push(" }");
11853
+ helpers.push(" ElitVideoPlayerController(source: source, autoPlay: autoPlay, muted: muted, controls: controls, playsInline: playsInline)");
11854
+ helpers.push(" .accessibilityLabel(label)");
11855
+ helpers.push(" }");
11856
+ helpers.push(" }");
11857
+ helpers.push("}");
11858
+ helpers.push("");
11859
+ helpers.push("struct ElitAudioSurface: View {");
11860
+ helpers.push(" let source: String");
11861
+ helpers.push(" let label: String");
11862
+ helpers.push(" let autoPlay: Bool");
11863
+ helpers.push(" let muted: Bool");
11864
+ helpers.push("");
11865
+ helpers.push(" var body: some View {");
11866
+ helpers.push(" VStack {");
11867
+ helpers.push(" if let url = URL(string: source), !source.isEmpty {");
11868
+ helpers.push(" AVPlayer(url: url).let { resolvedPlayer in");
11869
+ helpers.push(" resolvedPlayer.isMuted = muted");
11870
+ helpers.push(" if autoPlay { resolvedPlayer.play() }");
11871
+ helpers.push(" }");
11872
+ helpers.push(" }");
11873
+ helpers.push(' Label(label, systemImage: "waveform")');
11874
+ helpers.push(" .accessibilityLabel(label)");
11875
+ helpers.push(" }");
11876
+ helpers.push(" }");
11877
+ helpers.push("}");
11878
+ }
11879
+ if (context.helperFlags.has("unsupportedPlaceholder")) {
11610
11880
  helpers.push("");
11611
11881
  helpers.push("@ViewBuilder");
11612
- helpers.push("private func ElitAudioSurface(source: String, label: String, autoPlay: Bool, loop: Bool, muted: Bool) -> some View {");
11613
- helpers.push(' Label(label, systemImage: "waveform")');
11882
+ helpers.push("private func elitUnsupportedPlaceholder(label: String, sourceTag: String) -> some View {");
11883
+ helpers.push(" VStack(spacing: 8) {");
11884
+ helpers.push(" Text(label)");
11885
+ helpers.push(" Text(sourceTag)");
11886
+ helpers.push(" .font(.caption)");
11887
+ helpers.push(" .foregroundStyle(.secondary)");
11888
+ helpers.push(" }");
11614
11889
  helpers.push("}");
11615
11890
  }
11616
11891
  return helpers;
@@ -11638,17 +11913,30 @@ function renderSwiftUI(input, options = {}) {
11638
11913
  resolvedStyles: styleData.resolvedStyles,
11639
11914
  styleContexts: styleData.styleContexts
11640
11915
  };
11641
- const bodyLines = tree.roots.length === 1 ? renderSwiftUINode(tree.roots[0], 2, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }) : [
11916
+ const singleRoot = tree.roots.length === 1 ? tree.roots[0] : null;
11917
+ const isSingleScreenRoot = singleRoot !== null && singleRoot.kind === "element" && singleRoot.component === "Screen";
11918
+ const innerLines = tree.roots.length === 1 ? renderSwiftUINode(tree.roots[0], 2, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }) : [
11642
11919
  " VStack(alignment: .leading, spacing: 0) {",
11643
11920
  ...renderSwiftUIChildren(tree.roots, 3, context, renderSwiftUINode, "VStack", void 0, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }),
11644
11921
  " }"
11645
11922
  ];
11923
+ const bodyLines = isSingleScreenRoot ? [
11924
+ " ScrollView {",
11925
+ ...innerLines.map((line) => " " + line),
11926
+ " }"
11927
+ ] : innerLines;
11646
11928
  const lines = [];
11647
11929
  if (resolvedOptions.includeImports) {
11648
11930
  lines.push("import SwiftUI");
11649
11931
  if (context.helperFlags.has("backgroundImage") || context.helperFlags.has("imagePlaceholder") || context.helperFlags.has("openUrlHandler")) {
11650
11932
  lines.push("import Foundation");
11651
11933
  }
11934
+ if (context.helperFlags.has("webViewSurface")) {
11935
+ lines.push("import WebKit");
11936
+ }
11937
+ if (context.helperFlags.has("mediaSurface")) {
11938
+ lines.push("import AVKit");
11939
+ }
11652
11940
  lines.push("");
11653
11941
  }
11654
11942
  lines.push(`struct ${resolvedOptions.structName}: View {`);