@legendapp/spark 0.0.1-next.2 → 0.0.1-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +10 -4
- package/ai-codex.ts +1 -0
- package/ai.ts +1 -0
- package/app-documents.ts +1 -0
- package/config-plugin.d.cts +2 -0
- package/contracts.ts +2 -0
- package/diagnostics.ts +1 -0
- package/expo-config.d.cts +1 -0
- package/metro.cjs +4 -1
- package/metro.d.cts +12 -0
- package/native.cjs +3 -1
- package/native.d.cts +6 -0
- package/package.json +82 -14
- package/schema.json +276 -58
- package/settings-observable.ts +1 -0
- package/settings-window.ts +1 -0
- package/shortcuts-commands.ts +1 -0
- package/shortcuts-keyboard.ts +3 -0
- package/ui-classnames.ts +1 -0
- package/ui-glass.ts +1 -0
- package/ui-search.ts +1 -0
- package/ui-sidebar.ts +1 -0
- package/ui-split-view.ts +1 -0
- package/ui-symbol.ts +1 -0
- package/vendor/node_modules/@legendapp/spark-ai/package.json +19 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/availability.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/index.ts +15 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/invocations.ts +34 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/output.ts +62 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/run.ts +12 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/types.ts +16 -0
- package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.h +2 -1
- package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.mm +126 -24
- package/vendor/node_modules/@legendapp/spark-audio/package.json +5 -2
- package/vendor/node_modules/@legendapp/spark-audio/src/NativeSparkAudio.ts +5 -1
- package/vendor/node_modules/@legendapp/spark-audio/src/desktop.ts +36 -32
- package/vendor/node_modules/@legendapp/spark-audio/src/events.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-audio/src/hooks.ts +65 -0
- package/vendor/node_modules/@legendapp/spark-audio/src/index.web.ts +92 -33
- package/vendor/node_modules/@legendapp/spark-audio/src/media-session-unavailable.ts +6 -4
- package/vendor/node_modules/@legendapp/spark-audio/src/media-session.ts +52 -19
- package/vendor/node_modules/@legendapp/spark-audio/src/media-session.web.ts +18 -10
- package/vendor/node_modules/@legendapp/spark-audio/src/media-types.ts +40 -13
- package/vendor/node_modules/@legendapp/spark-audio/src/mobile.ts +40 -32
- package/vendor/node_modules/@legendapp/spark-audio/src/player.ts +97 -0
- package/vendor/node_modules/@legendapp/spark-audio/src/status-listeners.ts +21 -15
- package/vendor/node_modules/@legendapp/spark-audio/src/types.ts +9 -4
- package/vendor/node_modules/@legendapp/spark-audio/windows/SparkAudio/SparkAudio.h +67 -20
- package/vendor/node_modules/@legendapp/spark-auth-session/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-auth-session/src/core.ts +60 -42
- package/vendor/node_modules/@legendapp/spark-auth-session/src/index.ts +28 -12
- package/vendor/node_modules/@legendapp/spark-auth-session/src/mobile.ts +97 -41
- package/vendor/node_modules/@legendapp/spark-auth-session/src/types.ts +20 -9
- package/vendor/node_modules/@legendapp/spark-cli/dist/add-desktop.js +50 -7
- package/vendor/node_modules/@legendapp/spark-cli/dist/build-mode.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/build.js +83 -48
- package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +60 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +7 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +31 -5
- package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +69 -23
- package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +12 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +12 -9
- package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-dev-patch.cjs +4 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-metro.cjs +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-node.js +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +10 -38
- package/vendor/node_modules/@legendapp/spark-cli/dist/local.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/metro.cjs +26 -5
- package/vendor/node_modules/@legendapp/spark-cli/dist/native-metadata.js +26 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/native-patch-preflight.js +84 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/native-preparation.js +88 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/notary-profiles.js +75 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/package.js +4 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/platform.js +16 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +66 -13
- package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +84 -4
- package/vendor/node_modules/@legendapp/spark-cli/dist/release.json +23 -18
- package/vendor/node_modules/@legendapp/spark-cli/dist/runner-install.js +145 -31
- package/vendor/node_modules/@legendapp/spark-cli/dist/signing.js +4 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/updates.js +41 -4
- package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +4 -1
- package/vendor/node_modules/@legendapp/spark-cli/package.json +7 -6
- package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/App.tsx +6 -12
- package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/desktop.config.json +5 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/package.json +11 -9
- package/vendor/node_modules/@legendapp/spark-cli/templates/diff-lite/Git.macos.tsx +2 -2
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.macos.tsx +23 -21
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.windows.tsx +23 -21
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.desktop.ts +11 -8
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.macos.ts +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/Lifecycle.desktop.tsx +8 -14
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/OpenFiles.desktop.tsx +5 -10
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.desktop.ts +11 -8
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.macos.ts +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/music-lite/assets.desktop.ts +2 -2
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Activation.desktop.tsx +2 -2
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Desktop.desktop.ts +37 -15
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/session.ts +5 -6
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/window-session.ts +5 -4
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/desktop.config.json +5 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/metro.config.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/package.json +13 -11
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/react-native.config.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/templates/windows/package.json +13 -11
- package/vendor/node_modules/@legendapp/spark-cli/templates/windows/react-native.config.js +1 -1
- package/vendor/node_modules/@legendapp/spark-clipboard/macos/RNDesktopClipboard.mm +10 -16
- package/vendor/node_modules/@legendapp/spark-clipboard/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-clipboard/src/NativeDesktopClipboard.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-clipboard/src/desktop.ts +55 -31
- package/vendor/node_modules/@legendapp/spark-clipboard/src/expo.ts +21 -11
- package/vendor/node_modules/@legendapp/spark-clipboard/src/options.ts +8 -0
- package/vendor/node_modules/@legendapp/spark-clipboard/src/types.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.h +38 -15
- package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.vcxproj +2 -0
- package/vendor/node_modules/@legendapp/spark-codex/RNCodex.podspec +22 -0
- package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.hpp +23 -0
- package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.mm +1529 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitro.json +21 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/.gitattributes +1 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex+autolinking.rb +62 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.cpp +17 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.hpp +27 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Umbrella.hpp +38 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.mm +35 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.swift +16 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexAvailability.hpp +95 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexRunResult.hpp +99 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.cpp +24 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.hpp +71 -0
- package/vendor/node_modules/@legendapp/spark-codex/package.json +35 -0
- package/vendor/node_modules/@legendapp/spark-codex/react-native.config.js +11 -0
- package/vendor/node_modules/@legendapp/spark-codex/src/Codex.nitro.ts +30 -0
- package/vendor/node_modules/@legendapp/spark-codex/src/index.ts +65 -0
- package/vendor/node_modules/@legendapp/spark-codex/src/native.ts +6 -0
- package/vendor/node_modules/@legendapp/spark-commands/package.json +21 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/bindings.ts +90 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/index.tsx +400 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/router.ts +138 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/storage.ts +20 -0
- package/vendor/node_modules/@legendapp/spark-context-menu/macos/RNContextMenu.mm +7 -8
- package/vendor/node_modules/@legendapp/spark-context-menu/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-context-menu/src/NativeContextMenu.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-context-menu/src/index.ts +26 -26
- package/vendor/node_modules/@legendapp/spark-context-menu/windows/SparkContextMenu/SparkContextMenu.h +3 -2
- package/vendor/node_modules/@legendapp/spark-desktop-app/RNDesktopApp.podspec +2 -1
- package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryJSI.hpp +89 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryWindows.hpp +22 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/RNDesktopApp.mm +13 -7
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkBinaryJSI.h +48 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.h +4 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.mm +33 -33
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktopError.h +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.h +26 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.mm +49 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.h +9 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.mm +70 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkWindow.mm +9 -6
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/recent-documents/RNRecentDocumentController.mm +55 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/package.json +5 -3
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/NativeDesktopApp.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/api.ts +86 -40
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/accelerator.ts +40 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/index.ts +56 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/menu.ts +114 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/native-buffer.ts +22 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/path.ts +24 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/events.ts +75 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.ts +2 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.windows.ts +2 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/open-requests.ts +43 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/restoration.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/transport.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.cjs +10 -5
- package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.d.cts +3 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/autolinking.cjs +35 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/config.cjs +16 -3
- package/vendor/node_modules/@legendapp/spark-desktop-config/config.d.cts +89 -9
- package/vendor/node_modules/@legendapp/spark-desktop-config/expo.d.cts +3 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/identity.cjs +3 -2
- package/vendor/node_modules/@legendapp/spark-desktop-config/lifecycle.cjs +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +6 -5
- package/vendor/node_modules/@legendapp/spark-desktop-config/schema.json +276 -58
- package/vendor/node_modules/@legendapp/spark-desktop-config/updates.cjs +1 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/validation.cjs +35 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/windows.plugin.cjs +2 -1
- package/vendor/node_modules/@legendapp/spark-desktop-host/AppDelegate.mm +129 -11
- package/vendor/node_modules/@legendapp/spark-desktop-host/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-desktop-host/windows/application.inc +277 -121
- package/vendor/node_modules/@legendapp/spark-desktop-links/common/AuthLoopback.h +5 -7
- package/vendor/node_modules/@legendapp/spark-desktop-links/macos/RNDesktopLinks.mm +10 -9
- package/vendor/node_modules/@legendapp/spark-desktop-links/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/NativeDesktopLinks.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/api.ts +27 -33
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/expo.ts +3 -4
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/native.ts +9 -0
- package/vendor/node_modules/@legendapp/spark-desktop-links/windows/SparkLinks/SparkLinks.h +22 -22
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/RNDesktopShortcuts.podspec +1 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/RNDesktopShortcuts.mm +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.h +9 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.mm +307 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/package.json +8 -5
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/NativeDesktopShortcuts.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/api.ts +39 -14
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/NativeKeyboardManager.ts +12 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/codes.ts +108 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/index.ts +99 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/RNDesktopWindows.podspec +2 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/RNDesktopWindows.mm +229 -103
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/SparkWindowGeometry.h +63 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.h +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.mm +27 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.h +12 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.mm +3130 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManagerStartup.mm +31 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/SidebarStartupFallbacks.mm +14 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/package.json +10 -6
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/NativeDesktopWindowManager.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/api.ts +144 -70
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/diagnostics.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.windows.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos-adapter.ts +53 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos.ts +51 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/transport.ts +41 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/types.ts +140 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/validation.ts +162 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/window-manager/NativeWindowManager.ts +28 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/WindowProvider.tsx +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/createWindowsNavigator.tsx +78 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/index.ts +6 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/primaryWindowLifecycle.ts +34 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/types.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/usePrimaryWindowLifecycle.ts +18 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/useWindowFocusEffect.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-documents/package.json +22 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/controller.ts +97 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/documentTransition.ts +13 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/hooks.ts +139 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/index.tsx +50 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/reload.ts +39 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/requests.ts +15 -0
- package/vendor/node_modules/@legendapp/spark-drag-drop/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-drag-drop/src/DesktopDragViewNativeComponent.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-drag-drop/src/contracts.ts +73 -18
- package/vendor/node_modules/@legendapp/spark-drag-drop/src/index.tsx +58 -15
- package/vendor/node_modules/@legendapp/spark-drag-drop/windows/SparkDragDrop/SparkDragDrop.h +14 -8
- package/vendor/node_modules/@legendapp/spark-file-dialog/ios/RNFileDialog.mm +34 -121
- package/vendor/node_modules/@legendapp/spark-file-dialog/package.json +12 -5
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/NativeFileDialog.ts +1 -5
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.ts +19 -84
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/native.ts +12 -0
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/options.ts +34 -14
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/types.ts +26 -0
- package/vendor/node_modules/@legendapp/spark-file-dialog/windows/SparkFileDialog/SparkFileDialog.h +42 -58
- package/vendor/node_modules/@legendapp/spark-file-system/RNDesktopFileSystem.podspec +1 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/RNDesktopFileSystem.mm +32 -21
- package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.h +24 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.mm +215 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.h +9 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.mm +82 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.h +47 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.mm +306 -0
- package/vendor/node_modules/@legendapp/spark-file-system/package.json +8 -6
- package/vendor/node_modules/@legendapp/spark-file-system/src/NativeDesktopFileSystem.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/NativeFileScanner.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/index.ts +93 -0
- package/vendor/node_modules/@legendapp/spark-file-system/src/handles.ts +48 -14
- package/vendor/node_modules/@legendapp/spark-file-system/src/index.ts +134 -45
- package/vendor/node_modules/@legendapp/spark-file-system/src/path.ts +1 -9
- package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.h +102 -26
- package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.vcxproj +2 -0
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/RNDesktopGlobalShortcuts.podspec +1 -0
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/macos/RNDesktopGlobalShortcuts.mm +7 -4
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/NativeDesktopGlobalShortcuts.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/index.ts +39 -10
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/windows/SparkGlobalShortcuts/SparkGlobalShortcuts.h +5 -4
- package/vendor/node_modules/@legendapp/spark-message-dialog/macos/RNDesktopMessageDialog.mm +2 -2
- package/vendor/node_modules/@legendapp/spark-message-dialog/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-message-dialog/src/NativeDesktopMessageDialog.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-message-dialog/src/index.ts +48 -11
- package/vendor/node_modules/@legendapp/spark-native-menu/ios/RNNativeMenu.mm +141 -609
- package/vendor/node_modules/@legendapp/spark-native-menu/package.json +9 -4
- package/vendor/node_modules/@legendapp/spark-native-menu/src/NativeMenu.ts +2 -7
- package/vendor/node_modules/@legendapp/spark-native-menu/src/api.ts +76 -121
- package/vendor/node_modules/@legendapp/spark-native-menu/src/hooks.ts +97 -0
- package/vendor/node_modules/@legendapp/spark-native-menu/src/windows-menus.ts +43 -37
- package/vendor/node_modules/@legendapp/spark-notifications/macos/RNDesktopNotifications.mm +75 -30
- package/vendor/node_modules/@legendapp/spark-notifications/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-notifications/src/NativeDesktopNotifications.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-notifications/src/index.ts +130 -42
- package/vendor/node_modules/@legendapp/spark-notifications/windows/SparkNotifications/SparkNotifications.h +46 -8
- package/vendor/node_modules/@legendapp/spark-processes/macos/RNDesktopProcesses.mm +94 -21
- package/vendor/node_modules/@legendapp/spark-processes/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-processes/src/NativeDesktopProcesses.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-processes/src/index.ts +124 -28
- package/vendor/node_modules/@legendapp/spark-processes/src/types.ts +34 -0
- package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.h +83 -34
- package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.vcxproj +2 -0
- package/vendor/node_modules/@legendapp/spark-secure-storage/macos/RNDesktopSecureStorage.mm +49 -40
- package/vendor/node_modules/@legendapp/spark-secure-storage/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/NativeDesktopSecureStorage.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/desktop.ts +16 -14
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/expo.ts +0 -1
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/options.ts +3 -2
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/unavailable.ts +6 -3
- package/vendor/node_modules/@legendapp/spark-settings/package.json +10 -3
- package/vendor/node_modules/@legendapp/spark-settings/src/fileStorage.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-settings/src/index.ts +7 -9
- package/vendor/node_modules/@legendapp/spark-settings/src/storage/index.ts +142 -0
- package/vendor/node_modules/@legendapp/spark-settings/src/store.ts +63 -21
- package/vendor/node_modules/@legendapp/spark-settings-ui/package.json +20 -0
- package/vendor/node_modules/@legendapp/spark-settings-ui/src/index.tsx +436 -0
- package/vendor/node_modules/@legendapp/spark-settings-ui/src/options.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-sqlite/package.json +3 -3
- package/vendor/node_modules/@legendapp/spark-sqlite/src/database.ts +182 -0
- package/vendor/node_modules/@legendapp/spark-sqlite/src/index.ts +19 -6
- package/vendor/node_modules/@legendapp/spark-sqlite/src/types.ts +22 -0
- package/vendor/node_modules/@legendapp/spark-system/macos/RNDesktopSystem.mm +36 -11
- package/vendor/node_modules/@legendapp/spark-system/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-system/src/NativeDesktopSystem.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-system/src/index.ts +89 -32
- package/vendor/node_modules/@legendapp/spark-system/windows/SparkSystem/SparkSystem.h +8 -4
- package/vendor/node_modules/@legendapp/spark-tray/macos/RNDesktopTray.mm +20 -8
- package/vendor/node_modules/@legendapp/spark-tray/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-tray/src/NativeDesktopTray.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-tray/src/index.ts +91 -34
- package/vendor/node_modules/@legendapp/spark-tray/windows/SparkTray/SparkTray.h +45 -15
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkButton.mm +2 -1
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkTextInput.mm +10 -2
- package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.mm +1182 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/glass-effect-view/RNGlassEffectView.mm +72 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.mm +194 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.mm +180 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.mm +155 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.h +18 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.mm +645 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.mm +164 -0
- package/vendor/node_modules/@legendapp/spark-ui/package.json +16 -4
- package/vendor/node_modules/@legendapp/spark-ui/src/SparkSelectNativeComponent.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-ui/src/SparkTextInputNativeComponent.ts +6 -2
- package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/SidebarSplitViewNativeComponent.ts +31 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +77 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/availability.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/classnames/index.ts +8 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/control.ts +42 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/desktop.tsx +56 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/glass-effect-view/index.tsx +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/index.android.tsx +35 -21
- package/vendor/node_modules/@legendapp/spark-ui/src/index.ios.tsx +55 -26
- package/vendor/node_modules/@legendapp/spark-ui/src/index.tsx +1 -27
- package/vendor/node_modules/@legendapp/spark-ui/src/index.web.tsx +45 -14
- package/vendor/node_modules/@legendapp/spark-ui/src/index.windows.tsx +1 -42
- package/vendor/node_modules/@legendapp/spark-ui/src/mobile-input.tsx +11 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSegmentedControlNativeComponent.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSelectNativeComponent.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/select.ts +5 -3
- package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/SFSymbolNativeComponent.ts +13 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/index.tsx +27 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarItemNativeComponent.ts +25 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarNativeComponent.ts +24 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/index.tsx +104 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/specialized.ts +22 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/TextInputSearchNativeComponent.ts +33 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/index.tsx +34 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/types.ts +30 -19
- package/vendor/node_modules/@legendapp/spark-ui/uniwind.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-ui/uniwind.web.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-ui/windows/SparkUI/SparkUI.h +16 -11
- package/vendor/node_modules/@legendapp/spark-updates/macos/RNDesktopUpdates.mm +21 -8
- package/vendor/node_modules/@legendapp/spark-updates/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-updates/src/NativeDesktopUpdates.ts +3 -2
- package/vendor/node_modules/@legendapp/spark-updates/src/index.ts +71 -15
- package/vendor/node_modules/@legendapp/spark-webview/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-webview/src/WebView.tsx +11 -0
- package/vendor/node_modules/@legendapp/spark-webview/src/adapter.ts +55 -0
- package/vendor/node_modules/@legendapp/spark-webview/src/index.ts +2 -2
- package/vendor/node_modules/@legendapp/spark-webview/src/types.ts +32 -0
- package/vendor/node_modules/@legendapp/spark-webview/src/view-props.ts +28 -0
- package/vendor/node_modules/@legendapp/spark-window-options/index.cjs +51 -14
- package/vendor/node_modules/@legendapp/spark-window-options/index.d.ts +21 -8
- package/vendor/node_modules/@legendapp/spark-window-options/package.json +1 -1
- package/windows-macos.ts +1 -0
- package/expo-metro.cjs +0 -1
- package/message-dialog.ts +0 -1
- package/universal.cjs +0 -1
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/accelerator.ts +0 -22
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows-options.ts +0 -11
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.windows.ts +0 -15
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/accelerator.ts +0 -22
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.h +0 -4
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.mm +0 -53
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { addKeyboardListener, createKeyboardConsumption, type KeyboardConsumption, type KeyboardConsumptionRule, type KeyboardEvent } from "@legendapp/spark-desktop-shortcuts/src/keyboard-manager";
|
|
2
|
+
import { SparkError, asyncRegistration, type AsyncRegistration } from "@legendapp/spark-desktop-app/src/contracts";
|
|
3
|
+
import { getDefaultHotkeyBindings, matchesParsedHotkey, parseBinding, normalizeHotkeyBindings, validateDefinitions, type HotkeyBindingState, type HotkeyDefinition } from "./bindings";
|
|
4
|
+
export type HotkeyScope = { kind: "application" } | { kind: "window"; windowId: string };
|
|
5
|
+
export type HotkeyHandlerContext = { binding: string; event: KeyboardEvent; pressedKeys: ReadonlySet<number>; repeated: boolean };
|
|
6
|
+
export type RoutedHotkeyHandlers<Id extends string = string> = Partial<Record<Id, (context: HotkeyHandlerContext) => boolean | void>>;
|
|
7
|
+
export interface HotkeyRegistrationOptions<Id extends string = string> {
|
|
8
|
+
bindings?: Partial<HotkeyBindingState<Id>>;
|
|
9
|
+
definitions: readonly HotkeyDefinition<Id>[];
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
handlers: RoutedHotkeyHandlers<Id>;
|
|
12
|
+
priority?: number;
|
|
13
|
+
scope?: HotkeyScope;
|
|
14
|
+
}
|
|
15
|
+
export interface HotkeyRegistration extends AsyncRegistration { setEnabled(enabled: boolean): Promise<void> }
|
|
16
|
+
export interface HotkeyRouter {
|
|
17
|
+
register<Id extends string>(options: HotkeyRegistrationOptions<Id>): Promise<HotkeyRegistration>;
|
|
18
|
+
suspend(scope?: HotkeyScope): Promise<AsyncRegistration>;
|
|
19
|
+
setActiveWindowId(windowId: string | null): void;
|
|
20
|
+
getPressedKeys(): ReadonlySet<number>;
|
|
21
|
+
}
|
|
22
|
+
export let capturing = false;
|
|
23
|
+
export function setCapturing(value: boolean) { capturing = value; }
|
|
24
|
+
function validateScope(scope: HotkeyScope) {
|
|
25
|
+
if (!scope || (scope.kind !== "application" && scope.kind !== "window") || (scope.kind === "window" && (typeof scope.windowId !== "string" || !scope.windowId))) throw new SparkError("E_INVALID_ARGUMENT", "Expected application or window scope");
|
|
26
|
+
}
|
|
27
|
+
export function createHotkeyRouter(): HotkeyRouter {
|
|
28
|
+
type Registration = Required<Pick<HotkeyRegistrationOptions, "definitions" | "handlers" | "scope" | "priority">> & { enabled: boolean; appliedEnabled: boolean; revision: number; bindings: Partial<HotkeyBindingState>; order: number };
|
|
29
|
+
const registrations = new Set<Registration>(), suspensions = new Set<HotkeyScope>(), pressed = new Set<number>();
|
|
30
|
+
type Candidate = { registration: Registration; definition: HotkeyDefinition; handler: NonNullable<RoutedHotkeyHandlers[string]>; binding: string; parsed: ReturnType<typeof parseBinding> };
|
|
31
|
+
let candidates = new Map<string, Candidate[]>();
|
|
32
|
+
const modifierBits = [16, 17, 18, 19, 20, 23].map(bit => 1 << bit);
|
|
33
|
+
const rebuild = () => {
|
|
34
|
+
const next = new Map<string, Candidate[]>();
|
|
35
|
+
for (const registration of [...registrations].sort((a, b) => b.priority - a.priority || b.order - a.order)) {
|
|
36
|
+
for (const definition of registration.definitions) {
|
|
37
|
+
const handler = Object.hasOwn(registration.handlers, definition.id) ? registration.handlers[definition.id] : undefined;
|
|
38
|
+
if (!handler) continue;
|
|
39
|
+
for (const binding of Object.hasOwn(registration.bindings, definition.id) ? registration.bindings[definition.id]! : definition.defaultBindings) {
|
|
40
|
+
const parsed = parseBinding(binding), key = parsed.key.toLowerCase();
|
|
41
|
+
const list = next.get(key) ?? [];
|
|
42
|
+
list.push({ registration, definition, handler, binding, parsed }); next.set(key, list);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
candidates = next;
|
|
47
|
+
};
|
|
48
|
+
let activeWindowId: string | null = null, order = 0, transitions = Promise.resolve();
|
|
49
|
+
let consumption: KeyboardConsumption | undefined;
|
|
50
|
+
let subscriptions: AsyncRegistration[] | undefined, cleanup: AsyncRegistration[] | undefined;
|
|
51
|
+
const matchesScope = (scope: HotkeyScope, id: string | null) => scope.kind === "application" || scope.windowId === id;
|
|
52
|
+
const updateModifiers = (event: KeyboardEvent) => {
|
|
53
|
+
for (const modifier of modifierBits) { if (event.modifiers & modifier) pressed.add(modifier); else pressed.delete(modifier); }
|
|
54
|
+
};
|
|
55
|
+
const dispatch = (event: KeyboardEvent) => {
|
|
56
|
+
const windowId = event.windowId;
|
|
57
|
+
if (windowId !== activeWindowId) { pressed.clear(); activeWindowId = windowId; }
|
|
58
|
+
const repeated = event.repeated; pressed.add(event.keyCode);
|
|
59
|
+
updateModifiers(event);
|
|
60
|
+
if (capturing || event.captured || !event.consumed) return false;
|
|
61
|
+
for (const scope of suspensions) if (matchesScope(scope, windowId)) return false;
|
|
62
|
+
for (const { registration, definition, handler, binding, parsed } of candidates.get(event.key.toLowerCase()) ?? []) {
|
|
63
|
+
if (!registrations.has(registration) || !registration.enabled || !matchesScope(registration.scope, windowId)) continue;
|
|
64
|
+
if (repeated && !definition.repeat) continue;
|
|
65
|
+
if (matchesParsedHotkey(event, parsed, definition.allowExtraModifiers) && handler({ binding, event, pressedKeys: new Set(pressed), repeated }) !== false) return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
};
|
|
69
|
+
const nativeRules = (): KeyboardConsumptionRule[] => {
|
|
70
|
+
if ([...suspensions].some(scope => scope.kind === "application")) return [];
|
|
71
|
+
const excludedWindowIds = [...suspensions].flatMap(scope => scope.kind === "window" ? [scope.windowId] : []);
|
|
72
|
+
const rules: KeyboardConsumptionRule[] = [];
|
|
73
|
+
for (const values of candidates.values()) for (const { registration, definition, parsed } of values) {
|
|
74
|
+
if (!registration.enabled) continue;
|
|
75
|
+
rules.push({ ...parsed, allowExtraModifiers: definition.allowExtraModifiers ?? false, repeat: definition.repeat ?? false,
|
|
76
|
+
...(registration.scope.kind === "window" ? { windowIds: [registration.scope.windowId] } : {}), excludedWindowIds });
|
|
77
|
+
}
|
|
78
|
+
return rules;
|
|
79
|
+
};
|
|
80
|
+
const synchronize = () => {
|
|
81
|
+
const operation = transitions.then(async () => {
|
|
82
|
+
if (cleanup) { await Promise.all(cleanup.map(item => item.remove())); cleanup = undefined; }
|
|
83
|
+
if (registrations.size) {
|
|
84
|
+
if (!subscriptions) {
|
|
85
|
+
const down = await addKeyboardListener("down", dispatch);
|
|
86
|
+
try { subscriptions = [down, await addKeyboardListener("up", event => { pressed.delete(event.keyCode); updateModifiers(event); })]; }
|
|
87
|
+
catch (error) { cleanup = [down]; try { await down.remove(); cleanup = undefined; } catch {} throw error; }
|
|
88
|
+
}
|
|
89
|
+
const enabledSnapshot = [...registrations].map(registration => [registration, registration.enabled] as const);
|
|
90
|
+
if (consumption) await consumption.update(nativeRules());
|
|
91
|
+
else consumption = await createKeyboardConsumption(nativeRules());
|
|
92
|
+
for (const [registration, enabled] of enabledSnapshot) registration.appliedEnabled = enabled;
|
|
93
|
+
} else if (subscriptions) {
|
|
94
|
+
cleanup = [...(consumption ? [consumption] : []), ...subscriptions]; consumption = undefined; subscriptions = undefined; pressed.clear();
|
|
95
|
+
await Promise.all(cleanup.map(item => item.remove())); cleanup = undefined;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
transitions = operation.catch(() => {}); return operation;
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
async register(options) {
|
|
102
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) throw new SparkError("E_INVALID_ARGUMENT", "Expected command registration options");
|
|
103
|
+
for (const key of Object.keys(options)) if (!["definitions", "bindings", "enabled", "handlers", "scope", "priority"].includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unknown command option: ${key}`);
|
|
104
|
+
const { definitions, bindings = {}, enabled = true, handlers, scope = { kind: "application" }, priority = scope.kind === "window" ? 100 : 0 } = options;
|
|
105
|
+
validateDefinitions(definitions); validateScope(scope);
|
|
106
|
+
if (!Number.isFinite(priority) || typeof enabled !== "boolean" || !handlers || typeof handlers !== "object" || !bindings || typeof bindings !== "object" || Array.isArray(bindings)) throw new SparkError("E_INVALID_ARGUMENT", "Invalid command registration options");
|
|
107
|
+
const ids = new Set(definitions.map(definition => definition.id));
|
|
108
|
+
for (const [id, handler] of Object.entries(handlers)) if (!ids.has(id as never) || typeof handler !== "function") throw new SparkError("E_INVALID_ARGUMENT", `Invalid handler for command ${id}`);
|
|
109
|
+
for (const id of Object.keys(bindings)) if (!ids.has(id as never)) throw new SparkError("E_INVALID_ARGUMENT", `Unknown command binding: ${id}`);
|
|
110
|
+
const registration: Registration = { definitions: definitions.map(definition => ({ ...definition, defaultBindings: getDefaultHotkeyBindings(definition) })), bindings: Object.fromEntries(Object.entries(bindings).map(([id, values]) => [id, normalizeHotkeyBindings(values as readonly string[])])), enabled, appliedEnabled: enabled, revision: 0, handlers: { ...handlers }, scope: { ...scope }, priority, order: order++ };
|
|
111
|
+
registrations.add(registration);
|
|
112
|
+
rebuild();
|
|
113
|
+
try { await synchronize(); } catch (error) {
|
|
114
|
+
registrations.delete(registration); rebuild();
|
|
115
|
+
try { await synchronize(); } catch (cleanupError) { throw new AggregateError([error, cleanupError], "Command setup and cleanup failed"); }
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
const removal = asyncRegistration(() => { registrations.delete(registration); rebuild(); }, synchronize);
|
|
119
|
+
return { remove: removal.remove, async setEnabled(value) {
|
|
120
|
+
if (typeof value !== "boolean") throw new SparkError("E_INVALID_ARGUMENT", "Expected enabled boolean");
|
|
121
|
+
if (!registrations.has(registration)) throw new SparkError("E_CLOSED", "Command registration has been removed");
|
|
122
|
+
const revision = ++registration.revision; registration.enabled = value;
|
|
123
|
+
try { await synchronize(); } catch (error) { if (registration.revision === revision && registrations.has(registration)) registration.enabled = registration.appliedEnabled; throw error; }
|
|
124
|
+
} };
|
|
125
|
+
},
|
|
126
|
+
async suspend(scope = { kind: "application" }) {
|
|
127
|
+
validateScope(scope); const snapshot = { ...scope }; suspensions.add(snapshot);
|
|
128
|
+
try { await synchronize(); } catch (error) {
|
|
129
|
+
suspensions.delete(snapshot);
|
|
130
|
+
try { await synchronize(); } catch (cleanupError) { throw new AggregateError([error, cleanupError], "Command suspension and rollback failed"); }
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
return asyncRegistration(() => { suspensions.delete(snapshot); }, synchronize);
|
|
134
|
+
},
|
|
135
|
+
setActiveWindowId(id) { if (id !== null && (typeof id !== "string" || !id)) throw new SparkError("E_INVALID_ARGUMENT", "Expected a window ID or null"); if (activeWindowId !== id) pressed.clear(); activeWindowId = id; },
|
|
136
|
+
getPressedKeys: () => new Set(pressed),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SparkError } from "@legendapp/spark-desktop-app/src/contracts";
|
|
2
|
+
import type { ObservableFile } from "@legendapp/spark-settings/src/storage";
|
|
3
|
+
import type { SettingsStorage } from "@legendapp/spark-settings/src/store";
|
|
4
|
+
import { normalizeHotkeyFile, serializeHotkeyFile, type HotkeyBindingLimitOptions, type HotkeyDefinition, type HotkeyFile } from "./bindings";
|
|
5
|
+
export interface HotkeyStoreOptions<Id extends string> extends HotkeyBindingLimitOptions {
|
|
6
|
+
definitions: readonly HotkeyDefinition<Id>[];
|
|
7
|
+
path: string;
|
|
8
|
+
storage?: SettingsStorage;
|
|
9
|
+
debounceMs?: number;
|
|
10
|
+
}
|
|
11
|
+
export async function createHotkeyStore<Id extends string>(options: HotkeyStoreOptions<Id>): Promise<ObservableFile<HotkeyFile<Id>>> {
|
|
12
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) throw new SparkError("E_INVALID_ARGUMENT", "Expected hotkey store options");
|
|
13
|
+
for (const key of Object.keys(options)) if (!["definitions", "path", "maxBindingsPerCommand", "storage", "debounceMs"].includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unknown hotkey store option: ${key}`);
|
|
14
|
+
const { definitions, path, maxBindingsPerCommand, storage, debounceMs } = options;
|
|
15
|
+
const limit = { maxBindingsPerCommand };
|
|
16
|
+
const initialValue = normalizeHotkeyFile(undefined, definitions, limit);
|
|
17
|
+
const snapshot = definitions.map(definition => ({ ...definition, defaultBindings: [...definition.defaultBindings] }));
|
|
18
|
+
const { createObservableFile } = await import("@legendapp/spark-settings/src/storage");
|
|
19
|
+
return createObservableFile({ path, initialValue, storage, debounceMs, decode: value => normalizeHotkeyFile(value, snapshot, limit), encode: value => serializeHotkeyFile(value, snapshot, limit) });
|
|
20
|
+
}
|
|
@@ -47,6 +47,13 @@ RCT_EXPORT_MODULE(NativeContextMenu)
|
|
|
47
47
|
RCTExecuteOnMainQueue(^{
|
|
48
48
|
NSArray *items = [self parseJSON:itemsJson fallback:@[]];
|
|
49
49
|
NSDictionary *location = [self parseJSON:locationJson fallback:@{}];
|
|
50
|
+
NSString *windowId = [location[@"windowId"] isKindOfClass:NSString.class] ? location[@"windowId"] : @"";
|
|
51
|
+
NSWindow *window = nil;
|
|
52
|
+
NSString *identifier = [@"spark." stringByAppendingString:windowId];
|
|
53
|
+
for (NSWindow *candidate in NSApp.windows) if ([candidate.identifier isEqual:identifier]) { window = candidate; break; }
|
|
54
|
+
if (!window) for (NSWindow *candidate in NSApp.windows) if ([candidate.identifier isEqual:windowId]) { window = candidate; break; }
|
|
55
|
+
NSView *targetView = window.contentView;
|
|
56
|
+
if (!targetView) { reject(@"E_NOT_FOUND", @"Context menu owner window does not exist", nil); return; }
|
|
50
57
|
if (![items isKindOfClass:[NSArray class]] || items.count == 0) {
|
|
51
58
|
resolve(@"");
|
|
52
59
|
return;
|
|
@@ -87,14 +94,6 @@ RCT_EXPORT_MODULE(NativeContextMenu)
|
|
|
87
94
|
return;
|
|
88
95
|
}
|
|
89
96
|
|
|
90
|
-
NSWindow *window = NSApp.keyWindow ?: NSApp.mainWindow;
|
|
91
|
-
NSView *targetView = window.contentView;
|
|
92
|
-
if (!targetView) {
|
|
93
|
-
self.pendingResolve(@"");
|
|
94
|
-
self.pendingResolve = nil;
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
97
|
double x = [location[@"x"] isKindOfClass:[NSNumber class]] ? [location[@"x"] doubleValue] : 0;
|
|
99
98
|
double y = [location[@"y"] isKindOfClass:[NSNumber class]] ? [location[@"y"] doubleValue] : 0;
|
|
100
99
|
NSPoint point = NSMakePoint(x, y);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@legendapp/spark-context-menu",
|
|
3
|
-
"version": "0.0.1-next.
|
|
3
|
+
"version": "0.0.1-next.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"ios": {
|
|
23
23
|
"modulesProvider": {
|
|
24
24
|
"NativeContextMenu": "RNContextMenu"
|
|
25
|
-
}
|
|
25
|
+
},
|
|
26
|
+
"componentProvider": {}
|
|
26
27
|
}
|
|
27
28
|
},
|
|
28
29
|
"spark": {
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
]
|
|
41
42
|
},
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"@legendapp/spark-desktop-app": "0.0.1-next.
|
|
44
|
+
"@legendapp/spark-desktop-app": "0.0.1-next.3"
|
|
44
45
|
},
|
|
45
46
|
"private": true
|
|
46
47
|
}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import { Platform } from "react-native";
|
|
2
2
|
import NativeContextMenu from "./NativeContextMenu";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const result = await NativeContextMenu
|
|
26
|
-
|
|
3
|
+
import { SparkError, invokeNative, type Availability } from "@legendapp/spark-desktop-app/src/contracts";
|
|
4
|
+
import { menuItems, selectableMenuIds, type MenuItem as SharedMenuItem, type MenuAction as SharedMenuAction } from "@legendapp/spark-desktop-app/src/contracts/menu";
|
|
5
|
+
type ContextMenuEntry = { id: string; label: string; disabled?: boolean; hidden?: boolean };
|
|
6
|
+
export type MenuItem = { type: "separator" } | (ContextMenuEntry & { type: "action" }) | (ContextMenuEntry & { type: "checkbox"; checked: boolean });
|
|
7
|
+
export type MenuAction = Extract<SharedMenuAction, { type: "action" }>;
|
|
8
|
+
export type { MenuIcon } from "@legendapp/spark-desktop-app/src/contracts/menu";
|
|
9
|
+
export interface ContextMenuOptions { items: readonly MenuItem[]; windowId: string; position: { x: number; y: number } }
|
|
10
|
+
export type ContextMenuResult = { canceled: true } | { canceled: false; itemId: string };
|
|
11
|
+
export function getContextMenuAvailability(): Availability {
|
|
12
|
+
if (Platform.OS !== "macos" && Platform.OS !== "windows") return { available: false, reason: "unsupported-platform" };
|
|
13
|
+
return NativeContextMenu ? { available: true } : { available: false, reason: "missing-module" };
|
|
14
|
+
}
|
|
15
|
+
/** Owner-content logical coordinates. Empty menus cancel; missing owners reject. */
|
|
16
|
+
export async function showContextMenu(options: ContextMenuOptions): Promise<ContextMenuResult> {
|
|
17
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) throw new SparkError("E_INVALID_ARGUMENT", "Expected context menu options");
|
|
18
|
+
for (const key of Object.keys(options)) if (!["items", "windowId", "position"].includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unsupported context menu option: ${key}`);
|
|
19
|
+
if (typeof options.windowId !== "string" || !/^[a-zA-Z0-9_-]{1,100}$/.test(options.windowId)) throw new SparkError("E_INVALID_ARGUMENT", "Expected an owner windowId");
|
|
20
|
+
if (!options.position || typeof options.position !== "object" || Object.keys(options.position).some(key => key !== "x" && key !== "y") || !Number.isFinite(options.position.x) || !Number.isFinite(options.position.y) || Math.abs(options.position.x) > 1000000 || Math.abs(options.position.y) > 1000000) throw new SparkError("E_INVALID_ARGUMENT", "Menu position must be finite logical coordinates within one million units");
|
|
21
|
+
const availability = getContextMenuAvailability();
|
|
22
|
+
if (!availability.available) throw new SparkError(availability.reason === "unsupported-platform" ? "E_UNSUPPORTED_PLATFORM" : "E_MODULE_UNAVAILABLE", "Context menus are unavailable");
|
|
23
|
+
const items = menuItems(options.items, { types: ["action", "checkbox", "separator"] }, Platform.OS === "windows" ? "windows" : "macos");
|
|
24
|
+
const ids = selectableMenuIds(items);
|
|
25
|
+
const result = await invokeNative(() => NativeContextMenu!.showMenu(JSON.stringify(items), JSON.stringify({ ...options.position, windowId: options.windowId })));
|
|
26
|
+
if (result === "") return { canceled: true };
|
|
27
|
+
if (typeof result !== "string" || !ids.has(result)) throw new SparkError("E_INVALID_DATA", "Context menu returned an unknown or disabled item");
|
|
28
|
+
return { canceled: false, itemId: result };
|
|
27
29
|
}
|
|
28
|
-
|
|
29
|
-
export { default as NativeContextMenu } from "./NativeContextMenu";
|
|
@@ -76,8 +76,9 @@ struct SparkContextMenu {
|
|
|
76
76
|
if (current->stopped) { current->busy = false; promise.Resolve(""); return; }
|
|
77
77
|
auto items = Json::JsonArray::Parse(to_hstring(encoded));
|
|
78
78
|
auto position = Json::JsonObject::Parse(to_hstring(location));
|
|
79
|
-
auto owner = Parent(L"",
|
|
80
|
-
if (!owner
|
|
79
|
+
auto owner = Parent(std::wstring(position.GetNamedString(L"windowId")), true);
|
|
80
|
+
if (!owner) { current->busy = false; promise.Reject(React::ReactError{"E_NOT_FOUND", "Context menu owner window does not exist"}); return; }
|
|
81
|
+
if (items.Size() == 0) { current->busy = false; promise.Resolve(""); return; }
|
|
81
82
|
if (!IsWindowEnabled(owner)) { current->busy = false; promise.Reject(React::ReactError{"E_BUSY", "Menu parent has a modal operation"}); return; }
|
|
82
83
|
const double x = position.GetNamedNumber(L"x"), y = position.GetNamedNumber(L"y");
|
|
83
84
|
const double scale = static_cast<double>(GetDpiForWindow(owner)) / 96.0;
|
|
@@ -9,7 +9,8 @@ Pod::Spec.new do |s|
|
|
|
9
9
|
s.homepage = "https://legendapp.com"
|
|
10
10
|
s.source = { :path => "." }
|
|
11
11
|
s.platforms = { :osx => "14.0" }
|
|
12
|
-
s.source_files = "macos/**/*.{h,m,mm}"
|
|
12
|
+
s.source_files = "macos/**/*.{h,m,mm}", "cpp/**/*.hpp"
|
|
13
|
+
s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++20" }
|
|
13
14
|
s.dependency "React-Core"
|
|
14
15
|
s.dependency "ReactCodegen"
|
|
15
16
|
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#ifdef __cplusplus
|
|
3
|
+
#include <jsi/jsi.h>
|
|
4
|
+
#include <ReactCommon/CallInvoker.h>
|
|
5
|
+
#include <react/bridging/CallbackWrapper.h>
|
|
6
|
+
#include <atomic>
|
|
7
|
+
#include <functional>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
#include <cmath>
|
|
10
|
+
#include <optional>
|
|
11
|
+
#include <map>
|
|
12
|
+
#include <vector>
|
|
13
|
+
|
|
14
|
+
namespace spark::binary {
|
|
15
|
+
namespace jsi = facebook::jsi;
|
|
16
|
+
using Builder = std::function<jsi::Value(jsi::Runtime &)>;
|
|
17
|
+
using Completion = std::function<void(Builder, std::string, std::string)>;
|
|
18
|
+
using Start = std::function<void(std::string, std::string, std::optional<std::vector<uint8_t>>, Completion)>;
|
|
19
|
+
|
|
20
|
+
class Bytes final : public jsi::MutableBuffer {
|
|
21
|
+
public:
|
|
22
|
+
explicit Bytes(std::vector<uint8_t> value) : value_(std::move(value)) { if (value_.empty()) value_.reserve(1); }
|
|
23
|
+
size_t size() const override { return value_.size(); }
|
|
24
|
+
uint8_t *data() override { return value_.data(); }
|
|
25
|
+
private:
|
|
26
|
+
std::vector<uint8_t> value_;
|
|
27
|
+
};
|
|
28
|
+
inline jsi::Value Buffer(jsi::Runtime &rt, std::shared_ptr<jsi::MutableBuffer> const &bytes) { return jsi::ArrayBuffer(rt, bytes); }
|
|
29
|
+
inline Builder JSON(std::string value) {
|
|
30
|
+
return [value = std::move(value)](jsi::Runtime &rt) { auto json = rt.global().getPropertyAsObject(rt, "JSON"); return json.getPropertyAsFunction(rt, "parse").callWithThis(rt, json, jsi::String::createFromUtf8(rt, value)); };
|
|
31
|
+
}
|
|
32
|
+
struct Response {
|
|
33
|
+
std::string json = "null";
|
|
34
|
+
std::shared_ptr<jsi::MutableBuffer> bytes;
|
|
35
|
+
std::map<std::string, std::shared_ptr<jsi::MutableBuffer>> fields;
|
|
36
|
+
Builder Build() const {
|
|
37
|
+
return [value = *this](jsi::Runtime &rt) {
|
|
38
|
+
if (value.bytes) return Buffer(rt, value.bytes);
|
|
39
|
+
auto result = JSON(value.json)(rt);
|
|
40
|
+
if (!value.fields.empty()) { auto object = result.asObject(rt); for (auto const &[key, bytes] : value.fields) object.setProperty(rt, key.c_str(), Buffer(rt, bytes)); return jsi::Value(std::move(object)); }
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
// Uses React's runtime-owned callbacks; pending native work never retains a JS
|
|
46
|
+
// function after runtime teardown. Only the invoker accesses the owning runtime.
|
|
47
|
+
inline jsi::Value Promise(jsi::Runtime &rt, std::shared_ptr<facebook::react::CallInvoker> invoker, std::function<void(Completion)> start) {
|
|
48
|
+
auto executor = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "executor"), 2,
|
|
49
|
+
[invoker, start = std::move(start)](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t) {
|
|
50
|
+
auto resolve = facebook::react::CallbackWrapper::createWeak(args[0].asObject(rt).asFunction(rt), rt, invoker);
|
|
51
|
+
auto reject = facebook::react::CallbackWrapper::createWeak(args[1].asObject(rt).asFunction(rt), rt, invoker);
|
|
52
|
+
auto settled = std::make_shared<std::atomic<bool>>(false);
|
|
53
|
+
Completion finish = [resolve, reject, invoker, settled](Builder build, std::string code, std::string message) {
|
|
54
|
+
if (settled->exchange(true)) return;
|
|
55
|
+
invoker->invokeAsync([resolve, reject, build = std::move(build), code = std::move(code), message = std::move(message)](jsi::Runtime &rt) {
|
|
56
|
+
auto good = resolve.lock(), bad = reject.lock(); if (!good || !bad) return;
|
|
57
|
+
if (code.empty()) good->callback().call(rt, build(rt));
|
|
58
|
+
else { auto error = rt.global().getPropertyAsFunction(rt, "Error").callAsConstructor(rt, jsi::String::createFromUtf8(rt, message)).asObject(rt); error.setProperty(rt, "code", jsi::String::createFromUtf8(rt, code)); bad->callback().call(rt, error); }
|
|
59
|
+
good->destroy(); bad->destroy();
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
try { start(finish); } catch (std::exception const &error) { finish({}, "E_NATIVE", error.what()); }
|
|
63
|
+
return jsi::Value::undefined();
|
|
64
|
+
});
|
|
65
|
+
return rt.global().getPropertyAsFunction(rt, "Promise").callAsConstructor(rt, executor);
|
|
66
|
+
}
|
|
67
|
+
inline jsi::Function Function(jsi::Runtime &rt, std::shared_ptr<facebook::react::CallInvoker> invoker, Start start) {
|
|
68
|
+
return jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "binaryCall"), 5,
|
|
69
|
+
[invoker, start = std::move(start)](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
70
|
+
if (count < 2) throw jsi::JSError(rt, "Expected a binary operation and arguments");
|
|
71
|
+
auto method = args[0].asString(rt).utf8(rt), json = args[1].asString(rt).utf8(rt);
|
|
72
|
+
std::optional<std::vector<uint8_t>> bytes;
|
|
73
|
+
if (count > 2 && !args[2].isUndefined()) {
|
|
74
|
+
if (count < 5) throw jsi::JSError(rt, "Expected a binary buffer offset and length");
|
|
75
|
+
auto buffer = args[2].asObject(rt).getArrayBuffer(rt); auto offset = args[3].asNumber(), length = args[4].asNumber();
|
|
76
|
+
if (!std::isfinite(offset) || !std::isfinite(length) || offset < 0 || length < 0 || std::floor(offset) != offset || std::floor(length) != length || offset + length > buffer.size(rt)) throw jsi::JSError(rt, "Invalid binary buffer range");
|
|
77
|
+
// Snapshot once, before returning to JS. Never touch borrowed JS bytes on
|
|
78
|
+
// an I/O worker; views retain their exact offset and length.
|
|
79
|
+
bytes.emplace(static_cast<size_t>(length)); if (length) memcpy(bytes->data(), buffer.data(rt) + static_cast<size_t>(offset), static_cast<size_t>(length));
|
|
80
|
+
}
|
|
81
|
+
return Promise(rt, invoker, [start, method = std::move(method), json = std::move(json), bytes = std::move(bytes)](Completion finish) mutable { start(std::move(method), std::move(json), std::move(bytes), std::move(finish)); });
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
inline void Emit(std::shared_ptr<facebook::react::CallInvoker> invoker, Builder build, std::function<void()> delivered = {}) {
|
|
85
|
+
invoker->invokeAsync([build = std::move(build), delivered = std::move(delivered)](jsi::Runtime &rt) { struct Ack { std::function<void()> done; ~Ack() { if (done) done(); } } ack{delivered}; if (rt.global().getProperty(rt, "__rctDeviceEventEmitter").isUndefined()) return; auto emitter = rt.global().getPropertyAsObject(rt, "__rctDeviceEventEmitter"); emitter.getPropertyAsFunction(rt, "emit").callWithThis(rt, emitter, "desktop", build(rt)); });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#endif
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#ifdef _WIN32
|
|
3
|
+
#include "SparkBinaryJSI.hpp"
|
|
4
|
+
#include <JSI/JsiApiContext.h>
|
|
5
|
+
namespace spark::binary {
|
|
6
|
+
struct NativePromise {
|
|
7
|
+
std::function<void(Response)> resolve;
|
|
8
|
+
std::function<void(winrt::Microsoft::ReactNative::ReactError)> reject;
|
|
9
|
+
void Resolve(std::string json) const { resolve(Response{std::move(json), {}, {}}); }
|
|
10
|
+
void Resolve(Response value) const { resolve(std::move(value)); }
|
|
11
|
+
void Reject(winrt::Microsoft::ReactNative::ReactError value) const { reject(std::move(value)); }
|
|
12
|
+
NativePromise(Completion finish) : resolve([finish](Response value) { finish(value.Build(), {}, {}); }), reject([finish](auto error) { finish({}, error.Code, error.Message); }) {}
|
|
13
|
+
NativePromise(winrt::Microsoft::ReactNative::ReactPromise<std::string> promise) : resolve([promise](Response value) { promise.Resolve(value.json); }), reject([promise](auto error) { promise.Reject(error); }) {}
|
|
14
|
+
};
|
|
15
|
+
inline void Install(winrt::Microsoft::ReactNative::ReactContext context, const char *name, Start start) {
|
|
16
|
+
auto handle = context.Handle().JSRuntime(); if (!handle) throw std::runtime_error("The Windows runtime does not expose JSI");
|
|
17
|
+
auto &runtime = winrt::Microsoft::ReactNative::GetOrCreateContextRuntime(context, handle);
|
|
18
|
+
runtime.global().setProperty(runtime, name, Function(runtime, context.CallInvoker(), std::move(start)));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#endif
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#import "RNDesktopApp.h"
|
|
2
2
|
#import "SparkDesktop.h"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
@implementation RNDesktopApp
|
|
3
|
+
@implementation RNDesktopApp { NSMutableSet<NSString *> *_guardIds; }
|
|
4
|
+
- (instancetype)init { if ((self = [super init])) _guardIds = [NSMutableSet new]; return self; }
|
|
6
5
|
RCT_EXPORT_MODULE(NativeDesktopApp)
|
|
7
6
|
+ (BOOL)requiresMainQueueSetup { return YES; }
|
|
8
7
|
- (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); }
|
|
@@ -10,19 +9,26 @@ RCT_EXPORT_MODULE(NativeDesktopApp)
|
|
|
10
9
|
- (void)startObserving { [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(event:) name:SparkDesktopEvent object:nil]; }
|
|
11
10
|
- (void)stopObserving { [NSNotificationCenter.defaultCenter removeObserver:self]; }
|
|
12
11
|
- (void)event:(NSNotification *)note { [self sendEventWithName:@"desktop" body:note.userInfo]; }
|
|
13
|
-
- (void)invalidate { [self stopObserving]; dispatch_async(dispatch_get_main_queue(), ^{ SparkSetQuitGuard(NO); }); [super invalidate]; }
|
|
12
|
+
- (void)invalidate { [self stopObserving]; dispatch_async(dispatch_get_main_queue(), ^{ for (NSString *identifier in self->_guardIds) SparkSetQuitGuard(identifier, NO); [self->_guardIds removeAllObjects]; }); [super invalidate]; }
|
|
14
13
|
- (void)call:(NSString *)method args:(NSString *)json resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
15
14
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
16
15
|
NSDictionary *args = SparkArgs(json);
|
|
17
16
|
if ([method isEqual:@"context"]) resolve(SparkJSON(SparkContext()));
|
|
18
17
|
else if ([method isEqual:@"initialURL"]) resolve(SparkJSON(SparkInitialURL()));
|
|
19
18
|
else if ([method isEqual:@"pendingURLs"]) resolve(SparkJSON(SparkPendingURLs()));
|
|
20
|
-
else if ([method isEqual:@"quit"]) {
|
|
19
|
+
else if ([method isEqual:@"quit"]) {
|
|
20
|
+
SparkObserveQuitDecision(^(BOOL allow) { resolve(SparkJSON(allow ? @{ @"quitRequested": @YES } : @{ @"quitRequested": @NO, @"reason": @"vetoed" })); });
|
|
21
21
|
// AppKit can enter a modal run loop while awaiting the JS decision. Do
|
|
22
22
|
// not hold the main GCD queue that must deliver that decision.
|
|
23
23
|
[NSRunLoop.mainRunLoop performBlock:^{ [NSApp terminate:nil]; }]; }
|
|
24
|
-
else if ([method isEqual:@"quitGuard"]) {
|
|
25
|
-
|
|
24
|
+
else if ([method isEqual:@"quitGuard"]) {
|
|
25
|
+
NSString *identifier = args[@"id"];
|
|
26
|
+
if (![identifier isKindOfClass:NSString.class] || !identifier.length) { SparkInvalid(reject, @"Expected quit guard ID"); return; }
|
|
27
|
+
BOOL enabled = [args[@"enabled"] boolValue];
|
|
28
|
+
if (enabled) [self->_guardIds addObject:identifier]; else [self->_guardIds removeObject:identifier];
|
|
29
|
+
SparkSetQuitGuard(identifier, enabled); resolve(@"null");
|
|
30
|
+
}
|
|
31
|
+
else if ([method isEqual:@"replyQuit"]) { SparkReplyQuit(args[@"id"], [args[@"allow"] boolValue], [args[@"requestId"] unsignedIntegerValue]); resolve(@"null"); }
|
|
26
32
|
else if ([method isEqual:@"hide"]) { [NSApp hide:nil]; resolve(@"null"); }
|
|
27
33
|
else if ([method isEqual:@"activate"]) { [NSApp activateIgnoringOtherApps:YES]; resolve(@"null"); }
|
|
28
34
|
else SparkInvalid(reject, @"Unknown app operation");
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#import <Foundation/Foundation.h>
|
|
3
|
+
#import <React/RCTBridgeModule.h>
|
|
4
|
+
#include <RNDesktopApp/SparkBinaryJSI.hpp>
|
|
5
|
+
|
|
6
|
+
@protocol SparkBinaryModule <NSObject>
|
|
7
|
+
- (void)binaryCall:(NSString *)method args:(NSString *)json bytes:(NSData *)bytes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
|
|
8
|
+
@end
|
|
9
|
+
#ifdef __cplusplus
|
|
10
|
+
namespace spark::binary {
|
|
11
|
+
class Data final : public facebook::jsi::MutableBuffer {
|
|
12
|
+
public:
|
|
13
|
+
explicit Data(NSData *value) : value_([value isKindOfClass:NSMutableData.class] ? (NSMutableData *)value : [value mutableCopy]) {}
|
|
14
|
+
size_t size() const override { return value_.length; }
|
|
15
|
+
uint8_t *data() override { return static_cast<uint8_t *>(value_.mutableBytes); }
|
|
16
|
+
private:
|
|
17
|
+
NSMutableData *__strong value_;
|
|
18
|
+
};
|
|
19
|
+
inline jsi::Value Value(jsi::Runtime &rt, id value) {
|
|
20
|
+
if (!value || value == NSNull.null) return jsi::Value::null();
|
|
21
|
+
if ([value isKindOfClass:NSData.class]) return Buffer(rt, std::make_shared<Data>(value));
|
|
22
|
+
if ([value isKindOfClass:NSString.class]) return jsi::String::createFromUtf8(rt, [value UTF8String]);
|
|
23
|
+
if ([value isKindOfClass:NSNumber.class]) { if (CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) return jsi::Value([value boolValue]); return jsi::Value([value doubleValue]); }
|
|
24
|
+
if ([value isKindOfClass:NSArray.class]) { jsi::Array result(rt, [value count]); size_t index = 0; for (id item in value) result.setValueAtIndex(rt, index++, Value(rt, item)); return result; }
|
|
25
|
+
jsi::Object result(rt); for (NSString *key in value) result.setProperty(rt, key.UTF8String, Value(rt, value[key])); return result;
|
|
26
|
+
}
|
|
27
|
+
inline Start ObjectiveC(id<SparkBinaryModule> module) {
|
|
28
|
+
return [module](std::string method, std::string json, std::optional<std::vector<uint8_t>> bytes, Completion finish) {
|
|
29
|
+
NSData *input = nil;
|
|
30
|
+
if (bytes && bytes->empty()) input = [NSData data];
|
|
31
|
+
else if (bytes) { auto owned = new std::vector<uint8_t>(std::move(*bytes)); input = [[NSData alloc] initWithBytesNoCopy:owned->data() length:owned->size() deallocator:^(void *, NSUInteger) { delete owned; }]; }
|
|
32
|
+
[module binaryCall:[NSString stringWithUTF8String:method.c_str()] args:[NSString stringWithUTF8String:json.c_str()] bytes:input
|
|
33
|
+
resolve:^(id value) { finish([value](jsi::Runtime &rt) { return Value(rt, value); }, {}, {}); }
|
|
34
|
+
reject:^(NSString *code, NSString *message, NSError *) { finish({}, code.UTF8String ?: "E_NATIVE", message.UTF8String ?: "Native operation failed"); }];
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
template <class Spec> class Module final : public Spec {
|
|
38
|
+
public:
|
|
39
|
+
Module(const facebook::react::ObjCTurboModule::InitParams ¶ms) : Spec(params) { this->methodMap_["binaryCall"] = {5, nullptr}; }
|
|
40
|
+
protected:
|
|
41
|
+
jsi::Value create(jsi::Runtime &rt, const jsi::PropNameID &name) override {
|
|
42
|
+
if (name.utf8(rt) == "binaryCall") return Function(rt, this->jsInvoker_, ObjectiveC((id<SparkBinaryModule>)this->instance_));
|
|
43
|
+
return Spec::create(rt, name);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#endif
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#import <AppKit/AppKit.h>
|
|
2
|
+
#import "SparkLifecycle.h"
|
|
2
3
|
#import <React/RCTBridgeModule.h>
|
|
3
4
|
|
|
4
5
|
FOUNDATION_EXPORT NSString * const SparkDesktopEvent;
|
|
@@ -12,6 +13,9 @@ FOUNDATION_EXPORT NSArray *SparkPendingURLs(void);
|
|
|
12
13
|
FOUNDATION_EXPORT NSString *SparkInitialURL(void);
|
|
13
14
|
FOUNDATION_EXPORT void SparkMarkLaunchComplete(void);
|
|
14
15
|
FOUNDATION_EXPORT NSApplicationTerminateReply SparkShouldQuit(void);
|
|
16
|
+
FOUNDATION_EXPORT void SparkObserveQuitDecision(SparkQuitReply observer);
|
|
17
|
+
FOUNDATION_EXPORT void SparkSetQuitGuard(NSString *identifier, BOOL value);
|
|
18
|
+
FOUNDATION_EXPORT void SparkReplyQuit(NSString *identifier, BOOL allow, NSUInteger generation);
|
|
15
19
|
FOUNDATION_EXPORT NSString *SparkJSON(id value);
|
|
16
20
|
FOUNDATION_EXPORT NSDictionary *SparkArgs(NSString *json);
|
|
17
21
|
FOUNDATION_EXPORT void SparkReject(RCTPromiseRejectBlock reject, NSError *error);
|