craft-native 0.0.37 → 0.0.49

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 (89) hide show
  1. package/dist/android/src/index.js +268 -0
  2. package/dist/android/src/index.js.map +10 -0
  3. package/dist/android/templates/AndroidManifest.xml.template +69 -0
  4. package/dist/android/templates/CraftBridge.kt.template +3748 -0
  5. package/dist/android/templates/CraftBridgeExtensions.kt.template +383 -0
  6. package/dist/android/templates/CraftWidgetProvider.kt.template +246 -0
  7. package/dist/android/templates/MainActivity.kt.template +109 -0
  8. package/dist/android/templates/build.gradle.kts.app.template +80 -0
  9. package/dist/android/templates/build.gradle.kts.project.template +5 -0
  10. package/dist/android/templates/fastlane/Appfile +25 -0
  11. package/dist/android/templates/fastlane/Fastfile +192 -0
  12. package/dist/android/templates/fastlane/Gemfile +7 -0
  13. package/dist/android/templates/github-workflow-android.yml +248 -0
  14. package/dist/android/templates/github-workflow-test.yml +329 -0
  15. package/dist/android/templates/settings.gradle.kts.template +18 -0
  16. package/dist/android/templates/test-bridges.html +1463 -0
  17. package/dist/api/window.d.ts +18 -0
  18. package/dist/api/window.d.ts.map +1 -1
  19. package/dist/assets/index.d.ts.map +1 -1
  20. package/dist/bridge/core.d.ts +2 -0
  21. package/dist/bridge/core.d.ts.map +1 -1
  22. package/dist/cli.js +4168 -0
  23. package/dist/components/sidebar.d.ts +5 -1
  24. package/dist/components/sidebar.d.ts.map +1 -1
  25. package/dist/dev/devtools.d.ts +1 -1
  26. package/dist/dev/devtools.d.ts.map +1 -1
  27. package/dist/dev/hot-reload.d.ts +12 -3
  28. package/dist/dev/hot-reload.d.ts.map +1 -1
  29. package/dist/index.cjs +573 -208
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +585 -216
  32. package/dist/index.js.map +45 -0
  33. package/dist/ios/src/index.js +186 -0
  34. package/dist/ios/src/index.js.map +10 -0
  35. package/dist/ios/templates/CraftApp.swift +4241 -0
  36. package/dist/ios/templates/CraftAppExtensions.swift +361 -0
  37. package/dist/ios/templates/CraftWidget.swift +247 -0
  38. package/dist/ios/templates/Info.plist.template +65 -0
  39. package/dist/ios/templates/fastlane/Appfile +45 -0
  40. package/dist/ios/templates/fastlane/Fastfile +189 -0
  41. package/dist/ios/templates/fastlane/Gemfile +7 -0
  42. package/dist/ios/templates/fastlane/Matchfile +58 -0
  43. package/dist/ios/templates/github-workflow-ios.yml +192 -0
  44. package/dist/ios/templates/github-workflow-release.yml +175 -0
  45. package/dist/ios/templates/github-workflow-test.yml +272 -0
  46. package/dist/ios/templates/project.yml.template +25 -0
  47. package/dist/ios/templates/test-bridges.html +1463 -0
  48. package/dist/package.d.ts +106 -2
  49. package/dist/package.d.ts.map +1 -1
  50. package/dist/package.test.d.ts +2 -0
  51. package/dist/package.test.d.ts.map +1 -0
  52. package/dist/styles/sidebar-templates.d.ts.map +1 -1
  53. package/dist/styles/sidebars.d.ts +12 -8
  54. package/dist/styles/sidebars.d.ts.map +1 -1
  55. package/dist/templates/projects/blank/craft.config.ts +19 -0
  56. package/dist/templates/projects/blank/index.html +27 -0
  57. package/dist/templates/projects/blank/package.json +19 -0
  58. package/dist/templates/projects/blank/src/main.ts +26 -0
  59. package/dist/templates/projects/dashboard/craft.config.ts +42 -0
  60. package/dist/templates/projects/dashboard/index.html +13 -0
  61. package/dist/templates/projects/dashboard/package.json +20 -0
  62. package/dist/templates/projects/dashboard/src/main.ts +163 -0
  63. package/dist/templates/projects/dashboard/src/styles.css +280 -0
  64. package/dist/templates/projects/drawer/craft.config.ts +47 -0
  65. package/dist/templates/projects/drawer/index.html +13 -0
  66. package/dist/templates/projects/drawer/package.json +21 -0
  67. package/dist/templates/projects/drawer/src/main.ts +406 -0
  68. package/dist/templates/projects/drawer/src/styles.css +468 -0
  69. package/dist/templates/projects/ecommerce/craft.config.ts +32 -0
  70. package/dist/templates/projects/ecommerce/index.html +13 -0
  71. package/dist/templates/projects/ecommerce/package.json +19 -0
  72. package/dist/templates/projects/ecommerce/src/main.ts +194 -0
  73. package/dist/templates/projects/ecommerce/src/styles.css +169 -0
  74. package/dist/templates/projects/social/craft.config.ts +32 -0
  75. package/dist/templates/projects/social/index.html +13 -0
  76. package/dist/templates/projects/social/package.json +19 -0
  77. package/dist/templates/projects/social/src/main.ts +270 -0
  78. package/dist/templates/projects/social/src/styles.css +216 -0
  79. package/dist/templates/projects/tabs/craft.config.ts +47 -0
  80. package/dist/templates/projects/tabs/index.html +13 -0
  81. package/dist/templates/projects/tabs/package.json +21 -0
  82. package/dist/templates/projects/tabs/src/main.ts +177 -0
  83. package/dist/templates/projects/tabs/src/styles.css +163 -0
  84. package/dist/types.d.ts +29 -0
  85. package/dist/types.d.ts.map +1 -1
  86. package/dist/updater/index.d.ts +1 -0
  87. package/dist/updater/index.d.ts.map +1 -1
  88. package/package.json +5 -12
  89. package/bin/cli.ts +0 -937
@@ -0,0 +1,268 @@
1
+ // @bun
2
+ // ../android/src/index.ts
3
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync } from "fs";
4
+ import { join, dirname } from "path";
5
+ var {$ } = globalThis.Bun;
6
+ var TEMPLATES_DIR = join(dirname(import.meta.dir), "templates");
7
+ async function init(options) {
8
+ const { name, packageName, output } = options;
9
+ console.log(`
10
+ \u26A1 Initializing Craft Android project: ${name}`);
11
+ console.log(` Output: ${output}
12
+ `);
13
+ const finalPackageName = packageName || `com.craft.${name.toLowerCase().replace(/[^a-z0-9]/g, "")}`;
14
+ const packagePath = finalPackageName.replace(/\./g, "/");
15
+ const dirs = [
16
+ output,
17
+ join(output, "app/src/main/java", packagePath),
18
+ join(output, "app/src/main/res/layout"),
19
+ join(output, "app/src/main/res/values"),
20
+ join(output, "app/src/main/res/drawable"),
21
+ join(output, "app/src/main/assets"),
22
+ join(output, "gradle/wrapper")
23
+ ];
24
+ for (const dir of dirs) {
25
+ if (!existsSync(dir)) {
26
+ mkdirSync(dir, { recursive: true });
27
+ }
28
+ }
29
+ const config = {
30
+ appName: name,
31
+ packageName: finalPackageName,
32
+ version: "1.0.0",
33
+ versionCode: 1,
34
+ darkMode: true,
35
+ backgroundColor: "#1a1a2e",
36
+ enableSpeechRecognition: true,
37
+ enableHaptics: true,
38
+ enableShare: true,
39
+ enableCamera: true,
40
+ enableBiometric: true,
41
+ enablePushNotifications: false,
42
+ minSdk: 24,
43
+ targetSdk: 34
44
+ };
45
+ writeFileSync(join(output, "craft.config.json"), JSON.stringify(config, null, 2));
46
+ const mainActivityTemplate = readFileSync(join(TEMPLATES_DIR, "MainActivity.kt.template"), "utf-8");
47
+ const mainActivity = mainActivityTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{APP_NAME\}\}/g, name);
48
+ writeFileSync(join(output, "app/src/main/java", packagePath, "MainActivity.kt"), mainActivity);
49
+ const craftBridgeTemplate = readFileSync(join(TEMPLATES_DIR, "CraftBridge.kt.template"), "utf-8");
50
+ const craftBridge = craftBridgeTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName);
51
+ writeFileSync(join(output, "app/src/main/java", packagePath, "CraftBridge.kt"), craftBridge);
52
+ const manifestTemplate = readFileSync(join(TEMPLATES_DIR, "AndroidManifest.xml.template"), "utf-8");
53
+ const manifest = manifestTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{APP_NAME\}\}/g, name);
54
+ writeFileSync(join(output, "app/src/main/AndroidManifest.xml"), manifest);
55
+ const projectGradleTemplate = readFileSync(join(TEMPLATES_DIR, "build.gradle.kts.project.template"), "utf-8");
56
+ writeFileSync(join(output, "build.gradle.kts"), projectGradleTemplate);
57
+ const appGradleTemplate = readFileSync(join(TEMPLATES_DIR, "build.gradle.kts.app.template"), "utf-8");
58
+ const appGradle = appGradleTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{VERSION_NAME\}\}/g, config.version || "1.0.0").replace(/\{\{VERSION_CODE\}\}/g, String(config.versionCode || 1)).replace(/\{\{MIN_SDK\}\}/g, String(config.minSdk || 24)).replace(/\{\{TARGET_SDK\}\}/g, String(config.targetSdk || 34));
59
+ writeFileSync(join(output, "app/build.gradle.kts"), appGradle);
60
+ const settingsTemplate = readFileSync(join(TEMPLATES_DIR, "settings.gradle.kts.template"), "utf-8");
61
+ const settings = settingsTemplate.replace(/\{\{APP_NAME\}\}/g, name);
62
+ writeFileSync(join(output, "settings.gradle.kts"), settings);
63
+ const gradleProperties = `org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
64
+ android.useAndroidX=true
65
+ kotlin.code.style=official
66
+ android.nonTransitiveRClass=true
67
+ `;
68
+ writeFileSync(join(output, "gradle.properties"), gradleProperties);
69
+ writeFileSync(join(output, "local.properties"), `# SDK location will be set by Android Studio
70
+ `);
71
+ const gradleWrapperProps = `distributionBase=GRADLE_USER_HOME
72
+ distributionPath=wrapper/dists
73
+ distributionUrl=https\\://services.gradle.org/distributions/gradle-8.4-bin.zip
74
+ networkTimeout=10000
75
+ validateDistributionUrl=true
76
+ zipStoreBase=GRADLE_USER_HOME
77
+ zipStorePath=wrapper/dists
78
+ `;
79
+ writeFileSync(join(output, "gradle/wrapper/gradle-wrapper.properties"), gradleWrapperProps);
80
+ const stringsXml = `<?xml version="1.0" encoding="utf-8"?>
81
+ <resources>
82
+ <string name="app_name">${name}</string>
83
+ </resources>
84
+ `;
85
+ writeFileSync(join(output, "app/src/main/res/values/strings.xml"), stringsXml);
86
+ const colorsXml = `<?xml version="1.0" encoding="utf-8"?>
87
+ <resources>
88
+ <color name="primary">#1a1a2e</color>
89
+ <color name="primary_dark">#16162b</color>
90
+ <color name="accent">#4ade80</color>
91
+ <color name="background">${config.backgroundColor}</color>
92
+ </resources>
93
+ `;
94
+ writeFileSync(join(output, "app/src/main/res/values/colors.xml"), colorsXml);
95
+ const themesXml = `<?xml version="1.0" encoding="utf-8"?>
96
+ <resources>
97
+ <style name="Theme.CraftApp" parent="Theme.Material3.DayNight.NoActionBar">
98
+ <item name="android:statusBarColor">@color/primary_dark</item>
99
+ <item name="android:navigationBarColor">@color/primary</item>
100
+ <item name="android:windowBackground">@color/background</item>
101
+ </style>
102
+ </resources>
103
+ `;
104
+ writeFileSync(join(output, "app/src/main/res/values/themes.xml"), themesXml);
105
+ const activityMainXml = `<?xml version="1.0" encoding="utf-8"?>
106
+ <androidx.coordinatorlayout.widget.CoordinatorLayout
107
+ xmlns:android="http://schemas.android.com/apk/res/android"
108
+ android:layout_width="match_parent"
109
+ android:layout_height="match_parent"
110
+ android:fitsSystemWindows="true">
111
+
112
+ <WebView
113
+ android:id="@+id/webview"
114
+ android:layout_width="match_parent"
115
+ android:layout_height="match_parent" />
116
+
117
+ </androidx.coordinatorlayout.widget.CoordinatorLayout>
118
+ `;
119
+ writeFileSync(join(output, "app/src/main/res/layout/activity_main.xml"), activityMainXml);
120
+ const placeholderHtml = `<!DOCTYPE html>
121
+ <html>
122
+ <head>
123
+ <meta charset="UTF-8">
124
+ <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
125
+ <title>${name}</title>
126
+ <style>
127
+ * { margin: 0; padding: 0; box-sizing: border-box; }
128
+ body {
129
+ font-family: 'Roboto', sans-serif;
130
+ background: ${config.backgroundColor};
131
+ color: white;
132
+ min-height: 100vh;
133
+ display: flex;
134
+ justify-content: center;
135
+ align-items: center;
136
+ }
137
+ .container { text-align: center; padding: 2rem; }
138
+ h1 { font-size: 2.5rem; margin-bottom: 1rem; }
139
+ p { opacity: 0.7; }
140
+ .ready { color: #4ade80; font-size: 0.9rem; margin-top: 2rem; }
141
+ </style>
142
+ </head>
143
+ <body>
144
+ <div class="container">
145
+ <h1>\u26A1 ${name}</h1>
146
+ <p>Built with Craft Android</p>
147
+ <p class="ready" id="status">Waiting for Craft bridge...</p>
148
+ </div>
149
+ <script>
150
+ window.addEventListener('craftReady', (e) => {
151
+ document.getElementById('status').textContent = \`\u2713 Craft bridge ready (platform: \${e.detail.platform})\`;
152
+ console.log('Craft capabilities:', e.detail.capabilities);
153
+ });
154
+ </script>
155
+ </body>
156
+ </html>`;
157
+ writeFileSync(join(output, "app/src/main/assets/index.html"), placeholderHtml);
158
+ console.log("\u2705 Project initialized");
159
+ console.log("");
160
+ console.log("Next steps:");
161
+ console.log(` 1. cd ${output}`);
162
+ console.log(" 2. Add your web content to app/src/main/assets/index.html");
163
+ console.log(" 3. Run: craft android build");
164
+ console.log(" 4. Run: craft android open");
165
+ console.log("");
166
+ }
167
+ async function build(options) {
168
+ const { htmlPath, devServer, output, release } = options;
169
+ console.log(`
170
+ \uD83D\uDCE6 Building Craft Android project...`);
171
+ const configPath = join(output, "craft.config.json");
172
+ if (!existsSync(configPath)) {
173
+ throw new Error(`No craft.config.json found in ${output}. Run 'craft android init' first.`);
174
+ }
175
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
176
+ if (devServer) {
177
+ config.devServerURL = devServer;
178
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
179
+ console.log(` Dev server: ${devServer}`);
180
+ }
181
+ if (htmlPath) {
182
+ const assetsDir = join(output, "app/src/main/assets");
183
+ if (!existsSync(assetsDir)) {
184
+ mkdirSync(assetsDir, { recursive: true });
185
+ }
186
+ if (existsSync(htmlPath) && statSync(htmlPath).isFile()) {
187
+ const html = readFileSync(htmlPath, "utf-8");
188
+ writeFileSync(join(assetsDir, "index.html"), html);
189
+ console.log(` Copied: ${htmlPath} \u2192 assets/index.html`);
190
+ } else {
191
+ throw new Error(`HTML file not found or is a directory: ${htmlPath}`);
192
+ }
193
+ }
194
+ const gradlewPath = join(output, "gradlew");
195
+ if (!existsSync(gradlewPath)) {
196
+ console.log(" Generating Gradle wrapper...");
197
+ try {
198
+ await $`cd ${output} && gradle wrapper`.quiet();
199
+ } catch {
200
+ console.log("\u26A0\uFE0F Gradle not found. Please install Android Studio or Gradle.");
201
+ console.log(" Download: https://developer.android.com/studio");
202
+ return;
203
+ }
204
+ }
205
+ const buildType = release ? "assembleRelease" : "assembleDebug";
206
+ console.log(` Building ${release ? "release" : "debug"} APK...`);
207
+ try {
208
+ await $`cd ${output} && ./gradlew ${buildType}`;
209
+ const apkPath = release ? "app/build/outputs/apk/release/app-release.apk" : "app/build/outputs/apk/debug/app-debug.apk";
210
+ console.log(`\u2705 APK built: ${apkPath}`);
211
+ } catch (error) {
212
+ console.error("Build failed. Open in Android Studio for details.");
213
+ throw error;
214
+ }
215
+ console.log("");
216
+ }
217
+ async function open(options) {
218
+ const { output } = options;
219
+ if (!existsSync(output)) {
220
+ throw new Error(`No Android project found in ${output}. Run 'craft android init' first.`);
221
+ }
222
+ console.log(`\uD83D\uDE80 Opening Android project in Android Studio...`);
223
+ try {
224
+ await $`open -a "Android Studio" ${output}`.quiet();
225
+ } catch {
226
+ try {
227
+ await $`studio ${output}`.quiet();
228
+ } catch {
229
+ console.log("\u26A0\uFE0F Android Studio not found.");
230
+ console.log(" Please open the project manually in Android Studio:");
231
+ console.log(` ${output}`);
232
+ }
233
+ }
234
+ }
235
+ async function run(options) {
236
+ const { device, output } = options;
237
+ await build({ output });
238
+ console.log("\uD83D\uDCF1 Installing and running on device...");
239
+ try {
240
+ const apkPath = join(output, "app/build/outputs/apk/debug/app-debug.apk");
241
+ if (device) {
242
+ await $`adb -s ${device} install -r ${apkPath}`;
243
+ } else {
244
+ await $`adb install -r ${apkPath}`;
245
+ }
246
+ const config = JSON.parse(readFileSync(join(output, "craft.config.json"), "utf-8"));
247
+ const launchCmd = `${config.packageName}/.MainActivity`;
248
+ if (device) {
249
+ await $`adb -s ${device} shell am start -n ${launchCmd}`;
250
+ } else {
251
+ await $`adb shell am start -n ${launchCmd}`;
252
+ }
253
+ console.log("\u2705 App launched");
254
+ } catch (error) {
255
+ console.error("Failed to install/run. Make sure:");
256
+ console.error(" 1. ADB is installed and in PATH");
257
+ console.error(" 2. A device/emulator is connected (run: adb devices)");
258
+ throw error;
259
+ }
260
+ }
261
+ export {
262
+ run,
263
+ open,
264
+ init,
265
+ build
266
+ };
267
+
268
+ //# debugId=5497ABB54764526564756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../android/src/index.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Craft Android Builder\n *\n * Generates native Android apps from web content using WebView.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { $ } from 'bun'\n\nconst TEMPLATES_DIR = join(dirname(import.meta.dir), 'templates')\n\nexport interface CraftAndroidConfig {\n appName: string\n packageName: string\n version?: string\n versionCode?: number\n darkMode?: boolean\n backgroundColor?: string\n enableSpeechRecognition?: boolean\n enableHaptics?: boolean\n enableShare?: boolean\n enableCamera?: boolean\n enableBiometric?: boolean\n enablePushNotifications?: boolean\n devServerURL?: string\n minSdk?: number\n targetSdk?: number\n}\n\nexport interface InitOptions {\n name: string\n packageName?: string\n output: string\n}\n\nexport interface BuildOptions {\n htmlPath?: string\n devServer?: string\n output: string\n release?: boolean\n}\n\nexport interface OpenOptions {\n output: string\n}\n\nexport interface RunOptions {\n device?: string\n output: string\n}\n\n/**\n * Initialize a new Android project\n */\nexport async function init(options: InitOptions): Promise<void> {\n const { name, packageName, output } = options\n\n console.log(`\\n⚡ Initializing Craft Android project: ${name}`)\n console.log(` Output: ${output}\\n`)\n\n // Generate package name from app name if not provided\n const finalPackageName = packageName || `com.craft.${name.toLowerCase().replace(/[^a-z0-9]/g, '')}`\n const packagePath = finalPackageName.replace(/\\./g, '/')\n\n // Create directory structure\n const dirs = [\n output,\n join(output, 'app/src/main/java', packagePath),\n join(output, 'app/src/main/res/layout'),\n join(output, 'app/src/main/res/values'),\n join(output, 'app/src/main/res/drawable'),\n join(output, 'app/src/main/assets'),\n join(output, 'gradle/wrapper'),\n ]\n\n for (const dir of dirs) {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n }\n\n // Create craft.config.json\n const config: CraftAndroidConfig = {\n appName: name,\n packageName: finalPackageName,\n version: '1.0.0',\n versionCode: 1,\n darkMode: true,\n backgroundColor: '#1a1a2e',\n enableSpeechRecognition: true,\n enableHaptics: true,\n enableShare: true,\n enableCamera: true,\n enableBiometric: true,\n enablePushNotifications: false,\n minSdk: 24,\n targetSdk: 34,\n }\n\n writeFileSync(join(output, 'craft.config.json'), JSON.stringify(config, null, 2))\n\n // Copy templates\n const mainActivityTemplate = readFileSync(join(TEMPLATES_DIR, 'MainActivity.kt.template'), 'utf-8')\n const mainActivity = mainActivityTemplate\n .replace(/\\{\\{PACKAGE_NAME\\}\\}/g, finalPackageName)\n .replace(/\\{\\{APP_NAME\\}\\}/g, name)\n\n writeFileSync(join(output, 'app/src/main/java', packagePath, 'MainActivity.kt'), mainActivity)\n\n // Create CraftBridge.kt\n const craftBridgeTemplate = readFileSync(join(TEMPLATES_DIR, 'CraftBridge.kt.template'), 'utf-8')\n const craftBridge = craftBridgeTemplate.replace(/\\{\\{PACKAGE_NAME\\}\\}/g, finalPackageName)\n writeFileSync(join(output, 'app/src/main/java', packagePath, 'CraftBridge.kt'), craftBridge)\n\n // Create AndroidManifest.xml\n const manifestTemplate = readFileSync(join(TEMPLATES_DIR, 'AndroidManifest.xml.template'), 'utf-8')\n const manifest = manifestTemplate\n .replace(/\\{\\{PACKAGE_NAME\\}\\}/g, finalPackageName)\n .replace(/\\{\\{APP_NAME\\}\\}/g, name)\n\n writeFileSync(join(output, 'app/src/main/AndroidManifest.xml'), manifest)\n\n // Create build.gradle.kts (project level)\n const projectGradleTemplate = readFileSync(join(TEMPLATES_DIR, 'build.gradle.kts.project.template'), 'utf-8')\n writeFileSync(join(output, 'build.gradle.kts'), projectGradleTemplate)\n\n // Create build.gradle.kts (app level)\n const appGradleTemplate = readFileSync(join(TEMPLATES_DIR, 'build.gradle.kts.app.template'), 'utf-8')\n const appGradle = appGradleTemplate\n .replace(/\\{\\{PACKAGE_NAME\\}\\}/g, finalPackageName)\n .replace(/\\{\\{VERSION_NAME\\}\\}/g, config.version || '1.0.0')\n .replace(/\\{\\{VERSION_CODE\\}\\}/g, String(config.versionCode || 1))\n .replace(/\\{\\{MIN_SDK\\}\\}/g, String(config.minSdk || 24))\n .replace(/\\{\\{TARGET_SDK\\}\\}/g, String(config.targetSdk || 34))\n\n writeFileSync(join(output, 'app/build.gradle.kts'), appGradle)\n\n // Create settings.gradle.kts\n const settingsTemplate = readFileSync(join(TEMPLATES_DIR, 'settings.gradle.kts.template'), 'utf-8')\n const settings = settingsTemplate.replace(/\\{\\{APP_NAME\\}\\}/g, name)\n writeFileSync(join(output, 'settings.gradle.kts'), settings)\n\n // Create gradle.properties\n const gradleProperties = `org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\nandroid.useAndroidX=true\nkotlin.code.style=official\nandroid.nonTransitiveRClass=true\n`\n writeFileSync(join(output, 'gradle.properties'), gradleProperties)\n\n // Create local.properties placeholder\n writeFileSync(join(output, 'local.properties'), '# SDK location will be set by Android Studio\\n')\n\n // Create gradle wrapper properties\n const gradleWrapperProps = `distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\\\://services.gradle.org/distributions/gradle-8.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n`\n writeFileSync(join(output, 'gradle/wrapper/gradle-wrapper.properties'), gradleWrapperProps)\n\n // Create res/values files\n const stringsXml = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <string name=\"app_name\">${name}</string>\n</resources>\n`\n writeFileSync(join(output, 'app/src/main/res/values/strings.xml'), stringsXml)\n\n const colorsXml = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"primary\">#1a1a2e</color>\n <color name=\"primary_dark\">#16162b</color>\n <color name=\"accent\">#4ade80</color>\n <color name=\"background\">${config.backgroundColor}</color>\n</resources>\n`\n writeFileSync(join(output, 'app/src/main/res/values/colors.xml'), colorsXml)\n\n const themesXml = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <style name=\"Theme.CraftApp\" parent=\"Theme.Material3.DayNight.NoActionBar\">\n <item name=\"android:statusBarColor\">@color/primary_dark</item>\n <item name=\"android:navigationBarColor\">@color/primary</item>\n <item name=\"android:windowBackground\">@color/background</item>\n </style>\n</resources>\n`\n writeFileSync(join(output, 'app/src/main/res/values/themes.xml'), themesXml)\n\n // Create activity_main.xml\n const activityMainXml = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\"\n android:fitsSystemWindows=\"true\">\n\n <WebView\n android:id=\"@+id/webview\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\" />\n\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n`\n writeFileSync(join(output, 'app/src/main/res/layout/activity_main.xml'), activityMainXml)\n\n // Create placeholder index.html\n const placeholderHtml = `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"viewport-fit=cover, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n <title>${name}</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: 'Roboto', sans-serif;\n background: ${config.backgroundColor};\n color: white;\n min-height: 100vh;\n display: flex;\n justify-content: center;\n align-items: center;\n }\n .container { text-align: center; padding: 2rem; }\n h1 { font-size: 2.5rem; margin-bottom: 1rem; }\n p { opacity: 0.7; }\n .ready { color: #4ade80; font-size: 0.9rem; margin-top: 2rem; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>⚡ ${name}</h1>\n <p>Built with Craft Android</p>\n <p class=\"ready\" id=\"status\">Waiting for Craft bridge...</p>\n </div>\n <script>\n window.addEventListener('craftReady', (e) => {\n document.getElementById('status').textContent = \\`✓ Craft bridge ready (platform: \\${e.detail.platform})\\`;\n console.log('Craft capabilities:', e.detail.capabilities);\n });\n </script>\n</body>\n</html>`\n\n writeFileSync(join(output, 'app/src/main/assets/index.html'), placeholderHtml)\n\n console.log('✅ Project initialized')\n console.log('')\n console.log('Next steps:')\n console.log(` 1. cd ${output}`)\n console.log(' 2. Add your web content to app/src/main/assets/index.html')\n console.log(' 3. Run: craft android build')\n console.log(' 4. Run: craft android open')\n console.log('')\n}\n\n/**\n * Build Android project\n */\nexport async function build(options: BuildOptions): Promise<void> {\n const { htmlPath, devServer, output, release } = options\n\n console.log('\\n📦 Building Craft Android project...')\n\n // Load config\n const configPath = join(output, 'craft.config.json')\n if (!existsSync(configPath)) {\n throw new Error(`No craft.config.json found in ${output}. Run 'craft android init' first.`)\n }\n\n const config: CraftAndroidConfig = JSON.parse(readFileSync(configPath, 'utf-8'))\n\n // Update dev server URL if provided\n if (devServer) {\n config.devServerURL = devServer\n writeFileSync(configPath, JSON.stringify(config, null, 2))\n console.log(` Dev server: ${devServer}`)\n }\n\n // Copy HTML if provided\n if (htmlPath) {\n const assetsDir = join(output, 'app/src/main/assets')\n if (!existsSync(assetsDir)) {\n mkdirSync(assetsDir, { recursive: true })\n }\n\n if (existsSync(htmlPath) && statSync(htmlPath).isFile()) {\n const html = readFileSync(htmlPath, 'utf-8')\n writeFileSync(join(assetsDir, 'index.html'), html)\n console.log(` Copied: ${htmlPath} → assets/index.html`)\n }\nelse {\n throw new Error(`HTML file not found or is a directory: ${htmlPath}`)\n }\n }\n\n // Check if gradlew exists\n const gradlewPath = join(output, 'gradlew')\n if (!existsSync(gradlewPath)) {\n console.log(' Generating Gradle wrapper...')\n try {\n await $`cd ${output} && gradle wrapper`.quiet()\n }\ncatch {\n console.log('⚠️ Gradle not found. Please install Android Studio or Gradle.')\n console.log(' Download: https://developer.android.com/studio')\n return\n }\n }\n\n // Build the project\n const buildType = release ? 'assembleRelease' : 'assembleDebug'\n console.log(` Building ${release ? 'release' : 'debug'} APK...`)\n\n try {\n await $`cd ${output} && ./gradlew ${buildType}`\n const apkPath = release\n ? 'app/build/outputs/apk/release/app-release.apk'\n : 'app/build/outputs/apk/debug/app-debug.apk'\n console.log(`✅ APK built: ${apkPath}`)\n }\ncatch (error) {\n console.error('Build failed. Open in Android Studio for details.')\n throw error\n }\n\n console.log('')\n}\n\n/**\n * Open Android project in Android Studio\n */\nexport async function open(options: OpenOptions): Promise<void> {\n const { output } = options\n\n if (!existsSync(output)) {\n throw new Error(`No Android project found in ${output}. Run 'craft android init' first.`)\n }\n\n console.log(`🚀 Opening Android project in Android Studio...`)\n\n // Try to open with Android Studio\n try {\n // macOS\n await $`open -a \"Android Studio\" ${output}`.quiet()\n }\ncatch {\n try {\n // Linux\n await $`studio ${output}`.quiet()\n }\ncatch {\n console.log('⚠️ Android Studio not found.')\n console.log(' Please open the project manually in Android Studio:')\n console.log(` ${output}`)\n }\n }\n}\n\n/**\n * Run on Android device or emulator\n */\nexport async function run(options: RunOptions): Promise<void> {\n const { device, output } = options\n\n // First build\n await build({ output })\n\n // Install and run\n console.log('📱 Installing and running on device...')\n\n try {\n const apkPath = join(output, 'app/build/outputs/apk/debug/app-debug.apk')\n\n if (device) {\n await $`adb -s ${device} install -r ${apkPath}`\n }\nelse {\n await $`adb install -r ${apkPath}`\n }\n\n // Get package name from config\n const config: CraftAndroidConfig = JSON.parse(\n readFileSync(join(output, 'craft.config.json'), 'utf-8')\n )\n\n // Launch the app\n const launchCmd = `${config.packageName}/.MainActivity`\n if (device) {\n await $`adb -s ${device} shell am start -n ${launchCmd}`\n }\nelse {\n await $`adb shell am start -n ${launchCmd}`\n }\n\n console.log('✅ App launched')\n }\ncatch (error) {\n console.error('Failed to install/run. Make sure:')\n console.error(' 1. ADB is installed and in PATH')\n console.error(' 2. A device/emulator is connected (run: adb devices)')\n throw error\n }\n}\n\nexport type { CraftAndroidConfig }\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "5497ABB54764526564756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,69 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
+ xmlns:tools="http://schemas.android.com/tools">
4
+
5
+ <!-- Permissions -->
6
+ <uses-permission android:name="android.permission.INTERNET" />
7
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
8
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
9
+ <uses-permission android:name="android.permission.CAMERA" />
10
+ <uses-permission android:name="android.permission.VIBRATE" />
11
+ <uses-permission android:name="android.permission.USE_BIOMETRIC" />
12
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
13
+ android:maxSdkVersion="32" />
14
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
15
+ android:maxSdkVersion="28" />
16
+
17
+ <!-- Location permissions -->
18
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
19
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
20
+
21
+ <!-- Flashlight permission -->
22
+ <uses-permission android:name="android.permission.FLASHLIGHT" />
23
+
24
+ <!-- Contacts permissions -->
25
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
26
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
27
+
28
+ <!-- Calendar permissions -->
29
+ <uses-permission android:name="android.permission.READ_CALENDAR" />
30
+ <uses-permission android:name="android.permission.WRITE_CALENDAR" />
31
+
32
+ <!-- Notifications permission (Android 13+) -->
33
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
34
+
35
+ <!-- Keep screen on -->
36
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
37
+
38
+ <!-- Camera feature (optional) -->
39
+ <uses-feature android:name="android.hardware.camera" android:required="false" />
40
+ <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
41
+ <uses-feature android:name="android.hardware.camera.flash" android:required="false" />
42
+ <uses-feature android:name="android.hardware.location.gps" android:required="false" />
43
+
44
+ <application
45
+ android:allowBackup="true"
46
+ android:icon="@mipmap/ic_launcher"
47
+ android:label="@string/app_name"
48
+ android:roundIcon="@mipmap/ic_launcher_round"
49
+ android:supportsRtl="true"
50
+ android:theme="@style/Theme.CraftApp"
51
+ android:usesCleartextTraffic="true"
52
+ android:hardwareAccelerated="true"
53
+ tools:targetApi="34">
54
+
55
+ <activity
56
+ android:name=".MainActivity"
57
+ android:exported="true"
58
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
59
+ android:windowSoftInputMode="adjustResize"
60
+ android:launchMode="singleTask">
61
+ <intent-filter>
62
+ <action android:name="android.intent.action.MAIN" />
63
+ <category android:name="android.intent.category.LAUNCHER" />
64
+ </intent-filter>
65
+ </activity>
66
+
67
+ </application>
68
+
69
+ </manifest>