@thelacanians/vue-native-cli 0.4.14 → 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.
Files changed (123) hide show
  1. package/dist/cli.js +789 -200
  2. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +156 -5
  3. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +33 -13
  4. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +27 -6
  5. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSliderFactory.kt +9 -2
  6. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
  7. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/EventThrottle.kt +57 -0
  8. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
  9. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
  10. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
  11. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
  12. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
  13. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
  14. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
  15. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
  16. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
  17. package/native/android/gradle.properties +1 -0
  18. package/native/android/gradlew +1 -1
  19. package/native/ios/VueNativeCore/Package.swift +1 -1
  20. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +80 -0
  21. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +244 -112
  22. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VListFactory.swift +19 -2
  23. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VScrollViewFactory.swift +9 -4
  24. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift +8 -3
  25. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
  26. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
  27. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
  28. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
  29. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
  30. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
  31. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
  32. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
  33. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
  34. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
  35. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
  36. package/native/macos/VueNativeMacOS/Package.swift +34 -0
  37. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
  38. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
  39. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
  40. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
  41. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
  42. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
  43. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
  44. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
  45. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
  46. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
  47. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
  48. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
  49. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
  50. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
  51. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
  52. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
  53. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
  54. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
  55. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
  56. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
  57. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
  58. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
  59. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
  60. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
  61. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
  62. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
  63. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
  64. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
  65. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
  66. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
  67. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
  68. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
  69. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
  70. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
  71. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
  72. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
  73. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
  74. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
  75. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
  76. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
  77. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
  78. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
  79. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
  80. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
  81. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
  82. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
  83. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
  84. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
  85. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
  86. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
  87. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
  88. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
  89. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
  90. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
  91. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
  92. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
  93. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
  94. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
  95. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
  96. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
  97. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
  98. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
  99. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
  100. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
  101. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
  102. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
  103. package/native/shared/VueNativeShared/AGENTS.md +129 -0
  104. package/native/shared/VueNativeShared/Package.swift +14 -0
  105. package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
  106. package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
  107. package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
  108. package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
  109. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
  110. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
  111. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
  112. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
  113. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
  114. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
  115. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
  116. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
  117. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
  118. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
  119. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
  120. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
  121. package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
  122. package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
  123. package/package.json +8 -2
@@ -0,0 +1,125 @@
1
+ import AppKit
2
+
3
+ /// Base window controller for Vue Native macOS apps.
4
+ /// Subclass and override `bundleName` and optionally `devServerURL`.
5
+ ///
6
+ /// Usage:
7
+ /// ```swift
8
+ /// class MainWindowController: VueNativeWindowController {
9
+ /// override var bundleName: String { "vue-native-bundle" }
10
+ /// override var devServerURL: URL? {
11
+ /// #if DEBUG
12
+ /// URL(string: "ws://localhost:8174")
13
+ /// #else
14
+ /// nil
15
+ /// #endif
16
+ /// }
17
+ /// }
18
+ /// ```
19
+ open class VueNativeWindowController: NSWindowController {
20
+
21
+ // MARK: - Overridable API
22
+
23
+ /// Name of the JS bundle resource (without extension) bundled in your app target.
24
+ open var bundleName: String { "vue-native-bundle" }
25
+
26
+ /// WebSocket URL of the Vite dev server for hot reload.
27
+ /// Return `nil` (the default) to disable hot reload and load only from the bundle.
28
+ open var devServerURL: URL? { nil }
29
+
30
+ // MARK: - Private state
31
+
32
+ private let runtime = JSRuntime.shared
33
+ private let bridge = NativeBridge.shared
34
+
35
+ // MARK: - Convenience initializer
36
+
37
+ public convenience init() {
38
+ let window = NSWindow(
39
+ contentRect: NSRect(x: 0, y: 0, width: 800, height: 600),
40
+ styleMask: [.titled, .closable, .resizable, .miniaturizable],
41
+ backing: .buffered,
42
+ defer: false
43
+ )
44
+ window.center()
45
+ window.title = "Vue Native"
46
+
47
+ // Use a FlippedView as the content view so all coordinates are top-left origin.
48
+ let flippedContent = FlippedView(frame: window.contentView!.bounds)
49
+ flippedContent.autoresizingMask = [.width, .height]
50
+ window.contentView = flippedContent
51
+
52
+ self.init(window: window)
53
+ }
54
+
55
+ // MARK: - Lifecycle
56
+
57
+ override open func windowDidLoad() {
58
+ super.windowDidLoad()
59
+
60
+ guard let contentView = window?.contentView else { return }
61
+ contentView.wantsLayer = true
62
+ contentView.layer?.backgroundColor = NSColor.black.cgColor
63
+
64
+ // Initialize JS engine first, then bridge.
65
+ runtime.initialize { [weak self] in
66
+ guard let self = self else { return }
67
+ self.bridge.initialize(contentView: contentView)
68
+ DispatchQueue.main.async {
69
+ self.loadBundle()
70
+ }
71
+ }
72
+ }
73
+
74
+ // MARK: - Bundle loading
75
+
76
+ private func loadBundle() {
77
+ #if DEBUG
78
+ if let wsURL = devServerURL {
79
+ HotReloadManager.shared.connect(to: wsURL)
80
+ }
81
+ #endif
82
+ loadEmbeddedBundle()
83
+ }
84
+
85
+ private func loadEmbeddedBundle() {
86
+ runtime.loadBundle(source: .embedded(name: bundleName)) { success in
87
+ if !success {
88
+ NSLog("[VueNative macOS] ERROR: Failed to load bundle '%@'", self.bundleName)
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ // MARK: - VueNativeAppDelegate
95
+
96
+ /// Convenience NSApplicationDelegate for single-window Vue Native apps.
97
+ /// Subclass and override `createWindowController()` to provide your custom window controller.
98
+ ///
99
+ /// Usage:
100
+ /// ```swift
101
+ /// @main
102
+ /// class AppDelegate: VueNativeAppDelegate {
103
+ /// override func createWindowController() -> VueNativeWindowController {
104
+ /// return MainWindowController()
105
+ /// }
106
+ /// }
107
+ /// ```
108
+ open class VueNativeAppDelegate: NSObject, NSApplicationDelegate {
109
+ public var windowController: VueNativeWindowController?
110
+
111
+ open func applicationDidFinishLaunching(_ notification: Notification) {
112
+ let controller = createWindowController()
113
+ controller.showWindow(nil)
114
+ windowController = controller
115
+ }
116
+
117
+ /// Override this to provide your custom VueNativeWindowController subclass.
118
+ open func createWindowController() -> VueNativeWindowController {
119
+ return VueNativeWindowController()
120
+ }
121
+
122
+ open func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
123
+ return true
124
+ }
125
+ }
@@ -0,0 +1,209 @@
1
+ import AppKit
2
+ import ObjectiveC
3
+
4
+ // MARK: - Associated object key for factory reference
5
+
6
+ private var factoryAssociatedKey: UInt8 = 0
7
+
8
+ // MARK: - ComponentRegistry
9
+
10
+ /// Singleton registry that maps component type strings (e.g., "VView", "VText")
11
+ /// to their corresponding NativeComponentFactory instances.
12
+ /// When a view is created by a factory, the factory reference is stored on the
13
+ /// view via objc_setAssociatedObject for later prop updates and event wiring.
14
+ @MainActor
15
+ final class ComponentRegistry {
16
+
17
+ // MARK: - Singleton
18
+
19
+ static let shared = ComponentRegistry()
20
+
21
+ // MARK: - Properties
22
+
23
+ /// Mapping from component type strings to factory instances.
24
+ private var factories: [String: NativeComponentFactory] = [:]
25
+
26
+ // MARK: - Initialization
27
+
28
+ private init() {
29
+ registerDefaults()
30
+ }
31
+
32
+ /// Register all built-in component factories.
33
+ private func registerDefaults() {
34
+ // Core components (Phase 1)
35
+ register("VView", factory: VViewFactory())
36
+ register("VText", factory: VTextFactory())
37
+ register("VButton", factory: VButtonFactory())
38
+ register("VInput", factory: VInputFactory())
39
+ register("VSwitch", factory: VSwitchFactory())
40
+ register("__ROOT__", factory: VRootFactory())
41
+
42
+ // Simple controls (Phase 2)
43
+ register("VSlider", factory: VSliderFactory())
44
+ register("VProgressBar", factory: VProgressBarFactory())
45
+ register("VActivityIndicator", factory: VActivityIndicatorFactory())
46
+ register("VPicker", factory: VPickerFactory())
47
+ register("VSegmentedControl", factory: VSegmentedControlFactory())
48
+ register("VCheckbox", factory: VCheckboxFactory())
49
+ register("VRadio", factory: VRadioFactory())
50
+ register("VDropdown", factory: VDropdownFactory())
51
+
52
+ // Complex components (Phase 2)
53
+ register("VScrollView", factory: VScrollViewFactory())
54
+ register("VList", factory: VListFactory())
55
+ register("VSectionList", factory: VSectionListFactory())
56
+ register("VImage", factory: VImageFactory())
57
+ register("VPressable", factory: VPressableFactory())
58
+
59
+ // Dialogs and media (Phase 2)
60
+ register("VModal", factory: VModalFactory())
61
+ register("VAlertDialog", factory: VAlertDialogFactory())
62
+ register("VActionSheet", factory: VActionSheetFactory())
63
+ register("VWebView", factory: VWebViewFactory())
64
+ register("VVideo", factory: VVideoFactory())
65
+
66
+ // Platform stubs (no-op on macOS)
67
+ register("VStatusBar", factory: VStatusBarFactory())
68
+ register("VKeyboardAvoiding", factory: VKeyboardAvoidingFactory())
69
+ register("VSafeArea", factory: VSafeAreaFactory())
70
+ register("VRefreshControl", factory: VRefreshControlFactory())
71
+
72
+ // macOS-specific components (Phase 3)
73
+ register("VToolbar", factory: VToolbarFactory())
74
+ register("VSplitView", factory: VSplitViewFactory())
75
+ register("VOutlineView", factory: VOutlineViewFactory())
76
+ }
77
+
78
+ // MARK: - Registration
79
+
80
+ /// Register a factory for a given component type string.
81
+ /// Replaces any existing factory for that type.
82
+ func register(_ type: String, factory: NativeComponentFactory) {
83
+ factories[type] = factory
84
+ }
85
+
86
+ /// Unregister a factory for a given component type string.
87
+ func unregister(_ type: String) {
88
+ factories.removeValue(forKey: type)
89
+ }
90
+
91
+ // MARK: - View Creation
92
+
93
+ /// Create a new NSView for the given component type.
94
+ /// Returns nil if no factory is registered for the type.
95
+ /// The factory is stored as an associated object on the created view
96
+ /// so it can be retrieved later for prop updates and event handling.
97
+ func createView(type: String) -> NSView? {
98
+ guard let factory = factories[type] else {
99
+ NSLog("[VueNative] Warning: No factory registered for component type '%@'", type)
100
+ return nil
101
+ }
102
+
103
+ let view = factory.createView()
104
+
105
+ // Store factory reference on the view for later lookups
106
+ objc_setAssociatedObject(
107
+ view,
108
+ &factoryAssociatedKey,
109
+ FactoryBox(factory: factory),
110
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
111
+ )
112
+
113
+ return view
114
+ }
115
+
116
+ // MARK: - Factory Retrieval
117
+
118
+ /// Retrieve the factory for the given component type string.
119
+ func factory(for type: String) -> NativeComponentFactory? {
120
+ return factories[type]
121
+ }
122
+
123
+ /// Retrieve the factory that was used to create the given view.
124
+ /// Uses the associated object stored during createView().
125
+ static func factory(for view: NSView) -> NativeComponentFactory? {
126
+ guard let box = objc_getAssociatedObject(view, &factoryAssociatedKey) as? FactoryBox else {
127
+ return nil
128
+ }
129
+ return box.factory
130
+ }
131
+
132
+ // MARK: - Prop Updates
133
+
134
+ /// Update a property on a view using its associated factory.
135
+ func updateProp(view: NSView, key: String, value: Any?) {
136
+ guard let factory = ComponentRegistry.factory(for: view) else {
137
+ NSLog("[VueNative] Warning: No factory found for view %@", String(describing: Swift.type(of: view)))
138
+ return
139
+ }
140
+ factory.updateProp(view: view, key: key, value: value)
141
+ }
142
+
143
+ // MARK: - Event Listeners
144
+
145
+ /// Add an event listener to a view using its associated factory.
146
+ func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
147
+ guard let factory = ComponentRegistry.factory(for: view) else {
148
+ NSLog("[VueNative] Warning: No factory found for view %@", String(describing: Swift.type(of: view)))
149
+ return
150
+ }
151
+ factory.addEventListener(view: view, event: event, handler: handler)
152
+ }
153
+
154
+ /// Remove an event listener from a view using its associated factory.
155
+ func removeEventListener(view: NSView, event: String) {
156
+ guard let factory = ComponentRegistry.factory(for: view) else { return }
157
+ factory.removeEventListener(view: view, event: event)
158
+ }
159
+ }
160
+
161
+ // MARK: - FactoryBox
162
+
163
+ /// Box wrapper for NativeComponentFactory to store as an associated object.
164
+ /// objc_setAssociatedObject requires an AnyObject, so we wrap the protocol.
165
+ private final class FactoryBox {
166
+ let factory: NativeComponentFactory
167
+
168
+ init(factory: NativeComponentFactory) {
169
+ self.factory = factory
170
+ }
171
+ }
172
+
173
+ // MARK: - VRootFactory
174
+
175
+ /// Factory for the __ROOT__ component type.
176
+ /// Creates a FlippedView with a LayoutNode that serves as the root container.
177
+ final class VRootFactory: NativeComponentFactory {
178
+
179
+ func createView() -> NSView {
180
+ let view = FlippedView()
181
+ // Configure the root layout node to fill its container and stack children
182
+ // vertically (column direction matches the default CSS flex behaviour).
183
+ let node = view.ensureLayoutNode()
184
+ node.flexDirection = .column
185
+ node.flexGrow = 1
186
+ node.flexShrink = 1
187
+ node.width = .percent(100)
188
+ node.height = .percent(100)
189
+ return view
190
+ }
191
+
192
+ func updateProp(view: NSView, key: String, value: Any?) {
193
+ // Root view generally doesn't have custom props.
194
+ // Delegate to StyleEngine for any style-related props.
195
+ StyleEngine.apply(key: key, value: value, to: view)
196
+ }
197
+
198
+ func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
199
+ if event == "press" {
200
+ let wrapper = ClickGestureWrapper(handler: handler)
201
+ let click = NSClickGestureRecognizer(
202
+ target: wrapper,
203
+ action: #selector(ClickGestureWrapper.handleGesture(_:))
204
+ )
205
+ view.addGestureRecognizer(click)
206
+ GestureStorage.store(wrapper, for: view, event: event)
207
+ }
208
+ }
209
+ }
@@ -0,0 +1,155 @@
1
+ import AppKit
2
+ import ObjectiveC
3
+
4
+ /// Factory for VActionSheet — maps to NSMenu (context menu) on macOS.
5
+ /// macOS has no action sheet UI; an NSMenu popup is the closest equivalent.
6
+ /// The view itself is a zero-size hidden placeholder in the native tree.
7
+ final class VActionSheetFactory: NativeComponentFactory {
8
+
9
+ // MARK: - Associated object keys
10
+
11
+ private static var actionsKey: UInt8 = 0
12
+ private static var cancelLabelKey: UInt8 = 1
13
+ nonisolated(unsafe) static var onActionKey: UInt8 = 2
14
+ nonisolated(unsafe) static var onCancelKey: UInt8 = 3
15
+ private static var menuDelegateKey: UInt8 = 4
16
+
17
+ // MARK: - NativeComponentFactory
18
+
19
+ func createView() -> NSView {
20
+ let view = FlippedView()
21
+ view.isHidden = true
22
+ let node = view.ensureLayoutNode()
23
+ node.width = .points(0)
24
+ node.height = .points(0)
25
+ return view
26
+ }
27
+
28
+ func updateProp(view: NSView, key: String, value: Any?) {
29
+ switch key {
30
+ case "visible":
31
+ let visible = (value as? Bool) ?? ((value as? Int).map { $0 != 0 } ?? false)
32
+ if visible {
33
+ showMenu(for: view)
34
+ }
35
+
36
+ case "title":
37
+ // Ignored on macOS — NSMenu doesn't display a title in popup mode
38
+ break
39
+
40
+ case "actions":
41
+ objc_setAssociatedObject(
42
+ view, &VActionSheetFactory.actionsKey,
43
+ value,
44
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
45
+ )
46
+
47
+ case "cancelLabel":
48
+ objc_setAssociatedObject(
49
+ view, &VActionSheetFactory.cancelLabelKey,
50
+ value as? String,
51
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
52
+ )
53
+
54
+ default:
55
+ break
56
+ }
57
+ }
58
+
59
+ func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
60
+ switch event {
61
+ case "action":
62
+ objc_setAssociatedObject(
63
+ view, &VActionSheetFactory.onActionKey,
64
+ handler as AnyObject,
65
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
66
+ )
67
+ case "cancel":
68
+ objc_setAssociatedObject(
69
+ view, &VActionSheetFactory.onCancelKey,
70
+ handler as AnyObject,
71
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
72
+ )
73
+ default:
74
+ break
75
+ }
76
+ }
77
+
78
+ func removeEventListener(view: NSView, event: String) {
79
+ switch event {
80
+ case "action":
81
+ objc_setAssociatedObject(view, &VActionSheetFactory.onActionKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
82
+ case "cancel":
83
+ objc_setAssociatedObject(view, &VActionSheetFactory.onCancelKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
84
+ default:
85
+ break
86
+ }
87
+ }
88
+
89
+ // MARK: - Menu presentation
90
+
91
+ private func showMenu(for view: NSView) {
92
+ let menu = NSMenu()
93
+
94
+ // Parse actions — supports [String] and [[String: Any]] with {label, destructive?}
95
+ var actionLabels: [String] = []
96
+ if let actions = objc_getAssociatedObject(view, &VActionSheetFactory.actionsKey) as? [String] {
97
+ actionLabels = actions
98
+ } else if let actions = objc_getAssociatedObject(view, &VActionSheetFactory.actionsKey) as? [[String: Any]] {
99
+ actionLabels = actions.compactMap { $0["label"] as? String }
100
+ }
101
+
102
+ // Create proxy to handle menu item selection
103
+ let proxy = ActionSheetProxy(view: view)
104
+ objc_setAssociatedObject(
105
+ view, &VActionSheetFactory.menuDelegateKey,
106
+ proxy, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
107
+ )
108
+
109
+ for (index, label) in actionLabels.enumerated() {
110
+ let item = NSMenuItem(title: label, action: #selector(ActionSheetProxy.itemSelected(_:)), keyEquivalent: "")
111
+ item.target = proxy
112
+ item.tag = index
113
+ menu.addItem(item)
114
+ }
115
+
116
+ // Add separator + cancel item if cancelLabel is set
117
+ if let cancelLabel = objc_getAssociatedObject(view, &VActionSheetFactory.cancelLabelKey) as? String {
118
+ menu.addItem(.separator())
119
+ let cancelItem = NSMenuItem(title: cancelLabel, action: #selector(ActionSheetProxy.cancelSelected(_:)), keyEquivalent: "")
120
+ cancelItem.target = proxy
121
+ cancelItem.tag = -1
122
+ menu.addItem(cancelItem)
123
+ }
124
+
125
+ // Pop up the menu at the view's location (use superview if placeholder is hidden)
126
+ let targetView = view.superview ?? view
127
+ let location = NSPoint(x: 0, y: 0)
128
+ menu.popUp(positioning: nil, at: location, in: targetView)
129
+ }
130
+ }
131
+
132
+ // MARK: - ActionSheetProxy
133
+
134
+ /// Target-action proxy for NSMenu item selection.
135
+ private final class ActionSheetProxy: NSObject {
136
+ private weak var view: NSView?
137
+
138
+ init(view: NSView) {
139
+ self.view = view
140
+ }
141
+
142
+ @objc func itemSelected(_ sender: NSMenuItem) {
143
+ guard let view = view else { return }
144
+ if let handler = objc_getAssociatedObject(view, &VActionSheetFactory.onActionKey) as? ((Any?) -> Void) {
145
+ handler(["label": sender.title, "index": sender.tag])
146
+ }
147
+ }
148
+
149
+ @objc func cancelSelected(_ sender: NSMenuItem) {
150
+ guard let view = view else { return }
151
+ if let handler = objc_getAssociatedObject(view, &VActionSheetFactory.onCancelKey) as? ((Any?) -> Void) {
152
+ handler(nil)
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,85 @@
1
+ import AppKit
2
+
3
+ /// Factory for VActivityIndicator — a spinning progress indicator.
4
+ /// Maps to NSProgressIndicator with spinning style.
5
+ final class VActivityIndicatorFactory: NativeComponentFactory {
6
+
7
+ // MARK: - NativeComponentFactory
8
+
9
+ func createView() -> NSView {
10
+ let indicator = NSProgressIndicator()
11
+ indicator.style = .spinning
12
+ indicator.isIndeterminate = true
13
+ indicator.isDisplayedWhenStopped = false
14
+ indicator.wantsLayer = true
15
+ indicator.ensureLayoutNode()
16
+ indicator.startAnimation(nil)
17
+ return indicator
18
+ }
19
+
20
+ func updateProp(view: NSView, key: String, value: Any?) {
21
+ guard let indicator = view as? NSProgressIndicator else {
22
+ StyleEngine.apply(key: key, value: value, to: view)
23
+ return
24
+ }
25
+
26
+ switch key {
27
+ case "animating":
28
+ let animating: Bool
29
+ if let val = value as? Bool {
30
+ animating = val
31
+ } else if let val = value as? Int {
32
+ animating = val != 0
33
+ } else {
34
+ animating = true
35
+ }
36
+ if animating {
37
+ indicator.startAnimation(nil)
38
+ } else {
39
+ indicator.stopAnimation(nil)
40
+ }
41
+
42
+ case "color":
43
+ if let colorStr = value as? String {
44
+ indicator.appearance = nil // Reset to allow tinting
45
+ indicator.contentFilters = [CIFilter(name: "CIFalseColor",
46
+ parameters: [
47
+ "inputColor0": CIColor(color: NSColor.fromHex(colorStr))!,
48
+ "inputColor1": CIColor(color: NSColor.fromHex(colorStr))!
49
+ ])!]
50
+ } else {
51
+ indicator.contentFilters = []
52
+ }
53
+
54
+ case "hidesWhenStopped":
55
+ let hides: Bool
56
+ if let val = value as? Bool {
57
+ hides = val
58
+ } else if let val = value as? Int {
59
+ hides = val != 0
60
+ } else {
61
+ hides = true
62
+ }
63
+ indicator.isDisplayedWhenStopped = !hides
64
+
65
+ case "size":
66
+ if let sizeStr = value as? String {
67
+ switch sizeStr {
68
+ case "small":
69
+ indicator.controlSize = .small
70
+ case "large":
71
+ indicator.controlSize = .large
72
+ default:
73
+ indicator.controlSize = .regular
74
+ }
75
+ }
76
+
77
+ default:
78
+ StyleEngine.apply(key: key, value: value, to: view)
79
+ }
80
+ }
81
+
82
+ func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
83
+ // No events for activity indicator
84
+ }
85
+ }