elit 3.6.9 → 3.7.1

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