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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -4
  3. package/ai-codex.ts +1 -0
  4. package/ai.ts +1 -0
  5. package/app-documents.ts +1 -0
  6. package/config-plugin.d.cts +2 -0
  7. package/contracts.ts +2 -0
  8. package/diagnostics.ts +1 -0
  9. package/expo-config.d.cts +1 -0
  10. package/metro.cjs +4 -1
  11. package/metro.d.cts +12 -0
  12. package/native.cjs +3 -1
  13. package/native.d.cts +6 -0
  14. package/package.json +82 -14
  15. package/schema.json +276 -58
  16. package/settings-observable.ts +1 -0
  17. package/settings-window.ts +1 -0
  18. package/shortcuts-commands.ts +1 -0
  19. package/shortcuts-keyboard.ts +3 -0
  20. package/ui-classnames.ts +1 -0
  21. package/ui-glass.ts +1 -0
  22. package/ui-search.ts +1 -0
  23. package/ui-sidebar.ts +1 -0
  24. package/ui-split-view.ts +1 -0
  25. package/ui-symbol.ts +1 -0
  26. package/vendor/node_modules/@legendapp/spark-ai/package.json +19 -0
  27. package/vendor/node_modules/@legendapp/spark-ai/src/availability.ts +17 -0
  28. package/vendor/node_modules/@legendapp/spark-ai/src/index.ts +15 -0
  29. package/vendor/node_modules/@legendapp/spark-ai/src/invocations.ts +34 -0
  30. package/vendor/node_modules/@legendapp/spark-ai/src/output.ts +62 -0
  31. package/vendor/node_modules/@legendapp/spark-ai/src/run.ts +12 -0
  32. package/vendor/node_modules/@legendapp/spark-ai/src/types.ts +16 -0
  33. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.h +2 -1
  34. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.mm +126 -24
  35. package/vendor/node_modules/@legendapp/spark-audio/package.json +5 -2
  36. package/vendor/node_modules/@legendapp/spark-audio/src/NativeSparkAudio.ts +5 -1
  37. package/vendor/node_modules/@legendapp/spark-audio/src/desktop.ts +36 -32
  38. package/vendor/node_modules/@legendapp/spark-audio/src/events.ts +11 -0
  39. package/vendor/node_modules/@legendapp/spark-audio/src/hooks.ts +65 -0
  40. package/vendor/node_modules/@legendapp/spark-audio/src/index.web.ts +92 -33
  41. package/vendor/node_modules/@legendapp/spark-audio/src/media-session-unavailable.ts +6 -4
  42. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.ts +52 -19
  43. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.web.ts +18 -10
  44. package/vendor/node_modules/@legendapp/spark-audio/src/media-types.ts +40 -13
  45. package/vendor/node_modules/@legendapp/spark-audio/src/mobile.ts +40 -32
  46. package/vendor/node_modules/@legendapp/spark-audio/src/player.ts +97 -0
  47. package/vendor/node_modules/@legendapp/spark-audio/src/status-listeners.ts +21 -15
  48. package/vendor/node_modules/@legendapp/spark-audio/src/types.ts +9 -4
  49. package/vendor/node_modules/@legendapp/spark-audio/windows/SparkAudio/SparkAudio.h +67 -20
  50. package/vendor/node_modules/@legendapp/spark-auth-session/package.json +4 -3
  51. package/vendor/node_modules/@legendapp/spark-auth-session/src/core.ts +60 -42
  52. package/vendor/node_modules/@legendapp/spark-auth-session/src/index.ts +28 -12
  53. package/vendor/node_modules/@legendapp/spark-auth-session/src/mobile.ts +97 -41
  54. package/vendor/node_modules/@legendapp/spark-auth-session/src/types.ts +20 -9
  55. package/vendor/node_modules/@legendapp/spark-cli/dist/add-desktop.js +50 -7
  56. package/vendor/node_modules/@legendapp/spark-cli/dist/build-mode.js +1 -1
  57. package/vendor/node_modules/@legendapp/spark-cli/dist/build.js +83 -48
  58. package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +60 -0
  59. package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +7 -2
  60. package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +31 -5
  61. package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +69 -23
  62. package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +12 -3
  63. package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +12 -9
  64. package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +1 -1
  65. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-dev-patch.cjs +4 -2
  66. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-metro.cjs +1 -1
  67. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-node.js +3 -3
  68. package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +10 -38
  69. package/vendor/node_modules/@legendapp/spark-cli/dist/local.js +1 -1
  70. package/vendor/node_modules/@legendapp/spark-cli/dist/metro.cjs +26 -5
  71. package/vendor/node_modules/@legendapp/spark-cli/dist/native-metadata.js +26 -0
  72. package/vendor/node_modules/@legendapp/spark-cli/dist/native-patch-preflight.js +84 -0
  73. package/vendor/node_modules/@legendapp/spark-cli/dist/native-preparation.js +88 -0
  74. package/vendor/node_modules/@legendapp/spark-cli/dist/notary-profiles.js +75 -0
  75. package/vendor/node_modules/@legendapp/spark-cli/dist/package.js +4 -2
  76. package/vendor/node_modules/@legendapp/spark-cli/dist/platform.js +16 -2
  77. package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +66 -13
  78. package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +84 -4
  79. package/vendor/node_modules/@legendapp/spark-cli/dist/release.json +23 -18
  80. package/vendor/node_modules/@legendapp/spark-cli/dist/runner-install.js +145 -31
  81. package/vendor/node_modules/@legendapp/spark-cli/dist/signing.js +4 -3
  82. package/vendor/node_modules/@legendapp/spark-cli/dist/updates.js +41 -4
  83. package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +4 -1
  84. package/vendor/node_modules/@legendapp/spark-cli/package.json +7 -6
  85. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/App.tsx +6 -12
  86. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/desktop.config.json +5 -3
  87. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/package.json +11 -9
  88. package/vendor/node_modules/@legendapp/spark-cli/templates/diff-lite/Git.macos.tsx +2 -2
  89. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.macos.tsx +23 -21
  90. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.windows.tsx +23 -21
  91. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.desktop.ts +11 -8
  92. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.macos.ts +3 -3
  93. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/Lifecycle.desktop.tsx +8 -14
  94. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/OpenFiles.desktop.tsx +5 -10
  95. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.desktop.ts +11 -8
  96. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.macos.ts +3 -3
  97. package/vendor/node_modules/@legendapp/spark-cli/templates/music-lite/assets.desktop.ts +2 -2
  98. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Activation.desktop.tsx +2 -2
  99. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Desktop.desktop.ts +37 -15
  100. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/session.ts +5 -6
  101. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/window-session.ts +5 -4
  102. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/desktop.config.json +5 -3
  103. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/metro.config.js +1 -1
  104. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/package.json +13 -11
  105. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/react-native.config.js +1 -1
  106. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/package.json +13 -11
  107. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/react-native.config.js +1 -1
  108. package/vendor/node_modules/@legendapp/spark-clipboard/macos/RNDesktopClipboard.mm +10 -16
  109. package/vendor/node_modules/@legendapp/spark-clipboard/package.json +2 -2
  110. package/vendor/node_modules/@legendapp/spark-clipboard/src/NativeDesktopClipboard.ts +1 -1
  111. package/vendor/node_modules/@legendapp/spark-clipboard/src/desktop.ts +55 -31
  112. package/vendor/node_modules/@legendapp/spark-clipboard/src/expo.ts +21 -11
  113. package/vendor/node_modules/@legendapp/spark-clipboard/src/options.ts +8 -0
  114. package/vendor/node_modules/@legendapp/spark-clipboard/src/types.ts +10 -0
  115. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.h +38 -15
  116. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.vcxproj +2 -0
  117. package/vendor/node_modules/@legendapp/spark-codex/RNCodex.podspec +22 -0
  118. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.hpp +23 -0
  119. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.mm +1529 -0
  120. package/vendor/node_modules/@legendapp/spark-codex/nitro.json +21 -0
  121. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/.gitattributes +1 -0
  122. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex+autolinking.rb +62 -0
  123. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.cpp +17 -0
  124. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.hpp +27 -0
  125. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Umbrella.hpp +38 -0
  126. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.mm +35 -0
  127. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.swift +16 -0
  128. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexAvailability.hpp +95 -0
  129. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexRunResult.hpp +99 -0
  130. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.cpp +24 -0
  131. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.hpp +71 -0
  132. package/vendor/node_modules/@legendapp/spark-codex/package.json +35 -0
  133. package/vendor/node_modules/@legendapp/spark-codex/react-native.config.js +11 -0
  134. package/vendor/node_modules/@legendapp/spark-codex/src/Codex.nitro.ts +30 -0
  135. package/vendor/node_modules/@legendapp/spark-codex/src/index.ts +65 -0
  136. package/vendor/node_modules/@legendapp/spark-codex/src/native.ts +6 -0
  137. package/vendor/node_modules/@legendapp/spark-commands/package.json +21 -0
  138. package/vendor/node_modules/@legendapp/spark-commands/src/bindings.ts +90 -0
  139. package/vendor/node_modules/@legendapp/spark-commands/src/index.tsx +400 -0
  140. package/vendor/node_modules/@legendapp/spark-commands/src/router.ts +138 -0
  141. package/vendor/node_modules/@legendapp/spark-commands/src/storage.ts +20 -0
  142. package/vendor/node_modules/@legendapp/spark-context-menu/macos/RNContextMenu.mm +7 -8
  143. package/vendor/node_modules/@legendapp/spark-context-menu/package.json +4 -3
  144. package/vendor/node_modules/@legendapp/spark-context-menu/src/NativeContextMenu.ts +1 -1
  145. package/vendor/node_modules/@legendapp/spark-context-menu/src/index.ts +26 -26
  146. package/vendor/node_modules/@legendapp/spark-context-menu/windows/SparkContextMenu/SparkContextMenu.h +3 -2
  147. package/vendor/node_modules/@legendapp/spark-desktop-app/RNDesktopApp.podspec +2 -1
  148. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryJSI.hpp +89 -0
  149. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryWindows.hpp +22 -0
  150. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/RNDesktopApp.mm +13 -7
  151. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkBinaryJSI.h +48 -0
  152. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.h +4 -0
  153. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.mm +33 -33
  154. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktopError.h +20 -0
  155. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.h +26 -0
  156. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.mm +49 -0
  157. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.h +9 -0
  158. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.mm +70 -0
  159. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkWindow.mm +9 -6
  160. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/recent-documents/RNRecentDocumentController.mm +55 -0
  161. package/vendor/node_modules/@legendapp/spark-desktop-app/package.json +5 -3
  162. package/vendor/node_modules/@legendapp/spark-desktop-app/src/NativeDesktopApp.ts +1 -1
  163. package/vendor/node_modules/@legendapp/spark-desktop-app/src/api.ts +86 -40
  164. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/accelerator.ts +40 -0
  165. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/index.ts +56 -0
  166. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/menu.ts +114 -0
  167. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/native-buffer.ts +22 -0
  168. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/path.ts +24 -0
  169. package/vendor/node_modules/@legendapp/spark-desktop-app/src/events.ts +75 -0
  170. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.ts +2 -0
  171. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.windows.ts +2 -0
  172. package/vendor/node_modules/@legendapp/spark-desktop-app/src/open-requests.ts +43 -0
  173. package/vendor/node_modules/@legendapp/spark-desktop-app/src/restoration.ts +11 -0
  174. package/vendor/node_modules/@legendapp/spark-desktop-app/src/transport.ts +10 -0
  175. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.cjs +10 -5
  176. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.d.cts +3 -0
  177. package/vendor/node_modules/@legendapp/spark-desktop-config/autolinking.cjs +35 -0
  178. package/vendor/node_modules/@legendapp/spark-desktop-config/config.cjs +16 -3
  179. package/vendor/node_modules/@legendapp/spark-desktop-config/config.d.cts +89 -9
  180. package/vendor/node_modules/@legendapp/spark-desktop-config/expo.d.cts +3 -0
  181. package/vendor/node_modules/@legendapp/spark-desktop-config/identity.cjs +3 -2
  182. package/vendor/node_modules/@legendapp/spark-desktop-config/lifecycle.cjs +20 -0
  183. package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +6 -5
  184. package/vendor/node_modules/@legendapp/spark-desktop-config/schema.json +276 -58
  185. package/vendor/node_modules/@legendapp/spark-desktop-config/updates.cjs +1 -0
  186. package/vendor/node_modules/@legendapp/spark-desktop-config/validation.cjs +35 -0
  187. package/vendor/node_modules/@legendapp/spark-desktop-config/windows.plugin.cjs +2 -1
  188. package/vendor/node_modules/@legendapp/spark-desktop-host/AppDelegate.mm +129 -11
  189. package/vendor/node_modules/@legendapp/spark-desktop-host/package.json +2 -2
  190. package/vendor/node_modules/@legendapp/spark-desktop-host/windows/application.inc +277 -121
  191. package/vendor/node_modules/@legendapp/spark-desktop-links/common/AuthLoopback.h +5 -7
  192. package/vendor/node_modules/@legendapp/spark-desktop-links/macos/RNDesktopLinks.mm +10 -9
  193. package/vendor/node_modules/@legendapp/spark-desktop-links/package.json +2 -2
  194. package/vendor/node_modules/@legendapp/spark-desktop-links/src/NativeDesktopLinks.ts +1 -1
  195. package/vendor/node_modules/@legendapp/spark-desktop-links/src/api.ts +27 -33
  196. package/vendor/node_modules/@legendapp/spark-desktop-links/src/expo.ts +3 -4
  197. package/vendor/node_modules/@legendapp/spark-desktop-links/src/native.ts +9 -0
  198. package/vendor/node_modules/@legendapp/spark-desktop-links/windows/SparkLinks/SparkLinks.h +22 -22
  199. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/RNDesktopShortcuts.podspec +1 -0
  200. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/RNDesktopShortcuts.mm +1 -1
  201. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.h +9 -0
  202. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.mm +307 -0
  203. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/package.json +8 -5
  204. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/NativeDesktopShortcuts.ts +1 -1
  205. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/api.ts +39 -14
  206. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/NativeKeyboardManager.ts +12 -0
  207. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/codes.ts +108 -0
  208. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/index.ts +99 -0
  209. package/vendor/node_modules/@legendapp/spark-desktop-windows/RNDesktopWindows.podspec +2 -0
  210. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/RNDesktopWindows.mm +229 -103
  211. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/SparkWindowGeometry.h +63 -0
  212. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.h +20 -0
  213. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.mm +27 -0
  214. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.h +12 -0
  215. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.mm +3130 -0
  216. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManagerStartup.mm +31 -0
  217. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/SidebarStartupFallbacks.mm +14 -0
  218. package/vendor/node_modules/@legendapp/spark-desktop-windows/package.json +10 -6
  219. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/NativeDesktopWindowManager.ts +1 -1
  220. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/api.ts +144 -70
  221. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/diagnostics.ts +17 -0
  222. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.ts +3 -1
  223. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.windows.ts +3 -1
  224. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos-adapter.ts +53 -0
  225. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos.ts +51 -0
  226. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/transport.ts +41 -0
  227. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/types.ts +140 -0
  228. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/validation.ts +162 -0
  229. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/window-manager/NativeWindowManager.ts +28 -0
  230. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/WindowProvider.tsx +20 -0
  231. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/createWindowsNavigator.tsx +78 -0
  232. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/index.ts +6 -0
  233. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/primaryWindowLifecycle.ts +34 -0
  234. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/types.ts +11 -0
  235. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/usePrimaryWindowLifecycle.ts +18 -0
  236. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/useWindowFocusEffect.ts +17 -0
  237. package/vendor/node_modules/@legendapp/spark-documents/package.json +22 -0
  238. package/vendor/node_modules/@legendapp/spark-documents/src/controller.ts +97 -0
  239. package/vendor/node_modules/@legendapp/spark-documents/src/documentTransition.ts +13 -0
  240. package/vendor/node_modules/@legendapp/spark-documents/src/hooks.ts +139 -0
  241. package/vendor/node_modules/@legendapp/spark-documents/src/index.tsx +50 -0
  242. package/vendor/node_modules/@legendapp/spark-documents/src/reload.ts +39 -0
  243. package/vendor/node_modules/@legendapp/spark-documents/src/requests.ts +15 -0
  244. package/vendor/node_modules/@legendapp/spark-drag-drop/package.json +4 -3
  245. package/vendor/node_modules/@legendapp/spark-drag-drop/src/DesktopDragViewNativeComponent.ts +1 -1
  246. package/vendor/node_modules/@legendapp/spark-drag-drop/src/contracts.ts +73 -18
  247. package/vendor/node_modules/@legendapp/spark-drag-drop/src/index.tsx +58 -15
  248. package/vendor/node_modules/@legendapp/spark-drag-drop/windows/SparkDragDrop/SparkDragDrop.h +14 -8
  249. package/vendor/node_modules/@legendapp/spark-file-dialog/ios/RNFileDialog.mm +34 -121
  250. package/vendor/node_modules/@legendapp/spark-file-dialog/package.json +12 -5
  251. package/vendor/node_modules/@legendapp/spark-file-dialog/src/NativeFileDialog.ts +1 -5
  252. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.ts +19 -84
  253. package/vendor/node_modules/@legendapp/spark-file-dialog/src/native.ts +12 -0
  254. package/vendor/node_modules/@legendapp/spark-file-dialog/src/options.ts +34 -14
  255. package/vendor/node_modules/@legendapp/spark-file-dialog/src/types.ts +26 -0
  256. package/vendor/node_modules/@legendapp/spark-file-dialog/windows/SparkFileDialog/SparkFileDialog.h +42 -58
  257. package/vendor/node_modules/@legendapp/spark-file-system/RNDesktopFileSystem.podspec +1 -0
  258. package/vendor/node_modules/@legendapp/spark-file-system/macos/RNDesktopFileSystem.mm +32 -21
  259. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.h +24 -0
  260. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.mm +215 -0
  261. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.h +9 -0
  262. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.mm +82 -0
  263. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.h +47 -0
  264. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.mm +306 -0
  265. package/vendor/node_modules/@legendapp/spark-file-system/package.json +8 -6
  266. package/vendor/node_modules/@legendapp/spark-file-system/src/NativeDesktopFileSystem.ts +1 -1
  267. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/NativeFileScanner.ts +11 -0
  268. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/index.ts +93 -0
  269. package/vendor/node_modules/@legendapp/spark-file-system/src/handles.ts +48 -14
  270. package/vendor/node_modules/@legendapp/spark-file-system/src/index.ts +134 -45
  271. package/vendor/node_modules/@legendapp/spark-file-system/src/path.ts +1 -9
  272. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.h +102 -26
  273. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.vcxproj +2 -0
  274. package/vendor/node_modules/@legendapp/spark-global-shortcuts/RNDesktopGlobalShortcuts.podspec +1 -0
  275. package/vendor/node_modules/@legendapp/spark-global-shortcuts/macos/RNDesktopGlobalShortcuts.mm +7 -4
  276. package/vendor/node_modules/@legendapp/spark-global-shortcuts/package.json +4 -3
  277. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/NativeDesktopGlobalShortcuts.ts +1 -1
  278. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/index.ts +39 -10
  279. package/vendor/node_modules/@legendapp/spark-global-shortcuts/windows/SparkGlobalShortcuts/SparkGlobalShortcuts.h +5 -4
  280. package/vendor/node_modules/@legendapp/spark-message-dialog/macos/RNDesktopMessageDialog.mm +2 -2
  281. package/vendor/node_modules/@legendapp/spark-message-dialog/package.json +2 -2
  282. package/vendor/node_modules/@legendapp/spark-message-dialog/src/NativeDesktopMessageDialog.ts +1 -1
  283. package/vendor/node_modules/@legendapp/spark-message-dialog/src/index.ts +48 -11
  284. package/vendor/node_modules/@legendapp/spark-native-menu/ios/RNNativeMenu.mm +141 -609
  285. package/vendor/node_modules/@legendapp/spark-native-menu/package.json +9 -4
  286. package/vendor/node_modules/@legendapp/spark-native-menu/src/NativeMenu.ts +2 -7
  287. package/vendor/node_modules/@legendapp/spark-native-menu/src/api.ts +76 -121
  288. package/vendor/node_modules/@legendapp/spark-native-menu/src/hooks.ts +97 -0
  289. package/vendor/node_modules/@legendapp/spark-native-menu/src/windows-menus.ts +43 -37
  290. package/vendor/node_modules/@legendapp/spark-notifications/macos/RNDesktopNotifications.mm +75 -30
  291. package/vendor/node_modules/@legendapp/spark-notifications/package.json +2 -2
  292. package/vendor/node_modules/@legendapp/spark-notifications/src/NativeDesktopNotifications.ts +1 -1
  293. package/vendor/node_modules/@legendapp/spark-notifications/src/index.ts +130 -42
  294. package/vendor/node_modules/@legendapp/spark-notifications/windows/SparkNotifications/SparkNotifications.h +46 -8
  295. package/vendor/node_modules/@legendapp/spark-processes/macos/RNDesktopProcesses.mm +94 -21
  296. package/vendor/node_modules/@legendapp/spark-processes/package.json +4 -3
  297. package/vendor/node_modules/@legendapp/spark-processes/src/NativeDesktopProcesses.ts +1 -1
  298. package/vendor/node_modules/@legendapp/spark-processes/src/index.ts +124 -28
  299. package/vendor/node_modules/@legendapp/spark-processes/src/types.ts +34 -0
  300. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.h +83 -34
  301. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.vcxproj +2 -0
  302. package/vendor/node_modules/@legendapp/spark-secure-storage/macos/RNDesktopSecureStorage.mm +49 -40
  303. package/vendor/node_modules/@legendapp/spark-secure-storage/package.json +2 -2
  304. package/vendor/node_modules/@legendapp/spark-secure-storage/src/NativeDesktopSecureStorage.ts +1 -1
  305. package/vendor/node_modules/@legendapp/spark-secure-storage/src/desktop.ts +16 -14
  306. package/vendor/node_modules/@legendapp/spark-secure-storage/src/expo.ts +0 -1
  307. package/vendor/node_modules/@legendapp/spark-secure-storage/src/options.ts +3 -2
  308. package/vendor/node_modules/@legendapp/spark-secure-storage/src/unavailable.ts +6 -3
  309. package/vendor/node_modules/@legendapp/spark-settings/package.json +10 -3
  310. package/vendor/node_modules/@legendapp/spark-settings/src/fileStorage.ts +11 -0
  311. package/vendor/node_modules/@legendapp/spark-settings/src/index.ts +7 -9
  312. package/vendor/node_modules/@legendapp/spark-settings/src/storage/index.ts +142 -0
  313. package/vendor/node_modules/@legendapp/spark-settings/src/store.ts +63 -21
  314. package/vendor/node_modules/@legendapp/spark-settings-ui/package.json +20 -0
  315. package/vendor/node_modules/@legendapp/spark-settings-ui/src/index.tsx +436 -0
  316. package/vendor/node_modules/@legendapp/spark-settings-ui/src/options.ts +10 -0
  317. package/vendor/node_modules/@legendapp/spark-sqlite/package.json +3 -3
  318. package/vendor/node_modules/@legendapp/spark-sqlite/src/database.ts +182 -0
  319. package/vendor/node_modules/@legendapp/spark-sqlite/src/index.ts +19 -6
  320. package/vendor/node_modules/@legendapp/spark-sqlite/src/types.ts +22 -0
  321. package/vendor/node_modules/@legendapp/spark-system/macos/RNDesktopSystem.mm +36 -11
  322. package/vendor/node_modules/@legendapp/spark-system/package.json +2 -2
  323. package/vendor/node_modules/@legendapp/spark-system/src/NativeDesktopSystem.ts +1 -1
  324. package/vendor/node_modules/@legendapp/spark-system/src/index.ts +89 -32
  325. package/vendor/node_modules/@legendapp/spark-system/windows/SparkSystem/SparkSystem.h +8 -4
  326. package/vendor/node_modules/@legendapp/spark-tray/macos/RNDesktopTray.mm +20 -8
  327. package/vendor/node_modules/@legendapp/spark-tray/package.json +2 -2
  328. package/vendor/node_modules/@legendapp/spark-tray/src/NativeDesktopTray.ts +1 -1
  329. package/vendor/node_modules/@legendapp/spark-tray/src/index.ts +91 -34
  330. package/vendor/node_modules/@legendapp/spark-tray/windows/SparkTray/SparkTray.h +45 -15
  331. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkButton.mm +2 -1
  332. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkTextInput.mm +10 -2
  333. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.h +15 -0
  334. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.mm +1182 -0
  335. package/vendor/node_modules/@legendapp/spark-ui/macos/glass-effect-view/RNGlassEffectView.mm +72 -0
  336. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.h +15 -0
  337. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.mm +194 -0
  338. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.h +15 -0
  339. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.mm +180 -0
  340. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.h +15 -0
  341. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.mm +155 -0
  342. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.h +18 -0
  343. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.mm +645 -0
  344. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.h +15 -0
  345. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.mm +164 -0
  346. package/vendor/node_modules/@legendapp/spark-ui/package.json +16 -4
  347. package/vendor/node_modules/@legendapp/spark-ui/src/SparkSelectNativeComponent.ts +3 -1
  348. package/vendor/node_modules/@legendapp/spark-ui/src/SparkTextInputNativeComponent.ts +6 -2
  349. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/SidebarSplitViewNativeComponent.ts +31 -0
  350. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +77 -0
  351. package/vendor/node_modules/@legendapp/spark-ui/src/availability.ts +10 -0
  352. package/vendor/node_modules/@legendapp/spark-ui/src/classnames/index.ts +8 -0
  353. package/vendor/node_modules/@legendapp/spark-ui/src/control.ts +42 -0
  354. package/vendor/node_modules/@legendapp/spark-ui/src/desktop.tsx +56 -0
  355. package/vendor/node_modules/@legendapp/spark-ui/src/glass-effect-view/index.tsx +15 -0
  356. package/vendor/node_modules/@legendapp/spark-ui/src/index.android.tsx +35 -21
  357. package/vendor/node_modules/@legendapp/spark-ui/src/index.ios.tsx +55 -26
  358. package/vendor/node_modules/@legendapp/spark-ui/src/index.tsx +1 -27
  359. package/vendor/node_modules/@legendapp/spark-ui/src/index.web.tsx +45 -14
  360. package/vendor/node_modules/@legendapp/spark-ui/src/index.windows.tsx +1 -42
  361. package/vendor/node_modules/@legendapp/spark-ui/src/mobile-input.tsx +11 -0
  362. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSegmentedControlNativeComponent.ts +17 -0
  363. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSelectNativeComponent.ts +17 -0
  364. package/vendor/node_modules/@legendapp/spark-ui/src/select.ts +5 -3
  365. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/SFSymbolNativeComponent.ts +13 -0
  366. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/index.tsx +27 -0
  367. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarItemNativeComponent.ts +25 -0
  368. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarNativeComponent.ts +24 -0
  369. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/index.tsx +104 -0
  370. package/vendor/node_modules/@legendapp/spark-ui/src/specialized.ts +22 -0
  371. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/TextInputSearchNativeComponent.ts +33 -0
  372. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/index.tsx +34 -0
  373. package/vendor/node_modules/@legendapp/spark-ui/src/types.ts +30 -19
  374. package/vendor/node_modules/@legendapp/spark-ui/uniwind.ts +3 -1
  375. package/vendor/node_modules/@legendapp/spark-ui/uniwind.web.ts +3 -1
  376. package/vendor/node_modules/@legendapp/spark-ui/windows/SparkUI/SparkUI.h +16 -11
  377. package/vendor/node_modules/@legendapp/spark-updates/macos/RNDesktopUpdates.mm +21 -8
  378. package/vendor/node_modules/@legendapp/spark-updates/package.json +2 -2
  379. package/vendor/node_modules/@legendapp/spark-updates/src/NativeDesktopUpdates.ts +3 -2
  380. package/vendor/node_modules/@legendapp/spark-updates/src/index.ts +71 -15
  381. package/vendor/node_modules/@legendapp/spark-webview/package.json +2 -2
  382. package/vendor/node_modules/@legendapp/spark-webview/src/WebView.tsx +11 -0
  383. package/vendor/node_modules/@legendapp/spark-webview/src/adapter.ts +55 -0
  384. package/vendor/node_modules/@legendapp/spark-webview/src/index.ts +2 -2
  385. package/vendor/node_modules/@legendapp/spark-webview/src/types.ts +32 -0
  386. package/vendor/node_modules/@legendapp/spark-webview/src/view-props.ts +28 -0
  387. package/vendor/node_modules/@legendapp/spark-window-options/index.cjs +51 -14
  388. package/vendor/node_modules/@legendapp/spark-window-options/index.d.ts +21 -8
  389. package/vendor/node_modules/@legendapp/spark-window-options/package.json +1 -1
  390. package/windows-macos.ts +1 -0
  391. package/expo-metro.cjs +0 -1
  392. package/message-dialog.ts +0 -1
  393. package/universal.cjs +0 -1
  394. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/accelerator.ts +0 -22
  395. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows-options.ts +0 -11
  396. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.windows.ts +0 -15
  397. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/accelerator.ts +0 -22
  398. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.h +0 -4
  399. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.mm +0 -53
@@ -1,5 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
- import { closeSync, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
+ import { closeSync, existsSync, ftruncateSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
3
+ import { spawn } from "node:child_process";
3
4
  import { open } from "node:fs/promises";
4
5
  import path from "node:path";
5
6
  import { setTimeout as sleep } from "node:timers/promises";
@@ -9,6 +10,142 @@ import { architecture } from "./platform.js";
9
10
  import { VERSION } from "./project.js";
10
11
  import { installedRelease } from "./release.js";
11
12
  const defaults = { fetch: globalThis.fetch, run };
13
+ function readLegacyPid(lock) {
14
+ try {
15
+ const content = readFileSync(lock, "utf8").trim();
16
+ if (/^\d+$/.test(content))
17
+ return Number(content);
18
+ }
19
+ catch (error) {
20
+ if (error.code !== "ENOENT")
21
+ throw error;
22
+ }
23
+ return undefined;
24
+ }
25
+ /** The kernel releases this advisory lock if the installer exits, including on a crash. */
26
+ export async function acquireRunnerLock(lock, waitMs = 600_000) {
27
+ const deadline = Date.now() + waitMs;
28
+ let child;
29
+ let exited;
30
+ while (true) {
31
+ if (Date.now() >= deadline)
32
+ throw new Error("Another process is installing Runner. Retry desktop launch when it finishes.");
33
+ const existingPid = readLegacyPid(lock);
34
+ if (existingPid) {
35
+ try {
36
+ process.kill(existingPid, 0);
37
+ if (Date.now() >= deadline)
38
+ throw new Error("Another process is installing Runner. Retry desktop launch when it finishes.");
39
+ await sleep(Math.min(1000, deadline - Date.now()));
40
+ continue;
41
+ }
42
+ catch (error) {
43
+ if (error.code !== "ESRCH")
44
+ throw error;
45
+ }
46
+ }
47
+ const existed = existsSync(lock);
48
+ const observed = existed ? lstatSync(lock) : undefined;
49
+ const remaining = Math.max(1, deadline - Date.now());
50
+ child = spawn("/usr/bin/lockf", ["-k", "-t", String(Math.max(1, Math.ceil(remaining / 1000))), lock, process.execPath, "-e", "process.stdout.write('locked\\n'); process.stdin.resume()"], { detached: true, stdio: ["pipe", "pipe", "pipe"] });
51
+ let stderr = "";
52
+ child.stderr.setEncoding("utf8").on("data", chunk => { stderr += chunk; });
53
+ exited = new Promise(resolve => child.once("close", (code, signal) => resolve({ code, signal })));
54
+ try {
55
+ await new Promise((resolve, reject) => {
56
+ let output = "";
57
+ child.stdout.setEncoding("utf8").on("data", chunk => {
58
+ output += chunk;
59
+ if (output.includes("locked\n"))
60
+ resolve();
61
+ });
62
+ child.once("error", reject);
63
+ void exited.then(({ code }) => reject(new Error(`Could not acquire Runner install lock${stderr ? `: ${stderr.trim()}` : ` (lockf exited ${code})`}`)));
64
+ });
65
+ }
66
+ catch (error) {
67
+ try {
68
+ process.kill(-child.pid, "SIGTERM");
69
+ }
70
+ catch { }
71
+ await exited;
72
+ throw error;
73
+ }
74
+ const stopKeeper = async () => { child.stdin.end(); await exited; };
75
+ let fd;
76
+ try {
77
+ const acquired = lstatSync(lock);
78
+ if (observed && (acquired.dev !== observed.dev || acquired.ino !== observed.ino)) {
79
+ await stopKeeper();
80
+ continue;
81
+ }
82
+ const content = readFileSync(lock, "utf8").trim();
83
+ const legacyPid = readLegacyPid(lock);
84
+ if (legacyPid) {
85
+ let legacyLive = false;
86
+ try {
87
+ process.kill(legacyPid, 0);
88
+ legacyLive = true;
89
+ }
90
+ catch (error) {
91
+ if (error.code !== "ESRCH")
92
+ throw error;
93
+ }
94
+ if (legacyLive) {
95
+ await stopKeeper();
96
+ if (Date.now() >= deadline)
97
+ throw new Error("Another process is installing Runner. Retry desktop launch when it finishes.");
98
+ await sleep(Math.min(1000, deadline - Date.now()));
99
+ continue;
100
+ }
101
+ }
102
+ else if (!content && existed) {
103
+ const age = Date.now() - statSync(lock).mtimeMs;
104
+ if (age < 30_000) {
105
+ await stopKeeper();
106
+ if (Date.now() >= deadline)
107
+ throw new Error("Another process is installing Runner. Retry desktop launch when it finishes.");
108
+ await sleep(Math.min(1000, deadline - Date.now()));
109
+ continue;
110
+ }
111
+ }
112
+ fd = openSync(lock, "r+");
113
+ ftruncateSync(fd, 0);
114
+ writeFileSync(fd, `v2:${process.pid}`);
115
+ closeSync(fd);
116
+ fd = undefined;
117
+ break;
118
+ }
119
+ catch (error) {
120
+ if (fd !== undefined) {
121
+ try {
122
+ closeSync(fd);
123
+ }
124
+ catch { }
125
+ }
126
+ await stopKeeper();
127
+ throw error;
128
+ }
129
+ }
130
+ let released = false;
131
+ let releasePromise;
132
+ let unexpectedExit;
133
+ void exited.then(result => { if (!released)
134
+ unexpectedExit = result; });
135
+ const release = () => releasePromise ??= (async () => {
136
+ released = true;
137
+ child.stdin.end();
138
+ const result = await exited;
139
+ if (result.code !== 0)
140
+ throw new Error(`Runner install lock process exited unexpectedly (${result.signal ?? result.code}).`);
141
+ })();
142
+ return Object.assign(release, {
143
+ assertAlive() {
144
+ if (unexpectedExit)
145
+ throw new Error(`Runner install lock process exited unexpectedly (${unexpectedExit.signal ?? unexpectedExit.code}).`);
146
+ },
147
+ });
148
+ }
12
149
  export async function downloadAsset(asset, file, request = globalThis.fetch) {
13
150
  for (let attempt = 0; attempt < 3; attempt++) {
14
151
  let handle;
@@ -50,7 +187,7 @@ export async function downloadAsset(asset, file, request = globalThis.fetch) {
50
187
  }
51
188
  async function verify(app, asset, deps) {
52
189
  const runtime = readRuntime(app);
53
- if (!runtime || runtime.mode !== "go" || runtime.platform !== "macos" || runtime.arch !== "arm64" || runtime.fingerprint !== asset.fingerprint)
190
+ if (!runtime || runtime.mode !== "go" || runtime.platform !== "macos" || runtime.arch !== architecture("macos") || runtime.fingerprint !== asset.fingerprint)
54
191
  throw new Error("Downloaded Runner has incompatible runtime metadata");
55
192
  await deps.run(path.dirname(app), ["codesign", "--verify", "--deep", "--strict", app], { capture: true });
56
193
  const signature = await deps.run(path.dirname(app), ["codesign", "-dvvv", app], { capture: true });
@@ -65,32 +202,7 @@ export async function installRunner(asset, cache = sparkHome(), deps = defaults)
65
202
  const app = path.join(destination, asset.app);
66
203
  mkdirSync(parent, { recursive: true });
67
204
  const lock = `${destination}.lock`;
68
- let owned = false;
69
- for (let attempt = 0; !owned && attempt < 600; attempt++) {
70
- try {
71
- const fd = openSync(lock, "wx");
72
- writeFileSync(fd, String(process.pid));
73
- closeSync(fd);
74
- owned = true;
75
- }
76
- catch (error) {
77
- if (error.code !== "EEXIST")
78
- throw error;
79
- const pid = Number(readFileSync(lock, "utf8"));
80
- if (Number.isInteger(pid) && pid > 0) {
81
- try {
82
- process.kill(pid, 0);
83
- }
84
- catch (probe) {
85
- if (probe.code === "ESRCH")
86
- rmSync(lock, { force: true });
87
- }
88
- }
89
- await sleep(1000);
90
- }
91
- }
92
- if (!owned)
93
- throw new Error("Another process is installing Runner. Retry desktop launch when it finishes.");
205
+ const releaseLock = await acquireRunnerLock(lock);
94
206
  let staging;
95
207
  try {
96
208
  if (existsSync(app)) {
@@ -110,8 +222,10 @@ export async function installRunner(asset, cache = sparkHome(), deps = defaults)
110
222
  // Only publisher-pinned bytes reach the platform extractor.
111
223
  await deps.run(staging, ["ditto", "-x", "-k", archive, unpacked], { capture: true });
112
224
  await verify(path.join(unpacked, asset.app), asset, deps);
225
+ releaseLock.assertAlive();
113
226
  renameSync(unpacked, destination);
114
227
  }
228
+ releaseLock.assertAlive();
115
229
  registerRuntime(app);
116
230
  console.log("Spark Runner installed and verified.");
117
231
  return app;
@@ -119,7 +233,7 @@ export async function installRunner(asset, cache = sparkHome(), deps = defaults)
119
233
  finally {
120
234
  if (staging)
121
235
  rmSync(staging, { recursive: true, force: true });
122
- rmSync(lock, { force: true });
236
+ await releaseLock();
123
237
  }
124
238
  }
125
239
  export async function acquireRunner(platform) {
@@ -130,8 +244,8 @@ export async function acquireRunner(platform) {
130
244
  const asset = release.runners[target];
131
245
  if (!asset)
132
246
  throw new Error(`This Spark release has no Runner for ${target}. Use a custom development build.`);
133
- if (process.platform !== "darwin" || process.arch !== "arm64")
134
- throw new Error("This release's Runner requires Apple Silicon macOS and arm64 Node.");
247
+ if (process.platform !== "darwin")
248
+ throw new Error("This release's macOS Runner requires macOS.");
135
249
  app = await installRunner(asset);
136
250
  }
137
251
  return app;
@@ -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);
@@ -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();
@@ -8,6 +8,7 @@ import path from "node:path";
8
8
  import { createRequire } from "node:module";
9
9
  import { run } from "./commands.js";
10
10
  import { prepareConfig, nativePackages, runtimeFor, stateFile, readJson, writeJson, validateBuildModules, goConfigurationIssues, readAppConfig } from "./project.js";
11
+ import { assertNativePatchPreflight } from "./native-patch-preflight.js";
11
12
  // Windows .cmd shims are shell programs; run the package's actual JS entry.
12
13
  export function nodeCommand(root, name, bin, args = []) {
13
14
  const req = createRequire(path.join(root, "package.json"));
@@ -54,6 +55,7 @@ export async function preserveWindowsBuildOutputs(root, prepare) {
54
55
  }
55
56
  }
56
57
  export async function prepareWindows(root, mode) {
58
+ assertNativePatchPreflight(root);
57
59
  await checkExpoDesktopNode(root);
58
60
  prepareConfig(root);
59
61
  prepareWindowsGeometry(root);
@@ -70,7 +72,7 @@ export async function prepareWindows(root, mode) {
70
72
  const manifest = readFileSync(path.join(root, "package.json"), "utf8");
71
73
  try {
72
74
  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 });
75
+ 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
76
  });
75
77
  // The beta template expands all-platform dependencies. Keep the consumer's installed graph.
76
78
  }
@@ -82,6 +84,7 @@ export async function prepareWindows(root, mode) {
82
84
  export async function buildWindows(root, mode, force) {
83
85
  if (mode !== "go" && mode !== "dev")
84
86
  throw new Error("Windows currently supports Spark Runner runtimes and development builds. Use spark build --dev; production builds and packaging are not implemented.");
87
+ assertNativePatchPreflight(root);
85
88
  if (process.platform !== "win32")
86
89
  throw new Error("Windows native builds require Windows x64 or ARM64. Project generation and Metro bundle checks can run on macOS.");
87
90
  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.3",
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.3",
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.3"
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.3",
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",
@@ -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
  }