@thelacanians/vue-native-cli 0.4.15 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import VueNativeShared
|
|
3
|
+
|
|
4
|
+
/// Native module providing device and screen information for macOS.
|
|
5
|
+
///
|
|
6
|
+
/// Methods:
|
|
7
|
+
/// - getInfo() -> { platform, systemName, systemVersion, model, name, screenWidth, screenHeight, scale }
|
|
8
|
+
final class DeviceInfoModule: NativeModule {
|
|
9
|
+
let moduleName = "DeviceInfo"
|
|
10
|
+
|
|
11
|
+
func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
|
|
12
|
+
DispatchQueue.main.async {
|
|
13
|
+
switch method {
|
|
14
|
+
case "getInfo":
|
|
15
|
+
let processInfo = ProcessInfo.processInfo
|
|
16
|
+
let version = processInfo.operatingSystemVersion
|
|
17
|
+
|
|
18
|
+
var systemInfo = utsname()
|
|
19
|
+
uname(&systemInfo)
|
|
20
|
+
let machine = withUnsafePointer(to: &systemInfo.machine) {
|
|
21
|
+
$0.withMemoryRebound(to: CChar.self, capacity: 1) {
|
|
22
|
+
String(validatingUTF8: $0) ?? "unknown"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let screen = NSScreen.main
|
|
27
|
+
let screenWidth = screen?.frame.width ?? 0
|
|
28
|
+
let screenHeight = screen?.frame.height ?? 0
|
|
29
|
+
let scale = screen?.backingScaleFactor ?? 1.0
|
|
30
|
+
|
|
31
|
+
let info: [String: Any] = [
|
|
32
|
+
"platform": "macos",
|
|
33
|
+
"systemName": "macOS",
|
|
34
|
+
"systemVersion": "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)",
|
|
35
|
+
"model": machine,
|
|
36
|
+
"name": Host.current().localizedName ?? "Mac",
|
|
37
|
+
"isSimulator": false,
|
|
38
|
+
"screenWidth": screenWidth,
|
|
39
|
+
"screenHeight": screenHeight,
|
|
40
|
+
"scale": scale,
|
|
41
|
+
]
|
|
42
|
+
callback(info, nil)
|
|
43
|
+
|
|
44
|
+
default:
|
|
45
|
+
callback(nil, "DeviceInfoModule: Unknown method '\(method)'")
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import VueNativeShared
|
|
3
|
+
|
|
4
|
+
/// macOS-only module for drag and drop support.
|
|
5
|
+
///
|
|
6
|
+
/// Methods:
|
|
7
|
+
/// - enableDropZone(viewId: Int) -- register a view for file/text drops
|
|
8
|
+
/// - startDrag(data: { text: String }) -- initiate a drag operation
|
|
9
|
+
///
|
|
10
|
+
/// Events dispatched:
|
|
11
|
+
/// - dragDrop:drop { files: [String], text: String? }
|
|
12
|
+
final class DragDropModule: NativeModule {
|
|
13
|
+
let moduleName = "DragDrop"
|
|
14
|
+
private weak var dispatcher: NativeEventDispatcher?
|
|
15
|
+
|
|
16
|
+
init(dispatcher: NativeEventDispatcher) {
|
|
17
|
+
self.dispatcher = dispatcher
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
|
|
21
|
+
DispatchQueue.main.async { [weak self] in
|
|
22
|
+
switch method {
|
|
23
|
+
case "enableDropZone":
|
|
24
|
+
guard args.first is Int else {
|
|
25
|
+
callback(nil, "Invalid viewId")
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
// Register the main window's content view for file and string drops.
|
|
29
|
+
// Full per-view registration would require access to the bridge's view registry.
|
|
30
|
+
if let window = NSApp.mainWindow, let view = window.contentView {
|
|
31
|
+
view.registerForDraggedTypes([.fileURL, .string])
|
|
32
|
+
}
|
|
33
|
+
callback(nil, nil)
|
|
34
|
+
|
|
35
|
+
case "startDrag":
|
|
36
|
+
guard let data = args.first as? [String: Any] else {
|
|
37
|
+
callback(nil, "Invalid args")
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
// Drag operations are typically initiated by mouse events.
|
|
41
|
+
// This method prepares drag data that can be used by event handlers.
|
|
42
|
+
_ = data
|
|
43
|
+
callback(nil, nil)
|
|
44
|
+
|
|
45
|
+
default:
|
|
46
|
+
callback(nil, "DragDropModule: Unknown method '\(method)'")
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import UniformTypeIdentifiers
|
|
3
|
+
import VueNativeShared
|
|
4
|
+
|
|
5
|
+
/// macOS-only module for file open/save dialogs.
|
|
6
|
+
///
|
|
7
|
+
/// Methods:
|
|
8
|
+
/// - openFile(options?: { multiple?, allowedTypes?, title? }) -> [String]? (file paths)
|
|
9
|
+
/// - openDirectory(options?: { title? }) -> String? (directory path)
|
|
10
|
+
/// - saveFile(options?: { title?, defaultName? }) -> String? (save path)
|
|
11
|
+
final class FileDialogModule: NativeModule {
|
|
12
|
+
let moduleName = "FileDialog"
|
|
13
|
+
|
|
14
|
+
func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
|
|
15
|
+
DispatchQueue.main.async {
|
|
16
|
+
switch method {
|
|
17
|
+
case "openFile":
|
|
18
|
+
let options = args.first as? [String: Any] ?? [:]
|
|
19
|
+
let panel = NSOpenPanel()
|
|
20
|
+
panel.canChooseFiles = true
|
|
21
|
+
panel.canChooseDirectories = false
|
|
22
|
+
panel.allowsMultipleSelection = (options["multiple"] as? Bool) ?? false
|
|
23
|
+
|
|
24
|
+
if let types = options["allowedTypes"] as? [String] {
|
|
25
|
+
panel.allowedContentTypes = types.compactMap { ext in
|
|
26
|
+
UTType(filenameExtension: ext)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if let title = options["title"] as? String {
|
|
31
|
+
panel.title = title
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
panel.begin { response in
|
|
35
|
+
if response == .OK {
|
|
36
|
+
let urls = panel.urls.map { $0.path }
|
|
37
|
+
callback(urls, nil)
|
|
38
|
+
} else {
|
|
39
|
+
callback(nil, nil) // cancelled
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case "openDirectory":
|
|
44
|
+
let options = args.first as? [String: Any] ?? [:]
|
|
45
|
+
let panel = NSOpenPanel()
|
|
46
|
+
panel.canChooseFiles = false
|
|
47
|
+
panel.canChooseDirectories = true
|
|
48
|
+
panel.allowsMultipleSelection = false
|
|
49
|
+
|
|
50
|
+
if let title = options["title"] as? String {
|
|
51
|
+
panel.title = title
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
panel.begin { response in
|
|
55
|
+
if response == .OK {
|
|
56
|
+
callback(panel.url?.path, nil)
|
|
57
|
+
} else {
|
|
58
|
+
callback(nil, nil)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case "saveFile":
|
|
63
|
+
let options = args.first as? [String: Any] ?? [:]
|
|
64
|
+
let panel = NSSavePanel()
|
|
65
|
+
|
|
66
|
+
if let title = options["title"] as? String {
|
|
67
|
+
panel.title = title
|
|
68
|
+
}
|
|
69
|
+
if let defaultName = options["defaultName"] as? String {
|
|
70
|
+
panel.nameFieldStringValue = defaultName
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
panel.begin { response in
|
|
74
|
+
if response == .OK {
|
|
75
|
+
callback(panel.url?.path, nil)
|
|
76
|
+
} else {
|
|
77
|
+
callback(nil, nil)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
default:
|
|
82
|
+
callback(nil, "FileDialogModule: Unknown method '\(method)'")
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import VueNativeShared
|
|
3
|
+
|
|
4
|
+
/// Native module providing haptic feedback via Force Touch trackpad.
|
|
5
|
+
///
|
|
6
|
+
/// Methods:
|
|
7
|
+
/// - vibrate(style: String) -- trigger haptic feedback ("light"|"medium"|"heavy")
|
|
8
|
+
/// - notificationFeedback(type: String) -- generic haptic (macOS has no distinct types)
|
|
9
|
+
/// - selectionChanged() -- alignment feedback
|
|
10
|
+
final class HapticsModule: NativeModule {
|
|
11
|
+
let moduleName = "Haptics"
|
|
12
|
+
|
|
13
|
+
func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
|
|
14
|
+
DispatchQueue.main.async {
|
|
15
|
+
switch method {
|
|
16
|
+
case "vibrate":
|
|
17
|
+
let style = args.first as? String ?? "medium"
|
|
18
|
+
let pattern: NSHapticFeedbackManager.FeedbackPattern
|
|
19
|
+
switch style {
|
|
20
|
+
case "light": pattern = .alignment
|
|
21
|
+
case "medium": pattern = .levelChange
|
|
22
|
+
case "heavy": pattern = .generic
|
|
23
|
+
default: pattern = .generic
|
|
24
|
+
}
|
|
25
|
+
NSHapticFeedbackManager.defaultPerformer.perform(pattern, performanceTime: .now)
|
|
26
|
+
callback(nil, nil)
|
|
27
|
+
|
|
28
|
+
case "notificationFeedback":
|
|
29
|
+
// macOS doesn't have distinct notification haptics — use generic
|
|
30
|
+
NSHapticFeedbackManager.defaultPerformer.perform(.generic, performanceTime: .now)
|
|
31
|
+
callback(nil, nil)
|
|
32
|
+
|
|
33
|
+
case "selectionChanged":
|
|
34
|
+
NSHapticFeedbackManager.defaultPerformer.perform(.alignment, performanceTime: .now)
|
|
35
|
+
callback(nil, nil)
|
|
36
|
+
|
|
37
|
+
default:
|
|
38
|
+
callback(nil, "HapticsModule: Unknown method '\(method)'")
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import VueNativeShared
|
|
3
|
+
|
|
4
|
+
/// Native module providing keyboard management for macOS.
|
|
5
|
+
///
|
|
6
|
+
/// Methods:
|
|
7
|
+
/// - dismiss() -- resign first responder (dismiss any focused input)
|
|
8
|
+
/// - getHeight() -> { height: 0, isVisible: false } (macOS has no virtual keyboard)
|
|
9
|
+
final class KeyboardModule: NativeModule {
|
|
10
|
+
let moduleName = "Keyboard"
|
|
11
|
+
|
|
12
|
+
func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
|
|
13
|
+
DispatchQueue.main.async {
|
|
14
|
+
switch method {
|
|
15
|
+
case "dismiss":
|
|
16
|
+
NSApp.mainWindow?.makeFirstResponder(nil)
|
|
17
|
+
callback(nil, nil)
|
|
18
|
+
|
|
19
|
+
case "getHeight":
|
|
20
|
+
// macOS has no virtual keyboard
|
|
21
|
+
callback(["height": 0.0, "isVisible": false], nil)
|
|
22
|
+
|
|
23
|
+
default:
|
|
24
|
+
callback(nil, "KeyboardModule: Unknown method '\(method)'")
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import VueNativeShared
|
|
3
|
+
|
|
4
|
+
/// Native module for opening URLs and deep links on macOS.
|
|
5
|
+
///
|
|
6
|
+
/// Methods:
|
|
7
|
+
/// - openURL(url: String) -> Bool
|
|
8
|
+
/// - canOpenURL(url: String) -> Bool
|
|
9
|
+
/// - getInitialURL() -> String?
|
|
10
|
+
final class LinkingModule: NativeModule {
|
|
11
|
+
var moduleName: String { "Linking" }
|
|
12
|
+
|
|
13
|
+
/// The URL that launched the app. Set by the host app before the JS bundle loads.
|
|
14
|
+
static var initialURL: String?
|
|
15
|
+
|
|
16
|
+
func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
|
|
17
|
+
switch method {
|
|
18
|
+
case "openURL":
|
|
19
|
+
guard let urlString = args.first as? String,
|
|
20
|
+
let url = URL(string: urlString) else {
|
|
21
|
+
callback(nil, "Invalid URL")
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
DispatchQueue.main.async {
|
|
25
|
+
let success = NSWorkspace.shared.open(url)
|
|
26
|
+
callback(success, success ? nil : "Failed to open URL")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
case "canOpenURL":
|
|
30
|
+
guard let urlString = args.first as? String,
|
|
31
|
+
let url = URL(string: urlString) else {
|
|
32
|
+
callback(false, nil)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
DispatchQueue.main.async {
|
|
36
|
+
let canOpen = NSWorkspace.shared.urlForApplication(toOpen: url) != nil
|
|
37
|
+
callback(canOpen, nil)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
case "getInitialURL":
|
|
41
|
+
callback(LinkingModule.initialURL, nil)
|
|
42
|
+
|
|
43
|
+
default:
|
|
44
|
+
callback(nil, "LinkingModule: Unknown method '\(method)'")
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func invokeSync(method: String, args: [Any]) -> Any? { nil }
|
|
49
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import VueNativeShared
|
|
3
|
+
|
|
4
|
+
/// macOS-only module for menu bar control.
|
|
5
|
+
///
|
|
6
|
+
/// Methods:
|
|
7
|
+
/// - setAppMenu(items: [{ title, items: [{ title, key?, id?, separator?, disabled? }] }])
|
|
8
|
+
/// - showContextMenu(items: [{ title, key?, id?, separator?, disabled? }])
|
|
9
|
+
///
|
|
10
|
+
/// Events dispatched:
|
|
11
|
+
/// - menu:itemClick { id, title }
|
|
12
|
+
final class MenuModule: NativeModule {
|
|
13
|
+
let moduleName = "Menu"
|
|
14
|
+
private weak var dispatcher: NativeEventDispatcher?
|
|
15
|
+
|
|
16
|
+
init(dispatcher: NativeEventDispatcher) {
|
|
17
|
+
self.dispatcher = dispatcher
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
|
|
21
|
+
DispatchQueue.main.async { [weak self] in
|
|
22
|
+
switch method {
|
|
23
|
+
case "setAppMenu":
|
|
24
|
+
guard let items = args.first as? [[String: Any]] else {
|
|
25
|
+
callback(nil, "Invalid args")
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
self?.buildMenu(items: items)
|
|
29
|
+
callback(nil, nil)
|
|
30
|
+
|
|
31
|
+
case "showContextMenu":
|
|
32
|
+
guard let items = args.first as? [[String: Any]] else {
|
|
33
|
+
callback(nil, "Invalid args")
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
let menu = NSMenu()
|
|
37
|
+
self?.addMenuItems(items, to: menu)
|
|
38
|
+
|
|
39
|
+
if let window = NSApp.mainWindow, let view = window.contentView {
|
|
40
|
+
let location = NSEvent.mouseLocation
|
|
41
|
+
let windowPoint = window.convertPoint(fromScreen: location)
|
|
42
|
+
let viewPoint = view.convert(windowPoint, from: nil)
|
|
43
|
+
menu.popUp(positioning: nil, at: viewPoint, in: view)
|
|
44
|
+
}
|
|
45
|
+
callback(nil, nil)
|
|
46
|
+
|
|
47
|
+
default:
|
|
48
|
+
callback(nil, "MenuModule: Unknown method '\(method)'")
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private func buildMenu(items: [[String: Any]]) {
|
|
54
|
+
let mainMenu = NSMenu()
|
|
55
|
+
for item in items {
|
|
56
|
+
guard let title = item["title"] as? String else { continue }
|
|
57
|
+
let menuItem = NSMenuItem(title: title, action: nil, keyEquivalent: "")
|
|
58
|
+
let submenu = NSMenu(title: title)
|
|
59
|
+
if let children = item["items"] as? [[String: Any]] {
|
|
60
|
+
addMenuItems(children, to: submenu)
|
|
61
|
+
}
|
|
62
|
+
menuItem.submenu = submenu
|
|
63
|
+
mainMenu.addItem(menuItem)
|
|
64
|
+
}
|
|
65
|
+
NSApp.mainMenu = mainMenu
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private func addMenuItems(_ items: [[String: Any]], to menu: NSMenu) {
|
|
69
|
+
for item in items {
|
|
70
|
+
if let separator = item["separator"] as? Bool, separator {
|
|
71
|
+
menu.addItem(.separator())
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
guard let title = item["title"] as? String else { continue }
|
|
76
|
+
let key = item["key"] as? String ?? ""
|
|
77
|
+
let id = item["id"] as? String ?? title
|
|
78
|
+
|
|
79
|
+
let menuItem = NSMenuItem(title: title, action: #selector(menuItemClicked(_:)), keyEquivalent: key)
|
|
80
|
+
menuItem.target = self
|
|
81
|
+
menuItem.representedObject = id
|
|
82
|
+
|
|
83
|
+
if let disabled = item["disabled"] as? Bool, disabled {
|
|
84
|
+
menuItem.isEnabled = false
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
menu.addItem(menuItem)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@objc private func menuItemClicked(_ sender: NSMenuItem) {
|
|
92
|
+
guard let id = sender.representedObject as? String else { return }
|
|
93
|
+
dispatcher?.dispatchGlobalEvent("menu:itemClick", payload: ["id": id, "title": sender.title])
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import VueNativeShared
|
|
3
|
+
|
|
4
|
+
/// Singleton registry for all native modules.
|
|
5
|
+
/// Modules are registered by name and looked up when JS invokes them.
|
|
6
|
+
@MainActor
|
|
7
|
+
final class NativeModuleRegistry {
|
|
8
|
+
|
|
9
|
+
static let shared = NativeModuleRegistry()
|
|
10
|
+
|
|
11
|
+
private var modules: [String: NativeModule] = [:]
|
|
12
|
+
|
|
13
|
+
private init() {}
|
|
14
|
+
|
|
15
|
+
// MARK: - Registration
|
|
16
|
+
|
|
17
|
+
func register(_ module: NativeModule) {
|
|
18
|
+
modules[module.moduleName] = module
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Register all built-in macOS modules.
|
|
22
|
+
func registerDefaults(dispatcher: NativeEventDispatcher, viewLookup: @escaping (Int) -> NSView?) {
|
|
23
|
+
// Cross-platform ports
|
|
24
|
+
register(HapticsModule())
|
|
25
|
+
register(ClipboardModule())
|
|
26
|
+
register(DeviceInfoModule())
|
|
27
|
+
register(AnimationModule(viewLookup: viewLookup))
|
|
28
|
+
register(AppStateModule(dispatcher: dispatcher))
|
|
29
|
+
register(KeyboardModule())
|
|
30
|
+
register(LinkingModule())
|
|
31
|
+
register(ShareModule())
|
|
32
|
+
|
|
33
|
+
// macOS-only modules
|
|
34
|
+
register(WindowModule())
|
|
35
|
+
register(MenuModule(dispatcher: dispatcher))
|
|
36
|
+
register(FileDialogModule())
|
|
37
|
+
register(DragDropModule(dispatcher: dispatcher))
|
|
38
|
+
|
|
39
|
+
// Additional cross-platform modules
|
|
40
|
+
register(CameraModule())
|
|
41
|
+
register(NotificationsModule())
|
|
42
|
+
register(BiometryModule())
|
|
43
|
+
register(PermissionsModule())
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// MARK: - Invocation
|
|
47
|
+
|
|
48
|
+
func invoke(module moduleName: String, method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
|
|
49
|
+
guard let module = modules[moduleName] else {
|
|
50
|
+
callback(nil, "Module '\(moduleName)' not found")
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
module.invoke(method: method, args: args, callback: callback)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func invokeSync(module moduleName: String, method: String, args: [Any]) -> Any? {
|
|
57
|
+
guard let module = modules[moduleName] else {
|
|
58
|
+
NSLog("[VueNative] NativeModuleRegistry: Module '\(moduleName)' not found")
|
|
59
|
+
return nil
|
|
60
|
+
}
|
|
61
|
+
return module.invokeSync(method: method, args: args)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import UserNotifications
|
|
2
|
+
import AppKit
|
|
3
|
+
import VueNativeShared
|
|
4
|
+
|
|
5
|
+
/// Native module providing local notification access on macOS.
|
|
6
|
+
///
|
|
7
|
+
/// Methods:
|
|
8
|
+
/// - requestPermission() -> Bool
|
|
9
|
+
/// - checkPermission() -> "granted"/"denied"/"notDetermined"
|
|
10
|
+
/// - scheduleLocal(title, body, delay) -> { id }
|
|
11
|
+
/// - cancelAll()
|
|
12
|
+
/// - cancel(id)
|
|
13
|
+
/// - getBadgeCount() -> String?
|
|
14
|
+
/// - setBadgeCount(count)
|
|
15
|
+
final class NotificationsModule: NativeModule {
|
|
16
|
+
let moduleName = "Notifications"
|
|
17
|
+
|
|
18
|
+
func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
|
|
19
|
+
let center = UNUserNotificationCenter.current()
|
|
20
|
+
|
|
21
|
+
switch method {
|
|
22
|
+
case "requestPermission":
|
|
23
|
+
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
|
|
24
|
+
if let error = error {
|
|
25
|
+
callback(nil, "Notification permission error: \(error.localizedDescription)")
|
|
26
|
+
} else {
|
|
27
|
+
callback(granted, nil)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
case "checkPermission":
|
|
32
|
+
center.getNotificationSettings { settings in
|
|
33
|
+
let result: String
|
|
34
|
+
switch settings.authorizationStatus {
|
|
35
|
+
case .authorized, .provisional, .ephemeral:
|
|
36
|
+
result = "granted"
|
|
37
|
+
case .denied:
|
|
38
|
+
result = "denied"
|
|
39
|
+
case .notDetermined:
|
|
40
|
+
result = "notDetermined"
|
|
41
|
+
@unknown default:
|
|
42
|
+
result = "notDetermined"
|
|
43
|
+
}
|
|
44
|
+
callback(result, nil)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
case "scheduleLocal":
|
|
48
|
+
let title = args.count > 0 ? (args[0] as? String ?? "") : ""
|
|
49
|
+
let body = args.count > 1 ? (args[1] as? String ?? "") : ""
|
|
50
|
+
let delay = args.count > 2 ? (args[2] as? Double ?? 1.0) : 1.0
|
|
51
|
+
|
|
52
|
+
let content = UNMutableNotificationContent()
|
|
53
|
+
content.title = title
|
|
54
|
+
content.body = body
|
|
55
|
+
content.sound = .default
|
|
56
|
+
|
|
57
|
+
let trigger = UNTimeIntervalNotificationTrigger(
|
|
58
|
+
timeInterval: max(delay, 0.1),
|
|
59
|
+
repeats: false
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
let requestId = UUID().uuidString
|
|
63
|
+
let request = UNNotificationRequest(
|
|
64
|
+
identifier: requestId,
|
|
65
|
+
content: content,
|
|
66
|
+
trigger: trigger
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
center.add(request) { error in
|
|
70
|
+
if let error = error {
|
|
71
|
+
callback(nil, "Schedule error: \(error.localizedDescription)")
|
|
72
|
+
} else {
|
|
73
|
+
callback(["id": requestId], nil)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
case "cancelAll":
|
|
78
|
+
center.removeAllPendingNotificationRequests()
|
|
79
|
+
callback(nil, nil)
|
|
80
|
+
|
|
81
|
+
case "cancel":
|
|
82
|
+
guard let identifier = args.first as? String else {
|
|
83
|
+
callback(nil, "cancel: missing notification id")
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
center.removePendingNotificationRequests(withIdentifiers: [identifier])
|
|
87
|
+
callback(nil, nil)
|
|
88
|
+
|
|
89
|
+
case "getBadgeCount":
|
|
90
|
+
DispatchQueue.main.async {
|
|
91
|
+
let badge = NSApplication.shared.dockTile.badgeLabel
|
|
92
|
+
callback(badge, nil)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
case "setBadgeCount":
|
|
96
|
+
let count = args.first
|
|
97
|
+
DispatchQueue.main.async {
|
|
98
|
+
if let num = count as? Int {
|
|
99
|
+
NSApplication.shared.dockTile.badgeLabel = num == 0 ? nil : String(num)
|
|
100
|
+
} else if let str = count as? String {
|
|
101
|
+
NSApplication.shared.dockTile.badgeLabel = str.isEmpty ? nil : str
|
|
102
|
+
} else {
|
|
103
|
+
NSApplication.shared.dockTile.badgeLabel = nil
|
|
104
|
+
}
|
|
105
|
+
callback(nil, nil)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
default:
|
|
109
|
+
callback(nil, "NotificationsModule: Unknown method '\(method)'")
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|