@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
@@ -134,7 +134,7 @@ export async function validateApp(root, app, credentials, expected, notarized, e
134
134
  app = realpathSync(app);
135
135
  if (expected.runner) {
136
136
  const runtime = readJson(path.join(app, "Contents/Resources/spark-runtime.json"));
137
- if (runtime.mode !== "go" || runtime.platform !== "macos" || runtime.arch !== "arm64")
137
+ if (runtime.mode !== "go" || runtime.platform !== "macos" || runtime.arch !== expected.arch)
138
138
  throw new Error("Distribution app is not a macOS Spark Runner.");
139
139
  }
140
140
  else if (!existsSync(path.join(app, "Contents/Resources/main.jsbundle")))
@@ -145,8 +145,9 @@ export async function validateApp(root, app, credentials, expected, notarized, e
145
145
  throw new Error(`Packaged ${key} does not match the release build.`);
146
146
  }
147
147
  const arches = (await execute(root, ["lipo", "-archs", path.join(app, "Contents/MacOS", info.CFBundleExecutable)], { capture: true })).trim().split(/\s+/);
148
- if (arches.length !== 1 || arches[0] !== "arm64")
149
- throw new Error("The prototype package must contain an arm64 app.");
148
+ const expectedArch = expected.arch === "x64" ? "x86_64" : "arm64";
149
+ if (arches.length !== 1 || arches[0] !== expectedArch)
150
+ throw new Error(`The package must contain a ${expectedArch} app.`);
150
151
  await execute(root, ["codesign", "--verify", "--deep", "--strict", "--verbose=2", app], { capture: true });
151
152
  const order = signingOrder(app);
152
153
  const targets = await entitlementTargets(root, app, order, expected.entitlements, expected.byPath, execute);
@@ -2,13 +2,14 @@ const fs = require("node:fs");
2
2
  const { readConfig, statePath, supportedPlatforms } = require("@legendapp/spark-desktop-config/config.cjs");
3
3
 
4
4
  exports.nativeConfig = root => {
5
+ const patched = require("@legendapp/spark-desktop-config/native-patches.cjs").prepareNativePatches(root);
5
6
  const { expo } = readConfig(root);
6
7
  const platform = expo.platforms[0];
7
8
  const platforms = process.platform === "win32" ? {} : { windows: { npmPackageName: "react-native-windows" } };
8
9
  // Expo autolinking and React Native codegen have separate exclusion inputs.
9
10
  // An excluded desktop provider must not enter an iOS module registry either.
10
11
  const excluded = (expo.autolinking?.exclude ?? []).map(name => [name, { platforms: { ios: null, macos: null, android: null, windows: null } }]);
11
- if (!["macos", "windows"].includes(platform)) return { platforms, dependencies: Object.fromEntries(excluded) };
12
+ if (!["macos", "windows"].includes(platform)) return { platforms, dependencies: { ...patched, ...Object.fromEntries(excluded) } };
12
13
  const file = statePath(root, "native-selection.json");
13
14
  const selection = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, "utf8")) : { included: [], excluded: [] };
14
15
  return { platforms, dependencies: Object.fromEntries([
@@ -60,19 +60,56 @@ export function verifyUpdateSignature(archive, signature, publicKey) {
60
60
  if (!/^[A-Za-z0-9+/]{86}==$/.test(signature) || !verify(null, readFileSync(archive), key, Buffer.from(signature, "base64")))
61
61
  throw new Error("Update signature does not match the app's public key or archive");
62
62
  }
63
+ const buildNumberPattern = /^[0-9]+(?:\.[0-9]+){0,2}$/;
64
+ function buildNumberParts(version) { return version.split(".").map(part => part.replace(/^0+(?=\d)/, "")); }
65
+ function canonicalBuildNumber(version) { return buildNumberParts(version).concat(["0", "0"]).slice(0, 3).join("."); }
66
+ function compareBuildNumbers(left, right) {
67
+ const a = buildNumberParts(left), b = buildNumberParts(right);
68
+ for (let index = 0; index < 3; index++) {
69
+ const x = a[index] ?? "0", y = b[index] ?? "0";
70
+ if (x.length !== y.length)
71
+ return x.length < y.length ? -1 : 1;
72
+ if (x !== y)
73
+ return x < y ? -1 : 1;
74
+ }
75
+ return 0;
76
+ }
77
+ export function validateUpdateBuildVersion(buildVersion, records, archiveHash) {
78
+ if (!buildNumberPattern.test(buildVersion))
79
+ throw new Error("Updates require a numeric macOS buildNumber, increasing for every release");
80
+ const seen = new Map();
81
+ for (const version of Object.keys(records)) {
82
+ if (!buildNumberPattern.test(version))
83
+ throw new Error(`Invalid build number in Spark update history: ${version}`);
84
+ const canonical = canonicalBuildNumber(version);
85
+ const equivalent = seen.get(canonical);
86
+ if (equivalent && equivalent !== version)
87
+ throw new Error(`Spark update history reuses the numeric build number as both ${equivalent} and ${version}.`);
88
+ seen.set(canonical, version);
89
+ if (compareBuildNumbers(version, buildVersion) === 0 && version !== buildVersion)
90
+ throw new Error(`Build ${buildVersion} aliases the recorded build number ${version}; use a new increasing macos.buildNumber spelling.`);
91
+ }
92
+ const existing = records[buildVersion];
93
+ if (existing) {
94
+ if (existing.sha256 !== archiveHash)
95
+ throw new Error(`Build ${buildVersion} was already packaged with different bytes. Increase macos.buildNumber before publishing another update.`);
96
+ return;
97
+ }
98
+ for (const version of Object.keys(records)) {
99
+ if (compareBuildNumbers(buildVersion, version) <= 0)
100
+ throw new Error(`Build ${buildVersion} must be greater than every published build number. Increase macos.buildNumber before publishing another update.`);
101
+ }
102
+ }
63
103
  export async function prepareUpdate(root, archive, buildVersion, dependencies = { run, tools: sparkleTools }) {
64
104
  const expo = readAppConfig(root).expo;
65
105
  const updates = updateConfiguration(expo);
66
106
  if (!updates)
67
107
  return undefined;
68
- if (!/^[0-9]+(?:\.[0-9]+){0,2}$/.test(buildVersion))
69
- throw new Error("Updates require a numeric macOS buildNumber, increasing for every release");
70
108
  const directory = path.join(root, "dist/updates");
71
109
  const recordsFile = path.join(directory, "releases.json");
72
110
  const records = existsSync(recordsFile) ? readJson(recordsFile) : {};
73
111
  const hash = createHash("sha256").update(readFileSync(archive)).digest("hex");
74
- if (records[buildVersion] && records[buildVersion].sha256 !== hash)
75
- throw new Error(`Build ${buildVersion} was already packaged with different bytes. Increase macos.buildNumber before publishing another update.`);
112
+ validateUpdateBuildVersion(buildVersion, records, hash);
76
113
  const bin = await dependencies.tools(root, dependencies.run);
77
114
  const keyArgs = dependencies.keyFile ? ["--ed-key-file", dependencies.keyFile] : ["--account", account(expo)];
78
115
  const signature = (await dependencies.run(root, [path.join(bin, "sign_update"), ...keyArgs, "-p", archive], { capture: true })).trim();
@@ -1,19 +1,13 @@
1
- import { which } from "./process.js";
1
+ import { which, nodeCommand } from "./process.js";
2
2
  import { prepareWindowsGeometry } from "./windows-geometry.js";
3
3
  import { copyHelpers } from "./helpers.js";
4
4
  import { registerWindowsAssociations } from "./windows-associations.js";
5
5
  import { checkExpoDesktopNode } from "./expo-node.js";
6
6
  import { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, renameSync, rmSync, readFileSync, writeFileSync } from "node:fs";
7
7
  import path from "node:path";
8
- import { createRequire } from "node:module";
9
8
  import { run } from "./commands.js";
10
9
  import { prepareConfig, nativePackages, runtimeFor, stateFile, readJson, writeJson, validateBuildModules, goConfigurationIssues, readAppConfig } from "./project.js";
11
- // Windows .cmd shims are shell programs; run the package's actual JS entry.
12
- export function nodeCommand(root, name, bin, args = []) {
13
- const req = createRequire(path.join(root, "package.json"));
14
- const file = req.resolve(`${name}/package.json`), pkg = readJson(file);
15
- return ["node", path.resolve(path.dirname(file), typeof pkg.bin === "string" ? pkg.bin : pkg.bin[bin]), ...args];
16
- }
10
+ import { assertNativePatchPreflight } from "./native-patch-preflight.js";
17
11
  export async function preserveWindowsBuildOutputs(root, prepare) {
18
12
  const windows = path.join(root, "windows");
19
13
  const outputs = [];
@@ -54,6 +48,7 @@ export async function preserveWindowsBuildOutputs(root, prepare) {
54
48
  }
55
49
  }
56
50
  export async function prepareWindows(root, mode) {
51
+ assertNativePatchPreflight(root);
57
52
  await checkExpoDesktopNode(root);
58
53
  prepareConfig(root);
59
54
  prepareWindowsGeometry(root);
@@ -70,7 +65,7 @@ export async function prepareWindows(root, mode) {
70
65
  const manifest = readFileSync(path.join(root, "package.json"), "utf8");
71
66
  try {
72
67
  await preserveWindowsBuildOutputs(root, async () => {
73
- await run(root, nodeCommand(root, "expo-desktop", "expo-desktop", ["prebuild", "--platform", "windows", "--template", "expo-desktop-template-bare-minimum@54.81.1-beta.6", "--no-install"]), { env: { CI: "1" }, capture: true });
68
+ await run(root, nodeCommand(root, "expo-desktop", "expo-desktop", ["prebuild", "--platform", "windows", "--template", "expo-desktop-template-bare-minimum@54.81.1", "--no-install"]), { env: { CI: "1" }, capture: true });
74
69
  });
75
70
  // The beta template expands all-platform dependencies. Keep the consumer's installed graph.
76
71
  }
@@ -82,6 +77,7 @@ export async function prepareWindows(root, mode) {
82
77
  export async function buildWindows(root, mode, force) {
83
78
  if (mode !== "go" && mode !== "dev")
84
79
  throw new Error("Windows currently supports Spark Runner runtimes and development builds. Use spark build --dev; production builds and packaging are not implemented.");
80
+ assertNativePatchPreflight(root);
85
81
  if (process.platform !== "win32")
86
82
  throw new Error("Windows native builds require Windows x64 or ARM64. Project generation and Metro bundle checks can run on macOS.");
87
83
  for (const tool of ["node", "pwsh.exe", "dotnet.exe"])
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/spark-cli",
3
- "version": "0.0.1-next.2",
3
+ "version": "0.0.1-next.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "files": [
@@ -11,12 +11,13 @@
11
11
  "node": ">=24.19.0"
12
12
  },
13
13
  "dependencies": {
14
- "@legendapp/spark-desktop-config": "0.0.1-next.2",
15
- "ws": "8.18.3",
16
- "expo-desktop": "1.0.0-beta.6",
17
- "npm": "11.11.0",
14
+ "@legendapp/spark-desktop-config": "0.0.1-next.4",
15
+ "ws": "8.21.0",
16
+ "expo-desktop": "1.0.0",
17
+ "npm": "11.21.0",
18
18
  "typescript": "5.9.3",
19
- "cross-spawn": "7.0.6"
19
+ "cross-spawn": "7.0.6",
20
+ "@legendapp/spark-window-options": "0.0.1-next.4"
20
21
  },
21
22
  "private": true
22
23
  }
@@ -1,22 +1,16 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useState } from "react";
2
2
  import { Button, StyleSheet, Text, View } from "react-native";
3
3
  import { openFileDialog } from "@legendapp/spark/dialogs";
4
- import { configureMenus, clearMenus, addNativeMenuActionListener } from "@legendapp/spark/menus";
4
+ import { useMenu, type MenuRootItem } from "@legendapp/spark/menus";
5
5
 
6
- const menus = [{ id: "hello", title: "Hello", items: [{ id: "greet", title: "Say Hello" }] }];
6
+ const menus: MenuRootItem[] = [{ type: "submenu", id: "hello", label: "Hello", items: [{ type: "action", id: "greet", label: "Say Hello" }] }];
7
7
  export default function App({ runtime }: { runtime?: { mode: string } }) {
8
8
  const [message, setMessage] = useState("Ready");
9
- useEffect(() => {
10
- configureMenus("hello-world", menus);
11
- const subscription = addNativeMenuActionListener(action => {
12
- if (action.ownerId === "hello-world" && action.itemId === "greet") setMessage("Hello from the native menu");
13
- });
14
- return () => { subscription.remove(); clearMenus("hello-world"); };
15
- }, []);
9
+ useMenu({ id: "hello-world", items: menus, onAction: action => { if (action.itemId === "greet") setMessage("Hello from the native menu"); }, onError: error => setMessage(String(error)) });
16
10
  async function chooseFile() {
17
11
  try {
18
- const files = await openFileDialog({ title: "Choose a file", allowsMultipleSelection: false });
19
- setMessage(files?.join(", ") || "Dialog cancelled");
12
+ const files = await openFileDialog({ title: "Choose a file", multiple: false });
13
+ setMessage(files.canceled ? "Dialog cancelled" : files.paths.join(", "));
20
14
  } catch (error) { setMessage(String(error)); }
21
15
  }
22
16
  return <View style={styles.root}>
@@ -3,9 +3,11 @@
3
3
  "name": "HelloWorld",
4
4
  "version": "0.0.1",
5
5
  "window": {
6
- "width": 1000,
7
- "height": 700,
8
- "restoreFrame": true
6
+ "size": {
7
+ "width": 1000,
8
+ "height": 700
9
+ },
10
+ "restoreBounds": true
9
11
  },
10
12
  "macos": {
11
13
  "bundleIdentifier": "so.legend.spark.prototype.helloworld"
@@ -13,13 +13,13 @@
13
13
  "postinstall": "node node_modules/@legendapp/spark/init-template.cjs"
14
14
  },
15
15
  "dependencies": {
16
- "@legendapp/spark": "0.0.1-next.2",
16
+ "@legendapp/spark": "0.0.1-next.4",
17
17
  "expo": "54.0.37",
18
- "expo-desktop": "1.0.0-beta.6",
19
- "expo-desktop-prebuild-config": "1.1.0-beta.1",
20
- "expo-desktop-config-plugins": "1.2.0-beta.1",
21
- "expo-desktop-metro-config": "54.81.0-beta.3",
22
- "expo-desktop-template-bare-minimum": "54.81.1-beta.6",
18
+ "expo-desktop": "1.0.0",
19
+ "expo-desktop-prebuild-config": "1.1.0",
20
+ "expo-desktop-config-plugins": "1.2.0",
21
+ "expo-desktop-metro-config": "54.81.0",
22
+ "expo-desktop-template-bare-minimum": "54.81.1",
23
23
  "react": "19.1.4",
24
24
  "react-native": "0.81.6",
25
25
  "react-native-macos": "0.81.7",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "overrides": {
32
32
  "@expo/cli": "54.0.27",
33
- "react-native-nitro-modules": "0.35.7",
33
+ "react-native-nitro-modules": "0.37.0",
34
34
  "react-native-webview": "16.0.0",
35
35
  "@op-engineering/op-sqlite": "18.2.1",
36
36
  "expo-asset": "12.0.13",
@@ -39,10 +39,12 @@
39
39
  "expo-font": "14.0.12",
40
40
  "expo-keep-awake": "15.0.8",
41
41
  "expo-modules-core": "3.0.30",
42
- "expo-desktop-modules-core": "54.0.14-beta.0",
43
- "expo-desktop-stubs": "54.0.14-beta.0",
42
+ "expo-desktop-modules-core": "54.0.14",
43
+ "expo-desktop-stubs": "54.0.14",
44
44
  "react": "19.1.4",
45
- "react-native": "0.81.6"
45
+ "react-native": "0.81.6",
46
+ "react-native-macos": "0.81.7",
47
+ "react-native-windows": "0.81.35"
46
48
  },
47
49
  "expo": {
48
50
  "install": {
@@ -5,8 +5,8 @@ import { runCommand } from "@legendapp/spark/processes";
5
5
  export function Git({ onResult, onError }: { onResult(text: string): void; onError(message: string): void }) {
6
6
  const [busy, setBusy] = useState(false);
7
7
  return <Button disabled={busy} onPress={() => { setBusy(true); void (async () => {
8
- const paths = await openFileDialog({ canChooseDirectories: true, canChooseFiles: false }); if (!paths?.[0]) return;
9
- const result = await runCommand({ executable: "/usr/bin/git", args: ["--no-pager", "diff", "--no-ext-diff", "--no-textconv", "--", "."], cwd: paths[0], timeoutMs: 10_000 });
8
+ const paths = await openFileDialog({ selection: "directories" }); if (paths.canceled) return;
9
+ const result = await runCommand({ executable: "/usr/bin/git", args: ["--no-pager", "diff", "--no-ext-diff", "--no-textconv", "--", "."], cwd: paths.paths[0], timeoutMs: 10_000 });
10
10
  if (result.timedOut || result.outputTruncated || result.exitCode !== 0) throw new Error(result.stderr || "Git comparison failed or exceeded its output limit");
11
11
  onResult(result.stdout || "No unstaged changes.");
12
12
  })().catch(error => onError(String(error))).finally(() => setBusy(false)); }}>{busy ? "Reading Git…" : "Open Git changes"}</Button>;
@@ -1,17 +1,30 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { Text } from "react-native";
3
3
  import { Button } from "@legendapp/spark/ui";
4
- import { beforeWindowClose, openWindow, setWindowTitle, onWindowEvent } from "@legendapp/spark/windows";
4
+ import { beforeWindowClose, openWindow, setWindowOptions, addWindowListener } from "@legendapp/spark/windows";
5
5
  import { beforeQuit } from "@legendapp/spark/app";
6
- import { configureMenus, clearMenus, addNativeMenuActionListener } from "@legendapp/spark/menus";
6
+ import { useMenu, type MenuItem } from "@legendapp/spark/menus";
7
7
  import { registerShortcut } from "@legendapp/spark/shortcuts";
8
- import { onOpen } from "@legendapp/spark/links";
9
- import { readTextFile } from "@legendapp/spark/dialogs";
8
+ import { subscribeToOpenRequests } from "@legendapp/spark/app/documents";
9
+ import { readText } from "@legendapp/spark/files";
10
10
  import { sessions } from "./sessions";
11
11
  import type { DocumentSession } from "./document";
12
+ const items: MenuItem[] = [{ type: "submenu", id: "file", target: { menu: "file" }, label: "File", items: [
13
+ { type: "action", id: "new", label: "New" }, { type: "action", id: "open", label: "Open…" }, { type: "action", id: "save", label: "Save" }, { type: "action", id: "saveAs", label: "Save As…" },
14
+ ] }];
12
15
  let nextWindow = 0;
13
16
  export function Integration({ session, windowId, documentId, onReady }: { session: DocumentSession; windowId: string; documentId: string; onReady: (ready: boolean) => void }) {
14
17
  const [error, setError] = useState<string | null>(null);
18
+ const menu = useMenu({ id: `editor-${windowId}`, items, onAction: event => {
19
+ const actions: Record<string, () => unknown> = { new: () => session.newDocument(), open: () => session.open(), save: () => session.save(), saveAs: () => session.save(true) };
20
+ actions[event.itemId]?.();
21
+ }, onError: error => setError(String(error)) });
22
+ useEffect(() => {
23
+ if (menu.status !== "ready") return;
24
+ let disposed = false, focus: Awaited<ReturnType<typeof addWindowListener>> | undefined;
25
+ void addWindowListener(windowId, "focusChanged", event => { if (event.focused) void menu.menu.update({ items }).catch(error => setError(String(error))); }).then(value => { if (disposed) void value.remove(); else focus = value; }).catch(error => setError(String(error)));
26
+ return () => { disposed = true; void focus?.remove(); };
27
+ }, [menu, windowId]);
15
28
  useEffect(() => {
16
29
  let removed = false;
17
30
  const cleanups: (() => void | Promise<void>)[] = [];
@@ -19,17 +32,8 @@ export function Integration({ session, windowId, documentId, onReady }: { sessio
19
32
  const subscription = await registration;
20
33
  if (removed) await subscription.remove(); else cleanups.push(() => subscription.remove());
21
34
  };
22
- const owner = `editor-${windowId}`;
23
35
  const actions: Record<string, () => unknown> = { new: () => session.newDocument(), open: () => session.open(), save: () => session.save(), saveAs: () => session.save(true) };
24
- const menus = () => configureMenus(owner, [{ id: "file", title: "File", items: [
25
- { id: "new", title: "New" }, { id: "open", title: "Open…" }, { id: "save", title: "Save" }, { id: "saveAs", title: "Save As…" },
26
- ] }]);
27
- menus(); cleanups.push(() => clearMenus(owner));
28
- const menu = addNativeMenuActionListener(event => { if (event.ownerId === owner) actions[event.itemId]?.(); });
29
- cleanups.push(() => menu.remove());
30
- const focus = onWindowEvent(event => { if (event.type === "focus" && event.windowId === windowId) menus(); });
31
- cleanups.push(() => focus.remove());
32
- const title = () => { const state = session.getSnapshot(); void setWindowTitle(windowId, `${state.file.name}${session.dirty ? " •" : ""}`).catch(e => setError(String(e))); };
36
+ const title = () => { const state = session.getSnapshot(); void setWindowOptions(windowId, { title: `${state.file.name}${session.dirty ? " •" : ""}` }).catch(e => setError(String(e))); };
33
37
  title(); cleanups.push(session.subscribe(title));
34
38
  void (async () => {
35
39
  await retain(beforeWindowClose(windowId, session.canClose));
@@ -38,12 +42,10 @@ export function Integration({ session, windowId, documentId, onReady }: { sessio
38
42
  }
39
43
  if (windowId === "main") {
40
44
  await retain(beforeQuit(async () => { for (const document of sessions.values()) if (!await document.canClose()) return false; return true; }));
41
- await retain(onOpen(event => {
42
- if (event.type !== "openFile") return;
43
- const match = /^file:\/\/(?:localhost)?(\/.*)$/.exec(event.url);
44
- if (!match) { setError("Unsupported file URL"); return; }
45
- const location = decodeURIComponent(match[1]!);
46
- void readTextFile(location).then(text => session.load({ location, name: location.split("/").pop()! }, text)).catch(e => setError(String(e)));
45
+ await retain(subscribeToOpenRequests(event => {
46
+ if (event.type !== "file") return;
47
+ const location = event.path;
48
+ void readText(location).then(text => session.load({ location, name: location.split("/").pop()! }, text)).catch(e => setError(String(e)));
47
49
  }));
48
50
  }
49
51
  if (!removed) onReady(true);
@@ -51,7 +53,7 @@ export function Integration({ session, windowId, documentId, onReady }: { sessio
51
53
  return () => { removed = true; for (const cleanup of cleanups.reverse()) void Promise.resolve().then(cleanup).catch(console.error); };
52
54
  }, [session, windowId, onReady]);
53
55
  return <>
54
- <Button onPress={() => { void openWindow({ id: `document-${++nextWindow}`, title: session.getSnapshot().file.name, width: 800, height: 600, props: { documentId } }).catch(e => setError(String(e))); }}>Open another view</Button>
56
+ <Button onPress={() => { const id = `document-${++nextWindow}`; void openWindow({ id, title: session.getSnapshot().file.name, component: "main", size: { width: 800, height: 600 }, props: { windowId: id, windowProps: { documentId } } }).catch(e => setError(String(e))); }}>Open another view</Button>
55
57
  {error ? <Text accessibilityRole="alert">{error}</Text> : null}
56
58
  </>;
57
59
  }
@@ -1,17 +1,30 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { Text } from "react-native";
3
3
  import { Button } from "@legendapp/spark/ui";
4
- import { beforeWindowClose, openWindow, setWindowTitle, onWindowEvent } from "@legendapp/spark/windows";
4
+ import { beforeWindowClose, openWindow, setWindowOptions, addWindowListener } from "@legendapp/spark/windows";
5
5
  import { beforeQuit } from "@legendapp/spark/app";
6
- import { configureMenus, clearMenus, addNativeMenuActionListener } from "@legendapp/spark/menus";
6
+ import { useMenu, type MenuItem } from "@legendapp/spark/menus";
7
7
  import { registerShortcut } from "@legendapp/spark/shortcuts";
8
- import { onOpen } from "@legendapp/spark/links";
9
- import { readTextFile } from "@legendapp/spark/dialogs";
8
+ import { subscribeToOpenRequests } from "@legendapp/spark/app/documents";
9
+ import { readText } from "@legendapp/spark/files";
10
10
  import { sessions } from "./sessions";
11
11
  import type { DocumentSession } from "./document";
12
+ const items: MenuItem[] = [{ type: "submenu", id: "file", target: { menu: "file" }, label: "File", items: [
13
+ { type: "action", id: "new", label: "New" }, { type: "action", id: "open", label: "Open…" }, { type: "action", id: "save", label: "Save" }, { type: "action", id: "saveAs", label: "Save As…" },
14
+ ] }];
12
15
  let nextWindow = 0;
13
16
  export function Integration({ session, windowId, documentId, onReady }: { session: DocumentSession; windowId: string; documentId: string; onReady: (ready: boolean) => void }) {
14
17
  const [error, setError] = useState<string | null>(null);
18
+ const menu = useMenu({ id: `editor-${windowId}`, items, onAction: event => {
19
+ const actions: Record<string, () => unknown> = { new: () => session.newDocument(), open: () => session.open(), save: () => session.save(), saveAs: () => session.save(true) };
20
+ actions[event.itemId]?.();
21
+ }, onError: error => setError(String(error)) });
22
+ useEffect(() => {
23
+ if (menu.status !== "ready") return;
24
+ let disposed = false, focus: Awaited<ReturnType<typeof addWindowListener>> | undefined;
25
+ void addWindowListener(windowId, "focusChanged", event => { if (event.focused) void menu.menu.update({ items }).catch(error => setError(String(error))); }).then(value => { if (disposed) void value.remove(); else focus = value; }).catch(error => setError(String(error)));
26
+ return () => { disposed = true; void focus?.remove(); };
27
+ }, [menu, windowId]);
15
28
  useEffect(() => {
16
29
  let removed = false;
17
30
  const cleanups: (() => void | Promise<void>)[] = [];
@@ -19,17 +32,8 @@ export function Integration({ session, windowId, documentId, onReady }: { sessio
19
32
  const subscription = await registration;
20
33
  if (removed) await subscription.remove(); else cleanups.push(() => subscription.remove());
21
34
  };
22
- const owner = `editor-${windowId}`;
23
35
  const actions: Record<string, () => unknown> = { new: () => session.newDocument(), open: () => session.open(), save: () => session.save(), saveAs: () => session.save(true) };
24
- const menus = () => configureMenus(owner, [{ id: "file", title: "File", items: [
25
- { id: "new", title: "New" }, { id: "open", title: "Open…" }, { id: "save", title: "Save" }, { id: "saveAs", title: "Save As…" },
26
- ] }]);
27
- menus(); cleanups.push(() => clearMenus(owner));
28
- const menu = addNativeMenuActionListener(event => { if (event.ownerId === owner) actions[event.itemId]?.(); });
29
- cleanups.push(() => menu.remove());
30
- const focus = onWindowEvent(event => { if (event.type === "focus" && event.windowId === windowId) menus(); });
31
- cleanups.push(() => focus.remove());
32
- const title = () => { const state = session.getSnapshot(); void setWindowTitle(windowId, `${state.file.name}${session.dirty ? " •" : ""}`).catch(e => setError(String(e))); };
36
+ const title = () => { const state = session.getSnapshot(); void setWindowOptions(windowId, { title: `${state.file.name}${session.dirty ? " •" : ""}` }).catch(e => setError(String(e))); };
33
37
  title(); cleanups.push(session.subscribe(title));
34
38
  void (async () => {
35
39
  await retain(beforeWindowClose(windowId, session.canClose));
@@ -38,12 +42,10 @@ export function Integration({ session, windowId, documentId, onReady }: { sessio
38
42
  }
39
43
  if (windowId === "main") {
40
44
  await retain(beforeQuit(async () => { for (const document of sessions.values()) if (!await document.canClose()) return false; return true; }));
41
- await retain(onOpen(event => {
42
- if (event.type !== "openFile") return;
43
- const match = /^file:\/\/(?:localhost)?(\/.*)$/.exec(event.url);
44
- if (!match) { setError("Unsupported file URL"); return; }
45
- const location = decodeURIComponent(match[1]!).replace(/^\/([A-Za-z]:)/, "$1").replace(/\//g, "\\");
46
- void readTextFile(location).then(text => session.load({ location, name: location.split("/").pop()! }, text)).catch(e => setError(String(e)));
45
+ await retain(subscribeToOpenRequests(event => {
46
+ if (event.type !== "file") return;
47
+ const location = event.path;
48
+ void readText(location).then(text => session.load({ location, name: location.split("/").pop()! }, text)).catch(e => setError(String(e)));
47
49
  }));
48
50
  }
49
51
  if (!removed) onReady(true);
@@ -51,7 +53,7 @@ export function Integration({ session, windowId, documentId, onReady }: { sessio
51
53
  return () => { removed = true; for (const cleanup of cleanups.reverse()) void Promise.resolve().then(cleanup).catch(console.error); };
52
54
  }, [session, windowId, onReady]);
53
55
  return <>
54
- <Button onPress={() => { void openWindow({ id: `document-${++nextWindow}`, title: session.getSnapshot().file.name, width: 800, height: 600, props: { documentId } }).catch(e => setError(String(e))); }}>Open another view</Button>
56
+ <Button onPress={() => { const id = `document-${++nextWindow}`; void openWindow({ id, title: session.getSnapshot().file.name, component: "main", size: { width: 800, height: 600 }, props: { windowId: id, windowProps: { documentId } } }).catch(e => setError(String(e))); }}>Open another view</Button>
55
57
  {error ? <Text accessibilityRole="alert">{error}</Text> : null}
56
58
  </>;
57
59
  }
@@ -1,19 +1,22 @@
1
- import { openFileDialog, saveFileDialog, readTextFile, writeTextFile, writeTextFileIfUnchanged } from "@legendapp/spark/dialogs";
1
+ import { openFileDialog, saveFileDialog } from "@legendapp/spark/dialogs";
2
+ import { readText, writeText, writeTextIfUnchanged } from "@legendapp/spark/files";
2
3
  import { Alert } from "react-native";
3
4
  import type { DocumentIO } from "./document";
4
5
  export const io: DocumentIO = {
5
6
  async open() {
6
- const paths = await openFileDialog({ allowedFileTypes: ["txt", "md", "json"], allowsMultipleSelection: false });
7
- if (!paths?.[0]) return null;
8
- const location = paths[0];
9
- return { file: { location, name: location.split(/[\\/]/).pop()! }, text: await readTextFile(location) };
7
+ const paths = await openFileDialog({ filters: [{ extensions: ["txt", "md", "json"] }], multiple: false });
8
+ if (paths.canceled) return null;
9
+ const location = paths.paths[0];
10
+ return { file: { location, name: location.split(/[\\/]/).pop()! }, text: await readText(location) };
10
11
  },
11
12
  async save(file, text, expected, saveAs) {
12
- const location = saveAs || !file.location ? await saveFileDialog({ defaultName: file.name, allowedFileTypes: ["txt", "md", "json"] }) : file.location;
13
+ const result = saveAs || !file.location ? await saveFileDialog({ defaultName: file.name, filters: [{ extensions: ["txt", "md", "json"] }] }) : { canceled: false as const, path: file.location };
14
+ if (result.canceled) return null;
15
+ const location = result.path;
13
16
  if (!location) return null;
14
17
  if (location === file.location) {
15
- if (!await writeTextFileIfUnchanged(location, expected, text)) throw new Error("The file changed outside the editor. Use Save As to keep your edits in another file.");
16
- } else await writeTextFile(location, text);
18
+ if (!(await writeTextIfUnchanged(location, expected, text)).written) throw new Error("The file changed outside the editor. Use Save As to keep your edits in another file.");
19
+ } else await writeText(location, text);
17
20
  return { location, name: location.split(/[\\/]/).pop()! };
18
21
  },
19
22
  confirmDiscard: name => new Promise(resolve => Alert.alert("Unsaved changes", `Save changes to ${name}?`, [
@@ -1,7 +1,7 @@
1
1
  import { io as desktop } from "./io.desktop";
2
- import { showMessage } from "@legendapp/spark/message-dialog";
2
+ import { showMessage } from "@legendapp/spark/dialogs";
3
3
  import type { DocumentIO } from "./document";
4
4
  export const io: DocumentIO = { ...desktop, async confirmDiscard(name) {
5
- const { button } = await showMessage({ title: "Unsaved changes", message: `Save changes to ${name}?`, buttons: ["Cancel", "Discard", "Save"], cancelButton: 0, defaultButton: 2 });
6
- return (["cancel", "discard", "save"] as const)[button] ?? "cancel";
5
+ const { buttonId } = await showMessage({ title: "Unsaved changes", message: `Save changes to ${name}?`, buttons: [{ id: "cancel", label: "Cancel" }, { id: "discard", label: "Discard" }, { id: "save", label: "Save" }], cancelButtonId: "cancel", defaultButtonId: "save" });
6
+ return buttonId === "save" ? "save" : buttonId === "discard" ? "discard" : "cancel";
7
7
  } };
@@ -1,25 +1,19 @@
1
1
  import { useEffect } from "react";
2
2
  import { Platform } from "react-native";
3
- import { beforeWindowClose, setWindowTitle, onWindowEvent } from "@legendapp/spark/windows";
3
+ import { beforeWindowClose, setWindowOptions, addWindowListener } from "@legendapp/spark/windows";
4
4
  import { beforeQuit } from "@legendapp/spark/app";
5
- import { configureMenus, clearMenus, addNativeMenuActionListener } from "@legendapp/spark/menus";
6
- import { registerShortcut, parseAccelerator } from "@legendapp/spark/shortcuts";
5
+ import { createMenu, type MenuItem } from "@legendapp/spark/menus";
6
+ import { registerShortcut } from "@legendapp/spark/shortcuts";
7
7
  import { mountSerial } from "./lifetime";
8
8
  import type { LifecycleProps } from "./lifecycle-types";
9
- let activeMenuOwner: string | undefined;
10
9
  export function Lifecycle({ title, windowId = "main", flush, quit, commands, onError }: LifecycleProps) {
11
- useEffect(() => { void setWindowTitle(windowId, title).catch(error => onError(String(error))); }, [title, windowId, onError]);
10
+ useEffect(() => { void setWindowOptions(windowId, { title: title }).catch(error => onError(String(error))); }, [title, windowId, onError]);
12
11
  useEffect(() => mountSerial(`window-${windowId}`, async retain => {
13
12
  const owner = `example-${windowId}`;
14
- const menu = () => {
15
- if (activeMenuOwner && activeMenuOwner !== owner) clearMenus(activeMenuOwner);
16
- configureMenus(owner, [{ id: "file", title: "File", items: commands.map(({ id, title, key }) => ({ id, title, shortcut: parseAccelerator(`${Platform.OS === "windows" ? "Control" : "Command"}+${key}`) })) }]);
17
- activeMenuOwner = owner;
18
- };
19
- menu();
20
- await retain(Promise.resolve({ remove: () => { clearMenus(owner); if (activeMenuOwner === owner) activeMenuOwner = undefined; } }));
21
- await retain(Promise.resolve(addNativeMenuActionListener(event => { if (event.ownerId === owner) commands.find(command => command.id === event.itemId)?.run(); })));
22
- await retain(Promise.resolve(onWindowEvent(event => { if (event.windowId === windowId && event.type === "focus") menu(); })));
13
+ const items: MenuItem[] = [{ type: "submenu", id: "file", target: { menu: "file" }, label: "File", items: commands.map(({ id, title, key }) => ({ type: "action", id, label: title, shortcut: `CmdOrCtrl+${key}` })) }];
14
+ const menu = await createMenu({ id: owner, items, onAction: event => { commands.find(command => command.id === event.itemId)?.run(); } });
15
+ await retain(Promise.resolve(menu));
16
+ await retain(addWindowListener(windowId, "focusChanged", event => { if (event.focused) void menu.update({ items }).catch(error => onError(String(error))); }));
23
17
  const guarded = (save: () => Promise<boolean>) => async () => {
24
18
  try { return await save(); } catch (error) { onError(String(error)); return false; }
25
19
  };
@@ -1,24 +1,19 @@
1
1
  import { useEffect } from "react";
2
- import { Platform } from "react-native";
3
- import { onOpen } from "@legendapp/spark/links";
2
+ import { subscribeToOpenRequests } from "@legendapp/spark/app/documents";
4
3
  import { mountSerial } from "./lifetime";
5
4
  import type { OpenFilesProps } from "./OpenFiles";
6
- // onOpen replays queued launches to each subscription. Remounts must not import twice.
5
+ // Each subscription replays queued launches to each subscription. Remounts must not import twice.
7
6
  const imported = new Set<string>();
8
7
  const importing = new Set<string>();
9
8
  export function OpenFiles({ windowId = "main", onFile, onError }: OpenFilesProps) {
10
9
  useEffect(() => {
11
10
  if (windowId !== "main") return;
12
- return mountSerial("file-activation", async retain => { await retain(onOpen(event => {
13
- if (event.type !== "openFile") return;
11
+ return mountSerial("file-activation", async retain => { await retain(subscribeToOpenRequests(event => {
12
+ if (event.type !== "file") return;
14
13
  if (imported.has(event.id) || importing.has(event.id)) return;
15
- const match = /^file:\/\/(?:localhost)?(\/.*)$/.exec(event.url);
16
- if (!match) { onError("Unsupported file URL"); return; }
17
14
  importing.add(event.id);
18
15
  void (async () => {
19
- let path = decodeURIComponent(match[1]!);
20
- if (Platform.OS === "windows") path = path.replace(/^\/([a-z]:)/i, "$1").replace(/\//g, "\\");
21
- await onFile(path);
16
+ await onFile(event.path);
22
17
  imported.add(event.id);
23
18
  if (imported.size > 200) imported.delete(imported.values().next().value!);
24
19
  })().catch(error => onError(String(error))).finally(() => importing.delete(event.id));
@@ -1,19 +1,22 @@
1
- import { openFileDialog, saveFileDialog, readTextFile, writeTextFile, writeTextFileIfUnchanged } from "@legendapp/spark/dialogs";
1
+ import { openFileDialog, saveFileDialog } from "@legendapp/spark/dialogs";
2
+ import { readText, writeText, writeTextIfUnchanged } from "@legendapp/spark/files";
2
3
  import { Alert } from "react-native";
3
4
  import type { DocumentIO } from "./document";
4
5
  export const io: DocumentIO = {
5
6
  async open() {
6
- const paths = await openFileDialog({ allowedFileTypes: ["txt", "md", "json"], allowsMultipleSelection: false });
7
- if (!paths?.[0]) return null;
8
- const location = paths[0];
9
- return { file: { location, name: location.split(/[\\/]/).pop()! }, text: await readTextFile(location) };
7
+ const paths = await openFileDialog({ filters: [{ extensions: ["txt", "md", "json"] }], multiple: false });
8
+ if (paths.canceled) return null;
9
+ const location = paths.paths[0];
10
+ return { file: { location, name: location.split(/[\\/]/).pop()! }, text: await readText(location) };
10
11
  },
11
12
  async save(file, text, expected, saveAs) {
12
- const location = saveAs || !file.location ? await saveFileDialog({ defaultName: file.name, allowedFileTypes: ["txt", "md", "json"] }) : file.location;
13
+ const result = saveAs || !file.location ? await saveFileDialog({ defaultName: file.name, filters: [{ extensions: ["txt", "md", "json"] }] }) : { canceled: false as const, path: file.location };
14
+ if (result.canceled) return null;
15
+ const location = result.path;
13
16
  if (!location) return null;
14
17
  if (location === file.location) {
15
- if (!await writeTextFileIfUnchanged(location, expected, text)) throw new Error("The file changed outside the editor. Use Save As to keep your edits in another file.");
16
- } else await writeTextFile(location, text);
18
+ if (!(await writeTextIfUnchanged(location, expected, text)).written) throw new Error("The file changed outside the editor. Use Save As to keep your edits in another file.");
19
+ } else await writeText(location, text);
17
20
  return { location, name: location.split(/[\\/]/).pop()! };
18
21
  },
19
22
  confirmDiscard: name => new Promise(resolve => Alert.alert("Unsaved changes", `Save changes to ${name}?`, [
@@ -1,7 +1,7 @@
1
1
  import { io as desktop } from "./io.desktop";
2
- import { showMessage } from "@legendapp/spark/message-dialog";
2
+ import { showMessage } from "@legendapp/spark/dialogs";
3
3
  import type { DocumentIO } from "./document";
4
4
  export const io: DocumentIO = { ...desktop, async confirmDiscard(name) {
5
- const { button } = await showMessage({ title: "Unsaved changes", message: `Save changes to ${name}?`, buttons: ["Cancel", "Discard", "Save"], cancelButton: 0, defaultButton: 2 });
6
- return (["cancel", "discard", "save"] as const)[button] ?? "cancel";
5
+ const { buttonId } = await showMessage({ title: "Unsaved changes", message: `Save changes to ${name}?`, buttons: [{ id: "cancel", label: "Cancel" }, { id: "discard", label: "Discard" }, { id: "save", label: "Save" }], cancelButtonId: "cancel", defaultButtonId: "save" });
6
+ return buttonId === "save" ? "save" : buttonId === "discard" ? "discard" : "cancel";
7
7
  } };