@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,61 +1,150 @@
|
|
|
1
1
|
import { NativeEventEmitter, Platform } from "react-native";
|
|
2
|
+
import { callBinary, nativeBytes } from "@legendapp/spark-desktop-app/src/contracts/native-buffer";
|
|
3
|
+
import { SparkError, invokeNative, asyncRegistration, type AsyncRegistration, type Availability } from "@legendapp/spark-desktop-app/src/contracts";
|
|
4
|
+
export type { AsyncRegistration } from "@legendapp/spark-desktop-app/src/contracts";
|
|
5
|
+
import { nativePath } from "@legendapp/spark-desktop-app/src/contracts/path";
|
|
2
6
|
import Native from "./NativeDesktopFileSystem";
|
|
3
|
-
import {
|
|
4
|
-
export
|
|
7
|
+
import { createFileHandle, iterateFile, writeFileChunks, type FileMode, type ReadChunksOptions } from "./handles";
|
|
8
|
+
export { FileCleanupError } from "./handles";
|
|
9
|
+
export type { FileHandle, FileMode, ReadChunksOptions } from "./handles";
|
|
10
|
+
export interface FileInfo { type: "file" | "directory" | "symlink"; size: number; modifiedAt: number }
|
|
11
|
+
export interface DirectoryEntry { name: string; path: string }
|
|
12
|
+
export interface CreateDirectoryOptions { recursive?: boolean }
|
|
13
|
+
export interface RemoveOptions { recursive?: boolean }
|
|
14
|
+
export interface CopyMoveOptions { overwrite?: boolean }
|
|
15
|
+
export interface WatchOptions { recursive?: boolean }
|
|
16
|
+
export interface OpenFileOptions { mode?: FileMode }
|
|
17
|
+
export interface WriteChunksOptions { mode?: "write" | "createNew"; signal?: AbortSignal }
|
|
18
|
+
export function getFileSystemAvailability(): Availability {
|
|
19
|
+
if (Platform.OS !== "macos" && Platform.OS !== "windows") return { available: false, reason: "unsupported-platform" };
|
|
20
|
+
return Native ? { available: true } : { available: false, reason: "missing-module" };
|
|
21
|
+
}
|
|
22
|
+
function native() {
|
|
23
|
+
const availability = getFileSystemAvailability();
|
|
24
|
+
if (!availability.available) throw new SparkError(availability.reason === "unsupported-platform" ? "E_UNSUPPORTED_PLATFORM" : "E_MODULE_UNAVAILABLE", "File IO requires a desktop host with NativeDesktopFileSystem installed");
|
|
25
|
+
return Native!;
|
|
26
|
+
}
|
|
27
|
+
const absolute = (path: string) => nativePath(path, Platform.OS);
|
|
28
|
+
const isString = (value: unknown): value is string => typeof value === "string";
|
|
29
|
+
function validResponse(method: string, value: unknown): boolean {
|
|
30
|
+
if (["directory", "openFile"].includes(method)) return isString(value) && value.length > 0;
|
|
31
|
+
if (["readText"].includes(method)) return isString(value);
|
|
32
|
+
if (method === "readBytes" || method === "readChunk") return value instanceof ArrayBuffer;
|
|
33
|
+
if (method === "list") return Array.isArray(value) && value.every(name => isString(name) && name.length > 0 && ![".", ".."].includes(name) && !name.includes("/") && !name.includes("\0") && (Platform.OS !== "windows" || !name.includes("\\")));
|
|
34
|
+
if (method === "stat") {
|
|
35
|
+
if (!value || typeof value !== "object") return false;
|
|
36
|
+
const info = value as FileInfo;
|
|
37
|
+
return ["file", "directory", "symlink"].includes(info.type) && Number.isSafeInteger(info.size) && info.size >= 0 && typeof info.modifiedAt === "number" && Number.isFinite(info.modifiedAt);
|
|
38
|
+
}
|
|
39
|
+
if (method === "writeChunk") return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
40
|
+
if (method === "remove" || method === "writeTextIfUnchanged") return typeof value === "boolean";
|
|
41
|
+
return value === null;
|
|
42
|
+
}
|
|
5
43
|
async function call<T = void>(method: string, args: object): Promise<T> {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
44
|
+
const { bytes, ...metadata } = args as { bytes?: Uint8Array };
|
|
45
|
+
const value = await invokeNative(() => callBinary(native(), "__sparkFileSystemBinary", method, metadata, bytes));
|
|
46
|
+
if (!validResponse(method, value)) throw new SparkError("E_INVALID_DATA", "Invalid native file response");
|
|
47
|
+
return value as T;
|
|
48
|
+
}
|
|
49
|
+
/** Rejects unknown or extra option keys so a misspelled option can never silently no-op. */
|
|
50
|
+
function checkedOptions<T extends object>(options: T, allowed: readonly string[], label: string): T {
|
|
51
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) throw new SparkError("E_INVALID_ARGUMENT", `Expected ${label} options`);
|
|
52
|
+
for (const key of Object.keys(options)) if (!allowed.includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unknown ${label} option: ${key}`);
|
|
53
|
+
return options;
|
|
54
|
+
}
|
|
55
|
+
function recursiveOption(options: { recursive?: boolean }, fallback: boolean): boolean {
|
|
56
|
+
const value = checkedOptions(options, ["recursive"], "recursive");
|
|
57
|
+
if (value.recursive !== undefined && typeof value.recursive !== "boolean") throw new SparkError("E_INVALID_ARGUMENT", "recursive must be a boolean");
|
|
58
|
+
return value.recursive ?? fallback;
|
|
59
|
+
}
|
|
60
|
+
function overwriteOption(options: CopyMoveOptions): boolean {
|
|
61
|
+
const value = checkedOptions(options, ["overwrite"], "copy/move");
|
|
62
|
+
if (value.overwrite !== undefined && typeof value.overwrite !== "boolean") throw new SparkError("E_INVALID_ARGUMENT", "overwrite must be a boolean");
|
|
63
|
+
return value.overwrite ?? false;
|
|
64
|
+
}
|
|
65
|
+
export async function getDirectory(kind: "data" | "cache" | "temp"): Promise<string> {
|
|
66
|
+
if (!["data", "cache", "temp"].includes(kind)) throw new SparkError("E_INVALID_ARGUMENT", "Invalid directory kind");
|
|
67
|
+
const path = await call<string>("directory", { kind });
|
|
68
|
+
try { return absolute(path); } catch (cause) { throw new SparkError("E_INVALID_DATA", "Invalid native directory path", { cause }); }
|
|
69
|
+
}
|
|
70
|
+
/** UTF-8. Invalid text rejects instead of inserting replacement characters. */
|
|
71
|
+
export async function readText(path: string): Promise<string> { return call("readText", { path: absolute(path) }); }
|
|
72
|
+
export async function writeText(path: string, text: string): Promise<void> {
|
|
73
|
+
if (typeof text !== "string") throw new SparkError("E_INVALID_ARGUMENT", "Expected a text string");
|
|
74
|
+
await call("writeText", { path: absolute(path), text });
|
|
75
|
+
}
|
|
76
|
+
export async function readBytes(path: string): Promise<Uint8Array> { return nativeBytes(await call<ArrayBuffer>("readBytes", { path: absolute(path) })); }
|
|
77
|
+
export async function writeBytes(path: string, bytes: Uint8Array): Promise<void> {
|
|
78
|
+
if (!(bytes instanceof Uint8Array)) throw new SparkError("E_INVALID_ARGUMENT", "Expected Uint8Array");
|
|
79
|
+
await call("writeBytes", { path: absolute(path), bytes });
|
|
80
|
+
}
|
|
81
|
+
/** Describes the link itself, without following it. modifiedAt is Unix milliseconds. */
|
|
82
|
+
export async function stat(path: string): Promise<FileInfo> { return call("stat", { path: absolute(path) }); }
|
|
83
|
+
export async function exists(path: string): Promise<boolean> {
|
|
19
84
|
try { await stat(path); return true; } catch (error) {
|
|
20
|
-
if (
|
|
85
|
+
if (error instanceof SparkError && error.code === "E_NOT_FOUND") return false;
|
|
21
86
|
throw error;
|
|
22
87
|
}
|
|
23
88
|
}
|
|
24
|
-
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
89
|
+
/** Entries are sorted by name. Metadata is obtained explicitly with stat(entry.path). */
|
|
90
|
+
export async function list(path: string): Promise<DirectoryEntry[]> {
|
|
91
|
+
const directory = absolute(path);
|
|
92
|
+
const names = await call<string[]>("list", { path: directory });
|
|
93
|
+
const separator = Platform.OS === "windows" ? "\\" : "/";
|
|
94
|
+
return names.sort().map(name => ({ name, path: directory.replace(/[\\/]+$/, "") + separator + name }));
|
|
95
|
+
}
|
|
96
|
+
export async function mkdir(path: string, options: CreateDirectoryOptions = {}): Promise<void> { await call("mkdir", { path: absolute(path), recursive: recursiveOption(options, true) }); }
|
|
97
|
+
/** Absence is success; deleting a nonempty directory requires recursive: true. */
|
|
98
|
+
export async function remove(path: string, options: RemoveOptions = {}): Promise<void> { await call("remove", { path: absolute(path), recursive: recursiveOption(options, false) }); }
|
|
99
|
+
/** Copies directories recursively and preserves symlinks. An existing destination rejects unless overwrite: true. Self-transfers and overlapping source/destination paths reject. Overwrite stages beside the destination and restores the previous destination if publication fails. If backup cleanup fails after commit, the native error reports the published destination and recoverable backup path. */
|
|
100
|
+
export async function copy(source: string, destination: string, options: CopyMoveOptions = {}): Promise<void> {
|
|
101
|
+
await call("copy", { path: absolute(source), to: absolute(destination), overwrite: overwriteOption(options) });
|
|
102
|
+
}
|
|
103
|
+
/** An existing destination rejects unless overwrite: true. Self-transfers and overlapping source/destination paths reject. Cross-volume moves copy beside the destination before deleting the source. If source cleanup fails, the destination has been published, the source may be partially removed, and the previous destination is retained at a recovery path reported by the native error. Backup cleanup failures also report the committed destination and retained backup. */
|
|
104
|
+
export async function move(source: string, destination: string, options: CopyMoveOptions = {}): Promise<void> {
|
|
105
|
+
await call("move", { path: absolute(source), to: absolute(destination), overwrite: overwriteOption(options) });
|
|
106
|
+
}
|
|
29
107
|
let nextWatch = 0;
|
|
30
|
-
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export async function watch(path: string, listener: (path: string) => void, options: WatchOptions = {}) {
|
|
35
|
-
if (options.recursive !== undefined && typeof options.recursive !== "boolean") throw new TypeError("recursive must be a boolean");
|
|
108
|
+
/** Invalidation, not an exact change log. Recursive watches require a directory. */
|
|
109
|
+
export async function watch(path: string, listener: (path: string) => void, options: WatchOptions = {}): Promise<AsyncRegistration> {
|
|
110
|
+
const location = absolute(path), recursive = recursiveOption(options, false);
|
|
111
|
+
if (typeof listener !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected a watch listener");
|
|
36
112
|
const id = `watch-${Date.now()}-${++nextWatch}`;
|
|
37
113
|
let removed = false;
|
|
114
|
+
const emitter = new NativeEventEmitter(native());
|
|
38
115
|
const subscription = emitter.addListener("change", (event: { id: string; path: string }) => {
|
|
39
|
-
if (!removed && event
|
|
116
|
+
if (!removed && event?.id === id && typeof event.path === "string") listener(event.path);
|
|
40
117
|
});
|
|
41
|
-
try { await call("watch", { path:
|
|
118
|
+
try { await call("watch", { path: location, id, recursive }); }
|
|
42
119
|
catch (error) { subscription.remove(); throw error; }
|
|
43
|
-
return {
|
|
44
|
-
if (removed) return;
|
|
45
|
-
removed = true; subscription.remove(); await call("unwatch", { id });
|
|
46
|
-
} };
|
|
120
|
+
return asyncRegistration(() => { removed = true; subscription.remove(); }, async () => { await call("unwatch", { id }); });
|
|
47
121
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
export
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
122
|
+
/** read/readWrite require an existing regular file. write truncates; createNew rejects if it exists. */
|
|
123
|
+
export async function openFile(path: string, options: OpenFileOptions = {}) {
|
|
124
|
+
checkedOptions(options, ["mode"], "openFile");
|
|
125
|
+
return createFileHandle(call, absolute(path), options.mode);
|
|
126
|
+
}
|
|
127
|
+
/** Returns an iterator synchronously; option names validate now, while path/range validation occurs on first iteration. Closes on EOF, error, abort, or early loop exit. */
|
|
128
|
+
export function readChunks(path: string, options: ReadChunksOptions = {}): AsyncGenerator<Uint8Array> {
|
|
129
|
+
checkedOptions(options, ["offset", "chunkSize", "signal"], "readChunks");
|
|
130
|
+
return iterateFile(() => openFile(path), options);
|
|
131
|
+
}
|
|
132
|
+
/** Promise-based validation and IO; failure leaves a partial file, so use a temporary file + move for publication. */
|
|
133
|
+
export async function writeChunks(path: string, chunks: AsyncIterable<Uint8Array> | Iterable<Uint8Array>, options: WriteChunksOptions = {}): Promise<number> {
|
|
134
|
+
checkedOptions(options, ["mode", "signal"], "writeChunks");
|
|
135
|
+
if (options.mode !== undefined && options.mode !== "write" && options.mode !== "createNew") throw new SparkError("E_INVALID_ARGUMENT", "Streaming writes require write or createNew mode");
|
|
58
136
|
return writeFileChunks(() => openFile(path, { mode: options.mode ?? "write" }), chunks, options.signal);
|
|
59
|
-
}
|
|
137
|
+
}
|
|
60
138
|
/** Move to the OS Trash/Recycle Bin. Never falls back to permanent deletion. */
|
|
61
|
-
export
|
|
139
|
+
export async function trash(path: string): Promise<void> { await call("trash", { path: absolute(path) }); }
|
|
140
|
+
|
|
141
|
+
/** Reveal an existing path in Finder or Explorer. */
|
|
142
|
+
export async function revealInFileManager(path: string): Promise<void> { await call("reveal", { path: absolute(path) }); }
|
|
143
|
+
/** Compare before replacing. Detects observed changes; not an OS-wide atomic compare-and-swap. */
|
|
144
|
+
export async function writeTextIfUnchanged(path: string, expected: string, contents: string): Promise<{ written: boolean }> {
|
|
145
|
+
if (typeof expected !== "string" || typeof contents !== "string") throw new SparkError("E_INVALID_ARGUMENT", "Expected text strings");
|
|
146
|
+
return { written: await call<boolean>("writeTextIfUnchanged", { path: absolute(path), expected, text: contents }) };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export { scanFiles, getFileScanAvailability } from "./file-scanner";
|
|
150
|
+
export type { FileScanOptions, FileScanSkipEntry, ScannedFile, FileScanProgress, FileScanBatch, FileScanResult } from "./file-scanner";
|
|
@@ -1,9 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export function absolutePath(path: string, platform: string) {
|
|
3
|
-
const windows = /^(?:[a-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]+)/i.test(path);
|
|
4
|
-
if (typeof path !== "string" || path.includes("\0") ||
|
|
5
|
-
!(path.startsWith("file://") || (platform === "windows" ? windows : path.startsWith("/")))) {
|
|
6
|
-
throw new Error("Expected an absolute path or file URL");
|
|
7
|
-
}
|
|
8
|
-
return path;
|
|
9
|
-
}
|
|
1
|
+
export { absolutePath } from "@legendapp/spark-desktop-app/src/contracts/path";
|
package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.h
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
#include "NativeModules.h"
|
|
3
|
+
#include <SparkBinaryWindows.hpp>
|
|
3
4
|
#include <shlobj.h>
|
|
4
5
|
#include <shlwapi.h>
|
|
5
6
|
#include <winrt/Windows.Data.Json.h>
|
|
6
|
-
#include <winrt/Windows.Security.Cryptography.h>
|
|
7
7
|
#include <winrt/Windows.Storage.Streams.h>
|
|
8
8
|
#include <filesystem>
|
|
9
9
|
#include <algorithm>
|
|
@@ -26,7 +26,6 @@ namespace winrt::SparkFileSystem {
|
|
|
26
26
|
namespace React = Microsoft::ReactNative;
|
|
27
27
|
namespace Json = Windows::Data::Json;
|
|
28
28
|
namespace fs = std::filesystem;
|
|
29
|
-
using Crypto = Windows::Security::Cryptography::CryptographicBuffer;
|
|
30
29
|
|
|
31
30
|
inline fs::path FilePath(hstring const &input) {
|
|
32
31
|
std::wstring value(input);
|
|
@@ -58,7 +57,7 @@ inline std::string ErrorCode(DWORD error) {
|
|
|
58
57
|
default: return "E_IO";
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
|
-
inline void Reject(
|
|
60
|
+
template <class Promise> inline void Reject(Promise const &promise) noexcept {
|
|
62
61
|
try { throw; }
|
|
63
62
|
catch (fs::filesystem_error const &e) {
|
|
64
63
|
auto code = e.code(); std::string kind = "E_IO";
|
|
@@ -100,6 +99,58 @@ inline void Write(fs::path const &path, std::vector<uint8_t> const &data) {
|
|
|
100
99
|
if (!MoveFileExW(temporary.c_str(), path.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) throw_last_error();
|
|
101
100
|
} catch (...) { std::error_code ignored; fs::remove(temporary, ignored); throw; }
|
|
102
101
|
}
|
|
102
|
+
inline bool PathContains(fs::path const &parent, fs::path const &child) {
|
|
103
|
+
auto p = parent.begin(), c = child.begin();
|
|
104
|
+
for (; p != parent.end() && c != child.end() && CompareStringOrdinal(p->c_str(), -1, c->c_str(), -1, TRUE) == CSTR_EQUAL; ++p, ++c) {}
|
|
105
|
+
return p == parent.end();
|
|
106
|
+
}
|
|
107
|
+
inline void Transfer(fs::path const &from, fs::path const &to, bool move, bool overwrite) {
|
|
108
|
+
auto source = fs::weakly_canonical(from), destination = fs::weakly_canonical(to);
|
|
109
|
+
std::error_code identityError;
|
|
110
|
+
bool sameFile = fs::equivalent(from, to, identityError);
|
|
111
|
+
if (identityError && identityError != std::errc::no_such_file_or_directory) throw fs::filesystem_error("Could not compare transfer paths", from, to, identityError);
|
|
112
|
+
if (sameFile || PathContains(source, destination) || PathContains(destination, source))
|
|
113
|
+
throw hresult_invalid_argument(L"Source and destination identify or contain one another");
|
|
114
|
+
bool destinationExists = fs::symlink_status(to).type() != fs::file_type::not_found;
|
|
115
|
+
if (destinationExists && !overwrite) throw fs::filesystem_error("Destination already exists", to, std::make_error_code(std::errc::file_exists));
|
|
116
|
+
GUID guid{}; check_hresult(CoCreateGuid(&guid)); wchar_t identifier[40]{}; StringFromGUID2(guid, identifier, 40);
|
|
117
|
+
auto stage = to.parent_path() / (std::wstring(L".spark-stage-") + identifier);
|
|
118
|
+
auto backup = to.parent_path() / (std::wstring(L".spark-backup-") + identifier);
|
|
119
|
+
bool stagedByRename = false;
|
|
120
|
+
try {
|
|
121
|
+
wchar_t sourceVolume[32768]{}, destinationVolume[32768]{};
|
|
122
|
+
bool sameVolumeMove = move && GetVolumePathNameW(from.parent_path().c_str(), sourceVolume, ARRAYSIZE(sourceVolume)) &&
|
|
123
|
+
GetVolumePathNameW(to.parent_path().c_str(), destinationVolume, ARRAYSIZE(destinationVolume)) &&
|
|
124
|
+
_wcsicmp(sourceVolume, destinationVolume) == 0;
|
|
125
|
+
if (sameVolumeMove) { fs::rename(from, stage); stagedByRename = true; }
|
|
126
|
+
else fs::copy(from, stage, fs::copy_options::recursive | fs::copy_options::copy_symlinks);
|
|
127
|
+
if (destinationExists && !MoveFileExW(to.c_str(), backup.c_str(), MOVEFILE_WRITE_THROUGH)) {
|
|
128
|
+
auto failure = GetLastError();
|
|
129
|
+
bool restoredSource = !stagedByRename || MoveFileExW(stage.c_str(), from.c_str(), MOVEFILE_WRITE_THROUGH);
|
|
130
|
+
if (!restoredSource) throw hresult_error(HRESULT_FROM_WIN32(ERROR_WRITE_FAULT), L"Destination staging failed and source rollback failed; recoverable source: " + stage.wstring());
|
|
131
|
+
throw hresult_error(HRESULT_FROM_WIN32(failure));
|
|
132
|
+
}
|
|
133
|
+
if (!MoveFileExW(stage.c_str(), to.c_str(), MOVEFILE_WRITE_THROUGH)) {
|
|
134
|
+
auto failure = GetLastError();
|
|
135
|
+
bool restoredDestination = !destinationExists || MoveFileExW(backup.c_str(), to.c_str(), MOVEFILE_WRITE_THROUGH);
|
|
136
|
+
bool restoredSource = !stagedByRename || MoveFileExW(stage.c_str(), from.c_str(), MOVEFILE_WRITE_THROUGH);
|
|
137
|
+
if (!restoredDestination || !restoredSource)
|
|
138
|
+
throw hresult_error(HRESULT_FROM_WIN32(ERROR_WRITE_FAULT), L"Publication and rollback failed; recoverable paths: " + stage.wstring() + L"; " + backup.wstring());
|
|
139
|
+
throw hresult_error(HRESULT_FROM_WIN32(failure));
|
|
140
|
+
}
|
|
141
|
+
if (move && !stagedByRename) {
|
|
142
|
+
std::error_code sourceError; fs::remove_all(from, sourceError);
|
|
143
|
+
if (sourceError) throw fs::filesystem_error("Source cleanup failed after publishing " + PathString(to) + "; source may be partially removed; previous destination is retained at " + PathString(backup), from, sourceError);
|
|
144
|
+
}
|
|
145
|
+
if (destinationExists) {
|
|
146
|
+
std::error_code cleanupError; fs::remove_all(backup, cleanupError);
|
|
147
|
+
if (cleanupError) throw fs::filesystem_error("Transfer committed at " + PathString(to) + "; previous destination cleanup failed; recoverable backup: " + PathString(backup), backup, cleanupError);
|
|
148
|
+
}
|
|
149
|
+
} catch (...) {
|
|
150
|
+
if (!stagedByRename) { std::error_code ignored; fs::remove_all(stage, ignored); }
|
|
151
|
+
throw;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
103
154
|
inline fs::path Directory(hstring const &kind) {
|
|
104
155
|
wchar_t identity[201]{}; auto count = GetEnvironmentVariableW(L"SPARK_PROJECT_ID", identity, 201);
|
|
105
156
|
if (!count || count >= 201) throw hresult_invalid_argument(L"Launch through Spark to establish project storage identity");
|
|
@@ -182,7 +233,7 @@ struct FileQueue {
|
|
|
182
233
|
}) {}
|
|
183
234
|
~FileQueue() { { std::lock_guard lock(mutex); stopping = true; } ready.notify_one(); worker.join(); }
|
|
184
235
|
void Post(std::function<void()> action) { { std::lock_guard lock(mutex); pending.push_back(std::move(action)); } ready.notify_one(); }
|
|
185
|
-
Json::IJsonValue Call(std::string const &method, Json::JsonObject const &args) {
|
|
236
|
+
Json::IJsonValue Call(std::string const &method, Json::JsonObject const &args, std::optional<std::vector<uint8_t>> const &input = {}, std::vector<uint8_t> *output = nullptr) {
|
|
186
237
|
if (method == "directory") return Json::JsonValue::CreateStringValue(Directory(args.GetNamedString(L"kind")).wstring());
|
|
187
238
|
if (method == "unwatch") { watches.erase(to_string(args.GetNamedString(L"id"))); return Json::JsonValue::Parse(L"null"); }
|
|
188
239
|
if (method == "closeFile") { files.erase(to_string(args.GetNamedString(L"id"))); return Json::JsonValue::Parse(L"null"); }
|
|
@@ -200,13 +251,10 @@ struct FileQueue {
|
|
|
200
251
|
if (!std::isfinite(length) || length < 1 || length > 1048576 || std::floor(length) != length || offset + length > 9007199254740991.0) throw hresult_invalid_argument(L"Invalid chunk length");
|
|
201
252
|
std::vector<uint8_t> data(static_cast<size_t>(length)); DWORD count = 0;
|
|
202
253
|
if (!ReadFile(file, data.data(), static_cast<DWORD>(data.size()), &count, nullptr)) throw_last_error();
|
|
203
|
-
data.resize(count); return Json::JsonValue::
|
|
254
|
+
data.resize(count); if (!output) throw hresult_invalid_argument(L"Expected binary transport"); *output = std::move(data); return Json::JsonValue::CreateNullValue();
|
|
204
255
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
auto buffer = Crypto::DecodeFromBase64String(base64);
|
|
208
|
-
if (Crypto::EncodeToBase64String(buffer) != base64 || buffer.Length() > 1048576 || offset + buffer.Length() > 9007199254740991.0) throw hresult_invalid_argument(L"Invalid chunk");
|
|
209
|
-
com_array<uint8_t> data; Crypto::CopyToByteArray(buffer, data);
|
|
256
|
+
if (!input || input->size() > 1048576 || offset + input->size() > 9007199254740991.0) throw hresult_invalid_argument(L"Invalid chunk");
|
|
257
|
+
auto const &data = *input;
|
|
210
258
|
size_t written = 0;
|
|
211
259
|
while (written < data.size()) { DWORD count = 0; if (!WriteFile(file, data.data() + written, static_cast<DWORD>(data.size() - written), &count, nullptr)) throw_last_error(); if (!count) throw hresult_error(E_FAIL, L"File write made no progress"); written += count; }
|
|
212
260
|
return Json::JsonValue::CreateNumberValue(static_cast<double>(written));
|
|
@@ -234,13 +282,15 @@ struct FileQueue {
|
|
|
234
282
|
if (text.size() > INT_MAX) throw hresult_error(E_FAIL, L"Text exceeds the UTF-8 conversion limit");
|
|
235
283
|
if (!text.empty() && !MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text.data(), static_cast<int>(text.size()), nullptr, 0)) throw_last_error();
|
|
236
284
|
return Json::JsonValue::CreateStringValue(to_hstring(text));
|
|
237
|
-
} else if (method == "readBytes") return Json::JsonValue::
|
|
285
|
+
} else if (method == "readBytes") { if (!output) throw hresult_invalid_argument(L"Expected binary transport"); *output = Read(path); return Json::JsonValue::CreateNullValue(); }
|
|
286
|
+
else if (method == "writeTextIfUnchanged") {
|
|
287
|
+
if (Call("readText", args).GetString() != args.GetNamedString(L"expected")) return Json::JsonValue::CreateBooleanValue(false);
|
|
288
|
+
auto text = to_string(args.GetNamedString(L"text")); Write(path, std::vector<uint8_t>(text.begin(), text.end()));
|
|
289
|
+
return Json::JsonValue::CreateBooleanValue(true);
|
|
290
|
+
}
|
|
238
291
|
else if (method == "writeText") { auto text = to_string(args.GetNamedString(L"text")); Write(path, std::vector<uint8_t>(text.begin(), text.end())); }
|
|
239
292
|
else if (method == "writeBytes") {
|
|
240
|
-
|
|
241
|
-
try { buffer = base64.empty() ? Crypto::CreateFromByteArray(array_view<uint8_t const>{}) : Crypto::DecodeFromBase64String(base64); } catch (hresult_error const &) { throw hresult_invalid_argument(L"Invalid base64 data"); }
|
|
242
|
-
if (Crypto::EncodeToBase64String(buffer) != base64) throw hresult_invalid_argument(L"Invalid base64 data");
|
|
243
|
-
com_array<uint8_t> data; Crypto::CopyToByteArray(buffer, data); Write(path, std::vector<uint8_t>(data.begin(), data.end()));
|
|
293
|
+
if (!input) throw hresult_invalid_argument(L"Expected binary file data"); Write(path, *input);
|
|
244
294
|
} else if (method == "mkdir") {
|
|
245
295
|
if (args.GetNamedBoolean(L"recursive", true)) fs::create_directories(path);
|
|
246
296
|
else if (!fs::create_directory(path)) throw hresult_error(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
|
|
@@ -249,15 +299,7 @@ struct FileQueue {
|
|
|
249
299
|
return Json::JsonValue::CreateBooleanValue(fs::remove(path));
|
|
250
300
|
} else if (method == "copy" || method == "move") {
|
|
251
301
|
auto to = FilePath(args.GetNamedString(L"to"));
|
|
252
|
-
|
|
253
|
-
if (destination.type() != fs::file_type::not_found) throw hresult_error(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
|
|
254
|
-
if (method == "copy") fs::copy(path, to, fs::copy_options::recursive | fs::copy_options::copy_symlinks);
|
|
255
|
-
else if (!MoveFileExW(path.c_str(), to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
|
|
256
|
-
if (GetLastError() != ERROR_NOT_SAME_DEVICE || !fs::is_directory(path)) throw_last_error();
|
|
257
|
-
try { fs::copy(path, to, fs::copy_options::recursive | fs::copy_options::copy_symlinks); }
|
|
258
|
-
catch (...) { std::error_code ignored; fs::remove_all(to, ignored); throw; }
|
|
259
|
-
fs::remove_all(path);
|
|
260
|
-
}
|
|
302
|
+
Transfer(path, to, method == "move", args.GetNamedBoolean(L"overwrite", false));
|
|
261
303
|
} else if (method == "stat") {
|
|
262
304
|
// OPEN_REPARSE_POINT describes a link itself, including dangling links.
|
|
263
305
|
auto file = Open(path, FILE_READ_ATTRIBUTES, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
|
|
@@ -281,9 +323,43 @@ struct FileQueue {
|
|
|
281
323
|
};
|
|
282
324
|
REACT_MODULE(SparkFileSystem, L"NativeDesktopFileSystem")
|
|
283
325
|
struct SparkFileSystem {
|
|
284
|
-
|
|
285
|
-
|
|
326
|
+
React::ReactContext context;
|
|
327
|
+
std::shared_ptr<FileQueue> queue;
|
|
328
|
+
REACT_INIT(Initialize) void Initialize(React::ReactContext const &value) noexcept { context = value; queue = std::make_shared<FileQueue>(context); }
|
|
329
|
+
REACT_SYNC_METHOD(installBinary) std::optional<std::string> installBinary() noexcept {
|
|
330
|
+
try {
|
|
331
|
+
spark::binary::Install(context, "__sparkFileSystemBinary", [queue = queue](std::string method, std::string encoded, std::optional<std::vector<uint8_t>> bytes, spark::binary::Completion finish) {
|
|
332
|
+
if (method == "reveal") {
|
|
333
|
+
queue->context.UIDispatcher().Post([encoded, promise = spark::binary::NativePromise(finish)] {
|
|
334
|
+
PIDLIST_ABSOLUTE item = nullptr;
|
|
335
|
+
try { auto path = FilePath(Json::JsonObject::Parse(to_hstring(encoded)).GetNamedString(L"path")); check_hresult(SHParseDisplayName(path.c_str(), nullptr, &item, 0, nullptr)); auto result = SHOpenFolderAndSelectItems(item, 0, nullptr, 0); CoTaskMemFree(item); item = nullptr; check_hresult(result); promise.Resolve("null"); }
|
|
336
|
+
catch (...) { if (item) CoTaskMemFree(item); Reject(promise); }
|
|
337
|
+
}); return;
|
|
338
|
+
}
|
|
339
|
+
queue->Post([queue, method = std::move(method), encoded = std::move(encoded), bytes = std::move(bytes), promise = spark::binary::NativePromise(finish)] {
|
|
340
|
+
try { std::vector<uint8_t> output; auto result = queue->Call(method, Json::JsonObject::Parse(to_hstring(encoded)), bytes, &output); spark::binary::Response response{to_string(result.Stringify()), {}, {}}; if (method == "readBytes" || method == "readChunk") response.bytes = std::make_shared<spark::binary::Bytes>(std::move(output)); promise.Resolve(std::move(response)); }
|
|
341
|
+
catch (...) { Reject(promise); }
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
return std::nullopt;
|
|
345
|
+
} catch (std::exception const &error) { return error.what(); }
|
|
346
|
+
catch (hresult_error const &error) { return to_string(error.message()); }
|
|
347
|
+
catch (...) { return "Could not install native binary transport"; }
|
|
348
|
+
}
|
|
349
|
+
|
|
286
350
|
REACT_METHOD(call) void call(std::string method, std::string args, React::ReactPromise<std::string> promise) noexcept {
|
|
351
|
+
if (method == "reveal") {
|
|
352
|
+
context.UIDispatcher().Post([args, promise] {
|
|
353
|
+
PIDLIST_ABSOLUTE item = nullptr;
|
|
354
|
+
try {
|
|
355
|
+
auto path = FilePath(Json::JsonObject::Parse(to_hstring(args)).GetNamedString(L"path"));
|
|
356
|
+
check_hresult(SHParseDisplayName(path.c_str(), nullptr, &item, 0, nullptr));
|
|
357
|
+
auto result = SHOpenFolderAndSelectItems(item, 0, nullptr, 0);
|
|
358
|
+
CoTaskMemFree(item); item = nullptr; check_hresult(result); promise.Resolve("null");
|
|
359
|
+
} catch (...) { if (item) CoTaskMemFree(item); Reject(promise); }
|
|
360
|
+
});
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
287
363
|
queue->Post([state = queue.get(), method, args, promise] {
|
|
288
364
|
try { promise.Resolve(to_string(state->Call(method, Json::JsonObject::Parse(to_hstring(args))).Stringify())); }
|
|
289
365
|
catch (...) { Reject(promise); }
|
|
@@ -80,6 +80,8 @@
|
|
|
80
80
|
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
81
81
|
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
|
82
82
|
<AdditionalIncludeDirectories>
|
|
83
|
+
$(MSBuildThisFileDirectory)..\..\..\spark-desktop-app\cpp;
|
|
84
|
+
$(MSBuildThisFileDirectory)..\..\..\desktop-app\cpp;
|
|
83
85
|
$(MSBuildThisFileDirectory);
|
|
84
86
|
%(AdditionalIncludeDirectories)
|
|
85
87
|
</AdditionalIncludeDirectories>
|
package/vendor/node_modules/@legendapp/spark-global-shortcuts/macos/RNDesktopGlobalShortcuts.mm
CHANGED
|
@@ -36,6 +36,7 @@ RCT_EXPORT_MODULE(NativeDesktopGlobalShortcuts)
|
|
|
36
36
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
37
37
|
NSDictionary *args = SparkArgs(json); NSString *key = args[@"id"];
|
|
38
38
|
if ([method isEqual:@"register"]) {
|
|
39
|
+
if (![args[@"key"] isKindOfClass:NSString.class] || ![args[@"key"] length]) { SparkInvalid(reject, @"Expected a shortcut key"); return; }
|
|
39
40
|
NSInteger code = KeyCode(args[@"key"]);
|
|
40
41
|
if (code < 0) { reject(@"E_KEY", @"Key is unavailable in the current keyboard layout", nil); return; }
|
|
41
42
|
NSUInteger flags = [args[@"modifiers"] unsignedIntegerValue]; UInt32 modifiers = 0;
|
|
@@ -43,15 +44,17 @@ RCT_EXPORT_MODULE(NativeDesktopGlobalShortcuts)
|
|
|
43
44
|
if (flags & NSEventModifierFlagControl) modifiers |= controlKey;
|
|
44
45
|
if (flags & NSEventModifierFlagShift) modifiers |= shiftKey;
|
|
45
46
|
if (flags & NSEventModifierFlagOption) modifiers |= optionKey;
|
|
46
|
-
for (NSDictionary *existing in self.registrations.allValues) if ([existing[@"code"] integerValue] == code && [existing[@"modifiers"] unsignedIntValue] == modifiers) { reject(@"
|
|
47
|
-
if (self.registrations[key]) { reject(@"
|
|
47
|
+
for (NSDictionary *existing in self.registrations.allValues) if ([existing[@"code"] integerValue] == code && [existing[@"modifiers"] unsignedIntValue] == modifiers) { reject(@"E_BUSY", @"Shortcut already registered", nil); return; }
|
|
48
|
+
if (self.registrations[key]) { reject(@"E_BUSY", @"Shortcut id already exists", nil); return; }
|
|
48
49
|
if (!self.handler) { EventTypeSpec spec = { kEventClassKeyboard, kEventHotKeyPressed }; EventHandlerRef handler;
|
|
49
50
|
if (InstallEventHandler(GetEventDispatcherTarget(), Hotkey, 1, &spec, (__bridge void *)self, &handler) != noErr) { reject(@"E_SHORTCUT", @"Could not install hotkey handler", nil); return; } self.handler = handler; }
|
|
50
51
|
EventHotKeyID identity = { 'LGDS', ++self.sequence }; EventHotKeyRef reference;
|
|
51
|
-
if (RegisterEventHotKey((UInt32)code, modifiers, identity, GetEventDispatcherTarget(), 0, &reference) != noErr) { reject(@"
|
|
52
|
+
if (RegisterEventHotKey((UInt32)code, modifiers, identity, GetEventDispatcherTarget(), 0, &reference) != noErr) { reject(@"E_BUSY", @"Shortcut is unavailable or reserved by another app", nil); return; }
|
|
52
53
|
self.registrations[key] = @{ @"code": @(code), @"modifiers": @(modifiers), @"number": @(identity.id), @"reference": [NSValue valueWithPointer:reference] };
|
|
53
54
|
} else if ([method isEqual:@"remove"]) {
|
|
54
|
-
NSDictionary *value = self.registrations[key];
|
|
55
|
+
NSDictionary *value = self.registrations[key];
|
|
56
|
+
if (value && UnregisterEventHotKey((EventHotKeyRef)[value[@"reference"] pointerValue]) != noErr) { reject(@"E_NATIVE", @"Could not unregister global shortcut", nil); return; }
|
|
57
|
+
[self.registrations removeObjectForKey:key];
|
|
55
58
|
} else { SparkInvalid(reject, @"Unknown global shortcut operation"); return; }
|
|
56
59
|
resolve(@"null");
|
|
57
60
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@legendapp/spark-global-shortcuts",
|
|
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
|
"NativeDesktopGlobalShortcuts": "RNDesktopGlobalShortcuts"
|
|
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
|
}
|
package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/NativeDesktopGlobalShortcuts.ts
CHANGED
|
@@ -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>("NativeDesktopGlobalShortcuts");
|
|
@@ -1,16 +1,45 @@
|
|
|
1
1
|
import { Platform } from "react-native";
|
|
2
2
|
import Native from "./NativeDesktopGlobalShortcuts";
|
|
3
|
-
import { onDesktopEvent } from "@legendapp/spark-desktop-app";
|
|
4
|
-
import { parseAccelerator } from "
|
|
3
|
+
import { onDesktopEvent } from "@legendapp/spark-desktop-app/src/events";
|
|
4
|
+
import { parseAccelerator } from "@legendapp/spark-desktop-app/src/contracts/accelerator";
|
|
5
|
+
import { SparkError, invokeNative, parseNativeResult, asyncRegistration, type AsyncRegistration, type Availability } from "@legendapp/spark-desktop-app/src/contracts";
|
|
6
|
+
export type { AsyncRegistration } from "@legendapp/spark-desktop-app/src/contracts";
|
|
7
|
+
export interface GlobalShortcutOptions {
|
|
8
|
+
/** Fire on keyboard auto-repeat. Windows registers without MOD_NOREPEAT; macOS hotkeys never deliver repeats, so true rejects there. */
|
|
9
|
+
repeat?: boolean;
|
|
10
|
+
}
|
|
5
11
|
let sequence = 0;
|
|
6
|
-
export
|
|
12
|
+
export function getGlobalShortcutAvailability(): Availability {
|
|
13
|
+
if (Platform.OS !== "macos" && Platform.OS !== "windows") return { available: false, reason: "unsupported-platform" };
|
|
14
|
+
return Native ? { available: true } : { available: false, reason: "missing-module" };
|
|
15
|
+
}
|
|
16
|
+
function native() {
|
|
17
|
+
const availability = getGlobalShortcutAvailability();
|
|
18
|
+
if (!availability.available) throw new SparkError(availability.reason === "unsupported-platform" ? "E_UNSUPPORTED_PLATFORM" : "E_MODULE_UNAVAILABLE", "Global shortcuts are unavailable");
|
|
19
|
+
return Native!;
|
|
20
|
+
}
|
|
21
|
+
async function call(method: string, args: object) {
|
|
22
|
+
parseNativeResult(await invokeNative(() => native().call(method, JSON.stringify(args))), (value): value is null => value === null);
|
|
23
|
+
}
|
|
24
|
+
/** Conflicts/reserved shortcuts reject E_BUSY. Layout is resolved at registration. */
|
|
25
|
+
export async function registerGlobalShortcut(accelerator: string, handler: () => void, options: GlobalShortcutOptions = {}): Promise<AsyncRegistration> {
|
|
26
|
+
if (typeof handler !== "function" || !options || typeof options !== "object" || Array.isArray(options)) throw new SparkError("E_INVALID_ARGUMENT", "Expected global shortcut handler and options");
|
|
27
|
+
for (const key of Object.keys(options)) if (!["repeat"].includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unsupported global shortcut option: ${key}`);
|
|
28
|
+
if (options.repeat !== undefined && typeof options.repeat !== "boolean") throw new SparkError("E_INVALID_ARGUMENT", "Global shortcut repeat must be boolean");
|
|
29
|
+
if (options.repeat === true && Platform.OS === "macos") throw new SparkError("E_UNSUPPORTED_OPTION", "macOS global shortcuts never deliver keyboard repeats");
|
|
7
30
|
const parsed = parseAccelerator(accelerator, Platform.OS === "windows" ? "windows" : "macos"), id = `global-${Date.now()}-${++sequence}`;
|
|
31
|
+
native();
|
|
8
32
|
let removed = false;
|
|
9
|
-
const subscription = onDesktopEvent(event => { if (!removed && event.type === "globalShortcut" && event.id === id)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
33
|
+
const subscription = onDesktopEvent(event => { if (!removed && event.type === "globalShortcut" && event.id === id) {
|
|
34
|
+
try { handler(); }
|
|
35
|
+
catch (cause) { console.error(new SparkError("E_NATIVE", "Global shortcut handler failed", { cause })); }
|
|
36
|
+
} }, { types: ["globalShortcut"], target: { field: "id", value: id } });
|
|
37
|
+
try { await call("register", { id, repeat: options.repeat ?? false, ...parsed }); }
|
|
38
|
+
catch (cause) {
|
|
39
|
+
removed = true; subscription.remove();
|
|
40
|
+
try { await call("remove", { id }); }
|
|
41
|
+
catch (cleanup) { throw new SparkError("E_NATIVE", "Shortcut registration and cleanup failed", { cause: new AggregateError([cause, cleanup]) }); }
|
|
42
|
+
throw cause;
|
|
43
|
+
}
|
|
44
|
+
return asyncRegistration(() => { removed = true; subscription.remove(); }, async () => { await call("remove", { id }); });
|
|
16
45
|
}
|
|
@@ -58,18 +58,19 @@ struct SparkGlobalShortcuts {
|
|
|
58
58
|
auto args = Json::JsonObject::Parse(to_hstring(encoded)); auto id = to_string(args.GetNamedString(L"id"));
|
|
59
59
|
current->EnsureWindow();
|
|
60
60
|
if (method == "remove") {
|
|
61
|
-
for (auto item = current->keys.begin(); item != current->keys.end(); ++item) if (item->second == id) { UnregisterHotKey(current->hwnd, item->first); current->keys.erase(item); break; }
|
|
61
|
+
for (auto item = current->keys.begin(); item != current->keys.end(); ++item) if (item->second == id) { if (!UnregisterHotKey(current->hwnd, item->first)) winrt::throw_last_error(); current->keys.erase(item); break; }
|
|
62
62
|
} else if (method == "register") {
|
|
63
|
-
for (auto const &[key, existing] : current->keys) if (existing == id) { promise.Reject(React::ReactError{"
|
|
63
|
+
for (auto const &[key, existing] : current->keys) if (existing == id) { promise.Reject(React::ReactError{"E_BUSY", "Shortcut id already exists"}); return; }
|
|
64
64
|
auto flags = static_cast<int>(args.GetNamedNumber(L"modifiers"));
|
|
65
|
-
|
|
65
|
+
// MOD_NOREPEAT suppresses OS auto-repeat; repeat:true registers with repeats.
|
|
66
|
+
UINT modifiers = args.GetNamedBoolean(L"repeat", false) ? 0 : MOD_NOREPEAT;
|
|
66
67
|
if (flags & (1 << 17)) modifiers |= MOD_SHIFT; if (flags & (1 << 18)) modifiers |= MOD_CONTROL;
|
|
67
68
|
if (flags & (1 << 19)) modifiers |= MOD_ALT; if (flags & (1 << 20)) modifiers |= MOD_WIN;
|
|
68
69
|
const auto key = Hotkeys::Key(args.GetNamedString(L"key"), modifiers);
|
|
69
70
|
// Window-local registration IDs must remain within the documented range.
|
|
70
71
|
int identifier = 0;
|
|
71
72
|
for (int i = 0; i < 0xbfff; ++i) { int candidate = current->sequence = current->sequence % 0xbfff + 1; if (!current->keys.count(candidate)) { identifier = candidate; break; } }
|
|
72
|
-
if (!identifier || !RegisterHotKey(current->hwnd, identifier, modifiers, key)) { promise.Reject(React::ReactError{"
|
|
73
|
+
if (!identifier || !RegisterHotKey(current->hwnd, identifier, modifiers, key)) { promise.Reject(React::ReactError{"E_BUSY", "Shortcut is unavailable or reserved by another app"}); return; }
|
|
73
74
|
current->keys.emplace(identifier, id);
|
|
74
75
|
} else throw hresult_invalid_argument(L"Unknown global shortcut operation");
|
|
75
76
|
promise.Resolve("null");
|
|
@@ -9,9 +9,9 @@ RCT_EXPORT_MODULE(NativeDesktopMessageDialog)
|
|
|
9
9
|
[NSRunLoop.mainRunLoop performBlock:^{
|
|
10
10
|
if (![method isEqual:@"show"]) { SparkInvalid(reject, @"Unknown dialog operation"); return; }
|
|
11
11
|
if (self.alert) { reject(@"E_BUSY", @"A message dialog is already open", nil); return; }
|
|
12
|
-
NSDictionary *args = SparkArgs(json); NSWindow *parent;
|
|
12
|
+
NSDictionary *args = SparkArgs(json); NSWindow *parent = nil;
|
|
13
13
|
if (args[@"windowId"]) {
|
|
14
|
-
for (NSWindow *window in NSApp.windows) if ([window.identifier isEqual:[@"spark." stringByAppendingString:args[@"windowId"]]]) parent = window;
|
|
14
|
+
for (NSWindow *window in NSApp.windows) if ([window.identifier isEqual:args[@"windowId"]] || [window.identifier isEqual:[@"spark." stringByAppendingString:args[@"windowId"]]]) { parent = window; break; }
|
|
15
15
|
if (!parent) { reject(@"E_NOT_FOUND", @"Dialog parent does not exist", nil); return; }
|
|
16
16
|
if (parent.attachedSheet) { reject(@"E_BUSY", @"Parent already has a sheet", nil); return; }
|
|
17
17
|
}
|