@legendapp/spark 0.0.1-next.2 → 0.0.1-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/camera.ts +13 -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 +93 -15
- package/patches/camera/react-native-nitro-image.patch +320 -0
- package/patches/camera/react-native-nitro-modules.patch +50 -0
- package/patches/camera/react-native-vision-camera.patch +2662 -0
- package/patches/camera/upstream.json +22 -0
- package/patches/dependencies.json +8 -0
- package/patches/react-native-enriched-markdown@1.0.2.patch +8684 -0
- package/postinstall.cjs +1 -0
- 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-swipe.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 +145 -59
- package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +61 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +10 -4
- package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +37 -8
- package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +86 -25
- package/vendor/node_modules/@legendapp/spark-cli/dist/dependency-pins.js +132 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +13 -4
- package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +13 -10
- package/vendor/node_modules/@legendapp/spark-cli/dist/errors.js +100 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +2 -2
- 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-native.cjs +5 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-node.js +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/helpers.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +52 -44
- package/vendor/node_modules/@legendapp/spark-cli/dist/json.js +16 -0
- 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 +86 -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/process.js +7 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +90 -32
- package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +86 -5
- 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/universal.cjs +2 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/updates.js +41 -4
- package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +5 -9
- 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 +12 -10
- 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 +14 -12
- 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 +14 -12
- 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 +116 -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 +18 -3
- package/vendor/node_modules/@legendapp/spark-desktop-config/config.d.cts +89 -9
- package/vendor/node_modules/@legendapp/spark-desktop-config/expo.cjs +1 -0
- 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/native-patches.cjs +106 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/native-patches.d.cts +1 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +7 -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 +3169 -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 +146 -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 +51 -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 +1419 -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/swipe-actions/RNSwipeActions.h +10 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/swipe-actions/RNSwipeActions.mm +422 -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 +17 -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 +37 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +86 -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/swipe-actions/SwipeActionsNativeComponent.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/swipe-actions/index.tsx +41 -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-native-menu",
|
|
3
|
-
"version": "0.0.1-next.
|
|
3
|
+
"version": "0.0.1-next.4",
|
|
4
4
|
"main": "src/index",
|
|
5
5
|
"react-native": "src/index",
|
|
6
6
|
"codegenConfig": {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"ios": {
|
|
14
14
|
"modulesProvider": {
|
|
15
15
|
"NativeMenu": "RNNativeMenu"
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
|
+
"componentProvider": {}
|
|
17
18
|
}
|
|
18
19
|
},
|
|
19
20
|
"peerDependencies": {
|
|
@@ -24,7 +25,8 @@
|
|
|
24
25
|
"files": [
|
|
25
26
|
"src",
|
|
26
27
|
"ios",
|
|
27
|
-
"*.podspec"
|
|
28
|
+
"*.podspec",
|
|
29
|
+
"macos"
|
|
28
30
|
],
|
|
29
31
|
"spark": {
|
|
30
32
|
"sdk": true,
|
|
@@ -37,5 +39,8 @@
|
|
|
37
39
|
],
|
|
38
40
|
"requires": []
|
|
39
41
|
},
|
|
40
|
-
"private": true
|
|
42
|
+
"private": true,
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@legendapp/spark-desktop-app": "0.0.1-next.4"
|
|
45
|
+
}
|
|
41
46
|
}
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import type { TurboModule } from "react-native";
|
|
2
2
|
import { TurboModuleRegistry } from "react-native";
|
|
3
|
-
|
|
4
3
|
export interface Spec extends TurboModule {
|
|
5
|
-
|
|
6
|
-
updateMenuItems(ownerId: string, patchesJson: string): void;
|
|
7
|
-
clearMenus(ownerId: string): void;
|
|
8
|
-
clearAllMenus(): void;
|
|
4
|
+
publish(menusJson: string): Promise<void>;
|
|
9
5
|
addListener(eventName: string): void;
|
|
10
6
|
removeListeners(count: number): void;
|
|
11
7
|
}
|
|
12
|
-
|
|
13
|
-
export default TurboModuleRegistry.getEnforcing<Spec>("NativeMenu");
|
|
8
|
+
export default TurboModuleRegistry.get<Spec>("NativeMenu");
|
|
@@ -1,128 +1,83 @@
|
|
|
1
1
|
import { NativeEventEmitter, Platform } from "react-native";
|
|
2
|
-
import { useEffect } from "react";
|
|
3
2
|
import NativeMenu from "./NativeMenu";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export type NativeMenuItem = {
|
|
25
|
-
id: string;
|
|
26
|
-
title?: string;
|
|
27
|
-
targetTitle?: string;
|
|
28
|
-
targetTitles?: string[];
|
|
29
|
-
/** Nested native system-menu paths are macOS-only. Windows accepts one item title. */
|
|
30
|
-
targetPath?: string[];
|
|
31
|
-
enabled?: boolean;
|
|
32
|
-
checked?: boolean;
|
|
33
|
-
hidden?: boolean;
|
|
34
|
-
placement?: NativeMenuItemPlacement;
|
|
35
|
-
separator?: boolean;
|
|
36
|
-
shortcut?: NativeMenuShortcut | null;
|
|
37
|
-
payload?: Record<string, unknown>;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export type NativeMenuConfig = {
|
|
41
|
-
id: string;
|
|
42
|
-
title: string;
|
|
43
|
-
systemMenu?: "app";
|
|
44
|
-
placement?: {
|
|
45
|
-
before?: string;
|
|
46
|
-
after?: string;
|
|
47
|
-
};
|
|
48
|
-
items: NativeMenuItem[];
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export type NativeMenuItemPatch = Omit<Partial<NativeMenuItem>, "separator"> & {
|
|
52
|
-
id: string;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export type NativeMenuAction = {
|
|
56
|
-
ownerId: string;
|
|
57
|
-
menuId: string;
|
|
58
|
-
itemId: string;
|
|
59
|
-
payload?: Record<string, unknown>;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type NativeMenuActionHandlers = Record<string, (action: NativeMenuAction) => void>;
|
|
63
|
-
|
|
64
|
-
export type UseNativeMenuOptions = {
|
|
65
|
-
handlers?: NativeMenuActionHandlers;
|
|
66
|
-
menus: NativeMenuConfig[];
|
|
67
|
-
onAction?: (action: NativeMenuAction) => void;
|
|
68
|
-
ownerId: string;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const windowsOwners = new Map<string, NativeMenuConfig[]>();
|
|
72
|
-
function publishWindowsMenus(next: Map<string, NativeMenuConfig[]>) {
|
|
73
|
-
// Composition can reject unsupported targets. Preserve the last good owners
|
|
74
|
-
// and native menu when a proposed contribution is invalid.
|
|
75
|
-
const composed = composeWindowsMenus(next);
|
|
76
|
-
NativeMenu.configureMenus("spark.windows.menus", JSON.stringify(composed));
|
|
77
|
-
windowsOwners.clear(); for (const [owner, menus] of next) windowsOwners.set(owner, menus);
|
|
78
|
-
}
|
|
79
|
-
export function configureMenus(ownerId: string, menus: NativeMenuConfig[]) {
|
|
80
|
-
if (Platform.OS === "windows") { const next = new Map(windowsOwners); next.set(ownerId, JSON.parse(JSON.stringify(menus))); publishWindowsMenus(next); }
|
|
81
|
-
else if (Platform.OS === "macos") NativeMenu.configureMenus(ownerId, JSON.stringify(menus));
|
|
3
|
+
import { SparkError, asyncRegistration, invokeNative, type Availability, type AsyncRegistration } from "@legendapp/spark-desktop-app/src/contracts";
|
|
4
|
+
import { menuItems, selectableMenuIds, type MenuItem as SharedMenuItem, type MenuItemTree, type MenuWireItem, type MenuAction as SharedMenuAction } from "@legendapp/spark-desktop-app/src/contracts/menu";
|
|
5
|
+
import { composeWindowsMenus } from "./windows-menus";
|
|
6
|
+
type NativeMenuLeaf = Extract<SharedMenuItem, { type: "action" | "checkbox" | "separator" | "role" }>;
|
|
7
|
+
export type MenuItem = MenuItemTree<NativeMenuLeaf>;
|
|
8
|
+
export type MenuAction = Extract<SharedMenuAction, { type: "action" }>;
|
|
9
|
+
export type { MenuIcon, MenuRole, MenuTarget, MenuPlacement, MenuLocation } from "@legendapp/spark-desktop-app/src/contracts/menu";
|
|
10
|
+
export type { AsyncRegistration } from "@legendapp/spark-desktop-app/src/contracts";
|
|
11
|
+
export type MenuRootItem = Extract<MenuItem, { type: "submenu" }>;
|
|
12
|
+
export interface MenuUpdate { items: readonly MenuRootItem[] }
|
|
13
|
+
export interface MenuOptions extends MenuUpdate { id: string; onAction?: (action: MenuAction) => void }
|
|
14
|
+
export interface Menu extends AsyncRegistration { readonly id: string; update(options: MenuUpdate): Promise<void> }
|
|
15
|
+
interface Owner { id: string; token: string; items: MenuWireItem[] }
|
|
16
|
+
let owners = new Map<string, Owner>();
|
|
17
|
+
let sequence = 0, queue: Promise<unknown> = Promise.resolve();
|
|
18
|
+
function serial<T>(operation: () => Promise<T>): Promise<T> { const result = queue.then(operation); queue = result.catch(() => {}); return result; }
|
|
19
|
+
export function getMenuAvailability(): Availability {
|
|
20
|
+
if (Platform.OS !== "macos" && Platform.OS !== "windows") return { available: false, reason: "unsupported-platform" };
|
|
21
|
+
return NativeMenu ? { available: true } : { available: false, reason: "missing-module" };
|
|
82
22
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
} else if (Platform.OS === "macos") NativeMenu.updateMenuItems(ownerId, JSON.stringify(patches));
|
|
23
|
+
function available() {
|
|
24
|
+
const availability = getMenuAvailability();
|
|
25
|
+
if (!availability.available) throw new SparkError(availability.reason === "unsupported-platform" ? "E_UNSUPPORTED_PLATFORM" : "E_MODULE_UNAVAILABLE", "Application menus are unavailable");
|
|
87
26
|
}
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
27
|
+
function snapshot(value: MenuUpdate, token: string, creating = false): MenuWireItem[] {
|
|
28
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new SparkError("E_INVALID_ARGUMENT", "Expected menu options");
|
|
29
|
+
for (const key of Object.keys(value)) if (!["items", ...(creating ? ["id", "onAction"] : [])].includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unsupported menu option: ${key}`);
|
|
30
|
+
const items = menuItems(value.items, { types: Platform.OS === "macos" ? ["action", "checkbox", "separator", "submenu", "role"] : ["action", "checkbox", "separator", "submenu"], shortcuts: true, targeting: true, icons: Platform.OS === "macos" ? ["symbol", "image"] : [] }, Platform.OS === "windows" ? "windows" : "macos");
|
|
31
|
+
if (items.some(item => !item.items)) throw new SparkError("E_INVALID_ARGUMENT", "Application menu roots must be submenus");
|
|
32
|
+
function annotate(items: MenuWireItem[]) { for (const item of items) { item._sparkOwner = token; if (item.items) annotate(item.items); } }
|
|
33
|
+
annotate(items); return items;
|
|
91
34
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
35
|
+
async function publish(next: Map<string, Owner>): Promise<void> {
|
|
36
|
+
const menus = Platform.OS === "windows"
|
|
37
|
+
? composeWindowsMenus(new Map([...next].map(([id, owner]) => [id, owner.items])))
|
|
38
|
+
: [...next.values()].flatMap(owner => owner.items);
|
|
39
|
+
const result = await invokeNative(() => NativeMenu!.publish(JSON.stringify(menus)));
|
|
40
|
+
if (result !== undefined && result !== null) throw new SparkError("E_INVALID_DATA", "Native menu publication returned an unexpected result");
|
|
41
|
+
owners = next;
|
|
95
42
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
43
|
+
export async function createMenu(options: MenuOptions): Promise<Menu> {
|
|
44
|
+
available();
|
|
45
|
+
const token = `menu-${Date.now()}-${++sequence}`;
|
|
46
|
+
const items = snapshot(options, token, true);
|
|
47
|
+
if (typeof options.id !== "string" || !/^[a-zA-Z0-9_-]{1,100}$/.test(options.id)) throw new SparkError("E_INVALID_ARGUMENT", "Expected a menu owner id");
|
|
48
|
+
if (options.onAction !== undefined && typeof options.onAction !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected onAction callback");
|
|
49
|
+
const id = options.id, onAction = options.onAction;
|
|
50
|
+
let stopped = false, ready = false, ids = selectableMenuIds(items);
|
|
51
|
+
const subscription = new NativeEventEmitter(NativeMenu as never).addListener("NativeMenuAction", event => {
|
|
52
|
+
if (ready && !stopped && event && event.ownerId === token && typeof event.itemId === "string" && ids.has(event.itemId)) {
|
|
53
|
+
// All owners share this emitter; one owner's throw must not swallow the other owners' actions.
|
|
54
|
+
try { onAction?.({ type: "action", itemId: event.itemId }); }
|
|
55
|
+
catch (cause) { console.error(new SparkError("E_NATIVE", "Menu action handler failed", { cause })); }
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
try { await serial(async () => {
|
|
59
|
+
if (owners.has(id)) throw new SparkError("E_ALREADY_EXISTS", `Menu owner ${id} already exists`);
|
|
60
|
+
const next = new Map(owners); next.set(id, { id, token, items }); await publish(next); ready = true;
|
|
61
|
+
}); } catch (error) { subscription.remove(); throw error; }
|
|
62
|
+
const registration = asyncRegistration(() => { stopped = true; subscription.remove(); }, () => serial(async () => {
|
|
63
|
+
if (owners.get(id)?.token !== token) return;
|
|
64
|
+
const next = new Map(owners); next.delete(id); await publish(next);
|
|
65
|
+
}));
|
|
66
|
+
return {
|
|
67
|
+
id,
|
|
68
|
+
async update(options) {
|
|
69
|
+
if (stopped) throw new SparkError("E_CLOSED", "Menu was removed");
|
|
70
|
+
const items = snapshot(options, token);
|
|
71
|
+
await serial(async () => {
|
|
72
|
+
// Callers commonly rebuild identical item literals each render; republishing the whole
|
|
73
|
+
// bar would rebuild native menus for no visible change. Compare the wire trees first.
|
|
74
|
+
const existing = owners.get(id);
|
|
75
|
+
if (existing?.token === token && JSON.stringify(existing.items) === JSON.stringify(items)) return;
|
|
76
|
+
const next = new Map(owners); next.delete(id); next.set(id, { id, token, items });
|
|
77
|
+
await publish(next); ids = selectableMenuIds(items);
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
remove: registration.remove,
|
|
81
|
+
};
|
|
126
82
|
}
|
|
127
|
-
|
|
128
|
-
export { default as NativeMenu } from "./NativeMenu";
|
|
83
|
+
export { useMenu, type MenuState, type UseMenuOptions } from "./hooks";
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { createMenu, type Menu, type MenuOptions, type MenuAction } from "./api";
|
|
3
|
+
export interface UseMenuOptions extends MenuOptions { onError?: (error: unknown) => void; onCleanupError?: (error: unknown, menu: Menu) => void }
|
|
4
|
+
export type MenuState = { status: "loading" } | { status: "ready"; menu: Menu } | { status: "error"; error: unknown };
|
|
5
|
+
interface Lifetime { menu?: Menu; removing?: Promise<void>; blocker?: Lifetime; items: MenuOptions["items"]; disposed: boolean; completion: Promise<void> }
|
|
6
|
+
const lifetimes = new Map<string, Lifetime>();
|
|
7
|
+
/** Callback changes do not recreate native menus. Structural updates use the same handle. */
|
|
8
|
+
export function useMenu(options: UseMenuOptions): MenuState {
|
|
9
|
+
const [state, setState] = useState<MenuState>({ status: "loading" });
|
|
10
|
+
const callbacks = useRef(options);
|
|
11
|
+
useLayoutEffect(() => { callbacks.current = options; });
|
|
12
|
+
const current = useRef<Lifetime | undefined>(undefined);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const previous = lifetimes.get(options.id);
|
|
15
|
+
let end!: () => void;
|
|
16
|
+
const ended = new Promise<void>(resolve => { end = resolve; });
|
|
17
|
+
const owner: Lifetime = { items: options.items, disposed: false, completion: Promise.resolve(), blocker: previous?.disposed ? previous.blocker ?? previous : undefined };
|
|
18
|
+
current.current = owner; setState({ status: "loading" });
|
|
19
|
+
const report = (error: unknown) => {
|
|
20
|
+
if (!owner.disposed) setState({ status: "error", error });
|
|
21
|
+
try {
|
|
22
|
+
if (callbacks.current.onError) callbacks.current.onError(error);
|
|
23
|
+
else console.error("Menu lifecycle failed", error);
|
|
24
|
+
} catch (callbackError) { console.error(callbackError); }
|
|
25
|
+
};
|
|
26
|
+
const remove = (target: Lifetime) => {
|
|
27
|
+
const menu = target.menu;
|
|
28
|
+
if (!menu) return Promise.resolve();
|
|
29
|
+
if (!target.removing) target.removing = menu.remove().then(() => {
|
|
30
|
+
if (target.menu === menu) target.menu = undefined;
|
|
31
|
+
if (lifetimes.get(options.id) === target) lifetimes.delete(options.id);
|
|
32
|
+
}).catch(error => {
|
|
33
|
+
if (!owner.disposed) setState({ status: "error", error });
|
|
34
|
+
try {
|
|
35
|
+
if (callbacks.current.onCleanupError) callbacks.current.onCleanupError(error, menu);
|
|
36
|
+
else {
|
|
37
|
+
console.error("Menu cleanup failed; menu handle remains retryable", error, menu);
|
|
38
|
+
report(error);
|
|
39
|
+
}
|
|
40
|
+
} catch (callbackError) { console.error(callbackError); }
|
|
41
|
+
throw error;
|
|
42
|
+
}).finally(() => { target.removing = undefined; });
|
|
43
|
+
return target.removing;
|
|
44
|
+
};
|
|
45
|
+
if (!previous || previous.disposed) lifetimes.set(options.id, owner);
|
|
46
|
+
owner.completion = Promise.resolve().then(async () => {
|
|
47
|
+
// A remount waits for allocation/disposal, then retries a retained failed removal.
|
|
48
|
+
if (previous?.disposed) {
|
|
49
|
+
await previous.completion.catch(() => {});
|
|
50
|
+
const blocked = owner.blocker;
|
|
51
|
+
if (blocked) {
|
|
52
|
+
await blocked.completion.catch(() => {});
|
|
53
|
+
try { if (blocked.menu) await remove(blocked); }
|
|
54
|
+
catch { return; }
|
|
55
|
+
owner.blocker = undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (owner.disposed) {
|
|
59
|
+
if (lifetimes.get(options.id) === owner) lifetimes.delete(options.id);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const initial = owner.items;
|
|
63
|
+
let menu: Menu;
|
|
64
|
+
try {
|
|
65
|
+
menu = await createMenu({ id: options.id, items: initial, onAction: (event: MenuAction) => { if (!owner.disposed) callbacks.current.onAction?.(event); } });
|
|
66
|
+
owner.menu = menu;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
report(error);
|
|
69
|
+
if (lifetimes.get(options.id) === owner) lifetimes.delete(options.id);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
if (!owner.disposed && owner.items !== initial) await menu.update({ items: owner.items });
|
|
74
|
+
if (!owner.disposed) setState({ status: "ready", menu });
|
|
75
|
+
} catch (error) { report(error); }
|
|
76
|
+
await ended;
|
|
77
|
+
if (owner.disposed) await remove(owner).catch(() => {});
|
|
78
|
+
}).catch(report);
|
|
79
|
+
return () => { owner.disposed = true; end(); };
|
|
80
|
+
}, [options.id]);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const owner = current.current;
|
|
83
|
+
if (!owner || owner.disposed || owner.items === options.items) return;
|
|
84
|
+
owner.items = options.items;
|
|
85
|
+
if (owner.menu) void owner.menu.update({ items: options.items }).then(() => {
|
|
86
|
+
// Reusing the previous state keeps an identical-content update from cascading renders.
|
|
87
|
+
if (!owner.disposed) setState(state => state.status === "ready" && state.menu === owner.menu ? state : { status: "ready", menu: owner.menu! });
|
|
88
|
+
}).catch(error => {
|
|
89
|
+
if (!owner.disposed) setState({ status: "error", error });
|
|
90
|
+
try {
|
|
91
|
+
if (callbacks.current.onError) callbacks.current.onError(error);
|
|
92
|
+
else console.error("Menu update failed", error);
|
|
93
|
+
} catch (callbackError) { console.error(callbackError); }
|
|
94
|
+
});
|
|
95
|
+
}, [options.id, options.items]);
|
|
96
|
+
return state;
|
|
97
|
+
}
|
|
@@ -1,43 +1,49 @@
|
|
|
1
|
-
import
|
|
2
|
-
type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { SparkError } from "@legendapp/spark-desktop-app/src/contracts";
|
|
2
|
+
import type { MenuTarget, MenuWireItem } from "@legendapp/spark-desktop-app/src/contracts/menu";
|
|
3
|
+
function matches(item: MenuWireItem, target: MenuTarget): boolean {
|
|
4
|
+
return "id" in target ? (item._sparkIdentity ?? item.id) === target.id : "role" in target ? item.role === target.role : !!item.target && "menu" in item.target && item.target.menu === target.menu;
|
|
5
|
+
}
|
|
6
|
+
function insertion(items: MenuWireItem[], placement?: MenuWireItem["placement"]): number {
|
|
7
|
+
if (!placement) return items.length;
|
|
8
|
+
const target = placement.before ?? placement.after!;
|
|
9
|
+
const index = items.findIndex(item => matches(item, target));
|
|
10
|
+
if (index < 0) throw new SparkError("E_NOT_FOUND", "Menu placement target was not found");
|
|
11
|
+
return index + (placement.after ? 1 : 0);
|
|
12
|
+
}
|
|
13
|
+
function locate(items: MenuWireItem[], target: MenuTarget, recursive: boolean): { items: MenuWireItem[]; item: MenuWireItem } | undefined {
|
|
14
|
+
for (const item of items) {
|
|
15
|
+
if (matches(item, target)) return { items, item };
|
|
16
|
+
if (recursive && item.items) { const found = locate(item.items, target, true); if (found) return found; }
|
|
9
17
|
}
|
|
10
|
-
return items.length;
|
|
11
18
|
}
|
|
12
|
-
/**
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
menu
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const index =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
/** Compose fresh snapshots by stable IDs/semantic targets; failed proposals never mutate live owners. */
|
|
20
|
+
export function composeWindowsMenus(owners: ReadonlyMap<string, readonly MenuWireItem[]>): MenuWireItem[] {
|
|
21
|
+
const result: MenuWireItem[] = [];
|
|
22
|
+
function merge(into: MenuWireItem[], inputs: readonly MenuWireItem[], root: boolean) {
|
|
23
|
+
for (const input of inputs) {
|
|
24
|
+
const found = input.target || input.id ? locate(into, input.target ?? { id: input.id! }, !!input.target && !root) : undefined;
|
|
25
|
+
const target = found?.item, siblings = found?.items ?? into;
|
|
26
|
+
if (input.target && !target && !(root && "menu" in input.target)) throw new SparkError("E_NOT_FOUND", `Menu target for ${input.id} was not found`);
|
|
27
|
+
if (target && input.items) {
|
|
28
|
+
if (!target.items) throw new SparkError("E_INVALID_ARGUMENT", "Submenu target is not a submenu");
|
|
29
|
+
const { items: children, id, target: identity, placement, ...presentation } = input;
|
|
30
|
+
Object.assign(target, presentation);
|
|
31
|
+
merge(target.items, children, false);
|
|
32
|
+
if (placement) {
|
|
33
|
+
const old = siblings.indexOf(target), position = insertion(siblings, placement);
|
|
34
|
+
siblings.splice(old, 1); siblings.splice(position - (old < position ? 1 : 0), 0, target);
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
// Overlays keep the owner identity and the role AppKit would retain, so later placements still resolve semantic anchors.
|
|
38
|
+
const item = { ...input, ...(target ? { _sparkIdentity: target._sparkIdentity ?? target.id } : {}), ...(target?.role && !input.role ? { role: target.role } : {}), ...(input.items ? { items: [] as MenuWireItem[] } : {}) };
|
|
39
|
+
if (input.items) merge(item.items!, input.items, false);
|
|
40
|
+
const index = target ? siblings.indexOf(target) : -1;
|
|
41
|
+
const position = input.placement ? insertion(siblings, input.placement) : index < 0 ? siblings.length : index;
|
|
42
|
+
if (index >= 0) siblings.splice(index, 1);
|
|
43
|
+
siblings.splice(position - (index >= 0 && index < position ? 1 : 0), 0, item);
|
|
44
|
+
}
|
|
37
45
|
}
|
|
38
46
|
}
|
|
47
|
+
for (const items of owners.values()) merge(result, items, true);
|
|
39
48
|
return result;
|
|
40
49
|
}
|
|
41
|
-
export function patchWindowsMenus(menus: NativeMenuConfig[], patches: NativeMenuItemPatch[]): NativeMenuConfig[] {
|
|
42
|
-
return menus.map(menu => ({ ...menu, items: menu.items.map(item => patches.reduce((item, patch) => patch.id === item.id ? { ...item, ...patch } : item, item)) }));
|
|
43
|
-
}
|
|
@@ -16,25 +16,41 @@ static NSString *Identifier(NSString *value) { return [NSString stringWithFormat
|
|
|
16
16
|
static BOOL Owns(UNNotificationRequest *request) { return [request.content.userInfo[NamespaceKey] isEqual:SparkNamespace()]; }
|
|
17
17
|
@interface SparkNotificationCenter : NSObject <UNUserNotificationCenterDelegate>
|
|
18
18
|
@property NSMutableArray *responses;
|
|
19
|
+
@property NSMutableSet<UNNotificationCategory *> *categories;
|
|
20
|
+
@property BOOL categoriesReady;
|
|
21
|
+
@property NSMutableArray<void (^)(void)> *pendingCategories;
|
|
19
22
|
@property (weak) id<UNUserNotificationCenterDelegate> previous;
|
|
20
23
|
+ (instancetype)shared;
|
|
24
|
+
- (void)whenCategoriesReady:(void (^)(void))operation;
|
|
21
25
|
@end
|
|
22
26
|
@implementation SparkNotificationCenter
|
|
23
27
|
+ (instancetype)shared {
|
|
24
28
|
static SparkNotificationCenter *instance;
|
|
25
29
|
static dispatch_once_t once;
|
|
26
30
|
dispatch_once(&once, ^{
|
|
27
|
-
instance = [SparkNotificationCenter new]; instance.responses = [NSMutableArray new];
|
|
31
|
+
instance = [SparkNotificationCenter new]; instance.responses = [NSMutableArray new]; instance.categories = [NSMutableSet new];
|
|
32
|
+
instance.pendingCategories = [NSMutableArray new];
|
|
28
33
|
UNUserNotificationCenter *center = UNUserNotificationCenter.currentNotificationCenter;
|
|
29
34
|
instance.previous = center.delegate; center.delegate = instance;
|
|
35
|
+
// Merge the host categories before publishing any Spark notification.
|
|
30
36
|
[center getNotificationCategoriesWithCompletionHandler:^(NSSet<UNNotificationCategory *> *categories) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
38
|
+
[instance.categories addObjectsFromArray:categories.allObjects];
|
|
39
|
+
[instance.categories addObject:[UNNotificationCategory categoryWithIdentifier:@"spark.desktop.default" actions:@[] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction]];
|
|
40
|
+
[center setNotificationCategories:[instance.categories copy]];
|
|
41
|
+
instance.categoriesReady = YES;
|
|
42
|
+
NSArray<void (^)(void)> *pending = [instance.pendingCategories copy];
|
|
43
|
+
[instance.pendingCategories removeAllObjects];
|
|
44
|
+
for (void (^operation)(void) in pending) operation();
|
|
45
|
+
});
|
|
34
46
|
}];
|
|
35
47
|
});
|
|
36
48
|
return instance;
|
|
37
49
|
}
|
|
50
|
+
- (void)whenCategoriesReady:(void (^)(void))operation {
|
|
51
|
+
if (self.categoriesReady) operation();
|
|
52
|
+
else [self.pendingCategories addObject:[operation copy]];
|
|
53
|
+
}
|
|
38
54
|
+ (void)load {
|
|
39
55
|
// Install before launch finishes, including when a notification launches the app.
|
|
40
56
|
[NSNotificationCenter.defaultCenter addObserverForName:NSApplicationWillFinishLaunchingNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification *note) { [self shared]; }];
|
|
@@ -51,9 +67,12 @@ static BOOL Owns(UNNotificationRequest *request) { return [request.content.userI
|
|
|
51
67
|
return;
|
|
52
68
|
}
|
|
53
69
|
NSDictionary *info = response.notification.request.content.userInfo;
|
|
70
|
+
NSString *action = @"open";
|
|
71
|
+
if ([response.actionIdentifier isEqual:UNNotificationDismissActionIdentifier]) action = @"dismiss";
|
|
72
|
+
else if ([response.actionIdentifier hasPrefix:@"spark.action."]) action = [response.actionIdentifier substringFromIndex:@"spark.action.".length];
|
|
54
73
|
NSDictionary *event = @{ @"type": @"notificationResponse", @"id": NSUUID.UUID.UUIDString,
|
|
55
74
|
@"notificationId": info[@"sparkId"] ?: @"", @"data": info[@"sparkData"] ?: @{},
|
|
56
|
-
@"action":
|
|
75
|
+
@"action": action };
|
|
57
76
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
58
77
|
[self.responses addObject:event]; if (self.responses.count > 100) [self.responses removeObjectAtIndex:0];
|
|
59
78
|
SparkEmit(event); completion();
|
|
@@ -74,34 +93,60 @@ RCT_EXPORT_MODULE(NativeDesktopNotifications)
|
|
|
74
93
|
if ([method isEqual:@"permission"]) read();
|
|
75
94
|
else [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError *error) { if (error) SparkReject(reject, error); else read(); }];
|
|
76
95
|
} else if ([method isEqual:@"show"]) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
96
|
+
[delegate whenCategoriesReady:^{
|
|
97
|
+
if (![args[@"id"] isKindOfClass:NSString.class] || ![args[@"title"] isKindOfClass:NSString.class]) { SparkInvalid(reject, @"Notification needs an id and title"); return; }
|
|
98
|
+
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
|
|
99
|
+
NSArray *actions = [args[@"actions"] isKindOfClass:NSArray.class] ? args[@"actions"] : nil;
|
|
100
|
+
content.title = args[@"title"]; content.body = args[@"body"] ?: @""; content.subtitle = args[@"subtitle"] ?: @"";
|
|
101
|
+
// JS normalizes sound to "none", "default", or a tone name. macOS user
|
|
102
|
+
// notification sounds must ship in the app bundle or Library/Sounds, so
|
|
103
|
+
// tones present the platform default here; Windows maps tones to system sounds.
|
|
104
|
+
NSString *sound = [args[@"sound"] isKindOfClass:NSString.class] ? args[@"sound"] : nil;
|
|
105
|
+
if (sound.length > 0 && ![sound isEqual:@"none"]) content.sound = UNNotificationSound.defaultSound;
|
|
106
|
+
content.categoryIdentifier = @"spark.desktop.default";
|
|
107
|
+
if (actions.count > 0) {
|
|
108
|
+
// Categories are app-global; namespace identifiers so Spark registrations
|
|
109
|
+
// never collide with a host app's own UNNotificationCategory set.
|
|
110
|
+
NSMutableArray<UNNotificationAction *> *categoryActions = [NSMutableArray new];
|
|
111
|
+
NSMutableString *signature = [NSMutableString stringWithString:@"spark.actions."];
|
|
112
|
+
for (NSDictionary *action in actions) {
|
|
113
|
+
if (![action[@"id"] isKindOfClass:NSString.class] || ![action[@"label"] isKindOfClass:NSString.class]) { SparkInvalid(reject, @"Notification actions need an id and label"); return; }
|
|
114
|
+
[categoryActions addObject:[UNNotificationAction actionWithIdentifier:[@"spark.action." stringByAppendingString:action[@"id"]] title:action[@"label"] options:UNNotificationActionOptionNone]];
|
|
115
|
+
[signature appendFormat:@"%@:%@|", action[@"id"], action[@"label"]];
|
|
116
|
+
}
|
|
117
|
+
content.categoryIdentifier = signature;
|
|
118
|
+
UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:signature actions:categoryActions intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
|
|
119
|
+
if (![delegate.categories containsObject:category]) {
|
|
120
|
+
[delegate.categories addObject:category];
|
|
121
|
+
[center setNotificationCategories:[delegate.categories copy]];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
content.userInfo = @{ NamespaceKey: SparkNamespace(), @"sparkId": args[@"id"], @"sparkData": args[@"data"] ?: @{} };
|
|
125
|
+
UNNotificationTrigger *trigger = args[@"delay"] ? [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:[args[@"delay"] doubleValue] repeats:NO] : nil;
|
|
126
|
+
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:Identifier(args[@"id"]) content:content trigger:trigger];
|
|
127
|
+
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *settings) {
|
|
128
|
+
if (settings.authorizationStatus != UNAuthorizationStatusAuthorized && settings.authorizationStatus != UNAuthorizationStatusProvisional) { reject(@"E_PERMISSION_DENIED", @"Request notification permission from a user action before showing notifications", nil); return; }
|
|
129
|
+
[center addNotificationRequest:request withCompletionHandler:^(NSError *error) { if (error) SparkReject(reject, error); else resolve(@"null"); }];
|
|
130
|
+
}];
|
|
88
131
|
}];
|
|
89
|
-
} else if ([method isEqual:@"cancel"]) {
|
|
90
|
-
|
|
91
|
-
[
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
132
|
+
} else if ([method isEqual:@"cancel"] || [method isEqual:@"dismiss"]) {
|
|
133
|
+
NSArray *ids = @[Identifier(args[@"id"])];
|
|
134
|
+
if ([method isEqual:@"cancel"]) [center removePendingNotificationRequestsWithIdentifiers:ids];
|
|
135
|
+
else [center removeDeliveredNotificationsWithIdentifiers:ids];
|
|
136
|
+
resolve(@"null");
|
|
137
|
+
} else if ([method isEqual:@"pending"] || [method isEqual:@"cancelAll"]) {
|
|
138
|
+
[center getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> *requests) {
|
|
139
|
+
NSMutableArray *ids = [NSMutableArray new], *nativeIds = [NSMutableArray new];
|
|
96
140
|
for (UNNotificationRequest *request in requests) if (Owns(request)) { [ids addObject:request.content.userInfo[@"sparkId"]]; [nativeIds addObject:request.identifier]; }
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
NSMutableArray *delivered = [NSMutableArray new]; for (UNNotification *notification in notifications) if (Owns(notification.request)) [delivered addObject:notification.request.identifier];
|
|
100
|
-
[center removeDeliveredNotificationsWithIdentifiers:delivered]; resolve(@"null");
|
|
101
|
-
}];
|
|
141
|
+
if ([method isEqual:@"cancelAll"]) { [center removePendingNotificationRequestsWithIdentifiers:nativeIds]; resolve(@"null"); }
|
|
142
|
+
else resolve(SparkJSON(ids));
|
|
102
143
|
}];
|
|
103
|
-
|
|
104
|
-
|
|
144
|
+
} else if ([method isEqual:@"delivered"] || [method isEqual:@"dismissAll"]) {
|
|
145
|
+
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> *notifications) {
|
|
146
|
+
NSMutableArray *ids = [NSMutableArray new], *nativeIds = [NSMutableArray new];
|
|
147
|
+
for (UNNotification *notification in notifications) if (Owns(notification.request)) { [ids addObject:notification.request.content.userInfo[@"sparkId"]]; [nativeIds addObject:notification.request.identifier]; }
|
|
148
|
+
if ([method isEqual:@"dismissAll"]) { [center removeDeliveredNotificationsWithIdentifiers:nativeIds]; resolve(@"null"); }
|
|
149
|
+
else resolve(SparkJSON(ids));
|
|
105
150
|
}];
|
|
106
151
|
} else if ([method isEqual:@"responses"]) resolve(SparkJSON([delegate.responses copy]));
|
|
107
152
|
else SparkInvalid(reject, @"Unknown notification operation");
|