mirror of
https://github.com/ByteAtATime/raycast-linux.git
synced 2025-08-30 18:57:25 +00:00
feat: implement system utilities API
This commit introduces new system message schemas and corresponding commands for managing applications, including fetching applications, getting the default application, retrieving the frontmost application, showing an application in Finder, and trashing files.
This commit is contained in:
parent
418c23cb0a
commit
9c1ee6efe4
9 changed files with 358 additions and 101 deletions
|
@ -299,6 +299,50 @@ const OauthRemoveTokensMessageSchema = z.object({
|
|||
payload: OauthRemoveTokensPayloadSchema
|
||||
});
|
||||
|
||||
const SystemGetApplicationsPayloadSchema = z.object({
|
||||
requestId: z.string(),
|
||||
path: z.string().optional()
|
||||
});
|
||||
const SystemGetApplicationsMessageSchema = z.object({
|
||||
type: z.literal('system-get-applications'),
|
||||
payload: SystemGetApplicationsPayloadSchema
|
||||
});
|
||||
|
||||
const SystemGetDefaultApplicationPayloadSchema = z.object({
|
||||
requestId: z.string(),
|
||||
path: z.string()
|
||||
});
|
||||
const SystemGetDefaultApplicationMessageSchema = z.object({
|
||||
type: z.literal('system-get-default-application'),
|
||||
payload: SystemGetDefaultApplicationPayloadSchema
|
||||
});
|
||||
|
||||
const SystemGetFrontmostApplicationPayloadSchema = z.object({
|
||||
requestId: z.string()
|
||||
});
|
||||
const SystemGetFrontmostApplicationMessageSchema = z.object({
|
||||
type: z.literal('system-get-frontmost-application'),
|
||||
payload: SystemGetFrontmostApplicationPayloadSchema
|
||||
});
|
||||
|
||||
const SystemShowInFinderPayloadSchema = z.object({
|
||||
requestId: z.string(),
|
||||
path: z.string()
|
||||
});
|
||||
const SystemShowInFinderMessageSchema = z.object({
|
||||
type: z.literal('system-show-in-finder'),
|
||||
payload: SystemShowInFinderPayloadSchema
|
||||
});
|
||||
|
||||
const SystemTrashPayloadSchema = z.object({
|
||||
requestId: z.string(),
|
||||
paths: z.array(z.string())
|
||||
});
|
||||
const SystemTrashMessageSchema = z.object({
|
||||
type: z.literal('system-trash'),
|
||||
payload: SystemTrashPayloadSchema
|
||||
});
|
||||
|
||||
export const SidecarMessageWithPluginsSchema = z.union([
|
||||
BatchUpdateSchema,
|
||||
CommandSchema,
|
||||
|
@ -319,6 +363,11 @@ export const SidecarMessageWithPluginsSchema = z.union([
|
|||
OauthAuthorizeMessageSchema,
|
||||
OauthGetTokensMessageSchema,
|
||||
OauthSetTokensMessageSchema,
|
||||
OauthRemoveTokensMessageSchema
|
||||
OauthRemoveTokensMessageSchema,
|
||||
SystemGetApplicationsMessageSchema,
|
||||
SystemGetDefaultApplicationMessageSchema,
|
||||
SystemGetFrontmostApplicationMessageSchema,
|
||||
SystemShowInFinderMessageSchema,
|
||||
SystemTrashMessageSchema
|
||||
]);
|
||||
export type SidecarMessageWithPlugins = z.infer<typeof SidecarMessageWithPluginsSchema>;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { writeOutput } from '../io';
|
|||
import type { Application } from './types';
|
||||
import { config } from '../config';
|
||||
import { browserExtensionState } from '../state';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
const supportPath = config.supportDir;
|
||||
try {
|
||||
|
@ -20,6 +21,40 @@ export interface FileSystemItem {
|
|||
|
||||
export const BrowserExtension = { name: 'BrowserExtension' };
|
||||
|
||||
const pendingSystemRequests = new Map<
|
||||
string,
|
||||
{ resolve: (value: unknown) => void; reject: (reason?: unknown) => void }
|
||||
>();
|
||||
|
||||
function sendSystemRequest<T>(type: string, payload: object = {}): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestId = crypto.randomUUID();
|
||||
pendingSystemRequests.set(requestId, { resolve, reject });
|
||||
writeOutput({
|
||||
type: `system-${type}`,
|
||||
payload: { requestId, ...payload }
|
||||
});
|
||||
setTimeout(() => {
|
||||
if (pendingSystemRequests.has(requestId)) {
|
||||
pendingSystemRequests.delete(requestId);
|
||||
reject(new Error(`Request for ${type} timed out`));
|
||||
}
|
||||
}, 5000); // 5-second timeout
|
||||
});
|
||||
}
|
||||
|
||||
export function handleSystemResponse(requestId: string, result: unknown, error?: string) {
|
||||
const promise = pendingSystemRequests.get(requestId);
|
||||
if (promise) {
|
||||
if (error) {
|
||||
promise.reject(new Error(error));
|
||||
} else {
|
||||
promise.resolve(result);
|
||||
}
|
||||
pendingSystemRequests.delete(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
export const environment = {
|
||||
appearance: 'dark' as const,
|
||||
assetsPath: config.assetsDir,
|
||||
|
@ -40,84 +75,12 @@ export const environment = {
|
|||
}
|
||||
};
|
||||
|
||||
const pendingFinderItemsRequests = new Map<
|
||||
string,
|
||||
{ resolve: (items: FileSystemItem[]) => void; reject: (error: Error) => void }
|
||||
>();
|
||||
|
||||
export async function getSelectedFinderItems(): Promise<FileSystemItem[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestId = Math.random().toString(36).substring(7);
|
||||
|
||||
pendingFinderItemsRequests.set(requestId, { resolve, reject });
|
||||
|
||||
writeOutput({
|
||||
type: 'get-selected-finder-items',
|
||||
payload: { requestId }
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (pendingFinderItemsRequests.has(requestId)) {
|
||||
pendingFinderItemsRequests.delete(requestId);
|
||||
reject(new Error('Timeout: Could not get selected finder items'));
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
return sendSystemRequest<FileSystemItem[]>('get-selected-finder-items');
|
||||
}
|
||||
|
||||
export function handleGetSelectedFinderItemsResponse(
|
||||
requestId: string,
|
||||
items: FileSystemItem[] | null,
|
||||
error?: string
|
||||
) {
|
||||
const pending = pendingFinderItemsRequests.get(requestId);
|
||||
if (pending) {
|
||||
pendingFinderItemsRequests.delete(requestId);
|
||||
if (error) {
|
||||
pending.reject(new Error(error));
|
||||
} else {
|
||||
pending.resolve(items || []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pendingTextRequests = new Map<
|
||||
string,
|
||||
{ resolve: (text: string) => void; reject: (error: Error) => void }
|
||||
>();
|
||||
|
||||
export async function getSelectedText(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestId = Math.random().toString(36).substring(7);
|
||||
|
||||
pendingTextRequests.set(requestId, { resolve, reject });
|
||||
|
||||
writeOutput({
|
||||
type: 'get-selected-text',
|
||||
payload: {
|
||||
requestId
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (pendingTextRequests.has(requestId)) {
|
||||
pendingTextRequests.delete(requestId);
|
||||
reject(new Error('Timeout: Could not get selected text'));
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
export function handleSelectedTextResponse(requestId: string, text: string | null, error?: string) {
|
||||
const pending = pendingTextRequests.get(requestId);
|
||||
if (pending) {
|
||||
pendingTextRequests.delete(requestId);
|
||||
if (error) {
|
||||
pending.reject(new Error(error));
|
||||
} else {
|
||||
pending.resolve(text || '');
|
||||
}
|
||||
}
|
||||
return sendSystemRequest<string>('get-selected-text');
|
||||
}
|
||||
|
||||
export async function open(target: string, application?: Application | string): Promise<void> {
|
||||
|
@ -137,3 +100,25 @@ export async function open(target: string, application?: Application | string):
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function getApplications(path?: fs.PathLike): Promise<Application[]> {
|
||||
const pathString = path ? path.toString() : undefined;
|
||||
return sendSystemRequest<Application[]>('get-applications', { path: pathString });
|
||||
}
|
||||
|
||||
export async function getDefaultApplication(path: fs.PathLike): Promise<Application> {
|
||||
return sendSystemRequest<Application>('get-default-application', { path: path.toString() });
|
||||
}
|
||||
|
||||
export async function getFrontmostApplication(): Promise<Application> {
|
||||
return sendSystemRequest<Application>('get-frontmost-application');
|
||||
}
|
||||
|
||||
export async function showInFinder(path: fs.PathLike): Promise<void> {
|
||||
return sendSystemRequest<void>('show-in-finder', { path: path.toString() });
|
||||
}
|
||||
|
||||
export async function trash(path: fs.PathLike | fs.PathLike[]): Promise<void> {
|
||||
const paths = (Array.isArray(path) ? path : [path]).map((p) => p.toString());
|
||||
return sendSystemRequest<void>('trash', { paths });
|
||||
}
|
||||
|
|
|
@ -10,7 +10,17 @@ import { Grid } from './components/grid';
|
|||
import { Form } from './components/form';
|
||||
import { Action, ActionPanel } from './components/actions';
|
||||
import { Detail } from './components/detail';
|
||||
import { environment, getSelectedFinderItems, getSelectedText, open } from './environment';
|
||||
import {
|
||||
environment,
|
||||
getSelectedFinderItems,
|
||||
getSelectedText,
|
||||
open,
|
||||
getApplications,
|
||||
getDefaultApplication,
|
||||
getFrontmostApplication,
|
||||
showInFinder,
|
||||
trash
|
||||
} from './environment';
|
||||
import { preferencesStore } from '../preferences';
|
||||
import { showToast } from './toast';
|
||||
import { showHUD } from './hud';
|
||||
|
@ -56,6 +66,9 @@ export const getRaycastApi = () => {
|
|||
List,
|
||||
Clipboard,
|
||||
environment,
|
||||
getApplications,
|
||||
getDefaultApplication,
|
||||
getFrontmostApplication,
|
||||
getPreferenceValues: () => {
|
||||
if (currentPluginName) {
|
||||
return preferencesStore.getPreferenceValues(currentPluginName, currentPluginPreferences);
|
||||
|
@ -70,8 +83,10 @@ export const getRaycastApi = () => {
|
|||
getSelectedFinderItems,
|
||||
getSelectedText,
|
||||
open,
|
||||
showInFinder,
|
||||
showToast,
|
||||
showHUD,
|
||||
trash,
|
||||
useNavigation,
|
||||
usePersistentState: <T>(
|
||||
key: string,
|
||||
|
|
|
@ -5,11 +5,7 @@ import { instances, navigationStack, toasts, browserExtensionState } from './sta
|
|||
import { batchedUpdates, updateContainer } from './reconciler';
|
||||
import { preferencesStore } from './preferences';
|
||||
import type { RaycastInstance } from './types';
|
||||
import {
|
||||
handleGetSelectedFinderItemsResponse,
|
||||
handleSelectedTextResponse,
|
||||
type FileSystemItem
|
||||
} from './api/environment';
|
||||
import { handleSystemResponse } from './api/environment';
|
||||
import { handleBrowserExtensionResponse } from './api/browserExtension';
|
||||
import { handleClipboardResponse } from './api/clipboard';
|
||||
import { handleOAuthResponse, handleTokenResponse } from './api/oauth';
|
||||
|
@ -27,6 +23,16 @@ rl.on('line', (line) => {
|
|||
try {
|
||||
const command: { action: string; payload: unknown } = JSON.parse(line);
|
||||
|
||||
if (command.action.startsWith('system-') && command.action.endsWith('-response')) {
|
||||
const { requestId, result, error } = command.payload as {
|
||||
requestId: string;
|
||||
result?: unknown;
|
||||
error?: string;
|
||||
};
|
||||
handleSystemResponse(requestId, result, error);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command.action) {
|
||||
case 'request-plugin-list':
|
||||
sendPluginList();
|
||||
|
@ -120,24 +126,6 @@ rl.on('line', (line) => {
|
|||
toast?.hide();
|
||||
break;
|
||||
}
|
||||
case 'selected-text-response': {
|
||||
const { requestId, text, error } = command.payload as {
|
||||
requestId: string;
|
||||
text?: string | null;
|
||||
error?: string;
|
||||
};
|
||||
handleSelectedTextResponse(requestId, text ?? null, error);
|
||||
break;
|
||||
}
|
||||
case 'selected-finder-items-response': {
|
||||
const { requestId, items, error } = command.payload as {
|
||||
requestId: string;
|
||||
items?: FileSystemItem[] | null;
|
||||
error?: string;
|
||||
};
|
||||
handleGetSelectedFinderItemsResponse(requestId, items ?? null, error);
|
||||
break;
|
||||
}
|
||||
case 'browser-extension-response': {
|
||||
const { requestId, result, error } = command.payload as {
|
||||
requestId: string;
|
||||
|
@ -195,6 +183,8 @@ rl.on('line', (line) => {
|
|||
: { message: String(err) };
|
||||
writeLog(`ERROR: ${error.message} \n ${error.stack ?? ''}`);
|
||||
writeOutput({ type: 'error', payload: error.message });
|
||||
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
25
src-tauri/Cargo.lock
generated
25
src-tauri/Cargo.lock
generated
|
@ -4357,6 +4357,7 @@ dependencies = [
|
|||
"tauri-plugin-single-instance",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"trash",
|
||||
"url",
|
||||
"uuid",
|
||||
"zbus",
|
||||
|
@ -6046,6 +6047,24 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trash"
|
||||
version = "5.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22746c6b0c6d85d60a8f0d858f7057dfdf11297c132679f452ec908fba42b871"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"libc",
|
||||
"log",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"scopeguard",
|
||||
"urlencoding",
|
||||
"windows 0.56.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tray-icon"
|
||||
version = "0.20.1"
|
||||
|
@ -6220,6 +6239,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "urlpattern"
|
||||
version = "0.3.0"
|
||||
|
|
|
@ -51,6 +51,7 @@ image = "0.25.6"
|
|||
regex = "1.11.1"
|
||||
rand = "0.9.1"
|
||||
tauri-plugin-http = "2"
|
||||
trash = "5.2.2"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-global-shortcut = "2"
|
||||
|
|
|
@ -9,6 +9,7 @@ mod extensions;
|
|||
mod filesystem;
|
||||
mod oauth;
|
||||
mod quicklinks;
|
||||
mod system;
|
||||
|
||||
use crate::{app::App, cache::AppCache};
|
||||
use browser_extension::WsState;
|
||||
|
@ -191,7 +192,12 @@ pub fn run() {
|
|||
quicklinks::list_quicklinks,
|
||||
quicklinks::update_quicklink,
|
||||
quicklinks::delete_quicklink,
|
||||
quicklinks::execute_quicklink
|
||||
quicklinks::execute_quicklink,
|
||||
system::get_applications,
|
||||
system::get_default_application,
|
||||
system::get_frontmost_application,
|
||||
system::show_in_finder,
|
||||
system::trash
|
||||
])
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle().clone();
|
||||
|
@ -211,4 +217,4 @@ pub fn run() {
|
|||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
}
|
168
src-tauri/src/system.rs
Normal file
168
src-tauri/src/system.rs
Normal file
|
@ -0,0 +1,168 @@
|
|||
use std::process::Command;
|
||||
|
||||
#[derive(serde::Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Application {
|
||||
name: String,
|
||||
path: String,
|
||||
bundle_id: Option<String>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn trash(paths: Vec<String>) -> Result<(), String> {
|
||||
trash::delete_all(paths).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn show_in_finder(path: String) -> Result<(), String> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
Command::new("explorer")
|
||||
.args(["/select,", &path])
|
||||
.spawn()
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
Command::new("open")
|
||||
.args(["-R", &path])
|
||||
.spawn()
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let path = std::path::Path::new(&path);
|
||||
let parent = path.parent().unwrap_or(path).as_os_str();
|
||||
Command::new("xdg-open")
|
||||
.arg(parent)
|
||||
.spawn()
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_applications(_path: Option<String>) -> Result<Vec<Application>, String> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let script = r#"
|
||||
set output to ""
|
||||
set app_paths to paragraphs of (do shell script "mdfind 'kMDItemContentType == \"com.apple.application-bundle\"' -onlyin /Applications -onlyin /System/Applications -onlyin ~/Applications")
|
||||
repeat with app_path in app_paths
|
||||
if app_path is not "" then
|
||||
try
|
||||
set app_info to info for (app_path as POSIX file)
|
||||
set app_name to name of app_info
|
||||
set bundle_id to bundle identifier of app_info
|
||||
set output to output & app_name & "%%" & app_path & "%%" & bundle_id & "\n"
|
||||
on error
|
||||
-- ignore apps we can't get info for
|
||||
end try
|
||||
end if
|
||||
end repeat
|
||||
return output
|
||||
"#;
|
||||
let output = Command::new("osascript")
|
||||
.arg("-e")
|
||||
.arg(script)
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let result_str = String::from_utf8_lossy(&output.stdout);
|
||||
let apps = result_str
|
||||
.lines()
|
||||
.filter_map(|line| {
|
||||
let parts: Vec<&str> = line.split("%%").collect();
|
||||
if parts.len() == 3 {
|
||||
Some(Application {
|
||||
name: parts[0].to_string(),
|
||||
path: parts[1].to_string(),
|
||||
bundle_id: Some(parts[2].to_string()),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(apps)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
Ok(crate::get_installed_apps().into_iter().map(|app| Application {
|
||||
name: app.name,
|
||||
path: app.exec.unwrap_or_default(),
|
||||
bundle_id: None,
|
||||
}).collect())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use winreg::enums::*;
|
||||
use winreg::RegKey;
|
||||
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
||||
let uninstall = hklm.open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall").map_err(|e| e.to_string())?;
|
||||
let mut apps = Vec::new();
|
||||
|
||||
for key in uninstall.enum_keys().filter_map(Result::ok) {
|
||||
if let Ok(subkey) = uninstall.open_subkey(key) {
|
||||
if let (Ok(name), Ok(path)) = (subkey.get_value("DisplayName"), subkey.get_value("InstallLocation")) {
|
||||
let name_str: String = name;
|
||||
let path_str: String = path;
|
||||
if !name_str.is_empty() && !path_str.is_empty() {
|
||||
apps.push(Application { name: name_str, path: path_str, bundle_id: None });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(apps)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_default_application(path: String) -> Result<Application, String> {
|
||||
Err(format!("get_default_application for '{}' is not yet implemented for this platform.", path))
|
||||
}
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_frontmost_application() -> Result<Application, String> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let script = r#"
|
||||
tell application "System Events"
|
||||
set front_app to first application process whose frontmost is true
|
||||
set app_path to (path of application file of front_app)
|
||||
set app_name to (name of front_app)
|
||||
set bundle_id to (bundle identifier of front_app)
|
||||
return app_name & "%%" & app_path & "%%" & bundle_id
|
||||
end tell
|
||||
"#;
|
||||
let output = Command::new("osascript")
|
||||
.arg("-e")
|
||||
.arg(script)
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let result_str = String::from_utf8_lossy(&output.stdout);
|
||||
let parts: Vec<&str> = result_str.trim().split("%%").collect();
|
||||
if parts.len() == 3 {
|
||||
Ok(Application {
|
||||
name: parts[0].to_string(),
|
||||
path: parts[1].to_string(),
|
||||
bundle_id: Some(parts[2].to_string()),
|
||||
})
|
||||
} else {
|
||||
Err("Could not determine frontmost application".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||
{
|
||||
Err("get_frontmost_application is not yet implemented for this platform.".to_string())
|
||||
}
|
||||
}
|
|
@ -166,6 +166,24 @@ class SidecarService {
|
|||
return;
|
||||
}
|
||||
|
||||
if (typedMessage.type.startsWith('system-')) {
|
||||
const { requestId, ...params } = typedMessage.payload as {
|
||||
requestId: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
const command = typedMessage.type.replace('system-', '').replace(/-/g, '_');
|
||||
const responseType = `${typedMessage.type}-response`;
|
||||
try {
|
||||
const result = await invoke(command, params);
|
||||
this.dispatchEvent(responseType, { requestId, result });
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
this.#log(`ERROR from ${command}: ${errorMessage}`);
|
||||
this.dispatchEvent(responseType, { requestId, error: errorMessage });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typedMessage.type.startsWith('clipboard-')) {
|
||||
const { requestId, ...params } = typedMessage.payload as {
|
||||
requestId: string;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue