mirror of
https://github.com/project-gauntlet/gauntlet.git
synced 2025-12-23 10:35:53 +00:00
Abstract clipboard inside plugin runtime behind backend api
This commit is contained in:
parent
364beb647b
commit
37d8a218f7
6 changed files with 293 additions and 267 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
use crate::model::{AdditionalSearchItem, ClipboardData, PreferenceUserData};
|
||||
use common::model::{EntrypointId, PhysicalKey};
|
||||
use crate::model::{AdditionalSearchItem, PreferenceUserData};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub trait BackendForPluginRuntimeApi {
|
||||
async fn reload_search_index(&self, generated_commands: Vec<AdditionalSearchItem>, refresh_search_list: bool) -> anyhow::Result<()> ;
|
||||
|
|
@ -19,4 +19,9 @@ pub trait BackendForPluginRuntimeApi {
|
|||
async fn get_entrypoint_preferences(&self, entrypoint_id: EntrypointId) -> anyhow::Result<HashMap<String, PreferenceUserData>>;
|
||||
async fn plugin_preferences_required(&self) -> anyhow::Result<bool>;
|
||||
async fn entrypoint_preferences_required(&self, entrypoint_id: EntrypointId) -> anyhow::Result<bool>;
|
||||
async fn clipboard_read(&self) -> anyhow::Result<ClipboardData>;
|
||||
async fn clipboard_read_text(&self) -> anyhow::Result<Option<String>>;
|
||||
async fn clipboard_write(&self, data: ClipboardData) -> anyhow::Result<()>;
|
||||
async fn clipboard_write_text(&self, data: String) -> anyhow::Result<()>;
|
||||
async fn clipboard_clear(&self) -> anyhow::Result<()>;
|
||||
}
|
||||
|
|
@ -24,4 +24,10 @@ pub enum PreferenceUserData {
|
|||
Bool(bool),
|
||||
ListOfStrings(Vec<String>),
|
||||
ListOfNumbers(Vec<f64>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ClipboardData {
|
||||
pub text_data: Option<String>,
|
||||
pub png_data: Option<Vec<u8>>
|
||||
}
|
||||
147
rust/server/src/plugins/clipboard.rs
Normal file
147
rust/server/src/plugins/clipboard.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
use anyhow::{anyhow, Context, Error};
|
||||
use arboard::ImageData;
|
||||
use image::RgbaImage;
|
||||
use std::io::Cursor;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use common_plugin_runtime::model::ClipboardData;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Clipboard {
|
||||
clipboard: Arc<RwLock<arboard::Clipboard>>,
|
||||
}
|
||||
|
||||
impl Clipboard {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
let clipboard = arboard::Clipboard::new()
|
||||
.context("error while creating clipboard")?;
|
||||
|
||||
Ok(Self {
|
||||
clipboard: Arc::new(RwLock::new(clipboard)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read(&self) -> anyhow::Result<ClipboardData> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
let png_data = match clipboard.get_image() {
|
||||
Ok(data) => {
|
||||
let rgba_image = RgbaImage::from_raw(data.width as u32, data.height as u32, data.bytes.into());
|
||||
let rgba_image = image::DynamicImage::ImageRgba8(rgba_image.unwrap());
|
||||
|
||||
let mut result = Cursor::new(vec![]);
|
||||
|
||||
rgba_image.write_to(&mut result, image::ImageFormat::Png)
|
||||
.expect("should be able to convert to png");
|
||||
|
||||
Some(result.into_inner())
|
||||
},
|
||||
Err(err) => {
|
||||
match err {
|
||||
arboard::Error::ContentNotAvailable => None,
|
||||
err @ _ => {
|
||||
return Err(unknown_err_clipboard(err));
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let text_data = match clipboard.get_text() {
|
||||
Ok(data) => Some(data),
|
||||
Err(err) => {
|
||||
match err {
|
||||
arboard::Error::ContentNotAvailable => None,
|
||||
err @ _ => {
|
||||
return Err(unknown_err_clipboard(err));
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ClipboardData {
|
||||
text_data,
|
||||
png_data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_text(&self) -> anyhow::Result<Option<String>> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
let data = match clipboard.get_text() {
|
||||
Ok(data) => Some(data),
|
||||
Err(err) => {
|
||||
match err {
|
||||
arboard::Error::ContentNotAvailable => None,
|
||||
err @ _ => {
|
||||
return Err(unknown_err_clipboard(err));
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn write(&self, data: ClipboardData) -> anyhow::Result<()> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
if let Some(png_data) = data.png_data {
|
||||
|
||||
let cursor = Cursor::new(&png_data);
|
||||
|
||||
let mut reader = image::io::Reader::new(cursor);
|
||||
reader.set_format(image::ImageFormat::Png);
|
||||
|
||||
let image = reader.decode()
|
||||
.map_err(|_err| unable_to_convert_image_err())?
|
||||
.into_rgba8();
|
||||
|
||||
let (w, h) = image.dimensions();
|
||||
|
||||
let image_data = ImageData {
|
||||
width: w as usize,
|
||||
height: h as usize,
|
||||
bytes: image.into_raw().into()
|
||||
};
|
||||
|
||||
clipboard.set_image(image_data)
|
||||
.map_err(|err| unknown_err_clipboard(err))?;
|
||||
}
|
||||
|
||||
if let Some(text_data) = data.text_data {
|
||||
clipboard.set_text(text_data)
|
||||
.map_err(|err| unknown_err_clipboard(err))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_text(&self, data: String) -> anyhow::Result<()> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
clipboard.set_text(data)
|
||||
.map_err(|err| unknown_err_clipboard(err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear(&self) -> anyhow::Result<()> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
clipboard.clear()
|
||||
.map_err(|err| unknown_err_clipboard(err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn unknown_err_clipboard(err: arboard::Error) -> Error {
|
||||
anyhow!("UNKNOWN_ERROR: {:?}", err)
|
||||
}
|
||||
|
||||
fn unknown_err_image(err: image::ImageError) -> Error {
|
||||
anyhow!("UNKNOWN_ERROR: {:?}", err)
|
||||
}
|
||||
|
||||
fn unable_to_convert_image_err() -> Error {
|
||||
anyhow!("UNABLE_TO_CONVERT_IMAGE")
|
||||
}
|
||||
|
|
@ -1,308 +1,101 @@
|
|||
use std::cell::RefCell;
|
||||
use std::io::Cursor;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
use anyhow::{anyhow, Context, Error};
|
||||
use arboard::ImageData;
|
||||
use crate::plugins::js::BackendForPluginRuntimeApiImpl;
|
||||
use common_plugin_runtime::backend_for_plugin_runtime_api::BackendForPluginRuntimeApi;
|
||||
use common_plugin_runtime::model::ClipboardData;
|
||||
use deno_core::{op2, OpState};
|
||||
use image::RgbaImage;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::task::spawn_blocking;
|
||||
use crate::plugins::js::permissions::PluginPermissionsClipboard;
|
||||
use crate::plugins::js::{clipboard, PluginData};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Clipboard {
|
||||
clipboard: Arc<RwLock<arboard::Clipboard>>,
|
||||
}
|
||||
|
||||
impl Clipboard {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
let clipboard = arboard::Clipboard::new()
|
||||
.context("error while creating clipboard")?;
|
||||
|
||||
Ok(Self {
|
||||
clipboard: Arc::new(RwLock::new(clipboard)),
|
||||
})
|
||||
}
|
||||
|
||||
fn read(&self) -> anyhow::Result<ClipboardData> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
let png_data = match clipboard.get_image() {
|
||||
Ok(data) => {
|
||||
let rgba_image = RgbaImage::from_raw(data.width as u32, data.height as u32, data.bytes.into());
|
||||
let rgba_image = image::DynamicImage::ImageRgba8(rgba_image.unwrap());
|
||||
|
||||
let mut result = Cursor::new(vec![]);
|
||||
|
||||
rgba_image.write_to(&mut result, image::ImageFormat::Png)
|
||||
.expect("should be able to convert to png");
|
||||
|
||||
Some(result.into_inner())
|
||||
},
|
||||
Err(err) => {
|
||||
match err {
|
||||
arboard::Error::ContentNotAvailable => None,
|
||||
err @ _ => {
|
||||
return Err(unknown_err_clipboard(err));
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let text_data = match clipboard.get_text() {
|
||||
Ok(data) => Some(data),
|
||||
Err(err) => {
|
||||
match err {
|
||||
arboard::Error::ContentNotAvailable => None,
|
||||
err @ _ => {
|
||||
return Err(unknown_err_clipboard(err));
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ClipboardData {
|
||||
text_data,
|
||||
png_data,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_text(&self) -> anyhow::Result<Option<String>> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
let data = match clipboard.get_text() {
|
||||
Ok(data) => Some(data),
|
||||
Err(err) => {
|
||||
match err {
|
||||
arboard::Error::ContentNotAvailable => None,
|
||||
err @ _ => {
|
||||
return Err(unknown_err_clipboard(err));
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
fn write(&self, data: ClipboardData) -> anyhow::Result<()> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
if let Some(png_data) = data.png_data {
|
||||
|
||||
let cursor = Cursor::new(&png_data);
|
||||
|
||||
let mut reader = image::io::Reader::new(cursor);
|
||||
reader.set_format(image::ImageFormat::Png);
|
||||
|
||||
let image = reader.decode()
|
||||
.map_err(|_err| unable_to_convert_image_err())?
|
||||
.into_rgba8();
|
||||
|
||||
let (w, h) = image.dimensions();
|
||||
|
||||
let image_data = ImageData {
|
||||
width: w as usize,
|
||||
height: h as usize,
|
||||
bytes: image.into_raw().into()
|
||||
};
|
||||
|
||||
clipboard.set_image(image_data)
|
||||
.map_err(|err| unknown_err_clipboard(err))?;
|
||||
}
|
||||
|
||||
if let Some(text_data) = data.text_data {
|
||||
clipboard.set_text(text_data)
|
||||
.map_err(|err| unknown_err_clipboard(err))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_text(&self, data: String) -> anyhow::Result<()> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
clipboard.set_text(data)
|
||||
.map_err(|err| unknown_err_clipboard(err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear(&self) -> anyhow::Result<()> {
|
||||
let mut clipboard = self.clipboard.write().expect("lock is poisoned");
|
||||
|
||||
clipboard.clear()
|
||||
.map_err(|err| unknown_err_clipboard(err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn unknown_err_clipboard(err: arboard::Error) -> Error {
|
||||
anyhow!("UNKNOWN_ERROR: {:?}", err)
|
||||
}
|
||||
|
||||
fn unknown_err_image(err: image::ImageError) -> Error {
|
||||
anyhow!("UNKNOWN_ERROR: {:?}", err)
|
||||
}
|
||||
|
||||
fn unable_to_convert_image_err() -> Error {
|
||||
anyhow!("UNABLE_TO_CONVERT_IMAGE")
|
||||
}
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ClipboardData {
|
||||
struct JSClipboardData {
|
||||
text_data: Option<String>,
|
||||
png_data: Option<Vec<u8>>
|
||||
}
|
||||
|
||||
#[op2(async)]
|
||||
#[serde]
|
||||
pub async fn clipboard_read(state: Rc<RefCell<OpState>>) -> anyhow::Result<ClipboardData> {
|
||||
let clipboard = {
|
||||
pub async fn clipboard_read(state: Rc<RefCell<OpState>>) -> anyhow::Result<JSClipboardData> {
|
||||
let api = {
|
||||
let state = state.borrow();
|
||||
|
||||
let plugin_data = state
|
||||
.borrow::<PluginData>();
|
||||
|
||||
let allow = plugin_data
|
||||
.permissions()
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Read);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'read' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Reading from clipboard, plugin id: {:?}", plugin_data.plugin_id);
|
||||
|
||||
let clipboard = state
|
||||
.borrow::<Clipboard>()
|
||||
let api = state
|
||||
.borrow::<BackendForPluginRuntimeApiImpl>()
|
||||
.clone();
|
||||
|
||||
clipboard
|
||||
api
|
||||
};
|
||||
|
||||
spawn_blocking(move || clipboard.read()).await?
|
||||
let result = api.clipboard_read().await?;
|
||||
|
||||
Ok(JSClipboardData {
|
||||
text_data: result.text_data,
|
||||
png_data: result.png_data,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
#[op2(async)]
|
||||
#[string]
|
||||
pub async fn clipboard_read_text(state: Rc<RefCell<OpState>>) -> anyhow::Result<Option<String>> {
|
||||
let clipboard = {
|
||||
let api = {
|
||||
let state = state.borrow();
|
||||
|
||||
let plugin_data = state
|
||||
.borrow::<PluginData>();
|
||||
|
||||
let allow = plugin_data
|
||||
.permissions()
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Read);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'read' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Reading text from clipboard, plugin id: {:?}", plugin_data.plugin_id);
|
||||
|
||||
let clipboard = state
|
||||
.borrow::<Clipboard>()
|
||||
let api = state
|
||||
.borrow::<BackendForPluginRuntimeApiImpl>()
|
||||
.clone();
|
||||
|
||||
clipboard
|
||||
api
|
||||
};
|
||||
|
||||
spawn_blocking(move || clipboard.read_text()).await?
|
||||
api.clipboard_read_text().await
|
||||
}
|
||||
|
||||
#[op2(async)]
|
||||
pub async fn clipboard_write(state: Rc<RefCell<OpState>>, #[serde] data: ClipboardData) -> anyhow::Result<()> { // TODO deserialization broken, fix when migrating to deno's op2
|
||||
let clipboard = {
|
||||
pub async fn clipboard_write(state: Rc<RefCell<OpState>>, #[serde] data: JSClipboardData) -> anyhow::Result<()> { // TODO deserialization broken, fix when migrating to deno's op2
|
||||
let api = {
|
||||
let state = state.borrow();
|
||||
|
||||
let plugin_data = state
|
||||
.borrow::<PluginData>();
|
||||
|
||||
let allow = plugin_data
|
||||
.permissions()
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Write);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'write' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Writing to clipboard, plugin id: {:?}", plugin_data.plugin_id);
|
||||
|
||||
let clipboard = state
|
||||
.borrow::<Clipboard>()
|
||||
let api = state
|
||||
.borrow::<BackendForPluginRuntimeApiImpl>()
|
||||
.clone();
|
||||
|
||||
clipboard
|
||||
api
|
||||
};
|
||||
|
||||
spawn_blocking(move || clipboard.write(data)).await?
|
||||
let clipboard_data = ClipboardData {
|
||||
text_data: data.text_data,
|
||||
png_data: data.png_data,
|
||||
};
|
||||
|
||||
api.clipboard_write(clipboard_data).await
|
||||
}
|
||||
|
||||
#[op2(async)]
|
||||
pub async fn clipboard_write_text(state: Rc<RefCell<OpState>>, #[string] data: String) -> anyhow::Result<()> {
|
||||
let clipboard = {
|
||||
let api = {
|
||||
let state = state.borrow();
|
||||
|
||||
let plugin_data = state
|
||||
.borrow::<PluginData>();
|
||||
|
||||
let allow = plugin_data
|
||||
.permissions()
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Write);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'write' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Writing text to clipboard, plugin id: {:?}", plugin_data.plugin_id);
|
||||
|
||||
let clipboard = state
|
||||
.borrow::<Clipboard>()
|
||||
let api = state
|
||||
.borrow::<BackendForPluginRuntimeApiImpl>()
|
||||
.clone();
|
||||
|
||||
clipboard
|
||||
api
|
||||
};
|
||||
|
||||
spawn_blocking(move || clipboard.write_text(data)).await?
|
||||
api.clipboard_write_text(data).await
|
||||
}
|
||||
|
||||
#[op2(async)]
|
||||
pub async fn clipboard_clear(state: Rc<RefCell<OpState>>) -> anyhow::Result<()> {
|
||||
let clipboard = {
|
||||
let api = {
|
||||
let state = state.borrow();
|
||||
|
||||
let plugin_data = state
|
||||
.borrow::<PluginData>();
|
||||
|
||||
let allow = plugin_data
|
||||
.permissions()
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Clear);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'clear' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Clearing clipboard, plugin id: {:?}", plugin_data.plugin_id);
|
||||
|
||||
let clipboard = state
|
||||
.borrow::<Clipboard>()
|
||||
let api = state
|
||||
.borrow::<BackendForPluginRuntimeApiImpl>()
|
||||
.clone();
|
||||
|
||||
clipboard
|
||||
api
|
||||
};
|
||||
|
||||
spawn_blocking(move || clipboard.clear()).await?
|
||||
api.clipboard_clear().await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,20 +27,22 @@ use once_cell::sync::Lazy;
|
|||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::task::spawn_blocking;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use common::dirs::Dirs;
|
||||
use common::model::{EntrypointId, KeyboardEventOrigin, PhysicalKey, PluginId, SearchResultEntrypointType, UiPropertyValue, UiRenderLocation, UiWidgetId};
|
||||
use common::rpc::frontend_api::FrontendApi;
|
||||
use common_plugin_runtime::backend_for_plugin_runtime_api::BackendForPluginRuntimeApi;
|
||||
use common_plugin_runtime::model::{AdditionalSearchItem, PreferenceUserData};
|
||||
use common_plugin_runtime::model::{AdditionalSearchItem, ClipboardData, PreferenceUserData};
|
||||
use component_model::{create_component_model, Children, Component, Property, PropertyType, SharedType};
|
||||
|
||||
use crate::model::{IntermediateUiEvent, JsKeyboardEventOrigin, JsUiEvent, JsUiPropertyValue, JsUiRenderLocation, JsUiRequestData, JsUiResponseData};
|
||||
use crate::plugins::clipboard::Clipboard;
|
||||
use crate::plugins::data_db_repository::{db_entrypoint_from_str, DataDbRepository, DbPluginClipboardPermissions, DbPluginEntrypointType, DbPluginPreference, DbPluginPreferenceUserData, DbReadPlugin, DbReadPluginEntrypoint};
|
||||
use crate::plugins::icon_cache::IconCache;
|
||||
use crate::plugins::js::assets::{asset_data, asset_data_blocking};
|
||||
use crate::plugins::js::clipboard::{clipboard_clear, clipboard_read, clipboard_read_text, clipboard_write, clipboard_write_text, Clipboard};
|
||||
use crate::plugins::js::clipboard::{clipboard_clear, clipboard_read, clipboard_read_text, clipboard_write, clipboard_write_text};
|
||||
use crate::plugins::js::command_generators::{get_command_generator_entrypoint_ids};
|
||||
use crate::plugins::js::environment::{environment_gauntlet_version, environment_is_development, environment_plugin_cache_dir, environment_plugin_data_dir};
|
||||
use crate::plugins::js::logs::{op_log_debug, op_log_error, op_log_info, op_log_trace, op_log_warn};
|
||||
|
|
@ -357,7 +359,6 @@ async fn start_js_runtime(
|
|||
plugin_cache_dir,
|
||||
plugin_data_dir,
|
||||
inline_view_entrypoint_id,
|
||||
runtime_permissions,
|
||||
),
|
||||
frontend_api,
|
||||
ComponentModel::new(component_model),
|
||||
|
|
@ -365,11 +366,12 @@ async fn start_js_runtime(
|
|||
icon_cache,
|
||||
repository,
|
||||
search_index,
|
||||
clipboard,
|
||||
plugin_uuid,
|
||||
plugin_id
|
||||
plugin_id,
|
||||
runtime_permissions,
|
||||
),
|
||||
numbat_context,
|
||||
clipboard,
|
||||
),
|
||||
gauntlet_esm,
|
||||
];
|
||||
|
|
@ -601,7 +603,6 @@ deno_core::extension!(
|
|||
component_model: ComponentModel,
|
||||
backend_api: BackendForPluginRuntimeApiImpl,
|
||||
numbat_context: Option<NumbatContext>,
|
||||
clipboard: Clipboard,
|
||||
},
|
||||
state = |state, options| {
|
||||
state.put(options.event_receiver);
|
||||
|
|
@ -610,7 +611,6 @@ deno_core::extension!(
|
|||
state.put(options.component_model);
|
||||
state.put(options.backend_api);
|
||||
state.put(options.numbat_context);
|
||||
state.put(options.clipboard);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -882,7 +882,6 @@ pub struct PluginData {
|
|||
plugin_cache_dir: String,
|
||||
plugin_data_dir: String,
|
||||
inline_view_entrypoint_id: Option<String>,
|
||||
permissions: PluginRuntimePermissions
|
||||
}
|
||||
|
||||
impl PluginData {
|
||||
|
|
@ -894,7 +893,6 @@ impl PluginData {
|
|||
plugin_cache_dir: String,
|
||||
plugin_data_dir: String,
|
||||
inline_view_entrypoint_id: Option<String>,
|
||||
permissions: PluginRuntimePermissions
|
||||
) -> Self {
|
||||
Self {
|
||||
plugin_id,
|
||||
|
|
@ -904,7 +902,6 @@ impl PluginData {
|
|||
plugin_cache_dir,
|
||||
plugin_data_dir,
|
||||
inline_view_entrypoint_id,
|
||||
permissions
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -931,10 +928,6 @@ impl PluginData {
|
|||
fn inline_view_entrypoint_id(&self) -> Option<String> {
|
||||
self.inline_view_entrypoint_id.clone()
|
||||
}
|
||||
|
||||
fn permissions(&self) -> &PluginRuntimePermissions {
|
||||
&self.permissions
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ComponentModel {
|
||||
|
|
@ -974,8 +967,10 @@ pub struct BackendForPluginRuntimeApiImpl {
|
|||
icon_cache: IconCache,
|
||||
repository: DataDbRepository,
|
||||
search_index: SearchIndex,
|
||||
clipboard: Clipboard,
|
||||
plugin_uuid: String,
|
||||
plugin_id: PluginId,
|
||||
permissions: PluginRuntimePermissions
|
||||
}
|
||||
|
||||
impl BackendForPluginRuntimeApiImpl {
|
||||
|
|
@ -983,15 +978,19 @@ impl BackendForPluginRuntimeApiImpl {
|
|||
icon_cache: IconCache,
|
||||
repository: DataDbRepository,
|
||||
search_index: SearchIndex,
|
||||
clipboard: Clipboard,
|
||||
plugin_uuid: String,
|
||||
plugin_id: PluginId,
|
||||
permissions: PluginRuntimePermissions
|
||||
) -> Self {
|
||||
Self {
|
||||
icon_cache,
|
||||
repository,
|
||||
search_index,
|
||||
clipboard,
|
||||
plugin_uuid,
|
||||
plugin_id,
|
||||
permissions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1192,6 +1191,81 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl {
|
|||
|
||||
Ok(any_preferences_missing_value(preferences, preferences_user_data))
|
||||
}
|
||||
|
||||
async fn clipboard_read(&self) -> anyhow::Result<ClipboardData> {
|
||||
let allow = self
|
||||
.permissions
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Read);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'read' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Reading from clipboard, plugin id: {:?}", self.plugin_id);
|
||||
|
||||
self.clipboard.read()
|
||||
}
|
||||
|
||||
async fn clipboard_read_text(&self) -> anyhow::Result<Option<String>> {
|
||||
let allow = self
|
||||
.permissions
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Read);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'read' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Reading text from clipboard, plugin id: {:?}", self.plugin_id);
|
||||
|
||||
self.clipboard.read_text()
|
||||
}
|
||||
|
||||
async fn clipboard_write(&self, data: ClipboardData) -> anyhow::Result<()> {
|
||||
let allow = self
|
||||
.permissions
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Write);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'write' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Writing to clipboard, plugin id: {:?}", self.plugin_id);
|
||||
|
||||
self.clipboard.write(data)
|
||||
}
|
||||
|
||||
async fn clipboard_write_text(&self, data: String) -> anyhow::Result<()> {
|
||||
let allow = self
|
||||
.permissions
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Write);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'write' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Writing text to clipboard, plugin id: {:?}", self.plugin_id);
|
||||
|
||||
self.clipboard.write_text(data)
|
||||
}
|
||||
|
||||
async fn clipboard_clear(&self) -> anyhow::Result<()> {
|
||||
let allow = self
|
||||
.permissions
|
||||
.clipboard
|
||||
.contains(&PluginPermissionsClipboard::Clear);
|
||||
|
||||
if !allow {
|
||||
return Err(anyhow!("Plugin doesn't have 'clear' permission for clipboard"));
|
||||
}
|
||||
|
||||
tracing::debug!("Clearing clipboard, plugin id: {:?}", self.plugin_id);
|
||||
|
||||
self.clipboard.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ use common::{settings_env_data_to_string, SettingsEnvData};
|
|||
use utils::channel::RequestSender;
|
||||
use common::dirs::Dirs;
|
||||
use crate::model::{ActionShortcutKey, JsKeyboardEventOrigin};
|
||||
use crate::plugins::clipboard::Clipboard;
|
||||
use crate::plugins::config_reader::ConfigReader;
|
||||
use crate::plugins::data_db_repository::{db_entrypoint_from_str, DataDbRepository, DbPluginActionShortcutKind, DbPluginClipboardPermissions, DbPluginEntrypointType, DbPluginMainSearchBarPermissions, DbPluginPreference, DbPluginPreferenceUserData, DbReadPluginEntrypoint};
|
||||
use crate::plugins::icon_cache::IconCache;
|
||||
use crate::plugins::js::{start_plugin_runtime, AllPluginCommandData, OnePluginCommandData, PluginCode, PluginCommand, PluginRuntimeData};
|
||||
use crate::plugins::js::clipboard::Clipboard;
|
||||
use crate::plugins::js::permissions::{PluginPermissions, PluginPermissionsClipboard, PluginPermissionsExec, PluginPermissionsFileSystem, PluginPermissionsMainSearchBar};
|
||||
use crate::plugins::loader::PluginLoader;
|
||||
use crate::plugins::run_status::RunStatusHolder;
|
||||
|
|
@ -33,6 +33,7 @@ mod run_status;
|
|||
mod download_status;
|
||||
mod icon_cache;
|
||||
pub(super) mod frecency;
|
||||
mod clipboard;
|
||||
|
||||
static BUNDLED_PLUGINS: [(&str, Dir); 1] = [
|
||||
("gauntlet", include_dir!("$CARGO_MANIFEST_DIR/../../bundled_plugins/gauntlet/dist")),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue