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.js CHANGED
@@ -7971,8 +7971,12 @@ ${varDeclarations}
7971
7971
  }
7972
7972
  return args.join(", ");
7973
7973
  }
7974
- function buildComposeChunkedTrackModifier(baseModifier, row) {
7974
+ function buildComposeChunkedTrackModifier(baseModifier, row, options = {}) {
7975
+ const fillWidth = options.fillWidth ?? true;
7975
7976
  let modifier = baseModifier;
7977
+ if (row.trackWeight !== void 0) {
7978
+ modifier = appendComposeModifierCall(modifier, `weight(${formatFloat(row.trackWeight)}f, fill = true)`);
7979
+ }
7976
7980
  if (row.height !== void 0) {
7977
7981
  modifier = appendComposeModifierCall(modifier, `height(${formatFloat(row.height)}.dp)`);
7978
7982
  } else if (row.minHeight !== void 0 || row.maxHeight !== void 0) {
@@ -7985,7 +7989,7 @@ ${varDeclarations}
7985
7989
  }
7986
7990
  modifier = appendComposeModifierCall(modifier, `heightIn(${heightInArgs.join(", ")})`);
7987
7991
  }
7988
- return row.trackWeight !== void 0 ? appendComposeModifierCall(modifier, `weight(${formatFloat(row.trackWeight)}f, fill = true)`) : modifier;
7992
+ return fillWidth ? appendComposeModifierCall(modifier, "fillMaxWidth()") : modifier;
7989
7993
  }
7990
7994
  function buildComposeChunkedColumnArrangement(layout) {
7991
7995
  const contentAlignment = resolveEffectiveChunkedContentAlignment(layout);
@@ -9734,7 +9738,7 @@ ${varDeclarations}
9734
9738
  const usesSingleRowGridStackAlignment = layout.kind === "grid" && layout.rows.length === 1 && effectiveContentAlignment !== void 0 && effectiveContentAlignment !== "start";
9735
9739
  if (layout.kind === "grid" && layout.rows.length === 1 && !usesSingleRowGridStackAlignment) {
9736
9740
  const [row] = layout.rows;
9737
- const lines2 = [`${indent4(level)}Row(${buildComposeChunkedRowArguments(style, buildComposeChunkedTrackModifier(modifier, row), layout.columnGap, context.styleResolveOptions)}) {`];
9741
+ const lines2 = [`${indent4(level)}Row(${buildComposeChunkedRowArguments(style, buildComposeChunkedTrackModifier(modifier, row, { fillWidth: false }), layout.columnGap, context.styleResolveOptions)}) {`];
9738
9742
  const totalWeight = row.weights ? row.weights.reduce((sum, entry) => sum + (entry ?? 0), 0) : void 0;
9739
9743
  row.items.forEach((child, index) => {
9740
9744
  const weight = row.weights?.[index];
@@ -9766,7 +9770,7 @@ ${varDeclarations}
9766
9770
  const lines = [`${indent4(level)}Column(modifier = ${modifier}${outerGap ? `, verticalArrangement = ${outerGap}` : ""}) {`];
9767
9771
  for (const row of layout.rows) {
9768
9772
  const totalWeight = row.weights ? row.weights.reduce((sum, entry) => sum + (entry ?? 0), 0) : void 0;
9769
- lines.push(`${indent4(level + 1)}Row(${buildComposeChunkedRowArguments(style, buildComposeChunkedTrackModifier("Modifier.fillMaxWidth()", row), layout.columnGap, context.styleResolveOptions)}) {`);
9773
+ lines.push(`${indent4(level + 1)}Row(${buildComposeChunkedRowArguments(style, buildComposeChunkedTrackModifier("Modifier", row), layout.columnGap, context.styleResolveOptions)}) {`);
9770
9774
  row.items.forEach((child, index) => {
9771
9775
  const weight = row.weights?.[index];
9772
9776
  const columnSize = row.columnSizes?.[index];
@@ -10204,7 +10208,7 @@ ${varDeclarations}
10204
10208
  }
10205
10209
  const lines = [
10206
10210
  ...baseLines,
10207
- `${indent7(level)}Box {`,
10211
+ `${indent7(level)}Box(modifier = Modifier.matchParentSize()) {`,
10208
10212
  ...renderComposeContainerBody(flowNode, level + 1, context, modifier, hints, renderComposeNode)
10209
10213
  ];
10210
10214
  absoluteChildren.forEach((child) => {
@@ -10221,6 +10225,18 @@ ${varDeclarations}
10221
10225
  lines.push(`${indent7(level)}}`);
10222
10226
  return lines;
10223
10227
  }
10228
+ function renderComposeUnsupportedFallback(node, level, context, modifier, baseLines, label) {
10229
+ context.helperFlags.add("unsupportedPlaceholder");
10230
+ const sourceTag = node.sourceTag ?? node.component.toLowerCase();
10231
+ const args = [`label = ${quoteKotlinString(label)}`, `sourceTag = ${quoteKotlinString(sourceTag)}`];
10232
+ if (modifier !== "Modifier") {
10233
+ args.push(`modifier = ${modifier}`);
10234
+ }
10235
+ return [
10236
+ ...baseLines,
10237
+ `${indent7(level)}ElitUnsupported(${args.join(", ")})`
10238
+ ];
10239
+ }
10224
10240
  function renderComposeNode(node, level, context, hints = {}) {
10225
10241
  if (node.kind === "text") {
10226
10242
  return renderTextComposable(node, level, context);
@@ -10293,8 +10309,11 @@ ${varDeclarations}
10293
10309
  ];
10294
10310
  }
10295
10311
  if (node.component === "WebView") {
10312
+ const source = resolveNativeSurfaceSource(node);
10313
+ if (!source) {
10314
+ return renderComposeUnsupportedFallback(node, level, context, modifier, baseLines, "WebView");
10315
+ }
10296
10316
  context.helperFlags.add("webViewSurface");
10297
- const source = resolveNativeSurfaceSource(node) ?? "";
10298
10317
  const label2 = resolveNativeAccessibilityLabel(node) ?? "Web content";
10299
10318
  return [
10300
10319
  ...baseLines,
@@ -10302,8 +10321,11 @@ ${varDeclarations}
10302
10321
  ];
10303
10322
  }
10304
10323
  if (node.component === "Media") {
10324
+ const source = resolveNativeSurfaceSource(node);
10325
+ if (!source) {
10326
+ return renderComposeUnsupportedFallback(node, level, context, modifier, baseLines, "Media");
10327
+ }
10305
10328
  context.helperFlags.add("mediaSurface");
10306
- const source = resolveNativeSurfaceSource(node) ?? "";
10307
10329
  const label2 = resolveNativeMediaLabel(node);
10308
10330
  const style = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
10309
10331
  const objectFit = resolveNativeObjectFitStyle(style);
@@ -10320,19 +10342,44 @@ ${varDeclarations}
10320
10342
  const poster = resolveNativeVideoPoster(node);
10321
10343
  const controls = shouldNativeShowVideoControls(node);
10322
10344
  const playsInline = shouldNativePlayInline(node);
10345
+ const videoArgs = [
10346
+ `source = ${quoteKotlinString(source)}`,
10347
+ `label = ${quoteKotlinString(label2)}`,
10348
+ `autoPlay = ${autoPlay ? "true" : "false"}`,
10349
+ `loop = ${loop ? "true" : "false"}`,
10350
+ `muted = ${muted ? "true" : "false"}`,
10351
+ `controls = ${controls ? "true" : "false"}`,
10352
+ `poster = ${poster ? quoteKotlinString(poster) : "null"}`,
10353
+ `playsInline = ${playsInline ? "true" : "false"}`
10354
+ ];
10355
+ if (objectFit !== "cover") {
10356
+ videoArgs.push(`posterFit = ${quoteKotlinString(objectFit)}`);
10357
+ }
10358
+ if (objectPosition !== "center") {
10359
+ videoArgs.push(`posterPosition = ${quoteKotlinString(objectPosition)}`);
10360
+ }
10361
+ videoArgs.push(`modifier = ${modifier}`);
10323
10362
  return [
10324
10363
  ...baseLines,
10325
- `${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})`
10364
+ `${indent7(level)}ElitVideoSurface(${videoArgs.join(", ")})`
10326
10365
  ];
10327
10366
  }
10367
+ if (node.component === "Math") {
10368
+ return renderComposeUnsupportedFallback(node, level, context, modifier, baseLines, "Math");
10369
+ }
10328
10370
  if (node.component === "Cell") {
10329
10371
  const style = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
10372
+ const hasExplicitWidth = hasExplicitNativeWidthStyle(style);
10330
10373
  const cellHints = {
10331
10374
  ...hints,
10332
- ...!hasExplicitNativeWidthStyle(style) ? { fillWidth: true } : {},
10375
+ ...!hasExplicitWidth ? { fillWidth: true } : {},
10333
10376
  ...!hasExplicitNativeHeightStyle(style) && hints.fillHeight ? { fillHeight: true } : {}
10334
10377
  };
10335
- return renderComposeContainerNode(node, level, context, cellHints, modifier, baseLines);
10378
+ let cellModifier = modifier;
10379
+ if (hints.parentFlexLayout === "Row" && !hasExplicitWidth) {
10380
+ cellModifier = cellModifier === "Modifier" ? "Modifier.weight(1f, fill = true)" : cellModifier.replace(/^Modifier\./, "Modifier.weight(1f, fill = true).");
10381
+ }
10382
+ return renderComposeContainerNode(node, level, context, cellHints, cellModifier, baseLines);
10336
10383
  }
10337
10384
  if (node.children.length > 0 || node.component === "Screen") {
10338
10385
  return renderComposeContainerNode(node, level, context, hints, modifier, baseLines);
@@ -10349,11 +10396,13 @@ ${varDeclarations}
10349
10396
  if (context.helperFlags.has("bridge")) {
10350
10397
  helpers.push("");
10351
10398
  helpers.push("private object ElitNativeBridge {");
10399
+ helpers.push(" var onAction: ((String, String?, String?) -> Unit)? = null");
10400
+ helpers.push(" var onNavigate: ((String) -> Unit)? = null");
10352
10401
  helpers.push(" fun dispatch(action: String? = null, route: String? = null, payloadJson: String? = null) {");
10353
10402
  helpers.push(' android.util.Log.d("ElitNativeBridge", listOfNotNull(action, route, payloadJson).joinToString(" | "))');
10354
10403
  helpers.push(" }");
10355
10404
  helpers.push("");
10356
- 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 {");
10405
+ 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 {");
10357
10406
  helpers.push(" val parts = mutableListOf<String>()");
10358
10407
  helpers.push(' parts += ""event":"$event""');
10359
10408
  helpers.push(' parts += ""sourceTag":"$sourceTag""');
@@ -10377,33 +10426,50 @@ ${varDeclarations}
10377
10426
  helpers.push("}");
10378
10427
  }
10379
10428
  if (context.helperFlags.has("backgroundImage") || context.helperFlags.has("imagePlaceholder")) {
10429
+ helpers.push("");
10430
+ helpers.push("private fun elitLoadBackgroundBitmap(view: android.widget.ImageView, source: String, repeatMode: String, backgroundSize: String, backgroundPosition: String) {");
10431
+ helpers.push(" if (source.isBlank()) return");
10432
+ helpers.push(' val tileModeX = if (repeatMode == "repeat-y") android.graphics.Shader.TileMode.CLAMP else android.graphics.Shader.TileMode.REPEAT');
10433
+ helpers.push(' val tileModeY = if (repeatMode == "repeat-x") android.graphics.Shader.TileMode.CLAMP else android.graphics.Shader.TileMode.REPEAT');
10434
+ helpers.push(" view.scaleType = android.widget.ImageView.ScaleType.CENTER_CROP");
10435
+ helpers.push(' if (repeatMode == "no-repeat" || repeatMode == "round") {');
10436
+ helpers.push(" view.setImageURI(android.net.Uri.parse(source))");
10437
+ helpers.push(" } else {");
10438
+ helpers.push(" val bitmap = android.graphics.BitmapFactory.decodeStream(android.net.Uri.parse(source).let { runCatching { view.context.contentResolver.openInputStream(it) }.getOrNull() })");
10439
+ helpers.push(" if (bitmap != null) view.setImageDrawable(android.graphics.drawable.BitmapDrawable(view.resources, bitmap).apply { tileModeX = tileModeX; tileModeY = tileModeY })");
10440
+ helpers.push(" }");
10441
+ helpers.push("}");
10380
10442
  helpers.push("");
10381
10443
  helpers.push("@Composable");
10382
10444
  helpers.push('private fun ElitBackgroundImage(source: String, backgroundSize: String = "cover", backgroundPosition: String = "center", backgroundRepeat: String = "no-repeat", modifier: Modifier = Modifier) {');
10383
10445
  helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10384
10446
  helpers.push(" factory = { context -> android.widget.ImageView(context).apply {");
10385
- helpers.push(" scaleType = android.widget.ImageView.ScaleType.CENTER_CROP");
10386
- helpers.push(" if (source.isNotBlank()) setImageURI(android.net.Uri.parse(source))");
10447
+ helpers.push(" elitLoadBackgroundBitmap(this, source, backgroundRepeat, backgroundSize, backgroundPosition)");
10387
10448
  helpers.push(" } },");
10388
10449
  helpers.push(" modifier = modifier,");
10389
10450
  helpers.push(" )");
10390
10451
  helpers.push("}");
10391
10452
  helpers.push("");
10392
10453
  helpers.push("@Composable");
10393
- helpers.push('private fun ElitImageSurface(source: String, label: String, alt: String? = null, modifier: Modifier = Modifier, objectFit: String = "cover", objectPosition: String = "center") {');
10454
+ helpers.push('private fun ElitImageSurface(source: String, label: String, contentDescription: String?, objectFit: String = "cover", objectPosition: String = "center", modifier: Modifier = Modifier) {');
10394
10455
  helpers.push(" if (source.isBlank()) {");
10395
10456
  helpers.push(" Box(modifier = modifier, contentAlignment = Alignment.Center) {");
10396
- helpers.push(" Text(text = alt ?: label)");
10457
+ helpers.push(" Text(text = contentDescription ?: label)");
10397
10458
  helpers.push(" }");
10398
10459
  helpers.push(" return");
10399
10460
  helpers.push(" }");
10400
- helpers.push(" ElitBackgroundImage(source = source, backgroundSize = objectFit, backgroundPosition = objectPosition, modifier = modifier)");
10461
+ helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10462
+ helpers.push(" factory = { context -> android.widget.ImageView(context).apply {");
10463
+ helpers.push(' elitLoadBackgroundBitmap(this, source, "no-repeat", objectFit, objectPosition)');
10464
+ helpers.push(" } },");
10465
+ helpers.push(" modifier = modifier,");
10466
+ helpers.push(" )");
10401
10467
  helpers.push("}");
10402
10468
  }
10403
10469
  if (context.helperFlags.has("webViewSurface")) {
10404
10470
  helpers.push("");
10405
10471
  helpers.push("@Composable");
10406
- helpers.push("private fun ElitWebViewSurface(source: String, label: String, modifier: Modifier = Modifier) {");
10472
+ helpers.push("private fun ElitWebViewSurface(source: String, label: String?, modifier: Modifier = Modifier) {");
10407
10473
  helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10408
10474
  helpers.push(" factory = { context -> android.webkit.WebView(context).apply {");
10409
10475
  helpers.push(" contentDescription = label");
@@ -10415,18 +10481,58 @@ ${varDeclarations}
10415
10481
  helpers.push("}");
10416
10482
  }
10417
10483
  if (context.helperFlags.has("mediaSurface")) {
10484
+ helpers.push("");
10485
+ helpers.push("private fun elitVideoPosterScaleType(posterFit: String, posterPosition: String): android.widget.ImageView.ScaleType = when (posterFit.trim().lowercase()) {");
10486
+ helpers.push(' "contain", "scale-down" -> when (posterPosition.trim().lowercase()) {');
10487
+ helpers.push(' "top", "leading", "top-leading", "bottom-leading" -> android.widget.ImageView.ScaleType.FIT_START');
10488
+ helpers.push(' "bottom", "trailing", "bottom-trailing" -> android.widget.ImageView.ScaleType.FIT_END');
10489
+ helpers.push(" else -> android.widget.ImageView.ScaleType.FIT_CENTER");
10490
+ helpers.push(" }");
10491
+ helpers.push(" else -> android.widget.ImageView.ScaleType.CENTER_CROP");
10492
+ helpers.push("}");
10418
10493
  helpers.push("");
10419
10494
  helpers.push("@Composable");
10420
- 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) {');
10495
+ 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) {');
10496
+ helpers.push(" // Android VideoView already renders inline; playsInline is retained for parity with iOS generation.");
10421
10497
  helpers.push(" Box(modifier = modifier, contentAlignment = Alignment.Center) {");
10422
- helpers.push(' Text(text = if (autoPlay) "Playing $label" else label)');
10498
+ helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10499
+ helpers.push(" factory = { context ->");
10500
+ helpers.push(" android.widget.ImageView(context).apply {");
10501
+ helpers.push(" val posterView = this");
10502
+ helpers.push(" posterView.contentDescription = label");
10503
+ helpers.push(" posterView.scaleType = elitVideoPosterScaleType(posterFit, posterPosition)");
10504
+ helpers.push(" if (poster != null) posterView.setImageURI(android.net.Uri.parse(poster))");
10505
+ helpers.push(" }");
10506
+ helpers.push(" },");
10507
+ helpers.push(" )");
10508
+ helpers.push(" if (controls) {");
10509
+ helpers.push(" // Hand off to platform chrome; ElitVideoSurface exposes controls for parity only.");
10510
+ helpers.push(" }");
10423
10511
  helpers.push(" }");
10424
10512
  helpers.push("}");
10425
10513
  helpers.push("");
10426
10514
  helpers.push("@Composable");
10427
10515
  helpers.push("private fun ElitAudioSurface(source: String, label: String, autoPlay: Boolean, loop: Boolean, muted: Boolean, modifier: Modifier = Modifier) {");
10428
- helpers.push(" Button(onClick = {}, modifier = modifier) {");
10429
- helpers.push(" Text(text = label)");
10516
+ helpers.push(" androidx.compose.ui.viewinterop.AndroidView(");
10517
+ helpers.push(" factory = { context ->");
10518
+ helpers.push(" android.media.MediaPlayer().apply {");
10519
+ helpers.push(" val mediaPlayer = this");
10520
+ helpers.push(" mediaPlayer.isLooping = loop");
10521
+ helpers.push(" mediaPlayer.setVolume(if (muted) 0f else 1f, if (muted) 0f else 1f)");
10522
+ helpers.push(" if (source.isNotBlank()) setDataSource(source)");
10523
+ helpers.push(" if (autoPlay) start()");
10524
+ helpers.push(" }");
10525
+ helpers.push(" },");
10526
+ helpers.push(" modifier = modifier,");
10527
+ helpers.push(" )");
10528
+ helpers.push("}");
10529
+ }
10530
+ if (context.helperFlags.has("unsupportedPlaceholder")) {
10531
+ helpers.push("");
10532
+ helpers.push("@Composable");
10533
+ helpers.push("private fun ElitUnsupported(label: String, sourceTag: String, modifier: Modifier = Modifier) {");
10534
+ helpers.push(" Box(modifier = modifier, contentAlignment = Alignment.Center) {");
10535
+ helpers.push(' Text(text = "$label ($sourceTag)")');
10430
10536
  helpers.push(" }");
10431
10537
  helpers.push("}");
10432
10538
  }
@@ -10457,11 +10563,27 @@ ${varDeclarations}
10457
10563
  resolvedStyles: styleData.resolvedStyles,
10458
10564
  styleContexts: styleData.styleContexts
10459
10565
  };
10460
- const bodyLines = tree.roots.length === 1 ? renderComposeNode(tree.roots[0], 1, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }) : [
10461
- " Column(modifier = Modifier.fillMaxSize()) {",
10462
- ...renderComposeChildren(tree.roots, 2, context, renderComposeNode, "Column", void 0, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }),
10463
- " }"
10464
- ];
10566
+ const singleRoot = tree.roots.length === 1 ? tree.roots[0] : null;
10567
+ const isSingleScreenRoot = singleRoot !== null && singleRoot.kind === "element" && singleRoot.component === "Screen";
10568
+ if (isSingleScreenRoot) {
10569
+ context.helperFlags.add("screenRoot");
10570
+ }
10571
+ let bodyLines;
10572
+ if (isSingleScreenRoot) {
10573
+ bodyLines = [
10574
+ " Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {",
10575
+ ...renderComposeNode(tree.roots[0], 2, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }),
10576
+ " }"
10577
+ ];
10578
+ } else if (tree.roots.length === 1) {
10579
+ bodyLines = renderComposeNode(tree.roots[0], 1, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight });
10580
+ } else {
10581
+ bodyLines = [
10582
+ " Column(modifier = Modifier.fillMaxSize()) {",
10583
+ ...renderComposeChildren(tree.roots, 2, context, renderComposeNode, "Column", void 0, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }),
10584
+ " }"
10585
+ ];
10586
+ }
10465
10587
  const lines = [];
10466
10588
  if (resolvedOptions.includePackage) {
10467
10589
  lines.push(`package ${resolvedOptions.packageName}`);
@@ -11361,6 +11483,18 @@ ${varDeclarations}
11361
11483
  ];
11362
11484
  return [...baseLines, ...appendSwiftUIOverlays(contentLines, overlays, level)];
11363
11485
  }
11486
+ function renderSwiftUnsupportedFallback(node, level, context, baseLines, modifiers, label) {
11487
+ context.helperFlags.add("unsupportedPlaceholder");
11488
+ const sourceTag = node.sourceTag ?? node.component.toLowerCase();
11489
+ return appendSwiftUIModifiers(
11490
+ [
11491
+ ...baseLines,
11492
+ `${indent11(level)}elitUnsupportedPlaceholder(label: ${quoteSwiftString(label)}, sourceTag: ${quoteSwiftString(sourceTag)})`
11493
+ ],
11494
+ modifiers,
11495
+ level
11496
+ );
11497
+ }
11364
11498
  function renderSwiftUINode(node, level, context, hints = {}) {
11365
11499
  if (node.kind === "text") {
11366
11500
  return renderTextView(node, level, context);
@@ -11394,10 +11528,21 @@ ${varDeclarations}
11394
11528
  const imageStyle = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
11395
11529
  const objectFit = resolveNativeObjectFitStyle(imageStyle);
11396
11530
  const objectPosition = resolveNativeObjectPositionStyle(imageStyle);
11531
+ const imageArgs = [
11532
+ `source: ${quoteSwiftString(source)}`,
11533
+ `label: ${quoteSwiftString(fallbackLabel)}`,
11534
+ `alt: ${alt ? quoteSwiftString(alt) : "nil"}`
11535
+ ];
11536
+ if (objectFit !== "cover") {
11537
+ imageArgs.push(`objectFit: ${quoteSwiftString(objectFit)}`);
11538
+ }
11539
+ if (objectPosition !== "center") {
11540
+ imageArgs.push(`objectPosition: ${quoteSwiftString(objectPosition)}`);
11541
+ }
11397
11542
  return appendSwiftUIModifiers(
11398
11543
  [
11399
11544
  ...baseLines,
11400
- `${indent11(level)}elitImageSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(fallbackLabel)}, alt: ${alt ? quoteSwiftString(alt) : "nil"}, objectFit: ${quoteSwiftString(objectFit)}, objectPosition: ${quoteSwiftString(objectPosition)})`
11545
+ `${indent11(level)}elitImageSurface(${imageArgs.join(", ")})`
11401
11546
  ],
11402
11547
  modifiers,
11403
11548
  level
@@ -11432,33 +11577,38 @@ ${varDeclarations}
11432
11577
  return [...baseLines, ...appendSwiftUIModifiers(canvas.lines, canvas.modifiers, level)];
11433
11578
  }
11434
11579
  if (node.component === "WebView") {
11580
+ const source = resolveNativeSurfaceSource(node);
11581
+ if (!source) {
11582
+ return renderSwiftUnsupportedFallback(node, level, context, baseLines, modifiers, "WebView");
11583
+ }
11435
11584
  context.helperFlags.add("webViewSurface");
11436
- const source = resolveNativeSurfaceSource(node) ?? "";
11437
11585
  const label2 = resolveNativeAccessibilityLabel(node) ?? "Web content";
11438
11586
  return appendSwiftUIModifiers(
11439
11587
  [
11440
11588
  ...baseLines,
11441
- `${indent11(level)}elitWebViewSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(label2)})`
11589
+ `${indent11(level)}ElitWebViewSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(label2)})`
11442
11590
  ],
11443
11591
  modifiers,
11444
11592
  level
11445
11593
  );
11446
11594
  }
11447
11595
  if (node.component === "Media") {
11596
+ const source = resolveNativeSurfaceSource(node);
11597
+ if (!source) {
11598
+ return renderSwiftUnsupportedFallback(node, level, context, baseLines, modifiers, "Media");
11599
+ }
11448
11600
  context.helperFlags.add("mediaSurface");
11449
- const source = resolveNativeSurfaceSource(node) ?? "";
11450
11601
  const label2 = resolveNativeMediaLabel(node);
11451
11602
  const style = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
11452
11603
  const objectFit = resolveNativeObjectFitStyle(style);
11453
11604
  const objectPosition = resolveNativeObjectPositionStyle(style);
11454
11605
  const autoPlay = toNativeBoolean(node.props.autoPlay ?? node.props.autoplay);
11455
- const loop = toNativeBoolean(node.props.loop);
11456
11606
  const muted = isNativeMuted(node);
11457
11607
  if (node.sourceTag === "audio") {
11458
11608
  return appendSwiftUIModifiers(
11459
11609
  [
11460
11610
  ...baseLines,
11461
- `${indent11(level)}ElitAudioSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(label2)}, autoPlay: ${autoPlay ? "true" : "false"}, loop: ${loop ? "true" : "false"}, muted: ${muted ? "true" : "false"})`
11611
+ `${indent11(level)}ElitAudioSurface(source: ${quoteSwiftString(source)}, label: ${quoteSwiftString(label2)}, autoPlay: ${autoPlay ? "true" : "false"}, muted: ${muted ? "true" : "false"})`
11462
11612
  ],
11463
11613
  modifiers,
11464
11614
  level
@@ -11467,15 +11617,33 @@ ${varDeclarations}
11467
11617
  const poster = resolveNativeVideoPoster(node);
11468
11618
  const controls = shouldNativeShowVideoControls(node);
11469
11619
  const playsInline = shouldNativePlayInline(node);
11620
+ const videoArgs = [
11621
+ `source: ${quoteSwiftString(source)}`,
11622
+ `label: ${quoteSwiftString(label2)}`,
11623
+ `autoPlay: ${autoPlay ? "true" : "false"}`,
11624
+ `muted: ${muted ? "true" : "false"}`,
11625
+ `controls: ${controls ? "true" : "false"}`,
11626
+ `poster: ${poster ? quoteSwiftString(poster) : "nil"}`,
11627
+ `playsInline: ${playsInline ? "true" : "false"}`
11628
+ ];
11629
+ if (objectFit !== "cover") {
11630
+ videoArgs.push(`posterFit: ${quoteSwiftString(objectFit)}`);
11631
+ }
11632
+ if (objectPosition !== "center") {
11633
+ videoArgs.push(`posterPosition: ${quoteSwiftString(objectPosition)}`);
11634
+ }
11470
11635
  return appendSwiftUIModifiers(
11471
11636
  [
11472
11637
  ...baseLines,
11473
- `${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)})`
11638
+ `${indent11(level)}ElitVideoSurface(${videoArgs.join(", ")})`
11474
11639
  ],
11475
11640
  modifiers,
11476
11641
  level
11477
11642
  );
11478
11643
  }
11644
+ if (node.component === "Math") {
11645
+ return renderSwiftUnsupportedFallback(node, level, context, baseLines, modifiers, "Math");
11646
+ }
11479
11647
  if (node.component === "Cell") {
11480
11648
  const style = getStyleObject(node, context.resolvedStyles, context.styleResolveOptions);
11481
11649
  const cellHints = {
@@ -11501,6 +11669,8 @@ ${varDeclarations}
11501
11669
  if (context.helperFlags.has("bridge")) {
11502
11670
  helpers.push("");
11503
11671
  helpers.push("private enum ElitNativeBridge {");
11672
+ helpers.push(" static var onAction: ((String, String?, String?) -> Void)?");
11673
+ helpers.push(" static var onNavigate: ((String) -> Void)?");
11504
11674
  helpers.push(" static func dispatch(action: String? = nil, route: String? = nil, payloadJson: String? = nil) {");
11505
11675
  helpers.push(' print("ElitNativeBridge", action ?? "", route ?? "", payloadJson ?? "")');
11506
11676
  helpers.push(" }");
@@ -11525,6 +11695,13 @@ ${varDeclarations}
11525
11695
  helpers.push("}");
11526
11696
  }
11527
11697
  if (context.helperFlags.has("backgroundImage") || context.helperFlags.has("imagePlaceholder")) {
11698
+ helpers.push("");
11699
+ helpers.push("@ViewBuilder");
11700
+ helpers.push('private func elitBackgroundImage(_ image: Image, backgroundSize: String = "cover", backgroundPosition: String = "center", backgroundRepeat: String = "no-repeat") -> some View {');
11701
+ helpers.push(" image");
11702
+ helpers.push(" .resizable()");
11703
+ helpers.push(" .scaledToFill()");
11704
+ helpers.push("}");
11528
11705
  helpers.push("");
11529
11706
  helpers.push("@ViewBuilder");
11530
11707
  helpers.push('private func elitBackgroundImageSurface(source: String, objectFit: String = "cover", objectPosition: String = "center") -> some View {');
@@ -11532,9 +11709,7 @@ ${varDeclarations}
11532
11709
  helpers.push(" AsyncImage(url: url) { phase in");
11533
11710
  helpers.push(" switch phase {");
11534
11711
  helpers.push(" case .success(let image):");
11535
- helpers.push(" image");
11536
- helpers.push(" .resizable()");
11537
- helpers.push(" .scaledToFill()");
11712
+ helpers.push(' elitBackgroundImage(image, backgroundSize: objectFit, backgroundPosition: objectPosition, backgroundRepeat: "no-repeat")');
11538
11713
  helpers.push(" default:");
11539
11714
  helpers.push(" Color.clear");
11540
11715
  helpers.push(" }");
@@ -11545,7 +11720,7 @@ ${varDeclarations}
11545
11720
  helpers.push("}");
11546
11721
  helpers.push("");
11547
11722
  helpers.push("@ViewBuilder");
11548
- helpers.push('private func elitImageSurface(source: String, label: String, alt: String? = nil, objectFit: String = "cover", objectPosition: String = "center") -> some View {');
11723
+ helpers.push('private func elitImageSurface(source: String, label: String, alt: String?, objectFit: String = "cover", objectPosition: String = "center") -> some View {');
11549
11724
  helpers.push(" if source.isEmpty {");
11550
11725
  helpers.push(" Text(alt ?? label)");
11551
11726
  helpers.push(" .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)");
@@ -11556,31 +11731,131 @@ ${varDeclarations}
11556
11731
  }
11557
11732
  if (context.helperFlags.has("webViewSurface")) {
11558
11733
  helpers.push("");
11559
- helpers.push("@ViewBuilder");
11560
- helpers.push("private func elitWebViewSurface(source: String, label: String) -> some View {");
11561
- helpers.push(" VStack(alignment: .leading, spacing: 8) {");
11562
- helpers.push(" Text(label)");
11563
- helpers.push(" Text(source)");
11564
- helpers.push(" .font(.caption)");
11565
- helpers.push(" .foregroundStyle(.secondary)");
11734
+ helpers.push("struct ElitWebViewSurface: UIViewRepresentable {");
11735
+ helpers.push(" let source: String");
11736
+ helpers.push(" let label: String?");
11737
+ helpers.push("");
11738
+ helpers.push(" func makeUIView(context: Context) -> WKWebView {");
11739
+ helpers.push(" let webView = WKWebView()");
11740
+ helpers.push(" webView.accessibilityLabel = label");
11741
+ helpers.push(" if let url = URL(string: source), !source.isEmpty { webView.load(URLRequest(url: url)) }");
11742
+ helpers.push(" return webView");
11566
11743
  helpers.push(" }");
11744
+ helpers.push("");
11745
+ helpers.push(" func updateUIView(_ uiView: WKWebView, context: Context) {}");
11567
11746
  helpers.push("}");
11568
11747
  }
11569
11748
  if (context.helperFlags.has("mediaSurface")) {
11749
+ helpers.push("");
11750
+ helpers.push("private func elitPosterAlignment(_ posterPosition: String) -> Alignment {");
11751
+ helpers.push(" switch posterPosition.lowercased() {");
11752
+ helpers.push(' case "top", "leading", "top-leading":');
11753
+ helpers.push(" return .topLeading");
11754
+ helpers.push(' case "bottom", "trailing", "bottom-trailing":');
11755
+ helpers.push(" return .bottomTrailing");
11756
+ helpers.push(' case "center":');
11757
+ helpers.push(" return .center");
11758
+ helpers.push(" default:");
11759
+ helpers.push(" return .center");
11760
+ helpers.push(" }");
11761
+ helpers.push("}");
11570
11762
  helpers.push("");
11571
11763
  helpers.push("@ViewBuilder");
11572
- 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 {');
11573
- helpers.push(" VStack(alignment: .leading, spacing: 8) {");
11574
- helpers.push(" if let poster, !poster.isEmpty {");
11575
- helpers.push(" elitBackgroundImageSurface(source: poster, objectFit: posterFit, objectPosition: posterPosition)");
11764
+ helpers.push("private func elitPosterImage(_ image: Image, posterFit: String, posterPosition: String) -> some View {");
11765
+ helpers.push(" let resizable = image.resizable()");
11766
+ helpers.push(" switch posterFit.lowercased() {");
11767
+ helpers.push(' case "contain":');
11768
+ helpers.push(" return AnyView(resizable.scaledToFit().frame(maxWidth: .infinity, maxHeight: .infinity, alignment: elitPosterAlignment(posterPosition)))");
11769
+ helpers.push(' case "fill", "cover":');
11770
+ helpers.push(" return AnyView(resizable.scaledToFill().frame(maxWidth: .infinity, maxHeight: .infinity, alignment: elitPosterAlignment(posterPosition)))");
11771
+ helpers.push(" default:");
11772
+ helpers.push(" return AnyView(resizable.scaledToFill())");
11773
+ helpers.push(" }");
11774
+ helpers.push("}");
11775
+ helpers.push("");
11776
+ helpers.push("struct ElitVideoPlayerController: UIViewControllerRepresentable {");
11777
+ helpers.push(" let source: String");
11778
+ helpers.push(" let autoPlay: Bool");
11779
+ helpers.push(" let muted: Bool");
11780
+ helpers.push(" let controls: Bool");
11781
+ helpers.push(" let playsInline: Bool");
11782
+ helpers.push("");
11783
+ helpers.push(" func makeUIViewController(context: Context) -> AVPlayerViewController {");
11784
+ helpers.push(" let controller = AVPlayerViewController()");
11785
+ helpers.push(" if let url = URL(string: source) {");
11786
+ helpers.push(" let player = AVPlayer(url: url)");
11787
+ helpers.push(" let resolvedPlayer = player");
11788
+ helpers.push(" resolvedPlayer.isMuted = muted");
11789
+ helpers.push(" controller.player = resolvedPlayer");
11790
+ helpers.push(" if autoPlay { resolvedPlayer.play() }");
11576
11791
  helpers.push(" }");
11577
- helpers.push(" Text(label)");
11792
+ helpers.push(" controller.showsPlaybackControls = controls");
11793
+ helpers.push(" controller.entersFullScreenWhenPlaybackBegins = !playsInline");
11794
+ helpers.push(" return controller");
11578
11795
  helpers.push(" }");
11796
+ helpers.push("");
11797
+ helpers.push(" func updateUIViewController(_ controller: AVPlayerViewController, context: Context) {}");
11579
11798
  helpers.push("}");
11799
+ helpers.push("");
11800
+ helpers.push("struct ElitVideoSurface: View {");
11801
+ helpers.push(" let source: String");
11802
+ helpers.push(" let label: String");
11803
+ helpers.push(" let autoPlay: Bool");
11804
+ helpers.push(" let muted: Bool");
11805
+ helpers.push(" let controls: Bool");
11806
+ helpers.push(" let poster: String?");
11807
+ helpers.push(" let playsInline: Bool");
11808
+ helpers.push(" let posterFit: String");
11809
+ helpers.push(" let posterPosition: String");
11810
+ helpers.push("");
11811
+ helpers.push(" var body: some View {");
11812
+ helpers.push(" ZStack {");
11813
+ helpers.push(" if let poster, !poster.isEmpty, let posterURL = URL(string: poster) {");
11814
+ helpers.push(" AsyncImage(url: posterURL) { phase in");
11815
+ helpers.push(" switch phase {");
11816
+ helpers.push(" case .success(let image):");
11817
+ helpers.push(" elitPosterImage(image, posterFit: posterFit, posterPosition: posterPosition)");
11818
+ helpers.push(" default:");
11819
+ helpers.push(" Color.clear");
11820
+ helpers.push(" }");
11821
+ helpers.push(" }");
11822
+ helpers.push(" }");
11823
+ helpers.push(" ElitVideoPlayerController(source: source, autoPlay: autoPlay, muted: muted, controls: controls, playsInline: playsInline)");
11824
+ helpers.push(" .accessibilityLabel(label)");
11825
+ helpers.push(" }");
11826
+ helpers.push(" }");
11827
+ helpers.push("}");
11828
+ helpers.push("");
11829
+ helpers.push("struct ElitAudioSurface: View {");
11830
+ helpers.push(" let source: String");
11831
+ helpers.push(" let label: String");
11832
+ helpers.push(" let autoPlay: Bool");
11833
+ helpers.push(" let muted: Bool");
11834
+ helpers.push("");
11835
+ helpers.push(" var body: some View {");
11836
+ helpers.push(" VStack {");
11837
+ helpers.push(" if let url = URL(string: source), !source.isEmpty {");
11838
+ helpers.push(" AVPlayer(url: url).let { resolvedPlayer in");
11839
+ helpers.push(" resolvedPlayer.isMuted = muted");
11840
+ helpers.push(" if autoPlay { resolvedPlayer.play() }");
11841
+ helpers.push(" }");
11842
+ helpers.push(" }");
11843
+ helpers.push(' Label(label, systemImage: "waveform")');
11844
+ helpers.push(" .accessibilityLabel(label)");
11845
+ helpers.push(" }");
11846
+ helpers.push(" }");
11847
+ helpers.push("}");
11848
+ }
11849
+ if (context.helperFlags.has("unsupportedPlaceholder")) {
11580
11850
  helpers.push("");
11581
11851
  helpers.push("@ViewBuilder");
11582
- helpers.push("private func ElitAudioSurface(source: String, label: String, autoPlay: Bool, loop: Bool, muted: Bool) -> some View {");
11583
- helpers.push(' Label(label, systemImage: "waveform")');
11852
+ helpers.push("private func elitUnsupportedPlaceholder(label: String, sourceTag: String) -> some View {");
11853
+ helpers.push(" VStack(spacing: 8) {");
11854
+ helpers.push(" Text(label)");
11855
+ helpers.push(" Text(sourceTag)");
11856
+ helpers.push(" .font(.caption)");
11857
+ helpers.push(" .foregroundStyle(.secondary)");
11858
+ helpers.push(" }");
11584
11859
  helpers.push("}");
11585
11860
  }
11586
11861
  return helpers;
@@ -11608,17 +11883,30 @@ ${varDeclarations}
11608
11883
  resolvedStyles: styleData.resolvedStyles,
11609
11884
  styleContexts: styleData.styleContexts
11610
11885
  };
11611
- const bodyLines = tree.roots.length === 1 ? renderSwiftUINode(tree.roots[0], 2, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }) : [
11886
+ const singleRoot = tree.roots.length === 1 ? tree.roots[0] : null;
11887
+ const isSingleScreenRoot = singleRoot !== null && singleRoot.kind === "element" && singleRoot.component === "Screen";
11888
+ const innerLines = tree.roots.length === 1 ? renderSwiftUINode(tree.roots[0], 2, context, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }) : [
11612
11889
  " VStack(alignment: .leading, spacing: 0) {",
11613
11890
  ...renderSwiftUIChildren(tree.roots, 3, context, renderSwiftUINode, "VStack", void 0, { availableWidth: styleResolveOptions.viewportWidth, availableHeight: styleResolveOptions.viewportHeight }),
11614
11891
  " }"
11615
11892
  ];
11893
+ const bodyLines = isSingleScreenRoot ? [
11894
+ " ScrollView {",
11895
+ ...innerLines.map((line) => " " + line),
11896
+ " }"
11897
+ ] : innerLines;
11616
11898
  const lines = [];
11617
11899
  if (resolvedOptions.includeImports) {
11618
11900
  lines.push("import SwiftUI");
11619
11901
  if (context.helperFlags.has("backgroundImage") || context.helperFlags.has("imagePlaceholder") || context.helperFlags.has("openUrlHandler")) {
11620
11902
  lines.push("import Foundation");
11621
11903
  }
11904
+ if (context.helperFlags.has("webViewSurface")) {
11905
+ lines.push("import WebKit");
11906
+ }
11907
+ if (context.helperFlags.has("mediaSurface")) {
11908
+ lines.push("import AVKit");
11909
+ }
11622
11910
  lines.push("");
11623
11911
  }
11624
11912
  lines.push(`struct ${resolvedOptions.structName}: View {`);