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

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 (399) 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/config-plugin.d.cts +2 -0
  7. package/contracts.ts +2 -0
  8. package/diagnostics.ts +1 -0
  9. package/expo-config.d.cts +1 -0
  10. package/metro.cjs +4 -1
  11. package/metro.d.cts +12 -0
  12. package/native.cjs +3 -1
  13. package/native.d.cts +6 -0
  14. package/package.json +82 -14
  15. package/schema.json +276 -58
  16. package/settings-observable.ts +1 -0
  17. package/settings-window.ts +1 -0
  18. package/shortcuts-commands.ts +1 -0
  19. package/shortcuts-keyboard.ts +3 -0
  20. package/ui-classnames.ts +1 -0
  21. package/ui-glass.ts +1 -0
  22. package/ui-search.ts +1 -0
  23. package/ui-sidebar.ts +1 -0
  24. package/ui-split-view.ts +1 -0
  25. package/ui-symbol.ts +1 -0
  26. package/vendor/node_modules/@legendapp/spark-ai/package.json +19 -0
  27. package/vendor/node_modules/@legendapp/spark-ai/src/availability.ts +17 -0
  28. package/vendor/node_modules/@legendapp/spark-ai/src/index.ts +15 -0
  29. package/vendor/node_modules/@legendapp/spark-ai/src/invocations.ts +34 -0
  30. package/vendor/node_modules/@legendapp/spark-ai/src/output.ts +62 -0
  31. package/vendor/node_modules/@legendapp/spark-ai/src/run.ts +12 -0
  32. package/vendor/node_modules/@legendapp/spark-ai/src/types.ts +16 -0
  33. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.h +2 -1
  34. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.mm +126 -24
  35. package/vendor/node_modules/@legendapp/spark-audio/package.json +5 -2
  36. package/vendor/node_modules/@legendapp/spark-audio/src/NativeSparkAudio.ts +5 -1
  37. package/vendor/node_modules/@legendapp/spark-audio/src/desktop.ts +36 -32
  38. package/vendor/node_modules/@legendapp/spark-audio/src/events.ts +11 -0
  39. package/vendor/node_modules/@legendapp/spark-audio/src/hooks.ts +65 -0
  40. package/vendor/node_modules/@legendapp/spark-audio/src/index.web.ts +92 -33
  41. package/vendor/node_modules/@legendapp/spark-audio/src/media-session-unavailable.ts +6 -4
  42. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.ts +52 -19
  43. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.web.ts +18 -10
  44. package/vendor/node_modules/@legendapp/spark-audio/src/media-types.ts +40 -13
  45. package/vendor/node_modules/@legendapp/spark-audio/src/mobile.ts +40 -32
  46. package/vendor/node_modules/@legendapp/spark-audio/src/player.ts +97 -0
  47. package/vendor/node_modules/@legendapp/spark-audio/src/status-listeners.ts +21 -15
  48. package/vendor/node_modules/@legendapp/spark-audio/src/types.ts +9 -4
  49. package/vendor/node_modules/@legendapp/spark-audio/windows/SparkAudio/SparkAudio.h +67 -20
  50. package/vendor/node_modules/@legendapp/spark-auth-session/package.json +4 -3
  51. package/vendor/node_modules/@legendapp/spark-auth-session/src/core.ts +60 -42
  52. package/vendor/node_modules/@legendapp/spark-auth-session/src/index.ts +28 -12
  53. package/vendor/node_modules/@legendapp/spark-auth-session/src/mobile.ts +97 -41
  54. package/vendor/node_modules/@legendapp/spark-auth-session/src/types.ts +20 -9
  55. package/vendor/node_modules/@legendapp/spark-cli/dist/add-desktop.js +50 -7
  56. package/vendor/node_modules/@legendapp/spark-cli/dist/build-mode.js +1 -1
  57. package/vendor/node_modules/@legendapp/spark-cli/dist/build.js +83 -48
  58. package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +60 -0
  59. package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +7 -2
  60. package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +31 -5
  61. package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +69 -23
  62. package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +12 -3
  63. package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +12 -9
  64. package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +1 -1
  65. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-dev-patch.cjs +4 -2
  66. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-metro.cjs +1 -1
  67. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-node.js +3 -3
  68. package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +10 -38
  69. package/vendor/node_modules/@legendapp/spark-cli/dist/local.js +1 -1
  70. package/vendor/node_modules/@legendapp/spark-cli/dist/metro.cjs +26 -5
  71. package/vendor/node_modules/@legendapp/spark-cli/dist/native-metadata.js +26 -0
  72. package/vendor/node_modules/@legendapp/spark-cli/dist/native-patch-preflight.js +84 -0
  73. package/vendor/node_modules/@legendapp/spark-cli/dist/native-preparation.js +88 -0
  74. package/vendor/node_modules/@legendapp/spark-cli/dist/notary-profiles.js +75 -0
  75. package/vendor/node_modules/@legendapp/spark-cli/dist/package.js +4 -2
  76. package/vendor/node_modules/@legendapp/spark-cli/dist/platform.js +16 -2
  77. package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +66 -13
  78. package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +84 -4
  79. package/vendor/node_modules/@legendapp/spark-cli/dist/release.json +23 -18
  80. package/vendor/node_modules/@legendapp/spark-cli/dist/runner-install.js +145 -31
  81. package/vendor/node_modules/@legendapp/spark-cli/dist/signing.js +4 -3
  82. package/vendor/node_modules/@legendapp/spark-cli/dist/updates.js +41 -4
  83. package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +4 -1
  84. package/vendor/node_modules/@legendapp/spark-cli/package.json +7 -6
  85. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/App.tsx +6 -12
  86. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/desktop.config.json +5 -3
  87. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/package.json +11 -9
  88. package/vendor/node_modules/@legendapp/spark-cli/templates/diff-lite/Git.macos.tsx +2 -2
  89. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.macos.tsx +23 -21
  90. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.windows.tsx +23 -21
  91. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.desktop.ts +11 -8
  92. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.macos.ts +3 -3
  93. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/Lifecycle.desktop.tsx +8 -14
  94. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/OpenFiles.desktop.tsx +5 -10
  95. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.desktop.ts +11 -8
  96. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.macos.ts +3 -3
  97. package/vendor/node_modules/@legendapp/spark-cli/templates/music-lite/assets.desktop.ts +2 -2
  98. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Activation.desktop.tsx +2 -2
  99. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Desktop.desktop.ts +37 -15
  100. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/session.ts +5 -6
  101. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/window-session.ts +5 -4
  102. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/desktop.config.json +5 -3
  103. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/metro.config.js +1 -1
  104. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/package.json +13 -11
  105. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/react-native.config.js +1 -1
  106. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/package.json +13 -11
  107. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/react-native.config.js +1 -1
  108. package/vendor/node_modules/@legendapp/spark-clipboard/macos/RNDesktopClipboard.mm +10 -16
  109. package/vendor/node_modules/@legendapp/spark-clipboard/package.json +2 -2
  110. package/vendor/node_modules/@legendapp/spark-clipboard/src/NativeDesktopClipboard.ts +1 -1
  111. package/vendor/node_modules/@legendapp/spark-clipboard/src/desktop.ts +55 -31
  112. package/vendor/node_modules/@legendapp/spark-clipboard/src/expo.ts +21 -11
  113. package/vendor/node_modules/@legendapp/spark-clipboard/src/options.ts +8 -0
  114. package/vendor/node_modules/@legendapp/spark-clipboard/src/types.ts +10 -0
  115. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.h +38 -15
  116. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.vcxproj +2 -0
  117. package/vendor/node_modules/@legendapp/spark-codex/RNCodex.podspec +22 -0
  118. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.hpp +23 -0
  119. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.mm +1529 -0
  120. package/vendor/node_modules/@legendapp/spark-codex/nitro.json +21 -0
  121. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/.gitattributes +1 -0
  122. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex+autolinking.rb +62 -0
  123. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.cpp +17 -0
  124. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.hpp +27 -0
  125. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Umbrella.hpp +38 -0
  126. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.mm +35 -0
  127. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.swift +16 -0
  128. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexAvailability.hpp +95 -0
  129. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexRunResult.hpp +99 -0
  130. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.cpp +24 -0
  131. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.hpp +71 -0
  132. package/vendor/node_modules/@legendapp/spark-codex/package.json +35 -0
  133. package/vendor/node_modules/@legendapp/spark-codex/react-native.config.js +11 -0
  134. package/vendor/node_modules/@legendapp/spark-codex/src/Codex.nitro.ts +30 -0
  135. package/vendor/node_modules/@legendapp/spark-codex/src/index.ts +65 -0
  136. package/vendor/node_modules/@legendapp/spark-codex/src/native.ts +6 -0
  137. package/vendor/node_modules/@legendapp/spark-commands/package.json +21 -0
  138. package/vendor/node_modules/@legendapp/spark-commands/src/bindings.ts +90 -0
  139. package/vendor/node_modules/@legendapp/spark-commands/src/index.tsx +400 -0
  140. package/vendor/node_modules/@legendapp/spark-commands/src/router.ts +138 -0
  141. package/vendor/node_modules/@legendapp/spark-commands/src/storage.ts +20 -0
  142. package/vendor/node_modules/@legendapp/spark-context-menu/macos/RNContextMenu.mm +7 -8
  143. package/vendor/node_modules/@legendapp/spark-context-menu/package.json +4 -3
  144. package/vendor/node_modules/@legendapp/spark-context-menu/src/NativeContextMenu.ts +1 -1
  145. package/vendor/node_modules/@legendapp/spark-context-menu/src/index.ts +26 -26
  146. package/vendor/node_modules/@legendapp/spark-context-menu/windows/SparkContextMenu/SparkContextMenu.h +3 -2
  147. package/vendor/node_modules/@legendapp/spark-desktop-app/RNDesktopApp.podspec +2 -1
  148. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryJSI.hpp +89 -0
  149. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryWindows.hpp +22 -0
  150. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/RNDesktopApp.mm +13 -7
  151. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkBinaryJSI.h +48 -0
  152. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.h +4 -0
  153. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.mm +33 -33
  154. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktopError.h +20 -0
  155. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.h +26 -0
  156. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.mm +49 -0
  157. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.h +9 -0
  158. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.mm +70 -0
  159. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkWindow.mm +9 -6
  160. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/recent-documents/RNRecentDocumentController.mm +55 -0
  161. package/vendor/node_modules/@legendapp/spark-desktop-app/package.json +5 -3
  162. package/vendor/node_modules/@legendapp/spark-desktop-app/src/NativeDesktopApp.ts +1 -1
  163. package/vendor/node_modules/@legendapp/spark-desktop-app/src/api.ts +86 -40
  164. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/accelerator.ts +40 -0
  165. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/index.ts +56 -0
  166. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/menu.ts +114 -0
  167. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/native-buffer.ts +22 -0
  168. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/path.ts +24 -0
  169. package/vendor/node_modules/@legendapp/spark-desktop-app/src/events.ts +75 -0
  170. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.ts +2 -0
  171. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.windows.ts +2 -0
  172. package/vendor/node_modules/@legendapp/spark-desktop-app/src/open-requests.ts +43 -0
  173. package/vendor/node_modules/@legendapp/spark-desktop-app/src/restoration.ts +11 -0
  174. package/vendor/node_modules/@legendapp/spark-desktop-app/src/transport.ts +10 -0
  175. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.cjs +10 -5
  176. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.d.cts +3 -0
  177. package/vendor/node_modules/@legendapp/spark-desktop-config/autolinking.cjs +35 -0
  178. package/vendor/node_modules/@legendapp/spark-desktop-config/config.cjs +16 -3
  179. package/vendor/node_modules/@legendapp/spark-desktop-config/config.d.cts +89 -9
  180. package/vendor/node_modules/@legendapp/spark-desktop-config/expo.d.cts +3 -0
  181. package/vendor/node_modules/@legendapp/spark-desktop-config/identity.cjs +3 -2
  182. package/vendor/node_modules/@legendapp/spark-desktop-config/lifecycle.cjs +20 -0
  183. package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +6 -5
  184. package/vendor/node_modules/@legendapp/spark-desktop-config/schema.json +276 -58
  185. package/vendor/node_modules/@legendapp/spark-desktop-config/updates.cjs +1 -0
  186. package/vendor/node_modules/@legendapp/spark-desktop-config/validation.cjs +35 -0
  187. package/vendor/node_modules/@legendapp/spark-desktop-config/windows.plugin.cjs +2 -1
  188. package/vendor/node_modules/@legendapp/spark-desktop-host/AppDelegate.mm +129 -11
  189. package/vendor/node_modules/@legendapp/spark-desktop-host/package.json +2 -2
  190. package/vendor/node_modules/@legendapp/spark-desktop-host/windows/application.inc +277 -121
  191. package/vendor/node_modules/@legendapp/spark-desktop-links/common/AuthLoopback.h +5 -7
  192. package/vendor/node_modules/@legendapp/spark-desktop-links/macos/RNDesktopLinks.mm +10 -9
  193. package/vendor/node_modules/@legendapp/spark-desktop-links/package.json +2 -2
  194. package/vendor/node_modules/@legendapp/spark-desktop-links/src/NativeDesktopLinks.ts +1 -1
  195. package/vendor/node_modules/@legendapp/spark-desktop-links/src/api.ts +27 -33
  196. package/vendor/node_modules/@legendapp/spark-desktop-links/src/expo.ts +3 -4
  197. package/vendor/node_modules/@legendapp/spark-desktop-links/src/native.ts +9 -0
  198. package/vendor/node_modules/@legendapp/spark-desktop-links/windows/SparkLinks/SparkLinks.h +22 -22
  199. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/RNDesktopShortcuts.podspec +1 -0
  200. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/RNDesktopShortcuts.mm +1 -1
  201. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.h +9 -0
  202. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.mm +307 -0
  203. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/package.json +8 -5
  204. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/NativeDesktopShortcuts.ts +1 -1
  205. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/api.ts +39 -14
  206. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/NativeKeyboardManager.ts +12 -0
  207. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/codes.ts +108 -0
  208. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/index.ts +99 -0
  209. package/vendor/node_modules/@legendapp/spark-desktop-windows/RNDesktopWindows.podspec +2 -0
  210. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/RNDesktopWindows.mm +229 -103
  211. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/SparkWindowGeometry.h +63 -0
  212. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.h +20 -0
  213. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.mm +27 -0
  214. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.h +12 -0
  215. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.mm +3130 -0
  216. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManagerStartup.mm +31 -0
  217. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/SidebarStartupFallbacks.mm +14 -0
  218. package/vendor/node_modules/@legendapp/spark-desktop-windows/package.json +10 -6
  219. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/NativeDesktopWindowManager.ts +1 -1
  220. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/api.ts +144 -70
  221. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/diagnostics.ts +17 -0
  222. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.ts +3 -1
  223. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.windows.ts +3 -1
  224. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos-adapter.ts +53 -0
  225. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos.ts +51 -0
  226. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/transport.ts +41 -0
  227. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/types.ts +140 -0
  228. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/validation.ts +162 -0
  229. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/window-manager/NativeWindowManager.ts +28 -0
  230. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/WindowProvider.tsx +20 -0
  231. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/createWindowsNavigator.tsx +78 -0
  232. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/index.ts +6 -0
  233. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/primaryWindowLifecycle.ts +34 -0
  234. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/types.ts +11 -0
  235. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/usePrimaryWindowLifecycle.ts +18 -0
  236. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/useWindowFocusEffect.ts +17 -0
  237. package/vendor/node_modules/@legendapp/spark-documents/package.json +22 -0
  238. package/vendor/node_modules/@legendapp/spark-documents/src/controller.ts +97 -0
  239. package/vendor/node_modules/@legendapp/spark-documents/src/documentTransition.ts +13 -0
  240. package/vendor/node_modules/@legendapp/spark-documents/src/hooks.ts +139 -0
  241. package/vendor/node_modules/@legendapp/spark-documents/src/index.tsx +50 -0
  242. package/vendor/node_modules/@legendapp/spark-documents/src/reload.ts +39 -0
  243. package/vendor/node_modules/@legendapp/spark-documents/src/requests.ts +15 -0
  244. package/vendor/node_modules/@legendapp/spark-drag-drop/package.json +4 -3
  245. package/vendor/node_modules/@legendapp/spark-drag-drop/src/DesktopDragViewNativeComponent.ts +1 -1
  246. package/vendor/node_modules/@legendapp/spark-drag-drop/src/contracts.ts +73 -18
  247. package/vendor/node_modules/@legendapp/spark-drag-drop/src/index.tsx +58 -15
  248. package/vendor/node_modules/@legendapp/spark-drag-drop/windows/SparkDragDrop/SparkDragDrop.h +14 -8
  249. package/vendor/node_modules/@legendapp/spark-file-dialog/ios/RNFileDialog.mm +34 -121
  250. package/vendor/node_modules/@legendapp/spark-file-dialog/package.json +12 -5
  251. package/vendor/node_modules/@legendapp/spark-file-dialog/src/NativeFileDialog.ts +1 -5
  252. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.ts +19 -84
  253. package/vendor/node_modules/@legendapp/spark-file-dialog/src/native.ts +12 -0
  254. package/vendor/node_modules/@legendapp/spark-file-dialog/src/options.ts +34 -14
  255. package/vendor/node_modules/@legendapp/spark-file-dialog/src/types.ts +26 -0
  256. package/vendor/node_modules/@legendapp/spark-file-dialog/windows/SparkFileDialog/SparkFileDialog.h +42 -58
  257. package/vendor/node_modules/@legendapp/spark-file-system/RNDesktopFileSystem.podspec +1 -0
  258. package/vendor/node_modules/@legendapp/spark-file-system/macos/RNDesktopFileSystem.mm +32 -21
  259. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.h +24 -0
  260. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.mm +215 -0
  261. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.h +9 -0
  262. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.mm +82 -0
  263. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.h +47 -0
  264. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.mm +306 -0
  265. package/vendor/node_modules/@legendapp/spark-file-system/package.json +8 -6
  266. package/vendor/node_modules/@legendapp/spark-file-system/src/NativeDesktopFileSystem.ts +1 -1
  267. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/NativeFileScanner.ts +11 -0
  268. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/index.ts +93 -0
  269. package/vendor/node_modules/@legendapp/spark-file-system/src/handles.ts +48 -14
  270. package/vendor/node_modules/@legendapp/spark-file-system/src/index.ts +134 -45
  271. package/vendor/node_modules/@legendapp/spark-file-system/src/path.ts +1 -9
  272. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.h +102 -26
  273. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.vcxproj +2 -0
  274. package/vendor/node_modules/@legendapp/spark-global-shortcuts/RNDesktopGlobalShortcuts.podspec +1 -0
  275. package/vendor/node_modules/@legendapp/spark-global-shortcuts/macos/RNDesktopGlobalShortcuts.mm +7 -4
  276. package/vendor/node_modules/@legendapp/spark-global-shortcuts/package.json +4 -3
  277. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/NativeDesktopGlobalShortcuts.ts +1 -1
  278. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/index.ts +39 -10
  279. package/vendor/node_modules/@legendapp/spark-global-shortcuts/windows/SparkGlobalShortcuts/SparkGlobalShortcuts.h +5 -4
  280. package/vendor/node_modules/@legendapp/spark-message-dialog/macos/RNDesktopMessageDialog.mm +2 -2
  281. package/vendor/node_modules/@legendapp/spark-message-dialog/package.json +2 -2
  282. package/vendor/node_modules/@legendapp/spark-message-dialog/src/NativeDesktopMessageDialog.ts +1 -1
  283. package/vendor/node_modules/@legendapp/spark-message-dialog/src/index.ts +48 -11
  284. package/vendor/node_modules/@legendapp/spark-native-menu/ios/RNNativeMenu.mm +141 -609
  285. package/vendor/node_modules/@legendapp/spark-native-menu/package.json +9 -4
  286. package/vendor/node_modules/@legendapp/spark-native-menu/src/NativeMenu.ts +2 -7
  287. package/vendor/node_modules/@legendapp/spark-native-menu/src/api.ts +76 -121
  288. package/vendor/node_modules/@legendapp/spark-native-menu/src/hooks.ts +97 -0
  289. package/vendor/node_modules/@legendapp/spark-native-menu/src/windows-menus.ts +43 -37
  290. package/vendor/node_modules/@legendapp/spark-notifications/macos/RNDesktopNotifications.mm +75 -30
  291. package/vendor/node_modules/@legendapp/spark-notifications/package.json +2 -2
  292. package/vendor/node_modules/@legendapp/spark-notifications/src/NativeDesktopNotifications.ts +1 -1
  293. package/vendor/node_modules/@legendapp/spark-notifications/src/index.ts +130 -42
  294. package/vendor/node_modules/@legendapp/spark-notifications/windows/SparkNotifications/SparkNotifications.h +46 -8
  295. package/vendor/node_modules/@legendapp/spark-processes/macos/RNDesktopProcesses.mm +94 -21
  296. package/vendor/node_modules/@legendapp/spark-processes/package.json +4 -3
  297. package/vendor/node_modules/@legendapp/spark-processes/src/NativeDesktopProcesses.ts +1 -1
  298. package/vendor/node_modules/@legendapp/spark-processes/src/index.ts +124 -28
  299. package/vendor/node_modules/@legendapp/spark-processes/src/types.ts +34 -0
  300. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.h +83 -34
  301. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.vcxproj +2 -0
  302. package/vendor/node_modules/@legendapp/spark-secure-storage/macos/RNDesktopSecureStorage.mm +49 -40
  303. package/vendor/node_modules/@legendapp/spark-secure-storage/package.json +2 -2
  304. package/vendor/node_modules/@legendapp/spark-secure-storage/src/NativeDesktopSecureStorage.ts +1 -1
  305. package/vendor/node_modules/@legendapp/spark-secure-storage/src/desktop.ts +16 -14
  306. package/vendor/node_modules/@legendapp/spark-secure-storage/src/expo.ts +0 -1
  307. package/vendor/node_modules/@legendapp/spark-secure-storage/src/options.ts +3 -2
  308. package/vendor/node_modules/@legendapp/spark-secure-storage/src/unavailable.ts +6 -3
  309. package/vendor/node_modules/@legendapp/spark-settings/package.json +10 -3
  310. package/vendor/node_modules/@legendapp/spark-settings/src/fileStorage.ts +11 -0
  311. package/vendor/node_modules/@legendapp/spark-settings/src/index.ts +7 -9
  312. package/vendor/node_modules/@legendapp/spark-settings/src/storage/index.ts +142 -0
  313. package/vendor/node_modules/@legendapp/spark-settings/src/store.ts +63 -21
  314. package/vendor/node_modules/@legendapp/spark-settings-ui/package.json +20 -0
  315. package/vendor/node_modules/@legendapp/spark-settings-ui/src/index.tsx +436 -0
  316. package/vendor/node_modules/@legendapp/spark-settings-ui/src/options.ts +10 -0
  317. package/vendor/node_modules/@legendapp/spark-sqlite/package.json +3 -3
  318. package/vendor/node_modules/@legendapp/spark-sqlite/src/database.ts +182 -0
  319. package/vendor/node_modules/@legendapp/spark-sqlite/src/index.ts +19 -6
  320. package/vendor/node_modules/@legendapp/spark-sqlite/src/types.ts +22 -0
  321. package/vendor/node_modules/@legendapp/spark-system/macos/RNDesktopSystem.mm +36 -11
  322. package/vendor/node_modules/@legendapp/spark-system/package.json +2 -2
  323. package/vendor/node_modules/@legendapp/spark-system/src/NativeDesktopSystem.ts +1 -1
  324. package/vendor/node_modules/@legendapp/spark-system/src/index.ts +89 -32
  325. package/vendor/node_modules/@legendapp/spark-system/windows/SparkSystem/SparkSystem.h +8 -4
  326. package/vendor/node_modules/@legendapp/spark-tray/macos/RNDesktopTray.mm +20 -8
  327. package/vendor/node_modules/@legendapp/spark-tray/package.json +2 -2
  328. package/vendor/node_modules/@legendapp/spark-tray/src/NativeDesktopTray.ts +1 -1
  329. package/vendor/node_modules/@legendapp/spark-tray/src/index.ts +91 -34
  330. package/vendor/node_modules/@legendapp/spark-tray/windows/SparkTray/SparkTray.h +45 -15
  331. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkButton.mm +2 -1
  332. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkTextInput.mm +10 -2
  333. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.h +15 -0
  334. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.mm +1182 -0
  335. package/vendor/node_modules/@legendapp/spark-ui/macos/glass-effect-view/RNGlassEffectView.mm +72 -0
  336. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.h +15 -0
  337. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.mm +194 -0
  338. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.h +15 -0
  339. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.mm +180 -0
  340. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.h +15 -0
  341. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.mm +155 -0
  342. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.h +18 -0
  343. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.mm +645 -0
  344. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.h +15 -0
  345. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.mm +164 -0
  346. package/vendor/node_modules/@legendapp/spark-ui/package.json +16 -4
  347. package/vendor/node_modules/@legendapp/spark-ui/src/SparkSelectNativeComponent.ts +3 -1
  348. package/vendor/node_modules/@legendapp/spark-ui/src/SparkTextInputNativeComponent.ts +6 -2
  349. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/SidebarSplitViewNativeComponent.ts +31 -0
  350. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +77 -0
  351. package/vendor/node_modules/@legendapp/spark-ui/src/availability.ts +10 -0
  352. package/vendor/node_modules/@legendapp/spark-ui/src/classnames/index.ts +8 -0
  353. package/vendor/node_modules/@legendapp/spark-ui/src/control.ts +42 -0
  354. package/vendor/node_modules/@legendapp/spark-ui/src/desktop.tsx +56 -0
  355. package/vendor/node_modules/@legendapp/spark-ui/src/glass-effect-view/index.tsx +15 -0
  356. package/vendor/node_modules/@legendapp/spark-ui/src/index.android.tsx +35 -21
  357. package/vendor/node_modules/@legendapp/spark-ui/src/index.ios.tsx +55 -26
  358. package/vendor/node_modules/@legendapp/spark-ui/src/index.tsx +1 -27
  359. package/vendor/node_modules/@legendapp/spark-ui/src/index.web.tsx +45 -14
  360. package/vendor/node_modules/@legendapp/spark-ui/src/index.windows.tsx +1 -42
  361. package/vendor/node_modules/@legendapp/spark-ui/src/mobile-input.tsx +11 -0
  362. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSegmentedControlNativeComponent.ts +17 -0
  363. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSelectNativeComponent.ts +17 -0
  364. package/vendor/node_modules/@legendapp/spark-ui/src/select.ts +5 -3
  365. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/SFSymbolNativeComponent.ts +13 -0
  366. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/index.tsx +27 -0
  367. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarItemNativeComponent.ts +25 -0
  368. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarNativeComponent.ts +24 -0
  369. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/index.tsx +104 -0
  370. package/vendor/node_modules/@legendapp/spark-ui/src/specialized.ts +22 -0
  371. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/TextInputSearchNativeComponent.ts +33 -0
  372. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/index.tsx +34 -0
  373. package/vendor/node_modules/@legendapp/spark-ui/src/types.ts +30 -19
  374. package/vendor/node_modules/@legendapp/spark-ui/uniwind.ts +3 -1
  375. package/vendor/node_modules/@legendapp/spark-ui/uniwind.web.ts +3 -1
  376. package/vendor/node_modules/@legendapp/spark-ui/windows/SparkUI/SparkUI.h +16 -11
  377. package/vendor/node_modules/@legendapp/spark-updates/macos/RNDesktopUpdates.mm +21 -8
  378. package/vendor/node_modules/@legendapp/spark-updates/package.json +2 -2
  379. package/vendor/node_modules/@legendapp/spark-updates/src/NativeDesktopUpdates.ts +3 -2
  380. package/vendor/node_modules/@legendapp/spark-updates/src/index.ts +71 -15
  381. package/vendor/node_modules/@legendapp/spark-webview/package.json +2 -2
  382. package/vendor/node_modules/@legendapp/spark-webview/src/WebView.tsx +11 -0
  383. package/vendor/node_modules/@legendapp/spark-webview/src/adapter.ts +55 -0
  384. package/vendor/node_modules/@legendapp/spark-webview/src/index.ts +2 -2
  385. package/vendor/node_modules/@legendapp/spark-webview/src/types.ts +32 -0
  386. package/vendor/node_modules/@legendapp/spark-webview/src/view-props.ts +28 -0
  387. package/vendor/node_modules/@legendapp/spark-window-options/index.cjs +51 -14
  388. package/vendor/node_modules/@legendapp/spark-window-options/index.d.ts +21 -8
  389. package/vendor/node_modules/@legendapp/spark-window-options/package.json +1 -1
  390. package/windows-macos.ts +1 -0
  391. package/expo-metro.cjs +0 -1
  392. package/message-dialog.ts +0 -1
  393. package/universal.cjs +0 -1
  394. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/accelerator.ts +0 -22
  395. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows-options.ts +0 -11
  396. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.windows.ts +0 -15
  397. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/accelerator.ts +0 -22
  398. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.h +0 -4
  399. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.mm +0 -53
@@ -12,9 +12,8 @@
12
12
  #endif
13
13
  #include <atomic>
14
14
  #include <chrono>
15
- #include <mutex>
15
+ #include <functional>
16
16
  #include <string>
17
- #include <vector>
18
17
  #include <thread>
19
18
  #include <stdexcept>
20
19
  #include <algorithm>
@@ -33,8 +32,7 @@ class AuthLoopback {
33
32
  AuthSocket listener = InvalidSocket;
34
33
  std::atomic<bool> stopped{false};
35
34
  std::thread worker;
36
- std::mutex mutex;
37
- std::vector<std::string> results;
35
+ std::function<void(std::string)> received;
38
36
  std::string route, origin;
39
37
  bool initialized = false;
40
38
  void Receive() {
@@ -71,15 +69,16 @@ class AuthLoopback {
71
69
  (request.substr(end, first - end) == " HTTP/1.1" || request.substr(end, first - end) == " HTTP/1.0");
72
70
  }
73
71
  }
74
- if (valid) { std::lock_guard lock(mutex); if (results.size() < 16) results.push_back(origin + target); }
75
72
  const std::string body = valid ? "Callback received. You may return to the application." : "Invalid callback request.";
76
73
  const std::string response = std::string("HTTP/1.1 ") + (valid ? "200 OK" : "400 Bad Request") + "\r\nContent-Type: text/plain; charset=utf-8\r\nCache-Control: no-store\r\nConnection: close\r\nContent-Length: " + std::to_string(body.size()) + "\r\n\r\n" + body;
77
74
  send(client, response.data(), static_cast<int>(response.size()), 0); CloseSocket(client);
75
+ if (valid && !stopped) received(origin + target);
78
76
  }
79
77
  CloseSocket(listener); listener = InvalidSocket;
80
78
  }
81
79
  public:
82
- AuthLoopback(unsigned short port, std::string path) : route(std::move(path)) {
80
+ AuthLoopback(unsigned short port, std::string path, std::function<void(std::string)> callback) : received(std::move(callback)), route(std::move(path)) {
81
+ if (!received) throw std::invalid_argument("Expected a callback listener");
83
82
  if (route.empty() || route[0] != '/' || route.find_first_not_of("/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_") != std::string::npos) throw std::invalid_argument("Invalid callback path");
84
83
  #ifdef _WIN32
85
84
  WSADATA data{}; if (WSAStartup(MAKEWORD(2, 2), &data)) throw std::runtime_error("Could not initialize loopback socket"); initialized = true;
@@ -115,7 +114,6 @@ class AuthLoopback {
115
114
  #endif
116
115
  }
117
116
  std::string RedirectURI() const { return origin + route; }
118
- std::vector<std::string> Drain() { std::lock_guard lock(mutex); auto values = std::move(results); results.clear(); return values; }
119
117
  AuthLoopback(AuthLoopback const &) = delete;
120
118
  };
121
119
  }
@@ -29,30 +29,31 @@ RCT_EXPORT_MODULE(NativeDesktopLinks)
29
29
  } else if ([method isEqual:@"authPrepare"]) {
30
30
  if (key.empty() || _authSessions.count(key) || _authSessions.size() >= 4) throw std::invalid_argument("Invalid or busy auth session");
31
31
  NSInteger port = [args[@"port"] integerValue]; if (port < 0 || port > 65535) throw std::invalid_argument("Invalid callback port");
32
- auto receiver = std::make_unique<spark::AuthLoopback>((unsigned short)port, std::string([args[@"path"] UTF8String]));
32
+ auto receiver = std::make_unique<spark::AuthLoopback>((unsigned short)port, std::string([args[@"path"] UTF8String]), [identifier](std::string value) {
33
+ NSString *url = [NSString stringWithUTF8String:value.c_str()];
34
+ dispatch_async(dispatch_get_main_queue(), ^{ SparkEmit(@{ @"type": @"authRedirect", @"id": identifier, @"url": url }); });
35
+ });
33
36
  NSString *uri = [NSString stringWithUTF8String:receiver->RedirectURI().c_str()]; _authSessions.emplace(key, std::move(receiver)); resolve(SparkJSON(uri));
34
37
  } else if ([method isEqual:@"authClose"]) { _authSessions.erase(key); resolve(@"null"); }
35
- else if ([method isEqual:@"authPoll"]) {
36
- auto found = _authSessions.find(key); if (found == _authSessions.end()) throw std::invalid_argument("Auth session is closed");
37
- NSMutableArray *urls = [NSMutableArray new]; for (auto const &value : found->second->Drain()) [urls addObject:[NSString stringWithUTF8String:value.c_str()]];
38
- resolve(SparkJSON(urls));
39
- } else throw std::invalid_argument("Unknown auth operation");
38
+ else throw std::invalid_argument("Unknown auth operation");
40
39
  } catch (std::exception const &error) { reject(@"E_AUTH", [NSString stringWithUTF8String:error.what()], nil); }
41
40
  return;
42
41
  }
43
42
  if ([method isEqual:@"recent"]) {
44
43
  NSArray *result = [NSUserDefaults.standardUserDefaults arrayForKey:[SparkNamespace() stringByAppendingString:@".recentDocuments"]] ?: @[];
45
- resolve(SparkJSON(result)); return;
44
+ NSMutableArray *paths = [NSMutableArray new];
45
+ for (NSString *value in result) { NSURL *url = [NSURL URLWithString:value]; if (!url.isFileURL || !url.path.length) { reject(@"E_INVALID_DATA", @"Invalid recent document URL", nil); return; } [paths addObject:url.path]; }
46
+ resolve(SparkJSON(paths)); return;
46
47
  }
47
48
  if ([method isEqual:@"clearRecent"]) {
48
49
  [NSUserDefaults.standardUserDefaults removeObjectForKey:[SparkNamespace() stringByAppendingString:@".recentDocuments"]];
49
50
  if (![SparkContext()[@"runtime"][@"mode"] isEqual:@"go"]) [NSDocumentController.sharedDocumentController clearRecentDocuments:nil];
50
51
  resolve(@"null"); return;
51
52
  }
52
- NSURL *url = [NSURL URLWithString:args[@"url"] ?: @""];
53
+ NSURL *url = ([method isEqual:@"noteRecent"] || [method isEqual:@"openPath"]) ? [NSURL fileURLWithPath:args[@"path"]] : [NSURL URLWithString:args[@"url"] ?: @""];
53
54
  if (!url.scheme.length) { SparkInvalid(reject, @"URL must have a scheme"); return; }
54
55
  if ([method isEqual:@"canOpen"]) resolve(SparkJSON(@([NSWorkspace.sharedWorkspace URLForApplicationToOpenURL:url] != nil)));
55
- else if ([method isEqual:@"open"]) {
56
+ else if ([method isEqual:@"open"] || [method isEqual:@"openPath"]) {
56
57
  // Opening a URL can deliver an Apple event back to this app. Keep its
57
58
  // main run loop free while LaunchServices resolves and opens the target.
58
59
  [NSWorkspace.sharedWorkspace openURL:url configuration:NSWorkspaceOpenConfiguration.configuration
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/spark-desktop-links",
3
- "version": "0.0.1-next.2",
3
+ "version": "0.0.1-next.3",
4
4
  "license": "MIT",
5
5
  "main": "src/index",
6
6
  "react-native": "src/index",
@@ -42,7 +42,7 @@
42
42
  ]
43
43
  },
44
44
  "dependencies": {
45
- "@legendapp/spark-desktop-app": "0.0.1-next.2"
45
+ "@legendapp/spark-desktop-app": "0.0.1-next.3"
46
46
  },
47
47
  "peerDependenciesMeta": {
48
48
  "expo-linking": {
@@ -3,4 +3,4 @@ import { TurboModuleRegistry } from "react-native";
3
3
  export interface Spec extends TurboModule {
4
4
  call(method: string, args: string): Promise<string>;
5
5
  }
6
- export default TurboModuleRegistry.getEnforcing<Spec>("NativeDesktopLinks");
6
+ export default TurboModuleRegistry.get<Spec>("NativeDesktopLinks");
@@ -1,37 +1,31 @@
1
- import Native from "./NativeDesktopLinks";
2
- import { callApp, onDesktopEvent, type DesktopEvent } from "@legendapp/spark-desktop-app";
3
- export type OpenEvent = { type: "openFile" | "openURL"; id: string; url: string };
4
- function url(value: string) { if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) throw new Error("URL must include a scheme"); return value; }
5
- async function call<T = void>(method: string, args: object = {}): Promise<T> { return JSON.parse(await Native.call(method, JSON.stringify(args))) as T; }
6
- export async function openURL(value: string): Promise<true> {
7
- await call("open", { url: url(value) }); return true;
1
+ import { Platform } from "react-native";
2
+ import { onDesktopEvent } from "@legendapp/spark-desktop-app/src/events";
3
+ import { callAppNative } from "@legendapp/spark-desktop-app/src/transport";
4
+ import { nativePath } from "@legendapp/spark-desktop-app/src/contracts/path";
5
+ import { SparkError, type Subscription } from "@legendapp/spark-desktop-app/src/contracts";
6
+ export type { Subscription } from "@legendapp/spark-desktop-app/src/contracts";
7
+ import { callLinks, linksCommand } from "./native";
8
+ export type URLListener = (event: { url: string }) => void;
9
+ function url(value: string) {
10
+ if (typeof value !== "string" || !/^[a-z][a-z0-9+.-]*:/i.test(value) || value.includes("\0")) throw new SparkError("E_INVALID_ARGUMENT", "URL must include a scheme and contain no NUL");
11
+ return value;
8
12
  }
9
- export async function canOpenURL(value: string): Promise<boolean> { return call("canOpen", { url: url(value) }); }
10
- export const noteRecentDocument = (value: string) => call("noteRecent", { url: url(value) });
11
- export const clearRecentDocuments = () => call("clearRecent");
12
- export const getRecentDocuments = () => call<string[]>("recent");
13
- /** Subscribe before fetching queued launches; event ids deduplicate the overlap. */
14
- export async function onOpen(listener: (event: OpenEvent) => void) {
15
- const seen = new Set<string>(); let removed = false;
16
- function deliver(event: DesktopEvent) {
17
- if (removed || (event.type !== "openFile" && event.type !== "openURL") || !event.id || seen.has(event.id)) return;
18
- seen.add(event.id);
19
- if (seen.size > 200) seen.delete(seen.values().next().value!);
20
- listener(event as OpenEvent);
21
- }
22
- const subscription = onDesktopEvent(deliver);
23
- try { for (const event of await callApp<DesktopEvent[]>("pendingURLs")) deliver(event); }
24
- catch (error) { subscription.remove(); throw error; }
25
- return { remove() { removed = true; subscription.remove(); } };
13
+ /** Selected Expo Linking contract: resolves true after native launch acceptance. */
14
+ export async function openURL(value: string): Promise<true> { await linksCommand("open", { url: url(value) }); return true; }
15
+ export function canOpenURL(value: string): Promise<boolean> {
16
+ return Promise.resolve().then(() => callLinks("canOpen", { url: url(value) }, (value): value is boolean => typeof value === "boolean"));
26
17
  }
27
-
28
- export type URLListener = (event: { url: string }) => void;
29
- /** URL that launched this native process, or null; stable across later opens and JS reloads. */
30
- export const getInitialURL = (): Promise<string | null> => callApp("initialURL");
31
- /** Live URL events only. Use onOpen for queued desktop URLs and file-open events. */
32
- export function addEventListener(type: "url", listener: URLListener): { remove(): void } {
33
- if (type !== "url" || typeof listener !== "function") throw new TypeError("Expected a url event listener");
18
+ /** Spark extension: open a local file or directory in its associated application. */
19
+ export async function openPath(path: string): Promise<void> { await linksCommand("openPath", { path: nativePath(path, Platform.OS) }); }
20
+ /** Stable native-process launch URL; later opens and JS reloads do not replace it. */
21
+ export function getInitialURL(): Promise<string | null> {
22
+ return callAppNative("initialURL", (value): value is string | null => value === null || (typeof value === "string" && /^[a-z][a-z0-9+.-]*:/i.test(value) && !value.includes("\0")));
23
+ }
24
+ /** Live URL events only. Queued file/URL requests belong to app/documents. */
25
+ export function addEventListener(type: "url", listener: URLListener): Subscription {
26
+ if (type !== "url" || typeof listener !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected a url event listener");
27
+ if (Platform.OS !== "macos" && Platform.OS !== "windows") throw new SparkError("E_UNSUPPORTED_PLATFORM", "Desktop URL events require a desktop host");
34
28
  return onDesktopEvent(event => {
35
- if (event.type === "openURL" && event.initial !== true && typeof event.url === "string") listener({ url: event.url });
36
- });
29
+ if (event.type === "openURL" && event.initial !== true && typeof event.url === "string" && /^[a-z][a-z0-9+.-]*:/i.test(event.url) && !event.url.includes("\0")) listener({ url: event.url });
30
+ }, { types: ["openURL"] });
37
31
  }
@@ -1,5 +1,4 @@
1
+ import { SparkError } from "@legendapp/spark-desktop-app/src/contracts";
1
2
  export { openURL, canOpenURL, getInitialURL, addEventListener } from "expo-linking";
2
- export type { OpenEvent, URLListener } from "./index";
3
- /** Legacy desktop facilities have no shared equivalent. */
4
- const unavailable = async (..._args: unknown[]): Promise<never> => { throw Object.assign(new Error("Desktop document events/history are unavailable on this platform; use URL events instead"), { code: "E_UNAVAILABLE" }); };
5
- export const onOpen = unavailable, noteRecentDocument = unavailable, clearRecentDocuments = unavailable, getRecentDocuments = unavailable;
3
+ export type { URLListener } from "./api";
4
+ export async function openPath(_path: string): Promise<void> { throw new SparkError("E_UNSUPPORTED_PLATFORM", "Opening a native desktop path requires a desktop host"); }
@@ -0,0 +1,9 @@
1
+ import { Platform } from "react-native";
2
+ import Native from "./NativeDesktopLinks";
3
+ import { SparkError, invokeNative, parseNativeResult } from "@legendapp/spark-desktop-app/src/contracts";
4
+ export async function callLinks<T>(method: string, args: object, validate: (value: unknown) => value is T): Promise<T> {
5
+ if (Platform.OS !== "macos" && Platform.OS !== "windows") throw new SparkError("E_UNSUPPORTED_PLATFORM", "Desktop linking requires a desktop host");
6
+ if (!Native) throw new SparkError("E_MODULE_UNAVAILABLE", "Desktop linking module is unavailable");
7
+ return parseNativeResult(await invokeNative(() => Native!.call(method, JSON.stringify(args))), validate);
8
+ }
9
+ export async function linksCommand(method: string, args: object = {}): Promise<void> { await callLinks(method, args, (value): value is null => value === null); }
@@ -41,13 +41,17 @@ struct RecentDocuments {
41
41
  }
42
42
  void Write(Json::JsonArray const &list) { auto value = list.Stringify(); check_win32(RegSetValueExW(key, L"URLs", 0, REG_SZ, reinterpret_cast<BYTE const *>(value.c_str()), static_cast<DWORD>((value.size() + 1) * sizeof(wchar_t)))); }
43
43
  bool ShellEnabled() { wchar_t mode[16]{}; GetEnvironmentVariableW(L"SPARK_RUNTIME_MODE", mode, 16); return std::wstring(mode) == L"dev"; }
44
- void Add(hstring const &url) {
44
+ static std::wstring Path(hstring const &url) {
45
+ DWORD size = 32768; std::wstring path(size, L'\0'); check_hresult(PathCreateFromUrlW(url.c_str(), path.data(), &size, 0)); path.resize(wcslen(path.c_str())); return path;
46
+ }
47
+ Json::JsonArray Paths() { Json::JsonArray paths; for (auto const &entry : Read()) paths.Append(Json::JsonValue::CreateStringValue(Path(entry.GetString()))); return paths; }
48
+ void Add(hstring const &path) {
49
+ DWORD size = 32768; std::wstring url(size, L'\0'); check_hresult(UrlCreateFromPathW(path.c_str(), url.data(), &size, 0)); url.resize(wcslen(url.c_str()));
45
50
  Windows::Foundation::Uri uri(url);
46
51
  if (uri.SchemeName() != L"file") throw hresult_invalid_argument(L"Recent documents require file URLs");
47
52
  Json::JsonArray next; next.Append(Json::JsonValue::CreateStringValue(uri.AbsoluteUri()));
48
53
  for (auto const &entry : Read()) if (entry.GetString() != uri.AbsoluteUri() && next.Size() < 20) next.Append(entry);
49
54
  if (ShellEnabled()) {
50
- DWORD size = 32768; std::wstring path(size, L'\0'); check_hresult(PathCreateFromUrlW(uri.AbsoluteUri().c_str(), path.data(), &size, 0));
51
55
  com_ptr<IShellItem> item; check_hresult(SHCreateItemFromParsingName(path.c_str(), nullptr, IID_PPV_ARGS(item.put())));
52
56
  SHARDAPPIDINFO info{item.get(), appId.c_str()}; SHAddToRecentDocs(SHARD_APPIDINFO, &info);
53
57
  }
@@ -68,7 +72,7 @@ struct SparkLinks {
68
72
  React::ReactContext context;
69
73
  REACT_INIT(Initialize)
70
74
  void Initialize(React::ReactContext const &value) noexcept { context = value; }
71
- static fire_and_forget Invoke(std::shared_ptr<Receivers> receivers, std::string method, std::string encoded, React::ReactPromise<std::string> promise) {
75
+ static fire_and_forget Invoke(React::ReactContext context, std::shared_ptr<Receivers> receivers, std::string method, std::string encoded, React::ReactPromise<std::string> promise) {
72
76
  try {
73
77
  auto args = Json::JsonObject::Parse(to_hstring(encoded));
74
78
  if (method == "cryptoRandom") {
@@ -86,32 +90,28 @@ struct SparkLinks {
86
90
  if (method == "authPrepare") {
87
91
  if (id.empty() || receivers->count(id) || receivers->size() >= 4) throw hresult_invalid_argument(L"Invalid or busy auth session");
88
92
  auto port = args.GetNamedNumber(L"port"); if (port < 0 || port > 65535 || port != std::floor(port)) throw hresult_invalid_argument(L"Invalid callback port");
89
- auto receiver = std::make_unique<spark::AuthLoopback>(static_cast<unsigned short>(port), to_string(args.GetNamedString(L"path")));
93
+ auto receiver = std::make_unique<spark::AuthLoopback>(static_cast<unsigned short>(port), to_string(args.GetNamedString(L"path")), [context, id](std::string url) {
94
+ context.UIDispatcher().Post([context, id, url = std::move(url)] {
95
+ context.EmitJSEvent(L"RCTDeviceEventEmitter", L"desktop", React::JSValueObject{{"type", "authRedirect"}, {"id", id}, {"url", url}});
96
+ });
97
+ });
90
98
  auto uri = receiver->RedirectURI(); receivers->emplace(id, std::move(receiver)); promise.Resolve(to_string(Json::JsonValue::CreateStringValue(to_hstring(uri)).Stringify()));
91
99
  } else if (method == "authClose") { receivers->erase(id); promise.Resolve("null"); }
92
- else if (method == "authPoll") {
93
- auto found = receivers->find(id); if (found == receivers->end()) throw hresult_invalid_argument(L"Auth session is closed");
94
- Json::JsonArray urls; for (auto const &uri : found->second->Drain()) urls.Append(Json::JsonValue::CreateStringValue(to_hstring(uri))); promise.Resolve(to_string(urls.Stringify()));
95
- } else throw hresult_invalid_argument(L"Unknown auth operation");
100
+ else throw hresult_invalid_argument(L"Unknown auth operation");
96
101
  } else if (method == "recent" || method == "noteRecent" || method == "clearRecent") {
97
102
  RecentDocuments recent;
98
- if (method == "recent") promise.Resolve(to_string(recent.Read().Stringify()));
99
- else { if (method == "noteRecent") recent.Add(args.GetNamedString(L"url")); else recent.Clear(); promise.Resolve("null"); }
100
- } else if (method == "initialURL") {
101
- int count = 0; auto arguments = CommandLineToArgvW(GetCommandLineW(), &count); hstring initial;
102
- if (arguments) {
103
- for (int i = 1; i < count; ++i) {
104
- std::wstring candidate(arguments[i]); const auto colon = candidate.find(L':');
105
- if (colon != std::wstring::npos && colon > 1 && candidate[0] != L'-') { initial = candidate; break; }
106
- }
107
- LocalFree(arguments);
108
- }
109
- promise.Resolve(initial.empty() ? "null" : to_string(Json::JsonValue::CreateStringValue(initial).Stringify()));
103
+ if (method == "recent") promise.Resolve(to_string(recent.Paths().Stringify()));
104
+ else { if (method == "noteRecent") recent.Add(args.GetNamedString(L"path")); else recent.Clear(); promise.Resolve("null"); }
105
+ } else if (method == "openPath") {
106
+ auto path = args.GetNamedString(L"path");
107
+ auto result = reinterpret_cast<INT_PTR>(ShellExecuteW(nullptr, L"open", path.c_str(), nullptr, nullptr, SW_SHOWNORMAL));
108
+ if (result <= 32) { promise.Reject(React::ReactError{result == SE_ERR_FNF || result == SE_ERR_PNF ? "E_NOT_FOUND" : result == SE_ERR_ACCESSDENIED ? "E_PERMISSION_DENIED" : "E_NATIVE", "No application opened this path"}); co_return; }
109
+ promise.Resolve("null");
110
110
  } else {
111
111
  Windows::Foundation::Uri uri(args.GetNamedString(L"url"));
112
112
  if (method == "open") {
113
113
  if (!co_await Windows::System::Launcher::LaunchUriAsync(uri)) { promise.Reject(React::ReactError{"E_OPEN_URL", "No application opened this URL"}); co_return; }
114
- promise.Resolve("true");
114
+ promise.Resolve("null");
115
115
  } else if (method == "canOpen") {
116
116
  auto status = co_await Windows::System::Launcher::QueryUriSupportAsync(uri, Windows::System::LaunchQuerySupportType::Uri);
117
117
  promise.Resolve(status == Windows::System::LaunchQuerySupportStatus::Available ? "true" : "false");
@@ -122,7 +122,7 @@ struct SparkLinks {
122
122
  }
123
123
  REACT_METHOD(call)
124
124
  void call(std::string method, std::string args, React::ReactPromise<std::string> promise) noexcept {
125
- context.UIDispatcher().Post([receivers = receivers, method = std::move(method), args = std::move(args), promise]() { Invoke(receivers, method, args, promise); });
125
+ context.UIDispatcher().Post([context = context, receivers = receivers, method = std::move(method), args = std::move(args), promise]() { Invoke(context, receivers, method, args, promise); });
126
126
  }
127
127
  };
128
128
  }
@@ -13,4 +13,5 @@ Pod::Spec.new do |s|
13
13
  s.dependency "React-Core"
14
14
  s.dependency "ReactCodegen"
15
15
  s.dependency "RNDesktopApp"
16
+ s.osx.frameworks = "AppKit", "Carbon"
16
17
  end
@@ -16,7 +16,7 @@ RCT_EXPORT_MODULE(NativeDesktopShortcuts)
16
16
  for (NSDictionary *existing in self.shortcuts.allValues) {
17
17
  if ([existing[@"key"] isEqual:args[@"key"]] && [existing[@"modifiers"] isEqual:args[@"modifiers"]] &&
18
18
  ((!existing[@"windowId"] && !args[@"windowId"]) || [existing[@"windowId"] isEqual:args[@"windowId"]])) {
19
- reject(@"E_SHORTCUT_CONFLICT", @"Shortcut is already registered in this scope", nil); return;
19
+ reject(@"E_BUSY", @"Shortcut is already registered in this scope", nil); return;
20
20
  }
21
21
  }
22
22
  self.shortcuts[args[@"id"]] = args;
@@ -0,0 +1,9 @@
1
+ #import <React/RCTEventEmitter.h>
2
+ #import <RNDesktopShortcutsSpec/RNDesktopShortcutsSpec.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface RNKeyboardManager : RCTEventEmitter <NativeKeyboardManagerSpec>
7
+ @end
8
+
9
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,307 @@
1
+ #import "RNKeyboardManager.h"
2
+
3
+ #import <React/RCTBridgeModule.h>
4
+ #import <TargetConditionals.h>
5
+ #include <cmath>
6
+ #include <atomic>
7
+
8
+ #if TARGET_OS_OSX
9
+ #import <AppKit/AppKit.h>
10
+ #import <IOKit/hidsystem/IOLLEvent.h>
11
+ #import <IOKit/hidsystem/ev_keymap.h>
12
+ #endif
13
+
14
+ static NSInteger const RNKeyboardMediaPlayPause = 10001;
15
+ static NSInteger const RNKeyboardMediaNext = 10002;
16
+ static NSInteger const RNKeyboardMediaPrevious = 10003;
17
+
18
+ #if TARGET_OS_OSX
19
+ static NSUInteger const RNKeyboardModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl | NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagFunction | NSEventModifierFlagCapsLock;
20
+ @interface SparkKeyboardRule : NSObject
21
+ @property (nonatomic, copy) NSString *key;
22
+ @property (nonatomic) NSUInteger modifiers;
23
+ @property (nonatomic) BOOL allowExtraModifiers;
24
+ @property (nonatomic) BOOL repeat;
25
+ @property (nonatomic, copy) NSSet<NSString *> *windowIds;
26
+ @property (nonatomic, copy) NSSet<NSString *> *excludedWindowIds;
27
+ @end
28
+ @implementation SparkKeyboardRule @end
29
+
30
+ static BOOL SparkKeyboardBoolean(id value) { return [value isKindOfClass:NSNumber.class] && CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID(); }
31
+
32
+ // Stripping modifiers renders navigation and function keys as C0 control characters (Right is
33
+ // U+001D, Backspace U+0008), which no accelerator can name; accelerators parse AppKit's
34
+ // private-use code and U+007F instead. Tab and Return are also C0 but already spell their
35
+ // accelerators, so only keys whose unmodified character identifies them are substituted.
36
+ static NSString *SparkKeyboardKeyString(NSEvent *event) {
37
+ NSString *key = [event charactersByApplyingModifiers:0] ?: @"";
38
+ if (key.length == 1 && [key characterAtIndex:0] < 0x20) {
39
+ NSString *unmodified = event.charactersIgnoringModifiers;
40
+ if (unmodified.length == 1) {
41
+ unichar code = [unmodified characterAtIndex:0];
42
+ if ((code >= 0xF700 && code <= 0xF747) || code == 0x7F) return unmodified.lowercaseString;
43
+ }
44
+ }
45
+ return key.lowercaseString;
46
+ }
47
+ static NSSet<NSString *> *SparkKeyboardWindows(id value, BOOL *valid) {
48
+ if (!value) return nil;
49
+ if (![value isKindOfClass:NSArray.class]) { *valid = NO; return nil; }
50
+ for (id identifier in value) if (![identifier isKindOfClass:NSString.class] || ![identifier length]) { *valid = NO; return nil; }
51
+ return [NSSet setWithArray:value];
52
+ }
53
+ #endif
54
+
55
+ @implementation RNKeyboardManager {
56
+ std::atomic<bool> _hasListeners;
57
+ #if TARGET_OS_OSX
58
+ id _localEventMonitor;
59
+ NSMutableDictionary<NSString *, NSArray<SparkKeyboardRule *> *> *_consumptionOwners;
60
+ NSMutableSet<NSString *> *_captureOwners;
61
+ NSDictionary<NSString *, NSArray<SparkKeyboardRule *> *> *_rulesByKey;
62
+ NSMutableDictionary<NSNumber *, NSNumber *> *_consumedKeys;
63
+ #endif
64
+ }
65
+
66
+ RCT_EXPORT_MODULE(NativeKeyboardManager)
67
+
68
+ + (BOOL)requiresMainQueueSetup
69
+ {
70
+ return YES;
71
+ }
72
+
73
+ - (instancetype)init
74
+ {
75
+ if (self = [super init]) {
76
+ _hasListeners.store(false, std::memory_order_relaxed);
77
+ #if TARGET_OS_OSX
78
+ _consumptionOwners = [NSMutableDictionary new];
79
+ _captureOwners = [NSMutableSet new];
80
+ _rulesByKey = @{};
81
+ _consumedKeys = [NSMutableDictionary new];
82
+ [NSNotificationCenter.defaultCenter addObserver:self
83
+ selector:@selector(applicationWillTerminate:)
84
+ name:NSApplicationWillTerminateNotification
85
+ object:nil];
86
+ #endif
87
+ }
88
+ return self;
89
+ }
90
+
91
+ - (void)dealloc
92
+ {
93
+ #if TARGET_OS_OSX
94
+ id monitor = _localEventMonitor;
95
+ if (monitor) {
96
+ if (NSThread.isMainThread) [NSEvent removeMonitor:monitor];
97
+ else dispatch_async(dispatch_get_main_queue(), ^{ [NSEvent removeMonitor:monitor]; });
98
+ }
99
+ [NSNotificationCenter.defaultCenter removeObserver:self];
100
+ #endif
101
+ }
102
+
103
+ - (NSArray<NSString *> *)supportedEvents
104
+ {
105
+ return @[@"onKeyDown", @"onKeyUp"];
106
+ }
107
+
108
+ - (void)startObserving
109
+ {
110
+ _hasListeners.store(true, std::memory_order_relaxed);
111
+ }
112
+
113
+ - (void)stopObserving
114
+ {
115
+ _hasListeners.store(false, std::memory_order_relaxed);
116
+ #if TARGET_OS_OSX
117
+ if (NSThread.isMainThread) [self stopMonitoringInternal];
118
+ else dispatch_async(dispatch_get_main_queue(), ^{ [self stopMonitoringInternal]; });
119
+ #endif
120
+ }
121
+
122
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
123
+ (const facebook::react::ObjCTurboModule::InitParams &)params
124
+ {
125
+ return std::make_shared<facebook::react::NativeKeyboardManagerSpecJSI>(params);
126
+ }
127
+
128
+ - (void)startMonitoringKeyboard:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
129
+ {
130
+ #if TARGET_OS_OSX
131
+ dispatch_async(dispatch_get_main_queue(), ^{
132
+ [self startMonitoringInternal];
133
+ if (self->_localEventMonitor) resolve(@YES); else reject(@"E_UNAVAILABLE", @"Keyboard event monitor could not start", nil);
134
+ });
135
+ #else
136
+ reject(@"E_UNSUPPORTED_PLATFORM", @"Keyboard monitoring requires macOS", nil);
137
+ #endif
138
+ }
139
+
140
+ - (void)stopMonitoringKeyboard:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
141
+ {
142
+ #if TARGET_OS_OSX
143
+ dispatch_async(dispatch_get_main_queue(), ^{
144
+ [self stopMonitoringInternal];
145
+ resolve(@YES);
146
+ });
147
+ #else
148
+ resolve(@NO);
149
+ #endif
150
+ }
151
+
152
+ - (void)setConsumption:(NSString *)ownerId rulesJson:(NSString *)rulesJson capture:(BOOL)capture resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
153
+ {
154
+ #if TARGET_OS_OSX
155
+ id values = [NSJSONSerialization JSONObjectWithData:[rulesJson dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingFragmentsAllowed error:nil];
156
+ if (!ownerId.length || ![values isKindOfClass:NSArray.class]) { reject(@"E_INVALID_ARGUMENT", @"Expected an owner and keyboard consumption rules", nil); return; }
157
+ NSMutableArray<SparkKeyboardRule *> *rules = [NSMutableArray new];
158
+ for (id value in values) {
159
+ if (![value isKindOfClass:NSDictionary.class]) { reject(@"E_INVALID_ARGUMENT", @"Invalid keyboard consumption rule", nil); return; }
160
+ id key = value[@"key"], modifiers = value[@"modifiers"];
161
+ BOOL valid = [key isKindOfClass:NSString.class] && [key length] && [modifiers isKindOfClass:NSNumber.class] && !SparkKeyboardBoolean(modifiers);
162
+ double bits = valid ? [modifiers doubleValue] : -1;
163
+ valid = valid && std::isfinite(bits) && bits >= 0 && bits <= RNKeyboardModifierMask && std::floor(bits) == bits && (((NSUInteger)bits & ~RNKeyboardModifierMask) == 0);
164
+ valid = valid && SparkKeyboardBoolean(value[@"allowExtraModifiers"]) && SparkKeyboardBoolean(value[@"repeat"]);
165
+ SparkKeyboardRule *rule = [SparkKeyboardRule new];
166
+ rule.windowIds = SparkKeyboardWindows(value[@"windowIds"], &valid);
167
+ rule.excludedWindowIds = SparkKeyboardWindows(value[@"excludedWindowIds"], &valid);
168
+ if (!valid) { reject(@"E_INVALID_ARGUMENT", @"Invalid keyboard consumption rule", nil); return; }
169
+ rule.key = [key lowercaseString]; rule.modifiers = (NSUInteger)bits;
170
+ rule.allowExtraModifiers = [value[@"allowExtraModifiers"] boolValue]; rule.repeat = [value[@"repeat"] boolValue];
171
+ [rules addObject:rule];
172
+ }
173
+ dispatch_async(dispatch_get_main_queue(), ^{
174
+ if (rules.count) self->_consumptionOwners[ownerId] = rules; else [self->_consumptionOwners removeObjectForKey:ownerId];
175
+ if (capture) [self->_captureOwners addObject:ownerId]; else [self->_captureOwners removeObject:ownerId];
176
+ NSMutableDictionary<NSString *, NSMutableArray<SparkKeyboardRule *> *> *byKey = [NSMutableDictionary new];
177
+ for (NSArray<SparkKeyboardRule *> *ownerRules in self->_consumptionOwners.allValues) for (SparkKeyboardRule *rule in ownerRules) {
178
+ if (!byKey[rule.key]) byKey[rule.key] = [NSMutableArray new];
179
+ [byKey[rule.key] addObject:rule];
180
+ }
181
+ self->_rulesByKey = byKey;
182
+ resolve(@YES);
183
+ });
184
+ #else
185
+ reject(@"E_UNSUPPORTED_PLATFORM", @"Keyboard consumption requires macOS", nil);
186
+ #endif
187
+ }
188
+
189
+ - (void)invalidate
190
+ {
191
+ _hasListeners.store(false, std::memory_order_relaxed);
192
+ #if TARGET_OS_OSX
193
+ dispatch_async(dispatch_get_main_queue(), ^{
194
+ [self stopMonitoringInternal];
195
+ [self->_consumptionOwners removeAllObjects]; [self->_captureOwners removeAllObjects]; self->_rulesByKey = @{};
196
+ });
197
+ #endif
198
+ [super invalidate];
199
+ }
200
+
201
+ #if TARGET_OS_OSX
202
+ - (void)applicationWillTerminate:(NSNotification *)notification
203
+ {
204
+ [self stopMonitoringInternal];
205
+ }
206
+
207
+ - (void)startMonitoringInternal
208
+ {
209
+ [self stopMonitoringInternal];
210
+ __weak RNKeyboardManager *weakSelf = self;
211
+ NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskSystemDefined;
212
+ _localEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent *(NSEvent *event) {
213
+ RNKeyboardManager *strongSelf = weakSelf;
214
+ if (!strongSelf) {
215
+ return event;
216
+ }
217
+ return [strongSelf handleKeyboardEvent:event];
218
+ }];
219
+ }
220
+
221
+ - (void)stopMonitoringInternal
222
+ {
223
+ if (_localEventMonitor) {
224
+ [NSEvent removeMonitor:_localEventMonitor];
225
+ _localEventMonitor = nil;
226
+ }
227
+ [_consumedKeys removeAllObjects];
228
+ }
229
+
230
+ - (NSEvent *)handleKeyboardEvent:(NSEvent *)event
231
+ {
232
+ if (event.type == NSEventTypeSystemDefined) {
233
+ return [self handleSystemDefinedEvent:event];
234
+ }
235
+
236
+ if (event.type != NSEventTypeKeyDown && event.type != NSEventTypeKeyUp) {
237
+ return event;
238
+ }
239
+
240
+ NSString *eventName = event.type == NSEventTypeKeyDown ? @"onKeyDown" : @"onKeyUp";
241
+ BOOL handled = [self emitKeyboardEvent:eventName keyCode:event.keyCode key:SparkKeyboardKeyString(event) modifiers:event.modifierFlags window:event.window repeated:event.isARepeat];
242
+ return handled ? nil : event;
243
+ }
244
+
245
+ - (NSEvent *)handleSystemDefinedEvent:(NSEvent *)event
246
+ {
247
+ if (event.subtype != NX_SUBTYPE_AUX_CONTROL_BUTTONS) {
248
+ return event;
249
+ }
250
+ NSInteger data = event.data1;
251
+ NSInteger keyCode = (data & 0xFFFF0000) >> 16;
252
+ NSInteger keyFlags = data & 0x0000FFFF;
253
+ NSInteger keyState = (keyFlags & 0xFF00) >> 8;
254
+ NSNumber *mappedKeyCode = [self mappedMediaKeyCode:keyCode];
255
+ if (!mappedKeyCode) {
256
+ return event;
257
+ }
258
+ NSString *eventName = keyState == NX_KEYDOWN ? @"onKeyDown" : @"onKeyUp";
259
+ BOOL handled = [self emitKeyboardEvent:eventName keyCode:mappedKeyCode.integerValue key:@{@10001: @"MediaPlayPause", @10002: @"MediaNext", @10003: @"MediaPrevious"}[mappedKeyCode] modifiers:event.modifierFlags window:event.window repeated:(keyFlags & 1) != 0];
260
+ return handled ? nil : event;
261
+ }
262
+
263
+ - (NSNumber *)mappedMediaKeyCode:(NSInteger)keyCode
264
+ {
265
+ switch (keyCode) {
266
+ case NX_KEYTYPE_PLAY:
267
+ return @(RNKeyboardMediaPlayPause);
268
+ case NX_KEYTYPE_FAST:
269
+ case NX_KEYTYPE_NEXT:
270
+ return @(RNKeyboardMediaNext);
271
+ case NX_KEYTYPE_REWIND:
272
+ case NX_KEYTYPE_PREVIOUS:
273
+ return @(RNKeyboardMediaPrevious);
274
+ default:
275
+ return nil;
276
+ }
277
+ }
278
+
279
+ - (BOOL)emitKeyboardEvent:(NSString *)eventName keyCode:(NSInteger)keyCode key:(NSString *)key modifiers:(NSUInteger)modifiers window:(NSWindow *)window repeated:(BOOL)repeated
280
+ {
281
+ NSString *identifier = (window ?: NSApp.keyWindow).identifier;
282
+ NSString *windowId = [identifier hasPrefix:@"spark."] ? [identifier substringFromIndex:6] : identifier;
283
+ BOOL down = [eventName isEqual:@"onKeyDown"];
284
+ NSNumber *held = _consumedKeys[@(keyCode)];
285
+ BOOL captured = _captureOwners.count > 0 || (!down && held.boolValue);
286
+ BOOL consumed = captured || (!down && held != nil);
287
+ if (down && !consumed) {
288
+ NSString *normalizedKey = key.lowercaseString;
289
+ for (SparkKeyboardRule *rule in _rulesByKey[normalizedKey]) {
290
+ if ((repeated && !rule.repeat) || (rule.windowIds && (!windowId || ![rule.windowIds containsObject:windowId])) || (windowId && [rule.excludedWindowIds containsObject:windowId])) continue;
291
+ NSUInteger active = modifiers & RNKeyboardModifierMask;
292
+ if (!(rule.modifiers & NSEventModifierFlagCapsLock)) active &= ~NSEventModifierFlagCapsLock;
293
+ if (!(rule.modifiers & NSEventModifierFlagFunction) && key.length == 1 && [key characterAtIndex:0] >= 0xf700 && [key characterAtIndex:0] <= 0xf747) active &= ~NSEventModifierFlagFunction;
294
+ if (rule.allowExtraModifiers ? (active & rule.modifiers) == rule.modifiers : active == rule.modifiers) { consumed = YES; break; }
295
+ }
296
+ }
297
+ if (down && consumed) _consumedKeys[@(keyCode)] = @(captured || held.boolValue);
298
+ if (!down) [_consumedKeys removeObjectForKey:@(keyCode)];
299
+ if (_hasListeners.load(std::memory_order_relaxed)) [self sendEventWithName:eventName body:@{
300
+ @"keyCode": @(keyCode), @"key": key, @"modifiers": @(modifiers), @"windowId": windowId ?: (id)NSNull.null,
301
+ @"repeated": @(repeated), @"consumed": @(consumed), @"captured": @(captured),
302
+ }];
303
+ return consumed;
304
+ }
305
+ #endif
306
+
307
+ @end