basalt-core 0.0.0-rc5

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 (325) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli/basalt.d.ts +3 -0
  3. package/dist/cli/basalt.d.ts.map +1 -0
  4. package/dist/cli/basalt.js +47 -0
  5. package/dist/cli/basalt.js.map +1 -0
  6. package/dist/cli/bundle-cli.d.ts +12 -0
  7. package/dist/cli/bundle-cli.d.ts.map +1 -0
  8. package/dist/cli/bundle-cli.js +124 -0
  9. package/dist/cli/bundle-cli.js.map +1 -0
  10. package/dist/cli/bundleWithAssets.d.ts +79 -0
  11. package/dist/cli/bundleWithAssets.d.ts.map +1 -0
  12. package/dist/cli/bundleWithAssets.js +198 -0
  13. package/dist/cli/bundleWithAssets.js.map +1 -0
  14. package/dist/cli/desktop.d.ts +339 -0
  15. package/dist/cli/desktop.d.ts.map +1 -0
  16. package/dist/cli/desktop.js +1029 -0
  17. package/dist/cli/desktop.js.map +1 -0
  18. package/dist/cli/doctor.d.ts +31 -0
  19. package/dist/cli/doctor.d.ts.map +1 -0
  20. package/dist/cli/doctor.js +283 -0
  21. package/dist/cli/doctor.js.map +1 -0
  22. package/dist/cli/init.d.ts +77 -0
  23. package/dist/cli/init.d.ts.map +1 -0
  24. package/dist/cli/init.js +442 -0
  25. package/dist/cli/init.js.map +1 -0
  26. package/dist/cli/metro.d.ts +49 -0
  27. package/dist/cli/metro.d.ts.map +1 -0
  28. package/dist/cli/metro.js +148 -0
  29. package/dist/cli/metro.js.map +1 -0
  30. package/dist/cli/packageApp.d.ts +102 -0
  31. package/dist/cli/packageApp.d.ts.map +1 -0
  32. package/dist/cli/packageApp.js +319 -0
  33. package/dist/cli/packageApp.js.map +1 -0
  34. package/dist/cli/steps.d.ts +34 -0
  35. package/dist/cli/steps.d.ts.map +1 -0
  36. package/dist/cli/steps.js +53 -0
  37. package/dist/cli/steps.js.map +1 -0
  38. package/dist/metro-config.d.ts +168 -0
  39. package/dist/metro-config.d.ts.map +1 -0
  40. package/dist/metro-config.js +671 -0
  41. package/dist/metro-config.js.map +1 -0
  42. package/dist/react-native.config.d.ts +7 -0
  43. package/dist/react-native.config.d.ts.map +1 -0
  44. package/dist/react-native.config.js +80 -0
  45. package/dist/react-native.config.js.map +1 -0
  46. package/dist/src/DragSource.d.ts +52 -0
  47. package/dist/src/DragSource.d.ts.map +1 -0
  48. package/dist/src/DragSource.js +97 -0
  49. package/dist/src/DragSource.js.map +1 -0
  50. package/dist/src/DropTarget.d.ts +58 -0
  51. package/dist/src/DropTarget.d.ts.map +1 -0
  52. package/dist/src/DropTarget.js +131 -0
  53. package/dist/src/DropTarget.js.map +1 -0
  54. package/dist/src/KeyHandler.d.ts +57 -0
  55. package/dist/src/KeyHandler.d.ts.map +1 -0
  56. package/dist/src/KeyHandler.js +48 -0
  57. package/dist/src/KeyHandler.js.map +1 -0
  58. package/dist/src/Menu.d.ts +72 -0
  59. package/dist/src/Menu.d.ts.map +1 -0
  60. package/dist/src/Menu.js +179 -0
  61. package/dist/src/Menu.js.map +1 -0
  62. package/dist/src/TitleBar.d.ts +75 -0
  63. package/dist/src/TitleBar.d.ts.map +1 -0
  64. package/dist/src/TitleBar.js +236 -0
  65. package/dist/src/TitleBar.js.map +1 -0
  66. package/dist/src/Window.d.ts +87 -0
  67. package/dist/src/Window.d.ts.map +1 -0
  68. package/dist/src/Window.js +248 -0
  69. package/dist/src/Window.js.map +1 -0
  70. package/dist/src/closeRequest.d.ts +55 -0
  71. package/dist/src/closeRequest.d.ts.map +1 -0
  72. package/dist/src/closeRequest.js +134 -0
  73. package/dist/src/closeRequest.js.map +1 -0
  74. package/dist/src/index.d.ts +45 -0
  75. package/dist/src/index.d.ts.map +1 -0
  76. package/dist/src/index.js +92 -0
  77. package/dist/src/index.js.map +1 -0
  78. package/dist/src/overrides/Alert.d.ts +69 -0
  79. package/dist/src/overrides/Alert.d.ts.map +1 -0
  80. package/dist/src/overrides/Alert.js +112 -0
  81. package/dist/src/overrides/Alert.js.map +1 -0
  82. package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
  83. package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
  84. package/dist/src/overrides/BaseViewConfig.js +72 -0
  85. package/dist/src/overrides/BaseViewConfig.js.map +1 -0
  86. package/dist/src/overrides/Platform.linux.d.ts +10 -0
  87. package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
  88. package/dist/src/overrides/Platform.linux.js +15 -0
  89. package/dist/src/overrides/Platform.linux.js.map +1 -0
  90. package/dist/src/overrides/Platform.macos.d.ts +10 -0
  91. package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
  92. package/dist/src/overrides/Platform.macos.js +15 -0
  93. package/dist/src/overrides/Platform.macos.js.map +1 -0
  94. package/dist/src/overrides/Platform.windows.d.ts +10 -0
  95. package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
  96. package/dist/src/overrides/Platform.windows.js +15 -0
  97. package/dist/src/overrides/Platform.windows.js.map +1 -0
  98. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
  99. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
  100. package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
  101. package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
  102. package/dist/src/overrides/RefreshControl.d.ts +52 -0
  103. package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
  104. package/dist/src/overrides/RefreshControl.js +92 -0
  105. package/dist/src/overrides/RefreshControl.js.map +1 -0
  106. package/dist/src/overrides/ScrollView.d.ts +39 -0
  107. package/dist/src/overrides/ScrollView.d.ts.map +1 -0
  108. package/dist/src/overrides/ScrollView.js +59 -0
  109. package/dist/src/overrides/ScrollView.js.map +1 -0
  110. package/dist/src/overrides/Share.d.ts +96 -0
  111. package/dist/src/overrides/Share.d.ts.map +1 -0
  112. package/dist/src/overrides/Share.js +97 -0
  113. package/dist/src/overrides/Share.js.map +1 -0
  114. package/dist/src/overrides/TextInput.d.ts +81 -0
  115. package/dist/src/overrides/TextInput.d.ts.map +1 -0
  116. package/dist/src/overrides/TextInput.js +213 -0
  117. package/dist/src/overrides/TextInput.js.map +1 -0
  118. package/dist/src/overrides/View.d.ts +36 -0
  119. package/dist/src/overrides/View.d.ts.map +1 -0
  120. package/dist/src/overrides/View.js +99 -0
  121. package/dist/src/overrides/View.js.map +1 -0
  122. package/dist/src/overrides/createPlatform.d.ts +32 -0
  123. package/dist/src/overrides/createPlatform.d.ts.map +1 -0
  124. package/dist/src/overrides/createPlatform.js +84 -0
  125. package/dist/src/overrides/createPlatform.js.map +1 -0
  126. package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
  127. package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
  128. package/dist/src/overrides/setUpDeveloperTools.js +84 -0
  129. package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
  130. package/dist/src/overrides/setUpXHR.d.ts +46 -0
  131. package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
  132. package/dist/src/overrides/setUpXHR.js +236 -0
  133. package/dist/src/overrides/setUpXHR.js.map +1 -0
  134. package/dist/src/quitRequest.d.ts +55 -0
  135. package/dist/src/quitRequest.d.ts.map +1 -0
  136. package/dist/src/quitRequest.js +121 -0
  137. package/dist/src/quitRequest.js.map +1 -0
  138. package/dist/src/resourcePath.d.ts +18 -0
  139. package/dist/src/resourcePath.d.ts.map +1 -0
  140. package/dist/src/resourcePath.js +31 -0
  141. package/dist/src/resourcePath.js.map +1 -0
  142. package/dist/src/responseBody.d.ts +76 -0
  143. package/dist/src/responseBody.d.ts.map +1 -0
  144. package/dist/src/responseBody.js +94 -0
  145. package/dist/src/responseBody.js.map +1 -0
  146. package/dist/src/responseHeaders.d.ts +40 -0
  147. package/dist/src/responseHeaders.d.ts.map +1 -0
  148. package/dist/src/responseHeaders.js +75 -0
  149. package/dist/src/responseHeaders.js.map +1 -0
  150. package/dist/src/titleBarState.d.ts +43 -0
  151. package/dist/src/titleBarState.d.ts.map +1 -0
  152. package/dist/src/titleBarState.js +77 -0
  153. package/dist/src/titleBarState.js.map +1 -0
  154. package/dist/src/useContextMenu.d.ts +113 -0
  155. package/dist/src/useContextMenu.d.ts.map +1 -0
  156. package/dist/src/useContextMenu.js +109 -0
  157. package/dist/src/useContextMenu.js.map +1 -0
  158. package/dist/src/useDialog.d.ts +98 -0
  159. package/dist/src/useDialog.d.ts.map +1 -0
  160. package/dist/src/useDialog.js +112 -0
  161. package/dist/src/useDialog.js.map +1 -0
  162. package/dist/src/useDisplays.d.ts +70 -0
  163. package/dist/src/useDisplays.d.ts.map +1 -0
  164. package/dist/src/useDisplays.js +118 -0
  165. package/dist/src/useDisplays.js.map +1 -0
  166. package/dist/src/useHandledKeys.d.ts +38 -0
  167. package/dist/src/useHandledKeys.d.ts.map +1 -0
  168. package/dist/src/useHandledKeys.js +70 -0
  169. package/dist/src/useHandledKeys.js.map +1 -0
  170. package/dist/src/useWindow.d.ts +140 -0
  171. package/dist/src/useWindow.d.ts.map +1 -0
  172. package/dist/src/useWindow.js +233 -0
  173. package/dist/src/useWindow.js.map +1 -0
  174. package/dist/tsconfig.tsbuildinfo +1 -0
  175. package/native/CMakeLists.txt +355 -0
  176. package/native/bootstrap.sh +613 -0
  177. package/native/cmake/CompilerFlags.cmake +49 -0
  178. package/native/cmake/Expo.cmake +125 -0
  179. package/native/cmake/ReactNativeCore.cmake +612 -0
  180. package/native/cmake/Reanimated.cmake +108 -0
  181. package/native/cmake/Skia.cmake +379 -0
  182. package/native/cmake/ThirdParty.cmake +290 -0
  183. package/native/cmake/Worklets.cmake +67 -0
  184. package/native/cmake/codegen/CMakeLists.txt +32 -0
  185. package/native/core/AppIdentity.cpp +85 -0
  186. package/native/core/AppIdentity.h +59 -0
  187. package/native/core/AppPaths.cpp +96 -0
  188. package/native/core/AppPaths.h +53 -0
  189. package/native/core/AppearanceModule.cpp +60 -0
  190. package/native/core/AppearanceModule.h +40 -0
  191. package/native/core/Backface.h +59 -0
  192. package/native/core/BlobModule.cpp +283 -0
  193. package/native/core/BlobModule.h +76 -0
  194. package/native/core/BlobRegistry.cpp +75 -0
  195. package/native/core/BlobRegistry.h +44 -0
  196. package/native/core/ColorScheme.cpp +105 -0
  197. package/native/core/ColorScheme.h +59 -0
  198. package/native/core/ComponentRegistry.h +30 -0
  199. package/native/core/ControlMetrics.h +37 -0
  200. package/native/core/CoreModules.cpp +592 -0
  201. package/native/core/CoreModules.h +308 -0
  202. package/native/core/DebuggingOverlay.cpp +106 -0
  203. package/native/core/DebuggingOverlay.h +65 -0
  204. package/native/core/DesktopControls.cpp +232 -0
  205. package/native/core/DesktopControls.h +170 -0
  206. package/native/core/DevBundle.cpp +108 -0
  207. package/native/core/DevBundle.h +69 -0
  208. package/native/core/DevMenu.cpp +99 -0
  209. package/native/core/DevMenu.h +75 -0
  210. package/native/core/DialogModule.cpp +150 -0
  211. package/native/core/DialogModule.h +51 -0
  212. package/native/core/DragAndDrop.cpp +45 -0
  213. package/native/core/DragAndDrop.h +225 -0
  214. package/native/core/ExpoImageComponent.cpp +79 -0
  215. package/native/core/ExpoImageComponent.h +80 -0
  216. package/native/core/ExpoModules.cpp +415 -0
  217. package/native/core/ExpoModules.h +115 -0
  218. package/native/core/ExpoRuntime.cpp +366 -0
  219. package/native/core/ExpoRuntime.h +42 -0
  220. package/native/core/FocusRing.h +36 -0
  221. package/native/core/FontRegistry.h +51 -0
  222. package/native/core/GestureHandlerModule.cpp +136 -0
  223. package/native/core/GestureHandlerModule.h +71 -0
  224. package/native/core/Gestures.cpp +533 -0
  225. package/native/core/Gestures.h +145 -0
  226. package/native/core/HoverTracker.cpp +69 -0
  227. package/native/core/HoverTracker.h +109 -0
  228. package/native/core/HttpClient.cpp +282 -0
  229. package/native/core/ImageBytes.cpp +205 -0
  230. package/native/core/ImageBytes.h +29 -0
  231. package/native/core/ImageCache.cpp +51 -0
  232. package/native/core/ImageCache.h +89 -0
  233. package/native/core/JsRuntimeAccess.cpp +70 -0
  234. package/native/core/JsRuntimeAccess.h +60 -0
  235. package/native/core/JsiPromise.h +51 -0
  236. package/native/core/KeyEvents.cpp +116 -0
  237. package/native/core/KeyEvents.h +144 -0
  238. package/native/core/LogBoxSurface.cpp +46 -0
  239. package/native/core/LogBoxSurface.h +68 -0
  240. package/native/core/MenuModel.h +117 -0
  241. package/native/core/MenuModule.cpp +209 -0
  242. package/native/core/MenuModule.h +66 -0
  243. package/native/core/MountingWalk.h +524 -0
  244. package/native/core/PackageModules.cpp.in +25 -0
  245. package/native/core/PackageModules.h +50 -0
  246. package/native/core/PlatformConstantsModule.cpp +63 -0
  247. package/native/core/PlatformConstantsModule.h +64 -0
  248. package/native/core/PlatformServices.h +213 -0
  249. package/native/core/PointerButtons.h +63 -0
  250. package/native/core/PullToRefresh.cpp +57 -0
  251. package/native/core/PullToRefresh.h +73 -0
  252. package/native/core/ReanimatedCompat.h +47 -0
  253. package/native/core/ReanimatedModule.cpp +203 -0
  254. package/native/core/ReanimatedModule.h +55 -0
  255. package/native/core/ScrollAnimation.cpp +59 -0
  256. package/native/core/ScrollAnimation.h +70 -0
  257. package/native/core/ScrollBounds.cpp +26 -0
  258. package/native/core/ScrollBounds.h +68 -0
  259. package/native/core/ScrollIndicator.cpp +50 -0
  260. package/native/core/ScrollIndicator.h +69 -0
  261. package/native/core/ScrollMomentum.cpp +71 -0
  262. package/native/core/ScrollMomentum.h +73 -0
  263. package/native/core/ScrollSnap.cpp +123 -0
  264. package/native/core/ScrollSnap.h +80 -0
  265. package/native/core/ShareFallback.cpp +101 -0
  266. package/native/core/ShareFallback.h +45 -0
  267. package/native/core/SkiaPictureViewComponent.cpp +11 -0
  268. package/native/core/SkiaPictureViewComponent.h +47 -0
  269. package/native/core/SourceCodeModule.cpp +55 -0
  270. package/native/core/SourceCodeModule.h +53 -0
  271. package/native/core/StatusBarModule.cpp +23 -0
  272. package/native/core/StatusBarModule.h +42 -0
  273. package/native/core/TestDialog.cpp +148 -0
  274. package/native/core/TestDialog.h +96 -0
  275. package/native/core/TestKeys.cpp +78 -0
  276. package/native/core/TestKeys.h +50 -0
  277. package/native/core/TestQuitFile.cpp +33 -0
  278. package/native/core/TestQuitFile.h +60 -0
  279. package/native/core/TestSettle.h +192 -0
  280. package/native/core/TitleBarRegions.h +31 -0
  281. package/native/core/TurboModuleProxy.cpp +51 -0
  282. package/native/core/TurboModuleProxy.h +46 -0
  283. package/native/core/UIManagerAccess.cpp +43 -0
  284. package/native/core/UIManagerAccess.h +43 -0
  285. package/native/core/WindowBoundsCache.cpp +319 -0
  286. package/native/core/WindowControl.h +235 -0
  287. package/native/core/WindowHost.h +158 -0
  288. package/native/core/WindowMethods.cpp +167 -0
  289. package/native/core/WindowMethods.h +84 -0
  290. package/native/core/WindowsModule.cpp +448 -0
  291. package/native/core/WindowsModule.h +133 -0
  292. package/native/core/WorkletsModule.cpp +199 -0
  293. package/native/core/WorkletsModule.h +57 -0
  294. package/native/core/portability_probe.cpp +210 -0
  295. package/native/tests/EventRecorder.h +124 -0
  296. package/native/tests/TestHarness.cpp +109 -0
  297. package/native/tests/TestHarness.h +89 -0
  298. package/native/tests/TreeDump.h +49 -0
  299. package/native/tests/test_app_identity.cpp +114 -0
  300. package/native/tests/test_app_paths.cpp +48 -0
  301. package/native/tests/test_appearance.cpp +107 -0
  302. package/native/tests/test_backface.cpp +79 -0
  303. package/native/tests/test_blobs.cpp +157 -0
  304. package/native/tests/test_controls.cpp +185 -0
  305. package/native/tests/test_devbundle.cpp +103 -0
  306. package/native/tests/test_drag_and_drop.cpp +143 -0
  307. package/native/tests/test_gestures.cpp +331 -0
  308. package/native/tests/test_hover.cpp +92 -0
  309. package/native/tests/test_image_cache.cpp +109 -0
  310. package/native/tests/test_js_runtime_access.cpp +137 -0
  311. package/native/tests/test_key_events.cpp +235 -0
  312. package/native/tests/test_momentum.cpp +138 -0
  313. package/native/tests/test_quit_file.cpp +104 -0
  314. package/native/tests/test_scroll_animation.cpp +120 -0
  315. package/native/tests/test_scroll_bounds.cpp +66 -0
  316. package/native/tests/test_scroll_indicator.cpp +149 -0
  317. package/native/tests/test_scroll_snap.cpp +112 -0
  318. package/native/tests/test_settle.cpp +115 -0
  319. package/native/tests/test_share.cpp +75 -0
  320. package/native/tests/test_sourcecode.cpp +62 -0
  321. package/native/tests/test_window_close.cpp +119 -0
  322. package/native/tests/test_window_limits.cpp +123 -0
  323. package/package.json +43 -0
  324. package/react-native.config.js +86 -0
  325. package/supported-versions.json +66 -0
@@ -0,0 +1,144 @@
1
+ // Which view claims which key, and what a key is called.
2
+ //
3
+ // `onKeyPress` on a `<TextInput>` existed and a `<View>` had nothing, so an app
4
+ // could not bind a shortcut to anything it draws. On a phone that is no loss; on
5
+ // a desktop it is most of the interface.
6
+ //
7
+ // ## The vocabulary is a browser's
8
+ //
9
+ // A key is named the way `KeyboardEvent.key` names it: `"a"`, `"A"`, `" "`,
10
+ // `"ArrowLeft"`, `"Escape"`, `"F5"`. react-native-macos and react-native-windows
11
+ // both use these, so an app that binds `"ArrowLeft"` means the same thing on all
12
+ // five platforms it might run on. The names live here as constants rather than as
13
+ // string literals in three hosts, for the reason core/TestQuitFile.h gives about
14
+ // shared spellings: three places to write "ArrowLeft" is three places to write
15
+ // "ArrowLeft " instead, and the failure is a shortcut that silently never fires.
16
+ //
17
+ // Case follows the character, as it does in a browser: Shift+A is `"A"` and its
18
+ // `shift` modifier is also set. An app may bind either and both are honest.
19
+ //
20
+ // ## Why a view declares its keys in advance
21
+ //
22
+ // Because a host has to decide whether a key was handled *before* it can do
23
+ // anything else, and it cannot ask JavaScript at that moment. An unhandled key
24
+ // must still reach the application menu, a focused text field, or a scroll view
25
+ // that scrolls on arrows; a handled one must not. That decision is the return
26
+ // value of `performKeyEquivalent:`, of GTK's `key-pressed` handler, and of
27
+ // whether `WM_KEYDOWN` reaches `DefWindowProc` -- three synchronous answers, and
28
+ // a round trip through the JavaScript thread is not available to any of them.
29
+ //
30
+ // So `handledBy` is the whole point of this file: a host asks it, gets an answer
31
+ // in the same call stack, and consumes the key or passes it on accordingly.
32
+ // Reporting the press to JavaScript happens afterwards and asynchronously,
33
+ // because by then the decision has been made.
34
+ //
35
+ // ## Why the registry is here
36
+ //
37
+ // Three hosts asking "does this view claim Cmd+Z" is one decision, and a host
38
+ // keeping its own copy would be a fourth place for it to drift. The tags are
39
+ // React's, so nothing here knows what a view is.
40
+
41
+ #pragma once
42
+
43
+ #include <react/renderer/core/ReactPrimitives.h>
44
+
45
+ #include <cstdint>
46
+ #include <functional>
47
+ #include <optional>
48
+ #include <string>
49
+ #include <vector>
50
+
51
+ namespace basalt {
52
+
53
+ // The names that are not a character. A host maps its own key codes onto these;
54
+ // what it must not do is spell them itself.
55
+ inline constexpr const char *kKeyArrowUp = "ArrowUp";
56
+ inline constexpr const char *kKeyArrowDown = "ArrowDown";
57
+ inline constexpr const char *kKeyArrowLeft = "ArrowLeft";
58
+ inline constexpr const char *kKeyArrowRight = "ArrowRight";
59
+ inline constexpr const char *kKeyEnter = "Enter";
60
+ inline constexpr const char *kKeyTab = "Tab";
61
+ inline constexpr const char *kKeyEscape = "Escape";
62
+ inline constexpr const char *kKeyBackspace = "Backspace";
63
+ inline constexpr const char *kKeyDelete = "Delete";
64
+ inline constexpr const char *kKeyHome = "Home";
65
+ inline constexpr const char *kKeyEnd = "End";
66
+ inline constexpr const char *kKeyPageUp = "PageUp";
67
+ inline constexpr const char *kKeyPageDown = "PageDown";
68
+ // A space is a character like any other, and is named `" "` rather than
69
+ // `"Space"`. Worth a constant because it is the one that looks like a mistake.
70
+ inline constexpr const char *kKeySpace = " ";
71
+
72
+ struct KeyModifiers {
73
+ bool alt = false;
74
+ bool ctrl = false;
75
+ bool meta = false;
76
+ bool shift = false;
77
+
78
+ bool operator==(const KeyModifiers &other) const {
79
+ return alt == other.alt && ctrl == other.ctrl && meta == other.meta &&
80
+ shift == other.shift;
81
+ }
82
+ bool operator!=(const KeyModifiers &other) const { return !(*this == other); }
83
+ };
84
+
85
+ // A press, and also a claim: the two are compared for equality, so they are one
86
+ // shape rather than two that have to agree.
87
+ struct KeyCombination {
88
+ std::string key;
89
+ KeyModifiers modifiers;
90
+
91
+ bool operator==(const KeyCombination &other) const {
92
+ return key == other.key && modifiers == other.modifiers;
93
+ }
94
+ };
95
+
96
+ // What a view claims. Replaces whatever it claimed before rather than adding to
97
+ // it: a re-render produces the whole list, and merging would leave a shortcut
98
+ // live after the app stopped declaring it.
99
+ void setHandledKeys(facebook::react::Tag tag, std::vector<KeyCombination> combinations);
100
+
101
+ // On unmount. Not on every mutation -- an empty list is a view that claims
102
+ // nothing and still exists.
103
+ void clearHandledKeys(facebook::react::Tag tag);
104
+
105
+ // Whether that one view claims it.
106
+ bool handlesKey(facebook::react::Tag tag, const KeyCombination &pressed);
107
+
108
+ // The nearest view in `path` that claims it, or nothing. `path` runs from the
109
+ // focused view outwards to the root, so the first match is the innermost -- the
110
+ // same walk `DragAndDrop`'s drop targets use, and the same reason: a shortcut on
111
+ // a pane should win over the same shortcut on the window.
112
+ std::optional<facebook::react::Tag> handledBy(
113
+ const std::vector<facebook::react::Tag> &path, const KeyCombination &pressed);
114
+
115
+ // Any view that claims it, focus ignored. For the case where nothing has focus
116
+ // at all, which is the ordinary state of a window whose app has declared
117
+ // window-level shortcuts and focused nothing.
118
+ //
119
+ // **Only when nothing is focused.** A host with a focused view must use
120
+ // `handledBy`, because a claim outside the focus path is exactly how a background
121
+ // pane would steal a keystroke from a text field. With nothing focused there is no
122
+ // text field taking keys and no path to be outside of, so the choice is between a
123
+ // window-level shortcut working and it never firing -- and never firing is what a
124
+ // `<KeyHandler>` below the root got before this existed, which is every app that
125
+ // declares shortcuts once at the top.
126
+ //
127
+ // Ambiguous by construction when two views claim the same combination and neither
128
+ // is focused. The first found answers, and which that is depends on the registry's
129
+ // order -- so an app with two window-level handlers for one combination gets one
130
+ // of them and no promise which. Worth knowing rather than worth preventing: the
131
+ // alternative is refusing to fire at all.
132
+ std::optional<facebook::react::Tag> handledByAny(const KeyCombination &pressed);
133
+
134
+ // How many views currently claim anything. For tests, and for a host that wants
135
+ // to skip the walk entirely when nothing is listening.
136
+ std::size_t handledKeyViewCount();
137
+
138
+ // Reporting, after the host has already decided. Set by whatever carries events
139
+ // to JavaScript; absent until then, in which case a press is dropped rather than
140
+ // queued -- a shortcut that fires late is worse than one that did not fire.
141
+ void setKeyListener(std::function<void(facebook::react::Tag, const KeyCombination &)> listener);
142
+ void reportKey(facebook::react::Tag tag, const KeyCombination &pressed);
143
+
144
+ } // namespace basalt
@@ -0,0 +1,46 @@
1
+ #include "LogBoxSurface.h"
2
+
3
+ #include "PlatformServices.h"
4
+
5
+ namespace basalt {
6
+
7
+ LogBoxSurfaceDelegate::LogBoxSurfaceDelegate(Show show, Hide hide)
8
+ : show_(std::move(show)), hide_(std::move(hide)) {}
9
+
10
+ void LogBoxSurfaceDelegate::createContentView(std::string appKey) {
11
+ // Only recorded. The module constructs on the JavaScript thread and calls
12
+ // this at once, long before anything wants a surface on screen, and starting
13
+ // one here would mean touching widgets from the wrong thread for no gain.
14
+ appKey_ = std::move(appKey);
15
+ }
16
+
17
+ bool LogBoxSurfaceDelegate::isContentViewReady() {
18
+ return !appKey_.empty();
19
+ }
20
+
21
+ void LogBoxSurfaceDelegate::destroyContentView() {
22
+ hide();
23
+ appKey_.clear();
24
+ }
25
+
26
+ void LogBoxSurfaceDelegate::show() {
27
+ if (showing_ || appKey_.empty() || !show_) {
28
+ return;
29
+ }
30
+ showing_ = true;
31
+ postToUiThread([show = show_, appKey = appKey_]() { show(appKey); });
32
+ }
33
+
34
+ void LogBoxSurfaceDelegate::hide() {
35
+ if (!showing_ || !hide_) {
36
+ return;
37
+ }
38
+ showing_ = false;
39
+ postToUiThread([hide = hide_]() { hide(); });
40
+ }
41
+
42
+ bool LogBoxSurfaceDelegate::isShowing() {
43
+ return showing_;
44
+ }
45
+
46
+ } // namespace basalt
@@ -0,0 +1,68 @@
1
+ // React Native's error inspector, which is a second surface.
2
+ //
3
+ // The red box is not a native window that a host draws. It is React Native's
4
+ // own JavaScript -- `LogBoxInspectorContainer` -- registered by AppRegistry
5
+ // under the name `"LogBox"`, exactly as an app registers its own component. So
6
+ // a host does not implement an error overlay; it starts a second surface for a
7
+ // component it did not write, on top of the first.
8
+ //
9
+ // That is easy to miss, and missing it is why the inspector did not appear on
10
+ // this platform while the *toasts* did: the notification strip is rendered
11
+ // inside the app's own surface by `AppContainer`, so it has worked since
12
+ // `<View>` and `<Text>` did. Only the full-screen inspector needs a second
13
+ // surface, and the only thing that asks for one is the `LogBox` TurboModule --
14
+ // which ReactCxxPlatform already implements, and only provides when a host
15
+ // hands `ReactHost` a `SurfaceDelegate`. Passing null, as this project did,
16
+ // leaves `NativeLogBox.show()` a call into nothing.
17
+ //
18
+ // ## What is portable about a surface delegate
19
+ //
20
+ // Everything except making a root and starting a surface. The interface has six
21
+ // methods and four of them are bookkeeping; the two that are not are called
22
+ // from the JavaScript thread and have to reach the thread that owns widgets.
23
+ // Getting that wrong is a mounting-manager assertion on a good day. So the
24
+ // bookkeeping and the thread hop are here, once, and a host supplies two
25
+ // functions.
26
+
27
+ #pragma once
28
+
29
+ #include <react/renderer/scheduler/SurfaceDelegate.h>
30
+
31
+ #include <functional>
32
+ #include <string>
33
+
34
+ namespace basalt {
35
+
36
+ class LogBoxSurfaceDelegate final : public facebook::react::SurfaceDelegate {
37
+ public:
38
+ // `show` is handed the app key React Native registered the inspector under,
39
+ // which is always "LogBox" but is passed rather than assumed -- it is the
40
+ // module that decides, and a host should not have to know the string.
41
+ using Show = std::function<void(const std::string &appKey)>;
42
+ using Hide = std::function<void()>;
43
+
44
+ LogBoxSurfaceDelegate(Show show, Hide hide);
45
+
46
+ // --- SurfaceDelegate ------------------------------------------------------
47
+ //
48
+ // Called from the JavaScript thread: `createContentView` from the module's
49
+ // constructor, `show` and `hide` from LogBoxData's setTimeout. So the two
50
+ // that touch a surface hop to the UI thread and the rest only record.
51
+ void createContentView(std::string appKey) override;
52
+ bool isContentViewReady() override;
53
+ void destroyContentView() override;
54
+ void show() override;
55
+ void hide() override;
56
+ bool isShowing() override;
57
+
58
+ private:
59
+ Show show_;
60
+ Hide hide_;
61
+ std::string appKey_;
62
+ // Written on the JavaScript thread and read there too -- every caller of this
63
+ // interface is that thread -- so this needs no lock. The UI thread sees only
64
+ // the two callbacks, and never this.
65
+ bool showing_{false};
66
+ };
67
+
68
+ } // namespace basalt
@@ -0,0 +1,117 @@
1
+ // An application menu, described once.
2
+ //
3
+ // The menu bar is the one desktop feature where the three platforms disagree
4
+ // about whether it should exist at all, and this file is shaped by that rather
5
+ // than pretending otherwise.
6
+ //
7
+ // macOS Every application has one, it lives in the system menu bar, and it
8
+ // is not optional -- it is also load-bearing. AppKit routes a key
9
+ // equivalent through the main menu before anything else sees it, so
10
+ // an app with no Edit menu has no working Cmd-C: the field editor
11
+ // never receives `copy:`. That was true of this host until this file
12
+ // existed, and it is the reason it exists now.
13
+ //
14
+ // Windows Conventional for some applications and absent from many. It is a
15
+ // bar inside the window, so it costs client area.
16
+ //
17
+ // GNOME Discouraged outright: the HIG has said to use a header bar and a
18
+ // menu button since GNOME 3, and GTK4 removed the widget that made
19
+ // the old arrangement easy.
20
+ //
21
+ // So an app describes a menu here, and each platform decides what to do with
22
+ // the description. What it must never do is quietly nothing, which is why
23
+ // `applicationMenuSupported()` exists: an app can ask, and this project's own
24
+ // documentation can say, rather than an app shipping with a File menu nobody
25
+ // on Linux can see.
26
+ //
27
+ // ## Roles
28
+ //
29
+ // An item with a `role` is one the platform implements itself -- copy, paste,
30
+ // quit, minimise. It never reaches JavaScript, which is not an optimisation: on
31
+ // macOS `copy:` has to travel the responder chain to reach whichever field has
32
+ // focus, and a round trip through a callback would arrive with the chain gone.
33
+ // The role names are Electron's, because that is the vocabulary a desktop
34
+ // developer already has.
35
+
36
+ #pragma once
37
+
38
+ #include <functional>
39
+ #include <string>
40
+ #include <vector>
41
+
42
+ namespace basalt {
43
+
44
+ struct MenuItemModel {
45
+ // What it says. Ignored for a separator, and optional for a role, where the
46
+ // platform's own word for it is better than any this project would choose.
47
+ std::string label;
48
+
49
+ // One of the names in kMenuRoles below, or empty for an item the app
50
+ // implements. An unknown role is dropped rather than shown: a menu item that
51
+ // does nothing is worse than one that is not there.
52
+ std::string role;
53
+
54
+ // "CmdOrCtrl+O", in Electron's spelling. Ignored for a role, which brings the
55
+ // platform's own shortcut with it.
56
+ std::string accelerator;
57
+
58
+ bool enabled{true};
59
+ bool separator{false};
60
+
61
+ // What comes back when the app's own item is chosen. Zero for a role or a
62
+ // separator, and for a submenu's parent.
63
+ int id{0};
64
+
65
+ std::vector<MenuItemModel> submenu;
66
+ };
67
+
68
+ // The top level of a menu bar: each entry is a menu, and its `submenu` is what
69
+ // drops down. An entry with no submenu is dropped -- no desktop has a bare
70
+ // command in a menu bar.
71
+ using MenuModel = std::vector<MenuItemModel>;
72
+
73
+ // The roles a platform is expected to implement itself, in Electron's spelling.
74
+ // Anything else in `role` is unknown and the item is dropped.
75
+ //
76
+ // Deliberately short. Each one is a thing every desktop already has a standard
77
+ // action and shortcut for; a role that only one platform has would be a role
78
+ // apps cannot use.
79
+ inline constexpr const char *kMenuRoles[] = {
80
+ "about", "quit", "undo", "redo", "cut", "copy",
81
+ "paste", "delete", "selectAll", "minimize", "zoom", "close",
82
+ "togglefullscreen",
83
+ };
84
+
85
+ // Whether this platform puts an application menu anywhere a person can see it.
86
+ //
87
+ // False is not a failure and not a promise to fix: see the header. An app that
88
+ // asks can put its commands somewhere else; one that does not is no worse off
89
+ // than it was.
90
+ bool applicationMenuSupported();
91
+
92
+ // Installs `menu` as the application menu, replacing whatever was there.
93
+ //
94
+ // `onChosen` is called with the `id` of an app's own item, on the UI thread. A
95
+ // role never arrives there. An empty menu restores the platform's default,
96
+ // which on macOS is the minimum an application needs to be closable.
97
+ void setApplicationMenu(const MenuModel &menu, std::function<void(int id)> onChosen);
98
+
99
+ // The menu that is actually installed, as text, for BASALT_DUMP_MENU.
100
+ //
101
+ // Read back from the platform rather than remembered from the model, which is
102
+ // the whole point: it says the description became a real NSMenu or HMENU, with
103
+ // the shortcuts the platform attached to its roles. Empty on a platform with no
104
+ // menu bar.
105
+ //
106
+ // One line per item, two spaces of indent per level:
107
+ //
108
+ // Edit
109
+ // Copy [cmd+c]
110
+ // Paste [cmd+v]
111
+ // -
112
+ // Nothing doing (disabled)
113
+ //
114
+ // Called on the UI thread.
115
+ std::string describeApplicationMenu();
116
+
117
+ } // namespace basalt
@@ -0,0 +1,209 @@
1
+ #include "MenuModule.h"
2
+
3
+ #include "MenuModel.h"
4
+ #include "PlatformServices.h"
5
+ #include "TestDialog.h"
6
+
7
+ #include <react/bridging/Bridging.h>
8
+ #include <react/bridging/Promise.h>
9
+
10
+ #include <folly/dynamic.h>
11
+
12
+ #include <memory>
13
+ #include <string>
14
+ #include <utility>
15
+ #include <vector>
16
+
17
+ namespace basalt {
18
+
19
+ namespace {
20
+
21
+ using facebook::jsi::Array;
22
+ using facebook::jsi::Object;
23
+ using facebook::jsi::Runtime;
24
+ using facebook::jsi::Value;
25
+ using facebook::react::TurboModule;
26
+
27
+ std::string stringProperty(Runtime &runtime, const Object &item, const char *name) {
28
+ const Value value = item.getProperty(runtime, name);
29
+ return value.isString() ? value.asString(runtime).utf8(runtime) : std::string{};
30
+ }
31
+
32
+ bool boolProperty(Runtime &runtime, const Object &item, const char *name, bool fallback) {
33
+ const Value value = item.getProperty(runtime, name);
34
+ return value.isBool() ? value.asBool() : fallback;
35
+ }
36
+
37
+ // A context menu's entries, which are a flatter thing than the application
38
+ // menu's: one level, no roles, no submenus. That is not a simplification -- a
39
+ // popup menu on all three desktops is a list, and the nesting an application
40
+ // menu has is what a menu *bar* is for.
41
+ std::vector<MenuEntry> entriesFrom(Runtime &runtime, const Value &value) {
42
+ std::vector<MenuEntry> entries;
43
+ if (!value.isObject() || !value.asObject(runtime).isArray(runtime)) {
44
+ return entries;
45
+ }
46
+ const Array array = value.asObject(runtime).asArray(runtime);
47
+ const size_t length = array.size(runtime);
48
+ for (size_t i = 0; i < length; i++) {
49
+ const Value element = array.getValueAtIndex(runtime, i);
50
+ if (!element.isObject()) {
51
+ continue;
52
+ }
53
+ const Object item = element.asObject(runtime);
54
+ // `{separator: true}`, which reads better in JavaScript than the empty
55
+ // label the platforms use. An item with no label is one too, so an app that
56
+ // built its list by filtering does not have to notice.
57
+ if (boolProperty(runtime, item, "separator", false)) {
58
+ entries.push_back(MenuEntry::separator());
59
+ continue;
60
+ }
61
+ MenuEntry entry;
62
+ entry.label = stringProperty(runtime, item, "label");
63
+ entry.enabled = boolProperty(runtime, item, "enabled", true);
64
+ entry.shortcut = stringProperty(runtime, item, "shortcut");
65
+ entries.push_back(std::move(entry));
66
+ }
67
+ return entries;
68
+ }
69
+
70
+ std::vector<MenuItemModel> itemsFrom(Runtime &runtime, const Array &array);
71
+
72
+ MenuItemModel itemFrom(Runtime &runtime, const Object &item) {
73
+ MenuItemModel model;
74
+ model.label = stringProperty(runtime, item, "label");
75
+ model.role = stringProperty(runtime, item, "role");
76
+ model.accelerator = stringProperty(runtime, item, "accelerator");
77
+
78
+ const Value separator = item.getProperty(runtime, "separator");
79
+ model.separator = separator.isBool() && separator.asBool();
80
+
81
+ const Value enabled = item.getProperty(runtime, "enabled");
82
+ // Absent means enabled: an app listing an item without saying meant to show
83
+ // it, and defaulting the other way would grey out every menu ever written.
84
+ model.enabled = !enabled.isBool() || enabled.asBool();
85
+
86
+ const Value id = item.getProperty(runtime, "id");
87
+ model.id = id.isNumber() ? static_cast<int>(id.asNumber()) : 0;
88
+
89
+ const Value submenu = item.getProperty(runtime, "submenu");
90
+ if (submenu.isObject() && submenu.asObject(runtime).isArray(runtime)) {
91
+ model.submenu = itemsFrom(runtime, submenu.asObject(runtime).asArray(runtime));
92
+ }
93
+ return model;
94
+ }
95
+
96
+ std::vector<MenuItemModel> itemsFrom(Runtime &runtime, const Array &array) {
97
+ std::vector<MenuItemModel> items;
98
+ const size_t length = array.size(runtime);
99
+ items.reserve(length);
100
+ for (size_t i = 0; i < length; i++) {
101
+ const Value entry = array.getValueAtIndex(runtime, i);
102
+ if (entry.isObject()) {
103
+ items.push_back(itemFrom(runtime, entry.asObject(runtime)));
104
+ }
105
+ }
106
+ return items;
107
+ }
108
+
109
+ } // namespace
110
+
111
+ DesktopMenuModule::DesktopMenuModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker)
112
+ : TurboModule(kModuleName, std::move(jsInvoker)) {
113
+ methodMap_["setApplicationMenu"] = MethodMetadata{1, setMenu};
114
+ methodMap_["isSupported"] = MethodMetadata{0, isSupported};
115
+ methodMap_["showContextMenu"] = MethodMetadata{3, showContextMenu};
116
+ // What a NativeEventEmitter over this module calls; the events go out as
117
+ // device events either way.
118
+ methodMap_["addListener"] = MethodMetadata{1, noop};
119
+ methodMap_["removeListeners"] = MethodMetadata{1, noop};
120
+ }
121
+
122
+ DesktopMenuModule::~DesktopMenuModule() {
123
+ // The handler holds a pointer to this module's emitter. Cleared on the way
124
+ // out, for the same reason the title bar clears its metrics listener.
125
+ setApplicationMenu(MenuModel{}, nullptr);
126
+ }
127
+
128
+ Value DesktopMenuModule::setMenu(Runtime &runtime,
129
+ TurboModule &module,
130
+ const Value *args,
131
+ size_t count) {
132
+ MenuModel model;
133
+ if (count >= 1 && args[0].isObject() && args[0].asObject(runtime).isArray(runtime)) {
134
+ model = itemsFrom(runtime, args[0].asObject(runtime).asArray(runtime));
135
+ }
136
+
137
+ auto *self = static_cast<DesktopMenuModule *>(&module);
138
+ // Installed on the UI thread, like everything else that touches a window:
139
+ // this arrives on the JavaScript thread and an NSMenu or an HMENU may not be
140
+ // built there.
141
+ postToUiThread([self, model = std::move(model)] {
142
+ setApplicationMenu(model, [self](int id) {
143
+ self->emitDeviceEvent(kMenuChosenEvent,
144
+ [id](Runtime & /*runtime*/, std::vector<Value> &args) {
145
+ args.emplace_back(Value(id));
146
+ });
147
+ });
148
+ });
149
+ return Value::undefined();
150
+ }
151
+
152
+ Value DesktopMenuModule::isSupported(Runtime & /*runtime*/,
153
+ TurboModule & /*module*/,
154
+ const Value * /*args*/,
155
+ size_t /*count*/) {
156
+ return Value(applicationMenuSupported());
157
+ }
158
+
159
+ Value DesktopMenuModule::noop(Runtime & /*runtime*/,
160
+ TurboModule & /*module*/,
161
+ const Value * /*args*/,
162
+ size_t /*count*/) {
163
+ return Value::undefined();
164
+ }
165
+
166
+ Value DesktopMenuModule::showContextMenu(Runtime &runtime,
167
+ TurboModule &module,
168
+ const Value *args,
169
+ size_t count) {
170
+ MenuRequest request;
171
+ if (count >= 1) {
172
+ request.entries = entriesFrom(runtime, args[0]);
173
+ }
174
+ // Negative is "wherever the pointer is", which is what the seam already means
175
+ // and what a menu opened from the keyboard wants. An app that passes nothing
176
+ // gets that rather than the top-left corner.
177
+ if (count >= 3 && args[1].isNumber() && args[2].isNumber()) {
178
+ request.x = args[1].asNumber();
179
+ request.y = args[2].asNumber();
180
+ }
181
+
182
+ auto jsInvoker = static_cast<DesktopMenuModule &>(module).jsInvoker_;
183
+ auto promise =
184
+ std::make_shared<facebook::react::AsyncPromise<folly::dynamic>>(runtime, jsInvoker);
185
+
186
+ if (request.entries.empty()) {
187
+ // Nothing to show. Resolved rather than rejected: an app whose menu came
188
+ // out empty because everything was filtered away has not made a mistake,
189
+ // and a rejection would make it handle one.
190
+ promise->resolve(folly::dynamic(nullptr));
191
+ return Value(runtime, facebook::react::bridging::toJs(runtime, *promise, jsInvoker));
192
+ }
193
+
194
+ // `presentMenu`, not `showMenu`: everything that would put a popup on screen
195
+ // goes through the scripted answer, and here it is not merely tidiness. On
196
+ // macOS `popUpMenuPositioningItem` runs the menu's own tracking loop on the
197
+ // main thread, so a menu nobody dismisses stops an automated run where it
198
+ // stands. See core/TestDialog.h.
199
+ presentMenu(request, [promise](int index) {
200
+ // Null for dismissed, which is the same shape the file dialogs answer a
201
+ // cancel with: an index is a number, and a caller checking `if (index)`
202
+ // would read entry zero as nothing.
203
+ promise->resolve(index < 0 ? folly::dynamic(nullptr) : folly::dynamic(index));
204
+ });
205
+
206
+ return Value(runtime, facebook::react::bridging::toJs(runtime, *promise, jsInvoker));
207
+ }
208
+
209
+ } // namespace basalt
@@ -0,0 +1,66 @@
1
+ // `BasaltMenu`: the application menu, from JavaScript.
2
+ //
3
+ // The application menu: `<Menu>` describes one, this installs it, and an item
4
+ // the app owns comes back as a device event carrying its id -- which is the
5
+ // same arrangement the title bar and the window use, for the same reason: a
6
+ // menu item is chosen at a time nothing is waiting for it.
7
+ //
8
+ // And context menus, which are the other kind and answer differently. See
9
+ // `showContextMenu` below.
10
+ //
11
+ // Roles never come back. They are handled where they are: see
12
+ // core/MenuModel.h, and appkit/AppKitMenuBar.mm for the part that makes Cmd-C
13
+ // reach a text field.
14
+
15
+ #pragma once
16
+
17
+ #include <ReactCommon/TurboModule.h>
18
+
19
+ #include <memory>
20
+
21
+ namespace basalt {
22
+
23
+ class DesktopMenuModule : public facebook::react::TurboModule {
24
+ public:
25
+ static constexpr const char *kModuleName = "BasaltMenu";
26
+
27
+ explicit DesktopMenuModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker);
28
+ ~DesktopMenuModule() override;
29
+
30
+ private:
31
+ // `setApplicationMenu(items)`, where items are the shape core/MenuModel.h
32
+ // describes. An empty list restores the platform's default.
33
+ static facebook::jsi::Value setMenu(facebook::jsi::Runtime &runtime,
34
+ facebook::react::TurboModule &module,
35
+ const facebook::jsi::Value *args,
36
+ size_t count);
37
+ // `isSupported()`: whether this platform puts a menu bar anywhere a person
38
+ // can see it. False on GNOME, and not a promise to change; see
39
+ // core/MenuModel.h.
40
+ static facebook::jsi::Value isSupported(facebook::jsi::Runtime &runtime,
41
+ facebook::react::TurboModule &module,
42
+ const facebook::jsi::Value *args,
43
+ size_t count);
44
+ // `showContextMenu(items, x, y)` -> a promise of the index chosen, or null
45
+ // when it was dismissed.
46
+ //
47
+ // A promise rather than the device event the application menu uses, and the
48
+ // difference is worth saying. A menu bar is installed once and its items are
49
+ // chosen at times nothing is waiting for; a context menu is opened by an app
50
+ // that is, right then, asking a question -- so the answer belongs to the call
51
+ // that asked it rather than to a listener somewhere else.
52
+ static facebook::jsi::Value showContextMenu(facebook::jsi::Runtime &runtime,
53
+ facebook::react::TurboModule &module,
54
+ const facebook::jsi::Value *args,
55
+ size_t count);
56
+ static facebook::jsi::Value noop(facebook::jsi::Runtime &runtime,
57
+ facebook::react::TurboModule &module,
58
+ const facebook::jsi::Value *args,
59
+ size_t count);
60
+ };
61
+
62
+ // The device event an item's id arrives on. Named here so the two sides cannot
63
+ // drift.
64
+ inline constexpr const char *kMenuChosenEvent = "basaltMenuItemChosen";
65
+
66
+ } // namespace basalt