@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
@@ -1,11 +1,12 @@
1
1
  #import "SparkDesktop.h"
2
+ #import "SparkDesktopError.h"
3
+ #import "SparkQuitCoordinator.h"
2
4
  #import <CommonCrypto/CommonDigest.h>
3
5
  #import <sys/file.h>
4
6
  #import <fcntl.h>
5
7
  #import <unistd.h>
6
8
  NSString * const SparkDesktopEvent = @"SparkDesktopEvent";
7
- static BOOL quitGuard = NO;
8
- static BOOL quitting = NO;
9
+ static NSMutableDictionary<NSString *, NSDictionary *> *jsQuitReplies;
9
10
  static NSUInteger quitGeneration = 0;
10
11
  static NSMutableArray *pendingURLs;
11
12
  static NSString *initialURL;
@@ -21,13 +22,7 @@ NSDictionary *SparkArgs(NSString *json) {
21
22
  }
22
23
  void SparkInvalid(RCTPromiseRejectBlock reject, NSString *message) { reject(@"E_INVALID_ARGUMENT", message, nil); }
23
24
  void SparkReject(RCTPromiseRejectBlock reject, NSError *error) {
24
- NSString *code = @"E_IO";
25
- if ([error.domain isEqualToString:NSCocoaErrorDomain]) {
26
- if (error.code == NSFileReadNoSuchFileError || error.code == NSFileNoSuchFileError) code = @"E_NOT_FOUND";
27
- if (error.code == NSFileReadNoPermissionError || error.code == NSFileWriteNoPermissionError) code = @"E_PERMISSION";
28
- if (error.code == NSFileWriteFileExistsError) code = @"E_EXISTS";
29
- }
30
- reject(code, error.localizedDescription ?: @"Native operation failed", error);
25
+ reject(SparkDesktopErrorCode(error), error.localizedDescription ?: @"Native operation failed", error);
31
26
  }
32
27
  NSDictionary *SparkContext(void) {
33
28
  static NSDictionary *context;
@@ -36,7 +31,8 @@ NSDictionary *SparkContext(void) {
36
31
  NSBundle *bundle = NSBundle.mainBundle;
37
32
  NSString *runtimePath = [bundle pathForResource:@"spark-runtime" ofType:@"json"];
38
33
  NSData *data = runtimePath ? [NSData dataWithContentsOfFile:runtimePath] : nil;
39
- NSDictionary *runtime = data ? [NSJSONSerialization JSONObjectWithData:data options:0 error:nil] : @{};
34
+ id rawRuntime = data ? [NSJSONSerialization JSONObjectWithData:data options:0 error:nil] : nil;
35
+ NSDictionary *runtime = [rawRuntime isKindOfClass:NSDictionary.class] ? rawRuntime : @{};
40
36
  NSString *project = [bundle objectForInfoDictionaryKey:@"SparkProjectIdentifier"] ?: bundle.bundleIdentifier;
41
37
  NSString *version = [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"] ?: @"0.0.0";
42
38
  NSString *name = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"] ?: [bundle objectForInfoDictionaryKey:@"CFBundleName"];
@@ -87,31 +83,33 @@ void SparkOpenURLs(NSArray<NSURL *> *urls) {
87
83
  }
88
84
  }
89
85
  NSArray *SparkPendingURLs(void) { return [pendingURLs copy] ?: @[]; }
90
- void SparkSetQuitGuard(BOOL value) {
91
- if (value != quitGuard) {
92
- if (value) [NSProcessInfo.processInfo disableSuddenTermination];
93
- else [NSProcessInfo.processInfo enableSuddenTermination];
94
- }
95
- quitGuard = value;
96
- if (!value && quitting) { quitting = NO; [NSApp replyToApplicationShouldTerminate:NO]; }
97
- }
98
- void SparkReplyQuit(BOOL allow, NSUInteger generation) {
99
- if (!quitting || generation != quitGeneration) return;
100
- quitting = NO;
101
- [NSApp replyToApplicationShouldTerminate:allow];
86
+ static SparkQuitCoordinator *SparkQuitCoordinatorInstance(void) {
87
+ static SparkQuitCoordinator *coordinator;
88
+ static dispatch_once_t once;
89
+ dispatch_once(&once, ^{ coordinator = [[SparkQuitCoordinator alloc] initWithReply:^(BOOL allow) {
90
+ [NSApp replyToApplicationShouldTerminate:allow];
91
+ } timeout:30]; });
92
+ return coordinator;
102
93
  }
103
- NSApplicationTerminateReply SparkShouldQuit(void) {
104
- if (!quitGuard) return NSTerminateNow;
105
- if (quitting) return NSTerminateLater;
106
- quitting = YES;
107
- NSUInteger generation = ++quitGeneration;
108
- // Emit after returning Later so synchronous JS replies cannot race AppKit.
109
- dispatch_async(dispatch_get_main_queue(), ^{ SparkEmit(@{ @"type": @"beforeQuit", @"requestId": @(generation) }); });
110
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
111
- if (quitting && generation == quitGeneration) SparkReplyQuit(NO, generation);
94
+ void SparkRegisterQuitHandler(NSString *identifier, SparkQuitHandler handler) { [SparkQuitCoordinatorInstance() registerHandler:identifier handler:handler]; }
95
+ void SparkRemoveQuitHandler(NSString *identifier) { [SparkQuitCoordinatorInstance() removeHandler:identifier]; }
96
+ void SparkSetQuitGuard(NSString *identifier, BOOL value) {
97
+ if (!jsQuitReplies) jsQuitReplies = [NSMutableDictionary new];
98
+ NSString *key = [@"spark.javascript." stringByAppendingString:identifier];
99
+ if (value) SparkRegisterQuitHandler(key, ^(SparkQuitReply reply) {
100
+ NSUInteger generation = ++quitGeneration;
101
+ jsQuitReplies[identifier] = @{ @"generation": @(generation), @"reply": [reply copy] };
102
+ SparkEmit(@{ @"type": @"beforeQuit", @"guardId": identifier, @"requestId": @(generation) });
112
103
  });
113
- return NSTerminateLater;
104
+ else { [jsQuitReplies removeObjectForKey:identifier]; SparkRemoveQuitHandler(key); }
105
+ }
106
+ void SparkReplyQuit(NSString *identifier, BOOL allow, NSUInteger generation) {
107
+ NSDictionary *pending = jsQuitReplies[identifier];
108
+ if (!pending || [pending[@"generation"] unsignedIntegerValue] != generation) return;
109
+ SparkQuitReply reply = pending[@"reply"]; [jsQuitReplies removeObjectForKey:identifier]; reply(allow);
114
110
  }
111
+ void SparkObserveQuitDecision(SparkQuitReply observer) { [SparkQuitCoordinatorInstance() observeDecision:observer]; }
112
+ NSApplicationTerminateReply SparkShouldQuit(void) { return [SparkQuitCoordinatorInstance() requestQuit]; }
115
113
 
116
114
  BOOL SparkAcquireInstance(void) {
117
115
  static int lockFD = -1;
@@ -127,7 +125,9 @@ BOOL SparkAcquireInstance(void) {
127
125
  }
128
126
  observer = [NSDistributedNotificationCenter.defaultCenter addObserverForName:name object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification *note) {
129
127
  [NSApp activateIgnoringOtherApps:YES];
130
- for (NSWindow *window in NSApp.windows) if ([window.identifier isEqual:@"spark.main"]) [window makeKeyAndOrderFront:nil];
128
+ BOOL visible = NO;
129
+ for (NSWindow *window in NSApp.orderedWindows) if (window.isVisible && ![window isKindOfClass:NSPanel.class]) { visible = YES; break; }
130
+ [NSApp.delegate applicationShouldHandleReopen:NSApp hasVisibleWindows:visible];
131
131
  SparkEmit(@{ @"type": @"secondInstance", @"arguments": note.userInfo[@"arguments"] ?: @[] });
132
132
  }];
133
133
  return YES;
@@ -0,0 +1,20 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <errno.h>
3
+
4
+ static inline NSString *SparkDesktopErrorCode(NSError *error) {
5
+ NSString *code = @"E_IO";
6
+ if ([error.domain isEqualToString:NSCocoaErrorDomain]) {
7
+ if (error.code == NSFileReadNoSuchFileError || error.code == NSFileNoSuchFileError) return @"E_NOT_FOUND";
8
+ if (error.code == NSFileReadNoPermissionError || error.code == NSFileWriteNoPermissionError) return @"E_PERMISSION";
9
+ if (error.code == NSFileWriteFileExistsError) return @"E_EXISTS";
10
+ if (error.code == NSFileWriteInvalidFileNameError) return @"E_INVALID_ARGUMENT";
11
+ }
12
+ if ([error.domain isEqualToString:NSPOSIXErrorDomain]) {
13
+ if (error.code == ENOENT || error.code == ENOTDIR) return @"E_NOT_FOUND";
14
+ if (error.code == EACCES || error.code == EPERM) return @"E_PERMISSION";
15
+ if (error.code == EEXIST) return @"E_EXISTS";
16
+ if (error.code == EINVAL) return @"E_INVALID_ARGUMENT";
17
+ if (error.code == ENOTEMPTY) return @"E_NOT_EMPTY";
18
+ }
19
+ return code;
20
+ }
@@ -0,0 +1,26 @@
1
+ #import <AppKit/AppKit.h>
2
+
3
+ // Startup extensions own package-specific native chrome, never application dispatch.
4
+ // Only explicitly configured classes are instantiated, on the main thread.
5
+ @protocol SparkStartupPlugin <NSObject>
6
+ @optional
7
+ - (void)prepareApplication;
8
+ - (void)prepareMainWindow:(NSWindow *)window;
9
+ // At most one configured plugin may own root attachment. Call installRoot when
10
+ // attaching to the normal content view; otherwise retain the root in your view.
11
+ - (void)attachRootView:(NSView *)root toWindow:(NSWindow *)window installRoot:(void (^)(void))installRoot;
12
+ @end
13
+
14
+ FOUNDATION_EXPORT NSDictionary *SparkLifecycleConfiguration(void);
15
+ FOUNDATION_EXPORT void SparkPrepareApplication(void);
16
+ FOUNDATION_EXPORT void SparkPrepareMainWindow(NSWindow *window);
17
+ FOUNDATION_EXPORT void SparkAttachMainRootView(NSView *root, NSWindow *window, void (^installRoot)(void));
18
+ FOUNDATION_EXPORT void SparkRecordMainWindowShown(void);
19
+ FOUNDATION_EXPORT NSDictionary *SparkStartupTiming(void);
20
+
21
+ typedef void (^SparkQuitReply)(BOOL allow);
22
+ typedef void (^SparkQuitHandler)(SparkQuitReply reply);
23
+ // Registration, removal and replies are main-thread confined. No handlers means
24
+ // immediate quit. All registered handlers must approve; removal/timeout cancels.
25
+ FOUNDATION_EXPORT void SparkRegisterQuitHandler(NSString *identifier, SparkQuitHandler handler);
26
+ FOUNDATION_EXPORT void SparkRemoveQuitHandler(NSString *identifier);
@@ -0,0 +1,49 @@
1
+ #import "SparkLifecycle.h"
2
+ #import <QuartzCore/QuartzCore.h>
3
+
4
+ static NSArray<id<SparkStartupPlugin>> *plugins;
5
+ static double mainWindowShown = 0;
6
+ static double mainRootAttached = 0;
7
+ NSDictionary *SparkLifecycleConfiguration(void) {
8
+ return [NSBundle.mainBundle objectForInfoDictionaryKey:@"SparkLifecycleConfiguration"] ?: @{};
9
+ }
10
+ void SparkPrepareApplication(void) {
11
+ NSCAssert(NSThread.isMainThread, @"Startup is main-thread confined");
12
+ NSString *appearance = SparkLifecycleConfiguration()[@"appearance"];
13
+ if (appearance) NSApp.appearance = [appearance isEqual:@"dark"] ? [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua] : [appearance isEqual:@"light"] ? [NSAppearance appearanceNamed:NSAppearanceNameAqua] : nil;
14
+ NSMutableArray *instances = [NSMutableArray new];
15
+ NSUInteger rootOwners = 0;
16
+ for (NSString *name in SparkLifecycleConfiguration()[@"plugins"] ?: @[]) {
17
+ Class type = NSClassFromString(name);
18
+ if (!type || ![type conformsToProtocol:@protocol(SparkStartupPlugin)])
19
+ [NSException raise:NSInvalidArgumentException format:@"Startup plugin %@ is not linked or does not implement SparkStartupPlugin", name];
20
+ id<SparkStartupPlugin> plugin = [type new];
21
+ if ([plugin respondsToSelector:@selector(attachRootView:toWindow:installRoot:)]) rootOwners++;
22
+ [instances addObject:plugin];
23
+ }
24
+ if (rootOwners > 1) [NSException raise:NSInvalidArgumentException format:@"Only one startup plugin may own main root attachment"];
25
+ plugins = instances;
26
+ for (id<SparkStartupPlugin> plugin in plugins)
27
+ if ([plugin respondsToSelector:@selector(prepareApplication)]) [plugin prepareApplication];
28
+ }
29
+ void SparkPrepareMainWindow(NSWindow *window) {
30
+ for (id<SparkStartupPlugin> plugin in plugins)
31
+ if ([plugin respondsToSelector:@selector(prepareMainWindow:)]) [plugin prepareMainWindow:window];
32
+ }
33
+ void SparkAttachMainRootView(NSView *root, NSWindow *window, void (^installRoot)(void)) {
34
+ BOOL attached = NO;
35
+ for (id<SparkStartupPlugin> plugin in plugins) {
36
+ if ([plugin respondsToSelector:@selector(attachRootView:toWindow:installRoot:)]) {
37
+ [plugin attachRootView:root toWindow:window installRoot:installRoot]; attached = YES;
38
+ }
39
+ }
40
+ if (!attached) installRoot();
41
+ mainRootAttached = CACurrentMediaTime() * 1000;
42
+ }
43
+ void SparkRecordMainWindowShown(void) { if (!mainWindowShown) mainWindowShown = CACurrentMediaTime() * 1000; }
44
+ NSDictionary *SparkStartupTiming(void) {
45
+ NSMutableDictionary *result = [NSMutableDictionary new];
46
+ if (mainWindowShown) result[@"mainWindowFirstVisibleTime"] = @(mainWindowShown);
47
+ if (mainRootAttached) result[@"mainWindowReactRootAttachedTime"] = @(mainRootAttached);
48
+ return result;
49
+ }
@@ -0,0 +1,9 @@
1
+ #import "SparkLifecycle.h"
2
+ @interface SparkQuitCoordinator : NSObject
3
+ - (instancetype)initWithReply:(SparkQuitReply)reply timeout:(NSTimeInterval)timeout;
4
+ - (void)registerHandler:(NSString *)identifier handler:(SparkQuitHandler)handler;
5
+ - (void)removeHandler:(NSString *)identifier;
6
+ // Observe the current pending attempt, or the next request when none is pending.
7
+ - (void)observeDecision:(SparkQuitReply)observer;
8
+ - (NSApplicationTerminateReply)requestQuit;
9
+ @end
@@ -0,0 +1,70 @@
1
+ #import "SparkQuitCoordinator.h"
2
+ @implementation SparkQuitCoordinator {
3
+ NSMutableDictionary<NSString *, SparkQuitHandler> *_handlers;
4
+ NSMutableSet<NSString *> *_pending;
5
+ NSMutableArray<SparkQuitReply> *_observers;
6
+ SparkQuitReply _reply;
7
+ NSTimeInterval _timeout;
8
+ NSUInteger _generation;
9
+ }
10
+ - (instancetype)initWithReply:(SparkQuitReply)reply timeout:(NSTimeInterval)timeout {
11
+ if ((self = [super init])) { _handlers = [NSMutableDictionary new]; _observers = [NSMutableArray new]; _reply = [reply copy]; _timeout = timeout; }
12
+ return self;
13
+ }
14
+ - (void)observeDecision:(SparkQuitReply)observer {
15
+ NSAssert(NSThread.isMainThread, @"Quit observers are main-thread confined");
16
+ [_observers addObject:[observer copy]];
17
+ }
18
+ - (void)notifyObservers:(BOOL)allow {
19
+ NSArray<SparkQuitReply> *observers = [_observers copy]; [_observers removeAllObjects];
20
+ for (SparkQuitReply observer in observers) observer(allow);
21
+ }
22
+ - (void)finish:(BOOL)allow generation:(NSUInteger)generation {
23
+ if (!_pending || _generation != generation) return;
24
+ _pending = nil; [self notifyObservers:allow]; _reply(allow);
25
+ }
26
+ - (void)registerHandler:(NSString *)identifier handler:(SparkQuitHandler)handler {
27
+ NSAssert(NSThread.isMainThread, @"Quit handlers are main-thread confined");
28
+ if (_handlers[identifier]) [self removeHandler:identifier];
29
+ _handlers[identifier] = [handler copy];
30
+ [NSProcessInfo.processInfo disableSuddenTermination];
31
+ // A newly installed guard has not approved the current attempt.
32
+ [self finish:NO generation:_generation];
33
+ }
34
+ - (void)removeHandler:(NSString *)identifier {
35
+ NSAssert(NSThread.isMainThread, @"Quit handlers are main-thread confined");
36
+ if (!_handlers[identifier]) return;
37
+ [_handlers removeObjectForKey:identifier];
38
+ [NSProcessInfo.processInfo enableSuddenTermination];
39
+ [self finish:NO generation:_generation];
40
+ }
41
+ - (NSApplicationTerminateReply)requestQuit {
42
+ NSAssert(NSThread.isMainThread, @"Quit requests are main-thread confined");
43
+ if (_pending) return NSTerminateLater;
44
+ if (!_handlers.count) { [self notifyObservers:YES]; return NSTerminateNow; }
45
+ NSDictionary<NSString *, SparkQuitHandler> *handlers = [_handlers copy];
46
+ _pending = [NSMutableSet setWithArray:handlers.allKeys];
47
+ NSUInteger generation = ++_generation;
48
+ // Return Later before invoking even synchronous native handlers.
49
+ dispatch_async(dispatch_get_main_queue(), ^{
50
+ if (!self->_pending || self->_generation != generation) return;
51
+ for (NSString *identifier in handlers) {
52
+ if (!self->_pending || self->_generation != generation) break;
53
+ handlers[identifier](^(BOOL allow) {
54
+ NSAssert(NSThread.isMainThread, @"Quit replies are main-thread confined");
55
+ if (self->_generation != generation || ![self->_pending containsObject:identifier]) return;
56
+ if (!allow) { [self finish:NO generation:generation]; return; }
57
+ [self->_pending removeObject:identifier];
58
+ if (!self->_pending.count) [self finish:YES generation:generation];
59
+ });
60
+ }
61
+ });
62
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
63
+ [self finish:NO generation:generation];
64
+ });
65
+ return NSTerminateLater;
66
+ }
67
+ - (void)dealloc {
68
+ for (__unused NSString *identifier in _handlers) [NSProcessInfo.processInfo enableSuddenTermination];
69
+ }
70
+ @end
@@ -59,14 +59,17 @@ void SparkApplyWindowOptions(NSWindow *window, NSDictionary *patch) {
59
59
  view.wantsLayer = YES;
60
60
  view.layer.backgroundColor = material ? NSColor.clearColor.CGColor : background.CGColor;
61
61
  }
62
- window.contentMinSize = NSMakeSize(options[@"minWidth"] ? [options[@"minWidth"] doubleValue] : 100, options[@"minHeight"] ? [options[@"minHeight"] doubleValue] : 100);
63
- window.contentMaxSize = NSMakeSize(options[@"maxWidth"] ? [options[@"maxWidth"] doubleValue] : 20000, options[@"maxHeight"] ? [options[@"maxHeight"] doubleValue] : 20000);
64
- NSSize size = [window contentRectForFrameRect:window.frame].size;
62
+ window.minSize = NSMakeSize(options[@"minWidth"] ? [options[@"minWidth"] doubleValue] : 100, options[@"minHeight"] ? [options[@"minHeight"] doubleValue] : 100);
63
+ window.maxSize = NSMakeSize(options[@"maxWidth"] ? [options[@"maxWidth"] doubleValue] : 20000, options[@"maxHeight"] ? [options[@"maxHeight"] doubleValue] : 20000);
64
+ NSSize size = window.frame.size;
65
65
  if (patch[@"width"]) size.width = [patch[@"width"] doubleValue];
66
66
  if (patch[@"height"]) size.height = [patch[@"height"] doubleValue];
67
- size.width = MIN(MAX(size.width, window.contentMinSize.width), window.contentMaxSize.width);
68
- size.height = MIN(MAX(size.height, window.contentMinSize.height), window.contentMaxSize.height);
69
- [window setContentSize:size];
67
+ size.width = MIN(MAX(size.width, window.minSize.width), window.maxSize.width);
68
+ size.height = MIN(MAX(size.height, window.minSize.height), window.maxSize.height);
69
+ NSRect frame = window.frame;
70
+ frame.origin.y += frame.size.height - size.height;
71
+ frame.size = size;
72
+ [window setFrame:frame display:NO];
70
73
  if (options[@"title"]) window.title = options[@"title"];
71
74
  }
72
75
  void SparkRestoreWindow(NSWindow *window, NSString *key, NSDictionary *options) {
@@ -0,0 +1,55 @@
1
+ #import "../SparkDesktop.h"
2
+
3
+ #import <TargetConditionals.h>
4
+
5
+ #if TARGET_OS_OSX
6
+ #import <AppKit/AppKit.h>
7
+
8
+ @interface RNRecentDocumentController : NSDocumentController
9
+ @end
10
+
11
+ @implementation RNRecentDocumentController
12
+
13
+ - (void)openDocumentWithContentsOfURL:(NSURL *)url
14
+ display:(BOOL)displayDocument
15
+ completionHandler:(void (^)(NSDocument *_Nullable document,
16
+ BOOL documentWasAlreadyOpen,
17
+ NSError *_Nullable error))completionHandler
18
+ {
19
+ [self emitOpenDocumentURL:url];
20
+
21
+ if (completionHandler) {
22
+ completionHandler(nil, NO, nil);
23
+ }
24
+ }
25
+
26
+ - (void)reopenDocumentForURL:(NSURL *)urlOrNil
27
+ withContentsOfURL:(NSURL *)contentsURL
28
+ display:(BOOL)displayDocument
29
+ completionHandler:(void (^)(NSDocument *_Nullable document,
30
+ BOOL documentWasAlreadyOpen,
31
+ NSError *_Nullable error))completionHandler
32
+ {
33
+ [self emitOpenDocumentURL:contentsURL ?: urlOrNil];
34
+
35
+ if (completionHandler) {
36
+ completionHandler(nil, NO, nil);
37
+ }
38
+ }
39
+
40
+ - (void)emitOpenDocumentURL:(NSURL *)url
41
+ {
42
+ if (!url.isFileURL || url.path.length == 0) {
43
+ return;
44
+ }
45
+
46
+ SparkOpenURLs(@[url]);
47
+ }
48
+
49
+ @end
50
+ @interface RNRecentDocumentsStartup : NSObject <SparkStartupPlugin>
51
+ @end
52
+ @implementation RNRecentDocumentsStartup
53
+ - (void)prepareApplication { (void)[[RNRecentDocumentController alloc] init]; }
54
+ @end
55
+ #endif
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/spark-desktop-app",
3
- "version": "0.0.1-next.2",
3
+ "version": "0.0.1-next.4",
4
4
  "license": "MIT",
5
5
  "main": "src/index",
6
6
  "react-native": "src/index",
@@ -8,7 +8,8 @@
8
8
  "src",
9
9
  "macos",
10
10
  "*.podspec",
11
- "react-native.config.js"
11
+ "react-native.config.js",
12
+ "cpp"
12
13
  ],
13
14
  "peerDependencies": {
14
15
  "react": "19.1.4",
@@ -21,7 +22,8 @@
21
22
  "ios": {
22
23
  "modulesProvider": {
23
24
  "NativeDesktopApp": "RNDesktopApp"
24
- }
25
+ },
26
+ "componentProvider": {}
25
27
  }
26
28
  },
27
29
  "spark": {
@@ -5,4 +5,4 @@ export interface Spec extends TurboModule {
5
5
  addListener(eventName: string): void;
6
6
  removeListeners(count: number): void;
7
7
  }
8
- export default TurboModuleRegistry.getEnforcing<Spec>("NativeDesktopApp");
8
+ export default TurboModuleRegistry.get<Spec>("NativeDesktopApp");
@@ -1,42 +1,88 @@
1
- import { NativeEventEmitter } from "react-native";
2
- import Native from "./NativeDesktopApp";
3
- export type AppContext = {
4
- projectId: string; name: string; version: string;
5
- runtime: { mode?: string; modules?: Record<string, string> };
6
- launchArguments: string[];
1
+ import { Platform, TurboModuleRegistry } from "react-native";
2
+ import { onDesktopEvent, type DesktopEvent } from "./events";
3
+ import { callAppNative } from "./transport";
4
+ import { SparkError, asyncRegistration, nativeError, type Availability, type AsyncRegistration, type Subscription } from "./contracts";
5
+ export type { AsyncRegistration, Subscription } from "./contracts";
6
+ export interface AppRuntime { mode: "go" | "dev" | "preview" | "release"; modules: Readonly<Record<string, string>> }
7
+ export interface AppContext { projectId: string; name: string; version: string; runtime: AppRuntime; launchArguments: readonly string[] }
8
+ export type QuitResult = { quitRequested: true } | { quitRequested: false; reason: "vetoed" };
9
+ export type AppEventMap = {
10
+ windowOpened: { type: "windowOpened"; windowId: string };
11
+ windowClosed: { type: "windowClosed"; windowId: string };
12
+ activate: { type: "activate" };
13
+ deactivate: { type: "deactivate" };
14
+ reopen: { type: "reopen"; hasVisibleWindows: boolean; mainWindowWasVisible: boolean };
15
+ secondInstance: { type: "secondInstance"; arguments: readonly string[] };
16
+ willQuit: { type: "willQuit" };
7
17
  };
8
- export type DesktopEvent = { type: string; id?: string; url?: string; windowId?: string; [key: string]: unknown };
9
- const events = new NativeEventEmitter(Native);
10
- export async function callApp<T = void>(method: string, args: object = {}): Promise<T> {
11
- return JSON.parse(await Native.call(method, JSON.stringify(args))) as T;
12
- }
13
- export const getAppContext = () => callApp<AppContext>("context");
14
- export const quit = () => callApp("quit");
15
- export const hide = () => callApp("hide");
16
- export const activate = () => callApp("activate");
17
- export function onDesktopEvent(listener: (event: DesktopEvent) => void) {
18
- return events.addListener("desktop", listener);
19
- }
20
- export function onAppEvent(listener: (event: DesktopEvent) => void) {
21
- return onDesktopEvent(event => { if (["activate", "deactivate", "reopen", "beforeQuit", "secondInstance"].includes(event.type)) listener(event); });
22
- }
23
- /** Await registration before enabling edits. A rejection or timeout cancels quit. */
24
- export async function beforeQuit(handler: () => boolean | Promise<boolean>) {
25
- if (quitHandlerRegistered) throw new Error("A beforeQuit handler is already registered");
26
- quitHandlerRegistered = true;
27
- let removed = false;
18
+ export type AppEvent = AppEventMap[keyof AppEventMap];
19
+ export interface BeforeQuitOptions { onError?: (error: Error) => void }
20
+ export function getAppAvailability(): Availability {
21
+ if (Platform.OS !== "macos" && Platform.OS !== "windows") return { available: false, reason: "unsupported-platform" };
22
+ return TurboModuleRegistry.get("NativeDesktopApp") ? { available: true } : { available: false, reason: "missing-module" };
23
+ }
24
+ function record(value: unknown): value is Record<string, unknown> { return !!value && typeof value === "object" && !Array.isArray(value); }
25
+ function strings(value: unknown): value is string[] { return Array.isArray(value) && value.every(item => typeof item === "string"); }
26
+ function context(value: unknown): value is AppContext {
27
+ if (!record(value) || ![value.projectId, value.name, value.version].every(item => typeof item === "string" && item.length > 0) || !strings(value.launchArguments) || !record(value.runtime)) return false;
28
+ return ["go", "dev", "preview", "release"].includes(value.runtime.mode as string) && record(value.runtime.modules) && Object.values(value.runtime.modules).every(item => typeof item === "string");
29
+ }
30
+ export async function getAppContext(): Promise<AppContext> {
31
+ const value = await callAppNative("context", context);
32
+ return { projectId: value.projectId, name: value.name, version: value.version, launchArguments: [...value.launchArguments], runtime: { mode: value.runtime.mode, modules: { ...value.runtime.modules } } };
33
+ }
34
+ const command = (method: string, args: object = {}) => callAppNative(method, (value): value is null => value === null, args).then(() => {});
35
+ const orphanGuards = new Set<AsyncRegistration>();
36
+ async function cleanOrphanGuards(): Promise<void> {
37
+ await Promise.all([...orphanGuards].map(async guard => { await guard.remove(); orphanGuards.delete(guard); }));
38
+ }
39
+ let quitting: Promise<QuitResult> | undefined;
40
+ /** Joins an existing quit decision. Success permits shutdown; it is not an after-exit hook. */
41
+ export function quit(): Promise<QuitResult> {
42
+ return quitting ??= cleanOrphanGuards().then(() => callAppNative("quit", (value): value is QuitResult => record(value) && (value.quitRequested === true || (value.quitRequested === false && value.reason === "vetoed")))).finally(() => { quitting = undefined; });
43
+ }
44
+ export const hide = (): Promise<void> => command("hide");
45
+ export const activate = (): Promise<void> => command("activate");
46
+ function appEvent(value: DesktopEvent): AppEvent | undefined {
47
+ switch (value.type) {
48
+ case "opened": case "closed": if (typeof value.windowId === "string" && value.windowId) return { type: value.type === "opened" ? "windowOpened" : "windowClosed", windowId: value.windowId }; break;
49
+ case "activate": case "deactivate": case "willQuit": return { type: value.type };
50
+ case "reopen": if (typeof value.hasVisibleWindows === "boolean" && typeof value.mainWindowWasVisible === "boolean") return { type: "reopen", hasVisibleWindows: value.hasVisibleWindows, mainWindowWasVisible: value.mainWindowWasVisible }; break;
51
+ case "secondInstance": if (strings(value.arguments)) return { type: "secondInstance", arguments: [...value.arguments] }; break;
52
+ }
53
+ }
54
+ /** Live events only. Initial identity is getAppContext; file/URL replay belongs to documents. */
55
+ export function addAppListener<K extends keyof AppEventMap>(type: K, listener: (event: AppEventMap[K]) => void): Subscription {
56
+ if (!["activate", "deactivate", "reopen", "secondInstance", "willQuit", "windowOpened", "windowClosed"].includes(type) || typeof listener !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected an application event and listener");
57
+ const availability = getAppAvailability();
58
+ if (!availability.available) throw new SparkError(availability.reason === "missing-module" ? "E_MODULE_UNAVAILABLE" : "E_UNSUPPORTED_PLATFORM", "Application events are unavailable");
59
+ return onDesktopEvent(value => { const event = appEvent(value); if (event?.type === type) listener(event as AppEventMap[K]); }, { types: [type === "windowOpened" ? "opened" : type === "windowClosed" ? "closed" : type] });
60
+ }
61
+ let nextGuard = 0;
62
+ /** All registered guards must approve. Rejection, removal or the native 30s deadline vetoes. */
63
+ export async function beforeQuit(handler: () => boolean | Promise<boolean>, options: BeforeQuitOptions = {}): Promise<AsyncRegistration> {
64
+ if (typeof handler !== "function" || !options || typeof options !== "object" || Array.isArray(options)) throw new SparkError("E_INVALID_ARGUMENT", "Expected a quit handler and options");
65
+ for (const key of Object.keys(options)) if (key !== "onError") throw new SparkError("E_UNSUPPORTED_OPTION", `Unknown quit-guard option: ${key}`);
66
+ if (options.onError !== undefined && typeof options.onError !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected an error callback");
67
+ const availability = getAppAvailability();
68
+ if (!availability.available) throw new SparkError(availability.reason === "missing-module" ? "E_MODULE_UNAVAILABLE" : "E_UNSUPPORTED_PLATFORM", "Quit guards are unavailable");
69
+ await cleanOrphanGuards();
70
+ const id = `quit-${Date.now()}-${++nextGuard}-${Math.random().toString(36).slice(2)}`;
71
+ let removed = false, latestRequest = 0;
72
+ const onError = options.onError ?? console.error;
73
+ const reportError = (cause: unknown) => { if (!removed) { try { onError(nativeError(cause)); } catch (error) { console.error(error); } } };
28
74
  const subscription = onDesktopEvent(event => {
29
- if (event.type === "beforeQuit") void Promise.resolve().then(handler).then(
30
- allow => callApp("replyQuit", { allow: !removed && allow === true, requestId: event.requestId }),
31
- () => callApp("replyQuit", { allow: false, requestId: event.requestId }),
32
- ).catch(console.error);
33
- });
34
- try { await callApp("quitGuard", { enabled: true }); }
35
- catch (error) { subscription.remove(); quitHandlerRegistered = false; throw error; }
36
- return { async remove() {
37
- if (removed) return;
38
- removed = true; subscription.remove(); quitHandlerRegistered = false;
39
- await callApp("quitGuard", { enabled: false });
40
- } };
41
- }
42
- let quitHandlerRegistered = false;
75
+ if (removed || event.type !== "beforeQuit" || event.guardId !== id || !Number.isSafeInteger(event.requestId) || (event.requestId as number) <= latestRequest) return;
76
+ const requestId = latestRequest = event.requestId as number;
77
+ void Promise.resolve().then(() => removed ? false : handler()).catch(error => { reportError(error); return false; }).then(
78
+ allow => command("replyQuit", { id, allow: !removed && latestRequest === requestId && allow === true, requestId }),
79
+ ).catch(error => { reportError(error); });
80
+ }, { types: ["beforeQuit"], target: { field: "guardId", value: id } });
81
+ const registration = asyncRegistration(() => { removed = true; subscription.remove(); }, () => command("quitGuard", { id, enabled: false }));
82
+ try { await command("quitGuard", { id, enabled: true }); }
83
+ catch (cause) {
84
+ try { await registration.remove(); } catch (cleanup) { orphanGuards.add(registration); throw new SparkError("E_NATIVE", "Quit guard registration and cleanup failed", { cause: new AggregateError([cause, cleanup]) }); }
85
+ throw cause;
86
+ }
87
+ return registration;
88
+ }
@@ -0,0 +1,40 @@
1
+ import { SparkError } from "./index";
2
+ export type Accelerator = { key: string; modifiers: number };
3
+ // AppKit uses these bit positions in NSEventModifierFlags.
4
+ const modifiers: Record<string, number> = {
5
+ cmd: 1 << 20, command: 1 << 20, meta: 1 << 20, commandorcontrol: 1 << 20, cmdorctrl: 1 << 20,
6
+ ctrl: 1 << 18, control: 1 << 18, alt: 1 << 19, option: 1 << 19, shift: 1 << 17, fn: 1 << 23, capslock: 1 << 16,
7
+ };
8
+ const named: Record<string, string> = { enter: "\r", return: "\r", escape: "\u001b", esc: "\u001b", tab: "\t", space: " ", backspace: "\u007f", delete: "\uf728", up: "\uf700", down: "\uf701", left: "\uf702", right: "\uf703", plus: "+" };
9
+ const extended: Record<string, string> = { home: "\uf729", end: "\uf72b", pageup: "\uf72c", pagedown: "\uf72d", mediaplaypause: "MediaPlayPause", medianext: "MediaNext", mediaprevious: "MediaPrevious" };
10
+ export interface AcceleratorParseOptions { allowUnmodifiedCharacter?: boolean; allowExtendedKeys?: boolean }
11
+ export function parseAccelerator(value: string, platform: "macos" | "windows" = "macos", options: AcceleratorParseOptions = {}): Accelerator {
12
+ if (typeof value !== "string" || !["macos", "windows"].includes(platform)) throw new SparkError("E_INVALID_ARGUMENT", "Expected accelerator and desktop platform");
13
+ if (!options || typeof options !== "object" || Array.isArray(options)) throw new SparkError("E_INVALID_ARGUMENT", "Expected accelerator parsing options");
14
+ for (const key of Object.keys(options)) if (!["allowUnmodifiedCharacter", "allowExtendedKeys"].includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unknown accelerator option: ${key}`);
15
+ if ([options.allowUnmodifiedCharacter, options.allowExtendedKeys].some(value => value !== undefined && typeof value !== "boolean")) throw new SparkError("E_INVALID_ARGUMENT", "Accelerator parsing flags must be boolean");
16
+ const parts = value.toLowerCase().split("+").map(part => part.trim());
17
+ let flags = 0;
18
+ for (const part of parts.slice(0, -1)) {
19
+ const flag = platform === "windows" && (part === "cmdorctrl" || part === "commandorcontrol") ? 1 << 18 : modifiers[part];
20
+ if (!flag || (flags & flag)) throw new SparkError("E_INVALID_ARGUMENT", `Invalid or duplicate modifier: ${part}`);
21
+ if ((part === "fn" || part === "capslock") && !options.allowExtendedKeys) throw new SparkError("E_UNSUPPORTED_OPTION", `Modifier unavailable on this shortcut surface: ${part}`);
22
+ flags |= flag;
23
+ }
24
+ const last = parts.at(-1)!;
25
+ if (extended[last] && !options.allowExtendedKeys) throw new SparkError("E_UNSUPPORTED_OPTION", `Key unavailable on this shortcut surface: ${last}`);
26
+ let key = named[last] ?? extended[last] ?? last;
27
+ if (/^f([1-9]|1[0-9]|20)$/.test(last)) key = String.fromCharCode(0xf704 + Number(last.slice(1)) - 1);
28
+ if ((!extended[last] && key.length !== 1) || /[\ud800-\udfff]/.test(key)) throw new SparkError("E_INVALID_ARGUMENT", `Unknown shortcut key: ${last}`);
29
+ if (!options.allowUnmodifiedCharacter && !flags && !named[last] && !extended[last] && !/^f([1-9]|1[0-9]|20)$/.test(last)) throw new SparkError("E_INVALID_ARGUMENT", "Character shortcuts need a modifier");
30
+ return { key, modifiers: flags };
31
+ }
32
+
33
+ /** Canonical spelling for a parsed binding; this does not encode physical key codes. */
34
+ export function formatAccelerator(value: Accelerator): string {
35
+ const labels = [[1 << 20, "Cmd"], [1 << 18, "Ctrl"], [1 << 19, "Alt"], [1 << 17, "Shift"], [1 << 23, "Fn"], [1 << 16, "CapsLock"]] as const;
36
+ const keys: Record<string, string> = { "\r": "Enter", "\u001b": "Escape", "\t": "Tab", " ": "Space", "\u007f": "Backspace", "\uf728": "Delete", "\uf700": "Up", "\uf701": "Down", "\uf702": "Left", "\uf703": "Right", "+": "Plus", "\uf729": "Home", "\uf72b": "End", "\uf72c": "PageUp", "\uf72d": "PageDown" };
37
+ const code = value.key.charCodeAt(0);
38
+ const key = keys[value.key] ?? (value.key.length === 1 && code >= 0xf704 && code <= 0xf717 ? `F${code - 0xf704 + 1}` : value.key.length === 1 ? value.key.toUpperCase() : value.key);
39
+ return [...labels.filter(([flag]) => value.modifiers & flag).map(([, label]) => label), key].join("+");
40
+ }