@legendapp/spark 0.0.1-next.2 → 0.0.1-next.4

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 (422) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -4
  3. package/ai-codex.ts +1 -0
  4. package/ai.ts +1 -0
  5. package/app-documents.ts +1 -0
  6. package/camera.ts +13 -0
  7. package/config-plugin.d.cts +2 -0
  8. package/contracts.ts +2 -0
  9. package/diagnostics.ts +1 -0
  10. package/expo-config.d.cts +1 -0
  11. package/metro.cjs +4 -1
  12. package/metro.d.cts +12 -0
  13. package/native.cjs +3 -1
  14. package/native.d.cts +6 -0
  15. package/package.json +93 -15
  16. package/patches/camera/react-native-nitro-image.patch +320 -0
  17. package/patches/camera/react-native-nitro-modules.patch +50 -0
  18. package/patches/camera/react-native-vision-camera.patch +2662 -0
  19. package/patches/camera/upstream.json +22 -0
  20. package/patches/dependencies.json +8 -0
  21. package/patches/react-native-enriched-markdown@1.0.2.patch +8684 -0
  22. package/postinstall.cjs +1 -0
  23. package/schema.json +276 -58
  24. package/settings-observable.ts +1 -0
  25. package/settings-window.ts +1 -0
  26. package/shortcuts-commands.ts +1 -0
  27. package/shortcuts-keyboard.ts +3 -0
  28. package/ui-classnames.ts +1 -0
  29. package/ui-glass.ts +1 -0
  30. package/ui-search.ts +1 -0
  31. package/ui-sidebar.ts +1 -0
  32. package/ui-split-view.ts +1 -0
  33. package/ui-swipe.ts +1 -0
  34. package/ui-symbol.ts +1 -0
  35. package/vendor/node_modules/@legendapp/spark-ai/package.json +19 -0
  36. package/vendor/node_modules/@legendapp/spark-ai/src/availability.ts +17 -0
  37. package/vendor/node_modules/@legendapp/spark-ai/src/index.ts +15 -0
  38. package/vendor/node_modules/@legendapp/spark-ai/src/invocations.ts +34 -0
  39. package/vendor/node_modules/@legendapp/spark-ai/src/output.ts +62 -0
  40. package/vendor/node_modules/@legendapp/spark-ai/src/run.ts +12 -0
  41. package/vendor/node_modules/@legendapp/spark-ai/src/types.ts +16 -0
  42. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.h +2 -1
  43. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.mm +126 -24
  44. package/vendor/node_modules/@legendapp/spark-audio/package.json +5 -2
  45. package/vendor/node_modules/@legendapp/spark-audio/src/NativeSparkAudio.ts +5 -1
  46. package/vendor/node_modules/@legendapp/spark-audio/src/desktop.ts +36 -32
  47. package/vendor/node_modules/@legendapp/spark-audio/src/events.ts +11 -0
  48. package/vendor/node_modules/@legendapp/spark-audio/src/hooks.ts +65 -0
  49. package/vendor/node_modules/@legendapp/spark-audio/src/index.web.ts +92 -33
  50. package/vendor/node_modules/@legendapp/spark-audio/src/media-session-unavailable.ts +6 -4
  51. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.ts +52 -19
  52. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.web.ts +18 -10
  53. package/vendor/node_modules/@legendapp/spark-audio/src/media-types.ts +40 -13
  54. package/vendor/node_modules/@legendapp/spark-audio/src/mobile.ts +40 -32
  55. package/vendor/node_modules/@legendapp/spark-audio/src/player.ts +97 -0
  56. package/vendor/node_modules/@legendapp/spark-audio/src/status-listeners.ts +21 -15
  57. package/vendor/node_modules/@legendapp/spark-audio/src/types.ts +9 -4
  58. package/vendor/node_modules/@legendapp/spark-audio/windows/SparkAudio/SparkAudio.h +67 -20
  59. package/vendor/node_modules/@legendapp/spark-auth-session/package.json +4 -3
  60. package/vendor/node_modules/@legendapp/spark-auth-session/src/core.ts +60 -42
  61. package/vendor/node_modules/@legendapp/spark-auth-session/src/index.ts +28 -12
  62. package/vendor/node_modules/@legendapp/spark-auth-session/src/mobile.ts +97 -41
  63. package/vendor/node_modules/@legendapp/spark-auth-session/src/types.ts +20 -9
  64. package/vendor/node_modules/@legendapp/spark-cli/dist/add-desktop.js +50 -7
  65. package/vendor/node_modules/@legendapp/spark-cli/dist/build-mode.js +1 -1
  66. package/vendor/node_modules/@legendapp/spark-cli/dist/build.js +145 -59
  67. package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +61 -0
  68. package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +10 -4
  69. package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +37 -8
  70. package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +86 -25
  71. package/vendor/node_modules/@legendapp/spark-cli/dist/dependency-pins.js +132 -0
  72. package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +13 -4
  73. package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +13 -10
  74. package/vendor/node_modules/@legendapp/spark-cli/dist/errors.js +100 -0
  75. package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +2 -2
  76. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-dev-patch.cjs +4 -2
  77. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-metro.cjs +1 -1
  78. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-native.cjs +5 -1
  79. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-node.js +3 -3
  80. package/vendor/node_modules/@legendapp/spark-cli/dist/helpers.js +1 -1
  81. package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +52 -44
  82. package/vendor/node_modules/@legendapp/spark-cli/dist/json.js +16 -0
  83. package/vendor/node_modules/@legendapp/spark-cli/dist/local.js +1 -1
  84. package/vendor/node_modules/@legendapp/spark-cli/dist/metro.cjs +26 -5
  85. package/vendor/node_modules/@legendapp/spark-cli/dist/native-metadata.js +26 -0
  86. package/vendor/node_modules/@legendapp/spark-cli/dist/native-patch-preflight.js +86 -0
  87. package/vendor/node_modules/@legendapp/spark-cli/dist/native-preparation.js +88 -0
  88. package/vendor/node_modules/@legendapp/spark-cli/dist/notary-profiles.js +75 -0
  89. package/vendor/node_modules/@legendapp/spark-cli/dist/package.js +4 -2
  90. package/vendor/node_modules/@legendapp/spark-cli/dist/platform.js +16 -2
  91. package/vendor/node_modules/@legendapp/spark-cli/dist/process.js +7 -0
  92. package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +90 -32
  93. package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +86 -5
  94. package/vendor/node_modules/@legendapp/spark-cli/dist/release.json +23 -18
  95. package/vendor/node_modules/@legendapp/spark-cli/dist/runner-install.js +145 -31
  96. package/vendor/node_modules/@legendapp/spark-cli/dist/signing.js +4 -3
  97. package/vendor/node_modules/@legendapp/spark-cli/dist/universal.cjs +2 -1
  98. package/vendor/node_modules/@legendapp/spark-cli/dist/updates.js +41 -4
  99. package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +5 -9
  100. package/vendor/node_modules/@legendapp/spark-cli/package.json +7 -6
  101. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/App.tsx +6 -12
  102. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/desktop.config.json +5 -3
  103. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/package.json +12 -10
  104. package/vendor/node_modules/@legendapp/spark-cli/templates/diff-lite/Git.macos.tsx +2 -2
  105. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.macos.tsx +23 -21
  106. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.windows.tsx +23 -21
  107. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.desktop.ts +11 -8
  108. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.macos.ts +3 -3
  109. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/Lifecycle.desktop.tsx +8 -14
  110. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/OpenFiles.desktop.tsx +5 -10
  111. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.desktop.ts +11 -8
  112. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.macos.ts +3 -3
  113. package/vendor/node_modules/@legendapp/spark-cli/templates/music-lite/assets.desktop.ts +2 -2
  114. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Activation.desktop.tsx +2 -2
  115. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Desktop.desktop.ts +37 -15
  116. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/session.ts +5 -6
  117. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/window-session.ts +5 -4
  118. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/desktop.config.json +5 -3
  119. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/metro.config.js +1 -1
  120. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/package.json +14 -12
  121. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/react-native.config.js +1 -1
  122. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/package.json +14 -12
  123. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/react-native.config.js +1 -1
  124. package/vendor/node_modules/@legendapp/spark-clipboard/macos/RNDesktopClipboard.mm +10 -16
  125. package/vendor/node_modules/@legendapp/spark-clipboard/package.json +2 -2
  126. package/vendor/node_modules/@legendapp/spark-clipboard/src/NativeDesktopClipboard.ts +1 -1
  127. package/vendor/node_modules/@legendapp/spark-clipboard/src/desktop.ts +55 -31
  128. package/vendor/node_modules/@legendapp/spark-clipboard/src/expo.ts +21 -11
  129. package/vendor/node_modules/@legendapp/spark-clipboard/src/options.ts +8 -0
  130. package/vendor/node_modules/@legendapp/spark-clipboard/src/types.ts +10 -0
  131. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.h +38 -15
  132. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.vcxproj +2 -0
  133. package/vendor/node_modules/@legendapp/spark-codex/RNCodex.podspec +22 -0
  134. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.hpp +23 -0
  135. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.mm +1529 -0
  136. package/vendor/node_modules/@legendapp/spark-codex/nitro.json +21 -0
  137. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/.gitattributes +1 -0
  138. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex+autolinking.rb +62 -0
  139. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.cpp +17 -0
  140. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.hpp +27 -0
  141. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Umbrella.hpp +38 -0
  142. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.mm +35 -0
  143. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.swift +16 -0
  144. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexAvailability.hpp +95 -0
  145. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexRunResult.hpp +99 -0
  146. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.cpp +24 -0
  147. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.hpp +71 -0
  148. package/vendor/node_modules/@legendapp/spark-codex/package.json +35 -0
  149. package/vendor/node_modules/@legendapp/spark-codex/react-native.config.js +11 -0
  150. package/vendor/node_modules/@legendapp/spark-codex/src/Codex.nitro.ts +30 -0
  151. package/vendor/node_modules/@legendapp/spark-codex/src/index.ts +65 -0
  152. package/vendor/node_modules/@legendapp/spark-codex/src/native.ts +6 -0
  153. package/vendor/node_modules/@legendapp/spark-commands/package.json +21 -0
  154. package/vendor/node_modules/@legendapp/spark-commands/src/bindings.ts +90 -0
  155. package/vendor/node_modules/@legendapp/spark-commands/src/index.tsx +400 -0
  156. package/vendor/node_modules/@legendapp/spark-commands/src/router.ts +138 -0
  157. package/vendor/node_modules/@legendapp/spark-commands/src/storage.ts +20 -0
  158. package/vendor/node_modules/@legendapp/spark-context-menu/macos/RNContextMenu.mm +7 -8
  159. package/vendor/node_modules/@legendapp/spark-context-menu/package.json +4 -3
  160. package/vendor/node_modules/@legendapp/spark-context-menu/src/NativeContextMenu.ts +1 -1
  161. package/vendor/node_modules/@legendapp/spark-context-menu/src/index.ts +26 -26
  162. package/vendor/node_modules/@legendapp/spark-context-menu/windows/SparkContextMenu/SparkContextMenu.h +3 -2
  163. package/vendor/node_modules/@legendapp/spark-desktop-app/RNDesktopApp.podspec +2 -1
  164. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryJSI.hpp +89 -0
  165. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryWindows.hpp +22 -0
  166. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/RNDesktopApp.mm +13 -7
  167. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkBinaryJSI.h +48 -0
  168. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.h +4 -0
  169. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.mm +33 -33
  170. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktopError.h +20 -0
  171. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.h +26 -0
  172. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.mm +49 -0
  173. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.h +9 -0
  174. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.mm +70 -0
  175. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkWindow.mm +9 -6
  176. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/recent-documents/RNRecentDocumentController.mm +55 -0
  177. package/vendor/node_modules/@legendapp/spark-desktop-app/package.json +5 -3
  178. package/vendor/node_modules/@legendapp/spark-desktop-app/src/NativeDesktopApp.ts +1 -1
  179. package/vendor/node_modules/@legendapp/spark-desktop-app/src/api.ts +86 -40
  180. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/accelerator.ts +40 -0
  181. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/index.ts +56 -0
  182. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/menu.ts +116 -0
  183. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/native-buffer.ts +22 -0
  184. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/path.ts +24 -0
  185. package/vendor/node_modules/@legendapp/spark-desktop-app/src/events.ts +75 -0
  186. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.ts +2 -0
  187. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.windows.ts +2 -0
  188. package/vendor/node_modules/@legendapp/spark-desktop-app/src/open-requests.ts +43 -0
  189. package/vendor/node_modules/@legendapp/spark-desktop-app/src/restoration.ts +11 -0
  190. package/vendor/node_modules/@legendapp/spark-desktop-app/src/transport.ts +10 -0
  191. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.cjs +10 -5
  192. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.d.cts +3 -0
  193. package/vendor/node_modules/@legendapp/spark-desktop-config/autolinking.cjs +35 -0
  194. package/vendor/node_modules/@legendapp/spark-desktop-config/config.cjs +18 -3
  195. package/vendor/node_modules/@legendapp/spark-desktop-config/config.d.cts +89 -9
  196. package/vendor/node_modules/@legendapp/spark-desktop-config/expo.cjs +1 -0
  197. package/vendor/node_modules/@legendapp/spark-desktop-config/expo.d.cts +3 -0
  198. package/vendor/node_modules/@legendapp/spark-desktop-config/identity.cjs +3 -2
  199. package/vendor/node_modules/@legendapp/spark-desktop-config/lifecycle.cjs +20 -0
  200. package/vendor/node_modules/@legendapp/spark-desktop-config/native-patches.cjs +106 -0
  201. package/vendor/node_modules/@legendapp/spark-desktop-config/native-patches.d.cts +1 -0
  202. package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +7 -5
  203. package/vendor/node_modules/@legendapp/spark-desktop-config/schema.json +276 -58
  204. package/vendor/node_modules/@legendapp/spark-desktop-config/updates.cjs +1 -0
  205. package/vendor/node_modules/@legendapp/spark-desktop-config/validation.cjs +35 -0
  206. package/vendor/node_modules/@legendapp/spark-desktop-config/windows.plugin.cjs +2 -1
  207. package/vendor/node_modules/@legendapp/spark-desktop-host/AppDelegate.mm +129 -11
  208. package/vendor/node_modules/@legendapp/spark-desktop-host/package.json +2 -2
  209. package/vendor/node_modules/@legendapp/spark-desktop-host/windows/application.inc +277 -121
  210. package/vendor/node_modules/@legendapp/spark-desktop-links/common/AuthLoopback.h +5 -7
  211. package/vendor/node_modules/@legendapp/spark-desktop-links/macos/RNDesktopLinks.mm +10 -9
  212. package/vendor/node_modules/@legendapp/spark-desktop-links/package.json +2 -2
  213. package/vendor/node_modules/@legendapp/spark-desktop-links/src/NativeDesktopLinks.ts +1 -1
  214. package/vendor/node_modules/@legendapp/spark-desktop-links/src/api.ts +27 -33
  215. package/vendor/node_modules/@legendapp/spark-desktop-links/src/expo.ts +3 -4
  216. package/vendor/node_modules/@legendapp/spark-desktop-links/src/native.ts +9 -0
  217. package/vendor/node_modules/@legendapp/spark-desktop-links/windows/SparkLinks/SparkLinks.h +22 -22
  218. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/RNDesktopShortcuts.podspec +1 -0
  219. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/RNDesktopShortcuts.mm +1 -1
  220. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.h +9 -0
  221. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.mm +307 -0
  222. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/package.json +8 -5
  223. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/NativeDesktopShortcuts.ts +1 -1
  224. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/api.ts +39 -14
  225. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/NativeKeyboardManager.ts +12 -0
  226. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/codes.ts +108 -0
  227. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/index.ts +99 -0
  228. package/vendor/node_modules/@legendapp/spark-desktop-windows/RNDesktopWindows.podspec +2 -0
  229. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/RNDesktopWindows.mm +229 -103
  230. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/SparkWindowGeometry.h +63 -0
  231. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.h +20 -0
  232. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.mm +27 -0
  233. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.h +12 -0
  234. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.mm +3169 -0
  235. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManagerStartup.mm +31 -0
  236. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/SidebarStartupFallbacks.mm +14 -0
  237. package/vendor/node_modules/@legendapp/spark-desktop-windows/package.json +10 -6
  238. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/NativeDesktopWindowManager.ts +1 -1
  239. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/api.ts +146 -70
  240. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/diagnostics.ts +17 -0
  241. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.ts +3 -1
  242. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.windows.ts +3 -1
  243. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos-adapter.ts +53 -0
  244. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos.ts +51 -0
  245. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/transport.ts +41 -0
  246. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/types.ts +140 -0
  247. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/validation.ts +162 -0
  248. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/window-manager/NativeWindowManager.ts +28 -0
  249. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/WindowProvider.tsx +20 -0
  250. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/createWindowsNavigator.tsx +78 -0
  251. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/index.ts +6 -0
  252. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/primaryWindowLifecycle.ts +34 -0
  253. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/types.ts +11 -0
  254. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/usePrimaryWindowLifecycle.ts +18 -0
  255. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/useWindowFocusEffect.ts +17 -0
  256. package/vendor/node_modules/@legendapp/spark-documents/package.json +22 -0
  257. package/vendor/node_modules/@legendapp/spark-documents/src/controller.ts +97 -0
  258. package/vendor/node_modules/@legendapp/spark-documents/src/documentTransition.ts +13 -0
  259. package/vendor/node_modules/@legendapp/spark-documents/src/hooks.ts +139 -0
  260. package/vendor/node_modules/@legendapp/spark-documents/src/index.tsx +50 -0
  261. package/vendor/node_modules/@legendapp/spark-documents/src/reload.ts +39 -0
  262. package/vendor/node_modules/@legendapp/spark-documents/src/requests.ts +15 -0
  263. package/vendor/node_modules/@legendapp/spark-drag-drop/package.json +4 -3
  264. package/vendor/node_modules/@legendapp/spark-drag-drop/src/DesktopDragViewNativeComponent.ts +1 -1
  265. package/vendor/node_modules/@legendapp/spark-drag-drop/src/contracts.ts +73 -18
  266. package/vendor/node_modules/@legendapp/spark-drag-drop/src/index.tsx +58 -15
  267. package/vendor/node_modules/@legendapp/spark-drag-drop/windows/SparkDragDrop/SparkDragDrop.h +14 -8
  268. package/vendor/node_modules/@legendapp/spark-file-dialog/ios/RNFileDialog.mm +34 -121
  269. package/vendor/node_modules/@legendapp/spark-file-dialog/package.json +12 -5
  270. package/vendor/node_modules/@legendapp/spark-file-dialog/src/NativeFileDialog.ts +1 -5
  271. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.ts +19 -84
  272. package/vendor/node_modules/@legendapp/spark-file-dialog/src/native.ts +12 -0
  273. package/vendor/node_modules/@legendapp/spark-file-dialog/src/options.ts +34 -14
  274. package/vendor/node_modules/@legendapp/spark-file-dialog/src/types.ts +26 -0
  275. package/vendor/node_modules/@legendapp/spark-file-dialog/windows/SparkFileDialog/SparkFileDialog.h +42 -58
  276. package/vendor/node_modules/@legendapp/spark-file-system/RNDesktopFileSystem.podspec +1 -0
  277. package/vendor/node_modules/@legendapp/spark-file-system/macos/RNDesktopFileSystem.mm +32 -21
  278. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.h +24 -0
  279. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.mm +215 -0
  280. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.h +9 -0
  281. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.mm +82 -0
  282. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.h +47 -0
  283. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.mm +306 -0
  284. package/vendor/node_modules/@legendapp/spark-file-system/package.json +8 -6
  285. package/vendor/node_modules/@legendapp/spark-file-system/src/NativeDesktopFileSystem.ts +1 -1
  286. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/NativeFileScanner.ts +11 -0
  287. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/index.ts +93 -0
  288. package/vendor/node_modules/@legendapp/spark-file-system/src/handles.ts +48 -14
  289. package/vendor/node_modules/@legendapp/spark-file-system/src/index.ts +134 -45
  290. package/vendor/node_modules/@legendapp/spark-file-system/src/path.ts +1 -9
  291. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.h +102 -26
  292. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.vcxproj +2 -0
  293. package/vendor/node_modules/@legendapp/spark-global-shortcuts/RNDesktopGlobalShortcuts.podspec +1 -0
  294. package/vendor/node_modules/@legendapp/spark-global-shortcuts/macos/RNDesktopGlobalShortcuts.mm +7 -4
  295. package/vendor/node_modules/@legendapp/spark-global-shortcuts/package.json +4 -3
  296. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/NativeDesktopGlobalShortcuts.ts +1 -1
  297. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/index.ts +39 -10
  298. package/vendor/node_modules/@legendapp/spark-global-shortcuts/windows/SparkGlobalShortcuts/SparkGlobalShortcuts.h +5 -4
  299. package/vendor/node_modules/@legendapp/spark-message-dialog/macos/RNDesktopMessageDialog.mm +2 -2
  300. package/vendor/node_modules/@legendapp/spark-message-dialog/package.json +2 -2
  301. package/vendor/node_modules/@legendapp/spark-message-dialog/src/NativeDesktopMessageDialog.ts +1 -1
  302. package/vendor/node_modules/@legendapp/spark-message-dialog/src/index.ts +48 -11
  303. package/vendor/node_modules/@legendapp/spark-native-menu/ios/RNNativeMenu.mm +141 -609
  304. package/vendor/node_modules/@legendapp/spark-native-menu/package.json +9 -4
  305. package/vendor/node_modules/@legendapp/spark-native-menu/src/NativeMenu.ts +2 -7
  306. package/vendor/node_modules/@legendapp/spark-native-menu/src/api.ts +76 -121
  307. package/vendor/node_modules/@legendapp/spark-native-menu/src/hooks.ts +97 -0
  308. package/vendor/node_modules/@legendapp/spark-native-menu/src/windows-menus.ts +43 -37
  309. package/vendor/node_modules/@legendapp/spark-notifications/macos/RNDesktopNotifications.mm +75 -30
  310. package/vendor/node_modules/@legendapp/spark-notifications/package.json +2 -2
  311. package/vendor/node_modules/@legendapp/spark-notifications/src/NativeDesktopNotifications.ts +1 -1
  312. package/vendor/node_modules/@legendapp/spark-notifications/src/index.ts +130 -42
  313. package/vendor/node_modules/@legendapp/spark-notifications/windows/SparkNotifications/SparkNotifications.h +51 -8
  314. package/vendor/node_modules/@legendapp/spark-processes/macos/RNDesktopProcesses.mm +94 -21
  315. package/vendor/node_modules/@legendapp/spark-processes/package.json +4 -3
  316. package/vendor/node_modules/@legendapp/spark-processes/src/NativeDesktopProcesses.ts +1 -1
  317. package/vendor/node_modules/@legendapp/spark-processes/src/index.ts +124 -28
  318. package/vendor/node_modules/@legendapp/spark-processes/src/types.ts +34 -0
  319. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.h +83 -34
  320. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.vcxproj +2 -0
  321. package/vendor/node_modules/@legendapp/spark-secure-storage/macos/RNDesktopSecureStorage.mm +49 -40
  322. package/vendor/node_modules/@legendapp/spark-secure-storage/package.json +2 -2
  323. package/vendor/node_modules/@legendapp/spark-secure-storage/src/NativeDesktopSecureStorage.ts +1 -1
  324. package/vendor/node_modules/@legendapp/spark-secure-storage/src/desktop.ts +16 -14
  325. package/vendor/node_modules/@legendapp/spark-secure-storage/src/expo.ts +0 -1
  326. package/vendor/node_modules/@legendapp/spark-secure-storage/src/options.ts +3 -2
  327. package/vendor/node_modules/@legendapp/spark-secure-storage/src/unavailable.ts +6 -3
  328. package/vendor/node_modules/@legendapp/spark-settings/package.json +10 -3
  329. package/vendor/node_modules/@legendapp/spark-settings/src/fileStorage.ts +11 -0
  330. package/vendor/node_modules/@legendapp/spark-settings/src/index.ts +7 -9
  331. package/vendor/node_modules/@legendapp/spark-settings/src/storage/index.ts +142 -0
  332. package/vendor/node_modules/@legendapp/spark-settings/src/store.ts +63 -21
  333. package/vendor/node_modules/@legendapp/spark-settings-ui/package.json +20 -0
  334. package/vendor/node_modules/@legendapp/spark-settings-ui/src/index.tsx +436 -0
  335. package/vendor/node_modules/@legendapp/spark-settings-ui/src/options.ts +10 -0
  336. package/vendor/node_modules/@legendapp/spark-sqlite/package.json +3 -3
  337. package/vendor/node_modules/@legendapp/spark-sqlite/src/database.ts +182 -0
  338. package/vendor/node_modules/@legendapp/spark-sqlite/src/index.ts +19 -6
  339. package/vendor/node_modules/@legendapp/spark-sqlite/src/types.ts +22 -0
  340. package/vendor/node_modules/@legendapp/spark-system/macos/RNDesktopSystem.mm +36 -11
  341. package/vendor/node_modules/@legendapp/spark-system/package.json +2 -2
  342. package/vendor/node_modules/@legendapp/spark-system/src/NativeDesktopSystem.ts +1 -1
  343. package/vendor/node_modules/@legendapp/spark-system/src/index.ts +89 -32
  344. package/vendor/node_modules/@legendapp/spark-system/windows/SparkSystem/SparkSystem.h +8 -4
  345. package/vendor/node_modules/@legendapp/spark-tray/macos/RNDesktopTray.mm +20 -8
  346. package/vendor/node_modules/@legendapp/spark-tray/package.json +2 -2
  347. package/vendor/node_modules/@legendapp/spark-tray/src/NativeDesktopTray.ts +1 -1
  348. package/vendor/node_modules/@legendapp/spark-tray/src/index.ts +91 -34
  349. package/vendor/node_modules/@legendapp/spark-tray/windows/SparkTray/SparkTray.h +45 -15
  350. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkButton.mm +2 -1
  351. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkTextInput.mm +10 -2
  352. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.h +15 -0
  353. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.mm +1419 -0
  354. package/vendor/node_modules/@legendapp/spark-ui/macos/glass-effect-view/RNGlassEffectView.mm +72 -0
  355. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.h +15 -0
  356. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.mm +194 -0
  357. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.h +15 -0
  358. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.mm +180 -0
  359. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.h +15 -0
  360. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.mm +155 -0
  361. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.h +18 -0
  362. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.mm +645 -0
  363. package/vendor/node_modules/@legendapp/spark-ui/macos/swipe-actions/RNSwipeActions.h +10 -0
  364. package/vendor/node_modules/@legendapp/spark-ui/macos/swipe-actions/RNSwipeActions.mm +422 -0
  365. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.h +15 -0
  366. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.mm +164 -0
  367. package/vendor/node_modules/@legendapp/spark-ui/package.json +17 -4
  368. package/vendor/node_modules/@legendapp/spark-ui/src/SparkSelectNativeComponent.ts +3 -1
  369. package/vendor/node_modules/@legendapp/spark-ui/src/SparkTextInputNativeComponent.ts +6 -2
  370. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/SidebarSplitViewNativeComponent.ts +37 -0
  371. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +86 -0
  372. package/vendor/node_modules/@legendapp/spark-ui/src/availability.ts +10 -0
  373. package/vendor/node_modules/@legendapp/spark-ui/src/classnames/index.ts +8 -0
  374. package/vendor/node_modules/@legendapp/spark-ui/src/control.ts +42 -0
  375. package/vendor/node_modules/@legendapp/spark-ui/src/desktop.tsx +56 -0
  376. package/vendor/node_modules/@legendapp/spark-ui/src/glass-effect-view/index.tsx +15 -0
  377. package/vendor/node_modules/@legendapp/spark-ui/src/index.android.tsx +35 -21
  378. package/vendor/node_modules/@legendapp/spark-ui/src/index.ios.tsx +55 -26
  379. package/vendor/node_modules/@legendapp/spark-ui/src/index.tsx +1 -27
  380. package/vendor/node_modules/@legendapp/spark-ui/src/index.web.tsx +45 -14
  381. package/vendor/node_modules/@legendapp/spark-ui/src/index.windows.tsx +1 -42
  382. package/vendor/node_modules/@legendapp/spark-ui/src/mobile-input.tsx +11 -0
  383. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSegmentedControlNativeComponent.ts +17 -0
  384. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSelectNativeComponent.ts +17 -0
  385. package/vendor/node_modules/@legendapp/spark-ui/src/select.ts +5 -3
  386. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/SFSymbolNativeComponent.ts +13 -0
  387. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/index.tsx +27 -0
  388. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarItemNativeComponent.ts +25 -0
  389. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarNativeComponent.ts +24 -0
  390. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/index.tsx +104 -0
  391. package/vendor/node_modules/@legendapp/spark-ui/src/specialized.ts +22 -0
  392. package/vendor/node_modules/@legendapp/spark-ui/src/swipe-actions/SwipeActionsNativeComponent.ts +11 -0
  393. package/vendor/node_modules/@legendapp/spark-ui/src/swipe-actions/index.tsx +41 -0
  394. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/TextInputSearchNativeComponent.ts +33 -0
  395. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/index.tsx +34 -0
  396. package/vendor/node_modules/@legendapp/spark-ui/src/types.ts +30 -19
  397. package/vendor/node_modules/@legendapp/spark-ui/uniwind.ts +3 -1
  398. package/vendor/node_modules/@legendapp/spark-ui/uniwind.web.ts +3 -1
  399. package/vendor/node_modules/@legendapp/spark-ui/windows/SparkUI/SparkUI.h +16 -11
  400. package/vendor/node_modules/@legendapp/spark-updates/macos/RNDesktopUpdates.mm +21 -8
  401. package/vendor/node_modules/@legendapp/spark-updates/package.json +2 -2
  402. package/vendor/node_modules/@legendapp/spark-updates/src/NativeDesktopUpdates.ts +3 -2
  403. package/vendor/node_modules/@legendapp/spark-updates/src/index.ts +71 -15
  404. package/vendor/node_modules/@legendapp/spark-webview/package.json +2 -2
  405. package/vendor/node_modules/@legendapp/spark-webview/src/WebView.tsx +11 -0
  406. package/vendor/node_modules/@legendapp/spark-webview/src/adapter.ts +55 -0
  407. package/vendor/node_modules/@legendapp/spark-webview/src/index.ts +2 -2
  408. package/vendor/node_modules/@legendapp/spark-webview/src/types.ts +32 -0
  409. package/vendor/node_modules/@legendapp/spark-webview/src/view-props.ts +28 -0
  410. package/vendor/node_modules/@legendapp/spark-window-options/index.cjs +51 -14
  411. package/vendor/node_modules/@legendapp/spark-window-options/index.d.ts +21 -8
  412. package/vendor/node_modules/@legendapp/spark-window-options/package.json +1 -1
  413. package/windows-macos.ts +1 -0
  414. package/expo-metro.cjs +0 -1
  415. package/message-dialog.ts +0 -1
  416. package/universal.cjs +0 -1
  417. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/accelerator.ts +0 -22
  418. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows-options.ts +0 -11
  419. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.windows.ts +0 -15
  420. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/accelerator.ts +0 -22
  421. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.h +0 -4
  422. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.mm +0 -53
@@ -0,0 +1,2662 @@
1
+ diff --git a/VisionCamera.podspec b/VisionCamera.podspec
2
+ index e798a1d..5a0daf9 100644
3
+ --- a/VisionCamera.podspec
4
+ +++ b/VisionCamera.podspec
5
+ @@ -10,7 +10,8 @@ Pod::Spec.new do |s|
6
+ s.license = package["license"]
7
+ s.authors = package["author"]
8
+
9
+ - s.platforms = { :ios => min_ios_version_supported, :visionos => 1.0 }
10
+ + s.platforms = { :ios => min_ios_version_supported, :visionos => 1.0, :osx => "14.0" }
11
+ + s.osx.prefix_header_contents = "#include <CoreGraphics/CoreGraphics.h>"
12
+ s.source = { :git => "https://github.com/mrousavy/nitro.git", :tag => "#{s.version}" }
13
+
14
+ s.source_files = [
15
+ diff --git a/ios/Delegates/CapturePhotoDelegate.swift b/ios/Delegates/CapturePhotoDelegate.swift
16
+ index 58fdb4e..023bbf5 100644
17
+ --- a/ios/Delegates/CapturePhotoDelegate.swift
18
+ +++ b/ios/Delegates/CapturePhotoDelegate.swift
19
+ @@ -88,6 +88,9 @@ final class CapturePhotoDelegate: NSObject, AVCapturePhotoCaptureDelegate {
20
+ CapturePhotoDelegate.delegates.removeAll { $0 == self }
21
+ }
22
+
23
+ +#if os(macOS)
24
+ +
25
+ +#else
26
+ @available(iOS 17.0, *)
27
+ func photoOutput(
28
+ _ output: AVCapturePhotoOutput,
29
+ @@ -95,5 +98,6 @@ final class CapturePhotoDelegate: NSObject, AVCapturePhotoCaptureDelegate {
30
+ error: (any Error)?
31
+ ) {
32
+ logger.warning("didFinishCapturingDeferredPhotoProxy is not implemented!")
33
+ - }
34
+ + }#endif
35
+ +
36
+ }
37
+ diff --git a/ios/Delegates/DepthFrameDelegate.swift b/ios/Delegates/DepthFrameDelegate.swift
38
+ index 8d3d0b5..cff3568 100644
39
+ --- a/ios/Delegates/DepthFrameDelegate.swift
40
+ +++ b/ios/Delegates/DepthFrameDelegate.swift
41
+ @@ -1,3 +1,4 @@
42
+ +#if !os(macOS)
43
+ //
44
+ // DepthFrameDelegate.swift
45
+ // VisionCamera
46
+ @@ -30,3 +31,5 @@ final class DepthFrameDelegate: NSObject, AVCaptureDepthDataOutputDelegate {
47
+ }
48
+ }
49
+ }
50
+ +
51
+ +#endif
52
+ diff --git a/ios/Delegates/SynchronizedOutputDelegate.swift b/ios/Delegates/SynchronizedOutputDelegate.swift
53
+ index 16bb97f..938c4f1 100644
54
+ --- a/ios/Delegates/SynchronizedOutputDelegate.swift
55
+ +++ b/ios/Delegates/SynchronizedOutputDelegate.swift
56
+ @@ -1,3 +1,4 @@
57
+ +#if !os(macOS)
58
+ //
59
+ // SynchronizedOutputDelegate.swift
60
+ // VisionCamera
61
+ @@ -108,3 +109,5 @@ final class SynchronizedOutputDelegate: NSObject, AVCaptureDataOutputSynchronize
62
+ return SynchronizedDataFrame(data: .depth(buffer), metadata: metadata)
63
+ }
64
+ }
65
+ +
66
+ +#endif
67
+ diff --git a/ios/Extensions/AVFoundation/AVCaptureConnection+orientation.swift b/ios/Extensions/AVFoundation/AVCaptureConnection+orientation.swift
68
+ index 36181e9..112d08e 100644
69
+ --- a/ios/Extensions/AVFoundation/AVCaptureConnection+orientation.swift
70
+ +++ b/ios/Extensions/AVFoundation/AVCaptureConnection+orientation.swift
71
+ @@ -10,10 +10,18 @@ import NitroModules
72
+
73
+ extension AVCaptureConnection {
74
+ var orientation: CameraOrientation {
75
+ +#if os(macOS)
76
+ + return .up
77
+ +#else
78
+ return CameraOrientation(avOrientation: videoOrientation)
79
+ +#endif
80
+ }
81
+
82
+ func setOrientation(_ orientation: CameraOrientation) throws {
83
+ +#if os(macOS)
84
+ + // Desktop cameras supply upright buffers; portrait is an iOS screen convention.
85
+ + guard orientation == .up else { throw RuntimeError.error(withMessage: "Custom capture orientation is not supported by the macOS prototype") }
86
+ +#else
87
+ guard self.isVideoOrientationSupported else {
88
+ throw RuntimeError.error(
89
+ withMessage:
90
+ @@ -21,5 +29,6 @@ extension AVCaptureConnection {
91
+ )
92
+ }
93
+ self.videoOrientation = orientation.toAVCaptureVideoOrientation()
94
+ +#endif
95
+ }
96
+ }
97
+ diff --git a/ios/Extensions/AVFoundation/AVCaptureDevice.DeviceType+all.swift b/ios/Extensions/AVFoundation/AVCaptureDevice.DeviceType+all.swift
98
+ index 1f5adb8..adbfd55 100644
99
+ --- a/ios/Extensions/AVFoundation/AVCaptureDevice.DeviceType+all.swift
100
+ +++ b/ios/Extensions/AVFoundation/AVCaptureDevice.DeviceType+all.swift
101
+ @@ -14,6 +14,9 @@ extension AVCaptureDevice.DeviceType {
102
+ * This list should be kept up to date by just adding new device types here.
103
+ */
104
+ static var all: [AVCaptureDevice.DeviceType] {
105
+ +#if os(macOS)
106
+ + return [.builtInWideAngleCamera, .external, .continuityCamera, .deskViewCamera]
107
+ +#else
108
+ var devices: [AVCaptureDevice.DeviceType] = []
109
+
110
+ devices.append(.builtInWideAngleCamera)
111
+ @@ -38,5 +41,6 @@ extension AVCaptureDevice.DeviceType {
112
+ devices.append(.builtInTrueDepthCamera)
113
+
114
+ return devices
115
+ +#endif
116
+ }
117
+ }
118
+ diff --git a/ios/Extensions/AVFoundation/AVCaptureDevice.Format+supportedResolutionsFor.swift b/ios/Extensions/AVFoundation/AVCaptureDevice.Format+supportedResolutionsFor.swift
119
+ index 7adc7fd..13fdbde 100644
120
+ --- a/ios/Extensions/AVFoundation/AVCaptureDevice.Format+supportedResolutionsFor.swift
121
+ +++ b/ios/Extensions/AVFoundation/AVCaptureDevice.Format+supportedResolutionsFor.swift
122
+ @@ -13,11 +13,16 @@ extension AVCaptureDevice.Format {
123
+ return [self.formatDescription.dimensions]
124
+ }
125
+ private var photoDimensions: [CMVideoDimensions] {
126
+ +#if os(macOS)
127
+ + let dimensions = self.supportedMaxPhotoDimensions
128
+ + return dimensions.isEmpty ? self.videoDimensions : dimensions
129
+ +#else
130
+ if #available(iOS 16.0, *) {
131
+ return self.supportedMaxPhotoDimensions
132
+ } else {
133
+ return [self.highResolutionStillImageDimensions]
134
+ }
135
+ +#endif
136
+ }
137
+
138
+ /**
139
+ @@ -29,6 +34,9 @@ extension AVCaptureDevice.Format {
140
+ return self.videoDimensions
141
+ case .photo:
142
+ return self.photoDimensions
143
+ +#if os(macOS)
144
+ + case .depthVideo, .depthPhoto: return []
145
+ +#else
146
+ case .depthVideo:
147
+ return self.supportedDepthDataFormats
148
+ .flatMap { $0.videoDimensions }
149
+ @@ -37,6 +45,7 @@ extension AVCaptureDevice.Format {
150
+ return self.supportedDepthDataFormats
151
+ .flatMap { $0.photoDimensions }
152
+ .withoutDuplicates()
153
+ +#endif
154
+ }
155
+ }
156
+ }
157
+ diff --git a/ios/Extensions/AVFoundation/AVCaptureDevice.Format+supportsOutput.swift b/ios/Extensions/AVFoundation/AVCaptureDevice.Format+supportsOutput.swift
158
+ index 5a496ce..cd71719 100644
159
+ --- a/ios/Extensions/AVFoundation/AVCaptureDevice.Format+supportsOutput.swift
160
+ +++ b/ios/Extensions/AVFoundation/AVCaptureDevice.Format+supportsOutput.swift
161
+ @@ -9,7 +9,11 @@ import Foundation
162
+
163
+ extension AVCaptureDevice.Format {
164
+ func supportsOutput(_ output: AVCaptureOutput) -> Bool {
165
+ +#if os(macOS)
166
+ + return output is AVCapturePhotoOutput || output is AVCaptureMovieFileOutput || output is AVCaptureVideoDataOutput
167
+ +#else
168
+ let targetOutputClass = type(of: output)
169
+ return self.unsupportedCaptureOutputClasses.allSatisfy { $0 != targetOutputClass }
170
+ +#endif
171
+ }
172
+ }
173
+ diff --git a/ios/Extensions/AVFoundation/AVCapturePhoto+getMediaSampleMetadata.swift b/ios/Extensions/AVFoundation/AVCapturePhoto+getMediaSampleMetadata.swift
174
+ index 066ae6d..f5140d5 100644
175
+ --- a/ios/Extensions/AVFoundation/AVCapturePhoto+getMediaSampleMetadata.swift
176
+ +++ b/ios/Extensions/AVFoundation/AVCapturePhoto+getMediaSampleMetadata.swift
177
+ @@ -6,10 +6,20 @@
178
+
179
+ import AVFoundation
180
+ import Foundation
181
+ +import ImageIO
182
+ import NitroModules
183
+
184
+ extension AVCapturePhoto {
185
+ func getMediaSampleMetadata() throws -> MediaSampleMetadata {
186
+ +#if os(macOS)
187
+ + var exif: UInt32 = 1
188
+ + if let data = fileDataRepresentation(), let source = CGImageSourceCreateWithData(data as CFData, nil), let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any] {
189
+ + exif = (properties[kCGImagePropertyOrientation] as? NSNumber)?.uint32Value ?? 1
190
+ + }
191
+ + let orientation: CameraOrientation
192
+ + switch exif { case 3, 4: orientation = .down; case 5, 8: orientation = .left; case 6, 7: orientation = .right; default: orientation = .up }
193
+ + return MediaSampleMetadata(timestamp: timestamp, orientation: orientation, isMirrored: [2, 4, 5, 7].contains(exif))
194
+ +#else
195
+ guard let orientationAny = self.metadata[kCGImagePropertyOrientation as String],
196
+ let exifOrientation = orientationAny as? UInt32
197
+ else {
198
+ @@ -24,5 +34,6 @@ extension AVCapturePhoto {
199
+ timestamp: timestamp,
200
+ orientation: orientation,
201
+ isMirrored: isMirrored)
202
+ +#endif
203
+ }
204
+ }
205
+ diff --git a/ios/Extensions/AVFoundation/AVCapturePhoto+memorySize.swift b/ios/Extensions/AVFoundation/AVCapturePhoto+memorySize.swift
206
+ index d1f8ea7..8e366c1 100644
207
+ --- a/ios/Extensions/AVFoundation/AVCapturePhoto+memorySize.swift
208
+ +++ b/ios/Extensions/AVFoundation/AVCapturePhoto+memorySize.swift
209
+ @@ -14,7 +14,7 @@ extension AVCapturePhoto {
210
+
211
+ // base native buffer (width * height * RGB)
212
+ let width = resolvedSettings.photoDimensions.width
213
+ - let height = resolvedSettings.photoDimensions.width
214
+ + let height = resolvedSettings.photoDimensions.height
215
+ let bytesPerPixel = 3 // RGB
216
+ estimate += Int(width) * Int(height) * bytesPerPixel
217
+
218
+ @@ -23,6 +23,7 @@ extension AVCapturePhoto {
219
+ estimate += pixelBuffer.memorySize
220
+ }
221
+
222
+ +#if !os(macOS)
223
+ // raw preview buffer
224
+ if let previewPixelBuffer {
225
+ estimate += previewPixelBuffer.memorySize
226
+ @@ -32,6 +33,7 @@ extension AVCapturePhoto {
227
+ if let depthData {
228
+ estimate += depthData.depthDataMap.memorySize
229
+ }
230
+ +#endif
231
+ return estimate
232
+ }
233
+ }
234
+ diff --git a/ios/Extensions/AVFoundation/AVDepthData+toUIImage.swift b/ios/Extensions/AVFoundation/AVDepthData+toUIImage.swift
235
+ index 6f48cd8..0be886f 100644
236
+ --- a/ios/Extensions/AVFoundation/AVDepthData+toUIImage.swift
237
+ +++ b/ios/Extensions/AVFoundation/AVDepthData+toUIImage.swift
238
+ @@ -7,10 +7,13 @@
239
+ import AVFoundation
240
+ import CoreImage
241
+ import Foundation
242
+ +#if os(macOS)
243
+ +import ImageIO
244
+ +#endif
245
+ import NitroModules
246
+
247
+ extension AVDepthData {
248
+ - func toUIImage(orientation: UIImage.Orientation) throws -> UIImage {
249
+ + func toUIImage(orientation: CameraImageOrientation) throws -> CameraPlatformImage {
250
+ // No-copy create CIImage from AVDepthData
251
+ guard let ciImage = CIImage(depthData: self) else {
252
+ throw RuntimeError.error(withMessage: "Failed to convert Depth to Image!")
253
+ diff --git a/ios/Extensions/CIImage+toUIImage.swift b/ios/Extensions/CIImage+toUIImage.swift
254
+ index 22ee164..3277a1b 100644
255
+ --- a/ios/Extensions/CIImage+toUIImage.swift
256
+ +++ b/ios/Extensions/CIImage+toUIImage.swift
257
+ @@ -7,12 +7,15 @@
258
+ import AVFoundation
259
+ import CoreImage
260
+ import Foundation
261
+ +#if os(macOS)
262
+ +import ImageIO
263
+ +#endif
264
+ import NitroModules
265
+
266
+ extension CIImage {
267
+ private static let context = CIContext(options: [.useSoftwareRenderer: false])
268
+
269
+ - func toUIImage(orientation: UIImage.Orientation) throws -> UIImage {
270
+ + func toUIImage(orientation: CameraImageOrientation) throws -> CameraPlatformImage {
271
+ // Copy the CIImage into a CGImage (render)
272
+ guard
273
+ let cgImage = Self.context.createCGImage(
274
+ @@ -21,10 +24,16 @@ extension CIImage {
275
+ else {
276
+ throw RuntimeError.error(withMessage: "Failed to copy CIImage into CGImage!")
277
+ }
278
+ + #if os(macOS)
279
+ + let oriented = CIImage(cgImage: cgImage).oriented(orientation)
280
+ + guard let result = Self.context.createCGImage(oriented, from: oriented.extent) else { throw RuntimeError.error(withMessage: "Failed to orient image") }
281
+ + return result
282
+ + #else
283
+ // No-copy wrap the CGImage in a UIImage
284
+ return UIImage(
285
+ cgImage: cgImage,
286
+ scale: 1,
287
+ orientation: orientation)
288
+ + #endif
289
+ }
290
+ }
291
+ diff --git a/ios/Extensions/Converters/AV+CapturePhotoSettings.swift b/ios/Extensions/Converters/AV+CapturePhotoSettings.swift
292
+ index e9490e1..0e89e79 100644
293
+ --- a/ios/Extensions/Converters/AV+CapturePhotoSettings.swift
294
+ +++ b/ios/Extensions/Converters/AV+CapturePhotoSettings.swift
295
+ @@ -10,10 +10,18 @@ import NitroModules
296
+
297
+ extension AVCapturePhotoSettings {
298
+ private var isRAWFormat: Bool {
299
+ +#if os(macOS)
300
+ + return false
301
+ +#else
302
+ return self.rawPhotoPixelFormatType != 0
303
+ +#endif
304
+ }
305
+ private var isAppleProRAWFormat: Bool {
306
+ +#if os(macOS)
307
+ + return false
308
+ +#else
309
+ return AVCapturePhotoOutput.isAppleProRAWPixelFormat(self.rawPhotoPixelFormatType)
310
+ +#endif
311
+ }
312
+
313
+ var supportsQualityPrioritization: Bool {
314
+ @@ -37,6 +45,9 @@ extension AVCapturePhotoSettings {
315
+
316
+ extension AVCapturePhotoOutput {
317
+ var bestRawPixelFormat: OSType? {
318
+ +#if os(macOS)
319
+ + return self.availableRawPhotoPixelFormatTypes.first
320
+ +#else
321
+ if self.isAppleProRAWEnabled {
322
+ if let appleProRawFormat = self.availableRawPhotoPixelFormatTypes.first(where: {
323
+ AVCapturePhotoOutput.isAppleProRAWPixelFormat($0)
324
+ @@ -47,6 +58,7 @@ extension AVCapturePhotoOutput {
325
+ }
326
+ // No Apple ProRAW enabled or available, just use the first available default RAW format (or nil if there is none)
327
+ return self.availableRawPhotoPixelFormatTypes.first
328
+ +#endif
329
+ }
330
+ }
331
+
332
+ @@ -78,6 +90,9 @@ extension CapturePhotoSettings {
333
+ ],
334
+ ])
335
+ case .dng:
336
+ +#if os(macOS)
337
+ + throw RuntimeError.error(withMessage: "RAW photo capture is not supported on macOS")
338
+ +#else
339
+ // For RAW, we need to get the first available RAW format (often Bayer14)
340
+ guard let rawFormat = output.bestRawPixelFormat else {
341
+ throw RuntimeError.error(
342
+ @@ -86,9 +101,14 @@ extension CapturePhotoSettings {
343
+ )
344
+ }
345
+ return AVCapturePhotoSettings(rawPixelFormatType: rawFormat)
346
+ +#endif
347
+ case .native:
348
+ +#if os(macOS)
349
+ + return try getBaseAVCapturePhotoSettings(for: output, in: .jpeg, quality: quality)
350
+ +#else
351
+ // Apple's default processed photo format is HEIC.
352
+ return try getBaseAVCapturePhotoSettings(for: output, in: .heic, quality: quality)
353
+ +#endif
354
+ }
355
+ }
356
+
357
+ @@ -109,7 +129,9 @@ extension CapturePhotoSettings {
358
+
359
+ // Shoot in max resolution
360
+ if #available(iOS 16.0, *) {
361
+ - settings.maxPhotoDimensions = output.maxPhotoDimensions
362
+ + if output.maxPhotoDimensions.width > 0 && output.maxPhotoDimensions.height > 0 {
363
+ + settings.maxPhotoDimensions = output.maxPhotoDimensions
364
+ + }
365
+ }
366
+
367
+ // photoQualityPrioritization can only be set on processed photos.
368
+ @@ -125,6 +147,11 @@ extension CapturePhotoSettings {
369
+ settings.flashMode = avFlashMode
370
+ }
371
+ }
372
+ +#if os(macOS)
373
+ + if enableDepthData == true || enableCameraCalibrationDataDelivery == true || options.previewImageTargetSize != nil {
374
+ + throw RuntimeError.error(withMessage: "Depth, calibration and preview thumbnails are not supported on macOS")
375
+ + }
376
+ +#else
377
+ // enableShutterSound={...}
378
+ if let enableShutterSound {
379
+ if #available(iOS 18.0, *) {
380
+ @@ -188,6 +215,10 @@ extension CapturePhotoSettings {
381
+ ]
382
+ }
383
+ }
384
+ +#endif
385
+ +#if os(macOS)
386
+ + if location != nil { throw RuntimeError.error(withMessage: "Photo location metadata is not supported on macOS") }
387
+ +#else
388
+ // metadata
389
+ settings.addLibraryMetadataTag()
390
+ if let location {
391
+ @@ -197,10 +228,12 @@ extension CapturePhotoSettings {
392
+ settings.addLocationMetadataTag(location)
393
+ }
394
+
395
+ +#endif
396
+ return settings
397
+ }
398
+ }
399
+
400
+ +#if !os(macOS)
401
+ extension AVCapturePhotoSettings {
402
+ fileprivate func addLibraryMetadataTag() {
403
+ var metadataCopy = metadata
404
+ @@ -216,3 +249,5 @@ extension AVCapturePhotoSettings {
405
+ self.metadata = metadataCopy
406
+ }
407
+ }
408
+ +
409
+ +#endif
410
+ diff --git a/ios/Extensions/Converters/AV+DeviceType.swift b/ios/Extensions/Converters/AV+DeviceType.swift
411
+ index 79b869c..77c87f3 100644
412
+ --- a/ios/Extensions/Converters/AV+DeviceType.swift
413
+ +++ b/ios/Extensions/Converters/AV+DeviceType.swift
414
+ @@ -10,6 +10,14 @@ import NitroModules
415
+
416
+ extension DeviceType {
417
+ init(type: AVCaptureDevice.DeviceType) {
418
+ +#if os(macOS)
419
+ + switch type {
420
+ + case .builtInWideAngleCamera: self = .wideAngle
421
+ + case .continuityCamera: self = .continuity
422
+ + case .external, .deskViewCamera: self = .external
423
+ + default: self = .unknown
424
+ + }
425
+ +#else
426
+ switch type {
427
+ case .builtInWideAngleCamera:
428
+ self = .wideAngle
429
+ @@ -45,5 +53,6 @@ extension DeviceType {
430
+ logger.error("VisionCamera: Unknown DeviceType: \(type.rawValue)!")
431
+ self = .unknown
432
+ }
433
+ +#endif
434
+ }
435
+ }
436
+ diff --git a/ios/Extensions/Converters/AV+InterruptionReason.swift b/ios/Extensions/Converters/AV+InterruptionReason.swift
437
+ index 7f1d93b..d94dd83 100644
438
+ --- a/ios/Extensions/Converters/AV+InterruptionReason.swift
439
+ +++ b/ios/Extensions/Converters/AV+InterruptionReason.swift
440
+ @@ -1,3 +1,4 @@
441
+ +#if !os(macOS)
442
+ ///
443
+ /// AV+InterruptionReason.swift
444
+ /// VisionCamera
445
+ @@ -28,3 +29,5 @@ extension InterruptionReason {
446
+ }
447
+ }
448
+ }
449
+ +
450
+ +#endif
451
+ diff --git a/ios/Extensions/Converters/AV+MediaType.swift b/ios/Extensions/Converters/AV+MediaType.swift
452
+ index 2321198..88ac77b 100644
453
+ --- a/ios/Extensions/Converters/AV+MediaType.swift
454
+ +++ b/ios/Extensions/Converters/AV+MediaType.swift
455
+ @@ -15,8 +15,10 @@ extension MediaType {
456
+ self = .video
457
+ case .depthData:
458
+ self = .depth
459
+ +#if !os(macOS)
460
+ case .metadataObject:
461
+ self = .metadata
462
+ +#endif
463
+ default:
464
+ self = .other
465
+ }
466
+ @@ -29,7 +31,11 @@ extension MediaType {
467
+ case .depth:
468
+ return .depthData
469
+ case .metadata:
470
+ +#if os(macOS)
471
+ + return AVMediaType(rawValue: "meta")
472
+ +#else
473
+ return .metadataObject
474
+ +#endif
475
+ case .other:
476
+ fatalError("Cannot convert 'other' to AVMediaType!")
477
+ }
478
+ diff --git a/ios/Extensions/Converters/AV+PhotoContainerFormat.swift b/ios/Extensions/Converters/AV+PhotoContainerFormat.swift
479
+ index d54d635..e9255ac 100644
480
+ --- a/ios/Extensions/Converters/AV+PhotoContainerFormat.swift
481
+ +++ b/ios/Extensions/Converters/AV+PhotoContainerFormat.swift
482
+ @@ -20,7 +20,7 @@ extension PhotoContainerFormat {
483
+ case .tif:
484
+ self = .tiff
485
+ default:
486
+ - if #available(iOS 26.0, *) {
487
+ + if #available(iOS 26.0, macOS 26.0, *) {
488
+ if avFileType == .dcm {
489
+ self = .dcm
490
+ return
491
+ @@ -62,7 +62,7 @@ extension PhotoContainerFormat {
492
+ }
493
+ return type
494
+ case .dng:
495
+ - guard #available(iOS 18.0, *) else {
496
+ + guard #available(iOS 18.0, macOS 15.0, *) else {
497
+ throw RuntimeError.error(
498
+ withMessage: "PhotoContainerFormat \"dng\" is only available on iOS 18.0 or newer!")
499
+ }
500
+ diff --git a/ios/Extensions/Converters/AV+ScannedObjectType.swift b/ios/Extensions/Converters/AV+ScannedObjectType.swift
501
+ index ce76ac2..d3f34f3 100644
502
+ --- a/ios/Extensions/Converters/AV+ScannedObjectType.swift
503
+ +++ b/ios/Extensions/Converters/AV+ScannedObjectType.swift
504
+ @@ -62,9 +62,9 @@ extension ScannedObjectType {
505
+ self = .microQr
506
+ } else if #available(iOS 17.0, *), type == .humanFullBody {
507
+ self = .humanFullBody
508
+ - } else if #available(iOS 26.0, *), type == .dogHead {
509
+ + } else if #available(iOS 26.0, macOS 26.0, *), type == .dogHead {
510
+ self = .dogHead
511
+ - } else if #available(iOS 26.0, *), type == .catHead {
512
+ + } else if #available(iOS 26.0, macOS 26.0, *), type == .catHead {
513
+ self = .catHead
514
+ } else {
515
+ logger.error("Unknown AVMetadataObject.ObjectType: \(type.rawValue)")
516
+ @@ -146,7 +146,7 @@ extension ScannedObjectType {
517
+ return nil
518
+ }
519
+ case .dogHead:
520
+ - if #available(iOS 26.0, *) {
521
+ + if #available(iOS 26.0, macOS 26.0, *) {
522
+ return .dogHead
523
+ } else {
524
+ return nil
525
+ @@ -154,7 +154,7 @@ extension ScannedObjectType {
526
+ case .dogBody:
527
+ return .dogBody
528
+ case .catHead:
529
+ - if #available(iOS 26.0, *) {
530
+ + if #available(iOS 26.0, macOS 26.0, *) {
531
+ return .catHead
532
+ } else {
533
+ return nil
534
+ diff --git a/ios/Extensions/Converters/AV+TargetColorSpace.swift b/ios/Extensions/Converters/AV+TargetColorSpace.swift
535
+ index 10762ca..f585051 100644
536
+ --- a/ios/Extensions/Converters/AV+TargetColorSpace.swift
537
+ +++ b/ios/Extensions/Converters/AV+TargetColorSpace.swift
538
+ @@ -10,6 +10,12 @@ import NitroModules
539
+
540
+ extension TargetColorSpace {
541
+ func toAVCaptureColorSpace() -> AVCaptureColorSpace {
542
+ +#if os(macOS)
543
+ + switch self {
544
+ + case .p3D65: return .P3_D65
545
+ + default: return .sRGB
546
+ + }
547
+ +#else
548
+ switch self {
549
+ case .srgb:
550
+ return .sRGB
551
+ @@ -35,5 +41,6 @@ extension TargetColorSpace {
552
+ // Dolby Vision is not supported on iOS
553
+ return .HLG_BT2020
554
+ }
555
+ +#endif
556
+ }
557
+ }
558
+ diff --git a/ios/Extensions/Converters/AV+TargetStabilizationMode.swift b/ios/Extensions/Converters/AV+TargetStabilizationMode.swift
559
+ index 6809612..0330e72 100644
560
+ --- a/ios/Extensions/Converters/AV+TargetStabilizationMode.swift
561
+ +++ b/ios/Extensions/Converters/AV+TargetStabilizationMode.swift
562
+ @@ -1,3 +1,4 @@
563
+ +#if !os(macOS)
564
+ ///
565
+ /// AV+TargetStabilizationMode.swift
566
+ /// VisionCamera
567
+ @@ -42,3 +43,5 @@ extension TargetStabilizationMode {
568
+ }
569
+ }
570
+ }
571
+ +
572
+ +#endif
573
+ diff --git a/ios/Extensions/Converters/AV+VideoCodec.swift b/ios/Extensions/Converters/AV+VideoCodec.swift
574
+ index 894bc6e..6e9d5f0 100644
575
+ --- a/ios/Extensions/Converters/AV+VideoCodec.swift
576
+ +++ b/ios/Extensions/Converters/AV+VideoCodec.swift
577
+ @@ -30,13 +30,13 @@ extension VideoCodec {
578
+ case .jpeg:
579
+ self = .jpeg
580
+ default:
581
+ - if #available(iOS 18.0, *) {
582
+ + if #available(iOS 18.0, macOS 15.0, *) {
583
+ if avCodec == .appleProRes4444XQ {
584
+ self = .proRes4444Xq
585
+ return
586
+ }
587
+ }
588
+ - if #available(iOS 26.0, *) {
589
+ + if #available(iOS 26.0, macOS 26.0, *) {
590
+ if avCodec == .proResRAW {
591
+ self = .proResRaw
592
+ return
593
+ @@ -84,19 +84,19 @@ extension VideoCodec {
594
+ case .jpeg:
595
+ return .jpeg
596
+ case .proRes4444Xq:
597
+ - guard #available(iOS 18.0, *) else {
598
+ + guard #available(iOS 18.0, macOS 15.0, *) else {
599
+ throw RuntimeError.error(
600
+ withMessage: "VideoCodec \"pro-res-4444-xq\" is only available on iOS 18.0 or higher!")
601
+ }
602
+ return .appleProRes4444XQ
603
+ case .proResRaw:
604
+ - guard #available(iOS 26.0, *) else {
605
+ + guard #available(iOS 26.0, macOS 26.0, *) else {
606
+ throw RuntimeError.error(
607
+ withMessage: "VideoCodec \"pro-res-raw\" is only available on iOS 18.0 or higher!")
608
+ }
609
+ return .proResRAW
610
+ case .proResRawHq:
611
+ - guard #available(iOS 26.0, *) else {
612
+ + guard #available(iOS 26.0, macOS 26.0, *) else {
613
+ throw RuntimeError.error(
614
+ withMessage: "VideoCodec \"pro-res-raw-hq\" is only available on iOS 18.0 or higher!")
615
+ }
616
+ diff --git a/ios/Extensions/Converters/UI+CameraOrientation.swift b/ios/Extensions/Converters/UI+CameraOrientation.swift
617
+ index 97757ed..2eb6bb8 100644
618
+ --- a/ios/Extensions/Converters/UI+CameraOrientation.swift
619
+ +++ b/ios/Extensions/Converters/UI+CameraOrientation.swift
620
+ @@ -1,3 +1,6 @@
621
+ +#if os(macOS)
622
+ +
623
+ +#else
624
+ ///
625
+ /// UI+CameraOrientation.swift
626
+ /// VisionCamera
627
+ @@ -53,3 +56,5 @@ extension CameraOrientation {
628
+ }
629
+ }
630
+ }
631
+ +
632
+ +#endif
633
+ diff --git a/ios/Extensions/CoreMedia/CMAccelerometerData+deviceOrientation.swift b/ios/Extensions/CoreMedia/CMAccelerometerData+deviceOrientation.swift
634
+ index 1eecf74..1989d0f 100644
635
+ --- a/ios/Extensions/CoreMedia/CMAccelerometerData+deviceOrientation.swift
636
+ +++ b/ios/Extensions/CoreMedia/CMAccelerometerData+deviceOrientation.swift
637
+ @@ -1,3 +1,4 @@
638
+ +#if !os(macOS)
639
+ //
640
+ // CMAccelerometerData+deviceOrientation.swift
641
+ // VisionCamera
642
+ @@ -38,3 +39,5 @@ extension CMAccelerometerData {
643
+ }
644
+ }
645
+ }
646
+ +
647
+ +#endif
648
+ diff --git a/ios/Extensions/CoreMedia/CMSampleBuffer+toUIImage.swift b/ios/Extensions/CoreMedia/CMSampleBuffer+toUIImage.swift
649
+ index 5fa9814..ef5dc43 100644
650
+ --- a/ios/Extensions/CoreMedia/CMSampleBuffer+toUIImage.swift
651
+ +++ b/ios/Extensions/CoreMedia/CMSampleBuffer+toUIImage.swift
652
+ @@ -7,10 +7,13 @@
653
+ import AVFoundation
654
+ import CoreImage
655
+ import Foundation
656
+ +#if os(macOS)
657
+ +import ImageIO
658
+ +#endif
659
+ import NitroModules
660
+
661
+ extension CMSampleBuffer {
662
+ - func toUIImage(orientation: UIImage.Orientation) throws -> UIImage {
663
+ + func toUIImage(orientation: CameraImageOrientation) throws -> CameraPlatformImage {
664
+ guard let imageBuffer else {
665
+ throw RuntimeError.error(withMessage: "This Frame does not have a PixelBuffer!")
666
+ }
667
+ diff --git a/ios/Extensions/UIApplication+interfaceOrientation.swift b/ios/Extensions/UIApplication+interfaceOrientation.swift
668
+ index 96464fa..396961a 100644
669
+ --- a/ios/Extensions/UIApplication+interfaceOrientation.swift
670
+ +++ b/ios/Extensions/UIApplication+interfaceOrientation.swift
671
+ @@ -1,3 +1,6 @@
672
+ +#if os(macOS)
673
+ +
674
+ +#else
675
+ //
676
+ // UIApplication+interfaceOrientation.swift
677
+ // VisionCamera
678
+ @@ -28,3 +31,5 @@ extension UIApplication {
679
+ return windowScene.interfaceOrientation
680
+ }
681
+ }
682
+ +
683
+ +#endif
684
+ diff --git a/ios/Extensions/UIImageOrientation+exif.swift b/ios/Extensions/UIImageOrientation+exif.swift
685
+ index 53506e0..98a404f 100644
686
+ --- a/ios/Extensions/UIImageOrientation+exif.swift
687
+ +++ b/ios/Extensions/UIImageOrientation+exif.swift
688
+ @@ -1,3 +1,4 @@
689
+ +#if !os(macOS)
690
+ ///
691
+ /// CIImage+toUIImage.swift
692
+ /// VisionCamera
693
+ @@ -44,3 +45,5 @@ extension UIImage.Orientation {
694
+ }
695
+ }
696
+ }
697
+ +
698
+ +#endif
699
+ diff --git a/ios/Hybrid Objects/Constraints/ConstraintResolver.swift b/ios/Hybrid Objects/Constraints/ConstraintResolver.swift
700
+ index 22a5793..dd37721 100644
701
+ --- a/ios/Hybrid Objects/Constraints/ConstraintResolver.swift
702
+ +++ b/ios/Hybrid Objects/Constraints/ConstraintResolver.swift
703
+ @@ -103,7 +103,11 @@ enum ConstraintResolver {
704
+ // If we need depth data, also find the best depth format
705
+ let needsDepth = outputs.contains { $0.requiresDepthFormat }
706
+ // TODO: Don't just choose the first format, but also negotiate depth data format in our constraint resolution.
707
+ +#if os(macOS)
708
+ + let bestDepthFormat: AVCaptureDevice.Format? = nil
709
+ +#else
710
+ let bestDepthFormat = needsDepth ? best.format.supportedDepthDataFormats.first : nil
711
+ +#endif
712
+
713
+ return ResolvedConstraints(
714
+ device: device,
715
+ @@ -174,6 +178,11 @@ enum ConstraintResolver {
716
+ throw RuntimeError.error(withMessage: "The given `device` does not have any `formats`!")
717
+ }
718
+
719
+ +#if os(macOS)
720
+ + if isMultiCam || outputs.contains(where: { $0.requiresDepthFormat }) {
721
+ + throw RuntimeError.error(withMessage: "Depth and multi-camera sessions are not supported on macOS")
722
+ + }
723
+ +#else
724
+ // 1. If we need a Depth Output, filter out formats without depth support
725
+ let needsDepth = outputs.contains { $0.requiresDepthFormat }
726
+ if needsDepth {
727
+ @@ -197,6 +206,7 @@ enum ConstraintResolver {
728
+ }
729
+ }
730
+
731
+ +#endif
732
+ // 3. Ensure formats support all target outputs
733
+ formats = formats.filter { format in
734
+ outputs.allSatisfy { format.supportsOutput($0.output) }
735
+ @@ -277,7 +287,11 @@ extension InternalConstraint {
736
+ let r = afSystem.resolve(for: format)
737
+ return ConstraintEvaluation(penalty: r.penalty, resolved: .formatOnly)
738
+ case .preferHighestPhotoQuality:
739
+ +#if os(macOS)
740
+ + let penalty = 0.0
741
+ +#else
742
+ let penalty = format.isHighestPhotoQualitySupported ? 0.0 : 1.0
743
+ +#endif
744
+ return ConstraintEvaluation(
745
+ penalty: ConstraintPenalty(distance: penalty),
746
+ resolved: .formatOnly)
747
+ diff --git a/ios/Hybrid Objects/Constraints/HybridCameraSessionConfig.swift b/ios/Hybrid Objects/Constraints/HybridCameraSessionConfig.swift
748
+ index 0ae6b5c..8a7863e 100644
749
+ --- a/ios/Hybrid Objects/Constraints/HybridCameraSessionConfig.swift
750
+ +++ b/ios/Hybrid Objects/Constraints/HybridCameraSessionConfig.swift
751
+ @@ -35,7 +35,11 @@ final class HybridCameraSessionConfig: HybridCameraSessionConfigSpec, NativeCame
752
+
753
+ var isPhotoHDREnabled: Bool {
754
+ // Highest Photo Quality implies Smart HDR, this is not directly controllable.
755
+ +#if os(macOS)
756
+ + return false
757
+ +#else
758
+ return negotiatedFormat.format.isHighestPhotoQualitySupported
759
+ +#endif
760
+ }
761
+
762
+ var nativePixelFormat: PixelFormat {
763
+ @@ -49,7 +53,11 @@ final class HybridCameraSessionConfig: HybridCameraSessionConfigSpec, NativeCame
764
+ }
765
+
766
+ var isBinned: Bool {
767
+ +#if os(macOS)
768
+ + return false
769
+ +#else
770
+ return negotiatedFormat.format.isVideoBinned
771
+ +#endif
772
+ }
773
+
774
+ func toString() -> String {
775
+ diff --git a/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+BinnedConstraint.swift b/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+BinnedConstraint.swift
776
+ index 22c0e23..4366a55 100644
777
+ --- a/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+BinnedConstraint.swift
778
+ +++ b/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+BinnedConstraint.swift
779
+ @@ -11,7 +11,12 @@ extension BinnedConstraint: ResolvableConstraint {
780
+
781
+ func resolve(for format: AVCaptureDevice.Format) -> ConstraintResolution<Void> {
782
+ let penalty: Double
783
+ - switch (format.isVideoBinned, self.binned) {
784
+ +#if os(macOS)
785
+ + let isBinned = false
786
+ +#else
787
+ + let isBinned = format.isVideoBinned
788
+ +#endif
789
+ + switch (isBinned, self.binned) {
790
+ case (true, true), (false, false):
791
+ penalty = 0.0
792
+ case (true, false):
793
+ diff --git a/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+StabilizationModeConstraint.swift b/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+StabilizationModeConstraint.swift
794
+ index 1494372..c1eaa3c 100644
795
+ --- a/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+StabilizationModeConstraint.swift
796
+ +++ b/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+StabilizationModeConstraint.swift
797
+ @@ -49,6 +49,9 @@ extension TargetStabilizationMode {
798
+ /// Walks the fallback chain until we find a mode the format supports.
799
+ /// Returns both the penalty (number of fallback steps) and the resolved mode.
800
+ func resolve(for format: AVCaptureDevice.Format) -> ConstraintResolution<TargetStabilizationMode> {
801
+ +#if os(macOS)
802
+ + return ConstraintResolution(penalty: ConstraintPenalty(distance: self == .off ? 0 : 1), resolvedValue: .off)
803
+ +#else
804
+ var penalty = 0
805
+ var currentMode = self
806
+ while currentMode != .off {
807
+ @@ -61,5 +64,6 @@ extension TargetStabilizationMode {
808
+ return ConstraintResolution(
809
+ penalty: ConstraintPenalty(distance: Double(penalty)),
810
+ resolvedValue: currentMode)
811
+ +#endif
812
+ }
813
+ }
814
+ diff --git a/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+VideoDynamicRangeConstraint.swift b/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+VideoDynamicRangeConstraint.swift
815
+ index f250bab..c8d4e0b 100644
816
+ --- a/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+VideoDynamicRangeConstraint.swift
817
+ +++ b/ios/Hybrid Objects/Constraints/ResolvableConstraint/ResolvableConstraint+VideoDynamicRangeConstraint.swift
818
+ @@ -69,7 +69,12 @@ extension TargetColorSpace {
819
+ var penalty = 0
820
+ var currentColorSpace = self
821
+ while currentColorSpace != .srgb {
822
+ - if format.supportedColorSpaces.contains(currentColorSpace.toAVCaptureColorSpace()) {
823
+ +#if os(macOS)
824
+ + let supported = (currentColorSpace == .srgb || currentColorSpace == .p3D65) && format.supportedColorSpaces.contains(currentColorSpace.toAVCaptureColorSpace())
825
+ +#else
826
+ + let supported = format.supportedColorSpaces.contains(currentColorSpace.toAVCaptureColorSpace())
827
+ +#endif
828
+ + if supported {
829
+ break
830
+ }
831
+ currentColorSpace = currentColorSpace.nextBestMode()
832
+ diff --git a/ios/Hybrid Objects/Gestures/HybridTapToFocusGestureController.swift b/ios/Hybrid Objects/Gestures/HybridTapToFocusGestureController.swift
833
+ index 79cdcfc..a1a8199 100644
834
+ --- a/ios/Hybrid Objects/Gestures/HybridTapToFocusGestureController.swift
835
+ +++ b/ios/Hybrid Objects/Gestures/HybridTapToFocusGestureController.swift
836
+ @@ -7,23 +7,34 @@
837
+
838
+ import AVFoundation
839
+ import NitroModules
840
+ +#if os(macOS)
841
+ +import AppKit
842
+ +typealias CameraTapGestureRecognizer = NSClickGestureRecognizer
843
+ +#else
844
+ import UIKit
845
+ +typealias CameraTapGestureRecognizer = UITapGestureRecognizer
846
+ +#endif
847
+
848
+ final class HybridTapToFocusGestureController: HybridTapToFocusGestureControllerSpec,
849
+ NativeGestureController
850
+ {
851
+ - let gestureRecognizer = UITapGestureRecognizer()
852
+ + let gestureRecognizer = CameraTapGestureRecognizer()
853
+ weak var controller: (any HybridCameraControllerSpec)? = nil
854
+
855
+ private weak var previewView: (any HybridPreviewViewSpec)? = nil
856
+
857
+ override init() {
858
+ super.init()
859
+ + #if os(macOS)
860
+ + gestureRecognizer.target = self
861
+ + gestureRecognizer.action = #selector(onTap(_:))
862
+ + #else
863
+ gestureRecognizer.cancelsTouchesInView = false
864
+ gestureRecognizer.addTarget(self, action: #selector(onTap(_:)))
865
+ + #endif
866
+ }
867
+
868
+ - @objc private func onTap(_ gestureRecognizer: UITapGestureRecognizer) {
869
+ + @objc private func onTap(_ gestureRecognizer: CameraTapGestureRecognizer) {
870
+ guard let controller, let previewView else {
871
+ return
872
+ }
873
+ diff --git a/ios/Hybrid Objects/Gestures/HybridZoomGestureController.swift b/ios/Hybrid Objects/Gestures/HybridZoomGestureController.swift
874
+ index e2f5ab5..c1a791c 100644
875
+ --- a/ios/Hybrid Objects/Gestures/HybridZoomGestureController.swift
876
+ +++ b/ios/Hybrid Objects/Gestures/HybridZoomGestureController.swift
877
+ @@ -1,3 +1,21 @@
878
+ +#if os(macOS)
879
+ +import AppKit
880
+ +import NitroModules
881
+ +final class HybridZoomGestureController: HybridZoomGestureControllerSpec, NativeGestureController {
882
+ + let gestureRecognizer = NSMagnificationGestureRecognizer()
883
+ + weak var controller: (any HybridCameraControllerSpec)?
884
+ + private var initialZoom = 1.0
885
+ + override init() { super.init(); gestureRecognizer.target = self; gestureRecognizer.action = #selector(onZoom(_:)) }
886
+ + @objc private func onZoom(_ gesture: NSMagnificationGestureRecognizer) {
887
+ + guard let controller else { return }
888
+ + if gesture.state == .began { initialZoom = controller.zoom }
889
+ + if gesture.state == .changed {
890
+ + let zoom = min(controller.maxZoom, max(controller.minZoom, initialZoom * (1 + gesture.magnification)))
891
+ + do { _ = try controller.setZoom(zoom: zoom) } catch { logger.error("Zoom failed: \(error)") }
892
+ + }
893
+ + }
894
+ +}
895
+ +#else
896
+ ///
897
+ /// HybridCameraPhotoOutput.swift
898
+ /// VisionCamera
899
+ @@ -51,3 +69,5 @@ final class HybridZoomGestureController: HybridZoomGestureControllerSpec, Native
900
+ }
901
+ }
902
+ }
903
+ +
904
+ +#endif
905
+ diff --git a/ios/Hybrid Objects/HybridCameraController.swift b/ios/Hybrid Objects/HybridCameraController.swift
906
+ index ff48fd9..2036a30 100644
907
+ --- a/ios/Hybrid Objects/HybridCameraController.swift
908
+ +++ b/ios/Hybrid Objects/HybridCameraController.swift
909
+ @@ -51,46 +51,82 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
910
+ }
911
+
912
+ var whiteBalanceGains: WhiteBalanceGains {
913
+ +#if os(macOS)
914
+ + return WhiteBalanceGains(redGain: 1, blueGain: 1, greenGain: 1)
915
+ +#else
916
+ let gains = captureDevice.deviceWhiteBalanceGains
917
+ return WhiteBalanceGains(
918
+ redGain: Double(gains.redGain),
919
+ blueGain: Double(gains.blueGain),
920
+ greenGain: Double(gains.greenGain))
921
+ +#endif
922
+ }
923
+
924
+ var isLowLightBoostEnabled: Bool {
925
+ +#if os(macOS)
926
+ + return false
927
+ +#else
928
+ return captureDevice.automaticallyEnablesLowLightBoostWhenAvailable
929
+ +#endif
930
+ }
931
+
932
+ var isSmoothAutoFocusEnabled: Bool {
933
+ +#if os(macOS)
934
+ + return false
935
+ +#else
936
+ return captureDevice.isSmoothAutoFocusEnabled
937
+ +#endif
938
+ }
939
+
940
+ var isDistortionCorrectionEnabled: Bool {
941
+ +#if os(macOS)
942
+ + return false
943
+ +#else
944
+ return captureDevice.isGeometricDistortionCorrectionEnabled
945
+ +#endif
946
+ }
947
+
948
+ var zoom: Double {
949
+ +#if os(macOS)
950
+ + return 1
951
+ +#else
952
+ return captureDevice.videoZoomFactor
953
+ +#endif
954
+ }
955
+
956
+ var minZoom: Double {
957
+ +#if os(macOS)
958
+ + return 1
959
+ +#else
960
+ return captureDevice.minAvailableVideoZoomFactor
961
+ +#endif
962
+ }
963
+
964
+ var maxZoom: Double {
965
+ +#if os(macOS)
966
+ + return 1
967
+ +#else
968
+ return captureDevice.maxAvailableVideoZoomFactor
969
+ +#endif
970
+ }
971
+
972
+ var displayableZoomFactor: Double {
973
+ +#if os(macOS)
974
+ + return 1
975
+ +#else
976
+ guard #available(iOS 18.0, *) else {
977
+ return zoom
978
+ }
979
+ return zoom * captureDevice.displayVideoZoomFactorMultiplier
980
+ +#endif
981
+ }
982
+
983
+ var torchStrength: Double {
984
+ +#if os(macOS)
985
+ + return 0
986
+ +#else
987
+ return Double(captureDevice.torchLevel)
988
+ +#endif
989
+ }
990
+
991
+ var torchMode: TorchMode {
992
+ @@ -98,26 +134,52 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
993
+ }
994
+
995
+ var lensPosition: Double {
996
+ +#if os(macOS)
997
+ + return 0
998
+ +#else
999
+ return Double(captureDevice.lensPosition)
1000
+ +#endif
1001
+ }
1002
+
1003
+ var exposureDuration: Double {
1004
+ +#if os(macOS)
1005
+ + return 0
1006
+ +#else
1007
+ return captureDevice.exposureDuration.seconds
1008
+ +#endif
1009
+ }
1010
+
1011
+ var activeMaxExposureDuration: Double {
1012
+ +#if os(macOS)
1013
+ + return 0
1014
+ +#else
1015
+ return captureDevice.activeMaxExposureDuration.seconds
1016
+ +#endif
1017
+ }
1018
+
1019
+ var exposureBias: Double {
1020
+ +#if os(macOS)
1021
+ + return 0
1022
+ +#else
1023
+ return Double(captureDevice.exposureTargetBias)
1024
+ +#endif
1025
+ }
1026
+
1027
+ var iso: Double {
1028
+ +#if os(macOS)
1029
+ + return 0
1030
+ +#else
1031
+ return Double(captureDevice.iso)
1032
+ +#endif
1033
+ }
1034
+
1035
+ func configure(config: CameraControllerConfiguration) -> Promise<Void> {
1036
+ +#if os(macOS)
1037
+ + if config.enableLowLightBoost != nil || config.enableSmoothAutoFocus != nil || config.enableDistortionCorrection != nil {
1038
+ + return .rejected(withError: RuntimeError.error(withMessage: "These controller options are not supported on macOS"))
1039
+ + }
1040
+ + return .resolved()
1041
+ +#else
1042
+ return captureDevice.withLock(queue) {
1043
+ let device = self.captureDevice
1044
+
1045
+ @@ -148,9 +210,13 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1046
+ device.isGeometricDistortionCorrectionEnabled = enableDistortionCorrection
1047
+ }
1048
+ }
1049
+ +#endif
1050
+ }
1051
+
1052
+ func setZoom(zoom: Double) -> Promise<Void> {
1053
+ +#if os(macOS)
1054
+ + return .rejected(withError: RuntimeError.error(withMessage: "setZoom is not supported on macOS"))
1055
+ +#else
1056
+ return captureDevice.withLock(queue) {
1057
+ // 1. Ensure zoom is within valid range
1058
+ let device = self.captureDevice
1059
+ @@ -166,6 +232,7 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1060
+ // 3. Apply zoom
1061
+ device.videoZoomFactor = clampedForFormat
1062
+ }
1063
+ +#endif
1064
+ }
1065
+
1066
+ func focusTo(
1067
+ @@ -268,10 +335,14 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1068
+ }
1069
+ }
1070
+
1071
+ - func addSubjectAreaChangedListener(onSubjectAreaChanged: @escaping () -> Void) -> ListenerSubscription {
1072
+ + func addSubjectAreaChangedListener(onSubjectAreaChanged: @escaping () -> Void) throws -> ListenerSubscription {
1073
+ +#if os(macOS)
1074
+ + throw RuntimeError.error(withMessage: "Subject-area monitoring is not supported on macOS")
1075
+ +#else
1076
+ return SubjectAreaMonitor.addObserver(
1077
+ device: captureDevice,
1078
+ onSubjectAreaDidChange: onSubjectAreaChanged)
1079
+ +#endif
1080
+ }
1081
+
1082
+ func setTorchMode(mode: TorchMode) -> Promise<Void> {
1083
+ @@ -299,6 +370,9 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1084
+ }
1085
+
1086
+ func enableTorchWithStrength(strength: Double) -> Promise<Void> {
1087
+ +#if os(macOS)
1088
+ + return .rejected(withError: RuntimeError.error(withMessage: "enableTorchWithStrength is not supported on macOS"))
1089
+ +#else
1090
+ return captureDevice.withLock(queue) {
1091
+ guard self.device.supportsTorchStrength else {
1092
+ throw RuntimeError.error(
1093
+ @@ -314,9 +388,13 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1094
+ }
1095
+ try self.captureDevice.setTorchModeOn(level: Float(strength))
1096
+ }
1097
+ +#endif
1098
+ }
1099
+
1100
+ func startZoomAnimation(zoom: Double, rate: Double) -> Promise<Void> {
1101
+ +#if os(macOS)
1102
+ + return .rejected(withError: RuntimeError.error(withMessage: "startZoomAnimation is not supported on macOS"))
1103
+ +#else
1104
+ return captureDevice.withLock(queue) { completion in
1105
+ if self.captureDevice.videoZoomFactor == zoom {
1106
+ // We are already exactly on our target value
1107
+ @@ -337,15 +415,23 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1108
+
1109
+ self.captureDevice.ramp(toVideoZoomFactor: zoom, withRate: Float(rate))
1110
+ }
1111
+ +#endif
1112
+ }
1113
+
1114
+ func cancelZoomAnimation() -> Promise<Void> {
1115
+ +#if os(macOS)
1116
+ + return .rejected(withError: RuntimeError.error(withMessage: "cancelZoomAnimation is not supported on macOS"))
1117
+ +#else
1118
+ return captureDevice.withLock(queue) {
1119
+ self.captureDevice.cancelVideoZoomRamp()
1120
+ }
1121
+ +#endif
1122
+ }
1123
+
1124
+ func setExposureBias(exposure: Double) -> Promise<Void> {
1125
+ +#if os(macOS)
1126
+ + return .rejected(withError: RuntimeError.error(withMessage: "setExposureBias is not supported on macOS"))
1127
+ +#else
1128
+ return captureDevice.withLock(queue) { completion in
1129
+ // 1. Ensure exposureBias is within range
1130
+ let device = self.captureDevice
1131
+ @@ -362,9 +448,13 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1132
+ completion()
1133
+ }
1134
+ }
1135
+ +#endif
1136
+ }
1137
+
1138
+ func setFocusLocked(lensPosition: Double) -> Promise<Void> {
1139
+ +#if os(macOS)
1140
+ + return .rejected(withError: RuntimeError.error(withMessage: "setFocusLocked is not supported on macOS"))
1141
+ +#else
1142
+ return captureDevice.withLock(queue) { completion in
1143
+ // 1. Ensure values are within valid range
1144
+ guard lensPosition >= 0 && lensPosition <= 1 else {
1145
+ @@ -378,34 +468,61 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1146
+ completion()
1147
+ }
1148
+ }
1149
+ +#endif
1150
+ }
1151
+
1152
+ func lockCurrentFocus() -> Promise<Void> {
1153
+ +#if os(macOS)
1154
+ + return captureDevice.withLock(queue) {
1155
+ + guard self.captureDevice.isFocusModeSupported(.locked) else { throw RuntimeError.error(withMessage: "lockCurrentFocus is unsupported by this camera") }
1156
+ + self.captureDevice.focusMode = .locked
1157
+ + }
1158
+ +#else
1159
+ return captureDevice.withLock(queue) { completion in
1160
+ self.captureDevice.setFocusModeLocked(lensPosition: AVCaptureDevice.currentLensPosition) {
1161
+ _ in
1162
+ completion()
1163
+ }
1164
+ }
1165
+ +#endif
1166
+ }
1167
+
1168
+ var minExposureDuration: Double {
1169
+ +#if os(macOS)
1170
+ + return 0
1171
+ +#else
1172
+ return captureDevice.activeFormat.minExposureDuration.seconds
1173
+ +#endif
1174
+ }
1175
+
1176
+ var maxExposureDuration: Double {
1177
+ +#if os(macOS)
1178
+ + return 0
1179
+ +#else
1180
+ return captureDevice.activeFormat.maxExposureDuration.seconds
1181
+ +#endif
1182
+ }
1183
+
1184
+ var minISO: Double {
1185
+ +#if os(macOS)
1186
+ + return 0
1187
+ +#else
1188
+ return Double(captureDevice.activeFormat.minISO)
1189
+ +#endif
1190
+ }
1191
+
1192
+ var maxISO: Double {
1193
+ +#if os(macOS)
1194
+ + return 0
1195
+ +#else
1196
+ return Double(captureDevice.activeFormat.maxISO)
1197
+ +#endif
1198
+ }
1199
+
1200
+ func setExposureLocked(duration: Double, iso: Double) -> Promise<Void> {
1201
+ +#if os(macOS)
1202
+ + return .rejected(withError: RuntimeError.error(withMessage: "setExposureLocked is not supported on macOS"))
1203
+ +#else
1204
+ return captureDevice.withLock(queue) { completion in
1205
+ let device = self.captureDevice
1206
+ // 1. Ensure values are within valid range
1207
+ @@ -433,9 +550,16 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1208
+ completion()
1209
+ }
1210
+ }
1211
+ +#endif
1212
+ }
1213
+
1214
+ func lockCurrentExposure() -> Promise<Void> {
1215
+ +#if os(macOS)
1216
+ + return captureDevice.withLock(queue) {
1217
+ + guard self.captureDevice.isExposureModeSupported(.locked) else { throw RuntimeError.error(withMessage: "lockCurrentExposure is unsupported by this camera") }
1218
+ + self.captureDevice.exposureMode = .locked
1219
+ + }
1220
+ +#else
1221
+ return captureDevice.withLock(queue) { completion in
1222
+ self.captureDevice.setExposureModeCustom(
1223
+ duration: AVCaptureDevice.currentExposureDuration,
1224
+ @@ -444,11 +568,15 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1225
+ completion()
1226
+ }
1227
+ }
1228
+ +#endif
1229
+ }
1230
+
1231
+ func convertWhiteBalanceTemperatureAndTintValues(
1232
+ whiteBalanceTemperatureAndTint: WhiteBalanceTemperatureAndTint
1233
+ ) throws -> WhiteBalanceGains {
1234
+ +#if os(macOS)
1235
+ + throw RuntimeError.error(withMessage: "Manual white balance gains are not supported on macOS")
1236
+ +#else
1237
+ // 1. Convert our type to the AVFoundation type
1238
+ let tintValues = AVCaptureDevice.WhiteBalanceTemperatureAndTintValues(
1239
+ temperature: Float(whiteBalanceTemperatureAndTint.temperature),
1240
+ @@ -460,9 +588,13 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1241
+ redGain: Double(gains.redGain),
1242
+ blueGain: Double(gains.blueGain),
1243
+ greenGain: Double(gains.greenGain))
1244
+ +#endif
1245
+ }
1246
+
1247
+ func setWhiteBalanceLocked(whiteBalanceGains: WhiteBalanceGains) -> Promise<Void> {
1248
+ +#if os(macOS)
1249
+ + return .rejected(withError: RuntimeError.error(withMessage: "setWhiteBalanceLocked is not supported on macOS"))
1250
+ +#else
1251
+ return captureDevice.withLock(queue) { completion in
1252
+ // 1. Ensure values are within valid range
1253
+ let device = self.captureDevice
1254
+ @@ -498,14 +630,22 @@ final class HybridCameraController: HybridCameraControllerSpec, NativeCameraCont
1255
+ completion()
1256
+ }
1257
+ }
1258
+ +#endif
1259
+ }
1260
+
1261
+ func lockCurrentWhiteBalance() -> Promise<Void> {
1262
+ +#if os(macOS)
1263
+ + return captureDevice.withLock(queue) {
1264
+ + guard self.captureDevice.isWhiteBalanceModeSupported(.locked) else { throw RuntimeError.error(withMessage: "lockCurrentWhiteBalance is unsupported by this camera") }
1265
+ + self.captureDevice.whiteBalanceMode = .locked
1266
+ + }
1267
+ +#else
1268
+ return captureDevice.withLock(queue) { completion in
1269
+ self.captureDevice.setWhiteBalanceModeLocked(with: AVCaptureDevice.currentWhiteBalanceGains) {
1270
+ _ in
1271
+ completion()
1272
+ }
1273
+ }
1274
+ +#endif
1275
+ }
1276
+ }
1277
+ diff --git a/ios/Hybrid Objects/HybridCameraDeviceFactory.swift b/ios/Hybrid Objects/HybridCameraDeviceFactory.swift
1278
+ index 013833c..9e8d1d0 100644
1279
+ --- a/ios/Hybrid Objects/HybridCameraDeviceFactory.swift
1280
+ +++ b/ios/Hybrid Objects/HybridCameraDeviceFactory.swift
1281
+ @@ -23,9 +23,13 @@ final class HybridCameraDeviceFactory: HybridCameraDeviceFactorySpec {
1282
+ }
1283
+
1284
+ var supportedMultiCamDeviceCombinations: [[any HybridCameraDeviceSpec]] {
1285
+ +#if os(macOS)
1286
+ + return []
1287
+ +#else
1288
+ return discoverySession.supportedMultiCamDeviceSets.map { devices in
1289
+ return devices.map { HybridCameraDevice(device: $0) }
1290
+ }
1291
+ +#endif
1292
+ }
1293
+
1294
+ var userPreferredCamera: (any HybridCameraDeviceSpec)? {
1295
+ @@ -53,7 +57,7 @@ final class HybridCameraDeviceFactory: HybridCameraDeviceFactorySpec {
1296
+ -> ListenerSubscription
1297
+ {
1298
+ // 1. Attach a listener using KVO
1299
+ - let observation = discoverySession.observe(\.devices) { _, change in
1300
+ + let observation = discoverySession.observe(\.devices, options: [.old, .new]) { _, change in
1301
+ guard let newDevices = change.newValue else { return }
1302
+ logger.info("Devices changed! \(change.oldValue?.count ?? 0) -> \(newDevices.count)")
1303
+ let devices = newDevices.map { HybridCameraDevice(device: $0) }
1304
+ diff --git a/ios/Hybrid Objects/HybridCameraFactory.swift b/ios/Hybrid Objects/HybridCameraFactory.swift
1305
+ index 3f35188..637fff3 100644
1306
+ --- a/ios/Hybrid Objects/HybridCameraFactory.swift
1307
+ +++ b/ios/Hybrid Objects/HybridCameraFactory.swift
1308
+ @@ -44,7 +44,11 @@ final class VisionCamera: HybridCameraFactorySpec {
1309
+ }
1310
+
1311
+ var supportsMultiCamSessions: Bool {
1312
+ +#if os(macOS)
1313
+ + return false
1314
+ +#else
1315
+ return AVCaptureMultiCamSession.isMultiCamSupported
1316
+ +#endif
1317
+ }
1318
+
1319
+ func createCameraSession(enableMultiCam: Bool) -> Promise<any HybridCameraSessionSpec> {
1320
+ @@ -71,7 +75,10 @@ final class VisionCamera: HybridCameraFactorySpec {
1321
+ return try HybridCameraPhotoOutput(options: options)
1322
+ }
1323
+
1324
+ - func createVideoOutput(options: VideoOutputOptions) -> any HybridCameraVideoOutputSpec {
1325
+ + func createVideoOutput(options: VideoOutputOptions) throws -> any HybridCameraVideoOutputSpec {
1326
+ +#if os(macOS)
1327
+ + if options.targetBitRate != nil { throw RuntimeError.error(withMessage: "Custom movie bit rate is not supported by the macOS prototype") }
1328
+ +#endif
1329
+ if options.enablePersistentRecorder == true {
1330
+ // On iOS, the `AVCaptureMovieFileOutput` does not allow flipping the Camera while
1331
+ // recording, which is a feature we call "enablePersistentRecorder".
1332
+ @@ -88,13 +95,21 @@ final class VisionCamera: HybridCameraFactorySpec {
1333
+ }
1334
+
1335
+ func createDepthFrameOutput(options: DepthFrameOutputOptions)
1336
+ - -> any HybridCameraDepthFrameOutputSpec
1337
+ + throws -> any HybridCameraDepthFrameOutputSpec
1338
+ {
1339
+ +#if os(macOS)
1340
+ + throw RuntimeError.error(withMessage: "createDepthFrameOutput is not supported on macOS")
1341
+ +#else
1342
+ return HybridCameraDepthFrameOutput(options: options)
1343
+ +#endif
1344
+ }
1345
+
1346
+ - func createObjectOutput(options: ObjectOutputOptions) -> any HybridCameraObjectOutputSpec {
1347
+ + func createObjectOutput(options: ObjectOutputOptions) throws -> any HybridCameraObjectOutputSpec {
1348
+ +#if os(macOS)
1349
+ + throw RuntimeError.error(withMessage: "createObjectOutput is not supported on macOS")
1350
+ +#else
1351
+ return HybridCameraObjectOutput(options: options)
1352
+ +#endif
1353
+ }
1354
+
1355
+ func createPreviewOutput() -> any HybridCameraPreviewOutputSpec {
1356
+ @@ -104,7 +119,11 @@ final class VisionCamera: HybridCameraFactorySpec {
1357
+ func createOutputSynchronizer(outputs: [any HybridCameraOutputSpec]) throws
1358
+ -> any HybridCameraOutputSynchronizerSpec
1359
+ {
1360
+ +#if os(macOS)
1361
+ + throw RuntimeError.error(withMessage: "createOutputSynchronizer is not supported on macOS")
1362
+ +#else
1363
+ return try HybridCameraOutputSynchronizer(outputs: outputs)
1364
+ +#endif
1365
+ }
1366
+
1367
+ func createZoomGestureController() -> any HybridZoomGestureControllerSpec {
1368
+ diff --git a/ios/Hybrid Objects/HybridCameraSession.swift b/ios/Hybrid Objects/HybridCameraSession.swift
1369
+ index c74356e..23574b7 100644
1370
+ --- a/ios/Hybrid Objects/HybridCameraSession.swift
1371
+ +++ b/ios/Hybrid Objects/HybridCameraSession.swift
1372
+ @@ -18,14 +18,20 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1373
+ target: nil)
1374
+
1375
+ init(enableMultiCam: Bool) {
1376
+ +#if os(macOS)
1377
+ + self.session = AVCaptureSession()
1378
+ +#else
1379
+ if enableMultiCam {
1380
+ self.session = AVCaptureMultiCamSession()
1381
+ } else {
1382
+ self.session = AVCaptureSession()
1383
+ }
1384
+ +#endif
1385
+ // We manage formats manually via `device.activeFormat`, so opt out of the
1386
+ // session's preset-based format selection upfront.
1387
+ +#if !os(macOS)
1388
+ self.session.sessionPreset = .inputPriority
1389
+ +#endif
1390
+ super.init()
1391
+ }
1392
+
1393
+ @@ -45,11 +51,15 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1394
+ // Ensure multi-cam is enabled if we have multiple inputs
1395
+ let isMultiCam = connections.count > 1
1396
+ if isMultiCam {
1397
+ +#if os(macOS)
1398
+ + throw RuntimeError.error(withMessage: "Multi-camera sessions are not supported on macOS")
1399
+ +#else
1400
+ guard self.session is AVCaptureMultiCamSession else {
1401
+ throw RuntimeError(
1402
+ "Cannot add multiple inputs (\(connections)) to a single-cam CameraSession! "
1403
+ + "Create your CameraSession as a multi-cam session (`enableMultiCamSupport = true`) to add multiple camera inputs.")
1404
+ }
1405
+ +#endif
1406
+ }
1407
+
1408
+ logger.info("Reconfiguring CameraSession with \(connections.count) connection(s)...")
1409
+ @@ -90,8 +100,11 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1410
+ return constraint.isType(VideoDynamicRangeConstraint.self)
1411
+ }
1412
+ }
1413
+ +#if !os(macOS)
1414
+ self.session.automaticallyConfiguresCaptureDeviceForWideColor = !hasCustomDynamicRangeConstraint
1415
+ +#endif
1416
+
1417
+ +#if !os(macOS)
1418
+ // Haptics and System Sounds Playback
1419
+ if let allowHapticsAndSystemSoundsPlayback = config?.allowHapticsAndSystemSoundsPlayback {
1420
+ let audioSession = AVAudioSession.sharedInstance()
1421
+ @@ -106,6 +119,7 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1422
+ }
1423
+ }
1424
+
1425
+ +#endif
1426
+ // Return CameraControllers per connection to adjust camera settings (focus, etc)
1427
+ return try connections.map { connection in
1428
+ return try HybridCameraController(device: connection.input, queue: Self.queue)
1429
+ @@ -146,6 +160,9 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1430
+ if device.activeFormat != resolved.negotiatedFormat.format {
1431
+ device.activeFormat = resolved.negotiatedFormat.format
1432
+ }
1433
+ +#if os(macOS)
1434
+ +
1435
+ +#else
1436
+ if let depthFormat = resolved.negotiatedFormat.depthFormat {
1437
+ // On devices that support depth, `device.activeDepthDataFormat`
1438
+ // cannot be set to `nil`, so we only set the depth format when
1439
+ @@ -156,6 +173,7 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1440
+ }
1441
+ }
1442
+
1443
+ +#endif
1444
+ // TODO: Do we want to do any sanity checks here? Or assume that the negotiated format is properly validated before being set here?
1445
+
1446
+ // TODO: We could add a `allowFpsThrottling` flag in the future to run at variable frame rate.
1447
+ @@ -165,6 +183,9 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1448
+ device.activeVideoMinFrameDuration = CMTime(value: 1, timescale: Int32(fps))
1449
+ }
1450
+
1451
+ +#if os(macOS)
1452
+ +
1453
+ +#else
1454
+ // Video Stabilization
1455
+ if let videoStabilizationMode = resolved.enabledConstraints.selectedVideoStabilizationMode {
1456
+ for connection in self.session.connections where !connection.isConnectedToPreview {
1457
+ @@ -180,10 +201,14 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1458
+ }
1459
+ }
1460
+
1461
+ +#endif
1462
+ // Dynamic Range
1463
+ if let dynamicRange = resolved.enabledConstraints.selectedVideoDynamicRange {
1464
+ device.activeColorSpace = dynamicRange.colorSpace.toAVCaptureColorSpace()
1465
+ // Bit-Depth + ColorRange is implicitly set via `activeFormat`'s pixel format
1466
+ +#if os(macOS)
1467
+ +
1468
+ +#else
1469
+ if device.activeFormat.isVideoHDRSupported {
1470
+ if dynamicRange.isHDR {
1471
+ // TODO: If we refactor `TargetDynamicRange` to an enum, `EDR` (which `isVideoHDREnabled` effectively is) could be a separate value.
1472
+ @@ -195,6 +220,7 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1473
+ device.isVideoHDREnabled = false
1474
+ }
1475
+ }
1476
+ +#endif
1477
+ }
1478
+ }
1479
+
1480
+ @@ -289,6 +315,9 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1481
+ if !containsInput {
1482
+ // 2.1. It doesn't exist yet - add it to the session..
1483
+ let deviceInput = try AVCaptureDeviceInput(device: device)
1484
+ + guard self.session.canAddInput(deviceInput) else {
1485
+ + throw RuntimeError.error(withMessage: "This camera input cannot be added to the capture session")
1486
+ + }
1487
+ self.session.addInputWithNoConnections(deviceInput)
1488
+ // 2.2. Configure the input with initial settings
1489
+ if connection.initialZoom != nil || connection.initialExposureBias != nil {
1490
+ @@ -391,6 +420,11 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1491
+ ) throws {
1492
+ try device.lockForConfiguration()
1493
+ defer { device.unlockForConfiguration() }
1494
+ +#if os(macOS)
1495
+ + if initialZoom != nil || initialExposureBias != nil {
1496
+ + throw RuntimeError.error(withMessage: "Initial zoom and exposure bias are not supported on macOS")
1497
+ + }
1498
+ +#else
1499
+ if let initialZoom = initialZoom {
1500
+ let clamped = max(
1501
+ min(initialZoom, device.maxAvailableVideoZoomFactor), device.minAvailableVideoZoomFactor)
1502
+ @@ -401,6 +435,7 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1503
+ min(Float(initialExposure), device.maxExposureTargetBias), device.minExposureTargetBias)
1504
+ device.setExposureTargetBias(clamped)
1505
+ }
1506
+ +#endif
1507
+ }
1508
+
1509
+ // pragma MARK: On Started/Stopped Listeners
1510
+ @@ -440,6 +475,9 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1511
+ ) throws -> ListenerSubscription {
1512
+ return self.session.addListener(for: AVCaptureSession.wasInterruptedNotification) {
1513
+ notification in
1514
+ +#if os(macOS)
1515
+ + onInterruptionStarted(.unknown)
1516
+ +#else
1517
+ if let reasonAny = notification.userInfo?[AVCaptureSessionInterruptionReasonKey],
1518
+ let reasonRawValue = reasonAny as? NSNumber,
1519
+ let avReason = AVCaptureSession.InterruptionReason(rawValue: reasonRawValue.intValue)
1520
+ @@ -449,6 +487,7 @@ final class HybridCameraSession: HybridCameraSessionSpec {
1521
+ } else {
1522
+ onInterruptionStarted(.unknown)
1523
+ }
1524
+ +#endif
1525
+ }
1526
+ }
1527
+
1528
+ diff --git a/ios/Hybrid Objects/Image Types/HybridPhoto.swift b/ios/Hybrid Objects/Image Types/HybridPhoto.swift
1529
+ index 03129f1..ff11de2 100644
1530
+ --- a/ios/Hybrid Objects/Image Types/HybridPhoto.swift
1531
+ +++ b/ios/Hybrid Objects/Image Types/HybridPhoto.swift
1532
+ @@ -7,6 +7,8 @@
1533
+
1534
+ import AVFoundation
1535
+ import Foundation
1536
+ +import CoreImage
1537
+ +import ImageIO
1538
+ import NitroImage
1539
+ import NitroModules
1540
+
1541
+ @@ -43,24 +45,36 @@ final class HybridPhoto: HybridPhotoSpec, NativePhoto {
1542
+ }
1543
+
1544
+ var isRawPhoto: Bool {
1545
+ +#if os(macOS)
1546
+ + return false
1547
+ +#else
1548
+ return photo.isRawPhoto
1549
+ +#endif
1550
+ }
1551
+
1552
+ var depth: (any HybridDepthSpec)? {
1553
+ +#if os(macOS)
1554
+ + return nil
1555
+ +#else
1556
+ guard let depthData = photo.depthData else {
1557
+ return nil
1558
+ }
1559
+ return HybridDepth(
1560
+ depthData: depthData,
1561
+ metadata: metadata)
1562
+ +#endif
1563
+ }
1564
+
1565
+ var size: CMVideoDimensions {
1566
+ +#if os(macOS)
1567
+ + return photo.resolvedSettings.photoDimensions
1568
+ +#else
1569
+ if isRawPhoto {
1570
+ return photo.resolvedSettings.rawPhotoDimensions
1571
+ } else {
1572
+ return photo.resolvedSettings.photoDimensions
1573
+ }
1574
+ +#endif
1575
+ }
1576
+ var width: Double {
1577
+ return Double(size.width)
1578
+ @@ -70,10 +84,14 @@ final class HybridPhoto: HybridPhotoSpec, NativePhoto {
1579
+ }
1580
+
1581
+ var calibrationData: (any HybridCameraCalibrationDataSpec)? {
1582
+ +#if os(macOS)
1583
+ + return nil
1584
+ +#else
1585
+ guard let calibrationData = photo.cameraCalibrationData else {
1586
+ return nil
1587
+ }
1588
+ return HybridCameraCalibrationData(calibrationData: calibrationData)
1589
+ +#endif
1590
+ }
1591
+
1592
+ // pragma MARK: Pixel Buffer access
1593
+ @@ -135,6 +153,14 @@ final class HybridPhoto: HybridPhotoSpec, NativePhoto {
1594
+ }
1595
+
1596
+ func toImage() throws -> any HybridImageSpec {
1597
+ +#if os(macOS)
1598
+ + guard !isRawPhoto, let data = photo.fileDataRepresentation(),
1599
+ + let source = CGImageSourceCreateWithData(data as CFData, nil),
1600
+ + let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, [kCGImageSourceCreateThumbnailFromImageAlways: true, kCGImageSourceCreateThumbnailWithTransform: true, kCGImageSourceThumbnailMaxPixelSize: max(width, height)] as CFDictionary) else {
1601
+ + throw RuntimeError.error(withMessage: "Could not decode captured photo")
1602
+ + }
1603
+ + return HybridCGImage(cgImage: cgImage)
1604
+ +#else
1605
+ guard let cgImage = photo.cgImageRepresentation() else {
1606
+ if isRawPhoto {
1607
+ throw RuntimeError.error(
1608
+ @@ -143,9 +169,15 @@ final class HybridPhoto: HybridPhotoSpec, NativePhoto {
1609
+ throw RuntimeError.error(withMessage: "Failed to get Image data!")
1610
+ }
1611
+ }
1612
+ + #if os(macOS)
1613
+ + let image = try CIImage(cgImage: cgImage).toUIImage(orientation: orientation.toImageOrientation(isMirrored: isMirrored))
1614
+ + return HybridCGImage(cgImage: image)
1615
+ + #else
1616
+ let uiOrientation = orientation.toUIImageOrientation(isMirrored: isMirrored)
1617
+ let uiImage = UIImage(cgImage: cgImage, scale: 1, orientation: uiOrientation)
1618
+ return HybridUIImage(uiImage: uiImage)
1619
+ + #endif
1620
+ +#endif
1621
+ }
1622
+
1623
+ func toImageAsync() throws -> Promise<any HybridImageSpec> {
1624
+ diff --git a/ios/Hybrid Objects/Image Types/HybridUIImage.swift b/ios/Hybrid Objects/Image Types/HybridUIImage.swift
1625
+ index bcc192a..69ed299 100644
1626
+ --- a/ios/Hybrid Objects/Image Types/HybridUIImage.swift
1627
+ +++ b/ios/Hybrid Objects/Image Types/HybridUIImage.swift
1628
+ @@ -1,3 +1,6 @@
1629
+ +#if os(macOS)
1630
+ +
1631
+ +#else
1632
+ //
1633
+ // HybridPhoto.swift
1634
+ // VisionCamera
1635
+ @@ -18,3 +21,5 @@ internal final class HybridUIImage: HybridImageSpec, NativeImage {
1636
+ super.init()
1637
+ }
1638
+ }
1639
+ +
1640
+ +#endif
1641
+ diff --git a/ios/Hybrid Objects/Inputs/HybridCameraDevice.swift b/ios/Hybrid Objects/Inputs/HybridCameraDevice.swift
1642
+ index bf7bafa..ecb567c 100644
1643
+ --- a/ios/Hybrid Objects/Inputs/HybridCameraDevice.swift
1644
+ +++ b/ios/Hybrid Objects/Inputs/HybridCameraDevice.swift
1645
+ @@ -17,7 +17,11 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1646
+
1647
+ lazy var supportedPixelFormats: [PixelFormat] = {
1648
+ let videoFormats = device.formats
1649
+ +#if os(macOS)
1650
+ + let depthFormats: [AVCaptureDevice.Format] = []
1651
+ +#else
1652
+ let depthFormats = device.formats.flatMap { $0.supportedDepthDataFormats }
1653
+ +#endif
1654
+ let allFormats = videoFormats + depthFormats
1655
+ return
1656
+ allFormats
1657
+ @@ -95,8 +99,12 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1658
+ }
1659
+
1660
+ func supportsVideoStabilizationMode(videoStabilizationMode: TargetStabilizationMode) throws -> Bool {
1661
+ +#if os(macOS)
1662
+ + return videoStabilizationMode == .off
1663
+ +#else
1664
+ let stabilizationMode = videoStabilizationMode.toAVCaptureVideoStabilizationMode()
1665
+ return device.formats.contains { $0.isVideoStabilizationModeSupported(stabilizationMode) }
1666
+ +#endif
1667
+ }
1668
+
1669
+ func supportsPreviewStabilizationMode(previewStabilizationMode: TargetStabilizationMode) throws -> Bool {
1670
+ @@ -134,22 +142,38 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1671
+ return CameraPosition(position: device.position)
1672
+ }
1673
+
1674
+ +#if os(macOS)
1675
+ + let physicalDevices: [any HybridCameraDeviceSpec] = []
1676
+ +#else
1677
+ lazy var physicalDevices: [any HybridCameraDeviceSpec] =
1678
+ device.constituentDevices.map { HybridCameraDevice(device: $0) }
1679
+ +#endif
1680
+
1681
+ var isVirtualDevice: Bool {
1682
+ +#if os(macOS)
1683
+ + return false
1684
+ +#else
1685
+ return device.isVirtualDevice
1686
+ +#endif
1687
+ }
1688
+
1689
+ var focalLength: Double? {
1690
+ +#if os(macOS)
1691
+ + return nil
1692
+ +#else
1693
+ guard #available(iOS 26.0, *) else {
1694
+ return nil
1695
+ }
1696
+ return Double(device.nominalFocalLengthIn35mmFilm)
1697
+ +#endif
1698
+ }
1699
+
1700
+ var lensAperture: Double {
1701
+ +#if os(macOS)
1702
+ + return 0
1703
+ +#else
1704
+ return Double(device.lensAperture)
1705
+ +#endif
1706
+ }
1707
+
1708
+ var isContinuityCamera: Bool {
1709
+ @@ -170,7 +194,11 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1710
+ }
1711
+
1712
+ var supportsSmoothAutoFocus: Bool {
1713
+ +#if os(macOS)
1714
+ + return false
1715
+ +#else
1716
+ return device.isSmoothAutoFocusSupported
1717
+ +#endif
1718
+ }
1719
+
1720
+ var supportsSpeedQualityPrioritization: Bool {
1721
+ @@ -179,13 +207,25 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1722
+ }
1723
+
1724
+ var supportsExposureBias: Bool {
1725
+ +#if os(macOS)
1726
+ + return false
1727
+ +#else
1728
+ return true
1729
+ +#endif
1730
+ }
1731
+ var minExposureBias: Double {
1732
+ +#if os(macOS)
1733
+ + return 0
1734
+ +#else
1735
+ return Double(device.minExposureTargetBias)
1736
+ +#endif
1737
+ }
1738
+ var maxExposureBias: Double {
1739
+ +#if os(macOS)
1740
+ + return 0
1741
+ +#else
1742
+ return Double(device.maxExposureTargetBias)
1743
+ +#endif
1744
+ }
1745
+
1746
+ var hasFlash: Bool {
1747
+ @@ -197,8 +237,12 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1748
+ }
1749
+
1750
+ var supportsTorchStrength: Bool {
1751
+ +#if os(macOS)
1752
+ + return false
1753
+ +#else
1754
+ // On iOS any device with a torch also supports custom torch level/strength
1755
+ return device.hasTorch
1756
+ +#endif
1757
+ }
1758
+
1759
+ var minTorchStrength: Double {
1760
+ @@ -210,27 +254,51 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1761
+ }
1762
+
1763
+ var supportsLowLightBoost: Bool {
1764
+ +#if os(macOS)
1765
+ + return false
1766
+ +#else
1767
+ return device.isLowLightBoostSupported
1768
+ +#endif
1769
+ }
1770
+
1771
+ var minZoom: Double {
1772
+ +#if os(macOS)
1773
+ + return 1
1774
+ +#else
1775
+ return device.minAvailableVideoZoomFactor
1776
+ +#endif
1777
+ }
1778
+
1779
+ var maxZoom: Double {
1780
+ +#if os(macOS)
1781
+ + return 1
1782
+ +#else
1783
+ return device.maxAvailableVideoZoomFactor
1784
+ +#endif
1785
+ }
1786
+
1787
+ var zoomLensSwitchFactors: [Double] {
1788
+ +#if os(macOS)
1789
+ + return []
1790
+ +#else
1791
+ return device.virtualDeviceSwitchOverVideoZoomFactors.map { $0.doubleValue }
1792
+ +#endif
1793
+ }
1794
+
1795
+ var supportsDistortionCorrection: Bool {
1796
+ +#if os(macOS)
1797
+ + return false
1798
+ +#else
1799
+ return device.isGeometricDistortionCorrectionSupported
1800
+ +#endif
1801
+ }
1802
+
1803
+ var supportsPreviewImage: Bool {
1804
+ +#if os(macOS)
1805
+ + return false
1806
+ +#else
1807
+ return true
1808
+ +#endif
1809
+ }
1810
+
1811
+ var supportsFocusMetering: Bool {
1812
+ @@ -239,7 +307,11 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1813
+ }
1814
+
1815
+ var supportsFocusLocking: Bool {
1816
+ +#if os(macOS)
1817
+ + return false
1818
+ +#else
1819
+ return device.isLockingFocusWithCustomLensPositionSupported
1820
+ +#endif
1821
+ }
1822
+
1823
+ var supportsExposureMetering: Bool {
1824
+ @@ -248,7 +320,11 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1825
+ }
1826
+
1827
+ var supportsExposureLocking: Bool {
1828
+ +#if os(macOS)
1829
+ + return false
1830
+ +#else
1831
+ return device.isExposureModeSupported(.custom)
1832
+ +#endif
1833
+ }
1834
+
1835
+ var supportsWhiteBalanceMetering: Bool {
1836
+ @@ -257,16 +333,28 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1837
+ }
1838
+
1839
+ var supportsWhiteBalanceLocking: Bool {
1840
+ +#if os(macOS)
1841
+ + return false
1842
+ +#else
1843
+ return device.isLockingWhiteBalanceWithCustomDeviceGainsSupported
1844
+ +#endif
1845
+ }
1846
+
1847
+ var maxWhiteBalanceGain: Double {
1848
+ +#if os(macOS)
1849
+ + return 1
1850
+ +#else
1851
+ return Double(device.maxWhiteBalanceGain)
1852
+ +#endif
1853
+ }
1854
+
1855
+ var mediaTypes: [MediaType] {
1856
+ +#if os(macOS)
1857
+ + return [.video]
1858
+ +#else
1859
+ let allMediaTypes: [MediaType] = [.video, .depth, .metadata]
1860
+ return allMediaTypes.filter { device.hasMediaType($0.toAVMediaType()) }
1861
+ +#endif
1862
+ }
1863
+
1864
+ func isSessionConfigSupported(config: any HybridCameraSessionConfigSpec) throws -> Bool {
1865
+ @@ -280,6 +368,9 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1866
+ return false
1867
+ }
1868
+
1869
+ +#if os(macOS)
1870
+ + if config.negotiatedFormat.depthFormat != nil { return false }
1871
+ +#else
1872
+ if let depthFormat = config.negotiatedFormat.depthFormat {
1873
+ guard format.supportedDepthDataFormats.contains(depthFormat) else {
1874
+ // Format is from a different `AVCaptureDevice.Format`
1875
+ @@ -287,6 +378,7 @@ final class HybridCameraDevice: HybridCameraDeviceSpec, NativeCameraDevice {
1876
+ }
1877
+ }
1878
+
1879
+ +#endif
1880
+ // TODO: Re-use the logic from `ConstraintResolver` for resolving the actual enabled
1881
+ // constraints (like FPS or HDR settings) so we have a single source of truth
1882
+ // when we modify anything in the future.
1883
+ diff --git a/ios/Hybrid Objects/Orientation/HybridDeviceOrientationManager.swift b/ios/Hybrid Objects/Orientation/HybridDeviceOrientationManager.swift
1884
+ index 2759093..3fdbb55 100644
1885
+ --- a/ios/Hybrid Objects/Orientation/HybridDeviceOrientationManager.swift
1886
+ +++ b/ios/Hybrid Objects/Orientation/HybridDeviceOrientationManager.swift
1887
+ @@ -1,3 +1,12 @@
1888
+ +#if os(macOS)
1889
+ +import NitroModules
1890
+ +final class HybridDeviceOrientationManager: HybridOrientationManagerSpec {
1891
+ + let source: OrientationSource = .device
1892
+ + var currentOrientation: CameraOrientation? { .up }
1893
+ + func startOrientationUpdates(onChanged: @escaping (CameraOrientation) -> Void) { onChanged(.up) }
1894
+ + func stopOrientationUpdates() { }
1895
+ +}
1896
+ +#else
1897
+ ///
1898
+ /// HybridDeviceOrientationManager.swift
1899
+ /// VisionCamera
1900
+ @@ -58,3 +67,5 @@ final class HybridDeviceOrientationManager: HybridOrientationManagerSpec {
1901
+ }
1902
+ }
1903
+ }
1904
+ +
1905
+ +#endif
1906
+ diff --git a/ios/Hybrid Objects/Orientation/HybridInterfaceOrientationManager.swift b/ios/Hybrid Objects/Orientation/HybridInterfaceOrientationManager.swift
1907
+ index 29b1017..8a3a2c9 100644
1908
+ --- a/ios/Hybrid Objects/Orientation/HybridInterfaceOrientationManager.swift
1909
+ +++ b/ios/Hybrid Objects/Orientation/HybridInterfaceOrientationManager.swift
1910
+ @@ -1,3 +1,12 @@
1911
+ +#if os(macOS)
1912
+ +import NitroModules
1913
+ +final class HybridInterfaceOrientationManager: HybridOrientationManagerSpec {
1914
+ + let source: OrientationSource = .interface
1915
+ + var currentOrientation: CameraOrientation? { .up }
1916
+ + func startOrientationUpdates(onChanged: @escaping (CameraOrientation) -> Void) { onChanged(.up) }
1917
+ + func stopOrientationUpdates() { }
1918
+ +}
1919
+ +#else
1920
+ ///
1921
+ /// HybridInterfaceOrientationManager.swift
1922
+ /// VisionCamera
1923
+ @@ -73,3 +82,5 @@ final class HybridInterfaceOrientationManager: HybridOrientationManagerSpec {
1924
+ }
1925
+ }
1926
+ }
1927
+ +
1928
+ +#endif
1929
+ diff --git a/ios/Hybrid Objects/Outputs/HybridCameraDepthFrameOutput.swift b/ios/Hybrid Objects/Outputs/HybridCameraDepthFrameOutput.swift
1930
+ index 0be7d9d..279b949 100644
1931
+ --- a/ios/Hybrid Objects/Outputs/HybridCameraDepthFrameOutput.swift
1932
+ +++ b/ios/Hybrid Objects/Outputs/HybridCameraDepthFrameOutput.swift
1933
+ @@ -1,3 +1,4 @@
1934
+ +#if !os(macOS)
1935
+ ///
1936
+ /// HybridCameraDepthFrameOutput.swift
1937
+ /// VisionCamera
1938
+ @@ -144,3 +145,5 @@ final class HybridCameraDepthFrameOutput: HybridCameraDepthFrameOutputSpec, Nati
1939
+ }
1940
+ }
1941
+ }
1942
+ +
1943
+ +#endif
1944
+ diff --git a/ios/Hybrid Objects/Outputs/HybridCameraFrameOutput.swift b/ios/Hybrid Objects/Outputs/HybridCameraFrameOutput.swift
1945
+ index 17c7a5f..377bd3a 100644
1946
+ --- a/ios/Hybrid Objects/Outputs/HybridCameraFrameOutput.swift
1947
+ +++ b/ios/Hybrid Objects/Outputs/HybridCameraFrameOutput.swift
1948
+ @@ -60,8 +60,10 @@ final class HybridCameraFrameOutput: HybridCameraFrameOutputSpec, NativeCameraOu
1949
+ // If the pipeline stalls, drop frames to avoid blowing up RAM
1950
+ output.alwaysDiscardsLateVideoFrames = options.dropFramesWhileBusy
1951
+ // JS configures the video resolution, we don't want to downscale here by default.
1952
+ +#if !os(macOS)
1953
+ output.automaticallyConfiguresOutputBufferDimensions = false
1954
+ - if #available(iOS 26.0, *), output.isDeferredStartSupported {
1955
+ +#endif
1956
+ + if #available(iOS 26.0, macOS 26.0, *), output.isDeferredStartSupported {
1957
+ // Deferred start allows the Session to delay this output's startup in favor
1958
+ // of preview-related outputs to make preview appear faster.
1959
+ output.isDeferredStartEnabled = options.allowDeferredStart
1960
+ @@ -71,10 +73,14 @@ final class HybridCameraFrameOutput: HybridCameraFrameOutputSpec, NativeCameraOu
1961
+ // and `targetResolution` to `.any` (to not participate in resolution negotiations).
1962
+ // If `allowBufferResizing` is false, we don't set `.videoSettings` ("native" resolution), and reflect our
1963
+ // target resolution via `targetResolution` to participate in resolution negotiations.
1964
+ +#if os(macOS)
1965
+ +
1966
+ +#else
1967
+ if #available(iOS 17.0, *), options.enablePreviewSizedOutputBuffers {
1968
+ output.deliversPreviewSizedOutputBuffers = true
1969
+ }
1970
+ - if #available(iOS 26.0, *) {
1971
+ +#endif
1972
+ + if #available(iOS 26.0, macOS 26.0, *) {
1973
+ // We don't need HDR metadata, as that's only useful for encoders.
1974
+ output.preservesDynamicHDRMetadata = false
1975
+ }
1976
+ @@ -93,12 +99,16 @@ final class HybridCameraFrameOutput: HybridCameraFrameOutputSpec, NativeCameraOu
1977
+ try? connection.setOrientation(outputOrientation)
1978
+ try? connection.setMirrorMode(config.mirrorMode)
1979
+ }
1980
+ +#if os(macOS)
1981
+ +
1982
+ +#else
1983
+ if options.enableCameraMatrixDelivery {
1984
+ // Pipeline should deliver camera matrix (via attachment on `CMSampleBuffer`)
1985
+ if connection.isCameraIntrinsicMatrixDeliverySupported {
1986
+ connection.isCameraIntrinsicMatrixDeliveryEnabled = true
1987
+ }
1988
+ }
1989
+ +#endif
1990
+ }
1991
+
1992
+ private func videoSettingsForPixelFormat(_ targetPixelFormat: TargetVideoPixelFormat) -> [String:
1993
+ diff --git a/ios/Hybrid Objects/Outputs/HybridCameraObjectOutput.swift b/ios/Hybrid Objects/Outputs/HybridCameraObjectOutput.swift
1994
+ index 9e9270a..f9ff425 100644
1995
+ --- a/ios/Hybrid Objects/Outputs/HybridCameraObjectOutput.swift
1996
+ +++ b/ios/Hybrid Objects/Outputs/HybridCameraObjectOutput.swift
1997
+ @@ -1,3 +1,4 @@
1998
+ +#if !os(macOS)
1999
+ ///
2000
+ /// HybridCameraObjectOutput.swift
2001
+ /// VisionCamera
2002
+ @@ -82,3 +83,5 @@ final class HybridCameraObjectOutput: HybridCameraObjectOutputSpec, NativeCamera
2003
+ }
2004
+ }
2005
+ }
2006
+ +
2007
+ +#endif
2008
+ diff --git a/ios/Hybrid Objects/Outputs/HybridCameraOutputSynchronizer.swift b/ios/Hybrid Objects/Outputs/HybridCameraOutputSynchronizer.swift
2009
+ index ba07982..b69d560 100644
2010
+ --- a/ios/Hybrid Objects/Outputs/HybridCameraOutputSynchronizer.swift
2011
+ +++ b/ios/Hybrid Objects/Outputs/HybridCameraOutputSynchronizer.swift
2012
+ @@ -1,3 +1,4 @@
2013
+ +#if !os(macOS)
2014
+ ///
2015
+ /// HybridCameraDepthFrameOutput.swift
2016
+ /// VisionCamera
2017
+ @@ -91,3 +92,5 @@ extension SynchronizedDataFrame {
2018
+ }
2019
+ }
2020
+ }
2021
+ +
2022
+ +#endif
2023
+ diff --git a/ios/Hybrid Objects/Outputs/HybridCameraPhotoOutput.swift b/ios/Hybrid Objects/Outputs/HybridCameraPhotoOutput.swift
2024
+ index e5249f2..3b20de6 100644
2025
+ --- a/ios/Hybrid Objects/Outputs/HybridCameraPhotoOutput.swift
2026
+ +++ b/ios/Hybrid Objects/Outputs/HybridCameraPhotoOutput.swift
2027
+ @@ -18,11 +18,19 @@ final class HybridCameraPhotoOutput: HybridCameraPhotoOutputSpec, NativeCameraOu
2028
+ let output: AVCapturePhotoOutput
2029
+
2030
+ var supportsDepthDataDelivery: Bool {
2031
+ +#if os(macOS)
2032
+ + return false
2033
+ +#else
2034
+ return output.isDepthDataDeliverySupported
2035
+ +#endif
2036
+ }
2037
+
2038
+ var supportsCameraCalibrationDataDelivery: Bool {
2039
+ +#if os(macOS)
2040
+ + return false
2041
+ +#else
2042
+ return output.isCameraCalibrationDataDeliverySupported
2043
+ +#endif
2044
+ }
2045
+ var outputOrientation: CameraOrientation = .up {
2046
+ didSet {
2047
+ @@ -38,11 +46,15 @@ final class HybridCameraPhotoOutput: HybridCameraPhotoOutputSpec, NativeCameraOu
2048
+ let dims = output.maxPhotoDimensions
2049
+ return Size(width: Double(dims.width), height: Double(dims.height))
2050
+ }
2051
+ +#if os(macOS)
2052
+ + return nil
2053
+ +#else
2054
+ guard let device = output.connections.first?.deviceInput?.device else {
2055
+ return nil
2056
+ }
2057
+ let dims = device.activeFormat.highResolutionStillImageDimensions
2058
+ return Size(width: Double(dims.width), height: Double(dims.height))
2059
+ +#endif
2060
+ }
2061
+
2062
+ var streamType: StreamType = .photo
2063
+ @@ -57,6 +69,9 @@ final class HybridCameraPhotoOutput: HybridCameraPhotoOutputSpec, NativeCameraOu
2064
+
2065
+ output.maxPhotoQualityPrioritization = options.qualityPrioritization.toAVQualityPrioritization()
2066
+
2067
+ +#if os(macOS)
2068
+ +
2069
+ +#else
2070
+ if options.containerFormat == .dng {
2071
+ // If we capture RAW photos, try using Apple ProRAW. If not, Bayer14 RAW will be used.
2072
+ output.isAppleProRAWEnabled = output.isAppleProRAWSupported
2073
+ @@ -69,6 +84,7 @@ final class HybridCameraPhotoOutput: HybridCameraPhotoOutputSpec, NativeCameraOu
2074
+ output.isCameraSensorOrientationCompensationEnabled = false
2075
+ }
2076
+
2077
+ +#endif
2078
+ // Prepare the default Photo Settings to make the pipeline ready - some things (like flashMode)
2079
+ // might change on a per-capture basis, but containerFormat and preview size is already known
2080
+ // and can be prepared already.
2081
+ @@ -112,7 +128,9 @@ final class HybridCameraPhotoOutput: HybridCameraPhotoOutputSpec, NativeCameraOu
2082
+ location: nil)
2083
+ let avSettings = try settings.toAVCapturePhotoSettings(for: output, withOptions: options)
2084
+ // Prepare the AVCapturePhotoOutput for those settings so coming up captures are faster
2085
+ +#if !os(macOS)
2086
+ self.output.setPreparedPhotoSettingsArray([avSettings])
2087
+ +#endif
2088
+ }
2089
+
2090
+ func capturePhoto(
2091
+ @@ -135,17 +153,23 @@ final class HybridCameraPhotoOutput: HybridCameraPhotoOutputSpec, NativeCameraOu
2092
+ if self.options.previewImageTargetSize != nil,
2093
+ let onPreviewImageAvailable = callbacks.onPreviewImageAvailable
2094
+ {
2095
+ +#if !os(macOS)
2096
+ Task {
2097
+ // On a separate Thread, start decoding the Preview Image Data
2098
+ - guard let cgPreviewImage = photo.previewCGImageRepresentation() else {
2099
+ + guard let cgPreviewImage = photo.previewCGImageRepresentation() as CGImage? else {
2100
+ return
2101
+ }
2102
+ // Wrap the `CGImage` in a `HybridUIImage` and notify the callback
2103
+ + #if os(macOS)
2104
+ + let image = HybridCGImage(cgImage: cgPreviewImage)
2105
+ + #else
2106
+ let uiImage = UIImage(
2107
+ cgImage: cgPreviewImage, scale: 1.0, orientation: metadata.uiImageOrientation)
2108
+ let image = HybridUIImage(uiImage: uiImage)
2109
+ + #endif
2110
+ onPreviewImageAvailable(image)
2111
+ }
2112
+ +#endif
2113
+ }
2114
+ // 2. Wrap the `AVCapturePhoto` in a `HybridPhoto` and resolve the Promise
2115
+ let image = HybridPhoto(
2116
+ @@ -189,6 +213,10 @@ final class HybridCameraPhotoOutput: HybridCameraPhotoOutputSpec, NativeCameraOu
2117
+ let captureSettings = try settings.map {
2118
+ try $0.toAVCapturePhotoSettings(for: self.output, withOptions: self.options)
2119
+ }
2120
+ +#if os(macOS)
2121
+ + // macOS has no preparation API; validate settings synchronously.
2122
+ + promise.resolve()
2123
+ +#else
2124
+ self.output.setPreparedPhotoSettingsArray(captureSettings) { prepared, error in
2125
+ if let error {
2126
+ promise.reject(withError: error)
2127
+ @@ -200,6 +228,7 @@ final class HybridCameraPhotoOutput: HybridCameraPhotoOutputSpec, NativeCameraOu
2128
+ }
2129
+ }
2130
+ }
2131
+ +#endif
2132
+ return promise
2133
+ }
2134
+ }
2135
+ diff --git a/ios/Hybrid Objects/Outputs/HybridCameraPreviewOutput.swift b/ios/Hybrid Objects/Outputs/HybridCameraPreviewOutput.swift
2136
+ index 9f63db3..056646e 100644
2137
+ --- a/ios/Hybrid Objects/Outputs/HybridCameraPreviewOutput.swift
2138
+ +++ b/ios/Hybrid Objects/Outputs/HybridCameraPreviewOutput.swift
2139
+ @@ -6,6 +6,9 @@
2140
+
2141
+ import AVFoundation
2142
+ import Foundation
2143
+ +#if os(macOS)
2144
+ +import AppKit
2145
+ +#endif
2146
+ import NitroModules
2147
+
2148
+ final class HybridCameraPreviewOutput: HybridCameraPreviewOutputSpec, NativePreviewViewOutput {
2149
+ @@ -38,8 +41,13 @@ final class HybridCameraPreviewOutput: HybridCameraPreviewOutputSpec, NativePrev
2150
+ /// because the view might not even be laid out yet at the time
2151
+ /// the resolution negotiation runs.
2152
+ var targetResolution: ResolutionRule {
2153
+ + #if os(macOS)
2154
+ + let screenSizeDp = CGSize(width: 1920, height: 1080)
2155
+ + let pixelRatio: CGFloat = 1
2156
+ + #else
2157
+ let screenSizeDp = UIScreen.main.bounds.size
2158
+ let pixelRatio = UIScreen.main.scale
2159
+ + #endif
2160
+ let screenSizePx = Size(
2161
+ width: screenSizeDp.width * pixelRatio,
2162
+ height: screenSizeDp.height * pixelRatio)
2163
+ diff --git a/ios/Hybrid Objects/Outputs/HybridCameraVideoFrameOutput.swift b/ios/Hybrid Objects/Outputs/HybridCameraVideoFrameOutput.swift
2164
+ index ddf5895..d713b9c 100644
2165
+ --- a/ios/Hybrid Objects/Outputs/HybridCameraVideoFrameOutput.swift
2166
+ +++ b/ios/Hybrid Objects/Outputs/HybridCameraVideoFrameOutput.swift
2167
+ @@ -62,8 +62,10 @@ final class HybridCameraVideoFrameOutput: HybridCameraVideoOutputSpec, NativeCam
2168
+ // Queue up Frames if there are any hiccups in AVAssetWriter so we don't lose Frames.
2169
+ output.alwaysDiscardsLateVideoFrames = false
2170
+ // JS configures the video resolution, we don't want to downscale here.
2171
+ +#if !os(macOS)
2172
+ output.automaticallyConfiguresOutputBufferDimensions = false
2173
+ - if #available(iOS 26.0, *) {
2174
+ +#endif
2175
+ + if #available(iOS 26.0, macOS 26.0, *) {
2176
+ // We only use this output for recording, allowing it to start deferred makes the session start faster.
2177
+ if output.isDeferredStartSupported {
2178
+ output.isDeferredStartEnabled = true
2179
+ @@ -141,7 +143,7 @@ final class HybridCameraVideoFrameOutput: HybridCameraVideoOutputSpec, NativeCam
2180
+ try recorder.initializeVideoTrack(withSettings: videoSettings)
2181
+
2182
+ // 3. Set video metadata
2183
+ - if #available(iOS 26.0, *) {
2184
+ + if #available(iOS 26.0, macOS 26.0, *) {
2185
+ // 2.1.a. Set video timescale if available. This avoids audio/video sync-drift in super long videos.
2186
+ recorder.setTimescale(self.output.recommendedMediaTimeScaleForAssetWriter)
2187
+ let codec = self.getCurrentVideoCodec()
2188
+ diff --git a/ios/Hybrid Objects/Outputs/HybridCameraVideoOutput.swift b/ios/Hybrid Objects/Outputs/HybridCameraVideoOutput.swift
2189
+ index 0353452..32cc690 100644
2190
+ --- a/ios/Hybrid Objects/Outputs/HybridCameraVideoOutput.swift
2191
+ +++ b/ios/Hybrid Objects/Outputs/HybridCameraVideoOutput.swift
2192
+ @@ -71,6 +71,9 @@ final class HybridCameraVideoOutput: HybridCameraVideoOutputSpec, NativeCameraOu
2193
+ }
2194
+
2195
+ func getSupportedVideoCodecs() throws -> [VideoCodec] {
2196
+ +#if os(macOS)
2197
+ + throw RuntimeError.error(withMessage: "Movie output codec enumeration is not available on macOS")
2198
+ +#else
2199
+ guard output.connection(with: .video) != nil else {
2200
+ throw RuntimeError.error(
2201
+ withMessage:
2202
+ @@ -78,9 +81,13 @@ final class HybridCameraVideoOutput: HybridCameraVideoOutputSpec, NativeCameraOu
2203
+ )
2204
+ }
2205
+ return output.availableVideoCodecTypes.map { VideoCodec(avCodec: $0) }
2206
+ +#endif
2207
+ }
2208
+
2209
+ func setOutputSettings(settings: VideoOutputSettings) -> Promise<Void> {
2210
+ +#if os(macOS)
2211
+ + return .rejected(withError: RuntimeError.error(withMessage: "Custom movie encoding settings are not supported by this macOS prototype"))
2212
+ +#else
2213
+ return Promise.parallel(queue) {
2214
+ guard let connection = self.output.connection(with: .video) else {
2215
+ throw RuntimeError.error(
2216
+ @@ -118,6 +125,7 @@ final class HybridCameraVideoOutput: HybridCameraVideoOutputSpec, NativeCameraOu
2217
+ }
2218
+ }
2219
+ }
2220
+ +#endif
2221
+ }
2222
+
2223
+ func createRecorder(settings: RecorderSettings) -> Promise<any HybridRecorderSpec> {
2224
+ @@ -145,6 +153,7 @@ final class HybridCameraVideoOutput: HybridCameraVideoOutputSpec, NativeCameraOu
2225
+ }
2226
+
2227
+ private func setOutputSettings(bitRate: Int, codec: AVVideoCodecType, for connection: AVCaptureConnection) {
2228
+ +#if !os(macOS)
2229
+ let supportedKeys = output.supportedOutputSettingsKeys(for: connection)
2230
+ guard supportedKeys.contains(AVVideoCompressionPropertiesKey) else {
2231
+ logger.error(
2232
+ @@ -152,6 +161,7 @@ final class HybridCameraVideoOutput: HybridCameraVideoOutputSpec, NativeCameraOu
2233
+ return
2234
+ }
2235
+
2236
+ +#endif
2237
+ let settings: [String: Any] = [
2238
+ AVVideoCodecKey: codec,
2239
+ AVVideoCompressionPropertiesKey: [
2240
+ diff --git a/ios/Hybrid Objects/Utils/HybridFrameConverter.swift b/ios/Hybrid Objects/Utils/HybridFrameConverter.swift
2241
+ index 803fd8c..265f780 100644
2242
+ --- a/ios/Hybrid Objects/Utils/HybridFrameConverter.swift
2243
+ +++ b/ios/Hybrid Objects/Utils/HybridFrameConverter.swift
2244
+ @@ -19,7 +19,11 @@ final class HybridFrameConverter: HybridFrameConverterSpec {
2245
+ }
2246
+ let uiOrientation = frame.metadata.uiImageOrientation
2247
+ let uiImage = try sampleBuffer.toUIImage(orientation: uiOrientation)
2248
+ + #if os(macOS)
2249
+ + return HybridCGImage(cgImage: uiImage)
2250
+ + #else
2251
+ return HybridUIImage(uiImage: uiImage)
2252
+ + #endif
2253
+ }
2254
+
2255
+ func convertFrameToImageAsync(frame: any HybridFrameSpec) throws -> Promise<any HybridImageSpec> {
2256
+ @@ -37,7 +41,11 @@ final class HybridFrameConverter: HybridFrameConverterSpec {
2257
+ }
2258
+ let uiOrientation = depth.metadata.uiImageOrientation
2259
+ let uiImage = try depthData.toUIImage(orientation: uiOrientation)
2260
+ + #if os(macOS)
2261
+ + return HybridCGImage(cgImage: uiImage)
2262
+ + #else
2263
+ return HybridUIImage(uiImage: uiImage)
2264
+ + #endif
2265
+ }
2266
+
2267
+ func convertDepthToImageAsync(depth: any HybridDepthSpec) throws -> Promise<any HybridImageSpec> {
2268
+ diff --git a/ios/Hybrid Objects/Views/HybridFrameRendererView.swift b/ios/Hybrid Objects/Views/HybridFrameRendererView.swift
2269
+ index d113a56..8d2a753 100644
2270
+ --- a/ios/Hybrid Objects/Views/HybridFrameRendererView.swift
2271
+ +++ b/ios/Hybrid Objects/Views/HybridFrameRendererView.swift
2272
+ @@ -9,7 +9,7 @@ import Foundation
2273
+ import NitroModules
2274
+
2275
+ final class HybridFrameRendererView: HybridFrameRendererViewSpec {
2276
+ - var view: UIView = AutoLayerResizingView()
2277
+ + var view = AutoLayerResizingView()
2278
+ var renderer: (any HybridFrameRendererSpec)? {
2279
+ didSet {
2280
+ updateRendererLayer()
2281
+ @@ -18,9 +18,9 @@ final class HybridFrameRendererView: HybridFrameRendererViewSpec {
2282
+
2283
+ private func updateRendererLayer() {
2284
+ DispatchQueue.main.async {
2285
+ - self.view.layer.sublayers?.removeAll()
2286
+ + self.view.contentLayer.sublayers?.removeAll()
2287
+ if let renderer = self.renderer as? any NativeFrameRenderer {
2288
+ - self.view.layer.addSublayer(renderer.layer)
2289
+ + self.view.contentLayer.addSublayer(renderer.layer)
2290
+ renderer.layer.frame = self.view.bounds
2291
+ }
2292
+ }
2293
+ diff --git a/ios/Hybrid Objects/Views/HybridPreviewView.swift b/ios/Hybrid Objects/Views/HybridPreviewView.swift
2294
+ index 00d7532..722a5cf 100644
2295
+ --- a/ios/Hybrid Objects/Views/HybridPreviewView.swift
2296
+ +++ b/ios/Hybrid Objects/Views/HybridPreviewView.swift
2297
+ @@ -10,7 +10,7 @@ import NitroImage
2298
+ import NitroModules
2299
+
2300
+ final class HybridPreviewView: HybridPreviewViewSpec {
2301
+ - var view: UIView = AutoLayerResizingView()
2302
+ + var view = AutoLayerResizingView()
2303
+ var previewOutput: (any HybridCameraPreviewOutputSpec)? {
2304
+ didSet {
2305
+ updatePreviewLayer()
2306
+ @@ -86,6 +86,9 @@ final class HybridPreviewView: HybridPreviewViewSpec {
2307
+ func convertScannedObjectCoordinatesToViewCoordinates(
2308
+ scannedObject: (any HybridScannedObjectSpec)
2309
+ ) throws -> (any HybridScannedObjectSpec) {
2310
+ +#if os(macOS)
2311
+ + throw RuntimeError.error(withMessage: "Object scanning is not supported on macOS")
2312
+ +#else
2313
+ guard let previewLayer else {
2314
+ throw RuntimeError.error(
2315
+ withMessage:
2316
+ @@ -100,6 +103,7 @@ final class HybridPreviewView: HybridPreviewViewSpec {
2317
+ withMessage: "Failed to transform the Scanned Object's coordinates into view coordinates!")
2318
+ }
2319
+ return HybridCameraObjectOutput.createHybridScannedObject(from: transformedObject)
2320
+ +#endif
2321
+ }
2322
+
2323
+ func createMeteringPoint(viewX: Double, viewY: Double, size: Double?) throws
2324
+ @@ -127,10 +131,10 @@ final class HybridPreviewView: HybridPreviewViewSpec {
2325
+ DispatchQueue.main.async {
2326
+ if let previewLayer = self.previewLayer {
2327
+ // Remove all sublayers that are not our AVCaptureVideoPreviewLayer
2328
+ - self.view.layer.sublayers?.removeAll { $0 != previewLayer }
2329
+ - if self.view.layer.sublayers?.contains(previewLayer) != true {
2330
+ + self.view.contentLayer.sublayers?.removeAll { $0 != previewLayer }
2331
+ + if self.view.contentLayer.sublayers?.contains(previewLayer) != true {
2332
+ // If we don't have the preview layer, we add it here
2333
+ - self.view.layer.addSublayer(previewLayer)
2334
+ + self.view.contentLayer.addSublayer(previewLayer)
2335
+ }
2336
+ previewLayer.frame = self.view.bounds
2337
+
2338
+ @@ -138,6 +142,7 @@ final class HybridPreviewView: HybridPreviewViewSpec {
2339
+ let resizeMode = self.resizeMode ?? .cover
2340
+ previewLayer.videoGravity = resizeMode.toAVLayerVideoGravity()
2341
+
2342
+ +#if !os(macOS)
2343
+ // Add listener to `isPreviewing`
2344
+ self.isPreviewingObserver = previewLayer.observe(
2345
+ \.isPreviewing,
2346
+ @@ -156,9 +161,10 @@ final class HybridPreviewView: HybridPreviewViewSpec {
2347
+ self.onPreviewStopped?()
2348
+ }
2349
+ })
2350
+ +#endif
2351
+ } else {
2352
+ // Remove all sublayers (including the AVCaptureVideoPreviewLayer)
2353
+ - self.view.layer.sublayers?.removeAll()
2354
+ + self.view.contentLayer.sublayers?.removeAll()
2355
+ // Remove listener to `isPreviewing`
2356
+ self.onPreviewStopped?()
2357
+ self.isPreviewingObserver = nil
2358
+ diff --git a/ios/Public/MediaSampleMetadata.swift b/ios/Public/MediaSampleMetadata.swift
2359
+ index 4917891..b6af8b6 100644
2360
+ --- a/ios/Public/MediaSampleMetadata.swift
2361
+ +++ b/ios/Public/MediaSampleMetadata.swift
2362
+ @@ -8,7 +8,11 @@
2363
+ import AVFoundation
2364
+ import Foundation
2365
+ import NitroModules
2366
+ +#if os(macOS)
2367
+ +import ImageIO
2368
+ +#else
2369
+ import UIKit
2370
+ +#endif
2371
+
2372
+ public struct MediaSampleMetadata {
2373
+ let timestamp: CMTime
2374
+ @@ -32,7 +36,11 @@ public struct MediaSampleMetadata {
2375
+ self.isMirrored = isMirrored
2376
+ }
2377
+
2378
+ + #if os(macOS)
2379
+ + var uiImageOrientation: CGImagePropertyOrientation { orientation.toImageOrientation(isMirrored: isMirrored) }
2380
+ + #else
2381
+ var uiImageOrientation: UIImage.Orientation {
2382
+ return orientation.toUIImageOrientation(isMirrored: isMirrored)
2383
+ }
2384
+ + #endif
2385
+ }
2386
+ diff --git a/ios/Public/NativeGestureController.swift b/ios/Public/NativeGestureController.swift
2387
+ index 7ff5397..ddbf76d 100644
2388
+ --- a/ios/Public/NativeGestureController.swift
2389
+ +++ b/ios/Public/NativeGestureController.swift
2390
+ @@ -6,10 +6,16 @@
2391
+ //
2392
+
2393
+ import Foundation
2394
+ +#if os(macOS)
2395
+ +import AppKit
2396
+ +public typealias CameraGestureRecognizer = NSGestureRecognizer
2397
+ +#else
2398
+ import UIKit
2399
+ +public typealias CameraGestureRecognizer = UIGestureRecognizer
2400
+ +#endif
2401
+
2402
+ public protocol NativeGestureController: AnyObject {
2403
+ - associatedtype GestureRecognizer: UIGestureRecognizer
2404
+ + associatedtype GestureRecognizer: CameraGestureRecognizer
2405
+
2406
+ /**
2407
+ * Gets the underlying `UIGestureRecognizer`.
2408
+ diff --git a/ios/Utils/CameraPlatformImage.swift b/ios/Utils/CameraPlatformImage.swift
2409
+ new file mode 100644
2410
+ index 0000000..ff64ce6
2411
+ --- /dev/null
2412
+ +++ b/ios/Utils/CameraPlatformImage.swift
2413
+ @@ -0,0 +1,17 @@
2414
+ +import AVFoundation
2415
+ +import CoreImage
2416
+ +import Foundation
2417
+ +import NitroImage
2418
+ +#if os(macOS)
2419
+ +import ImageIO
2420
+ +typealias CameraImageOrientation = CGImagePropertyOrientation
2421
+ +typealias CameraPlatformImage = CGImage
2422
+ +extension CameraOrientation {
2423
+ + func toImageOrientation(isMirrored: Bool) -> CGImagePropertyOrientation {
2424
+ + switch self { case .up: return isMirrored ? .upMirrored : .up; case .down: return isMirrored ? .downMirrored : .down; case .left: return isMirrored ? .leftMirrored : .left; case .right: return isMirrored ? .rightMirrored : .right }
2425
+ + }
2426
+ +}
2427
+ +#else
2428
+ +typealias CameraImageOrientation = UIImage.Orientation
2429
+ +typealias CameraPlatformImage = UIImage
2430
+ +#endif
2431
+ diff --git a/ios/Utils/LazyLockablePixelBuffer.swift b/ios/Utils/LazyLockablePixelBuffer.swift
2432
+ index 89434ed..bfa5edc 100644
2433
+ --- a/ios/Utils/LazyLockablePixelBuffer.swift
2434
+ +++ b/ios/Utils/LazyLockablePixelBuffer.swift
2435
+ @@ -8,7 +8,7 @@
2436
+ import AVFoundation
2437
+ import Foundation
2438
+ import NitroModules
2439
+ -import UIKit
2440
+ +import CoreGraphics
2441
+
2442
+ protocol LazyLockableBuffer: AnyObject {
2443
+ var isLocked: Bool { get set }
2444
+ diff --git a/ios/Utils/SubjectAreaMonitor.swift b/ios/Utils/SubjectAreaMonitor.swift
2445
+ index 3be310e..4eb4938 100644
2446
+ --- a/ios/Utils/SubjectAreaMonitor.swift
2447
+ +++ b/ios/Utils/SubjectAreaMonitor.swift
2448
+ @@ -1,3 +1,4 @@
2449
+ +#if !os(macOS)
2450
+ //
2451
+ // SubjectAreaMonitor.swift
2452
+ // VisionCamera
2453
+ @@ -89,3 +90,5 @@ enum SubjectAreaMonitor {
2454
+ }
2455
+ }
2456
+ }
2457
+ +
2458
+ +#endif
2459
+ diff --git a/ios/Views/AutoLayerResizingView.swift b/ios/Views/AutoLayerResizingView.swift
2460
+ index 65f6dd9..566bdbc 100644
2461
+ --- a/ios/Views/AutoLayerResizingView.swift
2462
+ +++ b/ios/Views/AutoLayerResizingView.swift
2463
+ @@ -1,3 +1,14 @@
2464
+ +#if os(macOS)
2465
+ +import AppKit
2466
+ +import QuartzCore
2467
+ +final class AutoLayerResizingView: NSView {
2468
+ + override var isFlipped: Bool { true }
2469
+ + var contentLayer: CALayer { layer! }
2470
+ + override init(frame: NSRect) { super.init(frame: frame); wantsLayer = true }
2471
+ + required init?(coder: NSCoder) { super.init(coder: coder); wantsLayer = true }
2472
+ + override func layout() { super.layout(); contentLayer.sublayers?.forEach { $0.frame = bounds } }
2473
+ +}
2474
+ +#else
2475
+ //
2476
+ // AutoLayerResizingView.swift
2477
+ // VisionCamera
2478
+ @@ -10,6 +21,7 @@ import Foundation
2479
+
2480
+ /// A view that auto-resizes its sublayers each time
2481
+ final class AutoLayerResizingView: UIView {
2482
+ + var contentLayer: CALayer { layer }
2483
+ override func layoutSubviews() {
2484
+ super.layoutSubviews()
2485
+ layer.sublayers?.forEach { layer in
2486
+ @@ -17,3 +29,5 @@ final class AutoLayerResizingView: UIView {
2487
+ }
2488
+ }
2489
+ }
2490
+ +
2491
+ +#endif
2492
+ diff --git a/nitrogen/generated/ios/VisionCamera+autolinking.rb b/nitrogen/generated/ios/VisionCamera+autolinking.rb
2493
+ index c1a02e7..518a0f8 100644
2494
+ --- a/nitrogen/generated/ios/VisionCamera+autolinking.rb
2495
+ +++ b/nitrogen/generated/ios/VisionCamera+autolinking.rb
2496
+ @@ -54,6 +54,8 @@ def add_nitrogen_files(spec)
2497
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
2498
+ # Enables C++ <-> Swift interop (by default it's only ObjC)
2499
+ "SWIFT_OBJC_INTEROP_MODE" => "objcxx",
2500
+ + # Nitro's Fabric-only templates must not enter its Swift umbrella.
2501
+ + "HEADER_SEARCH_PATHS" => "$(inherited) $(PODS_ROOT)/Headers/Private",
2502
+ # Enables stricter modular headers
2503
+ "DEFINES_MODULE" => "YES",
2504
+ # Disable auto-generated ObjC header for Swift (Static linkage on Xcode 26.4 breaks here)
2505
+ diff --git a/nitrogen/generated/ios/c++/views/HybridFrameRendererViewComponent.mm b/nitrogen/generated/ios/c++/views/HybridFrameRendererViewComponent.mm
2506
+ index 3b6dda4..361f192 100644
2507
+ --- a/nitrogen/generated/ios/c++/views/HybridFrameRendererViewComponent.mm
2508
+ +++ b/nitrogen/generated/ios/c++/views/HybridFrameRendererViewComponent.mm
2509
+ @@ -12,7 +12,12 @@
2510
+ #import <React/RCTComponentViewFactory.h>
2511
+ #import <React/UIView+ComponentViewProtocol.h>
2512
+ #import <NitroModules/NitroDefines.hpp>
2513
+ +#import <TargetConditionals.h>
2514
+ +#if TARGET_OS_OSX
2515
+ +#import <AppKit/AppKit.h>
2516
+ +#else
2517
+ #import <UIKit/UIKit.h>
2518
+ +#endif
2519
+
2520
+ #import "HybridFrameRendererViewSpecSwift.hpp"
2521
+ #import "VisionCamera-Swift-Cxx-Umbrella.hpp"
2522
+ @@ -65,12 +70,21 @@ - (void) updateView {
2523
+
2524
+ // 2. Get UIView*
2525
+ void* viewUnsafe = swiftPart.getView();
2526
+ + #if TARGET_OS_OSX
2527
+ + NSView* view = (__bridge_transfer NSView*) viewUnsafe;
2528
+ +#else
2529
+ UIView* view = (__bridge_transfer UIView*) viewUnsafe;
2530
+ +#endif
2531
+
2532
+ // 3. Update RCTViewComponentView's [contentView]
2533
+ [self setContentView:view];
2534
+ }
2535
+
2536
+ +// RN < 0.82 does not call invalidate for views which opt out of recycling.
2537
+ +- (void) dealloc {
2538
+ + if (_hybridView != nullptr) [self notifyOnDropView];
2539
+ +}
2540
+ +
2541
+ - (void) notifyOnDropView {
2542
+ // A recycled component can later be invalidated. Notify only once per mount.
2543
+ if (_didDropView) {
2544
+ diff --git a/nitrogen/generated/ios/c++/views/HybridPreviewViewComponent.mm b/nitrogen/generated/ios/c++/views/HybridPreviewViewComponent.mm
2545
+ index 3251d7b..ce0265b 100644
2546
+ --- a/nitrogen/generated/ios/c++/views/HybridPreviewViewComponent.mm
2547
+ +++ b/nitrogen/generated/ios/c++/views/HybridPreviewViewComponent.mm
2548
+ @@ -12,7 +12,12 @@
2549
+ #import <React/RCTComponentViewFactory.h>
2550
+ #import <React/UIView+ComponentViewProtocol.h>
2551
+ #import <NitroModules/NitroDefines.hpp>
2552
+ +#import <TargetConditionals.h>
2553
+ +#if TARGET_OS_OSX
2554
+ +#import <AppKit/AppKit.h>
2555
+ +#else
2556
+ #import <UIKit/UIKit.h>
2557
+ +#endif
2558
+
2559
+ #import "HybridPreviewViewSpecSwift.hpp"
2560
+ #import "VisionCamera-Swift-Cxx-Umbrella.hpp"
2561
+ @@ -65,12 +70,21 @@ - (void) updateView {
2562
+
2563
+ // 2. Get UIView*
2564
+ void* viewUnsafe = swiftPart.getView();
2565
+ + #if TARGET_OS_OSX
2566
+ + NSView* view = (__bridge_transfer NSView*) viewUnsafe;
2567
+ +#else
2568
+ UIView* view = (__bridge_transfer UIView*) viewUnsafe;
2569
+ +#endif
2570
+
2571
+ // 3. Update RCTViewComponentView's [contentView]
2572
+ [self setContentView:view];
2573
+ }
2574
+
2575
+ +// RN < 0.82 does not call invalidate for views which opt out of recycling.
2576
+ +- (void) dealloc {
2577
+ + if (_hybridView != nullptr) [self notifyOnDropView];
2578
+ +}
2579
+ +
2580
+ - (void) notifyOnDropView {
2581
+ // A recycled component can later be invalidated. Notify only once per mount.
2582
+ if (_didDropView) {
2583
+ diff --git a/nitrogen/generated/shared/c++/views/HybridFrameRendererViewComponent.cpp b/nitrogen/generated/shared/c++/views/HybridFrameRendererViewComponent.cpp
2584
+ index 5255f8e..ca364f4 100644
2585
+ --- a/nitrogen/generated/shared/c++/views/HybridFrameRendererViewComponent.cpp
2586
+ +++ b/nitrogen/generated/shared/c++/views/HybridFrameRendererViewComponent.cpp
2587
+ @@ -7,6 +7,9 @@
2588
+
2589
+ #include "HybridFrameRendererViewComponent.hpp"
2590
+
2591
+ +#if defined(__APPLE__)
2592
+ +#include <TargetConditionals.h>
2593
+ +#endif
2594
+ #include <NitroModules/NitroHash.hpp>
2595
+ #include <NitroModules/ReactProp.hpp>
2596
+
2597
+ @@ -19,7 +22,11 @@ namespace margelo::nitro::camera::views {
2598
+ HybridFrameRendererViewProps::HybridFrameRendererViewProps(const react::PropsParserContext& context,
2599
+ const HybridFrameRendererViewProps& sourceProps,
2600
+ const react::RawProps& rawProps):
2601
+ - react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
2602
+ + react::ViewProps(context, sourceProps, rawProps
2603
+ + #if !TARGET_OS_OSX
2604
+ + , filterObjectKeys
2605
+ + #endif
2606
+ + ),
2607
+ renderer(nitro::ReactProp<std::optional<std::shared_ptr<HybridFrameRendererSpec>>>::fromRawValue("FrameRendererView", "renderer", rawProps, sourceProps.renderer)),
2608
+ hybridRef(nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridFrameRendererViewSpec>& /* ref */)>>>::fromRawValue("FrameRendererView", "hybridRef", rawProps, sourceProps.hybridRef)) { }
2609
+
2610
+ diff --git a/nitrogen/generated/shared/c++/views/HybridPreviewViewComponent.cpp b/nitrogen/generated/shared/c++/views/HybridPreviewViewComponent.cpp
2611
+ index 7e6f73b..e5ac262 100644
2612
+ --- a/nitrogen/generated/shared/c++/views/HybridPreviewViewComponent.cpp
2613
+ +++ b/nitrogen/generated/shared/c++/views/HybridPreviewViewComponent.cpp
2614
+ @@ -7,6 +7,9 @@
2615
+
2616
+ #include "HybridPreviewViewComponent.hpp"
2617
+
2618
+ +#if defined(__APPLE__)
2619
+ +#include <TargetConditionals.h>
2620
+ +#endif
2621
+ #include <NitroModules/NitroHash.hpp>
2622
+ #include <NitroModules/ReactProp.hpp>
2623
+
2624
+ @@ -19,7 +22,11 @@ namespace margelo::nitro::camera::views {
2625
+ HybridPreviewViewProps::HybridPreviewViewProps(const react::PropsParserContext& context,
2626
+ const HybridPreviewViewProps& sourceProps,
2627
+ const react::RawProps& rawProps):
2628
+ - react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
2629
+ + react::ViewProps(context, sourceProps, rawProps
2630
+ + #if !TARGET_OS_OSX
2631
+ + , filterObjectKeys
2632
+ + #endif
2633
+ + ),
2634
+ previewOutput(nitro::ReactProp<std::optional<std::shared_ptr<HybridCameraPreviewOutputSpec>>>::fromRawValue("PreviewView", "previewOutput", rawProps, sourceProps.previewOutput)),
2635
+ resizeMode(nitro::ReactProp<std::optional<PreviewResizeMode>>::fromRawValue("PreviewView", "resizeMode", rawProps, sourceProps.resizeMode)),
2636
+ implementationMode(nitro::ReactProp<std::optional<PreviewImplementationMode>>::fromRawValue("PreviewView", "implementationMode", rawProps, sourceProps.implementationMode)),
2637
+ diff --git a/package.json b/package.json
2638
+ index 341aa56..306f6a1 100644
2639
+ --- a/package.json
2640
+ +++ b/package.json
2641
+ @@ -27,7 +27,8 @@
2642
+ "cpp/**/*.cpp",
2643
+ "nitro.json",
2644
+ "*.podspec",
2645
+ - "README.md"
2646
+ + "README.md",
2647
+ + "macos/**/*.swift"
2648
+ ],
2649
+ "scripts": {
2650
+ "typecheck": "tsc --noEmit",
2651
+ diff --git a/react-native.config.js b/react-native.config.js
2652
+ index 3fdf8ea..e2ad767 100644
2653
+ --- a/react-native.config.js
2654
+ +++ b/react-native.config.js
2655
+ @@ -7,6 +7,7 @@ module.exports = {
2656
+ * @type {import('@react-native-community/cli-types').IOSDependencyParams}
2657
+ */
2658
+ ios: {},
2659
+ + macos: { podspecPath: __dirname + "/VisionCamera.podspec" },
2660
+ /**
2661
+ * @type {import('@react-native-community/cli-types').AndroidDependencyParams}
2662
+ */