@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,632 +1,164 @@
1
1
  #import "RNNativeMenu.h"
2
-
3
- #import <React/RCTBridgeModule.h>
4
2
  #import <React/RCTUtils.h>
5
- #import <TargetConditionals.h>
6
- #import <objc/runtime.h>
7
-
8
- #if TARGET_OS_OSX
9
3
  #import <AppKit/AppKit.h>
4
+ #import <objc/runtime.h>
10
5
 
11
- @class RNNativeMenu;
12
-
13
- static BOOL RNNativeMenuHandleBoundSender(id sender);
14
- static void RNNativeMenuInstallCommandBridge(void);
15
-
16
- @interface NSApplication (RNNativeMenuCommandBridge)
17
- @end
18
-
19
- @implementation NSApplication (RNNativeMenuCommandBridge)
20
-
21
- - (BOOL)rnNativeMenu_sendAction:(SEL)action to:(id)target from:(id)sender
22
- {
23
- if (RNNativeMenuHandleBoundSender(sender)) {
24
- return YES;
6
+ static char RoleKey, LocationKey;
7
+ static NSDictionary<NSString *, NSString *> *RoleSelectors(void) {
8
+ return @{ @"new": @"newDocument:", @"open": @"openDocument:", @"save": @"saveDocument:", @"saveAs": @"saveDocumentAs:", @"clearRecentDocuments": @"clearRecentDocuments:",
9
+ @"about": @"orderFrontStandardAboutPanel:", @"settings": @"showPreferencesWindow:", @"hide": @"hide:", @"hideOthers": @"hideOtherApplications:", @"showAll": @"unhideAllApplications:", @"quit": @"terminate:",
10
+ @"undo": @"undo:", @"redo": @"redo:", @"cut": @"cut:", @"copy": @"copy:", @"paste": @"paste:", @"selectAll": @"selectAll:", @"minimize": @"performMiniaturize:", @"zoom": @"performZoom:", @"close": @"performClose:", @"toggleFullscreen": @"toggleFullScreen:" };
11
+ }
12
+ static NSString *Role(NSMenuItem *item) {
13
+ NSString *role = objc_getAssociatedObject(item, &RoleKey);
14
+ if (role) return role;
15
+ if (item.submenu && item.submenu == NSApp.servicesMenu) return @"services";
16
+ // React Native's macOS storyboard reserves Command-comma for Preferences,
17
+ // but leaves its action unset until an application supplies a handler.
18
+ if (!item.action && !item.submenu && [item.keyEquivalent isEqual:@","] &&
19
+ item.keyEquivalentModifierMask == NSEventModifierFlagCommand) return @"settings";
20
+ NSString *action = item.action ? NSStringFromSelector(item.action) : @"";
21
+ if ([action isEqual:@"showSettingsWindow:"] || [action isEqual:@"showPreferences:"]) return @"settings";
22
+ for (NSString *key in RoleSelectors()) if ([RoleSelectors()[key] isEqual:action]) return key;
23
+ return nil;
24
+ }
25
+ static NSMenuItem *Find(NSMenu *menu, NSDictionary *target, BOOL recursive) {
26
+ for (NSMenuItem *item in menu.itemArray) {
27
+ if ((target[@"id"] && [item.identifier isEqual:target[@"id"]]) || (target[@"role"] && [Role(item) isEqual:target[@"role"]]) || (target[@"menu"] && [objc_getAssociatedObject(item, &LocationKey) isEqual:target[@"menu"]])) return item;
28
+ if (recursive && item.submenu) { NSMenuItem *found = Find(item.submenu, target, YES); if (found) return found; }
25
29
  }
26
- return [self rnNativeMenu_sendAction:action to:target from:sender];
30
+ return nil;
27
31
  }
28
-
29
- @end
30
- #endif
32
+ static void Fail(NSString *code, NSString *message) { @throw [NSException exceptionWithName:code reason:message userInfo:nil]; }
31
33
 
32
34
  @interface RNNativeMenu ()
33
- @property (nonatomic, strong) NSMutableDictionary<NSString *, NSArray *> *ownerMenus;
34
- @property (nonatomic, strong) NSMutableDictionary<NSString *, NSArray *> *ownerMergedMenuItems;
35
- @property (nonatomic, strong) NSMutableDictionary<NSString *, NSArray *> *ownerBoundMenuItems;
36
- @property (nonatomic, strong) NSMutableDictionary<NSString *, id> *menuItemsByKey;
37
- #if TARGET_OS_OSX
38
- - (void)emitBoundMenuItemAction:(NSDictionary *)payload;
39
- - (NSMenuItem *)appendItem:(NSDictionary *)config ownerId:(NSString *)ownerId menuId:(NSString *)menuId toMenu:(NSMenu *)menu;
40
- - (NSMenuItem *)targetItemForConfig:(NSDictionary *)config inMenu:(NSMenu *)menu;
41
- - (BOOL)configTargetsExistingItem:(NSDictionary *)config;
42
- - (NSDictionary *)representedObjectForConfig:(NSDictionary *)config ownerId:(NSString *)ownerId menuId:(NSString *)menuId;
43
- - (void)bindExistingItem:(NSMenuItem *)item
44
- config:(NSDictionary *)config
45
- ownerId:(NSString *)ownerId
46
- menuId:(NSString *)menuId
47
- boundRecords:(NSMutableArray *)boundRecords;
48
- - (void)moveExistingItem:(NSMenuItem *)item config:(NSDictionary *)config inMenu:(NSMenu *)menu;
49
- - (void)normalizeMenuItemLayout:(NSMenu *)menu;
50
- - (void)restoreBoundItem:(NSDictionary *)record;
51
- #endif
35
+ @property NSMutableArray<void (^)(void)> *undo;
36
+ @property NSArray *published;
37
+ - (void)restore;
52
38
  @end
53
-
54
39
  @implementation RNNativeMenu
55
-
56
40
  RCT_EXPORT_MODULE(NativeMenu)
57
-
58
- #if TARGET_OS_OSX
59
- static __weak RNNativeMenu *RNNativeMenuActiveModule;
60
-
61
- static NSMapTable<NSMenuItem *, NSDictionary *> *RNNativeMenuBoundMenuItems(void)
62
- {
63
- static NSMapTable<NSMenuItem *, NSDictionary *> *boundItems;
64
- static dispatch_once_t onceToken;
65
- dispatch_once(&onceToken, ^{
66
- boundItems = [NSMapTable weakToStrongObjectsMapTable];
67
- });
68
- return boundItems;
69
- }
70
-
71
- static void RNNativeMenuInstallCommandBridge(void)
72
- {
73
- static dispatch_once_t onceToken;
74
- dispatch_once(&onceToken, ^{
75
- Method originalMethod = class_getInstanceMethod(NSApplication.class, @selector(sendAction:to:from:));
76
- Method bridgeMethod = class_getInstanceMethod(NSApplication.class, @selector(rnNativeMenu_sendAction:to:from:));
77
- if (originalMethod && bridgeMethod) {
78
- method_exchangeImplementations(originalMethod, bridgeMethod);
79
- }
80
- });
81
- }
82
-
83
- static BOOL RNNativeMenuHandleBoundSender(id sender)
84
- {
85
- if (![sender isKindOfClass:NSMenuItem.class]) {
86
- return NO;
87
- }
88
-
89
- NSDictionary *payload = [RNNativeMenuBoundMenuItems() objectForKey:(NSMenuItem *)sender];
90
- if (!payload) {
91
- return NO;
92
- }
93
-
94
- RNNativeMenu *module = RNNativeMenuActiveModule;
95
- if (!module) {
96
- return NO;
97
- }
98
-
99
- [module emitBoundMenuItemAction:payload];
100
- return YES;
101
- }
102
- #endif
103
-
104
- - (instancetype)init
105
- {
106
- if (self = [super init]) {
107
- _ownerMenus = [NSMutableDictionary new];
108
- _ownerMergedMenuItems = [NSMutableDictionary new];
109
- _ownerBoundMenuItems = [NSMutableDictionary new];
110
- _menuItemsByKey = [NSMutableDictionary new];
111
- #if TARGET_OS_OSX
112
- RNNativeMenuActiveModule = self;
113
- RNNativeMenuInstallCommandBridge();
114
- #endif
115
- }
116
- return self;
117
- }
118
-
119
- - (NSArray<NSString *> *)supportedEvents
120
- {
121
- return @[@"NativeMenuAction"];
122
- }
123
-
124
- - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
125
- (const facebook::react::ObjCTurboModule::InitParams &)params
126
- {
127
- return std::make_shared<facebook::react::NativeMenuSpecJSI>(params);
128
- }
129
-
130
- - (NSArray *)parseArrayJSON:(NSString *)json
131
- {
132
- NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
133
- if (!data) {
134
- return @[];
135
- }
136
-
137
- id value = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
138
- return [value isKindOfClass:[NSArray class]] ? value : @[];
139
- }
140
-
141
- - (NSString *)itemKeyForOwner:(NSString *)ownerId itemId:(NSString *)itemId
142
- {
143
- return [NSString stringWithFormat:@"%@:%@", ownerId ?: @"", itemId ?: @""];
41
+ + (BOOL)requiresMainQueueSetup { return YES; }
42
+ - (instancetype)init { if (self = [super init]) { _undo = [NSMutableArray new]; _published = @[]; } return self; }
43
+ - (NSArray<NSString *> *)supportedEvents { return @[@"NativeMenuAction"]; }
44
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { return std::make_shared<facebook::react::NativeMenuSpecJSI>(params); }
45
+ - (void)restore { for (void (^undo)(void) in self.undo.reverseObjectEnumerator) undo(); [self.undo removeAllObjects]; }
46
+ - (void)identifyNativeMenus {
47
+ NSMenu *main = NSApp.mainMenu;
48
+ for (NSMenuItem *item in main.itemArray) {
49
+ if (objc_getAssociatedObject(item, &LocationKey)) continue;
50
+ NSString *location = nil;
51
+ if (item == main.itemArray.firstObject) location = @"app";
52
+ else if (item.submenu == NSApp.windowsMenu) location = @"window";
53
+ else if (item.submenu == NSApp.helpMenu) location = @"help";
54
+ else if (Find(item.submenu, @{ @"role": @"copy" }, YES) || Find(item.submenu, @{ @"role": @"undo" }, YES)) location = @"edit";
55
+ else if (Find(item.submenu, @{ @"role": @"open" }, YES) || Find(item.submenu, @{ @"role": @"close" }, YES)) location = @"file";
56
+ else if (Find(item.submenu, @{ @"role": @"toggleFullscreen" }, YES)) location = @"view";
57
+ if (location) objc_setAssociatedObject(item, &LocationKey, location, OBJC_ASSOCIATION_COPY_NONATOMIC);
58
+ }
59
+ }
60
+ - (NSInteger)index:(NSDictionary *)placement inMenu:(NSMenu *)menu {
61
+ if (!placement) return menu.numberOfItems;
62
+ NSDictionary *target = placement[@"before"] ?: placement[@"after"];
63
+ NSMenuItem *item = Find(menu, target, NO);
64
+ if (!item) Fail(@"E_NOT_FOUND", @"Menu placement target was not found");
65
+ return [menu indexOfItem:item] + (placement[@"after"] ? 1 : 0);
66
+ }
67
+ - (void)save:(NSMenuItem *)item {
68
+ NSMenu *menu = item.menu; NSInteger index = [menu indexOfItem:item];
69
+ NSString *title = item.title, *key = item.keyEquivalent, *identifier = item.identifier;
70
+ id target = item.target, represented = item.representedObject, role = objc_getAssociatedObject(item, &RoleKey);
71
+ SEL action = item.action; BOOL enabled = item.enabled, hidden = item.hidden;
72
+ NSControlStateValue state = item.state; NSEventModifierFlags modifiers = item.keyEquivalentModifierMask;
73
+ NSImage *image = item.image;
74
+ [self.undo addObject:^{
75
+ item.title = title; item.keyEquivalent = key; item.identifier = identifier; item.target = target; item.action = action;
76
+ item.representedObject = represented; item.enabled = enabled; item.hidden = hidden; item.state = state; item.keyEquivalentModifierMask = modifiers; item.image = image;
77
+ objc_setAssociatedObject(item, &RoleKey, role, OBJC_ASSOCIATION_COPY_NONATOMIC);
78
+ if (item.menu == menu && index >= 0 && [menu indexOfItem:item] != index) { [menu removeItem:item]; [menu insertItem:item atIndex:MIN(index, menu.numberOfItems)]; }
79
+ }];
144
80
  }
145
-
146
- - (void)configureMenus:(NSString *)ownerId menusJson:(NSString *)menusJson
147
- {
148
- #if TARGET_OS_OSX
149
- RCTExecuteOnMainQueue(^{
150
- [self clearMenus:ownerId];
151
-
152
- NSMenu *mainMenu = NSApp.mainMenu;
153
- if (!mainMenu || ownerId.length == 0) {
154
- return;
155
- }
156
-
157
- NSMutableArray<NSMenuItem *> *installedRoots = [NSMutableArray array];
158
- NSMutableArray<NSMenuItem *> *installedMergedItems = [NSMutableArray array];
159
- NSMutableArray<NSDictionary *> *installedBoundItems = [NSMutableArray array];
160
- NSArray *menus = [self parseArrayJSON:menusJson];
161
-
162
- for (NSDictionary *menuConfig in menus) {
163
- if (![menuConfig isKindOfClass:[NSDictionary class]]) {
164
- continue;
165
- }
166
-
167
- NSString *menuId = [menuConfig[@"id"] isKindOfClass:[NSString class]] ? menuConfig[@"id"] : nil;
168
- NSString *title = [menuConfig[@"title"] isKindOfClass:[NSString class]] ? menuConfig[@"title"] : menuId;
169
- if (menuId.length == 0 || title.length == 0) {
170
- continue;
171
- }
172
-
173
- NSString *systemMenu = [menuConfig[@"systemMenu"] isKindOfClass:[NSString class]] ? menuConfig[@"systemMenu"] : nil;
174
- NSMenuItem *rootItem = nil;
175
- if ([systemMenu isEqualToString:@"app"] && mainMenu.numberOfItems > 0) {
176
- rootItem = [mainMenu itemAtIndex:0];
177
- } else {
178
- rootItem = [mainMenu itemWithTitle:title];
179
- }
180
- BOOL isMergedMenu = rootItem.submenu != nil;
181
- NSMenu *submenu = rootItem.submenu;
182
-
183
- if (!submenu) {
184
- if (systemMenu.length > 0) {
185
- continue;
186
- }
187
- rootItem = [[NSMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""];
188
- submenu = [[NSMenu alloc] initWithTitle:title];
189
- rootItem.submenu = submenu;
190
- }
191
-
192
- NSArray *items = [menuConfig[@"items"] isKindOfClass:[NSArray class]] ? menuConfig[@"items"] : @[];
193
- for (NSDictionary *itemConfig in items) {
194
- if (![itemConfig isKindOfClass:[NSDictionary class]]) {
195
- continue;
196
- }
197
-
198
- NSMenuItem *targetItem = [self targetItemForConfig:itemConfig inMenu:submenu];
199
- if (targetItem) {
200
- [self bindExistingItem:targetItem config:itemConfig ownerId:ownerId menuId:menuId boundRecords:installedBoundItems];
201
- } else if ([self configTargetsExistingItem:itemConfig]) {
202
- continue;
81
+ - (void)apply:(NSDictionary *)config to:(NSMenuItem *)item {
82
+ if (config[@"title"]) item.title = config[@"title"];
83
+ if (config[@"enabled"]) item.enabled = [config[@"enabled"] boolValue];
84
+ if (config[@"hidden"]) item.hidden = [config[@"hidden"] boolValue];
85
+ if (config[@"checked"]) item.state = [config[@"checked"] boolValue] ? NSControlStateValueOn : NSControlStateValueOff;
86
+ NSDictionary *shortcut = config[@"shortcut"];
87
+ if (shortcut) { item.keyEquivalent = shortcut[@"key"]; item.keyEquivalentModifierMask = [shortcut[@"modifiers"] unsignedIntegerValue]; }
88
+ if (config[@"systemImageName"] || config[@"imagePath"]) {
89
+ NSImage *image = config[@"systemImageName"] ? [NSImage imageWithSystemSymbolName:config[@"systemImageName"] accessibilityDescription:item.title] : [[NSImage alloc] initWithContentsOfFile:config[@"imagePath"]];
90
+ if (!image) Fail(@"E_INVALID_ARGUMENT", @"Menu image could not be loaded");
91
+ item.image = image;
92
+ }
93
+ }
94
+ - (void)install:(NSArray *)configs into:(NSMenu *)menu root:(BOOL)root {
95
+ for (NSDictionary *config in configs) {
96
+ if ([config[@"separator"] boolValue]) {
97
+ NSMenuItem *item = NSMenuItem.separatorItem; [menu addItem:item]; [self.undo addObject:^{ if (item.menu) [item.menu removeItem:item]; }]; continue;
98
+ }
99
+ NSDictionary *target = config[@"target"];
100
+ NSMenuItem *item = Find(menu, target ?: @{ @"id": config[@"id"] }, target && !root);
101
+ BOOL existing = item != nil;
102
+ if (target && !existing && !(root && target[@"menu"])) Fail(@"E_NOT_FOUND", @"Menu target was not found");
103
+ if (existing) [self save:item];
104
+ else {
105
+ NSString *label = config[@"title"] ?: config[@"role"];
106
+ item = [[NSMenuItem alloc] initWithTitle:label action:nil keyEquivalent:@""];
107
+ item.identifier = config[@"id"];
108
+ if (root && target[@"menu"]) objc_setAssociatedObject(item, &LocationKey, target[@"menu"], OBJC_ASSOCIATION_COPY_NONATOMIC);
109
+ [menu insertItem:item atIndex:[self index:config[@"placement"] inMenu:menu]];
110
+ [self.undo addObject:^{ if (item.menu) [item.menu removeItem:item]; }];
111
+ }
112
+ if (config[@"items"]) {
113
+ if (existing && !item.submenu) Fail(@"E_INVALID_ARGUMENT", @"Submenu target is not a submenu");
114
+ if (!item.submenu) item.submenu = [[NSMenu alloc] initWithTitle:item.title];
115
+ [self apply:config to:item];
116
+ [self install:config[@"items"] into:item.submenu root:NO];
117
+ } else {
118
+ if (item.submenu && ![config[@"role"] isEqual:@"services"]) Fail(@"E_INVALID_ARGUMENT", @"An action cannot replace a submenu; target it with a submenu item");
119
+ NSString *oldRole = Role(item); if (oldRole) objc_setAssociatedObject(item, &RoleKey, oldRole, OBJC_ASSOCIATION_COPY_NONATOMIC);
120
+ if (config[@"role"]) {
121
+ NSString *role = config[@"role"];
122
+ if ([role isEqual:@"services"]) {
123
+ if (!existing) Fail(@"E_UNSUPPORTED_OPTION", @"Services must target the native services submenu");
203
124
  } else {
204
- NSMenuItem *installedItem = [self appendItem:itemConfig ownerId:ownerId menuId:menuId toMenu:submenu];
205
- if (isMergedMenu && installedItem) {
206
- [installedMergedItems addObject:installedItem];
207
- }
125
+ item.action = NSSelectorFromString(RoleSelectors()[role]); item.target = nil;
126
+ objc_setAssociatedObject(item, &RoleKey, role, OBJC_ASSOCIATION_COPY_NONATOMIC);
208
127
  }
128
+ } else {
129
+ item.action = @selector(selected:); item.target = self;
130
+ item.representedObject = @{ @"ownerId": config[@"_sparkOwner"], @"itemId": config[@"id"] };
209
131
  }
210
-
211
- [self normalizeMenuItemLayout:submenu];
212
-
213
- if (!isMergedMenu) {
214
- NSInteger insertIndex = [self insertionIndexForMenuConfig:menuConfig mainMenu:mainMenu];
215
- [mainMenu insertItem:rootItem atIndex:insertIndex];
216
- [installedRoots addObject:rootItem];
217
- }
218
- }
219
-
220
- self.ownerMenus[ownerId] = installedRoots;
221
- self.ownerMergedMenuItems[ownerId] = installedMergedItems;
222
- self.ownerBoundMenuItems[ownerId] = installedBoundItems;
223
- });
224
- #endif
225
- }
226
-
227
- - (void)updateMenuItems:(NSString *)ownerId patchesJson:(NSString *)patchesJson
228
- {
229
- #if TARGET_OS_OSX
230
- RCTExecuteOnMainQueue(^{
231
- NSArray *patches = [self parseArrayJSON:patchesJson];
232
- for (NSDictionary *patch in patches) {
233
- if (![patch isKindOfClass:[NSDictionary class]]) {
234
- continue;
235
- }
236
- NSString *itemId = [patch[@"id"] isKindOfClass:[NSString class]] ? patch[@"id"] : nil;
237
- NSMenuItem *item = self.menuItemsByKey[[self itemKeyForOwner:ownerId itemId:itemId]];
238
- if (!item) {
239
- continue;
240
- }
241
- [self applyItemConfig:patch toMenuItem:item];
242
- }
243
- });
244
- #endif
245
- }
246
-
247
- - (void)clearMenus:(NSString *)ownerId
248
- {
249
- #if TARGET_OS_OSX
250
- RCTExecuteOnMainQueue(^{
251
- NSArray<NSMenuItem *> *mergedItems = self.ownerMergedMenuItems[ownerId] ?: @[];
252
- for (NSMenuItem *item in mergedItems) {
253
- if (item.menu) {
254
- [item.menu removeItem:item];
255
- }
256
- }
257
- [self.ownerMergedMenuItems removeObjectForKey:ownerId];
258
-
259
- NSArray<NSDictionary *> *boundItems = self.ownerBoundMenuItems[ownerId] ?: @[];
260
- for (NSDictionary *record in boundItems) {
261
- [self restoreBoundItem:record];
262
- }
263
- [self.ownerBoundMenuItems removeObjectForKey:ownerId];
264
-
265
- NSArray<NSMenuItem *> *rootItems = self.ownerMenus[ownerId] ?: @[];
266
- for (NSMenuItem *item in rootItems) {
267
- if (item.menu) {
268
- [item.menu removeItem:item];
269
- }
132
+ [self apply:config to:item];
133
+ if (config[@"role"] && !item.enabled) item.action = nil;
270
134
  }
271
- [self.ownerMenus removeObjectForKey:ownerId];
272
-
273
- NSString *prefix = [NSString stringWithFormat:@"%@:", ownerId ?: @""];
274
- for (NSString *key in self.menuItemsByKey.allKeys.copy) {
275
- if ([key hasPrefix:prefix]) {
276
- [self.menuItemsByKey removeObjectForKey:key];
277
- }
135
+ if (existing && config[@"placement"]) {
136
+ NSMenu *parent = item.menu;
137
+ // Resolve before removal so self-target placement remains a no-op.
138
+ NSInteger old = [parent indexOfItem:item], position = [self index:config[@"placement"] inMenu:parent];
139
+ [parent removeItem:item]; if (old < position) position--; [parent insertItem:item atIndex:position];
278
140
  }
279
- });
280
- #endif
141
+ }
281
142
  }
282
-
283
- - (void)clearAllMenus
284
- {
285
- #if TARGET_OS_OSX
143
+ - (void)publish:(NSString *)json resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
286
144
  RCTExecuteOnMainQueue(^{
287
- for (NSString *ownerId in self.ownerMenus.allKeys.copy) {
288
- [self clearMenus:ownerId];
145
+ NSError *error = nil; id value = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error];
146
+ if (![value isKindOfClass:NSArray.class]) { reject(@"E_INVALID_ARGUMENT", @"Expected menu array", error); return; }
147
+ if (!NSApp.mainMenu) { reject(@"E_UNAVAILABLE", @"Native application menu is unavailable", nil); return; }
148
+ if ([value isEqual:self.published]) { resolve(nil); return; }
149
+ [self identifyNativeMenus];
150
+ NSArray *previous = self.published;
151
+ [self restore];
152
+ @try { [self install:value into:NSApp.mainMenu root:YES]; self.published = value; resolve(nil); }
153
+ @catch (NSException *failure) {
154
+ [self restore];
155
+ @try { [self install:previous into:NSApp.mainMenu root:YES]; }
156
+ @catch (NSException *rollback) { [self restore]; self.published = @[]; reject(@"E_NATIVE", [NSString stringWithFormat:@"Menu publication failed (%@); restoring previous menus also failed (%@)", failure.reason, rollback.reason], nil); return; }
157
+ reject([failure.name hasPrefix:@"E_"] ? failure.name : @"E_NATIVE", failure.reason, nil);
289
158
  }
290
159
  });
291
- #endif
292
- }
293
-
294
- #if TARGET_OS_OSX
295
- - (NSString *)normalizedMenuTitle:(NSString *)title
296
- {
297
- return [title stringByReplacingOccurrencesOfString:@"..." withString:@"…"];
298
- }
299
-
300
- - (NSMenuItem *)targetItemForConfig:(NSDictionary *)config inMenu:(NSMenu *)menu
301
- {
302
- NSArray *targetPath = [config[@"targetPath"] isKindOfClass:[NSArray class]] ? config[@"targetPath"] : nil;
303
- if (targetPath.count > 0) {
304
- NSMenu *currentMenu = menu;
305
- NSMenuItem *targetItem = nil;
306
-
307
- for (NSUInteger index = 0; index < targetPath.count; index++) {
308
- NSString *pathTitle = [targetPath[index] isKindOfClass:[NSString class]] ? targetPath[index] : nil;
309
- if (pathTitle.length == 0 || !currentMenu) {
310
- return nil;
311
- }
312
-
313
- NSString *normalizedPathTitle = [self normalizedMenuTitle:pathTitle];
314
- targetItem = nil;
315
- for (NSMenuItem *item in currentMenu.itemArray) {
316
- if ([[self normalizedMenuTitle:item.title ?: @""] isEqualToString:normalizedPathTitle]) {
317
- targetItem = item;
318
- break;
319
- }
320
- }
321
-
322
- if (!targetItem) {
323
- return nil;
324
- }
325
-
326
- if (index < targetPath.count - 1) {
327
- currentMenu = targetItem.submenu;
328
- }
329
- }
330
-
331
- return targetItem;
332
- }
333
-
334
- NSMutableArray<NSString *> *targetTitles = [NSMutableArray array];
335
- NSString *targetTitle = [config[@"targetTitle"] isKindOfClass:[NSString class]] ? config[@"targetTitle"] : nil;
336
- if (targetTitle.length > 0) {
337
- [targetTitles addObject:targetTitle];
338
- }
339
-
340
- NSArray *additionalTargetTitles = [config[@"targetTitles"] isKindOfClass:[NSArray class]] ? config[@"targetTitles"] : nil;
341
- for (id title in additionalTargetTitles) {
342
- if ([title isKindOfClass:[NSString class]] && [title length] > 0) {
343
- [targetTitles addObject:title];
344
- }
345
- }
346
-
347
- if (targetTitles.count == 0) {
348
- return nil;
349
- }
350
-
351
- for (NSString *candidateTitle in targetTitles) {
352
- NSString *normalizedTargetTitle = [self normalizedMenuTitle:candidateTitle];
353
- for (NSMenuItem *item in menu.itemArray) {
354
- if ([[self normalizedMenuTitle:item.title ?: @""] isEqualToString:normalizedTargetTitle]) {
355
- return item;
356
- }
357
- }
358
- }
359
- return nil;
360
- }
361
-
362
- - (BOOL)configTargetsExistingItem:(NSDictionary *)config
363
- {
364
- NSString *targetTitle = [config[@"targetTitle"] isKindOfClass:[NSString class]] ? config[@"targetTitle"] : nil;
365
- NSArray *targetTitles = [config[@"targetTitles"] isKindOfClass:[NSArray class]] ? config[@"targetTitles"] : nil;
366
- NSArray *targetPath = [config[@"targetPath"] isKindOfClass:[NSArray class]] ? config[@"targetPath"] : nil;
367
- return targetTitle.length > 0 || targetTitles.count > 0 || targetPath.count > 0;
368
- }
369
-
370
- - (NSDictionary *)representedObjectForConfig:(NSDictionary *)config ownerId:(NSString *)ownerId menuId:(NSString *)menuId
371
- {
372
- NSString *itemId = [config[@"id"] isKindOfClass:[NSString class]] ? config[@"id"] : @"";
373
- return @{
374
- @"ownerId": ownerId ?: @"",
375
- @"menuId": menuId ?: @"",
376
- @"itemId": itemId ?: @"",
377
- @"payload": [config[@"payload"] isKindOfClass:[NSDictionary class]] ? config[@"payload"] : @{},
378
- };
379
- }
380
-
381
- - (void)bindExistingItem:(NSMenuItem *)item
382
- config:(NSDictionary *)config
383
- ownerId:(NSString *)ownerId
384
- menuId:(NSString *)menuId
385
- boundRecords:(NSMutableArray *)boundRecords
386
- {
387
- NSString *itemId = [config[@"id"] isKindOfClass:[NSString class]] ? config[@"id"] : nil;
388
- if (itemId.length == 0) {
389
- return;
390
- }
391
-
392
- BOOL shouldPreserveNativeAction = [config[@"targetPath"] isKindOfClass:[NSArray class]];
393
- NSDictionary *payload = [self representedObjectForConfig:config ownerId:ownerId menuId:menuId];
394
- [boundRecords addObject:@{
395
- @"item": item,
396
- @"target": item.target ?: (id)kCFNull,
397
- @"action": item.action ? NSStringFromSelector(item.action) : @"",
398
- @"representedObject": item.representedObject ?: (id)kCFNull,
399
- @"enabled": @(item.enabled),
400
- @"hidden": @(item.hidden),
401
- @"state": @(item.state),
402
- @"title": item.title ?: @"",
403
- @"keyEquivalent": item.keyEquivalent ?: @"",
404
- @"keyEquivalentModifierMask": @(item.keyEquivalentModifierMask),
405
- @"submenu": item.submenu ?: (id)kCFNull,
406
- @"menu": item.menu ?: (id)kCFNull,
407
- @"index": @(item.menu ? [item.menu indexOfItem:item] : -1),
408
- }];
409
-
410
- [self moveExistingItem:item config:config inMenu:item.menu];
411
-
412
- if (shouldPreserveNativeAction) {
413
- [RNNativeMenuBoundMenuItems() setObject:payload forKey:item];
414
- } else {
415
- item.target = self;
416
- item.action = @selector(handleMenuAction:);
417
- item.representedObject = payload;
418
- }
419
- [self applyItemConfig:config toMenuItem:item];
420
- self.menuItemsByKey[[self itemKeyForOwner:ownerId itemId:itemId]] = item;
421
- }
422
-
423
- - (void)restoreBoundItem:(NSDictionary *)record
424
- {
425
- NSMenuItem *item = [record[@"item"] isKindOfClass:[NSMenuItem class]] ? record[@"item"] : nil;
426
- if (!item) {
427
- return;
428
- }
429
-
430
- [RNNativeMenuBoundMenuItems() removeObjectForKey:item];
431
-
432
- id target = record[@"target"];
433
- item.target = target == (id)kCFNull ? nil : target;
434
-
435
- NSString *action = [record[@"action"] isKindOfClass:[NSString class]] ? record[@"action"] : @"";
436
- item.action = action.length > 0 ? NSSelectorFromString(action) : nil;
437
-
438
- id representedObject = record[@"representedObject"];
439
- item.representedObject = representedObject == (id)kCFNull ? nil : representedObject;
440
- item.enabled = [record[@"enabled"] boolValue];
441
- item.hidden = [record[@"hidden"] boolValue];
442
- item.state = [record[@"state"] integerValue];
443
- item.title = [record[@"title"] isKindOfClass:[NSString class]] ? record[@"title"] : item.title;
444
- item.keyEquivalent = [record[@"keyEquivalent"] isKindOfClass:[NSString class]] ? record[@"keyEquivalent"] : item.keyEquivalent;
445
- item.keyEquivalentModifierMask = [record[@"keyEquivalentModifierMask"] unsignedIntegerValue];
446
-
447
- id submenu = record[@"submenu"];
448
- item.submenu = submenu == (id)kCFNull ? nil : submenu;
449
-
450
- id menu = record[@"menu"];
451
- NSInteger index = [record[@"index"] integerValue];
452
- if (menu != (id)kCFNull && [menu isKindOfClass:[NSMenu class]] && item.menu == menu && index >= 0) {
453
- NSMenu *originalMenu = (NSMenu *)menu;
454
- NSInteger currentIndex = [originalMenu indexOfItem:item];
455
- if (currentIndex >= 0 && currentIndex != index) {
456
- [originalMenu removeItem:item];
457
- NSInteger restoredIndex = MIN(index, originalMenu.numberOfItems);
458
- [originalMenu insertItem:item atIndex:restoredIndex];
459
- }
460
- }
461
- }
462
-
463
- - (NSInteger)insertionIndexForMenuConfig:(NSDictionary *)menuConfig mainMenu:(NSMenu *)mainMenu
464
- {
465
- NSDictionary *placement = [menuConfig[@"placement"] isKindOfClass:[NSDictionary class]] ? menuConfig[@"placement"] : nil;
466
- NSString *before = [placement[@"before"] isKindOfClass:[NSString class]] ? placement[@"before"] : nil;
467
- NSString *after = [placement[@"after"] isKindOfClass:[NSString class]] ? placement[@"after"] : nil;
468
-
469
- if (before.length > 0) {
470
- NSInteger index = [mainMenu indexOfItemWithTitle:before];
471
- if (index >= 0) {
472
- return index;
473
- }
474
- }
475
-
476
- if (after.length > 0) {
477
- NSInteger index = [mainMenu indexOfItemWithTitle:after];
478
- if (index >= 0) {
479
- return MIN(index + 1, mainMenu.numberOfItems);
480
- }
481
- }
482
-
483
- NSInteger windowIndex = [mainMenu indexOfItemWithTitle:@"Window"];
484
- return windowIndex >= 0 ? windowIndex : mainMenu.numberOfItems;
485
- }
486
-
487
- - (NSInteger)indexOfItemMatchingTitle:(NSString *)title inMenu:(NSMenu *)menu
488
- {
489
- if (title.length == 0) {
490
- return -1;
491
- }
492
-
493
- NSString *normalizedTitle = [self normalizedMenuTitle:title];
494
- for (NSInteger index = 0; index < menu.numberOfItems; index += 1) {
495
- NSMenuItem *item = [menu itemAtIndex:index];
496
- if ([[self normalizedMenuTitle:item.title ?: @""] isEqualToString:normalizedTitle]) {
497
- return index;
498
- }
499
- }
500
-
501
- return -1;
502
- }
503
-
504
- - (NSInteger)insertionIndexForItemConfig:(NSDictionary *)config inMenu:(NSMenu *)menu
505
- {
506
- NSDictionary *placement = [config[@"placement"] isKindOfClass:[NSDictionary class]] ? config[@"placement"] : nil;
507
- NSString *before = [placement[@"before"] isKindOfClass:[NSString class]] ? placement[@"before"] : nil;
508
- NSString *after = [placement[@"after"] isKindOfClass:[NSString class]] ? placement[@"after"] : nil;
509
-
510
- if (before.length > 0) {
511
- NSInteger index = [self indexOfItemMatchingTitle:before inMenu:menu];
512
- if (index >= 0) {
513
- return index;
514
- }
515
- }
516
-
517
- if (after.length > 0) {
518
- NSInteger index = [self indexOfItemMatchingTitle:after inMenu:menu];
519
- if (index >= 0) {
520
- return MIN(index + 1, menu.numberOfItems);
521
- }
522
- }
523
-
524
- return menu.numberOfItems;
525
160
  }
526
-
527
- - (void)moveExistingItem:(NSMenuItem *)item config:(NSDictionary *)config inMenu:(NSMenu *)menu
528
- {
529
- NSDictionary *placement = [config[@"placement"] isKindOfClass:[NSDictionary class]] ? config[@"placement"] : nil;
530
- NSString *before = [placement[@"before"] isKindOfClass:[NSString class]] ? placement[@"before"] : nil;
531
- NSString *after = [placement[@"after"] isKindOfClass:[NSString class]] ? placement[@"after"] : nil;
532
- if (!menu || (before.length == 0 && after.length == 0)) {
533
- return;
534
- }
535
-
536
- NSInteger currentIndex = [menu indexOfItem:item];
537
- NSInteger insertionIndex = [self insertionIndexForItemConfig:config inMenu:menu];
538
- if (currentIndex < 0 || insertionIndex < 0 || currentIndex == insertionIndex) {
539
- return;
540
- }
541
-
542
- [menu removeItem:item];
543
- if (currentIndex < insertionIndex) {
544
- insertionIndex -= 1;
545
- }
546
- insertionIndex = MAX(0, MIN(insertionIndex, menu.numberOfItems));
547
- [menu insertItem:item atIndex:insertionIndex];
548
- }
549
-
550
- - (void)normalizeMenuItemLayout:(NSMenu *)menu
551
- {
552
- for (NSMenuItem *item in menu.itemArray) {
553
- if (!item.separatorItem) {
554
- item.image = nil;
555
- item.indentationLevel = 0;
556
- }
557
- }
558
- }
559
-
560
- - (NSMenuItem *)appendItem:(NSDictionary *)config ownerId:(NSString *)ownerId menuId:(NSString *)menuId toMenu:(NSMenu *)menu
561
- {
562
- NSInteger insertionIndex = [self insertionIndexForItemConfig:config inMenu:menu];
563
- if ([config[@"separator"] boolValue]) {
564
- NSMenuItem *separator = [NSMenuItem separatorItem];
565
- [menu insertItem:separator atIndex:insertionIndex];
566
- return separator;
567
- }
568
-
569
- NSString *itemId = [config[@"id"] isKindOfClass:[NSString class]] ? config[@"id"] : nil;
570
- NSString *title = [config[@"title"] isKindOfClass:[NSString class]] ? config[@"title"] : itemId;
571
- if (itemId.length == 0 || title.length == 0) {
572
- return nil;
573
- }
574
-
575
- NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title action:@selector(handleMenuAction:) keyEquivalent:@""];
576
- item.target = self;
577
- item.representedObject = [self representedObjectForConfig:config ownerId:ownerId menuId:menuId];
578
- [self applyItemConfig:config toMenuItem:item];
579
- [menu insertItem:item atIndex:insertionIndex];
580
- self.menuItemsByKey[[self itemKeyForOwner:ownerId itemId:itemId]] = item;
581
- return item;
582
- }
583
-
584
- - (void)applyItemConfig:(NSDictionary *)config toMenuItem:(NSMenuItem *)item
585
- {
586
- NSString *title = [config[@"title"] isKindOfClass:[NSString class]] ? config[@"title"] : nil;
587
- if (title) {
588
- item.title = title;
589
- }
590
-
591
- NSNumber *enabled = [config[@"enabled"] isKindOfClass:[NSNumber class]] ? config[@"enabled"] : nil;
592
- if (enabled) {
593
- item.enabled = enabled.boolValue;
594
- }
595
-
596
- NSNumber *hidden = [config[@"hidden"] isKindOfClass:[NSNumber class]] ? config[@"hidden"] : nil;
597
- if (hidden) {
598
- item.hidden = hidden.boolValue;
599
- }
600
-
601
- NSNumber *checked = [config[@"checked"] isKindOfClass:[NSNumber class]] ? config[@"checked"] : nil;
602
- if (checked) {
603
- item.state = checked.boolValue ? NSControlStateValueOn : NSControlStateValueOff;
604
- }
605
-
606
- NSDictionary *shortcut = [config[@"shortcut"] isKindOfClass:[NSDictionary class]] ? config[@"shortcut"] : nil;
607
- if (shortcut || config[@"shortcut"] == (id)kCFNull) {
608
- NSString *key = [shortcut[@"key"] isKindOfClass:[NSString class]] ? shortcut[@"key"] : @"";
609
- NSNumber *modifiers = [shortcut[@"modifiers"] isKindOfClass:[NSNumber class]] ? shortcut[@"modifiers"] : @0;
610
- item.keyEquivalent = key ?: @"";
611
- item.keyEquivalentModifierMask = key.length > 0 ? modifiers.unsignedIntegerValue : 0;
612
- }
613
- }
614
-
615
- - (void)handleMenuAction:(NSMenuItem *)sender
616
- {
617
- NSDictionary *payload = [sender.representedObject isKindOfClass:[NSDictionary class]] ? sender.representedObject : @{};
618
- [self sendEventWithName:@"NativeMenuAction" body:payload];
619
- }
620
-
621
- - (void)emitBoundMenuItemAction:(NSDictionary *)payload
622
- {
623
- [self sendEventWithName:@"NativeMenuAction" body:payload];
624
- }
625
-
626
- - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
627
- {
628
- return menuItem.enabled;
629
- }
630
- #endif
631
-
161
+ - (void)selected:(NSMenuItem *)sender { if (sender.enabled && !sender.hidden) [self sendEventWithName:@"NativeMenuAction" body:sender.representedObject]; }
162
+ - (BOOL)validateMenuItem:(NSMenuItem *)item { return item.enabled; }
163
+ - (void)invalidate { RCTExecuteOnMainQueue(^{ [self restore]; self.published = @[]; }); [super invalidate]; }
632
164
  @end