mirror of
https://github.com/ByteAtATime/raycast-linux.git
synced 2025-08-31 11:17:27 +00:00
feat: implement Clipboard API
This commit is contained in:
parent
a598ccf50b
commit
3c3566136a
9 changed files with 461 additions and 97 deletions
|
@ -194,6 +194,61 @@ const BrowserExtensionRequestMessageSchema = z.object({
|
|||
payload: BrowserExtensionRequestPayloadSchema
|
||||
});
|
||||
|
||||
const ClipboardContentSchema = z.object({
|
||||
text: z.string().optional(),
|
||||
html: z.string().optional(),
|
||||
file: z.string().optional()
|
||||
});
|
||||
|
||||
const CopyOptionsSchema = z.object({
|
||||
concealed: z.boolean().optional()
|
||||
});
|
||||
|
||||
const ClipboardCopyPayloadSchema = z.object({
|
||||
requestId: z.string(),
|
||||
content: ClipboardContentSchema,
|
||||
options: CopyOptionsSchema.optional()
|
||||
});
|
||||
const ClipboardCopyMessageSchema = z.object({
|
||||
type: z.literal('clipboard-copy'),
|
||||
payload: ClipboardCopyPayloadSchema
|
||||
});
|
||||
|
||||
const ClipboardPastePayloadSchema = z.object({
|
||||
requestId: z.string(),
|
||||
content: ClipboardContentSchema
|
||||
});
|
||||
const ClipboardPasteMessageSchema = z.object({
|
||||
type: z.literal('clipboard-paste'),
|
||||
payload: ClipboardPastePayloadSchema
|
||||
});
|
||||
|
||||
const ClipboardReadPayloadSchema = z.object({
|
||||
requestId: z.string(),
|
||||
offset: z.number().optional()
|
||||
});
|
||||
const ClipboardReadMessageSchema = z.object({
|
||||
type: z.literal('clipboard-read'),
|
||||
payload: ClipboardReadPayloadSchema
|
||||
});
|
||||
|
||||
const ClipboardReadTextPayloadSchema = z.object({
|
||||
requestId: z.string(),
|
||||
offset: z.number().optional()
|
||||
});
|
||||
const ClipboardReadTextMessageSchema = z.object({
|
||||
type: z.literal('clipboard-read-text'),
|
||||
payload: ClipboardReadTextPayloadSchema
|
||||
});
|
||||
|
||||
const ClipboardClearPayloadSchema = z.object({
|
||||
requestId: z.string()
|
||||
});
|
||||
const ClipboardClearMessageSchema = z.object({
|
||||
type: z.literal('clipboard-clear'),
|
||||
payload: ClipboardClearPayloadSchema
|
||||
});
|
||||
|
||||
export const SidecarMessageWithPluginsSchema = z.union([
|
||||
BatchUpdateSchema,
|
||||
CommandSchema,
|
||||
|
@ -204,6 +259,11 @@ export const SidecarMessageWithPluginsSchema = z.union([
|
|||
OpenMessageSchema,
|
||||
GetSelectedTextMessageSchema,
|
||||
GetSelectedFinderItemsMessageSchema,
|
||||
BrowserExtensionRequestMessageSchema
|
||||
BrowserExtensionRequestMessageSchema,
|
||||
ClipboardCopyMessageSchema,
|
||||
ClipboardPasteMessageSchema,
|
||||
ClipboardReadMessageSchema,
|
||||
ClipboardReadTextMessageSchema,
|
||||
ClipboardClearMessageSchema
|
||||
]);
|
||||
export type SidecarMessageWithPlugins = z.infer<typeof SidecarMessageWithPluginsSchema>;
|
||||
|
|
79
sidecar/src/api/clipboard.ts
Normal file
79
sidecar/src/api/clipboard.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { writeOutput } from '../io';
|
||||
import * as crypto from 'crypto';
|
||||
import type * as api from '@raycast/api';
|
||||
|
||||
type ClipboardContent = {
|
||||
text?: string;
|
||||
html?: string;
|
||||
file?: string;
|
||||
};
|
||||
|
||||
type ReadResult = {
|
||||
text?: string;
|
||||
html?: string;
|
||||
file?: string;
|
||||
};
|
||||
|
||||
const pendingRequests = new Map<
|
||||
string,
|
||||
{ resolve: (value: any) => void; reject: (reason?: any) => void }
|
||||
>();
|
||||
|
||||
function sendRequest<T>(type: string, payload: object): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestId = crypto.randomUUID();
|
||||
pendingRequests.set(requestId, { resolve, reject });
|
||||
writeOutput({
|
||||
type,
|
||||
payload: { requestId, ...payload }
|
||||
});
|
||||
setTimeout(() => {
|
||||
if (pendingRequests.has(requestId)) {
|
||||
pendingRequests.delete(requestId);
|
||||
reject(new Error(`Request for ${type} timed out`));
|
||||
}
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
|
||||
export function handleClipboardResponse(requestId: string, result: any, error?: string) {
|
||||
const promise = pendingRequests.get(requestId);
|
||||
if (promise) {
|
||||
if (error) {
|
||||
promise.reject(new Error(error));
|
||||
} else {
|
||||
promise.resolve(result);
|
||||
}
|
||||
pendingRequests.delete(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeContent(content: string | number | api.Clipboard.Content): ClipboardContent {
|
||||
if (typeof content === 'string' || typeof content === 'number') {
|
||||
return { text: String(content) };
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
export const Clipboard: typeof api.Clipboard = {
|
||||
async copy(content, options) {
|
||||
const normalized = normalizeContent(content);
|
||||
return sendRequest<void>('clipboard-copy', { content: normalized, options });
|
||||
},
|
||||
async paste(content) {
|
||||
const normalized = normalizeContent(content);
|
||||
return sendRequest<void>('clipboard-paste', { content: normalized });
|
||||
},
|
||||
async clear() {
|
||||
return sendRequest<void>('clipboard-clear', {});
|
||||
},
|
||||
async read(options) {
|
||||
return sendRequest<ReadResult>('clipboard-read', { offset: options?.offset });
|
||||
},
|
||||
async readText(options) {
|
||||
const result = await sendRequest<ReadResult>('clipboard-read-text', {
|
||||
offset: options?.offset
|
||||
});
|
||||
return result.text;
|
||||
}
|
||||
};
|
|
@ -14,6 +14,7 @@ import { environment, getSelectedFinderItems, getSelectedText, open } from './en
|
|||
import { preferencesStore } from '../preferences';
|
||||
import { showToast } from './toast';
|
||||
import { BrowserExtensionAPI } from './browserExtension';
|
||||
import { Clipboard } from './clipboard';
|
||||
|
||||
let currentPluginName: string | null = null;
|
||||
let currentPluginPreferences: Array<{
|
||||
|
@ -50,6 +51,7 @@ export const getRaycastApi = () => {
|
|||
Form,
|
||||
Grid,
|
||||
List,
|
||||
Clipboard,
|
||||
environment,
|
||||
getPreferenceValues: () => {
|
||||
if (currentPluginName) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
type FileSystemItem
|
||||
} from './api/environment';
|
||||
import { handleBrowserExtensionResponse } from './api/browserExtension';
|
||||
import { handleClipboardResponse } from './api/clipboard';
|
||||
|
||||
process.on('unhandledRejection', (reason: unknown) => {
|
||||
writeLog(`--- UNHANDLED PROMISE REJECTION ---`);
|
||||
|
@ -150,6 +151,19 @@ rl.on('line', (line) => {
|
|||
browserExtensionState.isConnected = isConnected;
|
||||
break;
|
||||
}
|
||||
case 'clipboard-read-text-response':
|
||||
case 'clipboard-read-response':
|
||||
case 'clipboard-copy-response':
|
||||
case 'clipboard-paste-response':
|
||||
case 'clipboard-clear-response': {
|
||||
const { requestId, result, error } = command.payload as {
|
||||
requestId: string;
|
||||
result?: any;
|
||||
error?: string;
|
||||
};
|
||||
handleClipboardResponse(requestId, result, error);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
writeLog(`Unknown command action: ${command.action}`);
|
||||
}
|
||||
|
|
72
src-tauri/Cargo.lock
generated
72
src-tauri/Cargo.lock
generated
|
@ -785,6 +785,19 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics-types 0.2.0",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics-types"
|
||||
version = "0.1.3"
|
||||
|
@ -1197,7 +1210,28 @@ dependencies = [
|
|||
"log",
|
||||
"objc2 0.5.2",
|
||||
"windows 0.56.0",
|
||||
"xkbcommon",
|
||||
"xkbcommon 0.7.0",
|
||||
"xkeysym",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enigo"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71744ff36f35a4276e8827add8102d0e792378c574fd93cb4e1c8e0505f96b7c"
|
||||
dependencies = [
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics 0.25.0",
|
||||
"foreign-types-shared 0.3.1",
|
||||
"libc",
|
||||
"log",
|
||||
"nom 8.0.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.1",
|
||||
"windows 0.61.3",
|
||||
"x11rb",
|
||||
"xkbcommon 0.8.0",
|
||||
"xkeysym",
|
||||
]
|
||||
|
||||
|
@ -2583,6 +2617,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
|
@ -2740,6 +2783,15 @@ dependencies = [
|
|||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
|
@ -3630,6 +3682,7 @@ dependencies = [
|
|||
"arboard",
|
||||
"bincode",
|
||||
"bytes",
|
||||
"enigo 0.5.0",
|
||||
"freedesktop-file-parser",
|
||||
"futures-util",
|
||||
"rayon",
|
||||
|
@ -3991,7 +4044,7 @@ dependencies = [
|
|||
"accessibility-sys-ng",
|
||||
"arboard",
|
||||
"core-foundation 0.9.4",
|
||||
"enigo",
|
||||
"enigo 0.2.1",
|
||||
"log",
|
||||
"windows 0.56.0",
|
||||
"wl-clipboard-rs 0.8.1",
|
||||
|
@ -5208,7 +5261,7 @@ checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63"
|
|||
dependencies = [
|
||||
"fnv",
|
||||
"memchr",
|
||||
"nom",
|
||||
"nom 7.1.3",
|
||||
"once_cell",
|
||||
"petgraph",
|
||||
]
|
||||
|
@ -6435,7 +6488,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"memmap2",
|
||||
"memmap2 0.8.0",
|
||||
"xkeysym",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xkbcommon"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"memmap2 0.9.5",
|
||||
"xkeysym",
|
||||
]
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ tokio-tungstenite = "^0.27"
|
|||
futures-util = "^0.3.31"
|
||||
tokio = { version = "^1.45.1", features = ["full"] }
|
||||
uuid = { version = "^1.17.0", features = ["v4", "serde"] }
|
||||
enigo = "0.5.0"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-global-shortcut = "2"
|
||||
|
|
123
src-tauri/src/clipboard.rs
Normal file
123
src-tauri/src/clipboard.rs
Normal file
|
@ -0,0 +1,123 @@
|
|||
use enigo::{Enigo, Key, Keyboard, Settings};
|
||||
use std::{thread, time::Duration};
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ReadResult {
|
||||
text: Option<String>,
|
||||
html: Option<String>,
|
||||
file: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClipboardContent {
|
||||
text: Option<String>,
|
||||
html: Option<String>,
|
||||
file: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Default, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CopyOptions {
|
||||
concealed: Option<bool>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn clipboard_read_text(app: tauri::AppHandle) -> Result<ReadResult, String> {
|
||||
let clipboard = app.clipboard();
|
||||
let text = clipboard.read_text().ok();
|
||||
Ok(ReadResult {
|
||||
text,
|
||||
html: None,
|
||||
file: None
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn clipboard_read(app: tauri::AppHandle) -> Result<ReadResult, String> {
|
||||
let clipboard = app.clipboard();
|
||||
let text = clipboard.read_text().ok();
|
||||
let html = None; // read_html is not supported by the plugin
|
||||
|
||||
let file = if let Some(ref text_content) = text {
|
||||
if text_content.lines().count() == 1
|
||||
&& (text_content.starts_with('/') || text_content.starts_with("file://"))
|
||||
{
|
||||
Some(text_content.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(ReadResult { text, html, file })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn clipboard_copy(
|
||||
app: tauri::AppHandle,
|
||||
content: ClipboardContent,
|
||||
_options: Option<CopyOptions>
|
||||
) -> Result<(), String> {
|
||||
let clipboard = app.clipboard();
|
||||
|
||||
if let Some(file_path) = &content.file {
|
||||
clipboard
|
||||
.write_text(file_path.clone())
|
||||
.map_err(|e| e.to_string())?;
|
||||
} else if let Some(html) = &content.html {
|
||||
clipboard
|
||||
.write_html(html.clone(), content.text)
|
||||
.map_err(|e| e.to_string())?;
|
||||
} else if let Some(text) = &content.text {
|
||||
clipboard.write_text(text.clone()).map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn clipboard_paste(
|
||||
app: tauri::AppHandle,
|
||||
content: ClipboardContent
|
||||
) -> Result<(), String> {
|
||||
let clipboard = app.clipboard();
|
||||
let original_text = clipboard.read_text().ok();
|
||||
|
||||
clipboard_copy(app.clone(), content, None).await?;
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
let mut enigo = Enigo::new(&Settings::default()).map_err(|e| e.to_string())?;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
enigo.key(Key::Meta, enigo::Direction::Press).ok();
|
||||
enigo.key(Key::Unicode('v'), enigo::Direction::Click).ok();
|
||||
enigo.key(Key::Meta, enigo::Direction::Release).ok();
|
||||
}
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
enigo.key(Key::Control, enigo::Direction::Press).ok();
|
||||
enigo.key(Key::Unicode('v'), enigo::Direction::Click).ok();
|
||||
enigo.key(Key::Control, enigo::Direction::Release).ok();
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
if let Some(text) = original_text {
|
||||
clipboard.write_text(text).map_err(|e| e.to_string())?;
|
||||
} else {
|
||||
clipboard.clear().map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn clipboard_clear(app: tauri::AppHandle) -> Result<(), String> {
|
||||
app.clipboard().clear().map_err(|e| e.to_string())
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
mod app;
|
||||
mod browser_extension;
|
||||
mod cache;
|
||||
mod clipboard;
|
||||
mod desktop;
|
||||
mod error;
|
||||
mod extensions;
|
||||
|
@ -16,122 +17,124 @@ use tauri::Manager;
|
|||
|
||||
#[tauri::command]
|
||||
fn get_installed_apps() -> Vec<App> {
|
||||
match AppCache::get_apps() {
|
||||
Ok(apps) => apps,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to get apps: {:?}", e);
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
match AppCache::get_apps() {
|
||||
Ok(apps) => apps,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to get apps: {:?}", e);
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn launch_app(exec: String) -> Result<(), String> {
|
||||
let exec_parts: Vec<&str> = exec.split_whitespace().collect();
|
||||
if exec_parts.is_empty() {
|
||||
return Err("Empty exec command".to_string());
|
||||
}
|
||||
let exec_parts: Vec<&str> = exec.split_whitespace().collect();
|
||||
if exec_parts.is_empty() {
|
||||
return Err("Empty exec command".to_string());
|
||||
}
|
||||
|
||||
let mut command = Command::new(exec_parts[0]);
|
||||
for arg in &exec_parts[1..] {
|
||||
if !arg.starts_with('%') {
|
||||
command.arg(arg);
|
||||
}
|
||||
}
|
||||
let mut command = Command::new(exec_parts[0]);
|
||||
for arg in &exec_parts[1..] {
|
||||
if !arg.starts_with('%') {
|
||||
command.arg(arg);
|
||||
}
|
||||
}
|
||||
|
||||
command
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to launch app: {}", e))?;
|
||||
command
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to launch app: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_selected_text() -> String {
|
||||
get_text()
|
||||
get_text()
|
||||
}
|
||||
|
||||
fn setup_background_refresh() {
|
||||
thread::spawn(|| {
|
||||
thread::sleep(Duration::from_secs(60));
|
||||
loop {
|
||||
AppCache::refresh_background();
|
||||
thread::sleep(Duration::from_secs(300));
|
||||
}
|
||||
});
|
||||
thread::spawn(|| {
|
||||
thread::sleep(Duration::from_secs(60));
|
||||
loop {
|
||||
AppCache::refresh_background();
|
||||
thread::sleep(Duration::from_secs(300));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn setup_global_shortcut(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use tauri_plugin_global_shortcut::{
|
||||
Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState,
|
||||
};
|
||||
use tauri_plugin_global_shortcut::{
|
||||
Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState
|
||||
};
|
||||
|
||||
let spotlight_shortcut = Shortcut::new(Some(Modifiers::ALT), Code::Space);
|
||||
let handle = app.handle().clone();
|
||||
let spotlight_shortcut = Shortcut::new(Some(Modifiers::ALT), Code::Space);
|
||||
let handle = app.handle().clone();
|
||||
|
||||
println!("Spotlight shortcut: {:?}", spotlight_shortcut);
|
||||
println!("Spotlight shortcut: {:?}", spotlight_shortcut);
|
||||
|
||||
app.handle().plugin(
|
||||
tauri_plugin_global_shortcut::Builder::new()
|
||||
.with_handler(move |_app, shortcut, event| {
|
||||
println!("Shortcut: {:?}, Event: {:?}", shortcut, event);
|
||||
if shortcut == &spotlight_shortcut
|
||||
&& event.state() == ShortcutState::Pressed
|
||||
{
|
||||
let spotlight_window =
|
||||
handle.get_webview_window("raycast-linux").unwrap();
|
||||
println!("Spotlight window: {:?}", spotlight_window);
|
||||
if spotlight_window.is_visible().unwrap_or(false) {
|
||||
spotlight_window.hide().unwrap();
|
||||
} else {
|
||||
spotlight_window.show().unwrap();
|
||||
spotlight_window.set_focus().unwrap();
|
||||
}
|
||||
}
|
||||
})
|
||||
.build(),
|
||||
)?;
|
||||
app.handle().plugin(
|
||||
tauri_plugin_global_shortcut::Builder::new()
|
||||
.with_handler(move |_app, shortcut, event| {
|
||||
println!("Shortcut: {:?}, Event: {:?}", shortcut, event);
|
||||
if shortcut == &spotlight_shortcut && event.state() == ShortcutState::Pressed {
|
||||
let spotlight_window = handle.get_webview_window("raycast-linux").unwrap();
|
||||
println!("Spotlight window: {:?}", spotlight_window);
|
||||
if spotlight_window.is_visible().unwrap_or(false) {
|
||||
spotlight_window.hide().unwrap();
|
||||
} else {
|
||||
spotlight_window.show().unwrap();
|
||||
spotlight_window.set_focus().unwrap();
|
||||
}
|
||||
}
|
||||
})
|
||||
.build()
|
||||
)?;
|
||||
|
||||
app.global_shortcut().register(spotlight_shortcut)?;
|
||||
Ok(())
|
||||
app.global_shortcut().register(spotlight_shortcut)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.manage(WsState::default())
|
||||
.plugin(tauri_plugin_single_instance::init(|app, args, cwd| {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
if let Ok(true) = window.is_visible() {
|
||||
let _ = window.hide();
|
||||
} else {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
get_installed_apps,
|
||||
launch_app,
|
||||
get_selected_text,
|
||||
filesystem::get_selected_finder_items,
|
||||
extensions::install_extension,
|
||||
browser_extension::browser_extension_check_connection,
|
||||
browser_extension::browser_extension_request
|
||||
])
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle().clone();
|
||||
tauri::async_runtime::spawn(browser_extension::run_server(app_handle));
|
||||
tauri::Builder::default()
|
||||
.manage(WsState::default())
|
||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
if let Ok(true) = window.is_visible() {
|
||||
let _ = window.hide();
|
||||
} else {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
get_installed_apps,
|
||||
launch_app,
|
||||
get_selected_text,
|
||||
filesystem::get_selected_finder_items,
|
||||
extensions::install_extension,
|
||||
browser_extension::browser_extension_check_connection,
|
||||
browser_extension::browser_extension_request,
|
||||
clipboard::clipboard_read_text,
|
||||
clipboard::clipboard_read,
|
||||
clipboard::clipboard_copy,
|
||||
clipboard::clipboard_paste,
|
||||
clipboard::clipboard_clear
|
||||
])
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle().clone();
|
||||
tauri::async_runtime::spawn(browser_extension::run_server(app_handle));
|
||||
|
||||
setup_background_refresh();
|
||||
setup_global_shortcut(app)?;
|
||||
setup_background_refresh();
|
||||
setup_global_shortcut(app)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
|
@ -128,7 +128,7 @@ class SidecarService {
|
|||
}
|
||||
};
|
||||
|
||||
#routeMessage = (message: unknown) => {
|
||||
#routeMessage = async (message: unknown) => {
|
||||
const result = SidecarMessageWithPluginsSchema.safeParse(message);
|
||||
|
||||
if (!result.success) {
|
||||
|
@ -153,6 +153,24 @@ class SidecarService {
|
|||
return;
|
||||
}
|
||||
|
||||
if (typedMessage.type.startsWith('clipboard-')) {
|
||||
const { requestId, ...params } = typedMessage.payload as {
|
||||
requestId: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
const command = typedMessage.type.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 === 'plugin-list') {
|
||||
uiStore.setPluginList(typedMessage.payload);
|
||||
return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue