@jetstart/cli 1.1.3 → 1.1.4

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.
@@ -472,45 +472,619 @@ sdk.dir=${androidSdkPath.replace(/\\/g, '\\\\')}
472
472
  async function generateHotReload(projectPath, packageName) {
473
473
  const packagePath = packageName.replace(/\./g, '/');
474
474
  const hotReloadPath = path_1.default.join(projectPath, 'app/src/main/java', packagePath, 'HotReload.kt');
475
- // Copy from my-app template
476
- const sourceFile = path_1.default.join(__dirname, '../../../../my-app/app/src/main/java/com/jetstart/myapp/HotReload.kt');
477
- try {
478
- let content = await fs_extra_1.default.readFile(sourceFile, 'utf-8');
479
- // Replace package name
480
- content = content.replace(/package com\.jetstart\.myapp/g, `package ${packageName}`);
481
- await fs_extra_1.default.ensureDir(path_1.default.dirname(hotReloadPath));
482
- await fs_extra_1.default.writeFile(hotReloadPath, content);
483
- }
484
- catch (error) {
485
- console.warn('[Warning] Could not copy HotReload.kt from my-app. You may need to add it manually.');
475
+ const content = `package ${packageName}
476
+
477
+ import android.app.Activity
478
+ import android.content.Intent
479
+ import android.net.Uri
480
+ import android.os.Build
481
+ import android.util.Log
482
+ import androidx.core.content.FileProvider
483
+ import okhttp3.*
484
+ import org.json.JSONObject
485
+ import java.io.File
486
+ import java.io.IOException
487
+
488
+ /**
489
+ * Hot Reload Manager
490
+ * Connects to JetStart dev server and automatically reloads the app when code changes
491
+ */
492
+ object HotReload {
493
+ private const val TAG = "HotReload"
494
+ private var webSocket: WebSocket? = null
495
+ private var activity: Activity? = null
496
+ private var connectionTime: Long = 0
497
+ private var ignoreFirstBuild = true
498
+ private val httpClient = OkHttpClient()
499
+
500
+ fun connect(activity: Activity, serverUrl: String, sessionId: String) {
501
+ this.activity = activity
502
+
503
+ val wsUrl = serverUrl.replace("http://", "ws://").replace("https://", "wss://")
504
+ Log.d(TAG, "Connecting to dev server: \$wsUrl")
505
+
506
+ val client = OkHttpClient()
507
+ val request = Request.Builder()
508
+ .url(wsUrl)
509
+ .build()
510
+
511
+ webSocket = client.newWebSocket(request, object : WebSocketListener() {
512
+ override fun onOpen(webSocket: WebSocket, response: Response) {
513
+ Log.d(TAG, "WebSocket connected")
514
+ connectionTime = System.currentTimeMillis()
515
+ ignoreFirstBuild = true // Ignore the first build-complete after connecting
516
+
517
+ // Send connect message
518
+ val connectMsg = JSONObject().apply {
519
+ put("type", "client:connect")
520
+ put("sessionId", sessionId)
521
+ put("clientType", "test-app")
522
+ }
523
+ webSocket.send(connectMsg.toString())
524
+ }
525
+
526
+ override fun onMessage(webSocket: WebSocket, text: String) {
527
+ Log.d(TAG, "Received: \$text")
528
+
529
+ try {
530
+ val json = JSONObject(text)
531
+ val type = json.getString("type")
532
+
533
+ when (type) {
534
+ "core:ui-update" -> {
535
+ // DSL-based hot reload (FAST)
536
+ if (ignoreFirstBuild) {
537
+ Log.d(TAG, "Ignoring first UI update (old build)")
538
+ ignoreFirstBuild = false
539
+ return@onMessage
540
+ }
541
+
542
+ val timestamp = json.optLong("timestamp", 0)
543
+ val dslContent = json.optString("dslContent", "")
544
+
545
+ Log.d(TAG, "UI update received at \$timestamp, connection at \$connectionTime")
546
+
547
+ // Only update if changes happened AFTER we connected
548
+ if (timestamp > connectionTime && dslContent.isNotEmpty()) {
549
+ Log.d(TAG, "New UI update detected, recomposing (\${dslContent.length} bytes)")
550
+
551
+ // Update DSL on main thread
552
+ activity?.runOnUiThread {
553
+ DSLInterpreter.updateDSL(dslContent)
554
+ }
555
+ } else {
556
+ Log.d(TAG, "Ignoring old UI update")
557
+ }
558
+ }
559
+
560
+ "core:reload" -> {
561
+ Log.d(TAG, "Reload triggered!")
562
+ // Restart activity on main thread
563
+ activity?.runOnUiThread {
564
+ activity?.recreate()
565
+ }
566
+ }
567
+
568
+ "core:build-complete" -> {
569
+ // Full APK rebuild (SLOW - fallback for non-UI changes)
570
+ if (ignoreFirstBuild) {
571
+ Log.d(TAG, "Ignoring first build-complete (old build)")
572
+ ignoreFirstBuild = false
573
+ return@onMessage
574
+ }
575
+
576
+ val timestamp = json.optLong("timestamp", 0)
577
+ val downloadUrl = json.optString("downloadUrl", "")
578
+
579
+ Log.d(TAG, "Build complete at \$timestamp, connection at \$connectionTime")
580
+ Log.d(TAG, "Download URL: \$downloadUrl")
581
+
582
+ // Only reload if build happened AFTER we connected
583
+ if (timestamp > connectionTime && downloadUrl.isNotEmpty()) {
584
+ Log.d(TAG, "New build detected, downloading and installing APK")
585
+ downloadAndInstallApk(downloadUrl)
586
+ } else {
587
+ Log.d(TAG, "Ignoring old build (timestamp before connection)")
588
+ }
589
+ }
590
+ }
591
+ } catch (e: Exception) {
592
+ Log.e(TAG, "Failed to parse message: \${e.message}")
593
+ }
594
+ }
595
+
596
+ override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
597
+ Log.e(TAG, "WebSocket error: \${t.message}")
598
+ // Auto-reconnect after 5 seconds
599
+ android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
600
+ connect(activity!!, serverUrl, sessionId)
601
+ }, 5000)
602
+ }
603
+
604
+ override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
605
+ Log.d(TAG, "WebSocket closed: \$reason")
606
+ }
607
+ })
608
+ }
609
+
610
+ fun disconnect() {
611
+ webSocket?.close(1000, "App closing")
612
+ webSocket = null
613
+ activity = null
486
614
  }
615
+
616
+ private fun downloadAndInstallApk(downloadUrl: String) {
617
+ Log.d(TAG, "Starting APK download from: \$downloadUrl")
618
+
619
+ val request = Request.Builder()
620
+ .url(downloadUrl)
621
+ .build()
622
+
623
+ httpClient.newCall(request).enqueue(object : Callback {
624
+ override fun onFailure(call: Call, e: IOException) {
625
+ Log.e(TAG, "APK download failed: \${e.message}")
626
+ }
627
+
628
+ override fun onResponse(call: Call, response: Response) {
629
+ if (!response.isSuccessful) {
630
+ Log.e(TAG, "APK download failed with code: \${response.code}")
631
+ return
632
+ }
633
+
634
+ try {
635
+ val apkData = response.body?.bytes()
636
+ if (apkData == null) {
637
+ Log.e(TAG, "APK data is null")
638
+ return
639
+ }
640
+
641
+ Log.d(TAG, "APK downloaded successfully (\${apkData.size} bytes)")
642
+
643
+ // Save APK to cache directory
644
+ val cacheDir = activity?.cacheDir
645
+ if (cacheDir == null) {
646
+ Log.e(TAG, "Cache directory is null")
647
+ return
648
+ }
649
+
650
+ val apkFile = File(cacheDir, "update.apk")
651
+ apkFile.outputStream().use { output ->
652
+ output.write(apkData)
653
+ }
654
+
655
+ Log.d(TAG, "APK saved to: \${apkFile.absolutePath}")
656
+
657
+ // Install APK on main thread
658
+ activity?.runOnUiThread {
659
+ installApk(apkFile)
660
+ }
661
+ } catch (e: Exception) {
662
+ Log.e(TAG, "Failed to save/install APK: \${e.message}")
663
+ }
664
+ }
665
+ })
666
+ }
667
+
668
+ private fun installApk(apkFile: File) {
669
+ try {
670
+ val context = activity ?: return
671
+
672
+ val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
673
+ FileProvider.getUriForFile(
674
+ context,
675
+ "\${context.packageName}.fileprovider",
676
+ apkFile
677
+ )
678
+ } else {
679
+ Uri.fromFile(apkFile)
680
+ }
681
+
682
+ val intent = Intent(Intent.ACTION_VIEW).apply {
683
+ setDataAndType(uri, "application/vnd.android.package-archive")
684
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
685
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
686
+ }
687
+
688
+ context.startActivity(intent)
689
+ Log.d(TAG, "Installation intent started")
690
+ } catch (e: Exception) {
691
+ Log.e(TAG, "Failed to install APK: \${e.message}")
692
+ }
693
+ }
694
+ }
695
+ `;
696
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(hotReloadPath));
697
+ await fs_extra_1.default.writeFile(hotReloadPath, content);
487
698
  }
488
699
  async function generateDSLInterpreter(projectPath, packageName) {
489
700
  const packagePath = packageName.replace(/\./g, '/');
490
701
  const interpreterPath = path_1.default.join(projectPath, 'app/src/main/java', packagePath, 'DSLInterpreter.kt');
491
- const sourceFile = path_1.default.join(__dirname, '../../../../my-app/app/src/main/java/com/jetstart/myapp/DSLInterpreter.kt');
492
- try {
493
- let content = await fs_extra_1.default.readFile(sourceFile, 'utf-8');
494
- content = content.replace(/package com\.jetstart\.myapp/g, `package ${packageName}`);
495
- await fs_extra_1.default.ensureDir(path_1.default.dirname(interpreterPath));
496
- await fs_extra_1.default.writeFile(interpreterPath, content);
702
+ const content = `package ${packageName}
703
+
704
+ import android.util.Log
705
+ import androidx.compose.foundation.layout.*
706
+ import androidx.compose.material3.*
707
+ import androidx.compose.runtime.*
708
+ import androidx.compose.ui.Alignment
709
+ import androidx.compose.ui.Modifier
710
+ import androidx.compose.ui.graphics.Color
711
+ import androidx.compose.ui.text.font.FontWeight
712
+ import androidx.compose.ui.unit.dp
713
+ import kotlinx.coroutines.flow.MutableStateFlow
714
+ import kotlinx.coroutines.flow.StateFlow
715
+
716
+ /**
717
+ * DSL Interpreter
718
+ * Converts JSON DSL to Compose UI at runtime
719
+ */
720
+ object DSLInterpreter {
721
+ private const val TAG = "DSLInterpreter"
722
+
723
+ private val _currentDSL = MutableStateFlow<UIDefinition?>(null)
724
+ val currentDSL: StateFlow<UIDefinition?> = _currentDSL
725
+
726
+ /**
727
+ * Update the current DSL definition
728
+ */
729
+ fun updateDSL(jsonString: String) {
730
+ try {
731
+ val definition = parseUIDefinition(jsonString)
732
+ _currentDSL.value = definition
733
+ Log.d(TAG, "DSL updated successfully")
734
+ } catch (e: Exception) {
735
+ Log.e(TAG, "Failed to parse DSL: \${e.message}", e)
736
+ }
737
+ }
738
+
739
+ /**
740
+ * Render DSL as Compose UI
741
+ */
742
+ @Composable
743
+ fun RenderDSL(definition: UIDefinition) {
744
+ RenderElement(definition.screen)
745
+ }
746
+
747
+ /**
748
+ * Render individual DSL element
749
+ */
750
+ @Composable
751
+ fun RenderElement(element: DSLElement) {
752
+ when (element.type) {
753
+ "Column" -> RenderColumn(element)
754
+ "Row" -> RenderRow(element)
755
+ "Box" -> RenderBox(element)
756
+ "Text" -> RenderText(element)
757
+ "Button" -> RenderButton(element)
758
+ "Spacer" -> RenderSpacer(element)
759
+ else -> {
760
+ Log.w(TAG, "Unknown element type: \${element.type}")
761
+ Text("Unsupported: \${element.type}", color = Color.Red)
762
+ }
763
+ }
764
+ }
765
+
766
+ @Composable
767
+ private fun RenderColumn(element: DSLElement) {
768
+ Column(
769
+ modifier = parseModifier(element.modifier),
770
+ horizontalAlignment = parseHorizontalAlignment(element.horizontalAlignment),
771
+ verticalArrangement = parseVerticalArrangement(element.verticalArrangement)
772
+ ) {
773
+ element.children?.forEach { child ->
774
+ RenderElement(child)
775
+ }
776
+ }
777
+ }
778
+
779
+ @Composable
780
+ private fun RenderRow(element: DSLElement) {
781
+ Row(
782
+ modifier = parseModifier(element.modifier),
783
+ verticalAlignment = parseVerticalAlignment(element.horizontalAlignment),
784
+ horizontalArrangement = parseHorizontalArrangement(element.verticalArrangement)
785
+ ) {
786
+ element.children?.forEach { child ->
787
+ RenderElement(child)
788
+ }
789
+ }
790
+ }
791
+
792
+ @Composable
793
+ private fun RenderBox(element: DSLElement) {
794
+ Box(
795
+ modifier = parseModifier(element.modifier),
796
+ contentAlignment = parseContentAlignment(element.contentAlignment)
797
+ ) {
798
+ element.children?.forEach { child ->
799
+ RenderElement(child)
800
+ }
801
+ }
802
+ }
803
+
804
+ @Composable
805
+ private fun RenderText(element: DSLElement) {
806
+ Text(
807
+ text = element.text ?: "",
808
+ style = parseTextStyle(element.style),
809
+ color = parseColor(element.color) ?: Color.Unspecified,
810
+ modifier = parseModifier(element.modifier)
811
+ )
812
+ }
813
+
814
+ @Composable
815
+ private fun RenderButton(element: DSLElement) {
816
+ Button(
817
+ onClick = { handleClick(element.onClick) },
818
+ modifier = parseModifier(element.modifier),
819
+ enabled = element.enabled ?: true
820
+ ) {
821
+ Text(element.text ?: "Button")
822
+ }
823
+ }
824
+
825
+ @Composable
826
+ private fun RenderSpacer(element: DSLElement) {
827
+ Spacer(
828
+ modifier = Modifier
829
+ .height(element.height?.dp ?: 0.dp)
830
+ .width(element.width?.dp ?: 0.dp)
831
+ )
832
+ }
833
+
834
+ /**
835
+ * Parse DSL modifier to Compose Modifier
836
+ */
837
+ private fun parseModifier(dslModifier: DSLModifier?): Modifier {
838
+ var modifier: Modifier = Modifier
839
+
840
+ dslModifier?.let { m ->
841
+ if (m.fillMaxSize == true) modifier = modifier.fillMaxSize()
842
+ if (m.fillMaxWidth == true) modifier = modifier.fillMaxWidth()
843
+ if (m.fillMaxHeight == true) modifier = modifier.fillMaxHeight()
844
+
845
+ m.padding?.let { modifier = modifier.padding(it.dp) }
846
+ m.paddingHorizontal?.let { modifier = modifier.padding(horizontal = it.dp) }
847
+ m.paddingVertical?.let { modifier = modifier.padding(vertical = it.dp) }
848
+
849
+ m.size?.let { modifier = modifier.size(it.dp) }
850
+ m.height?.let { modifier = modifier.height(it.dp) }
851
+ m.width?.let { modifier = modifier.width(it.dp) }
852
+
853
+ // Note: weight() is only available in RowScope/ColumnScope
854
+ // We'll handle it separately when needed
855
+ }
856
+
857
+ return modifier
858
+ }
859
+
860
+ /**
861
+ * Parse alignment strings
862
+ */
863
+ private fun parseHorizontalAlignment(alignment: String?): Alignment.Horizontal {
864
+ return when (alignment?.lowercase()) {
865
+ "start" -> Alignment.Start
866
+ "centerhorizontally", "center" -> Alignment.CenterHorizontally
867
+ "end" -> Alignment.End
868
+ else -> Alignment.Start
869
+ }
497
870
  }
498
- catch (error) {
499
- console.warn('[Warning] Could not copy DSLInterpreter.kt from my-app. You may need to add it manually.');
871
+
872
+ private fun parseVerticalAlignment(alignment: String?): Alignment.Vertical {
873
+ return when (alignment?.lowercase()) {
874
+ "top" -> Alignment.Top
875
+ "centervertically", "center" -> Alignment.CenterVertically
876
+ "bottom" -> Alignment.Bottom
877
+ else -> Alignment.Top
878
+ }
879
+ }
880
+
881
+ private fun parseContentAlignment(alignment: String?): Alignment {
882
+ return when (alignment?.lowercase()) {
883
+ "center" -> Alignment.Center
884
+ "topcenter" -> Alignment.TopCenter
885
+ "topstart" -> Alignment.TopStart
886
+ "topend" -> Alignment.TopEnd
887
+ "bottomcenter" -> Alignment.BottomCenter
888
+ "bottomstart" -> Alignment.BottomStart
889
+ "bottomend" -> Alignment.BottomEnd
890
+ "centerstart" -> Alignment.CenterStart
891
+ "centerend" -> Alignment.CenterEnd
892
+ else -> Alignment.TopStart
893
+ }
894
+ }
895
+
896
+ private fun parseVerticalArrangement(arrangement: String?): Arrangement.Vertical {
897
+ return when (arrangement?.lowercase()) {
898
+ "top" -> Arrangement.Top
899
+ "center" -> Arrangement.Center
900
+ "bottom" -> Arrangement.Bottom
901
+ "spacebetween" -> Arrangement.SpaceBetween
902
+ "spacearound" -> Arrangement.SpaceAround
903
+ "spaceevenly" -> Arrangement.SpaceEvenly
904
+ else -> Arrangement.Top
905
+ }
906
+ }
907
+
908
+ private fun parseHorizontalArrangement(arrangement: String?): Arrangement.Horizontal {
909
+ return when (arrangement?.lowercase()) {
910
+ "start" -> Arrangement.Start
911
+ "center" -> Arrangement.Center
912
+ "end" -> Arrangement.End
913
+ "spacebetween" -> Arrangement.SpaceBetween
914
+ "spacearound" -> Arrangement.SpaceAround
915
+ "spaceevenly" -> Arrangement.SpaceEvenly
916
+ else -> Arrangement.Start
917
+ }
918
+ }
919
+
920
+ /**
921
+ * Parse text style
922
+ */
923
+ @Composable
924
+ private fun parseTextStyle(style: String?): androidx.compose.ui.text.TextStyle {
925
+ return when (style?.lowercase()) {
926
+ "headlinelarge" -> MaterialTheme.typography.headlineLarge
927
+ "headlinemedium" -> MaterialTheme.typography.headlineMedium
928
+ "headlinesmall" -> MaterialTheme.typography.headlineSmall
929
+ "titlelarge" -> MaterialTheme.typography.titleLarge
930
+ "titlemedium" -> MaterialTheme.typography.titleMedium
931
+ "titlesmall" -> MaterialTheme.typography.titleSmall
932
+ "bodylarge" -> MaterialTheme.typography.bodyLarge
933
+ "bodymedium" -> MaterialTheme.typography.bodyMedium
934
+ "bodysmall" -> MaterialTheme.typography.bodySmall
935
+ "labellarge" -> MaterialTheme.typography.labelLarge
936
+ "labelmedium" -> MaterialTheme.typography.labelMedium
937
+ "labelsmall" -> MaterialTheme.typography.labelSmall
938
+ else -> MaterialTheme.typography.bodyMedium
939
+ }
940
+ }
941
+
942
+ /**
943
+ * Parse color from string
944
+ */
945
+ private fun parseColor(colorString: String?): Color? {
946
+ if (colorString == null) return null
947
+
948
+ return try {
949
+ when {
950
+ colorString.startsWith("#") -> {
951
+ // Hex color
952
+ Color(android.graphics.Color.parseColor(colorString))
953
+ }
954
+ else -> null
955
+ }
956
+ } catch (e: Exception) {
957
+ Log.w(TAG, "Failed to parse color: \$colorString")
958
+ null
959
+ }
960
+ }
961
+
962
+ /**
963
+ * Handle click events
964
+ */
965
+ private fun handleClick(action: String?) {
966
+ if (action != null) {
967
+ Log.d(TAG, "Button clicked: \$action")
968
+ // TODO: Implement action handling
969
+ }
500
970
  }
501
971
  }
972
+ `;
973
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(interpreterPath));
974
+ await fs_extra_1.default.writeFile(interpreterPath, content);
975
+ }
502
976
  async function generateDSLTypes(projectPath, packageName) {
503
977
  const packagePath = packageName.replace(/\./g, '/');
504
978
  const typesPath = path_1.default.join(projectPath, 'app/src/main/java', packagePath, 'DSLTypes.kt');
505
- const sourceFile = path_1.default.join(__dirname, '../../../../my-app/app/src/main/java/com/jetstart/myapp/DSLTypes.kt');
506
- try {
507
- let content = await fs_extra_1.default.readFile(sourceFile, 'utf-8');
508
- content = content.replace(/package com\.jetstart\.myapp/g, `package ${packageName}`);
509
- await fs_extra_1.default.ensureDir(path_1.default.dirname(typesPath));
510
- await fs_extra_1.default.writeFile(typesPath, content);
511
- }
512
- catch (error) {
513
- console.warn('[Warning] Could not copy DSLTypes.kt from my-app. You may need to add it manually.');
514
- }
979
+ const content = `package ${packageName}
980
+
981
+ import org.json.JSONArray
982
+ import org.json.JSONObject
983
+
984
+ /**
985
+ * DSL Type Definitions
986
+ * Represents UI elements in JSON format that can be interpreted at runtime
987
+ */
988
+
989
+ data class UIDefinition(
990
+ val version: String = "1.0",
991
+ val screen: DSLElement
992
+ )
993
+
994
+ data class DSLElement(
995
+ val type: String,
996
+ val text: String? = null,
997
+ val style: String? = null,
998
+ val color: String? = null,
999
+ val modifier: DSLModifier? = null,
1000
+ val horizontalAlignment: String? = null,
1001
+ val verticalArrangement: String? = null,
1002
+ val contentAlignment: String? = null,
1003
+ val height: Int? = null,
1004
+ val width: Int? = null,
1005
+ val onClick: String? = null,
1006
+ val enabled: Boolean? = true,
1007
+ val imageVector: String? = null,
1008
+ val tint: String? = null,
1009
+ val contentDescription: String? = null,
1010
+ val children: List<DSLElement>? = null
1011
+ )
1012
+
1013
+ data class DSLModifier(
1014
+ val fillMaxSize: Boolean? = null,
1015
+ val fillMaxWidth: Boolean? = null,
1016
+ val fillMaxHeight: Boolean? = null,
1017
+ val padding: Int? = null,
1018
+ val paddingHorizontal: Int? = null,
1019
+ val paddingVertical: Int? = null,
1020
+ val size: Int? = null,
1021
+ val height: Int? = null,
1022
+ val width: Int? = null,
1023
+ val weight: Float? = null
1024
+ )
1025
+
1026
+ /**
1027
+ * Parse JSON string to UIDefinition
1028
+ */
1029
+ fun parseUIDefinition(json: String): UIDefinition {
1030
+ val obj = JSONObject(json)
1031
+ val version = obj.optString("version", "1.0")
1032
+ val screenObj = obj.getJSONObject("screen")
1033
+
1034
+ return UIDefinition(
1035
+ version = version,
1036
+ screen = parseDSLElement(screenObj)
1037
+ )
1038
+ }
1039
+
1040
+ /**
1041
+ * Parse JSONObject to DSLElement
1042
+ */
1043
+ fun parseDSLElement(obj: JSONObject): DSLElement {
1044
+ val children = if (obj.has("children")) {
1045
+ val childrenArray = obj.getJSONArray("children")
1046
+ List(childrenArray.length()) { i ->
1047
+ parseDSLElement(childrenArray.getJSONObject(i))
1048
+ }
1049
+ } else null
1050
+
1051
+ val modifier = if (obj.has("modifier")) {
1052
+ val modObj = obj.getJSONObject("modifier")
1053
+ DSLModifier(
1054
+ fillMaxSize = modObj.optBoolean("fillMaxSize"),
1055
+ fillMaxWidth = modObj.optBoolean("fillMaxWidth"),
1056
+ fillMaxHeight = modObj.optBoolean("fillMaxHeight"),
1057
+ padding = if (modObj.has("padding")) modObj.getInt("padding") else null,
1058
+ paddingHorizontal = if (modObj.has("paddingHorizontal")) modObj.getInt("paddingHorizontal") else null,
1059
+ paddingVertical = if (modObj.has("paddingVertical")) modObj.getInt("paddingVertical") else null,
1060
+ size = if (modObj.has("size")) modObj.getInt("size") else null,
1061
+ height = if (modObj.has("height")) modObj.getInt("height") else null,
1062
+ width = if (modObj.has("width")) modObj.getInt("width") else null,
1063
+ weight = if (modObj.has("weight")) modObj.getDouble("weight").toFloat() else null
1064
+ )
1065
+ } else null
1066
+
1067
+ return DSLElement(
1068
+ type = obj.getString("type"),
1069
+ text = if (obj.has("text")) obj.getString("text") else null,
1070
+ style = if (obj.has("style")) obj.getString("style") else null,
1071
+ color = if (obj.has("color")) obj.getString("color") else null,
1072
+ modifier = modifier,
1073
+ horizontalAlignment = if (obj.has("horizontalAlignment")) obj.getString("horizontalAlignment") else null,
1074
+ verticalArrangement = if (obj.has("verticalArrangement")) obj.getString("verticalArrangement") else null,
1075
+ contentAlignment = if (obj.has("contentAlignment")) obj.getString("contentAlignment") else null,
1076
+ height = if (obj.has("height")) obj.getInt("height") else null,
1077
+ width = if (obj.has("width")) obj.getInt("width") else null,
1078
+ onClick = if (obj.has("onClick")) obj.getString("onClick") else null,
1079
+ enabled = obj.optBoolean("enabled", true),
1080
+ imageVector = if (obj.has("imageVector")) obj.getString("imageVector") else null,
1081
+ tint = if (obj.has("tint")) obj.getString("tint") else null,
1082
+ contentDescription = if (obj.has("contentDescription")) obj.getString("contentDescription") else null,
1083
+ children = children
1084
+ )
1085
+ }
1086
+ `;
1087
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(typesPath));
1088
+ await fs_extra_1.default.writeFile(typesPath, content);
515
1089
  }
516
1090
  //# sourceMappingURL=template.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/utils/template.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/utils/template.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetstart/cli",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Command-line interface for JetStart",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -36,8 +36,8 @@
36
36
  },
37
37
  "homepage": "https://github.com/dev-phantom/jetstart#readme",
38
38
  "dependencies": {
39
- "@jetstart/core": "^1.1.3",
40
- "@jetstart/shared": "^1.1.3",
39
+ "@jetstart/core": "^1.1.4",
40
+ "@jetstart/shared": "^1.1.4",
41
41
  "axios": "^1.6.2",
42
42
  "chalk": "^4.1.2",
43
43
  "commander": "^11.1.0",
@@ -569,19 +569,230 @@ async function generateHotReload(
569
569
  'HotReload.kt'
570
570
  );
571
571
 
572
- // Copy from my-app template
573
- const sourceFile = path.join(__dirname, '../../../../my-app/app/src/main/java/com/jetstart/myapp/HotReload.kt');
574
-
575
- try {
576
- let content = await fs.readFile(sourceFile, 'utf-8');
577
- // Replace package name
578
- content = content.replace(/package com\.jetstart\.myapp/g, `package ${packageName}`);
579
-
580
- await fs.ensureDir(path.dirname(hotReloadPath));
581
- await fs.writeFile(hotReloadPath, content);
582
- } catch (error) {
583
- console.warn('[Warning] Could not copy HotReload.kt from my-app. You may need to add it manually.');
584
- }
572
+ const content = `package ${packageName}
573
+
574
+ import android.app.Activity
575
+ import android.content.Intent
576
+ import android.net.Uri
577
+ import android.os.Build
578
+ import android.util.Log
579
+ import androidx.core.content.FileProvider
580
+ import okhttp3.*
581
+ import org.json.JSONObject
582
+ import java.io.File
583
+ import java.io.IOException
584
+
585
+ /**
586
+ * Hot Reload Manager
587
+ * Connects to JetStart dev server and automatically reloads the app when code changes
588
+ */
589
+ object HotReload {
590
+ private const val TAG = "HotReload"
591
+ private var webSocket: WebSocket? = null
592
+ private var activity: Activity? = null
593
+ private var connectionTime: Long = 0
594
+ private var ignoreFirstBuild = true
595
+ private val httpClient = OkHttpClient()
596
+
597
+ fun connect(activity: Activity, serverUrl: String, sessionId: String) {
598
+ this.activity = activity
599
+
600
+ val wsUrl = serverUrl.replace("http://", "ws://").replace("https://", "wss://")
601
+ Log.d(TAG, "Connecting to dev server: \$wsUrl")
602
+
603
+ val client = OkHttpClient()
604
+ val request = Request.Builder()
605
+ .url(wsUrl)
606
+ .build()
607
+
608
+ webSocket = client.newWebSocket(request, object : WebSocketListener() {
609
+ override fun onOpen(webSocket: WebSocket, response: Response) {
610
+ Log.d(TAG, "WebSocket connected")
611
+ connectionTime = System.currentTimeMillis()
612
+ ignoreFirstBuild = true // Ignore the first build-complete after connecting
613
+
614
+ // Send connect message
615
+ val connectMsg = JSONObject().apply {
616
+ put("type", "client:connect")
617
+ put("sessionId", sessionId)
618
+ put("clientType", "test-app")
619
+ }
620
+ webSocket.send(connectMsg.toString())
621
+ }
622
+
623
+ override fun onMessage(webSocket: WebSocket, text: String) {
624
+ Log.d(TAG, "Received: \$text")
625
+
626
+ try {
627
+ val json = JSONObject(text)
628
+ val type = json.getString("type")
629
+
630
+ when (type) {
631
+ "core:ui-update" -> {
632
+ // DSL-based hot reload (FAST)
633
+ if (ignoreFirstBuild) {
634
+ Log.d(TAG, "Ignoring first UI update (old build)")
635
+ ignoreFirstBuild = false
636
+ return@onMessage
637
+ }
638
+
639
+ val timestamp = json.optLong("timestamp", 0)
640
+ val dslContent = json.optString("dslContent", "")
641
+
642
+ Log.d(TAG, "UI update received at \$timestamp, connection at \$connectionTime")
643
+
644
+ // Only update if changes happened AFTER we connected
645
+ if (timestamp > connectionTime && dslContent.isNotEmpty()) {
646
+ Log.d(TAG, "New UI update detected, recomposing (\${dslContent.length} bytes)")
647
+
648
+ // Update DSL on main thread
649
+ activity?.runOnUiThread {
650
+ DSLInterpreter.updateDSL(dslContent)
651
+ }
652
+ } else {
653
+ Log.d(TAG, "Ignoring old UI update")
654
+ }
655
+ }
656
+
657
+ "core:reload" -> {
658
+ Log.d(TAG, "Reload triggered!")
659
+ // Restart activity on main thread
660
+ activity?.runOnUiThread {
661
+ activity?.recreate()
662
+ }
663
+ }
664
+
665
+ "core:build-complete" -> {
666
+ // Full APK rebuild (SLOW - fallback for non-UI changes)
667
+ if (ignoreFirstBuild) {
668
+ Log.d(TAG, "Ignoring first build-complete (old build)")
669
+ ignoreFirstBuild = false
670
+ return@onMessage
671
+ }
672
+
673
+ val timestamp = json.optLong("timestamp", 0)
674
+ val downloadUrl = json.optString("downloadUrl", "")
675
+
676
+ Log.d(TAG, "Build complete at \$timestamp, connection at \$connectionTime")
677
+ Log.d(TAG, "Download URL: \$downloadUrl")
678
+
679
+ // Only reload if build happened AFTER we connected
680
+ if (timestamp > connectionTime && downloadUrl.isNotEmpty()) {
681
+ Log.d(TAG, "New build detected, downloading and installing APK")
682
+ downloadAndInstallApk(downloadUrl)
683
+ } else {
684
+ Log.d(TAG, "Ignoring old build (timestamp before connection)")
685
+ }
686
+ }
687
+ }
688
+ } catch (e: Exception) {
689
+ Log.e(TAG, "Failed to parse message: \${e.message}")
690
+ }
691
+ }
692
+
693
+ override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
694
+ Log.e(TAG, "WebSocket error: \${t.message}")
695
+ // Auto-reconnect after 5 seconds
696
+ android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
697
+ connect(activity!!, serverUrl, sessionId)
698
+ }, 5000)
699
+ }
700
+
701
+ override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
702
+ Log.d(TAG, "WebSocket closed: \$reason")
703
+ }
704
+ })
705
+ }
706
+
707
+ fun disconnect() {
708
+ webSocket?.close(1000, "App closing")
709
+ webSocket = null
710
+ activity = null
711
+ }
712
+
713
+ private fun downloadAndInstallApk(downloadUrl: String) {
714
+ Log.d(TAG, "Starting APK download from: \$downloadUrl")
715
+
716
+ val request = Request.Builder()
717
+ .url(downloadUrl)
718
+ .build()
719
+
720
+ httpClient.newCall(request).enqueue(object : Callback {
721
+ override fun onFailure(call: Call, e: IOException) {
722
+ Log.e(TAG, "APK download failed: \${e.message}")
723
+ }
724
+
725
+ override fun onResponse(call: Call, response: Response) {
726
+ if (!response.isSuccessful) {
727
+ Log.e(TAG, "APK download failed with code: \${response.code}")
728
+ return
729
+ }
730
+
731
+ try {
732
+ val apkData = response.body?.bytes()
733
+ if (apkData == null) {
734
+ Log.e(TAG, "APK data is null")
735
+ return
736
+ }
737
+
738
+ Log.d(TAG, "APK downloaded successfully (\${apkData.size} bytes)")
739
+
740
+ // Save APK to cache directory
741
+ val cacheDir = activity?.cacheDir
742
+ if (cacheDir == null) {
743
+ Log.e(TAG, "Cache directory is null")
744
+ return
745
+ }
746
+
747
+ val apkFile = File(cacheDir, "update.apk")
748
+ apkFile.outputStream().use { output ->
749
+ output.write(apkData)
750
+ }
751
+
752
+ Log.d(TAG, "APK saved to: \${apkFile.absolutePath}")
753
+
754
+ // Install APK on main thread
755
+ activity?.runOnUiThread {
756
+ installApk(apkFile)
757
+ }
758
+ } catch (e: Exception) {
759
+ Log.e(TAG, "Failed to save/install APK: \${e.message}")
760
+ }
761
+ }
762
+ })
763
+ }
764
+
765
+ private fun installApk(apkFile: File) {
766
+ try {
767
+ val context = activity ?: return
768
+
769
+ val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
770
+ FileProvider.getUriForFile(
771
+ context,
772
+ "\${context.packageName}.fileprovider",
773
+ apkFile
774
+ )
775
+ } else {
776
+ Uri.fromFile(apkFile)
777
+ }
778
+
779
+ val intent = Intent(Intent.ACTION_VIEW).apply {
780
+ setDataAndType(uri, "application/vnd.android.package-archive")
781
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
782
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
783
+ }
784
+
785
+ context.startActivity(intent)
786
+ Log.d(TAG, "Installation intent started")
787
+ } catch (e: Exception) {
788
+ Log.e(TAG, "Failed to install APK: \${e.message}")
789
+ }
790
+ }
791
+ }
792
+ `;
793
+
794
+ await fs.ensureDir(path.dirname(hotReloadPath));
795
+ await fs.writeFile(hotReloadPath, content);
585
796
  }
586
797
 
587
798
  async function generateDSLInterpreter(
@@ -596,17 +807,280 @@ async function generateDSLInterpreter(
596
807
  'DSLInterpreter.kt'
597
808
  );
598
809
 
599
- const sourceFile = path.join(__dirname, '../../../../my-app/app/src/main/java/com/jetstart/myapp/DSLInterpreter.kt');
600
-
601
- try {
602
- let content = await fs.readFile(sourceFile, 'utf-8');
603
- content = content.replace(/package com\.jetstart\.myapp/g, `package ${packageName}`);
604
-
605
- await fs.ensureDir(path.dirname(interpreterPath));
606
- await fs.writeFile(interpreterPath, content);
607
- } catch (error) {
608
- console.warn('[Warning] Could not copy DSLInterpreter.kt from my-app. You may need to add it manually.');
609
- }
810
+ const content = `package ${packageName}
811
+
812
+ import android.util.Log
813
+ import androidx.compose.foundation.layout.*
814
+ import androidx.compose.material3.*
815
+ import androidx.compose.runtime.*
816
+ import androidx.compose.ui.Alignment
817
+ import androidx.compose.ui.Modifier
818
+ import androidx.compose.ui.graphics.Color
819
+ import androidx.compose.ui.text.font.FontWeight
820
+ import androidx.compose.ui.unit.dp
821
+ import kotlinx.coroutines.flow.MutableStateFlow
822
+ import kotlinx.coroutines.flow.StateFlow
823
+
824
+ /**
825
+ * DSL Interpreter
826
+ * Converts JSON DSL to Compose UI at runtime
827
+ */
828
+ object DSLInterpreter {
829
+ private const val TAG = "DSLInterpreter"
830
+
831
+ private val _currentDSL = MutableStateFlow<UIDefinition?>(null)
832
+ val currentDSL: StateFlow<UIDefinition?> = _currentDSL
833
+
834
+ /**
835
+ * Update the current DSL definition
836
+ */
837
+ fun updateDSL(jsonString: String) {
838
+ try {
839
+ val definition = parseUIDefinition(jsonString)
840
+ _currentDSL.value = definition
841
+ Log.d(TAG, "DSL updated successfully")
842
+ } catch (e: Exception) {
843
+ Log.e(TAG, "Failed to parse DSL: \${e.message}", e)
844
+ }
845
+ }
846
+
847
+ /**
848
+ * Render DSL as Compose UI
849
+ */
850
+ @Composable
851
+ fun RenderDSL(definition: UIDefinition) {
852
+ RenderElement(definition.screen)
853
+ }
854
+
855
+ /**
856
+ * Render individual DSL element
857
+ */
858
+ @Composable
859
+ fun RenderElement(element: DSLElement) {
860
+ when (element.type) {
861
+ "Column" -> RenderColumn(element)
862
+ "Row" -> RenderRow(element)
863
+ "Box" -> RenderBox(element)
864
+ "Text" -> RenderText(element)
865
+ "Button" -> RenderButton(element)
866
+ "Spacer" -> RenderSpacer(element)
867
+ else -> {
868
+ Log.w(TAG, "Unknown element type: \${element.type}")
869
+ Text("Unsupported: \${element.type}", color = Color.Red)
870
+ }
871
+ }
872
+ }
873
+
874
+ @Composable
875
+ private fun RenderColumn(element: DSLElement) {
876
+ Column(
877
+ modifier = parseModifier(element.modifier),
878
+ horizontalAlignment = parseHorizontalAlignment(element.horizontalAlignment),
879
+ verticalArrangement = parseVerticalArrangement(element.verticalArrangement)
880
+ ) {
881
+ element.children?.forEach { child ->
882
+ RenderElement(child)
883
+ }
884
+ }
885
+ }
886
+
887
+ @Composable
888
+ private fun RenderRow(element: DSLElement) {
889
+ Row(
890
+ modifier = parseModifier(element.modifier),
891
+ verticalAlignment = parseVerticalAlignment(element.horizontalAlignment),
892
+ horizontalArrangement = parseHorizontalArrangement(element.verticalArrangement)
893
+ ) {
894
+ element.children?.forEach { child ->
895
+ RenderElement(child)
896
+ }
897
+ }
898
+ }
899
+
900
+ @Composable
901
+ private fun RenderBox(element: DSLElement) {
902
+ Box(
903
+ modifier = parseModifier(element.modifier),
904
+ contentAlignment = parseContentAlignment(element.contentAlignment)
905
+ ) {
906
+ element.children?.forEach { child ->
907
+ RenderElement(child)
908
+ }
909
+ }
910
+ }
911
+
912
+ @Composable
913
+ private fun RenderText(element: DSLElement) {
914
+ Text(
915
+ text = element.text ?: "",
916
+ style = parseTextStyle(element.style),
917
+ color = parseColor(element.color) ?: Color.Unspecified,
918
+ modifier = parseModifier(element.modifier)
919
+ )
920
+ }
921
+
922
+ @Composable
923
+ private fun RenderButton(element: DSLElement) {
924
+ Button(
925
+ onClick = { handleClick(element.onClick) },
926
+ modifier = parseModifier(element.modifier),
927
+ enabled = element.enabled ?: true
928
+ ) {
929
+ Text(element.text ?: "Button")
930
+ }
931
+ }
932
+
933
+ @Composable
934
+ private fun RenderSpacer(element: DSLElement) {
935
+ Spacer(
936
+ modifier = Modifier
937
+ .height(element.height?.dp ?: 0.dp)
938
+ .width(element.width?.dp ?: 0.dp)
939
+ )
940
+ }
941
+
942
+ /**
943
+ * Parse DSL modifier to Compose Modifier
944
+ */
945
+ private fun parseModifier(dslModifier: DSLModifier?): Modifier {
946
+ var modifier: Modifier = Modifier
947
+
948
+ dslModifier?.let { m ->
949
+ if (m.fillMaxSize == true) modifier = modifier.fillMaxSize()
950
+ if (m.fillMaxWidth == true) modifier = modifier.fillMaxWidth()
951
+ if (m.fillMaxHeight == true) modifier = modifier.fillMaxHeight()
952
+
953
+ m.padding?.let { modifier = modifier.padding(it.dp) }
954
+ m.paddingHorizontal?.let { modifier = modifier.padding(horizontal = it.dp) }
955
+ m.paddingVertical?.let { modifier = modifier.padding(vertical = it.dp) }
956
+
957
+ m.size?.let { modifier = modifier.size(it.dp) }
958
+ m.height?.let { modifier = modifier.height(it.dp) }
959
+ m.width?.let { modifier = modifier.width(it.dp) }
960
+
961
+ // Note: weight() is only available in RowScope/ColumnScope
962
+ // We'll handle it separately when needed
963
+ }
964
+
965
+ return modifier
966
+ }
967
+
968
+ /**
969
+ * Parse alignment strings
970
+ */
971
+ private fun parseHorizontalAlignment(alignment: String?): Alignment.Horizontal {
972
+ return when (alignment?.lowercase()) {
973
+ "start" -> Alignment.Start
974
+ "centerhorizontally", "center" -> Alignment.CenterHorizontally
975
+ "end" -> Alignment.End
976
+ else -> Alignment.Start
977
+ }
978
+ }
979
+
980
+ private fun parseVerticalAlignment(alignment: String?): Alignment.Vertical {
981
+ return when (alignment?.lowercase()) {
982
+ "top" -> Alignment.Top
983
+ "centervertically", "center" -> Alignment.CenterVertically
984
+ "bottom" -> Alignment.Bottom
985
+ else -> Alignment.Top
986
+ }
987
+ }
988
+
989
+ private fun parseContentAlignment(alignment: String?): Alignment {
990
+ return when (alignment?.lowercase()) {
991
+ "center" -> Alignment.Center
992
+ "topcenter" -> Alignment.TopCenter
993
+ "topstart" -> Alignment.TopStart
994
+ "topend" -> Alignment.TopEnd
995
+ "bottomcenter" -> Alignment.BottomCenter
996
+ "bottomstart" -> Alignment.BottomStart
997
+ "bottomend" -> Alignment.BottomEnd
998
+ "centerstart" -> Alignment.CenterStart
999
+ "centerend" -> Alignment.CenterEnd
1000
+ else -> Alignment.TopStart
1001
+ }
1002
+ }
1003
+
1004
+ private fun parseVerticalArrangement(arrangement: String?): Arrangement.Vertical {
1005
+ return when (arrangement?.lowercase()) {
1006
+ "top" -> Arrangement.Top
1007
+ "center" -> Arrangement.Center
1008
+ "bottom" -> Arrangement.Bottom
1009
+ "spacebetween" -> Arrangement.SpaceBetween
1010
+ "spacearound" -> Arrangement.SpaceAround
1011
+ "spaceevenly" -> Arrangement.SpaceEvenly
1012
+ else -> Arrangement.Top
1013
+ }
1014
+ }
1015
+
1016
+ private fun parseHorizontalArrangement(arrangement: String?): Arrangement.Horizontal {
1017
+ return when (arrangement?.lowercase()) {
1018
+ "start" -> Arrangement.Start
1019
+ "center" -> Arrangement.Center
1020
+ "end" -> Arrangement.End
1021
+ "spacebetween" -> Arrangement.SpaceBetween
1022
+ "spacearound" -> Arrangement.SpaceAround
1023
+ "spaceevenly" -> Arrangement.SpaceEvenly
1024
+ else -> Arrangement.Start
1025
+ }
1026
+ }
1027
+
1028
+ /**
1029
+ * Parse text style
1030
+ */
1031
+ @Composable
1032
+ private fun parseTextStyle(style: String?): androidx.compose.ui.text.TextStyle {
1033
+ return when (style?.lowercase()) {
1034
+ "headlinelarge" -> MaterialTheme.typography.headlineLarge
1035
+ "headlinemedium" -> MaterialTheme.typography.headlineMedium
1036
+ "headlinesmall" -> MaterialTheme.typography.headlineSmall
1037
+ "titlelarge" -> MaterialTheme.typography.titleLarge
1038
+ "titlemedium" -> MaterialTheme.typography.titleMedium
1039
+ "titlesmall" -> MaterialTheme.typography.titleSmall
1040
+ "bodylarge" -> MaterialTheme.typography.bodyLarge
1041
+ "bodymedium" -> MaterialTheme.typography.bodyMedium
1042
+ "bodysmall" -> MaterialTheme.typography.bodySmall
1043
+ "labellarge" -> MaterialTheme.typography.labelLarge
1044
+ "labelmedium" -> MaterialTheme.typography.labelMedium
1045
+ "labelsmall" -> MaterialTheme.typography.labelSmall
1046
+ else -> MaterialTheme.typography.bodyMedium
1047
+ }
1048
+ }
1049
+
1050
+ /**
1051
+ * Parse color from string
1052
+ */
1053
+ private fun parseColor(colorString: String?): Color? {
1054
+ if (colorString == null) return null
1055
+
1056
+ return try {
1057
+ when {
1058
+ colorString.startsWith("#") -> {
1059
+ // Hex color
1060
+ Color(android.graphics.Color.parseColor(colorString))
1061
+ }
1062
+ else -> null
1063
+ }
1064
+ } catch (e: Exception) {
1065
+ Log.w(TAG, "Failed to parse color: \$colorString")
1066
+ null
1067
+ }
1068
+ }
1069
+
1070
+ /**
1071
+ * Handle click events
1072
+ */
1073
+ private fun handleClick(action: String?) {
1074
+ if (action != null) {
1075
+ Log.d(TAG, "Button clicked: \$action")
1076
+ // TODO: Implement action handling
1077
+ }
1078
+ }
1079
+ }
1080
+ `;
1081
+
1082
+ await fs.ensureDir(path.dirname(interpreterPath));
1083
+ await fs.writeFile(interpreterPath, content);
610
1084
  }
611
1085
 
612
1086
  async function generateDSLTypes(
@@ -621,15 +1095,115 @@ async function generateDSLTypes(
621
1095
  'DSLTypes.kt'
622
1096
  );
623
1097
 
624
- const sourceFile = path.join(__dirname, '../../../../my-app/app/src/main/java/com/jetstart/myapp/DSLTypes.kt');
625
-
626
- try {
627
- let content = await fs.readFile(sourceFile, 'utf-8');
628
- content = content.replace(/package com\.jetstart\.myapp/g, `package ${packageName}`);
629
-
630
- await fs.ensureDir(path.dirname(typesPath));
631
- await fs.writeFile(typesPath, content);
632
- } catch (error) {
633
- console.warn('[Warning] Could not copy DSLTypes.kt from my-app. You may need to add it manually.');
634
- }
1098
+ const content = `package ${packageName}
1099
+
1100
+ import org.json.JSONArray
1101
+ import org.json.JSONObject
1102
+
1103
+ /**
1104
+ * DSL Type Definitions
1105
+ * Represents UI elements in JSON format that can be interpreted at runtime
1106
+ */
1107
+
1108
+ data class UIDefinition(
1109
+ val version: String = "1.0",
1110
+ val screen: DSLElement
1111
+ )
1112
+
1113
+ data class DSLElement(
1114
+ val type: String,
1115
+ val text: String? = null,
1116
+ val style: String? = null,
1117
+ val color: String? = null,
1118
+ val modifier: DSLModifier? = null,
1119
+ val horizontalAlignment: String? = null,
1120
+ val verticalArrangement: String? = null,
1121
+ val contentAlignment: String? = null,
1122
+ val height: Int? = null,
1123
+ val width: Int? = null,
1124
+ val onClick: String? = null,
1125
+ val enabled: Boolean? = true,
1126
+ val imageVector: String? = null,
1127
+ val tint: String? = null,
1128
+ val contentDescription: String? = null,
1129
+ val children: List<DSLElement>? = null
1130
+ )
1131
+
1132
+ data class DSLModifier(
1133
+ val fillMaxSize: Boolean? = null,
1134
+ val fillMaxWidth: Boolean? = null,
1135
+ val fillMaxHeight: Boolean? = null,
1136
+ val padding: Int? = null,
1137
+ val paddingHorizontal: Int? = null,
1138
+ val paddingVertical: Int? = null,
1139
+ val size: Int? = null,
1140
+ val height: Int? = null,
1141
+ val width: Int? = null,
1142
+ val weight: Float? = null
1143
+ )
1144
+
1145
+ /**
1146
+ * Parse JSON string to UIDefinition
1147
+ */
1148
+ fun parseUIDefinition(json: String): UIDefinition {
1149
+ val obj = JSONObject(json)
1150
+ val version = obj.optString("version", "1.0")
1151
+ val screenObj = obj.getJSONObject("screen")
1152
+
1153
+ return UIDefinition(
1154
+ version = version,
1155
+ screen = parseDSLElement(screenObj)
1156
+ )
1157
+ }
1158
+
1159
+ /**
1160
+ * Parse JSONObject to DSLElement
1161
+ */
1162
+ fun parseDSLElement(obj: JSONObject): DSLElement {
1163
+ val children = if (obj.has("children")) {
1164
+ val childrenArray = obj.getJSONArray("children")
1165
+ List(childrenArray.length()) { i ->
1166
+ parseDSLElement(childrenArray.getJSONObject(i))
1167
+ }
1168
+ } else null
1169
+
1170
+ val modifier = if (obj.has("modifier")) {
1171
+ val modObj = obj.getJSONObject("modifier")
1172
+ DSLModifier(
1173
+ fillMaxSize = modObj.optBoolean("fillMaxSize"),
1174
+ fillMaxWidth = modObj.optBoolean("fillMaxWidth"),
1175
+ fillMaxHeight = modObj.optBoolean("fillMaxHeight"),
1176
+ padding = if (modObj.has("padding")) modObj.getInt("padding") else null,
1177
+ paddingHorizontal = if (modObj.has("paddingHorizontal")) modObj.getInt("paddingHorizontal") else null,
1178
+ paddingVertical = if (modObj.has("paddingVertical")) modObj.getInt("paddingVertical") else null,
1179
+ size = if (modObj.has("size")) modObj.getInt("size") else null,
1180
+ height = if (modObj.has("height")) modObj.getInt("height") else null,
1181
+ width = if (modObj.has("width")) modObj.getInt("width") else null,
1182
+ weight = if (modObj.has("weight")) modObj.getDouble("weight").toFloat() else null
1183
+ )
1184
+ } else null
1185
+
1186
+ return DSLElement(
1187
+ type = obj.getString("type"),
1188
+ text = if (obj.has("text")) obj.getString("text") else null,
1189
+ style = if (obj.has("style")) obj.getString("style") else null,
1190
+ color = if (obj.has("color")) obj.getString("color") else null,
1191
+ modifier = modifier,
1192
+ horizontalAlignment = if (obj.has("horizontalAlignment")) obj.getString("horizontalAlignment") else null,
1193
+ verticalArrangement = if (obj.has("verticalArrangement")) obj.getString("verticalArrangement") else null,
1194
+ contentAlignment = if (obj.has("contentAlignment")) obj.getString("contentAlignment") else null,
1195
+ height = if (obj.has("height")) obj.getInt("height") else null,
1196
+ width = if (obj.has("width")) obj.getInt("width") else null,
1197
+ onClick = if (obj.has("onClick")) obj.getString("onClick") else null,
1198
+ enabled = obj.optBoolean("enabled", true),
1199
+ imageVector = if (obj.has("imageVector")) obj.getString("imageVector") else null,
1200
+ tint = if (obj.has("tint")) obj.getString("tint") else null,
1201
+ contentDescription = if (obj.has("contentDescription")) obj.getString("contentDescription") else null,
1202
+ children = children
1203
+ )
1204
+ }
1205
+ `;
1206
+
1207
+ await fs.ensureDir(path.dirname(typesPath));
1208
+ await fs.writeFile(typesPath, content);
635
1209
  }