@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@legendapp/spark-notifications",
|
|
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",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
]
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@legendapp/spark-desktop-app": "0.0.1-next.
|
|
43
|
+
"@legendapp/spark-desktop-app": "0.0.1-next.3"
|
|
44
44
|
},
|
|
45
45
|
"private": true
|
|
46
46
|
}
|
package/vendor/node_modules/@legendapp/spark-notifications/src/NativeDesktopNotifications.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { TurboModule } from "react-native";
|
|
2
2
|
import { TurboModuleRegistry } from "react-native";
|
|
3
3
|
export interface Spec extends TurboModule { call(method: string, args: string): Promise<string>; }
|
|
4
|
-
export default TurboModuleRegistry.
|
|
4
|
+
export default TurboModuleRegistry.get<Spec>("NativeDesktopNotifications");
|
|
@@ -1,53 +1,141 @@
|
|
|
1
1
|
import Native from "./NativeDesktopNotifications";
|
|
2
|
-
import { Platform } from "react-native";
|
|
3
|
-
import {
|
|
2
|
+
import { Platform, TurboModuleRegistry } from "react-native";
|
|
3
|
+
import type { Spec as AppSpec } from "@legendapp/spark-desktop-app/src/NativeDesktopApp";
|
|
4
|
+
import { onDesktopEvent } from "@legendapp/spark-desktop-app/src/events";
|
|
5
|
+
import { SparkError, invokeNative, parseNativeResult, type Availability, type Subscription } from "@legendapp/spark-desktop-app/src/contracts";
|
|
6
|
+
export type { Subscription } from "@legendapp/spark-desktop-app/src/contracts";
|
|
4
7
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
export interface NotificationPermission {
|
|
9
|
+
status: "undetermined" | "denied" | "granted" | "unknown";
|
|
10
|
+
granted: boolean;
|
|
11
|
+
/** null when the host cannot determine this; Windows has no in-app prompt. */
|
|
12
|
+
canAskAgain: boolean | null;
|
|
13
|
+
macos?: { authorization: "notDetermined" | "denied" | "authorized" | "provisional" | "unknown" };
|
|
14
|
+
}
|
|
15
|
+
export interface NotificationAction { id: string; label: string }
|
|
16
|
+
/** Portable tone names; each target maps them to its own system sounds (documented in the native bridges). */
|
|
17
|
+
export type NotificationTone = "message" | "mail" | "reminder" | "call" | "error";
|
|
18
|
+
export interface NotificationContent {
|
|
8
19
|
title: string;
|
|
9
20
|
body?: string;
|
|
10
21
|
subtitle?: string;
|
|
11
|
-
sound
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
/** false/omitted is silent; true or "default" plays the platform default; a tone selects a system sound. */
|
|
23
|
+
sound?: boolean | "default" | NotificationTone;
|
|
24
|
+
/** Custom buttons: 1–4 entries (the portable intersection of macOS categories and Windows toasts). macOS banners show the first two; the rest appear in Notification Center. Tapping the body reports "open", dismissing reports "dismiss". */
|
|
25
|
+
actions?: readonly NotificationAction[];
|
|
14
26
|
data?: Record<string, string>;
|
|
15
|
-
}
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
}
|
|
28
|
+
export interface ShowNotificationOptions { id: string; content: NotificationContent }
|
|
29
|
+
export type NotificationTrigger = { type: "delay"; delaySeconds: number };
|
|
30
|
+
export interface ScheduleNotificationOptions extends ShowNotificationOptions { trigger: NotificationTrigger }
|
|
31
|
+
/** action is "open", "dismiss", or the id of a tapped notification action. */
|
|
32
|
+
export interface NotificationResponse { type: "notificationResponse"; id: string; notificationId: string; action: "open" | "dismiss" | (string & {}); data: Record<string, string> }
|
|
33
|
+
export function getNotificationAvailability(): Availability {
|
|
34
|
+
if (Platform.OS !== "macos" && Platform.OS !== "windows") return { available: false, reason: "unsupported-platform" };
|
|
35
|
+
return Native ? { available: true } : { available: false, reason: "missing-module" };
|
|
36
|
+
}
|
|
37
|
+
function native() {
|
|
38
|
+
const availability = getNotificationAvailability();
|
|
39
|
+
if (!availability.available) throw new SparkError(availability.reason === "unsupported-platform" ? "E_UNSUPPORTED_PLATFORM" : "E_MODULE_UNAVAILABLE", "Desktop notifications are unavailable");
|
|
40
|
+
return Native!;
|
|
41
|
+
}
|
|
42
|
+
async function call<T>(method: string, args: object, validate: (value: unknown) => value is T): Promise<T> {
|
|
43
|
+
return parseNativeResult(await invokeNative(() => native().call(method, JSON.stringify(args))), validate);
|
|
44
|
+
}
|
|
45
|
+
async function command(method: string, args: object = {}): Promise<void> { await call(method, args, (value): value is null => value === null); }
|
|
46
|
+
function validId(id: unknown): id is string { return typeof id === "string" && /^[a-zA-Z0-9_.-]{1,100}$/.test(id); }
|
|
47
|
+
function id(value: string): string {
|
|
48
|
+
if (!validId(value)) throw new SparkError("E_INVALID_ARGUMENT", "Notification id must contain 1–100 letters, numbers, dots, underscores or hyphens");
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
function options(value: unknown, keys: string[]) {
|
|
52
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new SparkError("E_INVALID_ARGUMENT", "Expected notification options");
|
|
53
|
+
for (const key of Object.keys(value)) if (!keys.includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unsupported notification option: ${key}`);
|
|
54
|
+
}
|
|
55
|
+
function stringRecord(value: unknown): value is Record<string, string> {
|
|
56
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every(item => typeof item === "string");
|
|
57
|
+
}
|
|
58
|
+
const tones = ["message", "mail", "reminder", "call", "error"] as const;
|
|
59
|
+
function sound(value: unknown): "none" | "default" | NotificationTone {
|
|
60
|
+
if (value === undefined || value === false) return "none";
|
|
61
|
+
if (value === true || value === "default") return "default";
|
|
62
|
+
if (typeof value === "string" && (tones as readonly string[]).includes(value)) return value as NotificationTone;
|
|
63
|
+
throw new SparkError("E_INVALID_ARGUMENT", "Notification sound must be false, true, \"default\", or a tone name");
|
|
64
|
+
}
|
|
65
|
+
function actions(value: unknown): NotificationAction[] | undefined {
|
|
66
|
+
if (value === undefined) return undefined;
|
|
67
|
+
if (!Array.isArray(value) || !value.length || value.length > 4) throw new SparkError("E_INVALID_ARGUMENT", "Notification actions must contain 1–4 entries");
|
|
68
|
+
return value.map(action => {
|
|
69
|
+
if (!action || typeof action !== "object" || Array.isArray(action) || Object.keys(action).some(key => key !== "id" && key !== "label") || !validId((action as NotificationAction).id) || ["open", "dismiss"].includes((action as NotificationAction).id) || typeof (action as NotificationAction).label !== "string" || !(action as NotificationAction).label.trim() || (action as NotificationAction).label.includes("\0")) throw new SparkError("E_INVALID_ARGUMENT", "Notification actions need an id outside open/dismiss and a label");
|
|
70
|
+
return { id: action.id, label: action.label };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function notification(value: ShowNotificationOptions, scheduled: boolean) {
|
|
74
|
+
options(value, scheduled ? ["id", "content", "trigger"] : ["id", "content"]);
|
|
75
|
+
id(value.id); options(value.content, ["title", "body", "subtitle", "sound", "actions", "data"]);
|
|
76
|
+
const content = value.content;
|
|
77
|
+
if (typeof content.title !== "string" || !content.title.trim()) throw new SparkError("E_INVALID_ARGUMENT", "Notification title is required");
|
|
78
|
+
if ([content.body, content.subtitle].some(text => text !== undefined && typeof text !== "string") || (content.data !== undefined && !stringRecord(content.data))) throw new SparkError("E_INVALID_ARGUMENT", "Invalid notification content");
|
|
79
|
+
const result = { id: value.id, title: content.title, body: content.body, subtitle: content.subtitle, data: content.data, sound: sound(content.sound) };
|
|
80
|
+
const actionList = actions(content.actions);
|
|
81
|
+
return actionList === undefined ? result : { ...result, actions: actionList };
|
|
82
|
+
}
|
|
83
|
+
async function permission(method: string): Promise<NotificationPermission> {
|
|
84
|
+
const value = await call(method, {}, (value): value is NonNullable<NotificationPermission["macos"]>["authorization"] => typeof value === "string" && ["notDetermined", "denied", "authorized", "provisional", "unknown"].includes(value));
|
|
85
|
+
const granted = value === "authorized" || value === "provisional";
|
|
86
|
+
return { status: granted ? "granted" : value === "notDetermined" ? "undetermined" : value as "denied" | "unknown", granted,
|
|
87
|
+
canAskAgain: Platform.OS === "windows" ? false : value === "unknown" ? null : value !== "denied",
|
|
88
|
+
...(Platform.OS === "macos" ? { macos: { authorization: value } } : {}) };
|
|
89
|
+
}
|
|
90
|
+
export async function getNotificationPermission(): Promise<NotificationPermission> { return permission("permission"); }
|
|
91
|
+
/** Call from an explicit user action. Windows reads OS settings without a prompt. */
|
|
92
|
+
export async function requestNotificationPermission(): Promise<NotificationPermission> { return permission("requestPermission"); }
|
|
93
|
+
/** Submission accepted by the OS; it does not guarantee presentation. */
|
|
94
|
+
export async function showNotification(value: ShowNotificationOptions): Promise<void> { await command("show", notification(value, false)); }
|
|
95
|
+
export async function scheduleNotification(value: ScheduleNotificationOptions): Promise<void> {
|
|
96
|
+
const content = notification(value, true);
|
|
97
|
+
options(value.trigger, ["type", "delaySeconds"]);
|
|
98
|
+
if (value.trigger.type !== "delay" || !Number.isFinite(value.trigger.delaySeconds) || value.trigger.delaySeconds < 1 || value.trigger.delaySeconds > 315360000) throw new SparkError("E_INVALID_ARGUMENT", "Notification delay must be 1 second to ten years");
|
|
99
|
+
await command("show", { ...content, delay: value.trigger.delaySeconds });
|
|
100
|
+
}
|
|
101
|
+
/** Cancels pending delivery only. Missing IDs succeed. */
|
|
102
|
+
export async function cancelNotification(value: string): Promise<void> { await command("cancel", { id: id(value) }); }
|
|
103
|
+
export async function cancelAllNotifications(): Promise<void> { await command("cancelAll"); }
|
|
104
|
+
/** Removes delivered notifications only. Missing IDs succeed. */
|
|
105
|
+
export async function dismissNotification(value: string): Promise<void> { await command("dismiss", { id: id(value) }); }
|
|
106
|
+
export async function dismissAllNotifications(): Promise<void> { await command("dismissAll"); }
|
|
107
|
+
const ids = (value: unknown): value is string[] => Array.isArray(value) && value.every(validId);
|
|
108
|
+
export async function getPendingNotifications(): Promise<string[]> { return call("pending", {}, ids); }
|
|
109
|
+
export async function getDeliveredNotifications(): Promise<string[]> { return call("delivered", {}, ids); }
|
|
110
|
+
function response(value: unknown): value is NotificationResponse {
|
|
111
|
+
if (!value || typeof value !== "object") return false;
|
|
112
|
+
const event = value as NotificationResponse;
|
|
113
|
+
return event.type === "notificationResponse" && typeof event.id === "string" && event.id.length > 0 && validId(event.notificationId) && validId(event.action) && stringRecord(event.data);
|
|
114
|
+
}
|
|
115
|
+
/** Subscribe before reading retained cold-launch responses; deduplicate queued/live overlap.
|
|
116
|
+
* Each new subscription replays retained responses (up to 100). Cleanup is synchronous.
|
|
117
|
+
*/
|
|
118
|
+
export async function onNotificationResponse(listener: (event: NotificationResponse) => void): Promise<Subscription> {
|
|
119
|
+
if (typeof listener !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected notification response listener");
|
|
120
|
+
native();
|
|
41
121
|
let removed = false;
|
|
42
122
|
const seen = new Set<string>();
|
|
43
|
-
function receive(
|
|
44
|
-
if (removed || seen.has(
|
|
45
|
-
seen.add(
|
|
123
|
+
function receive(event: NotificationResponse) {
|
|
124
|
+
if (removed || seen.has(event.id)) return;
|
|
125
|
+
seen.add(event.id);
|
|
46
126
|
if (seen.size > 256) seen.delete(seen.values().next().value!);
|
|
47
|
-
listener(
|
|
127
|
+
listener(event);
|
|
48
128
|
}
|
|
49
|
-
const sub = onDesktopEvent(event => { if (event
|
|
50
|
-
try {
|
|
51
|
-
|
|
52
|
-
|
|
129
|
+
const sub = onDesktopEvent(event => { if (response(event)) receive(event); }, { types: ["notificationResponse"] });
|
|
130
|
+
try {
|
|
131
|
+
const responses = Platform.OS === "windows"
|
|
132
|
+
? parseNativeResult(await invokeNative(() => {
|
|
133
|
+
const app = TurboModuleRegistry.get<AppSpec>("NativeDesktopApp");
|
|
134
|
+
if (!app) throw new SparkError("E_MODULE_UNAVAILABLE", "Notification activation host is unavailable");
|
|
135
|
+
return app.call("notificationResponses", "{}");
|
|
136
|
+
}), (value): value is NotificationResponse[] => Array.isArray(value) && value.every(response))
|
|
137
|
+
: await call("responses", {}, (value): value is NotificationResponse[] => Array.isArray(value) && value.every(response));
|
|
138
|
+
responses.forEach(receive);
|
|
139
|
+
} catch (error) { removed = true; sub.remove(); throw error; }
|
|
140
|
+
return { remove() { if (!removed) { removed = true; sub.remove(); } } };
|
|
53
141
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <filesystem>
|
|
8
8
|
#include <map>
|
|
9
9
|
#include <memory>
|
|
10
|
+
#include <cstdint>
|
|
10
11
|
#include <cmath>
|
|
11
12
|
#include <winrt/Windows.Data.Json.h>
|
|
12
13
|
#include <winrt/Windows.Data.Xml.Dom.h>
|
|
@@ -38,8 +39,9 @@ struct NotificationsState : std::enable_shared_from_this<NotificationsState> {
|
|
|
38
39
|
React::ReactContext context;
|
|
39
40
|
std::wstring project, appId;
|
|
40
41
|
Toast::ToastNotifier notifier{nullptr};
|
|
41
|
-
struct Live { Toast::ToastNotification toast{nullptr}; event_token dismissed; };
|
|
42
|
+
struct Live { Toast::ToastNotification toast{nullptr}; event_token dismissed; uint64_t generation; };
|
|
42
43
|
std::map<std::wstring, Live> live;
|
|
44
|
+
uint64_t nextGeneration = 0;
|
|
43
45
|
static constexpr wchar_t const *group = L"Spark";
|
|
44
46
|
void Ensure() {
|
|
45
47
|
if (notifier) return;
|
|
@@ -75,6 +77,12 @@ struct NotificationsState : std::enable_shared_from_this<NotificationsState> {
|
|
|
75
77
|
notifier = candidate;
|
|
76
78
|
}
|
|
77
79
|
void Forget(std::wstring const &tag) { auto entry = live.find(tag); if (entry != live.end()) { entry->second.toast.Dismissed(entry->second.dismissed); live.erase(entry); } }
|
|
80
|
+
void Retire(std::wstring const &tag, uint64_t generation, bool userDismissed, std::wstring const &encoded) {
|
|
81
|
+
auto entry = live.find(tag);
|
|
82
|
+
if (entry == live.end() || entry->second.generation != generation) return;
|
|
83
|
+
Forget(tag);
|
|
84
|
+
if (userDismissed) Dismiss(hstring(encoded));
|
|
85
|
+
}
|
|
78
86
|
void Close() noexcept { for (auto &[tag, item] : live) try { item.toast.Dismissed(item.dismissed); } catch (...) {} live.clear(); }
|
|
79
87
|
void RemovePending(std::wstring const &tag) { for (auto const &item : notifier.GetScheduledToastNotifications()) if (item.Group() == group && (tag.empty() || item.Tag() == tag)) notifier.RemoveFromSchedule(item); }
|
|
80
88
|
static hstring Id(Xml::XmlDocument const &xml) { return Json::JsonObject::Parse(xml.DocumentElement().GetAttribute(L"launch")).GetNamedString(L"notificationId"); }
|
|
@@ -92,15 +100,43 @@ struct NotificationsState : std::enable_shared_from_this<NotificationsState> {
|
|
|
92
100
|
Xml::XmlDocument xml; xml.LoadXml(L"<toast><visual><binding template='ToastGeneric'/></visual></toast>"); xml.DocumentElement().SetAttribute(L"launch", response.Stringify());
|
|
93
101
|
auto binding = xml.SelectSingleNode(L"/toast/visual/binding");
|
|
94
102
|
for (auto field : {L"title", L"subtitle", L"body"}) { auto text = args.GetNamedString(field, L""); if (!text.empty()) { auto node = xml.CreateElement(L"text"); node.AppendChild(xml.CreateTextNode(text)); binding.AppendChild(node); } }
|
|
95
|
-
|
|
103
|
+
// JS normalizes sound to "none", "default", or a tone name mapped onto the
|
|
104
|
+
// system Notification.* catalog; tones without a Windows counterpart use Default.
|
|
105
|
+
auto sound = args.GetNamedString(L"sound", L"none");
|
|
106
|
+
if (sound == L"none") { auto audio = xml.CreateElement(L"audio"); audio.SetAttribute(L"silent", L"true"); xml.DocumentElement().AppendChild(audio); }
|
|
107
|
+
else {
|
|
108
|
+
auto audio = xml.CreateElement(L"audio");
|
|
109
|
+
auto tone = std::wstring(L"ms-winsoundevent:Notification.");
|
|
110
|
+
if (sound == L"mail") tone += L"Mail"; else if (sound == L"message") tone += L"IM"; else if (sound == L"reminder") tone += L"Reminder";
|
|
111
|
+
else if (sound == L"call") { tone += L"Looping.Call"; audio.SetAttribute(L"duration", L"long"); }
|
|
112
|
+
else tone += L"Default";
|
|
113
|
+
audio.SetAttribute(L"src", tone.c_str()); xml.DocumentElement().AppendChild(audio);
|
|
114
|
+
}
|
|
115
|
+
// Action buttons activate through the same CustomActivator as the toast body,
|
|
116
|
+
// carrying their own action id in the activation arguments.
|
|
117
|
+
for (auto const &entry : args.GetNamedArray(L"actions", Json::JsonArray())) {
|
|
118
|
+
auto action = entry.GetObject();
|
|
119
|
+
auto button = xml.CreateElement(L"action");
|
|
120
|
+
// JsonObject is a reference type; parse a copy so each action carries its
|
|
121
|
+
// own action id without mutating the body/dismiss payload.
|
|
122
|
+
auto arguments = Json::JsonObject::Parse(response.Stringify());
|
|
123
|
+
arguments.SetNamedValue(L"action", Json::JsonValue::CreateStringValue(action.GetNamedString(L"id")));
|
|
124
|
+
button.SetAttribute(L"content", action.GetNamedString(L"label").c_str());
|
|
125
|
+
button.SetAttribute(L"arguments", arguments.Stringify().c_str());
|
|
126
|
+
xml.DocumentElement().AppendChild(button);
|
|
127
|
+
}
|
|
96
128
|
auto tag = Hash(id); RemovePending(tag); Forget(tag);
|
|
97
129
|
if (delay > 0) {
|
|
98
130
|
Toast::ScheduledToastNotification toast(xml, clock::now() + std::chrono::milliseconds(static_cast<int64_t>(delay * 1000))); toast.Tag(tag); toast.Group(group); notifier.AddToSchedule(toast);
|
|
99
131
|
} else {
|
|
100
132
|
Toast::ToastNotification toast(xml); toast.Tag(tag); toast.Group(group);
|
|
101
133
|
response.SetNamedValue(L"action", Json::JsonValue::CreateStringValue(L"dismiss")); auto encoded = response.Stringify(); auto weak = weak_from_this(); auto dispatcher = context.UIDispatcher();
|
|
102
|
-
|
|
103
|
-
|
|
134
|
+
const auto generation = ++nextGeneration;
|
|
135
|
+
auto token = toast.Dismissed([weak, dispatcher, tag, generation, encoded](auto const &, Toast::ToastDismissedEventArgs const &event) {
|
|
136
|
+
const bool userDismissed = event.Reason() == Toast::ToastDismissalReason::UserCanceled;
|
|
137
|
+
dispatcher.Post([weak, tag, generation, userDismissed, encoded]() { if (auto state = weak.lock()) state->Retire(tag, generation, userDismissed, encoded); });
|
|
138
|
+
});
|
|
139
|
+
try { notifier.Show(toast); live.emplace(tag, Live{toast, token, generation}); } catch (...) { toast.Dismissed(token); throw; }
|
|
104
140
|
}
|
|
105
141
|
}
|
|
106
142
|
};
|
|
@@ -115,8 +151,10 @@ struct SparkNotifications {
|
|
|
115
151
|
value->Ensure(); auto args = Json::JsonObject::Parse(to_hstring(encoded));
|
|
116
152
|
if (method == "permission" || method == "requestPermission") { promise.Resolve(value->notifier.Setting() == Toast::NotificationSetting::Enabled ? "\"authorized\"" : "\"denied\""); return; }
|
|
117
153
|
if (method == "show") value->Show(args);
|
|
118
|
-
else if (method == "cancel")
|
|
119
|
-
else if (method == "
|
|
154
|
+
else if (method == "cancel") value->RemovePending(Hash(args.GetNamedString(L"id")));
|
|
155
|
+
else if (method == "dismiss") { auto tag = Hash(args.GetNamedString(L"id")); value->Forget(tag); Toast::ToastNotificationManager::History().Remove(tag, value->group, value->appId); }
|
|
156
|
+
else if (method == "cancelAll") value->RemovePending(L"");
|
|
157
|
+
else if (method == "dismissAll") { value->Close(); Toast::ToastNotificationManager::History().RemoveGroup(value->group, value->appId); }
|
|
120
158
|
else if (method == "pending" || method == "delivered") {
|
|
121
159
|
Json::JsonArray result;
|
|
122
160
|
if (method == "pending") { for (auto const &item : value->notifier.GetScheduledToastNotifications()) if (item.Group() == value->group) result.Append(Json::JsonValue::CreateStringValue(value->Id(item.Content()))); }
|
|
@@ -124,8 +162,8 @@ struct SparkNotifications {
|
|
|
124
162
|
promise.Resolve(to_string(result.Stringify())); return;
|
|
125
163
|
} else throw hresult_invalid_argument(L"Unsupported notification operation");
|
|
126
164
|
promise.Resolve("null");
|
|
127
|
-
} catch (hresult_error const &error) { promise.Reject(React::ReactError{error.code() == E_ACCESSDENIED ? "
|
|
128
|
-
catch (std::exception const &error) { promise.Reject(React::ReactError{"
|
|
165
|
+
} catch (hresult_error const &error) { promise.Reject(React::ReactError{error.code() == E_ACCESSDENIED ? "E_PERMISSION_DENIED" : "E_NATIVE", to_string(error.message())}); }
|
|
166
|
+
catch (std::exception const &error) { promise.Reject(React::ReactError{"E_NATIVE", error.what()}); }
|
|
129
167
|
});
|
|
130
168
|
}
|
|
131
169
|
};
|
|
@@ -1,10 +1,49 @@
|
|
|
1
1
|
#import "RNDesktopProcesses.h"
|
|
2
|
+
#import <RNDesktopApp/SparkBinaryJSI.h>
|
|
2
3
|
#import <RNDesktopApp/SparkDesktop.h>
|
|
3
4
|
#import <signal.h>
|
|
4
5
|
#import <AppKit/AppKit.h>
|
|
5
6
|
#import <spawn.h>
|
|
6
7
|
#import <sys/wait.h>
|
|
7
8
|
#import <vector>
|
|
9
|
+
static NSString *ResolveCommandPath(NSString *command)
|
|
10
|
+
{
|
|
11
|
+
NSString *homeDirectory = NSHomeDirectory();
|
|
12
|
+
NSArray<NSString *> *fallbackPaths = @[
|
|
13
|
+
[homeDirectory stringByAppendingPathComponent:@".local/bin"],
|
|
14
|
+
[homeDirectory stringByAppendingPathComponent:@".bun/bin"],
|
|
15
|
+
[homeDirectory stringByAppendingPathComponent:@"bin"],
|
|
16
|
+
@"/opt/homebrew/bin",
|
|
17
|
+
@"/usr/local/bin",
|
|
18
|
+
@"/usr/bin",
|
|
19
|
+
@"/bin",
|
|
20
|
+
@"/usr/sbin",
|
|
21
|
+
@"/sbin",
|
|
22
|
+
];
|
|
23
|
+
NSMutableArray<NSString *> *searchPaths = [NSMutableArray array];
|
|
24
|
+
NSString *pathValue = [[NSProcessInfo processInfo] environment][@"PATH"];
|
|
25
|
+
if (pathValue.length > 0) {
|
|
26
|
+
[searchPaths addObjectsFromArray:[pathValue componentsSeparatedByString:@":"]];
|
|
27
|
+
}
|
|
28
|
+
for (NSString *fallback in fallbackPaths) {
|
|
29
|
+
if (![searchPaths containsObject:fallback]) {
|
|
30
|
+
[searchPaths addObject:fallback];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
35
|
+
for (NSString *directory in searchPaths) {
|
|
36
|
+
if (![directory hasPrefix:@"/"]) continue;
|
|
37
|
+
NSString *candidate = [directory stringByAppendingPathComponent:command];
|
|
38
|
+
BOOL directoryEntry = NO;
|
|
39
|
+
if ([fileManager fileExistsAtPath:candidate isDirectory:&directoryEntry] && !directoryEntry && [fileManager isExecutableFileAtPath:candidate]) {
|
|
40
|
+
return candidate;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return nil;
|
|
45
|
+
}
|
|
46
|
+
|
|
8
47
|
@interface SparkProcess : NSObject
|
|
9
48
|
@property pid_t pid;
|
|
10
49
|
@property BOOL running;
|
|
@@ -12,6 +51,7 @@
|
|
|
12
51
|
@property BOOL timedOut;
|
|
13
52
|
@property BOOL truncated;
|
|
14
53
|
@property BOOL ended;
|
|
54
|
+
@property BOOL inputClosed;
|
|
15
55
|
@property dispatch_queue_t inputQueue;
|
|
16
56
|
@end
|
|
17
57
|
@implementation SparkProcess
|
|
@@ -19,6 +59,7 @@
|
|
|
19
59
|
@interface RNDesktopProcesses ()
|
|
20
60
|
@property NSMutableDictionary<NSString *, SparkProcess *> *processes;
|
|
21
61
|
@property BOOL invalidated;
|
|
62
|
+
@property (copy) void (^binaryEvent)(NSDictionary *event, dispatch_block_t delivered);
|
|
22
63
|
@end
|
|
23
64
|
@implementation RNDesktopProcesses
|
|
24
65
|
RCT_EXPORT_MODULE(NativeDesktopProcesses)
|
|
@@ -33,23 +74,32 @@ RCT_EXPORT_MODULE(NativeDesktopProcesses)
|
|
|
33
74
|
- (void)stopAll { for (SparkProcess *process in self.processes.allValues) if (process.running) kill(-process.pid, SIGKILL); }
|
|
34
75
|
- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
|
|
35
76
|
|
|
36
|
-
- (
|
|
77
|
+
- (BOOL)terminate:(SparkProcess *)process {
|
|
37
78
|
if (process.running) {
|
|
38
|
-
kill(-process.pid, SIGTERM);
|
|
79
|
+
if (kill(-process.pid, SIGTERM) != 0 && errno != ESRCH) return NO;
|
|
39
80
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
|
40
81
|
if (process.running) kill(-process.pid, SIGKILL);
|
|
41
82
|
});
|
|
42
83
|
}
|
|
84
|
+
return YES;
|
|
43
85
|
}
|
|
44
86
|
|
|
45
|
-
- (void)call:(NSString *)method args:(NSString *)json resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
87
|
+
- (void)call:(NSString *)method args:(NSString *)json resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [self perform:method args:SparkArgs(json) binary:NO resolve:resolve reject:reject]; }
|
|
88
|
+
- (void)binaryCall:(NSString *)method args:(NSString *)json bytes:(NSData *)bytes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { NSMutableDictionary *args = [SparkArgs(json) mutableCopy]; if (bytes) args[@"bytes"] = bytes; [self perform:method args:args binary:YES resolve:resolve reject:reject]; }
|
|
89
|
+
- (void)perform:(NSString *)method args:(NSDictionary *)args binary:(BOOL)binary resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
46
90
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
47
|
-
|
|
91
|
+
NSString *key = args[@"id"];
|
|
48
92
|
if (self.invalidated) { reject(@"E_CLOSED", @"Process module is closed", nil); return; }
|
|
49
|
-
|
|
93
|
+
if ([method isEqual:@"resolveCommand"]) {
|
|
94
|
+
NSString *command = args[@"command"];
|
|
95
|
+
if (![command isKindOfClass:NSString.class] || !command.length) { SparkInvalid(reject, @"Expected a command name"); return; }
|
|
96
|
+
resolve(binary ? (ResolveCommandPath(command) ?: (id)NSNull.null) : SparkJSON(ResolveCommandPath(command) ?: (id)NSNull.null)); return;
|
|
97
|
+
}
|
|
98
|
+
SparkProcess *process = key ? self.processes[key] : nil;
|
|
50
99
|
if ([method isEqual:@"spawn"]) {
|
|
51
100
|
if (process) { reject(@"E_EXISTS", @"Process id already exists", nil); return; }
|
|
52
101
|
NSString *executable = args[@"executable"];
|
|
102
|
+
if ([args[@"command"] boolValue]) { executable = ResolveCommandPath(executable); if (!executable) { reject(@"E_NOT_FOUND", @"Command not found", nil); return; } }
|
|
53
103
|
if ([executable hasPrefix:@"helper:"]) {
|
|
54
104
|
NSString *name = [executable substringFromIndex:7];
|
|
55
105
|
if (!name.length || [name rangeOfCharacterFromSet:[[NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"] invertedSet]].location != NSNotFound) { SparkInvalid(reject, @"Invalid helper name"); return; }
|
|
@@ -63,6 +113,10 @@ RCT_EXPORT_MODULE(NativeDesktopProcesses)
|
|
|
63
113
|
}
|
|
64
114
|
}
|
|
65
115
|
if (![executable hasPrefix:@"/"]) { SparkInvalid(reject, @"Executable path must be absolute"); return; }
|
|
116
|
+
NSUInteger captureLimit = [args[@"captureLimitBytes"] unsignedIntegerValue];
|
|
117
|
+
if (captureLimit > 8 * 1024 * 1024) { SparkInvalid(reject, @"Invalid capture limit"); return; }
|
|
118
|
+
NSData *initialInput = args[@"bytes"];
|
|
119
|
+
if (args[@"bytes"] && ![initialInput isKindOfClass:NSData.class]) { SparkInvalid(reject, @"Invalid process input"); return; }
|
|
66
120
|
process = [SparkProcess new];
|
|
67
121
|
process.inputQueue = dispatch_queue_create("desktop.process.input", DISPATCH_QUEUE_SERIAL);
|
|
68
122
|
NSMutableDictionary *environment = [NSProcessInfo.processInfo.environment mutableCopy]; [environment addEntriesFromDictionary:args[@"env"] ?: @{}];
|
|
@@ -71,7 +125,11 @@ RCT_EXPORT_MODULE(NativeDesktopProcesses)
|
|
|
71
125
|
// Create the process group atomically, before executable code can fork.
|
|
72
126
|
// NSTask + setpgid after launch has a race and cannot provide this contract.
|
|
73
127
|
posix_spawnattr_t attributes; posix_spawnattr_init(&attributes);
|
|
74
|
-
|
|
128
|
+
// GCD/host threads can block or ignore signals. Child defaults must not
|
|
129
|
+
// inherit that state or cooperative SIGTERM degrades into forced SIGKILL.
|
|
130
|
+
sigset_t mask, defaults; sigemptyset(&mask); sigfillset(&defaults);
|
|
131
|
+
posix_spawnattr_setsigmask(&attributes, &mask); posix_spawnattr_setsigdefault(&attributes, &defaults);
|
|
132
|
+
posix_spawnattr_setflags(&attributes, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_CLOEXEC_DEFAULT | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);
|
|
75
133
|
posix_spawnattr_setpgroup(&attributes, 0);
|
|
76
134
|
posix_spawn_file_actions_t actions; posix_spawn_file_actions_init(&actions);
|
|
77
135
|
posix_spawn_file_actions_adddup2(&actions, input.fileHandleForReading.fileDescriptor, STDIN_FILENO);
|
|
@@ -98,6 +156,9 @@ RCT_EXPORT_MODULE(NativeDesktopProcesses)
|
|
|
98
156
|
NSFileHandle *handle = streams[index]; NSMutableData *buffer = index ? stderrData : stdoutData;
|
|
99
157
|
NSString *stream = index ? @"stderr" : @"stdout";
|
|
100
158
|
dispatch_group_async(group, dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
|
159
|
+
// Keep up to 1 MiB in flight per stream; ordinary delivery never waits
|
|
160
|
+
// for a per-chunk JS acknowledgement. A stalled consumer backpressures the pipe.
|
|
161
|
+
dispatch_semaphore_t slots = dispatch_semaphore_create(64);
|
|
101
162
|
@try {
|
|
102
163
|
while (YES) {
|
|
103
164
|
// readDataOfLength can wait for the requested length. A service's
|
|
@@ -105,9 +166,13 @@ RCT_EXPORT_MODULE(NativeDesktopProcesses)
|
|
|
105
166
|
char bytes[16384]; ssize_t count;
|
|
106
167
|
do { count = read(handle.fileDescriptor, bytes, sizeof(bytes)); } while (count < 0 && errno == EINTR);
|
|
107
168
|
if (count <= 0) break;
|
|
108
|
-
|
|
109
|
-
@synchronized(process) { NSUInteger remaining =
|
|
110
|
-
if ([args[@"streamOutput"] boolValue])
|
|
169
|
+
NSMutableData *data = [NSMutableData dataWithBytes:bytes length:(NSUInteger)count];
|
|
170
|
+
@synchronized(process) { NSUInteger remaining = captureLimit - buffer.length; if (data.length > remaining) process.truncated = YES; [buffer appendData:[data subdataWithRange:NSMakeRange(0, MIN(remaining, data.length))]]; }
|
|
171
|
+
if ([args[@"streamOutput"] boolValue]) {
|
|
172
|
+
while (!self.invalidated && dispatch_semaphore_wait(slots, dispatch_time(DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC))) {}
|
|
173
|
+
if (self.invalidated) break;
|
|
174
|
+
dispatch_sync(dispatch_get_main_queue(), ^{ if (!self.invalidated) self.binaryEvent(@{ @"type": @"processOutput", @"processId": key, @"stream": stream, @"bytes": data }, ^{ dispatch_semaphore_signal(slots); }); else dispatch_semaphore_signal(slots); });
|
|
175
|
+
}
|
|
111
176
|
}
|
|
112
177
|
} @catch (NSException *exception) { /* Closing the runtime interrupts pipe reads. */ }
|
|
113
178
|
[handle closeFile];
|
|
@@ -123,31 +188,39 @@ RCT_EXPORT_MODULE(NativeDesktopProcesses)
|
|
|
123
188
|
int terminationStatus = status;
|
|
124
189
|
process.ended = YES;
|
|
125
190
|
dispatch_async(process.inputQueue, ^{ @try { [process.input closeFile]; } @catch (NSException *exception) {} });
|
|
126
|
-
if (!self.invalidated)
|
|
127
|
-
@"exitCode": @(WIFEXITED(terminationStatus) ? WEXITSTATUS(terminationStatus) : WTERMSIG(terminationStatus)), @"
|
|
128
|
-
@"stdout":
|
|
129
|
-
@"
|
|
130
|
-
@"timedOut": @(process.timedOut), @"outputTruncated": @(process.truncated) } });
|
|
191
|
+
if (!self.invalidated) self.binaryEvent(@{ @"type": @"processExit", @"processId": key, @"result": @{
|
|
192
|
+
@"exitCode": @(WIFEXITED(terminationStatus) ? WEXITSTATUS(terminationStatus) : WTERMSIG(terminationStatus)), @"terminated": @(WIFSIGNALED(terminationStatus)), @"terminationSignal": WIFSIGNALED(terminationStatus) ? @(WTERMSIG(terminationStatus)) : (id)NSNull.null,
|
|
193
|
+
@"stdout": stdoutData, @"stderr": stderrData,
|
|
194
|
+
@"timedOut": @(process.timedOut), @"outputTruncated": @(process.truncated) } }, nil);
|
|
131
195
|
[self.processes removeObjectForKey:key];
|
|
132
196
|
});
|
|
133
197
|
});
|
|
134
|
-
if (
|
|
198
|
+
if (initialInput) { NSData *data = initialInput; dispatch_async(process.inputQueue, ^{ @try { [process.input writeData:data]; } @catch (NSException *exception) {} }); }
|
|
135
199
|
if (args[@"timeoutMs"]) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, [args[@"timeoutMs"] doubleValue] * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
|
|
136
200
|
if (process.running) { process.timedOut = YES; [self terminate:process]; }
|
|
137
201
|
});
|
|
138
202
|
} else {
|
|
139
|
-
if (!process || process.ended) { if ([method isEqual:@"terminate"] || [method isEqual:@"closeInput"]) { resolve(@"null"); return; } reject(@"E_CLOSED", @"Process has exited", nil); return; }
|
|
140
|
-
if ([method isEqual:@"terminate"])
|
|
203
|
+
if (!process || process.ended) { if ([method isEqual:@"terminate"] || [method isEqual:@"closeInput"]) { resolve(binary ? NSNull.null : @"null"); return; } reject(@"E_CLOSED", @"Process has exited", nil); return; }
|
|
204
|
+
if ([method isEqual:@"terminate"]) {
|
|
205
|
+
if (![self terminate:process]) { SparkReject(reject, [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]); return; }
|
|
206
|
+
}
|
|
141
207
|
else if ([method isEqual:@"write"] || [method isEqual:@"closeInput"]) {
|
|
208
|
+
NSData *data = [method isEqual:@"write"] ? args[@"bytes"] : nil;
|
|
209
|
+
if ([method isEqual:@"write"] && !data) { SparkInvalid(reject, @"Invalid process input"); return; }
|
|
142
210
|
dispatch_async(process.inputQueue, ^{
|
|
143
|
-
@try {
|
|
144
|
-
|
|
211
|
+
@try {
|
|
212
|
+
if ([method isEqual:@"write"]) {
|
|
213
|
+
if (process.inputClosed) { reject(@"E_CLOSED", @"Process input is closed", nil); return; }
|
|
214
|
+
[process.input writeData:data];
|
|
215
|
+
} else if (!process.inputClosed) { [process.input closeFile]; process.inputClosed = YES; }
|
|
216
|
+
resolve(binary ? NSNull.null : @"null");
|
|
217
|
+
} @catch (NSException *exception) { reject(@"E_CLOSED", @"Process input is closed", nil); }
|
|
145
218
|
}); return;
|
|
146
219
|
} else { SparkInvalid(reject, @"Unknown process operation"); return; }
|
|
147
220
|
}
|
|
148
|
-
resolve(@"null");
|
|
221
|
+
resolve(binary ? NSNull.null : @"null");
|
|
149
222
|
});
|
|
150
223
|
}
|
|
151
224
|
- (void)invalidate { dispatch_async(dispatch_get_main_queue(), ^{ self.invalidated = YES; [self stopAll]; }); }
|
|
152
|
-
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { return std::make_shared<facebook::react::NativeDesktopProcessesSpecJSI
|
|
225
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { auto invoker = params.jsInvoker; self.binaryEvent = ^(NSDictionary *event, dispatch_block_t delivered) { spark::binary::Emit(invoker, [event](spark::binary::jsi::Runtime &rt) { return spark::binary::Value(rt, event); }, [delivered] { if (delivered) delivered(); }); }; return std::make_shared<spark::binary::Module<facebook::react::NativeDesktopProcessesSpecJSI>>(params); }
|
|
153
226
|
@end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@legendapp/spark-processes",
|
|
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
|
"NativeDesktopProcesses": "RNDesktopProcesses"
|
|
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,3 +1,3 @@
|
|
|
1
1
|
import { TurboModuleRegistry, type TurboModule } from "react-native";
|
|
2
2
|
export interface Spec extends TurboModule { call(method: string, args: string): Promise<string>; }
|
|
3
|
-
export default TurboModuleRegistry.
|
|
3
|
+
export default TurboModuleRegistry.get<Spec>("NativeDesktopProcesses");
|