@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,877 @@
1
+ import JavaScriptCore
2
+ import AppKit
3
+ import VueNativeShared
4
+
5
+ /// The main bridge between JavaScript and native AppKit.
6
+ ///
7
+ /// Responsibilities:
8
+ /// - Registers `__VN_flushOperations` on JSContext to receive batched ops from JS
9
+ /// - Parses operation batches (JSON) and processes them on the main thread
10
+ /// - Maintains `viewRegistry` mapping node IDs to native NSViews
11
+ /// - Triggers layout relayout after processing each batch
12
+ /// - Dispatches native events back to JS via `dispatchEventToJS`
13
+ ///
14
+ /// Thread safety:
15
+ /// - `__VN_flushOperations` is called on the JS queue
16
+ /// - JSON parsing happens on the JS queue
17
+ /// - All NSView operations dispatch to main thread
18
+ /// - `viewRegistry` is only accessed on the main thread
19
+ /// - `eventHandlers` is only accessed on the main thread
20
+ @MainActor
21
+ public final class NativeBridge: @preconcurrency NativeEventDispatcher {
22
+
23
+ // MARK: - Singleton
24
+
25
+ public static let shared = NativeBridge()
26
+
27
+ // MARK: - Properties
28
+
29
+ /// Maps node IDs to their native NSViews. Accessed only on main thread.
30
+ private var viewRegistry: [Int: NSView] = [:]
31
+
32
+ /// Maps node IDs to their component type strings. Accessed only on main thread.
33
+ private var typeRegistry: [Int: String] = [:]
34
+
35
+ /// Maps (nodeId, eventName) to event handler closures. Accessed only on main thread.
36
+ private var eventHandlers: [String: (Any?) -> Void] = [:]
37
+
38
+ /// Reverse index: maps nodeId to the set of event handler keys for that node.
39
+ private var eventKeysPerNode: [Int: Set<String>] = [:]
40
+
41
+ /// Maps each child node ID to its parent node ID.
42
+ private var nodeParent: [Int: Int] = [:]
43
+
44
+ /// Reverse index: maps parent node ID to an ordered list of child node IDs.
45
+ private var childrenOf: [Int: [Int]] = [:]
46
+
47
+ /// The root NSView that contains all rendered native views.
48
+ private weak var rootView: NSView?
49
+
50
+ /// The window's content view that hosts everything.
51
+ private weak var contentView: NSView?
52
+
53
+ /// The component registry for creating views and handling props/events.
54
+ private let registry = ComponentRegistry.shared
55
+
56
+ /// Reference to the JS runtime.
57
+ private let runtime = JSRuntime.shared
58
+
59
+ // MARK: - Teleport Support
60
+
61
+ /// Maps teleport marker IDs (start, end) for cleanup.
62
+ private var teleportMarkers: [Int: (start: Int, end: Int)] = [:]
63
+
64
+ /// Maps parent node IDs to their teleport containers.
65
+ private var teleportContainers: [Int: NSView] = [:]
66
+
67
+ /// Modal container for teleporting modal content.
68
+ private lazy var modalContainer: NSView = {
69
+ let container = FlippedView()
70
+ container.wantsLayer = true
71
+ container.layer?.backgroundColor = NSColor.clear.cgColor
72
+ container.translatesAutoresizingMaskIntoConstraints = false
73
+ return container
74
+ }()
75
+
76
+ // MARK: - Initialization
77
+
78
+ private init() {}
79
+
80
+ // MARK: - Bridge Function Registration
81
+
82
+ /// Register `__VN_flushOperations`, `__VN_teardown`, `__VN_log`, and `__VN_handleError`
83
+ /// on the given JSContext.
84
+ private func registerBridgeFunctions(on context: JSContext) {
85
+ let flushOps: @convention(block) (JSValue) -> Void = { [weak self] opsValue in
86
+ guard let self = self else { return }
87
+ guard let jsonString = opsValue.toString(), !jsonString.isEmpty else {
88
+ NSLog("[VueNative macOS Bridge] Warning: Empty operations batch")
89
+ return
90
+ }
91
+
92
+ guard let data = jsonString.data(using: .utf8),
93
+ let operations = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]] else {
94
+ NSLog("[VueNative macOS Bridge] Error: Failed to parse operations JSON")
95
+ return
96
+ }
97
+
98
+ DispatchQueue.main.async { [weak self] in
99
+ self?.processOperations(operations)
100
+ }
101
+ }
102
+ context.setObject(flushOps, forKeyedSubscript: "__VN_flushOperations" as NSString)
103
+
104
+ let teardown: @convention(block) () -> Void = { [weak self] in
105
+ NSLog("[VueNative macOS] Teardown called from JS")
106
+ _ = self
107
+ }
108
+ context.setObject(teardown, forKeyedSubscript: "__VN_teardown" as NSString)
109
+
110
+ let log: @convention(block) (JSValue) -> Void = { message in
111
+ NSLog("[VueNative macOS JS] \(message.toString() ?? "")")
112
+ }
113
+ context.setObject(log, forKeyedSubscript: "__VN_log" as NSString)
114
+
115
+ let handleError: @convention(block) (JSValue) -> Void = { errorInfoValue in
116
+ let jsonString = errorInfoValue.toString() ?? "{}"
117
+ NSLog("[VueNative macOS Error] %@", jsonString)
118
+
119
+ if let data = jsonString.data(using: .utf8),
120
+ let info = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
121
+ let message = info["message"] as? String ?? "Unknown error"
122
+ let stack = info["stack"] as? String ?? ""
123
+ let componentName = info["componentName"] as? String ?? "unknown"
124
+ NSLog("[VueNative macOS Error] Component: %@, Message: %@", componentName, message)
125
+ if !stack.isEmpty {
126
+ NSLog("[VueNative macOS Error] Stack: %@", stack)
127
+ }
128
+ }
129
+ }
130
+ context.setObject(handleError, forKeyedSubscript: "__VN_handleError" as NSString)
131
+ }
132
+
133
+ // MARK: - Setup
134
+
135
+ /// Initialize the bridge. Must be called after JSRuntime.initialize().
136
+ public func initialize(contentView: NSView) {
137
+ self.contentView = contentView
138
+
139
+ let runtime = self.runtime
140
+
141
+ runtime.jsQueue.async { [weak self] in
142
+ guard let self = self, let context = runtime.context else { return }
143
+ self.registerBridgeFunctions(on: context)
144
+ }
145
+
146
+ // Register all native modules, passing self as event dispatcher and
147
+ // a view lookup closure for modules that need to reference views (e.g. Animation).
148
+ NativeModuleRegistry.shared.registerDefaults(
149
+ dispatcher: self,
150
+ viewLookup: { [weak self] nodeId in self?.view(forId: nodeId) }
151
+ )
152
+ }
153
+
154
+ // MARK: - Operation Processing
155
+
156
+ private static let treeMutationOps: Set<String> = [
157
+ "create", "createText", "appendChild", "insertBefore", "removeChild",
158
+ "setRootView", "setText", "setElementText"
159
+ ]
160
+
161
+ private static let layoutAffectingStyles: Set<String> = [
162
+ "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight",
163
+ "flex", "flexGrow", "flexShrink", "flexBasis", "flexDirection",
164
+ "flexWrap", "alignItems", "alignSelf", "alignContent", "justifyContent",
165
+ "padding", "paddingTop", "paddingRight", "paddingBottom", "paddingLeft",
166
+ "paddingHorizontal", "paddingVertical", "paddingStart", "paddingEnd",
167
+ "margin", "marginTop", "marginRight", "marginBottom", "marginLeft",
168
+ "marginHorizontal", "marginVertical", "marginStart", "marginEnd",
169
+ "gap", "rowGap", "columnGap",
170
+ "position", "top", "right", "bottom", "left", "start", "end",
171
+ "aspectRatio", "display", "overflow", "direction",
172
+ ]
173
+
174
+ func processOperations(_ operations: [[String: Any]]) {
175
+ dispatchPrecondition(condition: .onQueue(.main))
176
+ #if DEBUG
177
+ NSLog("[VueNative macOS Bridge] Processing %d operations", operations.count)
178
+ #endif
179
+
180
+ var needsLayout = false
181
+
182
+ for operation in operations {
183
+ guard let op = operation["op"] as? String,
184
+ let args = operation["args"] as? [Any] else {
185
+ NSLog("[VueNative macOS Bridge] Warning: Invalid operation format: \(operation)")
186
+ continue
187
+ }
188
+
189
+ if !needsLayout && NativeBridge.treeMutationOps.contains(op) {
190
+ needsLayout = true
191
+ }
192
+
193
+ if !needsLayout && op == "updateStyle",
194
+ args.count >= 2,
195
+ let styles = args[1] as? [String: Any] {
196
+ for key in styles.keys {
197
+ if NativeBridge.layoutAffectingStyles.contains(key) {
198
+ needsLayout = true
199
+ break
200
+ }
201
+ }
202
+ }
203
+
204
+ switch op {
205
+ case "create":
206
+ handleCreate(args: args)
207
+ case "createText":
208
+ handleCreateText(args: args)
209
+ case "setText":
210
+ handleSetText(args: args)
211
+ case "setElementText":
212
+ handleSetElementText(args: args)
213
+ case "updateProp":
214
+ handleUpdateProp(args: args)
215
+ case "updateStyle":
216
+ handleUpdateStyle(args: args)
217
+ case "appendChild":
218
+ handleAppendChild(args: args)
219
+ case "insertBefore":
220
+ handleInsertBefore(args: args)
221
+ case "removeChild":
222
+ handleRemoveChild(args: args)
223
+ case "addEventListener":
224
+ handleAddEventListener(args: args)
225
+ case "removeEventListener":
226
+ handleRemoveEventListener(args: args)
227
+ case "setRootView":
228
+ handleSetRootView(args: args)
229
+ case "createTeleport":
230
+ handleCreateTeleport(args: args)
231
+ case "removeTeleport":
232
+ handleRemoveTeleport(args: args)
233
+ case "teleportTo":
234
+ handleTeleportTo(args: args)
235
+ case "invokeNativeModule":
236
+ handleInvokeNativeModule(args: args)
237
+ case "invokeNativeModuleSync":
238
+ handleInvokeNativeModuleSync(args: args)
239
+ default:
240
+ NSLog("[VueNative macOS Bridge] Warning: Unknown operation '\(op)'")
241
+ }
242
+ }
243
+
244
+ if needsLayout {
245
+ triggerLayout()
246
+ }
247
+ }
248
+
249
+ // MARK: - Operation Handlers
250
+
251
+ /// create: [nodeId: Int, type: String]
252
+ private func handleCreate(args: [Any]) {
253
+ guard args.count >= 2,
254
+ let nodeId = asInt(args[0]),
255
+ let type = args[1] as? String else {
256
+ NSLog("[VueNative macOS Bridge] Error: Invalid create args: \(args)")
257
+ return
258
+ }
259
+
260
+ guard let view = registry.createView(type: type) else {
261
+ NSLog("[VueNative macOS Bridge] Error: Failed to create view for type '\(type)'")
262
+ return
263
+ }
264
+
265
+ viewRegistry[nodeId] = view
266
+ typeRegistry[nodeId] = type
267
+ }
268
+
269
+ /// createText: [nodeId: Int, text: String]
270
+ private func handleCreateText(args: [Any]) {
271
+ guard args.count >= 2,
272
+ let nodeId = asInt(args[0]),
273
+ let text = args[1] as? String else {
274
+ NSLog("[VueNative macOS Bridge] Error: Invalid createText args: \(args)")
275
+ return
276
+ }
277
+
278
+ guard let view = registry.createView(type: "VText") else { return }
279
+
280
+ if let textField = view as? NSTextField {
281
+ textField.stringValue = text
282
+ textField.ensureLayoutNode().markDirty()
283
+ }
284
+
285
+ viewRegistry[nodeId] = view
286
+ typeRegistry[nodeId] = "VText"
287
+ }
288
+
289
+ /// setText: [nodeId: Int, text: String]
290
+ private func handleSetText(args: [Any]) {
291
+ guard args.count >= 2,
292
+ let nodeId = asInt(args[0]),
293
+ let text = args[1] as? String else {
294
+ return
295
+ }
296
+
297
+ guard let view = viewRegistry[nodeId] else { return }
298
+
299
+ if let textField = view as? NSTextField {
300
+ textField.stringValue = text
301
+ textField.ensureLayoutNode().markDirty()
302
+ }
303
+ }
304
+
305
+ /// setElementText: [nodeId: Int, text: String]
306
+ private func handleSetElementText(args: [Any]) {
307
+ guard args.count >= 2,
308
+ let nodeId = asInt(args[0]),
309
+ let text = args[1] as? String else {
310
+ return
311
+ }
312
+
313
+ guard let view = viewRegistry[nodeId] else { return }
314
+
315
+ if let textField = view as? NSTextField {
316
+ textField.stringValue = text
317
+ textField.ensureLayoutNode().markDirty()
318
+ } else {
319
+ if let existingTextField = view.subviews.first(where: { $0 is NSTextField }) as? NSTextField {
320
+ existingTextField.stringValue = text
321
+ existingTextField.ensureLayoutNode().markDirty()
322
+ }
323
+ }
324
+ }
325
+
326
+ /// updateProp: [nodeId: Int, key: String, value: Any?]
327
+ private func handleUpdateProp(args: [Any]) {
328
+ guard args.count >= 3,
329
+ let nodeId = asInt(args[0]),
330
+ let key = args[1] as? String else {
331
+ return
332
+ }
333
+
334
+ guard let view = viewRegistry[nodeId] else { return }
335
+ let value: Any? = (args[2] is NSNull) ? nil : args[2]
336
+ registry.updateProp(view: view, key: key, value: value)
337
+ }
338
+
339
+ /// updateStyle: [nodeId: Int, styles: [String: Any]]
340
+ private func handleUpdateStyle(args: [Any]) {
341
+ guard args.count >= 2,
342
+ let nodeId = asInt(args[0]),
343
+ let styles = args[1] as? [String: Any] else {
344
+ return
345
+ }
346
+
347
+ guard let view = viewRegistry[nodeId] else { return }
348
+
349
+ for (key, value) in styles {
350
+ let resolvedValue: Any? = (value is NSNull) ? nil : value
351
+ if let type = typeRegistry[nodeId], let factory = registry.factory(for: type) {
352
+ factory.updateProp(view: view, key: key, value: resolvedValue)
353
+ } else {
354
+ StyleEngine.apply(key: key, value: resolvedValue, to: view)
355
+ }
356
+ }
357
+ }
358
+
359
+ /// appendChild: [parentId: Int, childId: Int]
360
+ private func handleAppendChild(args: [Any]) {
361
+ guard args.count >= 2,
362
+ let parentId = asInt(args[0]),
363
+ let childId = asInt(args[1]) else {
364
+ return
365
+ }
366
+
367
+ guard let parentView = viewRegistry[parentId],
368
+ let childView = viewRegistry[childId] else {
369
+ return
370
+ }
371
+
372
+ nodeParent[childId] = parentId
373
+ childrenOf[parentId, default: []].append(childId)
374
+
375
+ let container = childContainer(for: parentView)
376
+ if let factory = ComponentRegistry.factory(for: parentView) {
377
+ factory.insertChild(childView, into: container, before: nil)
378
+ } else {
379
+ container.addSubview(childView)
380
+ }
381
+ }
382
+
383
+ /// insertBefore: [parentId: Int, childId: Int, beforeId: Int]
384
+ private func handleInsertBefore(args: [Any]) {
385
+ guard args.count >= 3,
386
+ let parentId = asInt(args[0]),
387
+ let childId = asInt(args[1]),
388
+ let beforeId = asInt(args[2]) else {
389
+ return
390
+ }
391
+
392
+ guard let parentView = viewRegistry[parentId],
393
+ let childView = viewRegistry[childId],
394
+ let beforeView = viewRegistry[beforeId] else {
395
+ return
396
+ }
397
+
398
+ nodeParent[childId] = parentId
399
+ var siblings = childrenOf[parentId, default: []]
400
+ if let idx = siblings.firstIndex(of: beforeId) {
401
+ siblings.insert(childId, at: idx)
402
+ } else {
403
+ siblings.append(childId)
404
+ }
405
+ childrenOf[parentId] = siblings
406
+
407
+ let container = childContainer(for: parentView)
408
+ if let factory = ComponentRegistry.factory(for: parentView) {
409
+ factory.insertChild(childView, into: container, before: beforeView)
410
+ } else if let subviews = container.subviews as? [NSView],
411
+ let index = subviews.firstIndex(of: beforeView) {
412
+ container.addSubview(childView, positioned: .below, relativeTo: beforeView)
413
+ } else {
414
+ container.addSubview(childView)
415
+ }
416
+ }
417
+
418
+ /// Returns the view that children should be inserted into.
419
+ private func childContainer(for view: NSView) -> NSView {
420
+ // For scroll views, children go into the documentView.
421
+ if let scrollView = view as? NSScrollView,
422
+ let docView = scrollView.documentView {
423
+ return docView
424
+ }
425
+ return view
426
+ }
427
+
428
+ /// removeChild: [childId: Int]
429
+ private func handleRemoveChild(args: [Any]) {
430
+ guard args.count >= 1,
431
+ let childId = asInt(args[0]) else {
432
+ return
433
+ }
434
+
435
+ guard let childView = viewRegistry[childId] else { return }
436
+
437
+ removeDescendantsFromIndex(childId)
438
+
439
+ if let parentId = nodeParent[childId],
440
+ let parentView = viewRegistry[parentId],
441
+ let factory = ComponentRegistry.factory(for: parentView) {
442
+ let container = childContainer(for: parentView)
443
+ factory.removeChild(childView, from: container)
444
+ } else {
445
+ childView.removeFromSuperview()
446
+ }
447
+
448
+ if let parentId = nodeParent[childId] {
449
+ childrenOf[parentId]?.removeAll { $0 == childId }
450
+ }
451
+
452
+ cleanupNodeRegistries(childId)
453
+ }
454
+
455
+ private func cleanupNodeRegistries(_ nodeId: Int) {
456
+ nodeParent.removeValue(forKey: nodeId)
457
+ viewRegistry.removeValue(forKey: nodeId)
458
+ typeRegistry.removeValue(forKey: nodeId)
459
+ childrenOf.removeValue(forKey: nodeId)
460
+
461
+ if let keys = eventKeysPerNode.removeValue(forKey: nodeId) {
462
+ for key in keys {
463
+ eventHandlers.removeValue(forKey: key)
464
+ }
465
+ }
466
+ }
467
+
468
+ private func removeDescendantsFromIndex(_ nodeId: Int) {
469
+ guard let children = childrenOf[nodeId] else { return }
470
+ for childId in children {
471
+ removeDescendantsFromIndex(childId)
472
+ cleanupNodeRegistries(childId)
473
+ }
474
+ }
475
+
476
+ /// addEventListener: [nodeId: Int, eventName: String, callbackId: Int]
477
+ private func handleAddEventListener(args: [Any]) {
478
+ guard args.count >= 2,
479
+ let nodeId = asInt(args[0]),
480
+ let eventName = args[1] as? String else {
481
+ return
482
+ }
483
+
484
+ guard let view = viewRegistry[nodeId] else { return }
485
+
486
+ let handlerKey = "\(nodeId):\(eventName)"
487
+
488
+ let handler: (Any?) -> Void = { [weak self] payload in
489
+ self?.dispatchEventToJS(nodeId: nodeId, eventName: eventName, payload: payload)
490
+ }
491
+
492
+ eventHandlers[handlerKey] = handler
493
+ eventKeysPerNode[nodeId, default: []].insert(handlerKey)
494
+
495
+ registry.addEventListener(view: view, event: eventName, handler: handler)
496
+ }
497
+
498
+ /// removeEventListener: [nodeId: Int, eventName: String]
499
+ private func handleRemoveEventListener(args: [Any]) {
500
+ guard args.count >= 2,
501
+ let nodeId = asInt(args[0]),
502
+ let eventName = args[1] as? String else {
503
+ return
504
+ }
505
+
506
+ let handlerKey = "\(nodeId):\(eventName)"
507
+ eventHandlers.removeValue(forKey: handlerKey)
508
+ eventKeysPerNode[nodeId]?.remove(handlerKey)
509
+
510
+ if let view = viewRegistry[nodeId] {
511
+ registry.removeEventListener(view: view, event: eventName)
512
+ }
513
+ }
514
+
515
+ /// setRootView: [nodeId: Int]
516
+ private func handleSetRootView(args: [Any]) {
517
+ guard args.count >= 1,
518
+ let nodeId = asInt(args[0]) else {
519
+ return
520
+ }
521
+
522
+ NSLog("[VueNative macOS Bridge] handleSetRootView called with nodeId=%d", nodeId)
523
+
524
+ guard let view = viewRegistry[nodeId] else {
525
+ NSLog("[VueNative macOS Bridge] Error: Root view \(nodeId) not found in registry")
526
+ return
527
+ }
528
+
529
+ guard let contentView = contentView else {
530
+ NSLog("[VueNative macOS Bridge] Error: No content view set")
531
+ return
532
+ }
533
+
534
+ NSLog("[VueNative macOS Bridge] Setting up root view, contentView bounds: %.1f x %.1f", contentView.bounds.width, contentView.bounds.height)
535
+
536
+ rootView = view
537
+ view.translatesAutoresizingMaskIntoConstraints = false
538
+
539
+ contentView.addSubview(view)
540
+
541
+ // Pin root view to content view edges
542
+ NSLayoutConstraint.activate([
543
+ view.topAnchor.constraint(equalTo: contentView.topAnchor),
544
+ view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
545
+ view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
546
+ view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
547
+ ])
548
+
549
+ contentView.needsLayout = true
550
+ contentView.layoutSubtreeIfNeeded()
551
+
552
+ DispatchQueue.main.async { [weak self] in
553
+ self?.triggerLayoutWithRetries(remaining: 3)
554
+ }
555
+
556
+ // Monitor dark mode changes
557
+ setupAppearanceObserver()
558
+
559
+ installModalContainerIfNeeded()
560
+ }
561
+
562
+ /// Set up observation for dark mode (effective appearance) changes.
563
+ private func setupAppearanceObserver() {
564
+ // On macOS, observe NSApp.effectiveAppearance via KVO
565
+ // or use DistributedNotificationCenter for theme changes.
566
+ DistributedNotificationCenter.default().addObserver(
567
+ forName: NSNotification.Name("AppleInterfaceThemeChangedNotification"),
568
+ object: nil,
569
+ queue: .main
570
+ ) { [weak self] _ in
571
+ let isDark = NSApp.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
572
+ self?.dispatchGlobalEvent("colorScheme:change", payload: ["colorScheme": isDark ? "dark" : "light"])
573
+ }
574
+ }
575
+
576
+ // MARK: - Teleport Handlers
577
+
578
+ private func handleCreateTeleport(args: [Any]) {
579
+ guard args.count >= 3,
580
+ let parentId = asInt(args[0]),
581
+ let startId = asInt(args[1]),
582
+ let endId = asInt(args[2]) else {
583
+ NSLog("[VueNative macOS Bridge] Error: Invalid createTeleport args: \(args)")
584
+ return
585
+ }
586
+
587
+ guard let parentView = viewRegistry[parentId] else {
588
+ NSLog("[VueNative macOS Bridge] Error: Parent view not found for teleport (id: \(parentId))")
589
+ return
590
+ }
591
+
592
+ teleportMarkers[parentId] = (start: startId, end: endId)
593
+
594
+ let container = FlippedView()
595
+ container.wantsLayer = true
596
+ container.layer?.backgroundColor = NSColor.clear.cgColor
597
+ parentView.addSubview(container)
598
+ teleportContainers[parentId] = container
599
+ }
600
+
601
+ private func handleRemoveTeleport(args: [Any]) {
602
+ guard args.count >= 1,
603
+ let parentId = asInt(args[0]) else {
604
+ NSLog("[VueNative macOS Bridge] Error: Invalid removeTeleport args: \(args)")
605
+ return
606
+ }
607
+
608
+ teleportContainers.removeValue(forKey: parentId)?.removeFromSuperview()
609
+ teleportMarkers.removeValue(forKey: parentId)
610
+ }
611
+
612
+ private func handleTeleportTo(args: [Any]) {
613
+ guard args.count >= 2,
614
+ let target = args[0] as? String,
615
+ let nodeId = asInt(args[1]) else {
616
+ NSLog("[VueNative macOS Bridge] Error: Invalid teleportTo args: \(args)")
617
+ return
618
+ }
619
+
620
+ guard let targetView = getTeleportTarget(target) else {
621
+ NSLog("[VueNative macOS Bridge] Warning: Teleport target '\(target)' not found")
622
+ return
623
+ }
624
+
625
+ guard let childView = viewRegistry[nodeId] else {
626
+ NSLog("[VueNative macOS Bridge] Warning: Node view not found for teleport (id: \(nodeId))")
627
+ return
628
+ }
629
+
630
+ childView.removeFromSuperview()
631
+ childView.translatesAutoresizingMaskIntoConstraints = false
632
+ targetView.addSubview(childView)
633
+ NSLayoutConstraint.activate([
634
+ childView.topAnchor.constraint(equalTo: targetView.topAnchor),
635
+ childView.leadingAnchor.constraint(equalTo: targetView.leadingAnchor),
636
+ childView.trailingAnchor.constraint(equalTo: targetView.trailingAnchor),
637
+ childView.bottomAnchor.constraint(equalTo: targetView.bottomAnchor),
638
+ ])
639
+ }
640
+
641
+ private func getTeleportTarget(_ target: String) -> NSView? {
642
+ switch target {
643
+ case "root":
644
+ return rootView
645
+ case "modal":
646
+ installModalContainerIfNeeded()
647
+ return modalContainer
648
+ default:
649
+ return nil
650
+ }
651
+ }
652
+
653
+ private func installModalContainerIfNeeded() {
654
+ guard let rootView = rootView, modalContainer.superview == nil else { return }
655
+ rootView.addSubview(modalContainer)
656
+ NSLayoutConstraint.activate([
657
+ modalContainer.topAnchor.constraint(equalTo: rootView.topAnchor),
658
+ modalContainer.leadingAnchor.constraint(equalTo: rootView.leadingAnchor),
659
+ modalContainer.trailingAnchor.constraint(equalTo: rootView.trailingAnchor),
660
+ modalContainer.bottomAnchor.constraint(equalTo: rootView.bottomAnchor),
661
+ ])
662
+ }
663
+
664
+ // MARK: - Native Module Handlers
665
+
666
+ private func handleInvokeNativeModule(args: [Any]) {
667
+ guard args.count >= 4,
668
+ let moduleName = args[0] as? String,
669
+ let methodName = args[1] as? String,
670
+ let moduleArgs = args[2] as? [Any],
671
+ let callbackId = asInt(args[3]) else {
672
+ NSLog("[VueNative macOS Bridge] Error: Invalid invokeNativeModule args: \(args)")
673
+ return
674
+ }
675
+
676
+ NativeModuleRegistry.shared.invoke(
677
+ module: moduleName,
678
+ method: methodName,
679
+ args: moduleArgs
680
+ ) { [weak self] result, error in
681
+ self?.resolveNativeCallback(callbackId: callbackId, result: result, error: error)
682
+ }
683
+ }
684
+
685
+ private func handleInvokeNativeModuleSync(args: [Any]) {
686
+ guard args.count >= 3,
687
+ let moduleName = args[0] as? String,
688
+ let methodName = args[1] as? String,
689
+ let moduleArgs = args[2] as? [Any] else {
690
+ NSLog("[VueNative macOS Bridge] Error: Invalid invokeNativeModuleSync args: \(args)")
691
+ return
692
+ }
693
+
694
+ _ = NativeModuleRegistry.shared.invokeSync(
695
+ module: moduleName,
696
+ method: methodName,
697
+ args: moduleArgs
698
+ )
699
+ }
700
+
701
+ private func resolveNativeCallback(callbackId: Int, result: Any?, error: String?) {
702
+ let safeResult: Any = result ?? NSNull()
703
+ let safeError: Any = error ?? NSNull()
704
+ runtime.callFunction("__VN_resolveCallback", withArguments: [callbackId, safeResult, safeError])
705
+ }
706
+
707
+ // MARK: - Layout
708
+
709
+ /// Trigger layout on the root view using the LayoutNode system.
710
+ private func triggerLayout() {
711
+ dispatchPrecondition(condition: .onQueue(.main))
712
+ guard let rootView = rootView else { return }
713
+
714
+ rootView.layoutSubtreeIfNeeded()
715
+
716
+ let bounds = rootView.bounds
717
+ NSLog("[VueNative macOS Bridge] triggerLayout() rootView bounds: %.1f x %.1f", bounds.width, bounds.height)
718
+
719
+ guard bounds.width > 0 && bounds.height > 0 else {
720
+ NSLog("[VueNative macOS Bridge] triggerLayout() skipped: bounds not yet resolved")
721
+ return
722
+ }
723
+
724
+ // Run layout using the LayoutNode system
725
+ if let layoutNode = rootView.layoutNode {
726
+ layoutNode.layout(availableWidth: bounds.width, availableHeight: bounds.height)
727
+ }
728
+ }
729
+
730
+ private func triggerLayoutWithRetries(remaining: Int) {
731
+ dispatchPrecondition(condition: .onQueue(.main))
732
+ guard let rootView = rootView else { return }
733
+
734
+ rootView.layoutSubtreeIfNeeded()
735
+ let bounds = rootView.bounds
736
+
737
+ if bounds.width > 0 && bounds.height > 0 {
738
+ triggerLayout()
739
+ } else if remaining > 0 {
740
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
741
+ self?.triggerLayoutWithRetries(remaining: remaining - 1)
742
+ }
743
+ } else {
744
+ NSLog("[VueNative macOS Bridge] triggerLayoutWithRetries exhausted -- root view bounds still zero")
745
+ }
746
+ }
747
+
748
+ // MARK: - Event Dispatch to JS
749
+
750
+ /// Dispatch a native event to the JS side.
751
+ public func dispatchEventToJS(nodeId: Int, eventName: String, payload: Any?) {
752
+ let safePayload: Any
753
+ if let payload = payload {
754
+ if JSONSerialization.isValidJSONObject(["v": payload]) {
755
+ safePayload = payload
756
+ } else if let str = payload as? String {
757
+ safePayload = str
758
+ } else if let num = payload as? NSNumber {
759
+ safePayload = num
760
+ } else {
761
+ safePayload = NSNull()
762
+ }
763
+ } else {
764
+ safePayload = NSNull()
765
+ }
766
+
767
+ runtime.callFunction("__VN_handleEvent", withArguments: [nodeId, eventName, safePayload])
768
+ }
769
+
770
+ /// Dispatch a global event to the JS side (not tied to any specific node).
771
+ public func dispatchGlobalEvent(_ eventName: String, payload: [String: Any] = [:]) {
772
+ let payloadJSON: String
773
+ if let data = try? JSONSerialization.data(withJSONObject: payload),
774
+ let str = String(data: data, encoding: .utf8) {
775
+ payloadJSON = str
776
+ } else {
777
+ payloadJSON = "{}"
778
+ }
779
+
780
+ runtime.callFunction("__VN_handleGlobalEvent", withArguments: [eventName, payloadJSON])
781
+ }
782
+
783
+ // MARK: - View Registry Access
784
+
785
+ /// Get a view from the registry by node ID. Must be called on main thread.
786
+ public func view(forNodeId nodeId: Int) -> NSView? {
787
+ dispatchPrecondition(condition: .onQueue(.main))
788
+ return viewRegistry[nodeId]
789
+ }
790
+
791
+ func view(forId id: Int) -> NSView? {
792
+ return viewRegistry[id]
793
+ }
794
+
795
+ public var registeredViewCount: Int {
796
+ return viewRegistry.count
797
+ }
798
+
799
+ // MARK: - Hot Reload
800
+
801
+ /// Reload the app with a new JavaScript bundle string.
802
+ public func reloadWithBundle(_ bundle: String) {
803
+ dispatchPrecondition(condition: .onQueue(.main))
804
+ NSLog("[VueNative macOS Bridge] reloadWithBundle: calling __VN_teardown on old context...")
805
+
806
+ runtime.jsQueue.sync {
807
+ if let context = runtime.context,
808
+ let teardown = context.objectForKeyedSubscript("__VN_teardown"),
809
+ !teardown.isUndefined {
810
+ teardown.call(withArguments: [])
811
+ context.evaluateScript("void 0;")
812
+ }
813
+ }
814
+
815
+ NSLog("[VueNative macOS Bridge] reloadWithBundle: clearing view hierarchy...")
816
+
817
+ for (_, view) in viewRegistry {
818
+ view.removeFromSuperview()
819
+ }
820
+ viewRegistry.removeAll()
821
+ typeRegistry.removeAll()
822
+ eventHandlers.removeAll()
823
+ eventKeysPerNode.removeAll()
824
+ nodeParent.removeAll()
825
+ childrenOf.removeAll()
826
+ teleportMarkers.removeAll()
827
+ teleportContainers.removeAll()
828
+ modalContainer.removeFromSuperview()
829
+
830
+ runtime.reload(bundle: bundle) { [weak self] success in
831
+ guard let self = self else { return }
832
+ guard success else {
833
+ NSLog("[VueNative macOS Bridge] reloadWithBundle: runtime reload failed")
834
+ ErrorOverlayView.show(error: "Hot reload failed.\n\nThe new bundle could not be evaluated. Check the terminal for the JS error.\n\nSave the file again to retry.")
835
+ return
836
+ }
837
+
838
+ guard let context = self.runtime.context else { return }
839
+ self.registerBridgeFunctions(on: context)
840
+
841
+ NSLog("[VueNative macOS Bridge] reloadWithBundle: bridge re-registered on new context")
842
+ }
843
+ }
844
+
845
+ // MARK: - Cleanup
846
+
847
+ public func reset() {
848
+ DispatchQueue.main.async { [weak self] in
849
+ guard let self = self else { return }
850
+ for (_, view) in self.viewRegistry {
851
+ view.removeFromSuperview()
852
+ }
853
+ self.viewRegistry.removeAll()
854
+ self.typeRegistry.removeAll()
855
+ self.eventHandlers.removeAll()
856
+ self.eventKeysPerNode.removeAll()
857
+ self.nodeParent.removeAll()
858
+ self.childrenOf.removeAll()
859
+ self.teleportMarkers.removeAll()
860
+ self.teleportContainers.removeAll()
861
+ self.modalContainer.removeFromSuperview()
862
+ self.rootView = nil
863
+ }
864
+ }
865
+
866
+ // MARK: - Helpers
867
+
868
+ private func asInt(_ value: Any) -> Int? {
869
+ if let i = value as? Int { return i }
870
+ if let d = value as? Double {
871
+ guard d.isFinite, d >= Double(Int.min), d <= Double(Int.max) else { return nil }
872
+ return Int(d)
873
+ }
874
+ if let n = value as? NSNumber { return n.intValue }
875
+ return nil
876
+ }
877
+ }