@thelacanians/vue-native-cli 0.4.15 → 0.6.2
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.
- package/dist/cli.js +329 -15
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +118 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
- package/native/android/gradle.properties +1 -0
- package/native/android/gradlew +1 -1
- package/native/ios/VueNativeCore/Package.swift +1 -1
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +1 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +143 -5
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
- package/native/macos/VueNativeMacOS/Package.swift +34 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
- package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
- package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
- package/native/shared/VueNativeShared/AGENTS.md +129 -0
- package/native/shared/VueNativeShared/Package.swift +14 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
- package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
- package/package.json +8 -2
package/dist/cli.js
CHANGED
|
@@ -59,15 +59,16 @@ function ensureOutputDir(outputPath) {
|
|
|
59
59
|
mkdirSync(outputPath, { recursive: true });
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
var buildCommand = new Command("build").description("Create a release build of the app").argument("<platform>", "platform to build for (ios, android)").option("--mode <mode>", "build mode", "release").option("--output <path>", "output directory for the build artifact", "./build").option("--scheme <scheme>", "Xcode scheme to build (iOS only)").option("--aab", "build Android App Bundle (.aab) instead of APK").action(async (platform, options) => {
|
|
63
|
-
if (platform !== "ios" && platform !== "android") {
|
|
64
|
-
console.error(pc.red('Platform must be "ios" or "
|
|
62
|
+
var buildCommand = new Command("build").description("Create a release build of the app").argument("<platform>", "platform to build for (ios, android, macos)").option("--mode <mode>", "build mode", "release").option("--output <path>", "output directory for the build artifact", "./build").option("--scheme <scheme>", "Xcode scheme to build (iOS only)").option("--aab", "build Android App Bundle (.aab) instead of APK").action(async (platform, options) => {
|
|
63
|
+
if (platform !== "ios" && platform !== "android" && platform !== "macos") {
|
|
64
|
+
console.error(pc.red('Platform must be "ios", "android", or "macos"'));
|
|
65
65
|
process.exit(1);
|
|
66
66
|
}
|
|
67
67
|
const cwd = process.cwd();
|
|
68
68
|
const outputDir = join(cwd, options.output);
|
|
69
|
+
const platformLabel = platform === "ios" ? "iOS" : platform === "android" ? "Android" : "macOS";
|
|
69
70
|
console.log(pc.cyan(`
|
|
70
|
-
Vue Native \u2014 ${options.mode.charAt(0).toUpperCase() + options.mode.slice(1)} Build (${
|
|
71
|
+
Vue Native \u2014 ${options.mode.charAt(0).toUpperCase() + options.mode.slice(1)} Build (${platformLabel})
|
|
71
72
|
`));
|
|
72
73
|
console.log(pc.white(" Building JS bundle for production..."));
|
|
73
74
|
try {
|
|
@@ -79,8 +80,10 @@ var buildCommand = new Command("build").description("Create a release build of t
|
|
|
79
80
|
}
|
|
80
81
|
if (platform === "ios") {
|
|
81
82
|
buildIOS(cwd, outputDir, options);
|
|
82
|
-
} else {
|
|
83
|
+
} else if (platform === "android") {
|
|
83
84
|
buildAndroid(cwd, outputDir, options);
|
|
85
|
+
} else {
|
|
86
|
+
buildMacOS(cwd, outputDir, options);
|
|
84
87
|
}
|
|
85
88
|
});
|
|
86
89
|
function buildIOS(cwd, outputDir, options) {
|
|
@@ -244,6 +247,85 @@ function buildAndroid(cwd, outputDir, options) {
|
|
|
244
247
|
}
|
|
245
248
|
});
|
|
246
249
|
}
|
|
250
|
+
function buildMacOS(cwd, outputDir, options) {
|
|
251
|
+
const macosDir = join(cwd, "macos");
|
|
252
|
+
const project = findXcodeProject(macosDir);
|
|
253
|
+
if (!project) {
|
|
254
|
+
console.log(pc.yellow(" No Xcode project found in ./macos/"));
|
|
255
|
+
console.log(pc.dim(" To add macOS support, create an Xcode project in the macos/ directory."));
|
|
256
|
+
console.log(pc.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const projectFlag = project.isWorkspace ? "-workspace" : "-project";
|
|
260
|
+
const scheme = options.scheme || project.path.split("/").pop()?.replace(/\.(xcworkspace|xcodeproj)$/, "") || "App";
|
|
261
|
+
const configuration = options.mode === "release" ? "Release" : "Debug";
|
|
262
|
+
const archivePath = join(outputDir, `${scheme}.xcarchive`);
|
|
263
|
+
ensureOutputDir(outputDir);
|
|
264
|
+
console.log(pc.white(` Archiving ${scheme} (${configuration}) for macOS...`));
|
|
265
|
+
console.log(pc.dim(` Archive path: ${archivePath}`));
|
|
266
|
+
const xcodebuild = spawn(
|
|
267
|
+
"xcodebuild",
|
|
268
|
+
[
|
|
269
|
+
projectFlag,
|
|
270
|
+
project.path,
|
|
271
|
+
"-scheme",
|
|
272
|
+
scheme,
|
|
273
|
+
"-configuration",
|
|
274
|
+
configuration,
|
|
275
|
+
"-destination",
|
|
276
|
+
"generic/platform=macOS",
|
|
277
|
+
"-archivePath",
|
|
278
|
+
archivePath,
|
|
279
|
+
"archive"
|
|
280
|
+
],
|
|
281
|
+
{
|
|
282
|
+
cwd,
|
|
283
|
+
stdio: "pipe",
|
|
284
|
+
env: { ...process.env, DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer" }
|
|
285
|
+
}
|
|
286
|
+
);
|
|
287
|
+
const cleanup = () => {
|
|
288
|
+
if (xcodebuild && !xcodebuild.killed) {
|
|
289
|
+
xcodebuild.kill();
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
process.on("exit", cleanup);
|
|
293
|
+
process.on("SIGINT", cleanup);
|
|
294
|
+
process.on("SIGTERM", cleanup);
|
|
295
|
+
xcodebuild.stdout?.on("data", (data) => {
|
|
296
|
+
const text = data.toString().trim();
|
|
297
|
+
if (text.includes("Compiling") || text.includes("Linking") || text.includes("Signing")) {
|
|
298
|
+
console.log(pc.dim(` ${text.split("\n").pop()}`));
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
xcodebuild.stderr?.on("data", (data) => {
|
|
302
|
+
const text = data.toString().trim();
|
|
303
|
+
if (text.includes("error:")) {
|
|
304
|
+
console.log(pc.red(` ${text}`));
|
|
305
|
+
} else if (text.includes("warning:")) {
|
|
306
|
+
console.log(pc.yellow(` ${text}`));
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
xcodebuild.on("error", (err) => {
|
|
310
|
+
console.error(pc.red(` xcodebuild process error: ${err.message}`));
|
|
311
|
+
cleanup();
|
|
312
|
+
});
|
|
313
|
+
xcodebuild.on("close", (code) => {
|
|
314
|
+
if (code !== 0) {
|
|
315
|
+
console.error(pc.red(` \u2717 Archive failed (exit code ${code})`));
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
console.log(pc.green(" \u2713 Archive successful\n"));
|
|
319
|
+
if (existsSync(archivePath)) {
|
|
320
|
+
console.log(pc.green(` Archive: ${archivePath}`));
|
|
321
|
+
console.log(pc.dim(" To export a .app or .pkg, open the archive in Xcode Organizer or run:"));
|
|
322
|
+
console.log(pc.dim(` xcodebuild -exportArchive -archivePath "${archivePath}" -exportOptionsPlist ExportOptions.plist -exportPath "${outputDir}"
|
|
323
|
+
`));
|
|
324
|
+
} else {
|
|
325
|
+
console.log(pc.yellow(" Archive path not found. Check Xcode build settings.\n"));
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
247
329
|
|
|
248
330
|
// src/commands/create.ts
|
|
249
331
|
import { Command as Command2 } from "commander";
|
|
@@ -253,6 +335,9 @@ import { fileURLToPath } from "url";
|
|
|
253
335
|
import { existsSync as existsSync2 } from "fs";
|
|
254
336
|
import pc2 from "picocolors";
|
|
255
337
|
var VERSION = "0.4.14";
|
|
338
|
+
var JS_PACKAGE_VERSION = "^0.5.0";
|
|
339
|
+
var VITE_PLUGIN_VUE_VERSION = "^6.0.5";
|
|
340
|
+
var VITE_VERSION = "^8.0.0";
|
|
256
341
|
var createCommand = new Command2("create").description("Create a new Vue Native project").argument("<name>", "project name").option("-t, --template <template>", "project template (blank, tabs, drawer)", "blank").action(async (name, options) => {
|
|
257
342
|
const template = options.template;
|
|
258
343
|
if (!["blank", "tabs", "drawer"].includes(template)) {
|
|
@@ -278,14 +363,14 @@ Creating Vue Native project: ${pc2.bold(name)} (template: ${template})
|
|
|
278
363
|
typecheck: "tsc --noEmit"
|
|
279
364
|
},
|
|
280
365
|
dependencies: {
|
|
281
|
-
"@thelacanians/vue-native-runtime":
|
|
282
|
-
"@thelacanians/vue-native-navigation":
|
|
366
|
+
"@thelacanians/vue-native-runtime": JS_PACKAGE_VERSION,
|
|
367
|
+
"@thelacanians/vue-native-navigation": JS_PACKAGE_VERSION,
|
|
283
368
|
"vue": "^3.5.0"
|
|
284
369
|
},
|
|
285
370
|
devDependencies: {
|
|
286
|
-
"@thelacanians/vue-native-vite-plugin":
|
|
287
|
-
"@vitejs/plugin-vue":
|
|
288
|
-
"vite":
|
|
371
|
+
"@thelacanians/vue-native-vite-plugin": JS_PACKAGE_VERSION,
|
|
372
|
+
"@vitejs/plugin-vue": VITE_PLUGIN_VUE_VERSION,
|
|
373
|
+
"vite": VITE_VERSION,
|
|
289
374
|
"typescript": "^5.7.0"
|
|
290
375
|
}
|
|
291
376
|
}, null, 2));
|
|
@@ -1337,14 +1422,15 @@ function findApkPath(androidDir) {
|
|
|
1337
1422
|
}
|
|
1338
1423
|
return null;
|
|
1339
1424
|
}
|
|
1340
|
-
var runCommand = new Command4("run").description("Build and run the app").argument("<platform>", "platform to run on (ios, android)").option("--device", "run on physical device instead of simulator").option("--scheme <scheme>", "Xcode scheme to build").option("--simulator <name>", "simulator name", "iPhone 16").option("--bundle-id <id>", "app bundle identifier").option("--package <name>", "Android package name", "com.vuenative.app").option("--activity <name>", "Android activity name", ".MainActivity").action(async (platform, options) => {
|
|
1341
|
-
if (platform !== "ios" && platform !== "android") {
|
|
1342
|
-
console.error(pc4.red('Platform must be "ios" or "
|
|
1425
|
+
var runCommand = new Command4("run").description("Build and run the app").argument("<platform>", "platform to run on (ios, android, macos)").option("--device", "run on physical device instead of simulator").option("--scheme <scheme>", "Xcode scheme to build").option("--simulator <name>", "simulator name", "iPhone 16").option("--bundle-id <id>", "app bundle identifier").option("--package <name>", "Android package name", "com.vuenative.app").option("--activity <name>", "Android activity name", ".MainActivity").action(async (platform, options) => {
|
|
1426
|
+
if (platform !== "ios" && platform !== "android" && platform !== "macos") {
|
|
1427
|
+
console.error(pc4.red('Platform must be "ios", "android", or "macos"'));
|
|
1343
1428
|
process.exit(1);
|
|
1344
1429
|
}
|
|
1345
1430
|
const cwd = process.cwd();
|
|
1431
|
+
const platformLabel = platform === "ios" ? "iOS" : platform === "android" ? "Android" : "macOS";
|
|
1346
1432
|
console.log(pc4.cyan(`
|
|
1347
|
-
\u{1F4F1} Vue Native \u2014 Run ${
|
|
1433
|
+
\u{1F4F1} Vue Native \u2014 Run ${platformLabel}
|
|
1348
1434
|
`));
|
|
1349
1435
|
console.log(pc4.white(" Building JS bundle..."));
|
|
1350
1436
|
try {
|
|
@@ -1356,8 +1442,10 @@ var runCommand = new Command4("run").description("Build and run the app").argume
|
|
|
1356
1442
|
}
|
|
1357
1443
|
if (platform === "ios") {
|
|
1358
1444
|
runIOS(cwd, options);
|
|
1359
|
-
} else {
|
|
1445
|
+
} else if (platform === "android") {
|
|
1360
1446
|
runAndroid(cwd, options);
|
|
1447
|
+
} else {
|
|
1448
|
+
runMacOS(cwd, options);
|
|
1361
1449
|
}
|
|
1362
1450
|
});
|
|
1363
1451
|
function runIOS(cwd, options) {
|
|
@@ -1529,6 +1617,231 @@ function runAndroid(cwd, options) {
|
|
|
1529
1617
|
}
|
|
1530
1618
|
});
|
|
1531
1619
|
}
|
|
1620
|
+
function runMacOS(cwd, options) {
|
|
1621
|
+
const macosDir = join4(cwd, "macos");
|
|
1622
|
+
if (!existsSync4(macosDir)) {
|
|
1623
|
+
console.log(pc4.yellow(" No macos/ directory found."));
|
|
1624
|
+
console.log(pc4.dim(" To add macOS support, create an Xcode project in the macos/ directory."));
|
|
1625
|
+
console.log(pc4.dim(" Bundle has been built to dist/vue-native-bundle.js\n"));
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
let xcodeProject = null;
|
|
1629
|
+
for (const ext of [".xcworkspace", ".xcodeproj"]) {
|
|
1630
|
+
try {
|
|
1631
|
+
const entries = readdirSync2(macosDir);
|
|
1632
|
+
const match = entries.find((e) => e.endsWith(ext));
|
|
1633
|
+
if (match) {
|
|
1634
|
+
xcodeProject = join4(macosDir, match);
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
} catch {
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
if (!xcodeProject) {
|
|
1641
|
+
console.log(pc4.yellow(" No Xcode project found in ./macos/"));
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
const isWorkspace = xcodeProject.endsWith(".xcworkspace");
|
|
1645
|
+
const scheme = options.scheme || xcodeProject.split("/").pop()?.replace(/\.(xcworkspace|xcodeproj)$/, "") || "App";
|
|
1646
|
+
const projectFlag = isWorkspace ? "-workspace" : "-project";
|
|
1647
|
+
console.log(pc4.white(` Building ${scheme} for macOS...`));
|
|
1648
|
+
const xcodebuild = spawn3(
|
|
1649
|
+
"xcodebuild",
|
|
1650
|
+
[projectFlag, xcodeProject, "-scheme", scheme, "-destination", "platform=macOS", "build"],
|
|
1651
|
+
{
|
|
1652
|
+
cwd,
|
|
1653
|
+
stdio: "pipe",
|
|
1654
|
+
env: { ...process.env, DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer" }
|
|
1655
|
+
}
|
|
1656
|
+
);
|
|
1657
|
+
xcodebuild.stderr?.on("data", (data) => {
|
|
1658
|
+
const text = data.toString().trim();
|
|
1659
|
+
if (text.includes("error:") || text.includes("warning:")) {
|
|
1660
|
+
console.log(pc4.dim(` ${text}`));
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
xcodebuild.on("close", (code) => {
|
|
1664
|
+
if (code !== 0) {
|
|
1665
|
+
console.error(pc4.red(` \u2717 Build failed (exit code ${code})`));
|
|
1666
|
+
process.exit(1);
|
|
1667
|
+
}
|
|
1668
|
+
console.log(pc4.green(" \u2713 Build successful\n"));
|
|
1669
|
+
const derivedDataBase = join4(process.env.HOME || "~", "Library/Developer/Xcode/DerivedData");
|
|
1670
|
+
let appPath = null;
|
|
1671
|
+
if (existsSync4(derivedDataBase)) {
|
|
1672
|
+
try {
|
|
1673
|
+
const projects = readdirSync2(derivedDataBase);
|
|
1674
|
+
for (const project of projects.reverse()) {
|
|
1675
|
+
const productsDir = join4(derivedDataBase, project, "Build/Products/Debug");
|
|
1676
|
+
if (existsSync4(productsDir)) {
|
|
1677
|
+
const entries = readdirSync2(productsDir);
|
|
1678
|
+
const app = entries.find((e) => e.endsWith(".app"));
|
|
1679
|
+
if (app) {
|
|
1680
|
+
appPath = join4(productsDir, app);
|
|
1681
|
+
break;
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
} catch {
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
if (appPath) {
|
|
1689
|
+
console.log(pc4.white(` Launching ${appPath}...`));
|
|
1690
|
+
try {
|
|
1691
|
+
execSync3(`open "${appPath}"`, { stdio: "pipe" });
|
|
1692
|
+
console.log(pc4.green(` \u2713 App launched
|
|
1693
|
+
`));
|
|
1694
|
+
} catch (err) {
|
|
1695
|
+
console.error(pc4.red(` \u2717 Failed to launch app: ${err.message}`));
|
|
1696
|
+
}
|
|
1697
|
+
} else {
|
|
1698
|
+
console.log(pc4.yellow(" Could not locate .app bundle in DerivedData."));
|
|
1699
|
+
console.log(pc4.dim(" Try running the app from Xcode directly.\n"));
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
// src/commands/generate.ts
|
|
1705
|
+
import { Command as Command5 } from "commander";
|
|
1706
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1707
|
+
import { join as join5 } from "path";
|
|
1708
|
+
import pc5 from "picocolors";
|
|
1709
|
+
import { parseDirectory } from "@thelacanians/vue-native-sfc-parser";
|
|
1710
|
+
import { generateCode, writeGeneratedFiles, cleanGeneratedFiles, validateNativeBlocks, formatValidationErrors } from "@thelacanians/vue-native-codegen";
|
|
1711
|
+
var generateCommand = new Command5("generate").description("Generate native code from <native> blocks in Vue SFC files").option("--root <path>", "project root directory", process.cwd()).option("--watch", "watch mode - regenerate on file changes").option("--clean", "clean generated files before generating").option("--ios-output <path>", "iOS Swift output directory").option("--android-output <path>", "Android Kotlin output directory").option("--macos-output <path>", "macOS Swift output directory").option("--ts-output <path>", "TypeScript output directory").option("--no-swift", "disable Swift generation").option("--no-kotlin", "disable Kotlin generation").option("--no-typescript", "disable TypeScript generation").option("--exclude <patterns>", "patterns to exclude (comma-separated)").action(async (options) => {
|
|
1712
|
+
const cwd = options.root;
|
|
1713
|
+
const appDir = join5(cwd, "app");
|
|
1714
|
+
if (!existsSync5(appDir)) {
|
|
1715
|
+
console.error(pc5.red(`Error: App directory not found at ${appDir}`));
|
|
1716
|
+
console.error(pc5.yellow("Make sure you run this command from your project root."));
|
|
1717
|
+
process.exit(1);
|
|
1718
|
+
}
|
|
1719
|
+
const codegenOptions = {
|
|
1720
|
+
root: cwd,
|
|
1721
|
+
includeHeader: true,
|
|
1722
|
+
generateSwift: options.swift !== false,
|
|
1723
|
+
generateKotlin: options.kotlin !== false,
|
|
1724
|
+
generateTypeScript: options.typescript !== false
|
|
1725
|
+
};
|
|
1726
|
+
if (options.iosOutput) codegenOptions.iosOutputDir = options.iosOutput;
|
|
1727
|
+
if (options.androidOutput) codegenOptions.androidOutputDir = options.androidOutput;
|
|
1728
|
+
if (options.macosOutput) codegenOptions.macosOutputDir = options.macosOutput;
|
|
1729
|
+
if (options.tsOutput) codegenOptions.typescriptOutputDir = options.tsOutput;
|
|
1730
|
+
const exclude = options.exclude ? options.exclude.split(",").map((p) => p.trim()) : ["node_modules", "dist", ".git", ".turbo"];
|
|
1731
|
+
async function runGeneration() {
|
|
1732
|
+
console.log(pc5.cyan("\n\u{1F527} Vue Native Code Generator"));
|
|
1733
|
+
console.log(pc5.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
1734
|
+
try {
|
|
1735
|
+
if (options.clean) {
|
|
1736
|
+
console.log(pc5.yellow("\u{1F5D1}\uFE0F Cleaning generated files..."));
|
|
1737
|
+
cleanGeneratedFiles(codegenOptions, cwd);
|
|
1738
|
+
console.log(pc5.green("\u2713 Cleaned\n"));
|
|
1739
|
+
}
|
|
1740
|
+
console.log(pc5.blue("\u{1F4C4} Scanning SFC files..."));
|
|
1741
|
+
const parseResult = parseDirectory(".", {
|
|
1742
|
+
root: cwd,
|
|
1743
|
+
exclude
|
|
1744
|
+
});
|
|
1745
|
+
if (parseResult.errors.length > 0) {
|
|
1746
|
+
console.log(pc5.yellow("\u26A0\uFE0F Parse warnings:"));
|
|
1747
|
+
parseResult.errors.forEach((err) => {
|
|
1748
|
+
console.log(pc5.dim(` - ${err.file}:${err.line || "?"} ${err.message}`));
|
|
1749
|
+
});
|
|
1750
|
+
console.log();
|
|
1751
|
+
}
|
|
1752
|
+
const blockCount = parseResult.allNativeBlocks.length;
|
|
1753
|
+
console.log(pc5.green(`\u2713 Found ${blockCount} <native> block${blockCount !== 1 ? "s" : ""}`));
|
|
1754
|
+
if (blockCount === 0) {
|
|
1755
|
+
console.log(pc5.yellow("\n\u26A0\uFE0F No <native> blocks found."));
|
|
1756
|
+
console.log(pc5.dim('Add <native platform="ios"> or <native platform="android"> blocks to your SFC files.'));
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
console.log(pc5.blue("\u{1F50D} Validating native code..."));
|
|
1760
|
+
const validation = validateNativeBlocks(parseResult.allNativeBlocks);
|
|
1761
|
+
if (!validation.isValid) {
|
|
1762
|
+
console.log(pc5.red("\n\u274C Validation failed:"));
|
|
1763
|
+
console.log(formatValidationErrors(validation));
|
|
1764
|
+
process.exit(1);
|
|
1765
|
+
}
|
|
1766
|
+
if (validation.warnings.length > 0) {
|
|
1767
|
+
console.log(pc5.yellow(`\u26A0\uFE0F ${validation.warnings.length} warning${validation.warnings.length !== 1 ? "s" : ""}`));
|
|
1768
|
+
} else {
|
|
1769
|
+
console.log(pc5.green("\u2713 Validation passed"));
|
|
1770
|
+
}
|
|
1771
|
+
console.log();
|
|
1772
|
+
const iosBlocks = parseResult.allNativeBlocks.filter((b) => b.platform === "ios" || b.platform === "macos");
|
|
1773
|
+
const androidBlocks = parseResult.allNativeBlocks.filter((b) => b.platform === "android");
|
|
1774
|
+
if (iosBlocks.length > 0) {
|
|
1775
|
+
console.log(pc5.dim(` - iOS/macOS: ${iosBlocks.length} block${iosBlocks.length !== 1 ? "s" : ""}`));
|
|
1776
|
+
}
|
|
1777
|
+
if (androidBlocks.length > 0) {
|
|
1778
|
+
console.log(pc5.dim(` - Android: ${androidBlocks.length} block${androidBlocks.length !== 1 ? "s" : ""}`));
|
|
1779
|
+
}
|
|
1780
|
+
console.log();
|
|
1781
|
+
console.log(pc5.blue("\u2699\uFE0F Generating code..."));
|
|
1782
|
+
const codegenResult = generateCode(parseResult.allNativeBlocks, codegenOptions);
|
|
1783
|
+
if (codegenResult.errors.length > 0) {
|
|
1784
|
+
console.log(pc5.red("\n\u274C Generation errors:"));
|
|
1785
|
+
codegenResult.errors.forEach((err) => {
|
|
1786
|
+
console.log(pc5.red(` - ${err.file} ${err.message}`));
|
|
1787
|
+
});
|
|
1788
|
+
process.exit(1);
|
|
1789
|
+
}
|
|
1790
|
+
console.log(pc5.blue("\u{1F4DD} Writing files..."));
|
|
1791
|
+
const writeResult = writeGeneratedFiles(codegenResult, cwd);
|
|
1792
|
+
if (writeResult.errors.length > 0) {
|
|
1793
|
+
console.log(pc5.red("\n\u274C Write errors:"));
|
|
1794
|
+
writeResult.errors.forEach((err) => {
|
|
1795
|
+
console.log(pc5.red(` - ${err.message}`));
|
|
1796
|
+
});
|
|
1797
|
+
process.exit(1);
|
|
1798
|
+
}
|
|
1799
|
+
console.log(pc5.green("\n\u2705 Generation complete!\n"));
|
|
1800
|
+
console.log(pc5.dim("Generated files:"));
|
|
1801
|
+
console.log(pc5.dim(` - Swift: ${codegenResult.stats.swiftFiles} file${codegenResult.stats.swiftFiles !== 1 ? "s" : ""}`));
|
|
1802
|
+
console.log(pc5.dim(` - Kotlin: ${codegenResult.stats.kotlinFiles} file${codegenResult.stats.kotlinFiles !== 1 ? "s" : ""}`));
|
|
1803
|
+
console.log(pc5.dim(` - TypeScript: ${codegenResult.stats.typescriptFiles} file${codegenResult.stats.typescriptFiles !== 1 ? "s" : ""}`));
|
|
1804
|
+
console.log();
|
|
1805
|
+
if (codegenResult.warnings.length > 0) {
|
|
1806
|
+
console.log(pc5.yellow("\u26A0\uFE0F Warnings:"));
|
|
1807
|
+
codegenResult.warnings.forEach((warn) => {
|
|
1808
|
+
console.log(pc5.dim(` - ${warn.message}`));
|
|
1809
|
+
});
|
|
1810
|
+
console.log();
|
|
1811
|
+
}
|
|
1812
|
+
console.log(pc5.green("\u{1F389} Ready to build!\n"));
|
|
1813
|
+
} catch (error) {
|
|
1814
|
+
console.error(pc5.red("\n\u274C Fatal error:"));
|
|
1815
|
+
console.error(pc5.red(error.message));
|
|
1816
|
+
console.error(pc5.dim(error.stack || ""));
|
|
1817
|
+
process.exit(1);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
await runGeneration();
|
|
1821
|
+
if (options.watch) {
|
|
1822
|
+
console.log(pc5.cyan("\n\u{1F441}\uFE0F Watch mode enabled. Press Ctrl+C to stop.\n"));
|
|
1823
|
+
const chokidar = await import("chokidar");
|
|
1824
|
+
const watcher = chokidar.default.watch("app/**/*.vue", {
|
|
1825
|
+
cwd,
|
|
1826
|
+
ignored: exclude,
|
|
1827
|
+
ignoreInitial: true
|
|
1828
|
+
});
|
|
1829
|
+
watcher.on("change", async (file) => {
|
|
1830
|
+
console.log(pc5.dim(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`) + pc5.blue(` Changed: ${file}`));
|
|
1831
|
+
await runGeneration();
|
|
1832
|
+
});
|
|
1833
|
+
watcher.on("error", (error) => {
|
|
1834
|
+
console.error(pc5.red("Watch error:"), error);
|
|
1835
|
+
});
|
|
1836
|
+
process.on("SIGINT", () => {
|
|
1837
|
+
console.log(pc5.yellow("\n\u23F9\uFE0F Stopping watch mode..."));
|
|
1838
|
+
watcher.close();
|
|
1839
|
+
process.exit(0);
|
|
1840
|
+
});
|
|
1841
|
+
await new Promise(() => {
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
});
|
|
1532
1845
|
|
|
1533
1846
|
// src/cli.ts
|
|
1534
1847
|
program.name("vue-native").description("Vue Native \u2014 build native iOS and Android apps with Vue.js").version("0.1.0");
|
|
@@ -1536,4 +1849,5 @@ program.addCommand(buildCommand);
|
|
|
1536
1849
|
program.addCommand(createCommand);
|
|
1537
1850
|
program.addCommand(devCommand);
|
|
1538
1851
|
program.addCommand(runCommand);
|
|
1852
|
+
program.addCommand(generateCommand);
|
|
1539
1853
|
program.parse(process.argv);
|
package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt
CHANGED
|
@@ -6,6 +6,7 @@ import android.os.Looper
|
|
|
6
6
|
import android.util.Log
|
|
7
7
|
import android.view.View
|
|
8
8
|
import android.view.ViewGroup
|
|
9
|
+
import android.widget.FrameLayout
|
|
9
10
|
import org.json.JSONArray
|
|
10
11
|
import org.json.JSONObject
|
|
11
12
|
|
|
@@ -67,6 +68,26 @@ class NativeBridge(private val context: Context) {
|
|
|
67
68
|
/** The container view from the host Activity — where the root view is attached. */
|
|
68
69
|
var hostContainer: ViewGroup? = null
|
|
69
70
|
|
|
71
|
+
// -- Teleport support --
|
|
72
|
+
/** Maps teleport marker IDs for cleanup */
|
|
73
|
+
private val teleportMarkers = mutableMapOf<Int, Pair<Int, Int>>()
|
|
74
|
+
|
|
75
|
+
/** Maps parent node IDs to their teleport containers */
|
|
76
|
+
private val teleportContainers = mutableMapOf<Int, ViewGroup>()
|
|
77
|
+
|
|
78
|
+
/** Modal container for teleporting modals */
|
|
79
|
+
private val modalContainer: FrameLayout by lazy {
|
|
80
|
+
FrameLayout(context).apply {
|
|
81
|
+
layoutParams = FrameLayout.LayoutParams(
|
|
82
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
83
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
84
|
+
)
|
|
85
|
+
setBackgroundColor(android.graphics.Color.TRANSPARENT)
|
|
86
|
+
isClickable = true
|
|
87
|
+
isFocusable = false
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
70
91
|
/** Called when native fires an event to JS (nodeId, eventName, payloadJson). */
|
|
71
92
|
var onFireEvent: ((nodeId: Int, eventName: String, payloadJson: String) -> Unit)? = null
|
|
72
93
|
|
|
@@ -143,6 +164,9 @@ class NativeBridge(private val context: Context) {
|
|
|
143
164
|
"insertBefore" -> handleInsertBefore(args)
|
|
144
165
|
"removeChild" -> handleRemoveChild(args)
|
|
145
166
|
"setRootView" -> handleSetRootView(args)
|
|
167
|
+
"createTeleport" -> handleCreateTeleport(args)
|
|
168
|
+
"removeTeleport" -> handleRemoveTeleport(args)
|
|
169
|
+
"teleportTo" -> handleTeleportTo(args)
|
|
146
170
|
"addEventListener" -> handleAddEventListener(args)
|
|
147
171
|
"removeEventListener" -> handleRemoveEventListener(args)
|
|
148
172
|
"invokeNativeModule" -> handleInvokeNativeModule(args)
|
|
@@ -311,6 +335,100 @@ class NativeBridge(private val context: Context) {
|
|
|
311
335
|
ViewGroup.LayoutParams.MATCH_PARENT
|
|
312
336
|
)
|
|
313
337
|
)
|
|
338
|
+
|
|
339
|
+
// Add modal container for teleport
|
|
340
|
+
container.addView(
|
|
341
|
+
modalContainer,
|
|
342
|
+
ViewGroup.LayoutParams(
|
|
343
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
344
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// -- Teleport handlers --
|
|
350
|
+
|
|
351
|
+
private fun handleCreateTeleport(args: JSONArray) {
|
|
352
|
+
val parentId = args.getInt(0)
|
|
353
|
+
val startId = args.getInt(1)
|
|
354
|
+
val endId = args.getInt(2)
|
|
355
|
+
|
|
356
|
+
val parentView = nodeViews[parentId] ?: run {
|
|
357
|
+
Log.w(TAG, "Parent view not found for teleport (id: $parentId)")
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Store teleport marker IDs
|
|
362
|
+
teleportMarkers[parentId] = Pair(startId, endId)
|
|
363
|
+
|
|
364
|
+
// Create container for teleported content
|
|
365
|
+
val container = FrameLayout(context).apply {
|
|
366
|
+
id = View.generateViewId()
|
|
367
|
+
setBackgroundColor(android.graphics.Color.TRANSPARENT)
|
|
368
|
+
isClickable = true
|
|
369
|
+
isFocusable = false
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (parentView is ViewGroup) {
|
|
373
|
+
parentView.addView(container)
|
|
374
|
+
teleportContainers[parentId] = container
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
Log.d(TAG, "Created teleport container for parent $parentId")
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private fun handleRemoveTeleport(args: JSONArray) {
|
|
381
|
+
val parentId = args.getInt(0)
|
|
382
|
+
|
|
383
|
+
// Remove teleport container
|
|
384
|
+
teleportContainers.remove(parentId)?.let { container ->
|
|
385
|
+
mainHandler.post {
|
|
386
|
+
(container.parent as? ViewGroup)?.removeView(container)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Clean up markers
|
|
391
|
+
teleportMarkers.remove(parentId)
|
|
392
|
+
|
|
393
|
+
Log.d(TAG, "Removed teleport container for parent $parentId")
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private fun handleTeleportTo(args: JSONArray) {
|
|
397
|
+
val target = args.getString(0)
|
|
398
|
+
val nodeId = args.getInt(1)
|
|
399
|
+
|
|
400
|
+
val targetView = getTeleportTarget(target) ?: run {
|
|
401
|
+
Log.w(TAG, "Teleport target '$target' not found")
|
|
402
|
+
return
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
val childView = nodeViews[nodeId] ?: run {
|
|
406
|
+
Log.w(TAG, "Node view not found for teleport (id: $nodeId)")
|
|
407
|
+
return
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Move view to teleport target
|
|
411
|
+
mainHandler.post {
|
|
412
|
+
(childView.parent as? ViewGroup)?.removeView(childView)
|
|
413
|
+
targetView.addView(childView)
|
|
414
|
+
childView.requestLayout()
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
Log.d(TAG, "Teleported node $nodeId to target '$target'")
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private fun getTeleportTarget(target: String): ViewGroup? {
|
|
421
|
+
return when (target) {
|
|
422
|
+
"root" -> rootView as? ViewGroup
|
|
423
|
+
"modal" -> {
|
|
424
|
+
// Ensure modal container is added to root if not already
|
|
425
|
+
if (modalContainer.parent == null && rootView != null) {
|
|
426
|
+
(rootView as? ViewGroup)?.addView(modalContainer)
|
|
427
|
+
}
|
|
428
|
+
modalContainer
|
|
429
|
+
}
|
|
430
|
+
else -> null
|
|
431
|
+
}
|
|
314
432
|
}
|
|
315
433
|
|
|
316
434
|
private fun handleAddEventListener(args: JSONArray) {
|