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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (422) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -4
  3. package/ai-codex.ts +1 -0
  4. package/ai.ts +1 -0
  5. package/app-documents.ts +1 -0
  6. package/camera.ts +13 -0
  7. package/config-plugin.d.cts +2 -0
  8. package/contracts.ts +2 -0
  9. package/diagnostics.ts +1 -0
  10. package/expo-config.d.cts +1 -0
  11. package/metro.cjs +4 -1
  12. package/metro.d.cts +12 -0
  13. package/native.cjs +3 -1
  14. package/native.d.cts +6 -0
  15. package/package.json +93 -15
  16. package/patches/camera/react-native-nitro-image.patch +320 -0
  17. package/patches/camera/react-native-nitro-modules.patch +50 -0
  18. package/patches/camera/react-native-vision-camera.patch +2662 -0
  19. package/patches/camera/upstream.json +22 -0
  20. package/patches/dependencies.json +8 -0
  21. package/patches/react-native-enriched-markdown@1.0.2.patch +8684 -0
  22. package/postinstall.cjs +1 -0
  23. package/schema.json +276 -58
  24. package/settings-observable.ts +1 -0
  25. package/settings-window.ts +1 -0
  26. package/shortcuts-commands.ts +1 -0
  27. package/shortcuts-keyboard.ts +3 -0
  28. package/ui-classnames.ts +1 -0
  29. package/ui-glass.ts +1 -0
  30. package/ui-search.ts +1 -0
  31. package/ui-sidebar.ts +1 -0
  32. package/ui-split-view.ts +1 -0
  33. package/ui-swipe.ts +1 -0
  34. package/ui-symbol.ts +1 -0
  35. package/vendor/node_modules/@legendapp/spark-ai/package.json +19 -0
  36. package/vendor/node_modules/@legendapp/spark-ai/src/availability.ts +17 -0
  37. package/vendor/node_modules/@legendapp/spark-ai/src/index.ts +15 -0
  38. package/vendor/node_modules/@legendapp/spark-ai/src/invocations.ts +34 -0
  39. package/vendor/node_modules/@legendapp/spark-ai/src/output.ts +62 -0
  40. package/vendor/node_modules/@legendapp/spark-ai/src/run.ts +12 -0
  41. package/vendor/node_modules/@legendapp/spark-ai/src/types.ts +16 -0
  42. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.h +2 -1
  43. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.mm +126 -24
  44. package/vendor/node_modules/@legendapp/spark-audio/package.json +5 -2
  45. package/vendor/node_modules/@legendapp/spark-audio/src/NativeSparkAudio.ts +5 -1
  46. package/vendor/node_modules/@legendapp/spark-audio/src/desktop.ts +36 -32
  47. package/vendor/node_modules/@legendapp/spark-audio/src/events.ts +11 -0
  48. package/vendor/node_modules/@legendapp/spark-audio/src/hooks.ts +65 -0
  49. package/vendor/node_modules/@legendapp/spark-audio/src/index.web.ts +92 -33
  50. package/vendor/node_modules/@legendapp/spark-audio/src/media-session-unavailable.ts +6 -4
  51. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.ts +52 -19
  52. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.web.ts +18 -10
  53. package/vendor/node_modules/@legendapp/spark-audio/src/media-types.ts +40 -13
  54. package/vendor/node_modules/@legendapp/spark-audio/src/mobile.ts +40 -32
  55. package/vendor/node_modules/@legendapp/spark-audio/src/player.ts +97 -0
  56. package/vendor/node_modules/@legendapp/spark-audio/src/status-listeners.ts +21 -15
  57. package/vendor/node_modules/@legendapp/spark-audio/src/types.ts +9 -4
  58. package/vendor/node_modules/@legendapp/spark-audio/windows/SparkAudio/SparkAudio.h +67 -20
  59. package/vendor/node_modules/@legendapp/spark-auth-session/package.json +4 -3
  60. package/vendor/node_modules/@legendapp/spark-auth-session/src/core.ts +60 -42
  61. package/vendor/node_modules/@legendapp/spark-auth-session/src/index.ts +28 -12
  62. package/vendor/node_modules/@legendapp/spark-auth-session/src/mobile.ts +97 -41
  63. package/vendor/node_modules/@legendapp/spark-auth-session/src/types.ts +20 -9
  64. package/vendor/node_modules/@legendapp/spark-cli/dist/add-desktop.js +50 -7
  65. package/vendor/node_modules/@legendapp/spark-cli/dist/build-mode.js +1 -1
  66. package/vendor/node_modules/@legendapp/spark-cli/dist/build.js +145 -59
  67. package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +61 -0
  68. package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +10 -4
  69. package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +37 -8
  70. package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +86 -25
  71. package/vendor/node_modules/@legendapp/spark-cli/dist/dependency-pins.js +132 -0
  72. package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +13 -4
  73. package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +13 -10
  74. package/vendor/node_modules/@legendapp/spark-cli/dist/errors.js +100 -0
  75. package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +2 -2
  76. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-dev-patch.cjs +4 -2
  77. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-metro.cjs +1 -1
  78. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-native.cjs +5 -1
  79. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-node.js +3 -3
  80. package/vendor/node_modules/@legendapp/spark-cli/dist/helpers.js +1 -1
  81. package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +52 -44
  82. package/vendor/node_modules/@legendapp/spark-cli/dist/json.js +16 -0
  83. package/vendor/node_modules/@legendapp/spark-cli/dist/local.js +1 -1
  84. package/vendor/node_modules/@legendapp/spark-cli/dist/metro.cjs +26 -5
  85. package/vendor/node_modules/@legendapp/spark-cli/dist/native-metadata.js +26 -0
  86. package/vendor/node_modules/@legendapp/spark-cli/dist/native-patch-preflight.js +86 -0
  87. package/vendor/node_modules/@legendapp/spark-cli/dist/native-preparation.js +88 -0
  88. package/vendor/node_modules/@legendapp/spark-cli/dist/notary-profiles.js +75 -0
  89. package/vendor/node_modules/@legendapp/spark-cli/dist/package.js +4 -2
  90. package/vendor/node_modules/@legendapp/spark-cli/dist/platform.js +16 -2
  91. package/vendor/node_modules/@legendapp/spark-cli/dist/process.js +7 -0
  92. package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +90 -32
  93. package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +86 -5
  94. package/vendor/node_modules/@legendapp/spark-cli/dist/release.json +23 -18
  95. package/vendor/node_modules/@legendapp/spark-cli/dist/runner-install.js +145 -31
  96. package/vendor/node_modules/@legendapp/spark-cli/dist/signing.js +4 -3
  97. package/vendor/node_modules/@legendapp/spark-cli/dist/universal.cjs +2 -1
  98. package/vendor/node_modules/@legendapp/spark-cli/dist/updates.js +41 -4
  99. package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +5 -9
  100. package/vendor/node_modules/@legendapp/spark-cli/package.json +7 -6
  101. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/App.tsx +6 -12
  102. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/desktop.config.json +5 -3
  103. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/package.json +12 -10
  104. package/vendor/node_modules/@legendapp/spark-cli/templates/diff-lite/Git.macos.tsx +2 -2
  105. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.macos.tsx +23 -21
  106. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.windows.tsx +23 -21
  107. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.desktop.ts +11 -8
  108. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.macos.ts +3 -3
  109. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/Lifecycle.desktop.tsx +8 -14
  110. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/OpenFiles.desktop.tsx +5 -10
  111. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.desktop.ts +11 -8
  112. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.macos.ts +3 -3
  113. package/vendor/node_modules/@legendapp/spark-cli/templates/music-lite/assets.desktop.ts +2 -2
  114. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Activation.desktop.tsx +2 -2
  115. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Desktop.desktop.ts +37 -15
  116. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/session.ts +5 -6
  117. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/window-session.ts +5 -4
  118. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/desktop.config.json +5 -3
  119. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/metro.config.js +1 -1
  120. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/package.json +14 -12
  121. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/react-native.config.js +1 -1
  122. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/package.json +14 -12
  123. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/react-native.config.js +1 -1
  124. package/vendor/node_modules/@legendapp/spark-clipboard/macos/RNDesktopClipboard.mm +10 -16
  125. package/vendor/node_modules/@legendapp/spark-clipboard/package.json +2 -2
  126. package/vendor/node_modules/@legendapp/spark-clipboard/src/NativeDesktopClipboard.ts +1 -1
  127. package/vendor/node_modules/@legendapp/spark-clipboard/src/desktop.ts +55 -31
  128. package/vendor/node_modules/@legendapp/spark-clipboard/src/expo.ts +21 -11
  129. package/vendor/node_modules/@legendapp/spark-clipboard/src/options.ts +8 -0
  130. package/vendor/node_modules/@legendapp/spark-clipboard/src/types.ts +10 -0
  131. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.h +38 -15
  132. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.vcxproj +2 -0
  133. package/vendor/node_modules/@legendapp/spark-codex/RNCodex.podspec +22 -0
  134. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.hpp +23 -0
  135. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.mm +1529 -0
  136. package/vendor/node_modules/@legendapp/spark-codex/nitro.json +21 -0
  137. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/.gitattributes +1 -0
  138. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex+autolinking.rb +62 -0
  139. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.cpp +17 -0
  140. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.hpp +27 -0
  141. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Umbrella.hpp +38 -0
  142. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.mm +35 -0
  143. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.swift +16 -0
  144. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexAvailability.hpp +95 -0
  145. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexRunResult.hpp +99 -0
  146. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.cpp +24 -0
  147. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.hpp +71 -0
  148. package/vendor/node_modules/@legendapp/spark-codex/package.json +35 -0
  149. package/vendor/node_modules/@legendapp/spark-codex/react-native.config.js +11 -0
  150. package/vendor/node_modules/@legendapp/spark-codex/src/Codex.nitro.ts +30 -0
  151. package/vendor/node_modules/@legendapp/spark-codex/src/index.ts +65 -0
  152. package/vendor/node_modules/@legendapp/spark-codex/src/native.ts +6 -0
  153. package/vendor/node_modules/@legendapp/spark-commands/package.json +21 -0
  154. package/vendor/node_modules/@legendapp/spark-commands/src/bindings.ts +90 -0
  155. package/vendor/node_modules/@legendapp/spark-commands/src/index.tsx +400 -0
  156. package/vendor/node_modules/@legendapp/spark-commands/src/router.ts +138 -0
  157. package/vendor/node_modules/@legendapp/spark-commands/src/storage.ts +20 -0
  158. package/vendor/node_modules/@legendapp/spark-context-menu/macos/RNContextMenu.mm +7 -8
  159. package/vendor/node_modules/@legendapp/spark-context-menu/package.json +4 -3
  160. package/vendor/node_modules/@legendapp/spark-context-menu/src/NativeContextMenu.ts +1 -1
  161. package/vendor/node_modules/@legendapp/spark-context-menu/src/index.ts +26 -26
  162. package/vendor/node_modules/@legendapp/spark-context-menu/windows/SparkContextMenu/SparkContextMenu.h +3 -2
  163. package/vendor/node_modules/@legendapp/spark-desktop-app/RNDesktopApp.podspec +2 -1
  164. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryJSI.hpp +89 -0
  165. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryWindows.hpp +22 -0
  166. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/RNDesktopApp.mm +13 -7
  167. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkBinaryJSI.h +48 -0
  168. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.h +4 -0
  169. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.mm +33 -33
  170. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktopError.h +20 -0
  171. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.h +26 -0
  172. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.mm +49 -0
  173. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.h +9 -0
  174. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.mm +70 -0
  175. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkWindow.mm +9 -6
  176. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/recent-documents/RNRecentDocumentController.mm +55 -0
  177. package/vendor/node_modules/@legendapp/spark-desktop-app/package.json +5 -3
  178. package/vendor/node_modules/@legendapp/spark-desktop-app/src/NativeDesktopApp.ts +1 -1
  179. package/vendor/node_modules/@legendapp/spark-desktop-app/src/api.ts +86 -40
  180. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/accelerator.ts +40 -0
  181. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/index.ts +56 -0
  182. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/menu.ts +116 -0
  183. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/native-buffer.ts +22 -0
  184. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/path.ts +24 -0
  185. package/vendor/node_modules/@legendapp/spark-desktop-app/src/events.ts +75 -0
  186. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.ts +2 -0
  187. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.windows.ts +2 -0
  188. package/vendor/node_modules/@legendapp/spark-desktop-app/src/open-requests.ts +43 -0
  189. package/vendor/node_modules/@legendapp/spark-desktop-app/src/restoration.ts +11 -0
  190. package/vendor/node_modules/@legendapp/spark-desktop-app/src/transport.ts +10 -0
  191. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.cjs +10 -5
  192. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.d.cts +3 -0
  193. package/vendor/node_modules/@legendapp/spark-desktop-config/autolinking.cjs +35 -0
  194. package/vendor/node_modules/@legendapp/spark-desktop-config/config.cjs +18 -3
  195. package/vendor/node_modules/@legendapp/spark-desktop-config/config.d.cts +89 -9
  196. package/vendor/node_modules/@legendapp/spark-desktop-config/expo.cjs +1 -0
  197. package/vendor/node_modules/@legendapp/spark-desktop-config/expo.d.cts +3 -0
  198. package/vendor/node_modules/@legendapp/spark-desktop-config/identity.cjs +3 -2
  199. package/vendor/node_modules/@legendapp/spark-desktop-config/lifecycle.cjs +20 -0
  200. package/vendor/node_modules/@legendapp/spark-desktop-config/native-patches.cjs +106 -0
  201. package/vendor/node_modules/@legendapp/spark-desktop-config/native-patches.d.cts +1 -0
  202. package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +7 -5
  203. package/vendor/node_modules/@legendapp/spark-desktop-config/schema.json +276 -58
  204. package/vendor/node_modules/@legendapp/spark-desktop-config/updates.cjs +1 -0
  205. package/vendor/node_modules/@legendapp/spark-desktop-config/validation.cjs +35 -0
  206. package/vendor/node_modules/@legendapp/spark-desktop-config/windows.plugin.cjs +2 -1
  207. package/vendor/node_modules/@legendapp/spark-desktop-host/AppDelegate.mm +129 -11
  208. package/vendor/node_modules/@legendapp/spark-desktop-host/package.json +2 -2
  209. package/vendor/node_modules/@legendapp/spark-desktop-host/windows/application.inc +277 -121
  210. package/vendor/node_modules/@legendapp/spark-desktop-links/common/AuthLoopback.h +5 -7
  211. package/vendor/node_modules/@legendapp/spark-desktop-links/macos/RNDesktopLinks.mm +10 -9
  212. package/vendor/node_modules/@legendapp/spark-desktop-links/package.json +2 -2
  213. package/vendor/node_modules/@legendapp/spark-desktop-links/src/NativeDesktopLinks.ts +1 -1
  214. package/vendor/node_modules/@legendapp/spark-desktop-links/src/api.ts +27 -33
  215. package/vendor/node_modules/@legendapp/spark-desktop-links/src/expo.ts +3 -4
  216. package/vendor/node_modules/@legendapp/spark-desktop-links/src/native.ts +9 -0
  217. package/vendor/node_modules/@legendapp/spark-desktop-links/windows/SparkLinks/SparkLinks.h +22 -22
  218. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/RNDesktopShortcuts.podspec +1 -0
  219. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/RNDesktopShortcuts.mm +1 -1
  220. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.h +9 -0
  221. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.mm +307 -0
  222. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/package.json +8 -5
  223. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/NativeDesktopShortcuts.ts +1 -1
  224. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/api.ts +39 -14
  225. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/NativeKeyboardManager.ts +12 -0
  226. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/codes.ts +108 -0
  227. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/index.ts +99 -0
  228. package/vendor/node_modules/@legendapp/spark-desktop-windows/RNDesktopWindows.podspec +2 -0
  229. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/RNDesktopWindows.mm +229 -103
  230. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/SparkWindowGeometry.h +63 -0
  231. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.h +20 -0
  232. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.mm +27 -0
  233. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.h +12 -0
  234. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.mm +3169 -0
  235. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManagerStartup.mm +31 -0
  236. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/SidebarStartupFallbacks.mm +14 -0
  237. package/vendor/node_modules/@legendapp/spark-desktop-windows/package.json +10 -6
  238. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/NativeDesktopWindowManager.ts +1 -1
  239. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/api.ts +146 -70
  240. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/diagnostics.ts +17 -0
  241. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.ts +3 -1
  242. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.windows.ts +3 -1
  243. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos-adapter.ts +53 -0
  244. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos.ts +51 -0
  245. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/transport.ts +41 -0
  246. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/types.ts +140 -0
  247. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/validation.ts +162 -0
  248. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/window-manager/NativeWindowManager.ts +28 -0
  249. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/WindowProvider.tsx +20 -0
  250. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/createWindowsNavigator.tsx +78 -0
  251. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/index.ts +6 -0
  252. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/primaryWindowLifecycle.ts +34 -0
  253. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/types.ts +11 -0
  254. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/usePrimaryWindowLifecycle.ts +18 -0
  255. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/useWindowFocusEffect.ts +17 -0
  256. package/vendor/node_modules/@legendapp/spark-documents/package.json +22 -0
  257. package/vendor/node_modules/@legendapp/spark-documents/src/controller.ts +97 -0
  258. package/vendor/node_modules/@legendapp/spark-documents/src/documentTransition.ts +13 -0
  259. package/vendor/node_modules/@legendapp/spark-documents/src/hooks.ts +139 -0
  260. package/vendor/node_modules/@legendapp/spark-documents/src/index.tsx +50 -0
  261. package/vendor/node_modules/@legendapp/spark-documents/src/reload.ts +39 -0
  262. package/vendor/node_modules/@legendapp/spark-documents/src/requests.ts +15 -0
  263. package/vendor/node_modules/@legendapp/spark-drag-drop/package.json +4 -3
  264. package/vendor/node_modules/@legendapp/spark-drag-drop/src/DesktopDragViewNativeComponent.ts +1 -1
  265. package/vendor/node_modules/@legendapp/spark-drag-drop/src/contracts.ts +73 -18
  266. package/vendor/node_modules/@legendapp/spark-drag-drop/src/index.tsx +58 -15
  267. package/vendor/node_modules/@legendapp/spark-drag-drop/windows/SparkDragDrop/SparkDragDrop.h +14 -8
  268. package/vendor/node_modules/@legendapp/spark-file-dialog/ios/RNFileDialog.mm +34 -121
  269. package/vendor/node_modules/@legendapp/spark-file-dialog/package.json +12 -5
  270. package/vendor/node_modules/@legendapp/spark-file-dialog/src/NativeFileDialog.ts +1 -5
  271. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.ts +19 -84
  272. package/vendor/node_modules/@legendapp/spark-file-dialog/src/native.ts +12 -0
  273. package/vendor/node_modules/@legendapp/spark-file-dialog/src/options.ts +34 -14
  274. package/vendor/node_modules/@legendapp/spark-file-dialog/src/types.ts +26 -0
  275. package/vendor/node_modules/@legendapp/spark-file-dialog/windows/SparkFileDialog/SparkFileDialog.h +42 -58
  276. package/vendor/node_modules/@legendapp/spark-file-system/RNDesktopFileSystem.podspec +1 -0
  277. package/vendor/node_modules/@legendapp/spark-file-system/macos/RNDesktopFileSystem.mm +32 -21
  278. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.h +24 -0
  279. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.mm +215 -0
  280. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.h +9 -0
  281. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.mm +82 -0
  282. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.h +47 -0
  283. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.mm +306 -0
  284. package/vendor/node_modules/@legendapp/spark-file-system/package.json +8 -6
  285. package/vendor/node_modules/@legendapp/spark-file-system/src/NativeDesktopFileSystem.ts +1 -1
  286. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/NativeFileScanner.ts +11 -0
  287. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/index.ts +93 -0
  288. package/vendor/node_modules/@legendapp/spark-file-system/src/handles.ts +48 -14
  289. package/vendor/node_modules/@legendapp/spark-file-system/src/index.ts +134 -45
  290. package/vendor/node_modules/@legendapp/spark-file-system/src/path.ts +1 -9
  291. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.h +102 -26
  292. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.vcxproj +2 -0
  293. package/vendor/node_modules/@legendapp/spark-global-shortcuts/RNDesktopGlobalShortcuts.podspec +1 -0
  294. package/vendor/node_modules/@legendapp/spark-global-shortcuts/macos/RNDesktopGlobalShortcuts.mm +7 -4
  295. package/vendor/node_modules/@legendapp/spark-global-shortcuts/package.json +4 -3
  296. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/NativeDesktopGlobalShortcuts.ts +1 -1
  297. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/index.ts +39 -10
  298. package/vendor/node_modules/@legendapp/spark-global-shortcuts/windows/SparkGlobalShortcuts/SparkGlobalShortcuts.h +5 -4
  299. package/vendor/node_modules/@legendapp/spark-message-dialog/macos/RNDesktopMessageDialog.mm +2 -2
  300. package/vendor/node_modules/@legendapp/spark-message-dialog/package.json +2 -2
  301. package/vendor/node_modules/@legendapp/spark-message-dialog/src/NativeDesktopMessageDialog.ts +1 -1
  302. package/vendor/node_modules/@legendapp/spark-message-dialog/src/index.ts +48 -11
  303. package/vendor/node_modules/@legendapp/spark-native-menu/ios/RNNativeMenu.mm +141 -609
  304. package/vendor/node_modules/@legendapp/spark-native-menu/package.json +9 -4
  305. package/vendor/node_modules/@legendapp/spark-native-menu/src/NativeMenu.ts +2 -7
  306. package/vendor/node_modules/@legendapp/spark-native-menu/src/api.ts +76 -121
  307. package/vendor/node_modules/@legendapp/spark-native-menu/src/hooks.ts +97 -0
  308. package/vendor/node_modules/@legendapp/spark-native-menu/src/windows-menus.ts +43 -37
  309. package/vendor/node_modules/@legendapp/spark-notifications/macos/RNDesktopNotifications.mm +75 -30
  310. package/vendor/node_modules/@legendapp/spark-notifications/package.json +2 -2
  311. package/vendor/node_modules/@legendapp/spark-notifications/src/NativeDesktopNotifications.ts +1 -1
  312. package/vendor/node_modules/@legendapp/spark-notifications/src/index.ts +130 -42
  313. package/vendor/node_modules/@legendapp/spark-notifications/windows/SparkNotifications/SparkNotifications.h +51 -8
  314. package/vendor/node_modules/@legendapp/spark-processes/macos/RNDesktopProcesses.mm +94 -21
  315. package/vendor/node_modules/@legendapp/spark-processes/package.json +4 -3
  316. package/vendor/node_modules/@legendapp/spark-processes/src/NativeDesktopProcesses.ts +1 -1
  317. package/vendor/node_modules/@legendapp/spark-processes/src/index.ts +124 -28
  318. package/vendor/node_modules/@legendapp/spark-processes/src/types.ts +34 -0
  319. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.h +83 -34
  320. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.vcxproj +2 -0
  321. package/vendor/node_modules/@legendapp/spark-secure-storage/macos/RNDesktopSecureStorage.mm +49 -40
  322. package/vendor/node_modules/@legendapp/spark-secure-storage/package.json +2 -2
  323. package/vendor/node_modules/@legendapp/spark-secure-storage/src/NativeDesktopSecureStorage.ts +1 -1
  324. package/vendor/node_modules/@legendapp/spark-secure-storage/src/desktop.ts +16 -14
  325. package/vendor/node_modules/@legendapp/spark-secure-storage/src/expo.ts +0 -1
  326. package/vendor/node_modules/@legendapp/spark-secure-storage/src/options.ts +3 -2
  327. package/vendor/node_modules/@legendapp/spark-secure-storage/src/unavailable.ts +6 -3
  328. package/vendor/node_modules/@legendapp/spark-settings/package.json +10 -3
  329. package/vendor/node_modules/@legendapp/spark-settings/src/fileStorage.ts +11 -0
  330. package/vendor/node_modules/@legendapp/spark-settings/src/index.ts +7 -9
  331. package/vendor/node_modules/@legendapp/spark-settings/src/storage/index.ts +142 -0
  332. package/vendor/node_modules/@legendapp/spark-settings/src/store.ts +63 -21
  333. package/vendor/node_modules/@legendapp/spark-settings-ui/package.json +20 -0
  334. package/vendor/node_modules/@legendapp/spark-settings-ui/src/index.tsx +436 -0
  335. package/vendor/node_modules/@legendapp/spark-settings-ui/src/options.ts +10 -0
  336. package/vendor/node_modules/@legendapp/spark-sqlite/package.json +3 -3
  337. package/vendor/node_modules/@legendapp/spark-sqlite/src/database.ts +182 -0
  338. package/vendor/node_modules/@legendapp/spark-sqlite/src/index.ts +19 -6
  339. package/vendor/node_modules/@legendapp/spark-sqlite/src/types.ts +22 -0
  340. package/vendor/node_modules/@legendapp/spark-system/macos/RNDesktopSystem.mm +36 -11
  341. package/vendor/node_modules/@legendapp/spark-system/package.json +2 -2
  342. package/vendor/node_modules/@legendapp/spark-system/src/NativeDesktopSystem.ts +1 -1
  343. package/vendor/node_modules/@legendapp/spark-system/src/index.ts +89 -32
  344. package/vendor/node_modules/@legendapp/spark-system/windows/SparkSystem/SparkSystem.h +8 -4
  345. package/vendor/node_modules/@legendapp/spark-tray/macos/RNDesktopTray.mm +20 -8
  346. package/vendor/node_modules/@legendapp/spark-tray/package.json +2 -2
  347. package/vendor/node_modules/@legendapp/spark-tray/src/NativeDesktopTray.ts +1 -1
  348. package/vendor/node_modules/@legendapp/spark-tray/src/index.ts +91 -34
  349. package/vendor/node_modules/@legendapp/spark-tray/windows/SparkTray/SparkTray.h +45 -15
  350. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkButton.mm +2 -1
  351. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkTextInput.mm +10 -2
  352. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.h +15 -0
  353. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.mm +1419 -0
  354. package/vendor/node_modules/@legendapp/spark-ui/macos/glass-effect-view/RNGlassEffectView.mm +72 -0
  355. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.h +15 -0
  356. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.mm +194 -0
  357. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.h +15 -0
  358. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.mm +180 -0
  359. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.h +15 -0
  360. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.mm +155 -0
  361. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.h +18 -0
  362. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.mm +645 -0
  363. package/vendor/node_modules/@legendapp/spark-ui/macos/swipe-actions/RNSwipeActions.h +10 -0
  364. package/vendor/node_modules/@legendapp/spark-ui/macos/swipe-actions/RNSwipeActions.mm +422 -0
  365. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.h +15 -0
  366. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.mm +164 -0
  367. package/vendor/node_modules/@legendapp/spark-ui/package.json +17 -4
  368. package/vendor/node_modules/@legendapp/spark-ui/src/SparkSelectNativeComponent.ts +3 -1
  369. package/vendor/node_modules/@legendapp/spark-ui/src/SparkTextInputNativeComponent.ts +6 -2
  370. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/SidebarSplitViewNativeComponent.ts +37 -0
  371. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +86 -0
  372. package/vendor/node_modules/@legendapp/spark-ui/src/availability.ts +10 -0
  373. package/vendor/node_modules/@legendapp/spark-ui/src/classnames/index.ts +8 -0
  374. package/vendor/node_modules/@legendapp/spark-ui/src/control.ts +42 -0
  375. package/vendor/node_modules/@legendapp/spark-ui/src/desktop.tsx +56 -0
  376. package/vendor/node_modules/@legendapp/spark-ui/src/glass-effect-view/index.tsx +15 -0
  377. package/vendor/node_modules/@legendapp/spark-ui/src/index.android.tsx +35 -21
  378. package/vendor/node_modules/@legendapp/spark-ui/src/index.ios.tsx +55 -26
  379. package/vendor/node_modules/@legendapp/spark-ui/src/index.tsx +1 -27
  380. package/vendor/node_modules/@legendapp/spark-ui/src/index.web.tsx +45 -14
  381. package/vendor/node_modules/@legendapp/spark-ui/src/index.windows.tsx +1 -42
  382. package/vendor/node_modules/@legendapp/spark-ui/src/mobile-input.tsx +11 -0
  383. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSegmentedControlNativeComponent.ts +17 -0
  384. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSelectNativeComponent.ts +17 -0
  385. package/vendor/node_modules/@legendapp/spark-ui/src/select.ts +5 -3
  386. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/SFSymbolNativeComponent.ts +13 -0
  387. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/index.tsx +27 -0
  388. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarItemNativeComponent.ts +25 -0
  389. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarNativeComponent.ts +24 -0
  390. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/index.tsx +104 -0
  391. package/vendor/node_modules/@legendapp/spark-ui/src/specialized.ts +22 -0
  392. package/vendor/node_modules/@legendapp/spark-ui/src/swipe-actions/SwipeActionsNativeComponent.ts +11 -0
  393. package/vendor/node_modules/@legendapp/spark-ui/src/swipe-actions/index.tsx +41 -0
  394. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/TextInputSearchNativeComponent.ts +33 -0
  395. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/index.tsx +34 -0
  396. package/vendor/node_modules/@legendapp/spark-ui/src/types.ts +30 -19
  397. package/vendor/node_modules/@legendapp/spark-ui/uniwind.ts +3 -1
  398. package/vendor/node_modules/@legendapp/spark-ui/uniwind.web.ts +3 -1
  399. package/vendor/node_modules/@legendapp/spark-ui/windows/SparkUI/SparkUI.h +16 -11
  400. package/vendor/node_modules/@legendapp/spark-updates/macos/RNDesktopUpdates.mm +21 -8
  401. package/vendor/node_modules/@legendapp/spark-updates/package.json +2 -2
  402. package/vendor/node_modules/@legendapp/spark-updates/src/NativeDesktopUpdates.ts +3 -2
  403. package/vendor/node_modules/@legendapp/spark-updates/src/index.ts +71 -15
  404. package/vendor/node_modules/@legendapp/spark-webview/package.json +2 -2
  405. package/vendor/node_modules/@legendapp/spark-webview/src/WebView.tsx +11 -0
  406. package/vendor/node_modules/@legendapp/spark-webview/src/adapter.ts +55 -0
  407. package/vendor/node_modules/@legendapp/spark-webview/src/index.ts +2 -2
  408. package/vendor/node_modules/@legendapp/spark-webview/src/types.ts +32 -0
  409. package/vendor/node_modules/@legendapp/spark-webview/src/view-props.ts +28 -0
  410. package/vendor/node_modules/@legendapp/spark-window-options/index.cjs +51 -14
  411. package/vendor/node_modules/@legendapp/spark-window-options/index.d.ts +21 -8
  412. package/vendor/node_modules/@legendapp/spark-window-options/package.json +1 -1
  413. package/windows-macos.ts +1 -0
  414. package/expo-metro.cjs +0 -1
  415. package/message-dialog.ts +0 -1
  416. package/universal.cjs +0 -1
  417. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/accelerator.ts +0 -22
  418. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows-options.ts +0 -11
  419. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.windows.ts +0 -15
  420. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/accelerator.ts +0 -22
  421. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.h +0 -4
  422. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.mm +0 -53
@@ -0,0 +1,37 @@
1
+ import type { HostComponent, ViewProps } from "react-native";
2
+ import { codegenNativeComponent } from "react-native";
3
+ import type { DirectEventHandler, Double } from "react-native/Libraries/Types/CodegenTypes";
4
+
5
+ export type SidebarSplitViewResizeEvent = Readonly<{
6
+ contentHeight: Double;
7
+ contentWidth: Double;
8
+ contentX: Double;
9
+ height: Double;
10
+ isLayoutReady: boolean;
11
+ isVertical: boolean;
12
+ listHeight: Double;
13
+ listWidth: Double;
14
+ listX: Double;
15
+ sidebarHeight: Double;
16
+ sidebarWidth: Double;
17
+ }>;
18
+
19
+ export interface NativeProps extends ViewProps {
20
+ appearance?: string;
21
+ contentTitlebarHeight?: Double;
22
+ contentTitlebarMaterial?: string;
23
+ contentTitlebarOverlayColor?: string;
24
+ contentTitlebarOverlayOpacity?: Double;
25
+ contentMinWidth?: Double;
26
+ hasList?: boolean;
27
+ listMinWidth?: Double;
28
+ listWidth?: Double;
29
+ onSplitViewDidResize?: DirectEventHandler<SidebarSplitViewResizeEvent>;
30
+ sidebarCollapsed?: boolean;
31
+ sidebarMinWidth?: Double;
32
+ sidebarTitlebarOverlayColor?: string;
33
+ sidebarTitlebarOverlayOpacity?: Double;
34
+ sidebarWidth?: Double;
35
+ }
36
+
37
+ export default codegenNativeComponent<NativeProps>("SidebarSplitView") as HostComponent<NativeProps>;
@@ -0,0 +1,86 @@
1
+ import { useState, type ReactNode } from "react";
2
+ import { View, type NativeSyntheticEvent } from "react-native";
3
+ import { SparkError } from "@legendapp/spark-desktop-app/src/contracts";
4
+ import { useControl } from "../control";
5
+ import { appearance, callback, finite, macosViewAvailability, type SpecializedViewProps } from "../specialized";
6
+ import NativeSplitView from "./SidebarSplitViewNativeComponent";
7
+ export type SidebarSplitViewAppearance = "system" | "light" | "dark";
8
+ export type SidebarSplitViewTitlebarMaterial = "none" | "glass" | "titlebar" | "headerView" | "hudWindow" | "sidebar" | "windowBackground";
9
+ export interface SidebarSplitViewPaneMetrics { contentHeight: number; contentWidth: number; sidebarHeight: number; sidebarWidth: number; listHeight?: number; listWidth?: number }
10
+ export interface SidebarSplitViewResizeEvent extends SidebarSplitViewPaneMetrics { listHeight: number; listWidth: number; contentX: number; listX: number; height: number; phase: "provisional" | "ready" }
11
+ export interface SplitViewTitleBarOverlay { color: string; opacity?: number }
12
+ export interface SplitViewTitleBarOptions {
13
+ content?: { height?: number; material?: SidebarSplitViewTitlebarMaterial; overlay?: SplitViewTitleBarOverlay };
14
+ sidebar?: { overlay?: SplitViewTitleBarOverlay };
15
+ }
16
+ export interface SidebarSplitViewProps extends Omit<SpecializedViewProps, "children"> {
17
+ sidebar: ReactNode;
18
+ content: ReactNode;
19
+ /** Optional middle column (AppKit content list, as in Mail): sidebar | list | content in one native split. */
20
+ list?: ReactNode;
21
+ children?: never;
22
+ appearance?: SidebarSplitViewAppearance;
23
+ contentMinWidth?: number;
24
+ sidebarMinWidth?: number;
25
+ sidebarWidth?: number;
26
+ listMinWidth?: number;
27
+ listWidth?: number;
28
+ sidebarCollapsed?: boolean;
29
+ /** Used only for initial layout, before native measurements arrive. */
30
+ initialPaneMetrics?: SidebarSplitViewPaneMetrics;
31
+ titleBar?: SplitViewTitleBarOptions;
32
+ onResize?: (event: SidebarSplitViewResizeEvent) => void;
33
+ }
34
+ export function getSplitViewAvailability() { return macosViewAvailability("SidebarSplitView"); }
35
+ function metrics(value: SidebarSplitViewPaneMetrics) {
36
+ if (!value || typeof value !== "object") throw new SparkError("E_INVALID_ARGUMENT", "Expected pane metrics");
37
+ for (const key of ["contentHeight", "contentWidth", "sidebarHeight", "sidebarWidth"] as const) finite(value[key], key);
38
+ for (const key of ["listHeight", "listWidth"] as const) if (value[key] !== undefined) finite(value[key], key);
39
+ }
40
+ const paneKeys = ["contentHeight", "contentWidth", "sidebarHeight", "sidebarWidth", "listHeight", "listWidth"] as const;
41
+ function keys(value: unknown, allowed: readonly string[]) {
42
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new SparkError("E_INVALID_ARGUMENT", "Expected title-bar options");
43
+ for (const key of Object.keys(value)) if (!allowed.includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unknown title-bar option: ${key}`);
44
+ }
45
+ function overlay(value?: SplitViewTitleBarOverlay) {
46
+ if (value === undefined) return;
47
+ keys(value, ["color", "opacity"]);
48
+ if (typeof value.color !== "string" || !/^#(?:[\da-f]{6}|[\da-f]{8})$/i.test(value.color)) throw new SparkError("E_INVALID_ARGUMENT", "Expected #RRGGBB or #RRGGBBAA title-bar color");
49
+ if (value.opacity !== undefined) finite(value.opacity, "overlay opacity", 0, 1);
50
+ }
51
+ export function SidebarSplitView({ sidebar, content, list, children, appearance: theme = "system", contentMinWidth = 320, sidebarMinWidth = 180, sidebarWidth, listMinWidth = 240, listWidth, sidebarCollapsed = false, initialPaneMetrics, titleBar = {}, onResize, ref, onError, ...props }: SidebarSplitViewProps) {
52
+ appearance(theme); finite(contentMinWidth, "minimum content width"); finite(sidebarMinWidth, "minimum sidebar width"); finite(listMinWidth, "minimum list width");
53
+ if (sidebarWidth !== undefined) finite(sidebarWidth, "sidebar width", sidebarMinWidth);
54
+ if (listWidth !== undefined) finite(listWidth, "list width", listMinWidth);
55
+ const hasList = list !== undefined && list !== null && typeof list !== "boolean" && list !== "";
56
+ if (typeof sidebarCollapsed !== "boolean" || children !== undefined) throw new SparkError("E_INVALID_ARGUMENT", "Use named sidebar/content panes and a boolean collapsed state");
57
+ if (initialPaneMetrics !== undefined) metrics(initialPaneMetrics);
58
+ keys(titleBar, ["content", "sidebar"]);
59
+ if (titleBar.content !== undefined) { keys(titleBar.content, ["height", "material", "overlay"]); if (titleBar.content.height !== undefined) finite(titleBar.content.height, "title-bar height"); }
60
+ if (titleBar.sidebar !== undefined) keys(titleBar.sidebar, ["overlay"]);
61
+ overlay(titleBar.content?.overlay); overlay(titleBar.sidebar?.overlay);
62
+ const material = titleBar.content?.material ?? "none";
63
+ if (!["none", "glass", "titlebar", "headerView", "hudWindow", "sidebar", "windowBackground"].includes(material)) throw new SparkError("E_INVALID_ARGUMENT", "Invalid title-bar material");
64
+ callback(onResize, "resize");
65
+ const control = useControl({ ref, onError }, getSplitViewAvailability());
66
+ const [paneMetrics, setPaneMetrics] = useState<SidebarSplitViewPaneMetrics | undefined>(() => initialPaneMetrics ? { ...initialPaneMetrics } : undefined);
67
+ function resize(event: NativeSyntheticEvent<SidebarSplitViewPaneMetrics & { contentX: number; listX: number; height: number; isLayoutReady: boolean }>) {
68
+ if (!control.active()) return;
69
+ let value: SidebarSplitViewResizeEvent;
70
+ try {
71
+ const data = event?.nativeEvent; metrics(data); finite(data.listHeight, "list height"); finite(data.listWidth, "list width"); finite(data.contentX, "content x", -Infinity); finite(data.listX, "list x", -Infinity); finite(data.height, "height");
72
+ if (typeof data.isLayoutReady !== "boolean") throw new Error("Expected layout readiness");
73
+ value = { contentHeight: data.contentHeight, contentWidth: data.contentWidth, sidebarHeight: data.sidebarHeight, sidebarWidth: data.sidebarWidth, listHeight: data.listHeight, listWidth: data.listWidth, contentX: data.contentX, listX: data.listX, height: data.height, phase: data.isLayoutReady ? "ready" : "provisional" };
74
+ } catch (cause) { control.error(new SparkError("E_INVALID_DATA", "Invalid split-view layout", { cause })); return; }
75
+ setPaneMetrics(current => current && paneKeys.every(key => current[key] === value[key]) ? current : value);
76
+ onResize?.(value);
77
+ }
78
+ if (control.failed) return <View {...props} ref={control.ref} style={[{ flexDirection: "row" }, props.style]}>{sidebarCollapsed ? null : sidebar}{list}{content}</View>;
79
+ return <NativeSplitView {...props} ref={control.ref} appearance={theme} contentMinWidth={contentMinWidth} sidebarMinWidth={sidebarMinWidth} sidebarWidth={sidebarWidth} hasList={hasList} listMinWidth={listMinWidth} listWidth={listWidth} sidebarCollapsed={sidebarCollapsed} onSplitViewDidResize={resize}
80
+ contentTitlebarHeight={titleBar.content?.height ?? 0} contentTitlebarMaterial={material} contentTitlebarOverlayColor={titleBar.content?.overlay?.color} contentTitlebarOverlayOpacity={titleBar.content?.overlay?.opacity ?? (titleBar.content?.overlay ? 1 : 0)}
81
+ sidebarTitlebarOverlayColor={titleBar.sidebar?.overlay?.color} sidebarTitlebarOverlayOpacity={titleBar.sidebar?.overlay?.opacity ?? (titleBar.sidebar?.overlay ? 1 : 0)}>
82
+ <View key="sidebar" style={{ position: "absolute", top: 0, left: 0, minWidth: 0, height: paneMetrics?.sidebarHeight, width: sidebarCollapsed ? 0 : paneMetrics?.sidebarWidth ?? sidebarWidth ?? sidebarMinWidth }}>{sidebar}</View>
83
+ <View key="content" style={{ position: "absolute", top: 0, left: 0, minWidth: 0, overflow: "hidden", height: paneMetrics?.contentHeight, width: paneMetrics?.contentWidth }}>{content}</View>
84
+ {hasList ? <View key="list" style={{ position: "absolute", top: 0, left: 0, minWidth: 0, height: paneMetrics?.listHeight, width: paneMetrics?.listWidth ?? listWidth ?? listMinWidth }}>{list}</View> : null}
85
+ </NativeSplitView>;
86
+ }
@@ -0,0 +1,10 @@
1
+ import { Platform, UIManager } from "react-native";
2
+ import { SparkError, type Availability } from "@legendapp/spark-desktop-app/src/contracts";
3
+ import type { ControlKind } from "./types";
4
+ export function getControlAvailability(control: ControlKind): Availability {
5
+ const names: Record<ControlKind, string> = { button: "SparkButton", "text-input": "SparkTextInput", select: Platform.OS === "windows" ? "SparkSelect" : "NativeSelect", "segmented-control": "NativeSegmentedControl" };
6
+ if (!Object.hasOwn(names, control)) throw new SparkError("E_INVALID_ARGUMENT", "Unknown control kind");
7
+ if (Platform.OS !== "macos" && Platform.OS !== "windows") return { available: false, reason: "unsupported-platform" };
8
+ if (Platform.OS === "windows" && control === "segmented-control") return { available: false, reason: "unsupported-platform" };
9
+ return UIManager.hasViewManagerConfig(names[control]) ? { available: true } : { available: false, reason: "missing-module" };
10
+ }
@@ -0,0 +1,8 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+
8
+ export { type ClassValue };
@@ -0,0 +1,42 @@
1
+ import { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
2
+ import { SparkError, type Availability } from "@legendapp/spark-desktop-app/src/contracts";
3
+ import type { ButtonProps, ControlProps, ControlRef, TextInputProps } from "./types";
4
+ export type MeasureTarget = ControlRef | null;
5
+ export function useControl(props: ControlProps, availability: Availability = { available: true }) {
6
+ const { disabled = false, onError = console.error } = props;
7
+ if (typeof disabled !== "boolean" || typeof onError !== "function" || (props.accessibilityLabel !== undefined && typeof props.accessibilityLabel !== "string") || (props.testID !== undefined && typeof props.testID !== "string")) throw new SparkError("E_INVALID_ARGUMENT", "Invalid control options");
8
+ const target = useRef<MeasureTarget>(null), mounted = useRef(false), reported = useRef<string | undefined>(undefined);
9
+ const [failed, setFailed] = useState(false);
10
+ useLayoutEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);
11
+ const reason = availability.available ? undefined : availability.reason;
12
+ useEffect(() => {
13
+ if (reason && reported.current !== reason) {
14
+ reported.current = reason;
15
+ onError(new SparkError(reason === "unsupported-platform" ? "E_UNSUPPORTED_PLATFORM" : reason === "missing-module" ? "E_MODULE_UNAVAILABLE" : "E_UNAVAILABLE", "Native control is unavailable"));
16
+ }
17
+ }, [reason, onError]);
18
+ useImperativeHandle(props.ref, () => ({ measureInWindow(callback) {
19
+ if (typeof callback !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected a measurement callback");
20
+ if (!target.current || !mounted.current) throw new SparkError("E_CLOSED", "Control is not mounted");
21
+ target.current.measureInWindow((...bounds) => { if (mounted.current) callback(...bounds); });
22
+ } }), []);
23
+ function error(error: SparkError) { if (mounted.current) onError(error); }
24
+ function unavailable(event: { nativeEvent: { message: string } }) {
25
+ if (!mounted.current) return;
26
+ if (typeof event?.nativeEvent?.message !== "string") { error(new SparkError("E_INVALID_DATA", "Invalid native control error")); return; }
27
+ setFailed(true); error(new SparkError("E_UNAVAILABLE", event.nativeEvent.message));
28
+ }
29
+ const ref = useCallback((value: MeasureTarget) => { target.current = value; }, []);
30
+ return { ref, disabled, failed: failed || !availability.available, error, unavailable, active: () => mounted.current && !disabled && !failed && availability.available };
31
+ }
32
+ export function validateButton(props: ButtonProps) {
33
+ if (typeof props.children !== "string" || !props.children.length || (props.onPress !== undefined && typeof props.onPress !== "function") || (props.variant !== undefined && !["default", "bordered", "borderless"].includes(props.variant))) throw new SparkError("E_INVALID_ARGUMENT", "Invalid button options");
34
+ }
35
+ export function useTextValue(props: TextInputProps) {
36
+ const controlled = props.value !== undefined;
37
+ const initialMode = useRef(controlled), initialText = useRef(props.defaultValue ?? ""), latestCount = useRef(0);
38
+ if (initialMode.current !== controlled) throw new SparkError("E_INVALID_ARGUMENT", "TextInput cannot switch controlled mode; remount it instead");
39
+ if ((controlled && (typeof props.value !== "string" || props.defaultValue !== undefined)) || (props.defaultValue !== undefined && typeof props.defaultValue !== "string") || (props.onChangeText !== undefined && typeof props.onChangeText !== "function")) throw new SparkError("E_INVALID_ARGUMENT", "TextInput requires a string value or defaultValue, not both");
40
+ const [eventCount, setEventCount] = useState(0);
41
+ return { controlled, text: props.value ?? "", defaultText: initialText.current, eventCount, acknowledge(count: number) { if (count <= latestCount.current) return false; latestCount.current = count; if (controlled) setEventCount(count); return true; } };
42
+ }
@@ -0,0 +1,56 @@
1
+ import { useState } from "react";
2
+ import { Platform, Text, View, type NativeSyntheticEvent } from "react-native";
3
+ import NativeTextInput from "./SparkTextInputNativeComponent";
4
+ import NativeButton from "./SparkButtonNativeComponent";
5
+ import NativeSelect from "./native-select/NativeSelectNativeComponent";
6
+ import WindowsSelect from "./SparkSelectNativeComponent";
7
+ import NativeSegmentedControl from "./native-select/NativeSegmentedControlNativeComponent";
8
+ import { SparkError } from "@legendapp/spark-desktop-app/src/contracts";
9
+ import { useControl, useTextValue, validateButton } from "./control";
10
+ import { getControlAvailability } from "./availability";
11
+ import { selectionIndex } from "./select";
12
+ import type { ButtonProps, TextInputProps, SelectProps, SegmentedControlProps } from "./types";
13
+ export type { ButtonProps, TextInputProps, ControlledTextInputProps, UncontrolledTextInputProps, SelectProps, SelectOption, SegmentedControlProps, ControlProps, ControlRef, ControlKind } from "./types";
14
+ export { getControlAvailability } from "./availability";
15
+
16
+ export function Button(props: ButtonProps) {
17
+ validateButton(props);
18
+ const control = useControl(props, getControlAvailability("button"));
19
+ const { children, variant = "default", onPress, accessibilityLabel = children, style, testID } = props;
20
+ if (control.failed) return <View ref={control.ref} style={[{ width: 160, height: 36 }, style]} testID={testID} accessible accessibilityLabel={accessibilityLabel} accessibilityState={{ disabled: true }}><Text>{children}</Text></View>;
21
+ return <NativeButton ref={control.ref} title={children} disabled={control.disabled} variant={variant} accessibilityLabel={accessibilityLabel}
22
+ onButtonPress={() => { if (control.active()) onPress?.(); }} onUnavailable={control.unavailable} testID={testID} style={[{ width: 160, height: 36 }, style]} />;
23
+ }
24
+ export function TextInput(props: TextInputProps) {
25
+ const control = useControl(props, getControlAvailability("text-input"));
26
+ const value = useTextValue(props);
27
+ function changed(event: NativeSyntheticEvent<{ text: string; eventCount: number }>) {
28
+ if (!control.active()) return;
29
+ const data = event?.nativeEvent;
30
+ if (typeof data?.text !== "string" || !Number.isSafeInteger(data.eventCount) || data.eventCount < 1 || data.eventCount > 2147483647) { control.error(new SparkError("E_INVALID_DATA", "Invalid native text change")); return; }
31
+ if (value.acknowledge(data.eventCount)) props.onChangeText?.(data.text);
32
+ }
33
+ const { accessibilityLabel, style, testID } = props;
34
+ if (control.failed) return <View ref={control.ref} style={[{ width: 240, height: 36 }, style]} testID={testID} accessible accessibilityLabel={accessibilityLabel} accessibilityState={{ disabled: true }}><Text>{value.controlled ? value.text : value.defaultText}</Text></View>;
35
+ return <NativeTextInput ref={control.ref} text={value.text} defaultText={value.defaultText} controlled={value.controlled} eventCount={value.eventCount} disabled={control.disabled} onTextChange={changed} onUnavailable={control.unavailable} accessibilityLabel={accessibilityLabel} testID={testID} style={[{ width: 240, height: 36 }, style]} />;
36
+ }
37
+ function Selection({ segmented, ...props }: SelectProps & { segmented: boolean }) {
38
+ selectionIndex(props.options, props.value);
39
+ if (typeof props.onValueChange !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected onValueChange");
40
+ const control = useControl(props, getControlAvailability(segmented ? "segmented-control" : "select"));
41
+ const [selectionRevision, revise] = useState(0);
42
+ const { options, value, onValueChange, accessibilityLabel, style, testID } = props;
43
+ function changed(event: NativeSyntheticEvent<{ value: string }>) {
44
+ if (!control.active()) return;
45
+ const next = event?.nativeEvent?.value;
46
+ if (!options.some(option => option.value === next)) { control.error(new SparkError("E_INVALID_DATA", "Native selection is not an option")); return; }
47
+ revise(revision => revision + 1); onValueChange(next);
48
+ }
49
+ const common = { ref: control.ref, value, accessibilityLabel, testID, style: [{ width: 240, height: 36 }, style], selectionRevision };
50
+ if (control.failed) return <View ref={control.ref} style={common.style} testID={testID} accessible accessibilityLabel={accessibilityLabel} accessibilityState={{ disabled: true }}><Text>{options.find(option => option.value === value)!.label}</Text></View>;
51
+ if (segmented) return <NativeSegmentedControl {...common} segmentsJson={JSON.stringify(options)} enabled={!control.disabled} onValueChange={changed} />;
52
+ if (Platform.OS === "windows") return <WindowsSelect {...common} itemsJson={JSON.stringify(options)} disabled={control.disabled} onSelectionChange={changed} onUnavailable={control.unavailable} />;
53
+ return <NativeSelect {...common} itemsJson={JSON.stringify(options)} enabled={!control.disabled} onValueChange={changed} />;
54
+ }
55
+ export function Select(props: SelectProps) { return <Selection {...props} segmented={false} />; }
56
+ export function SegmentedControl(props: SegmentedControlProps) { return <Selection {...props} segmented />; }
@@ -0,0 +1,15 @@
1
+ import { requireNativeComponent, View, type ColorValue } from "react-native";
2
+ import { SparkError } from "@legendapp/spark-desktop-app/src/contracts";
3
+ import { useControl } from "../control";
4
+ import { macosViewAvailability, type SpecializedViewProps } from "../specialized";
5
+ export type GlassStyle = "regular" | "clear";
6
+ export interface GlassViewProps extends SpecializedViewProps { glassStyle?: GlassStyle; tintColor?: ColorValue }
7
+ const NativeGlass = requireNativeComponent<GlassViewProps>("RNGlassEffectView");
8
+ export function getGlassAvailability() { return macosViewAvailability("RNGlassEffectView", 26); }
9
+ /** Native glass hosts its children; unsupported hosts preserve them without an effect. */
10
+ export function GlassView({ glassStyle = "regular", tintColor, ref, onError, ...props }: GlassViewProps) {
11
+ if (glassStyle !== "regular" && glassStyle !== "clear") throw new SparkError("E_INVALID_ARGUMENT", "Invalid glass style");
12
+ const control = useControl({ ref, onError }, getGlassAvailability());
13
+ if (control.failed) return <View {...props} ref={control.ref} />;
14
+ return <NativeGlass {...props} ref={control.ref} glassStyle={glassStyle} tintColor={tintColor} />;
15
+ }
@@ -1,25 +1,39 @@
1
- import { useCallback, useMemo } from "react";
1
+ import type { ComponentProps } from "react";
2
+ function ExpoButton(props: ComponentProps<typeof import("@expo/ui/jetpack-compose").Button>) {
3
+ const Component = (require("@expo/ui/jetpack-compose") as typeof import("@expo/ui/jetpack-compose")).Button;
4
+ return <Component {...props} />;
5
+ }
6
+ import { View, Text } from "react-native";
7
+ import { requireOptionalNativeModule } from "expo-modules-core";
8
+ import { SparkError, type Availability } from "@legendapp/spark-desktop-app/src/contracts";
2
9
  import { selectionIndex } from "./select";
3
- import { Button as ExpoButton, TextInput as ExpoTextInput, Picker } from "@expo/ui/jetpack-compose";
4
- import { StyleSheet, View } from "react-native";
5
- import type { ButtonProps, TextInputProps, SelectProps } from "./types";
6
- export type { ButtonProps, TextInputProps, SelectProps } from "./types";
7
-
8
- export function Button({ children, onPress, disabled = false, variant = "default", style, testID }: ButtonProps) {
9
- return <View style={[styles.button, style]} testID={testID}>
10
- <ExpoButton style={styles.content} onPress={disabled ? undefined : onPress}
11
- disabled={disabled} variant={variant}>{children}</ExpoButton>
12
- </View>;
10
+ import { useControl, validateButton } from "./control";
11
+ import type { ButtonProps, SelectProps, SegmentedControlProps, ControlKind } from "./types";
12
+ export type { ButtonProps, TextInputProps, ControlledTextInputProps, UncontrolledTextInputProps, SelectProps, SelectOption, SegmentedControlProps, ControlProps, ControlRef, ControlKind } from "./types";
13
+ export { TextInput } from "./mobile-input";
14
+ export function getControlAvailability(control: ControlKind): Availability {
15
+ if (!["button", "text-input", "select", "segmented-control"].includes(control)) throw new SparkError("E_INVALID_ARGUMENT", "Unknown control kind");
16
+ return control === "text-input" || requireOptionalNativeModule("ExpoUI") ? { available: true } : { available: false, reason: "missing-module" };
13
17
  }
14
- const styles = StyleSheet.create({ button: { width: 160, height: 48 }, content: { flex: 1 } });
15
-
16
- const ignoreText = (_value: string) => {};
17
- export function TextInput({ defaultValue, onChangeText, accessibilityLabel, style, testID }: TextInputProps) {
18
- return <View style={[{ width: 240, height: 56 }, style]} testID={testID}><ExpoTextInput defaultValue={defaultValue} onChangeText={onChangeText ?? ignoreText} style={styles.content} {...{ accessibilityLabel }} /></View>;
18
+ export function Button(props: ButtonProps) {
19
+ validateButton(props); const control = useControl(props, getControlAvailability("button"));
20
+ const { children, onPress, variant = "default", style, testID, accessibilityLabel = children } = props;
21
+ return <View ref={control.ref} style={[{ width: 160, height: 48 }, style]} testID={testID}>
22
+ {control.failed ? <Text accessibilityLabel={accessibilityLabel} accessibilityState={{ disabled: true }}>{children}</Text> :
23
+ <ExpoButton style={{ flex: 1 }} onPress={() => { if (control.active()) onPress?.(); }} disabled={control.disabled} variant={variant} {...{ accessibilityLabel }}>{children}</ExpoButton>}
24
+ </View>;
19
25
  }
20
- export function Select({ options, value, onValueChange, accessibilityLabel, style, testID }: SelectProps) {
21
- const selectedIndex = selectionIndex(options, value);
22
- const labels = useMemo(() => options.map(option => option.label), [options]);
23
- const changed = useCallback((event: { nativeEvent: { index: number } }) => { const option = options[event.nativeEvent.index]; if (option) onValueChange(option.value); }, [options, onValueChange]);
24
- return <View style={[{ width: 280, height: 48 }, style]} testID={testID}><Picker variant="segmented" options={labels} selectedIndex={selectedIndex} onOptionSelected={changed} style={styles.content} {...{ accessibilityLabel }} /></View>;
26
+ function Selection(props: SelectProps) {
27
+ const selected = selectionIndex(props.options, props.value);
28
+ if (typeof props.onValueChange !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected onValueChange");
29
+ const control = useControl(props, getControlAvailability("select"));
30
+ return <View ref={control.ref} style={[{ width: 280, minHeight: 48, flexDirection: "row" }, props.style]} testID={props.testID} accessibilityLabel={props.accessibilityLabel}>
31
+ {control.failed ? <Text accessibilityState={{ disabled: true }}>{props.options[selected].label}</Text> : props.options.map(option =>
32
+ <ExpoButton key={option.value} disabled={control.disabled} variant={option.value === props.value ? "bordered" : "borderless"} style={{ flex: 1 }}
33
+ {...{ accessibilityRole: "radio", accessibilityState: { checked: option.value === props.value, disabled: control.disabled } }}
34
+ onPress={() => { if (control.active()) props.onValueChange(option.value); }}>{option.label}</ExpoButton>)}
35
+ </View>;
25
36
  }
37
+ /** Android presents its choices inline; there is no dropdown backend in the pinned Expo UI. */
38
+ export function Select(props: SelectProps) { return <Selection {...props} />; }
39
+ export function SegmentedControl(props: SegmentedControlProps) { return <Selection {...props} />; }
@@ -1,30 +1,59 @@
1
- import { useCallback, useMemo } from "react";
1
+ import { useState, type ComponentProps } from "react";
2
+ function ExpoButton(props: ComponentProps<typeof import("@expo/ui/swift-ui").Button>) {
3
+ const Component = (require("@expo/ui/swift-ui") as typeof import("@expo/ui/swift-ui")).Button;
4
+ return <Component {...props} />;
5
+ }
6
+ function Host(props: ComponentProps<typeof import("@expo/ui/swift-ui").Host>) {
7
+ const Component = (require("@expo/ui/swift-ui") as typeof import("@expo/ui/swift-ui")).Host;
8
+ return <Component {...props} />;
9
+ }
10
+ function Picker(props: ComponentProps<typeof import("@expo/ui/swift-ui").Picker>) {
11
+ const Component = (require("@expo/ui/swift-ui") as typeof import("@expo/ui/swift-ui")).Picker;
12
+ return <Component {...props} />;
13
+ }
14
+ import { frame, disabled as disabledModifier, accessibilityLabel as labelModifier } from "@expo/ui/swift-ui/modifiers";
15
+ import { View, Text } from "react-native";
16
+ import { SparkError, type Availability } from "@legendapp/spark-desktop-app/src/contracts";
17
+ import { requireOptionalNativeModule } from "expo-modules-core";
18
+ import { useControl, validateButton } from "./control";
2
19
  import { selectionIndex } from "./select";
3
- import { Button as ExpoButton, Host, TextField, Picker } from "@expo/ui/swift-ui";
4
- import { frame, padding, accessibilityLabel as labelModifier } from "@expo/ui/swift-ui/modifiers";
5
- import { StyleSheet } from "react-native";
6
- import type { ButtonProps, TextInputProps, SelectProps } from "./types";
7
- export type { ButtonProps, TextInputProps, SelectProps } from "./types";
8
-
9
- const fill = [frame({ maxWidth: Infinity, maxHeight: Infinity })];
10
- export function Button({ children, onPress, disabled = false, variant = "default", style, testID }: ButtonProps) {
11
- return <Host style={[styles.button, style]}>
12
- <ExpoButton onPress={disabled ? undefined : onPress} disabled={disabled}
13
- variant={variant} modifiers={fill} testID={testID}>{children}</ExpoButton>
14
- </Host>;
20
+ import type { ButtonProps, SelectProps, SegmentedControlProps, ControlKind } from "./types";
21
+ export type { ButtonProps, TextInputProps, ControlledTextInputProps, UncontrolledTextInputProps, SelectProps, SelectOption, SegmentedControlProps, ControlProps, ControlRef, ControlKind } from "./types";
22
+ export { TextInput } from "./mobile-input";
23
+ export function getControlAvailability(control: ControlKind): Availability {
24
+ if (!["button", "text-input", "select", "segmented-control"].includes(control)) throw new SparkError("E_INVALID_ARGUMENT", "Unknown control kind");
25
+ return control === "text-input" || requireOptionalNativeModule("ExpoUI") ? { available: true } : { available: false, reason: "missing-module" };
15
26
  }
16
- const styles = StyleSheet.create({ button: { width: 160, height: 44 } });
17
-
18
- const fieldModifiers = [padding({ horizontal: 8 }), ...fill];
19
- const ignoreText = (_value: string) => {};
20
- export function TextInput({ defaultValue, onChangeText, accessibilityLabel, style, testID }: TextInputProps) {
21
- const modifiers = useMemo(() => accessibilityLabel ? [...fieldModifiers, labelModifier(accessibilityLabel)] : fieldModifiers, [accessibilityLabel]);
22
- return <Host style={[{ width: 240, height: 44, borderWidth: StyleSheet.hairlineWidth, borderColor: "#8e8e93", borderRadius: 6 }, style]}><TextField allowNewlines={false} multiline={false} defaultValue={defaultValue} onChangeText={onChangeText ?? ignoreText} modifiers={modifiers} testID={testID} /></Host>;
27
+ const fill = [frame({ maxWidth: Infinity, maxHeight: Infinity })];
28
+ export function Button(props: ButtonProps) {
29
+ validateButton(props); const control = useControl(props, getControlAvailability("button"));
30
+ const { children, onPress, variant = "default", style, testID, accessibilityLabel = children } = props;
31
+ return <View ref={control.ref} style={[{ width: 160, height: 44 }, style]}>
32
+ {control.failed ? <Text accessibilityLabel={accessibilityLabel} accessibilityState={{ disabled: true }} testID={testID}>{children}</Text> :
33
+ <Host style={{ flex: 1 }}><ExpoButton onPress={() => { if (control.active()) onPress?.(); }} disabled={control.disabled}
34
+ variant={variant} modifiers={[...fill, labelModifier(accessibilityLabel)]} testID={testID}>{children}</ExpoButton></Host>}
35
+ </View>;
23
36
  }
24
- export function Select({ options, value, onValueChange, accessibilityLabel, style, testID }: SelectProps) {
25
- const selectedIndex = selectionIndex(options, value);
26
- const labels = useMemo(() => options.map(option => option.label), [options]);
27
- const changed = useCallback((event: { nativeEvent: { index: number } }) => { const option = options[event.nativeEvent.index]; if (option) onValueChange(option.value); }, [options, onValueChange]);
28
- const modifiers = useMemo(() => accessibilityLabel ? [labelModifier(accessibilityLabel)] : [], [accessibilityLabel]);
29
- return <Host style={[{ width: 240, height: 44 }, style]}><Picker variant="menu" options={labels} selectedIndex={selectedIndex} onOptionSelected={changed} modifiers={modifiers} testID={testID} /></Host>;
37
+ function Selection({ segmented, ...props }: SelectProps & { segmented: boolean }) {
38
+ const selectedIndex = selectionIndex(props.options, props.value);
39
+ const [selectionRevision, revise] = useState(0);
40
+ if (typeof props.onValueChange !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected onValueChange");
41
+ const control = useControl(props, getControlAvailability(segmented ? "segmented-control" : "select"));
42
+ const { options, onValueChange, accessibilityLabel, style, testID } = props;
43
+ const modifiers = [disabledModifier(control.disabled), ...(accessibilityLabel ? [labelModifier(accessibilityLabel)] : [])];
44
+ return <View ref={control.ref} style={[{ width: 240, height: 44 }, style]}>
45
+ {control.failed ? <Text accessibilityLabel={accessibilityLabel} accessibilityState={{ disabled: true }} testID={testID}>{options[selectedIndex].label}</Text> :
46
+ <Host style={{ flex: 1 }}><Picker key={selectionRevision} variant={segmented ? "segmented" : "menu"} options={options.map(option => option.label)} selectedIndex={selectedIndex}
47
+ onOptionSelected={event => {
48
+ if (!control.active()) return;
49
+ const index = event?.nativeEvent?.index;
50
+ if (!Number.isInteger(index) || !options[index]) { control.error(new SparkError("E_INVALID_DATA", "Invalid native selection")); return; }
51
+ // Expo's SwiftUI picker retains local selection and ignores an unchanged
52
+ // selectedIndex. A fresh picker also restores a parent-vetoed selection.
53
+ revise(value => value + 1);
54
+ onValueChange(options[index].value);
55
+ }} modifiers={modifiers} testID={testID} /></Host>}
56
+ </View>;
30
57
  }
58
+ export function Select(props: SelectProps) { return <Selection {...props} segmented={false} />; }
59
+ export function SegmentedControl(props: SegmentedControlProps) { return <Selection {...props} segmented />; }
@@ -1,27 +1 @@
1
- import { useCallback, useMemo } from "react";
2
- import type { NativeSyntheticEvent } from "react-native";
3
- import NativeTextInput from "./SparkTextInputNativeComponent";
4
- import NativeSelect from "./SparkSelectNativeComponent";
5
- import { selectionIndex } from "./select";
6
- import { StyleSheet } from "react-native";
7
- import NativeButton from "./SparkButtonNativeComponent";
8
- import type { ButtonProps, TextInputProps, SelectProps } from "./types";
9
- export type { ButtonProps, TextInputProps, SelectProps } from "./types";
10
-
11
- export function Button({ children, onPress, disabled = false, variant = "default", style, testID }: ButtonProps) {
12
- return <NativeButton title={children} disabled={disabled} variant={variant}
13
- onButtonPress={disabled ? undefined : onPress} testID={testID}
14
- style={[styles.button, style]} />;
15
- }
16
- const styles = StyleSheet.create({ button: { width: 160, height: 36 } });
17
-
18
- export function TextInput({ defaultValue = "", onChangeText, style, ...props }: TextInputProps) {
19
- const changed = useCallback((event: NativeSyntheticEvent<{ text: string }>) => onChangeText?.(event.nativeEvent.text), [onChangeText]);
20
- return <NativeTextInput {...props} defaultText={defaultValue} onTextChange={changed} style={[{ width: 240, height: 36 }, style]} />;
21
- }
22
- export function Select({ options, value, onValueChange, style, ...props }: SelectProps) {
23
- selectionIndex(options, value);
24
- const itemsJson = useMemo(() => JSON.stringify(options), [options]);
25
- const changed = useCallback((event: NativeSyntheticEvent<{ value: string }>) => onValueChange(event.nativeEvent.value), [onValueChange]);
26
- return <NativeSelect {...props} itemsJson={itemsJson} value={value} onSelectionChange={changed} style={[{ width: 240, height: 36 }, style]} />;
27
- }
1
+ export * from "./desktop";
@@ -1,23 +1,54 @@
1
1
  import { selectionIndex } from "./select";
2
2
  import type { CSSProperties } from "react";
3
- import { StyleSheet, View } from "react-native";
4
- import type { ButtonProps, TextInputProps, SelectProps } from "./types";
5
- export type { ButtonProps, TextInputProps, SelectProps } from "./types";
6
-
3
+ import { View } from "react-native";
4
+ import { SparkError, type Availability } from "@legendapp/spark-desktop-app/src/contracts";
5
+ import { useControl, useTextValue, validateButton } from "./control";
6
+ import type { ButtonProps, TextInputProps, SelectProps, SegmentedControlProps, ControlKind } from "./types";
7
+ export type { ButtonProps, TextInputProps, ControlledTextInputProps, UncontrolledTextInputProps, SelectProps, SelectOption, SegmentedControlProps, ControlProps, ControlRef, ControlKind } from "./types";
8
+ export function getControlAvailability(control: ControlKind): Availability {
9
+ if (!["button", "text-input", "select", "segmented-control"].includes(control)) throw new SparkError("E_INVALID_ARGUMENT", "Unknown control kind");
10
+ return { available: true };
11
+ }
7
12
  const nativeStyle: CSSProperties = { width: "100%", height: "100%", boxSizing: "border-box", font: "14px system-ui" };
8
13
  const borderlessStyle: CSSProperties = { ...nativeStyle, border: "none", background: "transparent" };
9
- export function Button({ children, onPress, disabled = false, variant = "default", style, testID }: ButtonProps) {
10
- return <View style={[styles.button, style]} testID={testID}>
11
- <button type="button" disabled={disabled} onClick={disabled ? undefined : onPress}
14
+ export function Button(props: ButtonProps) {
15
+ validateButton(props); const control = useControl(props);
16
+ const { children, onPress, variant = "default", accessibilityLabel = children, style, testID } = props;
17
+ return <View ref={control.ref} style={[{ width: 160, height: 36 }, style]} testID={testID}>
18
+ <button type="button" disabled={control.disabled} onClick={() => { if (control.active()) onPress?.(); }} aria-label={accessibilityLabel}
12
19
  style={variant === "borderless" ? borderlessStyle : nativeStyle}>{children}</button>
13
20
  </View>;
14
21
  }
15
- const styles = StyleSheet.create({ button: { width: 160, height: 36 } });
16
-
17
- export function TextInput({ defaultValue, onChangeText, accessibilityLabel, style, testID }: TextInputProps) {
18
- return <View style={[{ width: 240, height: 36 }, style]} testID={testID}><input type="text" defaultValue={defaultValue} onChange={event => onChangeText?.(event.currentTarget.value)} aria-label={accessibilityLabel} style={nativeStyle} /></View>;
22
+ export function TextInput(props: TextInputProps) {
23
+ const control = useControl(props), state = useTextValue(props);
24
+ return <View ref={control.ref} style={[{ width: 240, height: 36 }, props.style]} testID={props.testID}>
25
+ <input type="text" value={state.controlled ? state.text : undefined} defaultValue={state.controlled ? undefined : state.defaultText} disabled={control.disabled}
26
+ onChange={event => { if (control.active()) props.onChangeText?.(event.currentTarget.value); }} aria-label={props.accessibilityLabel} style={nativeStyle} />
27
+ </View>;
28
+ }
29
+ function useSelection(props: SelectProps) {
30
+ selectionIndex(props.options, props.value);
31
+ if (typeof props.onValueChange !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected onValueChange");
32
+ const control = useControl(props);
33
+ return { ...control, change(value: string) {
34
+ if (!control.active()) return;
35
+ if (!props.options.some(option => option.value === value)) { control.error(new SparkError("E_INVALID_DATA", "Invalid native selection")); return; }
36
+ props.onValueChange(value);
37
+ } };
19
38
  }
20
- export function Select({ options, value, onValueChange, accessibilityLabel, style, testID }: SelectProps) {
21
- selectionIndex(options, value);
22
- return <View style={[{ width: 240, height: 36 }, style]} testID={testID}><select value={value} onChange={event => onValueChange(event.currentTarget.value)} aria-label={accessibilityLabel} style={nativeStyle}>{options.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}</select></View>;
39
+ export function Select(props: SelectProps) {
40
+ const control = useSelection(props);
41
+ return <View ref={control.ref} style={[{ width: 240, height: 36 }, props.style]} testID={props.testID}>
42
+ <select value={props.value} disabled={control.disabled} onChange={event => control.change(event.currentTarget.value)} aria-label={props.accessibilityLabel} style={nativeStyle}>
43
+ {props.options.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
44
+ </select>
45
+ </View>;
46
+ }
47
+ export function SegmentedControl(props: SegmentedControlProps) {
48
+ const control = useSelection(props);
49
+ return <View ref={control.ref} style={[{ width: 240, height: 36 }, props.style]} testID={props.testID}>
50
+ <div role="group" aria-label={props.accessibilityLabel} style={{ display: "flex", width: "100%", height: "100%" }}>
51
+ {props.options.map(option => <button type="button" key={option.value} disabled={control.disabled} aria-pressed={option.value === props.value} onClick={() => control.change(option.value)} style={{ ...nativeStyle, flex: 1 }}>{option.label}</button>)}
52
+ </div>
53
+ </View>;
23
54
  }
@@ -1,42 +1 @@
1
- import { useCallback, useMemo, useState } from "react";
2
- import { Text, View, TurboModuleRegistry } from "react-native";
3
- import type { NativeSyntheticEvent, TurboModule, StyleProp, ViewStyle } from "react-native";
4
- import NativeButton from "./SparkButtonNativeComponent";
5
- import NativeTextInput from "./SparkTextInputNativeComponent";
6
- import NativeSelect from "./SparkSelectNativeComponent";
7
- import { selectionIndex } from "./select";
8
- import type { ButtonProps, TextInputProps, SelectProps } from "./types";
9
- export type { ButtonProps, TextInputProps, SelectProps } from "./types";
10
- interface Availability extends TurboModule { isAvailable(): boolean }
11
- const available = TurboModuleRegistry.get<Availability>("NativeSparkUI")?.isAvailable() === true;
12
- function useAvailability() {
13
- const [failed, setFailed] = useState(!available);
14
- const unavailable = useCallback((event: NativeSyntheticEvent<{ message: string }>) => {
15
- console.warn(`Windows native control unavailable: ${event.nativeEvent.message}`); setFailed(true);
16
- }, []);
17
- return { failed, onUnavailable: unavailable };
18
- }
19
- function Placeholder({ label, style, testID }: { label: string; style?: StyleProp<ViewStyle>; testID?: string }) {
20
- return <View style={[{ minHeight: 36, padding: 8, borderWidth: 1, borderColor: "#999" }, style]} testID={testID} accessible accessibilityLabel={label} accessibilityState={{ disabled: true }}>
21
- <Text>{label} — Windows control unavailable; rebuild the development client.</Text>
22
- </View>;
23
- }
24
- export function Button({ children, onPress, disabled = false, variant = "default", style, testID }: ButtonProps) {
25
- const { failed, onUnavailable } = useAvailability();
26
- return failed ? <Placeholder label={children} style={style} testID={testID} /> :
27
- <NativeButton title={children} disabled={disabled} variant={variant} onButtonPress={disabled ? undefined : onPress} onUnavailable={onUnavailable} testID={testID} style={[{ width: 160, height: 36 }, style]} />;
28
- }
29
- export function TextInput({ defaultValue = "", onChangeText, style, ...props }: TextInputProps) {
30
- const { failed, onUnavailable } = useAvailability();
31
- const changed = useCallback((event: NativeSyntheticEvent<{ text: string }>) => onChangeText?.(event.nativeEvent.text), [onChangeText]);
32
- return failed ? <Placeholder label={props.accessibilityLabel ?? "Text input"} style={style} testID={props.testID} /> :
33
- <NativeTextInput {...props} defaultText={defaultValue} onTextChange={changed} onUnavailable={onUnavailable} style={[{ width: 240, height: 36 }, style]} />;
34
- }
35
- export function Select({ options, value, onValueChange, style, ...props }: SelectProps) {
36
- const selected = selectionIndex(options, value);
37
- const { failed, onUnavailable } = useAvailability();
38
- const itemsJson = useMemo(() => JSON.stringify(options), [options]);
39
- const changed = useCallback((event: NativeSyntheticEvent<{ value: string }>) => onValueChange(event.nativeEvent.value), [onValueChange]);
40
- return failed ? <Placeholder label={`${props.accessibilityLabel ?? "Select"}: ${options[selected]!.label}`} style={style} testID={props.testID} /> :
41
- <NativeSelect {...props} itemsJson={itemsJson} value={value} onSelectionChange={changed} onUnavailable={onUnavailable} style={[{ width: 240, height: 36 }, style]} />;
42
- }
1
+ export * from "./desktop";
@@ -0,0 +1,11 @@
1
+ import { TextInput as RNTextInput } from "react-native";
2
+ import { useControl, useTextValue } from "./control";
3
+ import type { TextInputProps } from "./types";
4
+ /** RN owns text editing, IME, controlled-value reconciliation and selection on mobile. */
5
+ export function TextInput(props: TextInputProps) {
6
+ const control = useControl(props), state = useTextValue(props);
7
+ return <RNTextInput ref={control.ref} value={state.controlled ? state.text : undefined} defaultValue={state.defaultText}
8
+ onChangeText={text => { if (control.active()) props.onChangeText?.(text); }} editable={!control.disabled} multiline={false}
9
+ accessibilityLabel={props.accessibilityLabel} accessibilityState={{ disabled: control.disabled }} testID={props.testID}
10
+ style={[{ width: 240, height: 44, borderWidth: 1, borderColor: "#8e8e93", borderRadius: 6, paddingHorizontal: 8 }, props.style]} />;
11
+ }
@@ -0,0 +1,17 @@
1
+ import type { HostComponent, ViewProps } from "react-native";
2
+ import { codegenNativeComponent } from "react-native";
3
+ import type { DirectEventHandler, Int32 } from "react-native/Libraries/Types/CodegenTypes";
4
+
5
+ export type NativeSegmentedControlChangeEvent = {
6
+ value: string;
7
+ };
8
+
9
+ export interface NativeProps extends ViewProps {
10
+ enabled?: boolean;
11
+ selectionRevision?: Int32;
12
+ segmentsJson: string;
13
+ onValueChange?: DirectEventHandler<NativeSegmentedControlChangeEvent>;
14
+ value: string;
15
+ }
16
+
17
+ export default codegenNativeComponent<NativeProps>("NativeSegmentedControl") as HostComponent<NativeProps>;