From e2428ff7590b8cea6475099d2e1a14accf17792e Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 2 Mar 2025 15:07:09 +0100 Subject: [PATCH 001/170] Update README.md --- README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index aac5d23..df60d5a 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,10 @@ Web-first cross-platform application launcher with React-based plugins -> [!NOTE] -> Launcher is in active development, expect bugs, missing features, incomplete ux, etc. +> [!WARNING] +> Launcher is being developed by single developer in their free time. +> Changes may be few and far between. +> If you face any issues they will likely not get fixed unless they become a problem for that developer > > There will probably be breaking changes which will be documented in [changelog](CHANGELOG.md). @@ -70,10 +72,15 @@ https://github.com/user-attachments/assets/19964ed6-9cd9-48d4-9835-6be04de14b66 ##### OS Support -- Linux - - Both X11 and Wayland (via LayerShell protocol) are supported +##### Official +- Linux X11 - Application plugin depends on `gtk-launch` - macOS + +##### Best-effort +- Linux Wayland + - LayerShell support required + - Application plugin depends on `gtk-launch` - Windows ##### Planned features From ecd21cf5181e5323f9c8d32b715da7720600b4ee Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 4 Mar 2025 19:32:55 +0100 Subject: [PATCH 002/170] Drop events if state doesn't exist due to race condition instead of panicking --- rust/client/src/ui/widget/events.rs | 32 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/rust/client/src/ui/widget/events.rs b/rust/client/src/ui/widget/events.rs index 3fb8d91..026ddf2 100644 --- a/rust/client/src/ui/widget/events.rs +++ b/rust/client/src/ui/widget/events.rs @@ -86,7 +86,9 @@ impl ComponentWidgetEvent { Some(create_action_on_action_event(widget_id, id)) } ComponentWidgetEvent::ToggleDatePicker { widget_id } => { - let state = state.expect("state should always exist for "); + let Some(state) = state else { + return None; + }; let ComponentWidgetState::DatePicker(DatePickerState { state_value: _, @@ -100,7 +102,9 @@ impl ComponentWidgetEvent { None } ComponentWidgetEvent::CancelDatePicker { widget_id } => { - let state = state.expect("state should always exist for "); + let Some(state) = state else { + return None; + }; let ComponentWidgetState::DatePicker(DatePickerState { state_value: _, @@ -114,7 +118,9 @@ impl ComponentWidgetEvent { None } ComponentWidgetEvent::SubmitDatePicker { widget_id, value } => { - let state = state.expect("state should always exist for "); + let Some(state) = state else { + return None; + }; { let ComponentWidgetState::DatePicker(DatePickerState { @@ -131,7 +137,9 @@ impl ComponentWidgetEvent { Some(create_date_picker_on_change_event(widget_id, Some(value))) } ComponentWidgetEvent::ToggleCheckbox { widget_id, value } => { - let state = state.expect("state should always exist for "); + let Some(state) = state else { + return None; + }; { let ComponentWidgetState::Checkbox(CheckboxState { state_value }) = state else { @@ -144,7 +152,9 @@ impl ComponentWidgetEvent { Some(create_checkbox_on_change_event(widget_id, value)) } ComponentWidgetEvent::SelectPickList { widget_id, value } => { - let state = state.expect("state should always exist for "); + let Some(state) = state else { + return None; + }; { let ComponentWidgetState::Select(SelectState { state_value }) = state else { @@ -157,7 +167,9 @@ impl ComponentWidgetEvent { Some(create_select_on_change_event(widget_id, Some(value))) } ComponentWidgetEvent::OnChangeTextField { widget_id, value } => { - let state = state.expect("state should always exist for "); + let Some(state) = state else { + return None; + }; { let ComponentWidgetState::TextField(TextFieldState { state_value, .. }) = state else { @@ -170,7 +182,9 @@ impl ComponentWidgetEvent { Some(create_text_field_on_change_event(widget_id, Some(value))) } ComponentWidgetEvent::OnChangePasswordField { widget_id, value } => { - let state = state.expect("state should always exist for "); + let Some(state) = state else { + return None; + }; { let ComponentWidgetState::TextField(TextFieldState { state_value, .. }) = state else { @@ -183,7 +197,9 @@ impl ComponentWidgetEvent { Some(create_password_field_on_change_event(widget_id, Some(value))) } ComponentWidgetEvent::OnChangeSearchBar { widget_id, value } => { - let state = state.expect("state should always exist for "); + let Some(state) = state else { + return None; + }; { let ComponentWidgetState::TextField(TextFieldState { state_value, .. }) = state else { From e1216598a9f1cbe47fb2912422fafa04cacb6012 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 4 Mar 2025 19:45:47 +0100 Subject: [PATCH 003/170] Cleanup formatting a little --- rust/client/src/ui/widget/events.rs | 78 +++++++++++------------------ 1 file changed, 28 insertions(+), 50 deletions(-) diff --git a/rust/client/src/ui/widget/events.rs b/rust/client/src/ui/widget/events.rs index 026ddf2..ed250b4 100644 --- a/rust/client/src/ui/widget/events.rs +++ b/rust/client/src/ui/widget/events.rs @@ -90,15 +90,12 @@ impl ComponentWidgetEvent { return None; }; - let ComponentWidgetState::DatePicker(DatePickerState { - state_value: _, - show_picker, - }) = state - else { + let ComponentWidgetState::DatePicker(DatePickerState { show_picker, .. }) = state else { panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) }; *show_picker = !*show_picker; + None } ComponentWidgetEvent::CancelDatePicker { widget_id } => { @@ -106,15 +103,12 @@ impl ComponentWidgetEvent { return None; }; - let ComponentWidgetState::DatePicker(DatePickerState { - state_value: _, - show_picker, - }) = state - else { + let ComponentWidgetState::DatePicker(DatePickerState { show_picker, .. }) = state else { panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) }; *show_picker = false; + None } ComponentWidgetEvent::SubmitDatePicker { widget_id, value } => { @@ -122,17 +116,11 @@ impl ComponentWidgetEvent { return None; }; - { - let ComponentWidgetState::DatePicker(DatePickerState { - state_value: _, - show_picker, - }) = state - else { - panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) - }; + let ComponentWidgetState::DatePicker(DatePickerState { show_picker, .. }) = state else { + panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) + }; - *show_picker = false; - } + *show_picker = false; Some(create_date_picker_on_change_event(widget_id, Some(value))) } @@ -141,13 +129,11 @@ impl ComponentWidgetEvent { return None; }; - { - let ComponentWidgetState::Checkbox(CheckboxState { state_value }) = state else { - panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) - }; + let ComponentWidgetState::Checkbox(CheckboxState { state_value }) = state else { + panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) + }; - *state_value = !*state_value; - } + *state_value = !*state_value; Some(create_checkbox_on_change_event(widget_id, value)) } @@ -156,13 +142,11 @@ impl ComponentWidgetEvent { return None; }; - { - let ComponentWidgetState::Select(SelectState { state_value }) = state else { - panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) - }; + let ComponentWidgetState::Select(SelectState { state_value }) = state else { + panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) + }; - *state_value = Some(value.clone()); - } + *state_value = Some(value.clone()); Some(create_select_on_change_event(widget_id, Some(value))) } @@ -171,13 +155,11 @@ impl ComponentWidgetEvent { return None; }; - { - let ComponentWidgetState::TextField(TextFieldState { state_value, .. }) = state else { - panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) - }; + let ComponentWidgetState::TextField(TextFieldState { state_value, .. }) = state else { + panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) + }; - *state_value = value.clone(); - } + *state_value = value.clone(); Some(create_text_field_on_change_event(widget_id, Some(value))) } @@ -186,13 +168,11 @@ impl ComponentWidgetEvent { return None; }; - { - let ComponentWidgetState::TextField(TextFieldState { state_value, .. }) = state else { - panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) - }; + let ComponentWidgetState::TextField(TextFieldState { state_value, .. }) = state else { + panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) + }; - *state_value = value.clone(); - } + *state_value = value.clone(); Some(create_password_field_on_change_event(widget_id, Some(value))) } @@ -201,13 +181,11 @@ impl ComponentWidgetEvent { return None; }; - { - let ComponentWidgetState::TextField(TextFieldState { state_value, .. }) = state else { - panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) - }; + let ComponentWidgetState::TextField(TextFieldState { state_value, .. }) = state else { + panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) + }; - *state_value = value.clone(); - } + *state_value = value.clone(); Some(create_search_bar_on_change_event(widget_id, Some(value))) } From b0f03daba75e493bdb7095dbc15b0cd1567dd185 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 4 Mar 2025 22:03:15 +0100 Subject: [PATCH 004/170] Fix event loss due to race condition by allowing only one concurrent view event --- README.md | 3 +- js/core/src/core.tsx | 6 +- js/react_renderer/src/renderer.ts | 4 +- js/typings/index.d.ts | 1 + rust/client/src/ui/mod.rs | 99 +++++++++++++++++++++-------- rust/common/src/model.rs | 4 +- rust/common/src/rpc/frontend_api.rs | 10 +++ rust/plugin_runtime/src/api.rs | 10 +++ rust/plugin_runtime/src/deno.rs | 2 + rust/plugin_runtime/src/events.rs | 20 ++++++ rust/plugin_runtime/src/model.rs | 1 + rust/server/src/plugins/js.rs | 11 ++++ 12 files changed, 139 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index df60d5a..33efdc7 100644 --- a/README.md +++ b/README.md @@ -75,13 +75,14 @@ https://github.com/user-attachments/assets/19964ed6-9cd9-48d4-9835-6be04de14b66 ##### Official - Linux X11 - Application plugin depends on `gtk-launch` -- macOS +- macOS M1 ##### Best-effort - Linux Wayland - LayerShell support required - Application plugin depends on `gtk-launch` - Windows +- macOS Intel ##### Planned features diff --git a/js/core/src/core.tsx b/js/core/src/core.tsx index aa4b2a4..6d8be58 100644 --- a/js/core/src/core.tsx +++ b/js/core/src/core.tsx @@ -12,7 +12,8 @@ import { op_plugin_get_pending_event, plugin_preferences_required, show_plugin_error_view, - show_preferences_required_view + show_preferences_required_view, + synchronize_event } from "ext:core/ops"; @@ -65,6 +66,9 @@ export async function runPluginLoop() { } catch (e) { console.error("Error occurred when receiving view event to handle", e) } + + await synchronize_event() + break; } case "KeyboardEvent": { diff --git a/js/react_renderer/src/renderer.ts b/js/react_renderer/src/renderer.ts index 875d2bd..dd5482e 100644 --- a/js/react_renderer/src/renderer.ts +++ b/js/react_renderer/src/renderer.ts @@ -1,6 +1,6 @@ import ReactReconciler, { HostConfig, OpaqueHandle } from "react-reconciler"; import { createContext, ReactNode, useContext } from 'react'; -import { DefaultEventPriority } from 'react-reconciler/constants'; +import { DiscreteEventPriority } from 'react-reconciler/constants'; import { asset_data, asset_data_blocking, @@ -261,7 +261,7 @@ export const createHostConfig = (): HostConfig< }, noTimeout: -1, isPrimaryRenderer: true, - getCurrentEventPriority: () => DefaultEventPriority, + getCurrentEventPriority: () => DiscreteEventPriority, getInstanceFromNode(_node: any): ReactReconciler.Fiber | null | undefined { return undefined; }, diff --git a/js/typings/index.d.ts b/js/typings/index.d.ts index 4f8652a..d016678 100644 --- a/js/typings/index.d.ts +++ b/js/typings/index.d.ts @@ -226,6 +226,7 @@ declare module "ext:core/ops" { function op_entrypoint_names(): Record; function clear_inline_view(): void; function op_plugin_get_pending_event(): Promise; + function synchronize_event(): Promise; function hide_window(): void; function get_entrypoint_generator_entrypoint_ids(): Promise diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index ee689da..efdaa19 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -3,6 +3,8 @@ use std::fs; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering; use std::sync::Arc; use std::sync::Mutex as StdMutex; use std::sync::Mutex; @@ -50,7 +52,6 @@ use iced::event; use iced::executor; use iced::font; use iced::futures; -use iced::futures::channel::mpsc::Sender; use iced::futures::SinkExt; use iced::keyboard; use iced::keyboard::key; @@ -94,6 +95,7 @@ use iced::Task; use iced_fonts::BOOTSTRAP_FONT_BYTES; use serde::Deserialize; use tokio::runtime::Handle; +use tokio::sync::oneshot; use tokio::sync::Mutex as TokioMutex; use tokio::sync::RwLock as TokioRwLock; @@ -160,6 +162,7 @@ pub struct AppModel { window_position_file: PathBuf, #[cfg(target_os = "linux")] x11_active_window: Option, + event_synchronizer: Arc>>>, // ephemeral state prompt: String, @@ -356,6 +359,9 @@ pub enum AppMsg { X11ActiveWindowChanged { window: u32, }, + EventHanded { + plugin_id: PluginId, + }, } #[cfg(target_os = "linux")] @@ -702,6 +708,7 @@ fn new( x11_active_window: None, // ephemeral state + event_synchronizer: Arc::new(Mutex::new(HashMap::new())), prompt: "".to_string(), // state @@ -723,6 +730,12 @@ fn title(state: &AppModel, window: window::Id) -> String { } } +static BD_SEQ: AtomicU32 = AtomicU32::new(0); + +fn get_bdseq() -> u32 { + BD_SEQ.fetch_add(1, Ordering::SeqCst) + 1 +} + fn update(state: &mut AppModel, message: AppMsg) -> Task { match message { AppMsg::OpenView { @@ -923,6 +936,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { container, images, } => { + println!("render"); let has_children = container.content.is_some(); Task::batch([ @@ -1612,6 +1626,18 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } } + AppMsg::EventHanded { plugin_id } => { + state + .event_synchronizer + .lock() + .expect("lock is poisoned") + .remove(&plugin_id) + .expect("there should always be a responder here") + .send(()) + .expect("the other side was dropped"); + + Task::none() + } } } @@ -2431,45 +2457,63 @@ impl AppModel { ) -> Task { let mut backend_client = self.backend_api.clone(); + let val = get_bdseq(); + let event = self .client_context .handle_event(render_location, &plugin_id, widget_event.clone()); - Task::perform( - async move { - if let Some(event) = event { - match event { - UiViewEvent::View { - widget_id, - event_name, - event_arguments, - } => { - let msg = match widget_event { - ComponentWidgetEvent::ActionClick { .. } => { - AppMsg::ToggleActionPanel { keyboard: false } - } - _ => AppMsg::Noop, - }; + println!("handle_plugin_event {} {:?}", val, event); + if let Some(event) = event { + match event { + UiViewEvent::View { + widget_id, + event_name, + event_arguments, + } => { + let msg = match widget_event { + ComponentWidgetEvent::ActionClick { .. } => AppMsg::ToggleActionPanel { keyboard: false }, + _ => AppMsg::Noop, + }; + + let (sender, receiver) = oneshot::channel(); + + self.event_synchronizer + .lock() + .expect("lock is poisoned") + .insert(plugin_id.clone(), sender); + + Task::perform( + async move { backend_client .send_view_event(plugin_id, widget_id, event_name, event_arguments) .await?; + receiver.await.expect("the other side was dropped"); + + println!("handle_plugin_event {} after", val); + Ok(msg) - } - UiViewEvent::Open { href } => { + }, + |result| handle_backend_error(result, |msg| msg), + ) + } + UiViewEvent::Open { href } => { + Task::perform( + async move { backend_client.send_open_event(plugin_id, href).await?; Ok(AppMsg::Noop) - } - UiViewEvent::AppEvent { event } => Ok(event), - } - } else { - Ok(AppMsg::Noop) + }, + |result| handle_backend_error(result, |msg| msg), + ) } - }, - |result| handle_backend_error(result, |msg| msg), - ) + UiViewEvent::AppEvent { event } => Task::done(event), + } + } else { + Task::none() + } } fn handle_main_view_keyboard_event( @@ -2860,7 +2904,7 @@ fn handle_backend_error(result: Result, conver async fn request_loop( frontend_receiver: Arc>>, - mut sender: Sender, + mut sender: futures::channel::mpsc::Sender, ) { let mut frontend_receiver = frontend_receiver.write().await; loop { @@ -3005,6 +3049,7 @@ async fn request_loop( action_index, } } + UiRequestData::SynchronizeEvent { plugin_id } => AppMsg::EventHanded { plugin_id }, } }; diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index e1aeb1f..27b6711 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use std::fmt::Display; use std::fmt::Formatter; -use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -292,6 +291,9 @@ pub enum UiRequestData { entrypoint_name: String, action_index: usize, }, + SynchronizeEvent { + plugin_id: PluginId, + }, } #[derive(Debug)] diff --git a/rust/common/src/rpc/frontend_api.rs b/rust/common/src/rpc/frontend_api.rs index 3c65ea2..c162f81 100644 --- a/rust/common/src/rpc/frontend_api.rs +++ b/rust/common/src/rpc/frontend_api.rs @@ -262,4 +262,14 @@ impl FrontendApi { Ok(()) } + + pub async fn synchronize_event(&self, plugin_id: PluginId) -> Result<(), FrontendApiError> { + let data = UiRequestData::SynchronizeEvent { plugin_id }; + + let UiResponseData::Nothing = self.frontend_sender.send_receive(data).await? else { + unreachable!() + }; + + Ok(()) + } } diff --git a/rust/plugin_runtime/src/api.rs b/rust/plugin_runtime/src/api.rs index 02fb265..fb3a7d7 100644 --- a/rust/plugin_runtime/src/api.rs +++ b/rust/plugin_runtime/src/api.rs @@ -67,6 +67,7 @@ pub trait BackendForPluginRuntimeApi { entrypoint_preferences_required: bool, ) -> anyhow::Result<()>; async fn ui_clear_inline_view(&self) -> anyhow::Result<()>; + async fn ui_synchronize_event(&self) -> anyhow::Result<()>; } #[derive(Clone)] @@ -334,4 +335,13 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiProxy { value @ _ => panic!("Unexpected JsResponse type: {:?}", value), } } + + async fn ui_synchronize_event(&self) -> anyhow::Result<()> { + let request = JsRequest::SynchronizeEvent; + + match self.request(request).await? { + JsResponse::Nothing => Ok(()), + value @ _ => panic!("Unexpected JsResponse type: {:?}", value), + } + } } diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index ac73602..6ddd8ef 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -49,6 +49,7 @@ use crate::environment::environment_is_development; use crate::environment::environment_plugin_cache_dir; use crate::environment::environment_plugin_data_dir; use crate::events::op_plugin_get_pending_event; +use crate::events::synchronize_event; use crate::events::EventReceiver; use crate::events::JsEvent; use crate::logs::op_log_debug; @@ -286,6 +287,7 @@ deno_core::extension!( ops = [ // core op_plugin_get_pending_event, + synchronize_event, // logs op_log_trace, diff --git a/rust/plugin_runtime/src/events.rs b/rust/plugin_runtime/src/events.rs index 4dc4ece..ef9f706 100644 --- a/rust/plugin_runtime/src/events.rs +++ b/rust/plugin_runtime/src/events.rs @@ -14,6 +14,9 @@ use serde::Deserialize; use serde::Serialize; use tokio::sync::mpsc::Receiver; +use crate::api::BackendForPluginRuntimeApiProxy; +use crate::BackendForPluginRuntimeApi; + #[derive(Debug, Deserialize, Serialize, Encode, Decode)] #[serde(tag = "type")] pub enum JsEvent { @@ -104,3 +107,20 @@ pub async fn op_plugin_get_pending_event(state: Rc>) -> anyhow: Ok(event) } + +#[op2(async)] +pub async fn synchronize_event(state: Rc>) -> anyhow::Result<()> { + let api = { + let state = state.borrow(); + + let api = state.borrow::().clone(); + + api + }; + + tracing::trace!(target = "renderer_rs", "Synchronizing event"); + + api.ui_synchronize_event().await?; + + Ok(()) +} diff --git a/rust/plugin_runtime/src/model.rs b/rust/plugin_runtime/src/model.rs index 1294429..2bbc9c7 100644 --- a/rust/plugin_runtime/src/model.rs +++ b/rust/plugin_runtime/src/model.rs @@ -122,6 +122,7 @@ pub enum JsRequest { container: RootWidget, }, ClearInlineView, + SynchronizeEvent, ShowPluginErrorView { entrypoint_id: EntrypointId, render_location: JsUiRenderLocation, diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 2615a0f..c115d63 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -682,6 +682,11 @@ async fn handle_message(message: JsRequest, api: &BackendForPluginRuntimeApiImpl Ok(JsResponse::ActionIdForShortcut { data }) } + JsRequest::SynchronizeEvent => { + api.ui_synchronize_event().await?; + + Ok(JsResponse::Nothing) + } } } @@ -1231,6 +1236,12 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { Ok(()) } + + async fn ui_synchronize_event(&self) -> anyhow::Result<()> { + self.frontend_api.synchronize_event(self.plugin_id.clone()).await?; + + Ok(()) + } } fn preferences_to_js( From b5575725f05830ee825f60a56d3ecdbece70eb4a Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 4 Mar 2025 22:18:17 +0100 Subject: [PATCH 005/170] Clean up debugging leftovers --- rust/client/src/ui/mod.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index efdaa19..f96ae36 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -3,8 +3,6 @@ use std::fs; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; -use std::sync::atomic::AtomicU32; -use std::sync::atomic::Ordering; use std::sync::Arc; use std::sync::Mutex as StdMutex; use std::sync::Mutex; @@ -730,12 +728,6 @@ fn title(state: &AppModel, window: window::Id) -> String { } } -static BD_SEQ: AtomicU32 = AtomicU32::new(0); - -fn get_bdseq() -> u32 { - BD_SEQ.fetch_add(1, Ordering::SeqCst) + 1 -} - fn update(state: &mut AppModel, message: AppMsg) -> Task { match message { AppMsg::OpenView { @@ -936,7 +928,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { container, images, } => { - println!("render"); let has_children = container.content.is_some(); Task::batch([ @@ -2457,14 +2448,10 @@ impl AppModel { ) -> Task { let mut backend_client = self.backend_api.clone(); - let val = get_bdseq(); - let event = self .client_context .handle_event(render_location, &plugin_id, widget_event.clone()); - println!("handle_plugin_event {} {:?}", val, event); - if let Some(event) = event { match event { UiViewEvent::View { @@ -2492,8 +2479,6 @@ impl AppModel { receiver.await.expect("the other side was dropped"); - println!("handle_plugin_event {} after", val); - Ok(msg) }, |result| handle_backend_error(result, |msg| msg), From 1aca32ae9cb03fcd7ba0d2409dc10f50cfecb292 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:54:15 +0100 Subject: [PATCH 006/170] Fix panic when closing inline view due to action being run --- rust/client/src/ui/mod.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index f96ae36..f143c48 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -161,6 +161,7 @@ pub struct AppModel { #[cfg(target_os = "linux")] x11_active_window: Option, event_synchronizer: Arc>>>, + pending_window_state_reset: bool, // ephemeral state prompt: String, @@ -704,9 +705,10 @@ fn new( window_position_file: setup_data.window_position_file, #[cfg(target_os = "linux")] x11_active_window: None, + event_synchronizer: Arc::new(Mutex::new(HashMap::new())), + pending_window_state_reset: false, // ephemeral state - event_synchronizer: Arc::new(Mutex::new(HashMap::new())), prompt: "".to_string(), // state @@ -2295,9 +2297,7 @@ impl AppModel { let mut commands = vec![]; - if reset_state { - commands.push(self.reset_window_state()); - } + self.pending_window_state_reset = reset_state; #[cfg(target_os = "linux")] if self.wayland { @@ -2370,7 +2370,13 @@ impl AppModel { window::change_mode(self.main_window_id, Mode::Windowed), ]); - open_task + let mut commands = vec![open_task]; + + if self.pending_window_state_reset { + commands.push(self.reset_window_state()); + } + + Task::batch(commands) } fn reset_window_state(&mut self) -> Task { From 7e5d4aa8eafd6e9d49e2b2aeaf4211af815a8877 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 7 Mar 2025 20:43:35 +0100 Subject: [PATCH 007/170] Improve logging of panic, abort process on panic --- rust/common/src/dirs.rs | 2 +- rust/server/src/lib.rs | 34 ++++++++++++++++++---------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/rust/common/src/dirs.rs b/rust/common/src/dirs.rs index c549f76..07a64dd 100644 --- a/rust/common/src/dirs.rs +++ b/rust/common/src/dirs.rs @@ -95,7 +95,7 @@ impl Dirs { } pub fn plugin_crash_log_file(&self, plugin_uuid: &str) -> PathBuf { - self.logs_dir().join(&plugin_uuid).join("crash.txt") + self.logs_dir().join(format!("crash-{}.txt", &plugin_uuid)) } pub fn plugin_log_files(&self, plugin_uuid: &str) -> (PathBuf, PathBuf) { diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 6ea48bf..decb5c0 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -2,6 +2,7 @@ use std::backtrace::Backtrace; use std::fs::File; use std::io::Write; use std::path::PathBuf; +use std::process::exit; use std::rc::Rc; use std::sync::Arc; use std::time::SystemTime; @@ -41,7 +42,6 @@ const PLUGIN_CONNECT_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_CONNECT__"; const PLUGIN_UUID_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_UUID__"; pub fn start(minimized: bool) { - #[cfg(not(feature = "release"))] register_panic_hook(std::env::var(PLUGIN_UUID_ENV).ok()); if let Ok(socket_name) = std::env::var(PLUGIN_CONNECT_ENV) { @@ -340,10 +340,9 @@ async fn handle_request( Ok(response_data) } -#[cfg(not(feature = "release"))] fn register_panic_hook(plugin_runtime: Option) { unsafe { - std::env::set_var("RUST_BACKTRACE", "1"); + std::env::set_var("RUST_BACKTRACE", "full"); }; let dirs = Dirs::new(); @@ -369,22 +368,25 @@ fn register_panic_hook(plugin_runtime: Option) { let location = panic_info.location().map(|l| l.to_string()); let backtrace = Backtrace::capture(); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .ok() + .map(|duration| duration.as_millis().to_string()) + .unwrap_or("Unknown".to_string()); + + let content = format!( + "Panic on {}\nPayload: {}\nLocation: {:?}\nBacktrace: {}\n", + now, payload, location, backtrace + ); + let crash_file = File::options().create(true).append(true).open(&crash_file); if let Ok(mut crash_file) = crash_file { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .ok() - .map(|duration| duration.as_millis().to_string()) - .unwrap_or("Unknown".to_string()); - - let _ = crash_file.write_all( - format!( - "Panic on {}\nPayload: {}\nLocation: {:?}\nBacktrace: {}\n", - now, payload, location, backtrace - ) - .as_bytes(), - ); + let _ = crash_file.write_all(content.as_bytes()); } + + eprintln!("{}", content); + + exit(101); // poor man's abort on panic because actual setting makes v8 linking fail })); } From f96941d4360d5b77bab5411b7186e30f6064adb4 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 7 Mar 2025 21:54:53 +0100 Subject: [PATCH 008/170] Add thread names to logs --- rust/cli/src/lib.rs | 6 +- .../plugins/applications/linux/wayland/mod.rs | 14 ++-- .../src/plugins/applications/linux/x11.rs | 14 ++-- rust/server/src/lib.rs | 10 ++- rust/server/src/plugins/loader.rs | 68 ++++++++++--------- 5 files changed, 66 insertions(+), 46 deletions(-) diff --git a/rust/cli/src/lib.rs b/rust/cli/src/lib.rs index f4c641c..74b8934 100644 --- a/rust/cli/src/lib.rs +++ b/rust/cli/src/lib.rs @@ -7,6 +7,7 @@ use gauntlet_client::open_window; use gauntlet_management_client::start_management_client; use gauntlet_server::run_action; use gauntlet_server::start; +use tracing_subscriber::EnvFilter; /// Gauntlet CLI /// @@ -48,7 +49,10 @@ enum Commands { } pub fn init() { - tracing_subscriber::fmt::init(); + tracing_subscriber::fmt::fmt() + .with_thread_names(true) + .with_env_filter(EnvFilter::from_default_env()) + .init(); let cli = Cli::parse(); diff --git a/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs b/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs index fdd780e..5a582ca 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::rc::Rc; +use std::thread; use anyhow::anyhow; use deno_core::op2; @@ -52,11 +53,14 @@ impl WaylandDesktopEnvironment { let handle = Handle::current(); - std::thread::spawn(|| { - if let Err(e) = run_wayland_client(handle, event_sender, activate_receiver) { - tracing::error!("Error while running wayland client: {:?}", e); - } - }); + thread::Builder::new() + .name("gauntlet-wayland-events".to_string()) + .spawn(|| { + if let Err(e) = run_wayland_client(handle, event_sender, activate_receiver) { + tracing::error!("Error while running wayland client: {:?}", e); + } + }) + .expect("failed to spawn thread"); Ok(environment) } diff --git a/rust/plugin_runtime/src/plugins/applications/linux/x11.rs b/rust/plugin_runtime/src/plugins/applications/linux/x11.rs index 7d4531f..341eae7 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/x11.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/x11.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::convert::Infallible; use std::rc::Rc; use std::str::FromStr; +use std::thread; use anyhow::anyhow; use deno_core::op2; @@ -47,11 +48,14 @@ impl X11DesktopEnvironment { let handle = Handle::current(); - std::thread::spawn(move || { - if let Err(e) = listen_on_x11_events(handle, sender.clone()) { - tracing::error!("Error while listening on x11 events: {}", e); - } - }); + thread::Builder::new() + .name("gauntlet-x11-events".to_string()) + .spawn(move || { + if let Err(e) = listen_on_x11_events(handle, sender.clone()) { + tracing::error!("Error while listening on x11 events: {}", e); + } + }) + .expect("failed to spawn thread"); Self { receiver: Rc::new(RefCell::new(receiver)), diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index decb5c0..1ed84fc 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -5,6 +5,7 @@ use std::path::PathBuf; use std::process::exit; use std::rc::Rc; use std::sync::Arc; +use std::thread; use std::time::SystemTime; use std::time::UNIX_EPOCH; @@ -68,9 +69,12 @@ pub fn start(minimized: bool) { let (frontend_sender, frontend_receiver) = channel::(); let (backend_sender, backend_receiver) = channel::(); - std::thread::spawn(|| { - start_server(frontend_sender, backend_receiver); - }); + thread::Builder::new() + .name("gauntlet-server".to_string()) + .spawn(|| { + start_server(frontend_sender, backend_receiver); + }) + .expect("failed to spawn thread"); start_client(minimized, frontend_receiver, backend_sender) } diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index 9dba73d..af56075 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -71,45 +71,49 @@ impl PluginLoader { let handle = tokio::runtime::Handle::current(); let plugin_id_clone = plugin_id.clone(); - thread::spawn(move || { - let result = handle.block_on(async move { - let temp_dir = tempfile::tempdir()?; - PluginLoader::download(temp_dir.path(), plugin_id_clone.clone())?; + thread::Builder::new() + .name("gauntlet-plugin-download".to_string()) + .spawn(move || { + let result = handle.block_on(async move { + let temp_dir = tempfile::tempdir()?; - let plugin_data = PluginLoader::read_plugin_dir(temp_dir.path(), plugin_id_clone.clone()).await?; + PluginLoader::download(temp_dir.path(), plugin_id_clone.clone())?; - data_db_repository - .save_plugin(DbWritePlugin { - id: plugin_data.id, - name: plugin_data.name, - description: plugin_data.description, - enabled: false, - code: plugin_data.code, - entrypoints: plugin_data.entrypoints, - asset_data: plugin_data.asset_data, - permissions: plugin_data.permissions, - plugin_type: db_plugin_type_to_str(DbPluginType::Normal).to_owned(), - preferences: plugin_data.preferences, - }) - .await?; + let plugin_data = PluginLoader::read_plugin_dir(temp_dir.path(), plugin_id_clone.clone()).await?; - anyhow::Ok(()) - }); + data_db_repository + .save_plugin(DbWritePlugin { + id: plugin_data.id, + name: plugin_data.name, + description: plugin_data.description, + enabled: false, + code: plugin_data.code, + entrypoints: plugin_data.entrypoints, + asset_data: plugin_data.asset_data, + permissions: plugin_data.permissions, + plugin_type: db_plugin_type_to_str(DbPluginType::Normal).to_owned(), + preferences: plugin_data.preferences, + }) + .await?; - handle.block_on(async move { - match result { - Ok(()) => { - tracing::info!("Finished download of plugin: {:?}", plugin_id); - download_status_guard.download_finished() + anyhow::Ok(()) + }); + + handle.block_on(async move { + match result { + Ok(()) => { + tracing::info!("Finished download of plugin: {:?}", plugin_id); + download_status_guard.download_finished() + } + Err(err) => { + tracing::warn!("Download of plugin {:?} returned an error {:?}", plugin_id, err); + download_status_guard.download_failed(format!("{}", err)) + } } - Err(err) => { - tracing::warn!("Download of plugin {:?} returned an error {:?}", plugin_id, err); - download_status_guard.download_failed(format!("{}", err)) - } - } + }) }) - }); + .expect("failed to spawn thread"); Ok(()) } From 76609064df5a35393736b6d1019d7d24660186b6 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 11 Mar 2025 20:05:14 +0100 Subject: [PATCH 009/170] Revert previous attempt to fix event loss --- js/core/src/core.tsx | 6 +----- js/react_renderer/src/renderer.ts | 4 ++-- js/typings/index.d.ts | 1 - rust/client/src/ui/mod.rs | 27 --------------------------- rust/common/src/model.rs | 3 --- rust/common/src/rpc/frontend_api.rs | 10 ---------- rust/plugin_runtime/src/api.rs | 10 ---------- rust/plugin_runtime/src/deno.rs | 2 -- rust/plugin_runtime/src/events.rs | 17 ----------------- rust/plugin_runtime/src/model.rs | 1 - rust/server/src/lib.rs | 2 +- rust/server/src/plugins/js.rs | 11 ----------- 12 files changed, 4 insertions(+), 90 deletions(-) diff --git a/js/core/src/core.tsx b/js/core/src/core.tsx index 6d8be58..aa4b2a4 100644 --- a/js/core/src/core.tsx +++ b/js/core/src/core.tsx @@ -12,8 +12,7 @@ import { op_plugin_get_pending_event, plugin_preferences_required, show_plugin_error_view, - show_preferences_required_view, - synchronize_event + show_preferences_required_view } from "ext:core/ops"; @@ -66,9 +65,6 @@ export async function runPluginLoop() { } catch (e) { console.error("Error occurred when receiving view event to handle", e) } - - await synchronize_event() - break; } case "KeyboardEvent": { diff --git a/js/react_renderer/src/renderer.ts b/js/react_renderer/src/renderer.ts index dd5482e..875d2bd 100644 --- a/js/react_renderer/src/renderer.ts +++ b/js/react_renderer/src/renderer.ts @@ -1,6 +1,6 @@ import ReactReconciler, { HostConfig, OpaqueHandle } from "react-reconciler"; import { createContext, ReactNode, useContext } from 'react'; -import { DiscreteEventPriority } from 'react-reconciler/constants'; +import { DefaultEventPriority } from 'react-reconciler/constants'; import { asset_data, asset_data_blocking, @@ -261,7 +261,7 @@ export const createHostConfig = (): HostConfig< }, noTimeout: -1, isPrimaryRenderer: true, - getCurrentEventPriority: () => DiscreteEventPriority, + getCurrentEventPriority: () => DefaultEventPriority, getInstanceFromNode(_node: any): ReactReconciler.Fiber | null | undefined { return undefined; }, diff --git a/js/typings/index.d.ts b/js/typings/index.d.ts index d016678..4f8652a 100644 --- a/js/typings/index.d.ts +++ b/js/typings/index.d.ts @@ -226,7 +226,6 @@ declare module "ext:core/ops" { function op_entrypoint_names(): Record; function clear_inline_view(): void; function op_plugin_get_pending_event(): Promise; - function synchronize_event(): Promise; function hide_window(): void; function get_entrypoint_generator_entrypoint_ids(): Promise diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index f143c48..78c8c60 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -160,7 +160,6 @@ pub struct AppModel { window_position_file: PathBuf, #[cfg(target_os = "linux")] x11_active_window: Option, - event_synchronizer: Arc>>>, pending_window_state_reset: bool, // ephemeral state @@ -358,9 +357,6 @@ pub enum AppMsg { X11ActiveWindowChanged { window: u32, }, - EventHanded { - plugin_id: PluginId, - }, } #[cfg(target_os = "linux")] @@ -705,7 +701,6 @@ fn new( window_position_file: setup_data.window_position_file, #[cfg(target_os = "linux")] x11_active_window: None, - event_synchronizer: Arc::new(Mutex::new(HashMap::new())), pending_window_state_reset: false, // ephemeral state @@ -1619,18 +1614,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } } - AppMsg::EventHanded { plugin_id } => { - state - .event_synchronizer - .lock() - .expect("lock is poisoned") - .remove(&plugin_id) - .expect("there should always be a responder here") - .send(()) - .expect("the other side was dropped"); - - Task::none() - } } } @@ -2470,21 +2453,12 @@ impl AppModel { _ => AppMsg::Noop, }; - let (sender, receiver) = oneshot::channel(); - - self.event_synchronizer - .lock() - .expect("lock is poisoned") - .insert(plugin_id.clone(), sender); - Task::perform( async move { backend_client .send_view_event(plugin_id, widget_id, event_name, event_arguments) .await?; - receiver.await.expect("the other side was dropped"); - Ok(msg) }, |result| handle_backend_error(result, |msg| msg), @@ -3040,7 +3014,6 @@ async fn request_loop( action_index, } } - UiRequestData::SynchronizeEvent { plugin_id } => AppMsg::EventHanded { plugin_id }, } }; diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index 27b6711..0b2217e 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -291,9 +291,6 @@ pub enum UiRequestData { entrypoint_name: String, action_index: usize, }, - SynchronizeEvent { - plugin_id: PluginId, - }, } #[derive(Debug)] diff --git a/rust/common/src/rpc/frontend_api.rs b/rust/common/src/rpc/frontend_api.rs index c162f81..3c65ea2 100644 --- a/rust/common/src/rpc/frontend_api.rs +++ b/rust/common/src/rpc/frontend_api.rs @@ -262,14 +262,4 @@ impl FrontendApi { Ok(()) } - - pub async fn synchronize_event(&self, plugin_id: PluginId) -> Result<(), FrontendApiError> { - let data = UiRequestData::SynchronizeEvent { plugin_id }; - - let UiResponseData::Nothing = self.frontend_sender.send_receive(data).await? else { - unreachable!() - }; - - Ok(()) - } } diff --git a/rust/plugin_runtime/src/api.rs b/rust/plugin_runtime/src/api.rs index fb3a7d7..02fb265 100644 --- a/rust/plugin_runtime/src/api.rs +++ b/rust/plugin_runtime/src/api.rs @@ -67,7 +67,6 @@ pub trait BackendForPluginRuntimeApi { entrypoint_preferences_required: bool, ) -> anyhow::Result<()>; async fn ui_clear_inline_view(&self) -> anyhow::Result<()>; - async fn ui_synchronize_event(&self) -> anyhow::Result<()>; } #[derive(Clone)] @@ -335,13 +334,4 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiProxy { value @ _ => panic!("Unexpected JsResponse type: {:?}", value), } } - - async fn ui_synchronize_event(&self) -> anyhow::Result<()> { - let request = JsRequest::SynchronizeEvent; - - match self.request(request).await? { - JsResponse::Nothing => Ok(()), - value @ _ => panic!("Unexpected JsResponse type: {:?}", value), - } - } } diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index 6ddd8ef..ac73602 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -49,7 +49,6 @@ use crate::environment::environment_is_development; use crate::environment::environment_plugin_cache_dir; use crate::environment::environment_plugin_data_dir; use crate::events::op_plugin_get_pending_event; -use crate::events::synchronize_event; use crate::events::EventReceiver; use crate::events::JsEvent; use crate::logs::op_log_debug; @@ -287,7 +286,6 @@ deno_core::extension!( ops = [ // core op_plugin_get_pending_event, - synchronize_event, // logs op_log_trace, diff --git a/rust/plugin_runtime/src/events.rs b/rust/plugin_runtime/src/events.rs index ef9f706..c808a19 100644 --- a/rust/plugin_runtime/src/events.rs +++ b/rust/plugin_runtime/src/events.rs @@ -107,20 +107,3 @@ pub async fn op_plugin_get_pending_event(state: Rc>) -> anyhow: Ok(event) } - -#[op2(async)] -pub async fn synchronize_event(state: Rc>) -> anyhow::Result<()> { - let api = { - let state = state.borrow(); - - let api = state.borrow::().clone(); - - api - }; - - tracing::trace!(target = "renderer_rs", "Synchronizing event"); - - api.ui_synchronize_event().await?; - - Ok(()) -} diff --git a/rust/plugin_runtime/src/model.rs b/rust/plugin_runtime/src/model.rs index 2bbc9c7..1294429 100644 --- a/rust/plugin_runtime/src/model.rs +++ b/rust/plugin_runtime/src/model.rs @@ -122,7 +122,6 @@ pub enum JsRequest { container: RootWidget, }, ClearInlineView, - SynchronizeEvent, ShowPluginErrorView { entrypoint_id: EntrypointId, render_location: JsUiRenderLocation, diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 1ed84fc..a7b75f5 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -379,7 +379,7 @@ fn register_panic_hook(plugin_runtime: Option) { .unwrap_or("Unknown".to_string()); let content = format!( - "Panic on {}\nPayload: {}\nLocation: {:?}\nBacktrace: {}\n", + "Panic on {}\nPayload: {}\nLocation: {:?}\nBacktrace:\n{}", now, payload, location, backtrace ); diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index c115d63..2615a0f 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -682,11 +682,6 @@ async fn handle_message(message: JsRequest, api: &BackendForPluginRuntimeApiImpl Ok(JsResponse::ActionIdForShortcut { data }) } - JsRequest::SynchronizeEvent => { - api.ui_synchronize_event().await?; - - Ok(JsResponse::Nothing) - } } } @@ -1236,12 +1231,6 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { Ok(()) } - - async fn ui_synchronize_event(&self) -> anyhow::Result<()> { - self.frontend_api.synchronize_event(self.plugin_id.clone()).await?; - - Ok(()) - } } fn preferences_to_js( From 705b42821c3e457a7cc7eeeb8a1a1f7ece133fc7 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 11 Mar 2025 20:08:10 +0100 Subject: [PATCH 010/170] Actually fix the event loss, finally --- js/react_renderer/src/renderer.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/js/react_renderer/src/renderer.ts b/js/react_renderer/src/renderer.ts index 875d2bd..0ac0984 100644 --- a/js/react_renderer/src/renderer.ts +++ b/js/react_renderer/src/renderer.ts @@ -138,20 +138,24 @@ export function showHudWindow(display: string): void { show_hud(display) } -function createWidget(hostContext: HostContext, type: ComponentType, properties: Props, children: UiWidget[] = []): Instance { +function createWidget(id: number | undefined, hostContext: HostContext, type: ComponentType, properties: Props, children: UiWidget[]): Instance { const props = Object.fromEntries( Object.entries(properties) .filter(([key, _]) => key !== "children") ); const instance: Instance = { - widgetId: hostContext.nextId, + widgetId: id != undefined ? id : hostContext.nextId, widgetType: type, widgetProperties: props, widgetChildren: children, hostContext }; - hostContext.nextId += 1 + + if (id == undefined) { + hostContext.nextId += 1 + } + return instance } @@ -183,7 +187,7 @@ export const createHostConfig = (): HostConfig< _internalHandle: OpaqueHandle, ): Instance => { op_log_trace("renderer_js_common", `createInstance is called, type: ${type}, props: ${Deno.inspect(props)}, rootContainer: ${Deno.inspect(rootContainer)}`) - const instance = createWidget(hostContext, type, props) + const instance = createWidget(undefined, hostContext, type, props, []) op_log_trace("renderer_js_common", `createInstance returned, widget: ${Deno.inspect(instance)}`) return instance; @@ -196,7 +200,7 @@ export const createHostConfig = (): HostConfig< _internalHandle: OpaqueHandle ): TextInstance => { op_log_trace("renderer_js_common", `createTextInstance is called, text: ${text}, rootContainer: ${Deno.inspect(rootContainer)}`) - const textInstance = createWidget(hostContext, "gauntlet:text_part", { value: text }) + const textInstance = createWidget(undefined, hostContext, "gauntlet:text_part", { value: text }, []) op_log_trace("renderer_js_common", `createTextInstance returned, widget: ${Deno.inspect(textInstance)}`) return textInstance; @@ -301,19 +305,21 @@ export const createHostConfig = (): HostConfig< ): Instance { op_log_trace("renderer_js_persistence", `cloneInstance is called, instance: ${Deno.inspect(instance)}, updatePayload: ${Deno.inspect(updatePayload)}, type: ${type}, oldProps: ${Deno.inspect(oldProps)}, newProps: ${Deno.inspect(newProps)}, keepChildren: ${keepChildren}, recyclableInstance: ${Deno.inspect(recyclableInstance)}`) + const recyclableId = recyclableInstance != null ? recyclableInstance.widgetId : undefined; + let clonedInstance: Instance; if (keepChildren) { if (updatePayload !== null) { - clonedInstance = createWidget(instance.hostContext, type, newProps, instance.widgetChildren) + clonedInstance = createWidget(recyclableId, instance.hostContext, type, newProps, instance.widgetChildren) } else { - clonedInstance = createWidget(instance.hostContext, type, oldProps, instance.widgetChildren) + clonedInstance = createWidget(recyclableId, instance.hostContext, type, oldProps, instance.widgetChildren) } } else { if (updatePayload !== null) { - clonedInstance = createWidget(instance.hostContext, type, newProps, []) + clonedInstance = createWidget(recyclableId, instance.hostContext, type, newProps, []) } else { - clonedInstance = createWidget(instance.hostContext, type, oldProps, []) + clonedInstance = createWidget(recyclableId, instance.hostContext, type, oldProps, []) } } From d4e88b97a59a3770dd5fbbb99b7715313b17aae3 Mon Sep 17 00:00:00 2001 From: DaRacci Date: Mon, 10 Mar 2025 06:31:09 +0000 Subject: [PATCH 011/170] fix(flake): use strings instead of deprecated url literals. --- flake.lock | 33 ++++++++++++++++++--------------- flake.nix | 10 +++++----- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/flake.lock b/flake.lock index e355443..005c4b5 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1733688869, - "narHash": "sha256-KrhxxFj1CjESDrL5+u/zsVH0K+Ik9tvoac/oFPoxSB8=", + "lastModified": 1741481578, + "narHash": "sha256-JBTSyJFQdO3V8cgcL08VaBUByEU6P5kXbTJN6R0PFQo=", "owner": "ipetkov", "repo": "crane", - "rev": "604637106e420ad99907cae401e13ab6b452e7d9", + "rev": "bb1c9567c43e4434f54e9481eb4b8e8e0d50f0b5", "type": "github" }, "original": { @@ -36,11 +36,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1733312601, - "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "lastModified": 1741352980, + "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9", "type": "github" }, "original": { @@ -51,11 +51,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1733392399, - "narHash": "sha256-kEsTJTUQfQFIJOcLYFt/RvNxIK653ZkTBIs4DG+cBns=", + "lastModified": 1741379970, + "narHash": "sha256-Wh7esNh7G24qYleLvgOSY/7HlDUzWaL/n4qzlBePpiw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "d0797a04b81caeae77bcff10a9dde78bc17f5661", + "rev": "36fd87baa9083f34f7f5027900b62ee6d09b1f2f", "type": "github" }, "original": { @@ -67,14 +67,17 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1733096140, - "narHash": "sha256-1qRH7uAUsyQI7R1Uwl4T+XvdNv778H0Nb5njNrqvylY=", - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz" + "lastModified": 1740877520, + "narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "147dee35aab2193b174e4c0868bd80ead5ce755c", + "type": "github" }, "original": { - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz" + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" } }, "root": { diff --git a/flake.nix b/flake.nix index a871c7b..bd6b95a 100644 --- a/flake.nix +++ b/flake.nix @@ -2,11 +2,11 @@ description = "https://github.com/project-gauntlet/gauntlet"; outputs = inputs: inputs.flake-parts.lib.mkFlake {inherit inputs;} {imports = [./nix];}; inputs = { - nixpkgs.url = github:nixos/nixpkgs/nixos-unstable; - systems.url = github:nix-systems/default; - flake-parts.url = github:hercules-ci/flake-parts; - flake-compat.url = github:edolstra/flake-compat; + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + systems.url = "github:nix-systems/default"; + flake-parts.url = "github:hercules-ci/flake-parts"; + flake-compat.url = "github:edolstra/flake-compat"; flake-compat.flake = false; - crane.url = github:ipetkov/crane; + crane.url = "github:ipetkov/crane"; }; } From fd481027e34b078a78d06c0fbe85cebcd9184715 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 14 Mar 2025 20:43:29 +0100 Subject: [PATCH 012/170] Fix zombie processes being left over after closing Settings UI --- CHANGELOG.md | 8 ++- Cargo.lock | 2 +- rust/common/Cargo.toml | 3 ++ rust/common/src/detached_process.rs | 44 ++++++++++++++++ rust/common/src/lib.rs | 1 + rust/plugin_runtime/Cargo.toml | 3 -- .../src/plugins/applications.rs | 51 +++---------------- .../src/plugins/applications/linux/mod.rs | 6 ++- rust/plugin_runtime/src/plugins/settings.rs | 3 +- rust/server/src/plugins/mod.rs | 5 +- 10 files changed, 73 insertions(+), 53 deletions(-) create mode 100644 rust/common/src/detached_process.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4af99fe..941e8b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ and this project doesn't adhere to Semantic Versioning, see [Versioning](./READM For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://github.com/project-gauntlet/tools/blob/main/CHANGELOG.md) ## [Unreleased] +- Fixed crash when typing/clicking fast in React-created plugin ui +- Fixed events sometimes overwriting other parallel events when typing/clicking fast in React-created plugin ui + - Fixes mouse clicks sometimes being ignored in high refresh rate views + - Fixes keystrokes being rewritten/ignored when holding keys in input fields +- Fixed crash when closing inline view due to action being run +- Fixed zombie processes being left over after closing Settings UI ## [16] - 2025-02-23 @@ -628,4 +634,4 @@ Themes are versioned and only one version is supported at the same time by appli ### Added -- Initial release. \ No newline at end of file +- Initial release. diff --git a/Cargo.lock b/Cargo.lock index 976ed4f..2b48fe6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4030,6 +4030,7 @@ dependencies = [ "gix-url", "indexmap 2.7.0", "itertools 0.13.0", + "libc", "prost", "serde", "serde_json", @@ -4097,7 +4098,6 @@ dependencies = [ "image 0.25.5", "indexmap 2.7.0", "interprocess", - "libc", "numbat", "objc2", "objc2-app-kit", diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml index 7318e97..a6df4b7 100644 --- a/rust/common/Cargo.toml +++ b/rust/common/Cargo.toml @@ -22,6 +22,9 @@ gix-url = { version = "0.28.1" } base64 = "0.22" directories = "5.0" +[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] +libc = "0.2" + [build-dependencies] # workspaces gauntlet-component-model.workspace = true diff --git a/rust/common/src/detached_process.rs b/rust/common/src/detached_process.rs new file mode 100644 index 0000000..d3197d6 --- /dev/null +++ b/rust/common/src/detached_process.rs @@ -0,0 +1,44 @@ +use std::io; +use std::process::Command; +use std::process::Stdio; + +pub trait CommandExt { + fn spawn_detached(&mut self) -> io::Result<()>; +} + +impl CommandExt for Command { + fn spawn_detached(&mut self) -> io::Result<()> { + // This is pretty much lifted from the implementation in Alacritty: + // https://github.com/alacritty/alacritty/blob/b9c886872d1202fc9302f68a0bedbb17daa35335/alacritty/src/daemon.rs + + self.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null()); + + #[cfg(unix)] + unsafe { + use std::os::unix::process::CommandExt as _; + + self.pre_exec(move || { + match libc::fork() { + -1 => return Err(io::Error::last_os_error()), + 0 => (), + _ => libc::_exit(0), + } + + if libc::setsid() == -1 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + }); + } + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + const CREATE_NO_WINDOW: u32 = 0x08000000; + self.creation_flags(CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW); + } + + self.spawn().map(|_| ()) + } +} diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs index 3c2f781..b6d78f4 100644 --- a/rust/common/src/lib.rs +++ b/rust/common/src/lib.rs @@ -1,6 +1,7 @@ use serde::Deserialize; use serde::Serialize; +pub mod detached_process; pub mod dirs; pub mod model; pub mod rpc; diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index 3347427..53e1099 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -36,9 +36,6 @@ uuid = "1.11.0" open = "5" sys-locale = "0.3.2" -[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] -libc = "0.2" - [target.'cfg(target_os = "linux")'.dependencies] freedesktop_entry_parser = "1.3" freedesktop-icons = "0.2" diff --git a/rust/plugin_runtime/src/plugins/applications.rs b/rust/plugin_runtime/src/plugins/applications.rs index 3dc7818..264622b 100644 --- a/rust/plugin_runtime/src/plugins/applications.rs +++ b/rust/plugin_runtime/src/plugins/applications.rs @@ -5,6 +5,7 @@ use std::rc::Rc; use anyhow::anyhow; use deno_core::op2; use deno_core::OpState; +use gauntlet_common::detached_process::CommandExt; use image::imageops::FilterType; use image::ImageFormat; use serde::Deserialize; @@ -181,7 +182,7 @@ pub fn macos_application_dirs() -> Vec { #[cfg(target_os = "macos")] #[op2(fast)] pub fn macos_open_application(#[string] app_path: String) -> anyhow::Result<()> { - spawn_detached("open", &[app_path])?; + std::process::Command::new("open").args([app_path]).spawn_detached()?; Ok(()) } @@ -203,7 +204,9 @@ pub fn macos_settings_13_and_post(#[string] lang: Option) -> Vec anyhow::Result<()> { - spawn_detached("open", &[format!("x-apple.systempreferences:{}", preferences_id)])?; + std::process::Command::new("open") + .args([format!("x-apple.systempreferences:{}", preferences_id)]) + .spawn_detached()?; Ok(()) } @@ -211,7 +214,9 @@ pub fn macos_open_setting_13_and_post(#[string] preferences_id: String) -> anyho #[cfg(target_os = "macos")] #[op2(fast)] pub fn macos_open_setting_pre_13(#[string] setting_path: String) -> anyhow::Result<()> { - spawn_detached("open", &["-b", "com.apple.systempreferences", &setting_path])?; + std::process::Command::new("open") + .args(["-b", "com.apple.systempreferences", &setting_path]) + .spawn_detached()?; Ok(()) } @@ -227,46 +232,6 @@ pub fn macos_get_localized_language() -> Option { .map(|s| s.to_string()) } -#[cfg(unix)] -pub fn spawn_detached(path: &str, args: I) -> std::io::Result<()> -where - I: IntoIterator + Copy, - S: AsRef, -{ - // from https://github.com/alacritty/alacritty/blob/5abb4b73937b17fe501b9ca20b602950f1218b96/alacritty/src/daemon.rs#L65 - use std::os::unix::prelude::CommandExt; - use std::process::Command; - use std::process::Stdio; - - let mut command = Command::new(path); - - command - .args(args) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()); - - unsafe { - command - .pre_exec(|| { - match libc::fork() { - -1 => return Err(std::io::Error::last_os_error()), - 0 => (), - _ => libc::_exit(0), - } - - if libc::setsid() == -1 { - return Err(std::io::Error::last_os_error()); - } - - Ok(()) - }) - .spawn()? - .wait() - .map(|_| ()) - } -} - pub(in crate::plugins::applications) fn resize_icon(data: Vec) -> anyhow::Result> { let data = image::load_from_memory_with_format(&data, ImageFormat::Png)?; let data = image::imageops::resize(&data, 48, 48, FilterType::Lanczos3); diff --git a/rust/plugin_runtime/src/plugins/applications/linux/mod.rs b/rust/plugin_runtime/src/plugins/applications/linux/mod.rs index 5880301..2e1f195 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/mod.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/mod.rs @@ -10,6 +10,7 @@ use deno_core::op2; use deno_core::OpState; use freedesktop_entry_parser::parse_entry; use freedesktop_icons::lookup; +use gauntlet_common::detached_process::CommandExt; use image::imageops::FilterType; use image::ImageFormat; use tokio::sync::mpsc::Sender; @@ -19,7 +20,6 @@ use walkdir::WalkDir; use crate::plugin_data::PluginData; use crate::plugins::applications::linux; use crate::plugins::applications::resize_icon; -use crate::plugins::applications::spawn_detached; use crate::plugins::applications::DesktopApplication; use crate::plugins::applications::DesktopPathAction; @@ -106,7 +106,9 @@ fn linux_application_dirs(state: Rc>) -> Vec { #[op2(fast)] fn linux_open_application(#[string] desktop_file_id: String) -> anyhow::Result<()> { - spawn_detached("gtk-launch", &[desktop_file_id])?; + std::process::Command::new("gtk-launch") + .args([desktop_file_id]) + .spawn_detached()?; Ok(()) } diff --git a/rust/plugin_runtime/src/plugins/settings.rs b/rust/plugin_runtime/src/plugins/settings.rs index ddac7cd..7a2a1d8 100644 --- a/rust/plugin_runtime/src/plugins/settings.rs +++ b/rust/plugin_runtime/src/plugins/settings.rs @@ -1,10 +1,11 @@ use deno_core::op2; +use gauntlet_common::detached_process::CommandExt; #[op2(fast)] pub fn open_settings() -> anyhow::Result<()> { std::process::Command::new(std::env::current_exe()?) .args(["settings"]) - .spawn()?; + .spawn_detached()?; Ok(()) } diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 8538a46..b2acd60 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -7,6 +7,7 @@ use std::time::Duration; use anyhow::anyhow; use anyhow::Context; +use gauntlet_common::detached_process::CommandExt; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::EntrypointId; @@ -734,7 +735,7 @@ impl ApplicationManager { std::process::Command::new(current_exe) .args(["settings"]) - .spawn() + .spawn_detached() .expect("failed to execute settings process"); } @@ -755,7 +756,7 @@ impl ApplicationManager { std::process::Command::new(current_exe) .args(["settings"]) .env(SETTINGS_ENV, settings_env_data_to_string(data)) - .spawn() + .spawn_detached() .expect("failed to execute settings process"); // this can fail in dev if binary was replaced by more recent compilation } From 9dfab9d537e024cc8967f8356a02a42ca9e9beac Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 14 Mar 2025 21:45:11 +0100 Subject: [PATCH 013/170] Better process cleanup on linux id server process is killed --- CHANGELOG.md | 4 ++-- Cargo.lock | 1 + rust/plugin_runtime/Cargo.toml | 3 +++ rust/plugin_runtime/src/lib.rs | 5 +++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 941e8b3..6d65ba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,8 @@ For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://git - Fixed crash when typing/clicking fast in React-created plugin ui - Fixed events sometimes overwriting other parallel events when typing/clicking fast in React-created plugin ui - Fixes mouse clicks sometimes being ignored in high refresh rate views - - Fixes keystrokes being rewritten/ignored when holding keys in input fields -- Fixed crash when closing inline view due to action being run + - Fixes keystrokes being rewritten/ignored when holding down keys or typing fast in input fields +- Fixed crash when closing inline view due to Action being run - Fixed zombie processes being left over after closing Settings UI ## [16] - 2025-02-23 diff --git a/Cargo.lock b/Cargo.lock index 2b48fe6..f079ca5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4098,6 +4098,7 @@ dependencies = [ "image 0.25.5", "indexmap 2.7.0", "interprocess", + "libc", "numbat", "objc2", "objc2-app-kit", diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index 53e1099..599db9e 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -57,6 +57,9 @@ objc2 = "0.5.2" [target.'cfg(target_os = "windows")'.dependencies] windows = { version = "0.58.0", features = ["Win32_Storage_FileSystem", "Win32_UI_WindowsAndMessaging", "Win32_UI_Shell", "Win32_UI_Controls"] } +[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] +libc = "0.2" + [features] scenario_runner = [] release = [] diff --git a/rust/plugin_runtime/src/lib.rs b/rust/plugin_runtime/src/lib.rs index 5dfbf3c..7ba8f2e 100644 --- a/rust/plugin_runtime/src/lib.rs +++ b/rust/plugin_runtime/src/lib.rs @@ -67,6 +67,11 @@ use crate::api::BackendForPluginRuntimeApiProxy; use crate::deno::start_js_runtime; pub fn run_plugin_runtime(socket_name: String) { + #[cfg(target_os = "linux")] + unsafe { + libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL); + + tokio::runtime::Builder::new_current_thread() .enable_all() .build() From 93dbba1def55c62476e7c9a4bb00d47616e69ed6 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 14 Mar 2025 21:46:42 +0100 Subject: [PATCH 014/170] Fix typo --- rust/plugin_runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/plugin_runtime/src/lib.rs b/rust/plugin_runtime/src/lib.rs index 7ba8f2e..0ee10e2 100644 --- a/rust/plugin_runtime/src/lib.rs +++ b/rust/plugin_runtime/src/lib.rs @@ -70,7 +70,7 @@ pub fn run_plugin_runtime(socket_name: String) { #[cfg(target_os = "linux")] unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL); - + } tokio::runtime::Builder::new_current_thread() .enable_all() From 704b466b209903970e32b40a94334723b88c36ea Mon Sep 17 00:00:00 2001 From: Exidex <16986685+Exidex@users.noreply.github.com> Date: Sat, 15 Mar 2025 07:19:55 +0000 Subject: [PATCH 015/170] Prepare for v17 release --- CHANGELOG.md | 2 ++ VERSION | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d65ba6..8090ebf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project doesn't adhere to Semantic Versioning, see [Versioning](./READM For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://github.com/project-gauntlet/tools/blob/main/CHANGELOG.md) ## [Unreleased] + +## [17] - 2025-03-15 - Fixed crash when typing/clicking fast in React-created plugin ui - Fixed events sometimes overwriting other parallel events when typing/clicking fast in React-created plugin ui - Fixes mouse clicks sometimes being ignored in high refresh rate views diff --git a/VERSION b/VERSION index 19c7bdb..8e2afd3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -16 \ No newline at end of file +17 \ No newline at end of file From 81c21e9ab66a1661a9b9795193bd7c0d36d4225d Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 15 Mar 2025 08:52:53 +0100 Subject: [PATCH 016/170] Bump version in nix overlay --- nix/overlay.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/overlay.nix b/nix/overlay.nix index 2774817..0918940 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -5,7 +5,7 @@ }: { flake.overlays.default = final: _: let # These must be updated following the instructions in ./nix/README.md when dependencies are updated or the version bumped - version = "v14"; + version = "v17"; npmDepsHash = "sha256-rk5aLdXoSpqMNcSVIRJTKN1KqtddVPiKkZ1YWZ+n5m8="; RUSTY_V8_ARCHIVE = fetchRustyV8 "130.0.2" { aarch64-darwin = "sha256-aWZ/4Q4Wttx37xOdBmTCPGP+eYGhr4CM1UkYq8pC7Qs="; From 4f715674c0fc227b4c0f8ab9e9d02787f3f3dffb Mon Sep 17 00:00:00 2001 From: Benno <107504783+BennoCrafter@users.noreply.github.com> Date: Sat, 15 Mar 2025 19:32:13 +0100 Subject: [PATCH 017/170] Plugin Manifest schema (#62) Co-authored-by: Exidex <16986685+exidex@users.noreply.github.com> --- Cargo.lock | 45 + Cargo.toml | 1 + docs/schema/plugin_manifest.schema.json | 788 ++++++++++++++++++ rust/manifest_schema/Cargo.toml | 14 + rust/manifest_schema/src/main.rs | 14 + rust/server/Cargo.toml | 1 + rust/server/src/lib.rs | 2 +- rust/server/src/plugins/data_db_repository.rs | 2 +- rust/server/src/plugins/loader.rs | 494 +---------- rust/server/src/plugins/mod.rs | 1 + rust/server/src/plugins/plugin_manifest.rs | 553 ++++++++++++ 11 files changed, 1420 insertions(+), 495 deletions(-) create mode 100644 docs/schema/plugin_manifest.schema.json create mode 100644 rust/manifest_schema/Cargo.toml create mode 100644 rust/manifest_schema/src/main.rs create mode 100644 rust/server/src/plugins/plugin_manifest.rs diff --git a/Cargo.lock b/Cargo.lock index f079ca5..555a3bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4076,6 +4076,15 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "gauntlet-manifest-schema" +version = "0.0.0" +dependencies = [ + "gauntlet-server", + "schemars", + "serde_json", +] + [[package]] name = "gauntlet-plugin-runtime" version = "0.1.0" @@ -4158,6 +4167,7 @@ dependencies = [ "once_cell", "open", "regex", + "schemars", "serde", "sqlx", "tantivy", @@ -8960,6 +8970,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.90", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -9104,6 +9138,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "serde_json" version = "1.0.133" diff --git a/Cargo.toml b/Cargo.toml index a467fec..ad4095d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "rust/component_model", "rust/scenario_runner", "rust/plugin_runtime", + "rust/manifest_schema", ] [workspace.package] edition = "2021" diff --git a/docs/schema/plugin_manifest.schema.json b/docs/schema/plugin_manifest.schema.json new file mode 100644 index 0000000..a6cacdb --- /dev/null +++ b/docs/schema/plugin_manifest.schema.json @@ -0,0 +1,788 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PluginManifest", + "description": "Manifest structure for a plugin.", + "type": "object", + "required": [ + "entrypoint", + "gauntlet" + ], + "properties": { + "$schema": { + "type": [ + "string", + "null" + ] + }, + "entrypoint": { + "description": "Entrypoints for the plugin.", + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestEntrypoint" + } + }, + "gauntlet": { + "description": "Metadata about the plugin.", + "allOf": [ + { + "$ref": "#/definitions/PluginManifestMetadata" + } + ] + }, + "permissions": { + "description": "Permissions required by the plugin.", + "default": { + "environment": [], + "network": [], + "filesystem": { + "read": [], + "write": [] + }, + "exec": { + "command": [], + "executable": [] + }, + "system": [], + "clipboard": [], + "main_search_bar": [] + }, + "allOf": [ + { + "$ref": "#/definitions/PluginManifestPermissions" + } + ] + }, + "preferences": { + "description": "Preferences that can be configured by the user in the settings view.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestPreference" + } + }, + "supported_system": { + "description": "List of supported operating systems.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestSupportedSystem" + } + } + }, + "definitions": { + "PluginManifestAction": { + "description": "Action that can be performed by the plugin.", + "type": "object", + "required": [ + "description", + "id", + "shortcut" + ], + "properties": { + "description": { + "description": "Description of what the action does.", + "type": "string" + }, + "id": { + "description": "Unique identifier for the action.", + "type": "string" + }, + "shortcut": { + "description": "Keyboard shortcut to trigger the action.", + "allOf": [ + { + "$ref": "#/definitions/PluginManifestActionShortcut" + } + ] + } + } + }, + "PluginManifestActionShortcut": { + "description": "Keyboard shortcut configuration for a plugin action.", + "type": "object", + "required": [ + "key", + "kind" + ], + "properties": { + "key": { + "description": "The key to be pressed for this shortcut.", + "allOf": [ + { + "$ref": "#/definitions/PluginManifestActionShortcutKey" + } + ] + }, + "kind": { + "description": "The type of shortcut.", + "allOf": [ + { + "$ref": "#/definitions/PluginManifestActionShortcutKind" + } + ] + } + } + }, + "PluginManifestActionShortcutKey": { + "type": "string", + "enum": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "!", + "@", + "#", + "$", + "%", + "^", + "&", + "*", + "(", + ")", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "-", + "=", + ",", + ".", + "/", + "[", + "]", + ";", + "'", + "\\", + "_", + "+", + "<", + ">", + "?", + "{", + "}", + ":", + "\"", + "|" + ] + }, + "PluginManifestActionShortcutKind": { + "description": "The type of shortcut.", + "oneOf": [ + { + "description": "Main shortcut for the action (e.g. cmd).", + "type": "string", + "enum": [ + "main" + ] + }, + { + "description": "Alternative shortcut for the action (e.g. opt).", + "type": "string", + "enum": [ + "alternative" + ] + } + ] + }, + "PluginManifestClipboardPermissions": { + "description": "Clipboard permissions for the plugin.", + "oneOf": [ + { + "description": "Allows the plugin to read from the clipboard.", + "type": "string", + "enum": [ + "read" + ] + }, + { + "description": "Allows the plugin to write to the clipboard.", + "type": "string", + "enum": [ + "write" + ] + }, + { + "description": "Allows the plugin to clear the clipboard contents.", + "type": "string", + "enum": [ + "clear" + ] + } + ] + }, + "PluginManifestEntrypoint": { + "description": "An entrypoint for the plugin.", + "type": "object", + "required": [ + "description", + "id", + "name", + "path", + "type" + ], + "properties": { + "actions": { + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestAction" + } + }, + "description": { + "type": "string" + }, + "icon": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "preferences": { + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestPreference" + } + }, + "type": { + "$ref": "#/definitions/PluginManifestEntrypointTypes" + } + } + }, + "PluginManifestEntrypointTypes": { + "description": "Types of plugin entrypoints.", + "oneOf": [ + { + "description": "A command entrypoint.", + "type": "string", + "enum": [ + "command" + ] + }, + { + "description": "A view-based entrypoint.", + "type": "string", + "enum": [ + "view" + ] + }, + { + "description": "An inline view entrypoint.", + "type": "string", + "enum": [ + "inline-view" + ] + }, + { + "description": "Generates new entrypoints dynamically.", + "type": "string", + "enum": [ + "entrypoint-generator" + ] + } + ] + }, + "PluginManifestMainSearchBarPermissions": { + "oneOf": [ + { + "description": "Allows the plugin to read the main search bar", + "type": "string", + "enum": [ + "read" + ] + } + ] + }, + "PluginManifestMetadata": { + "description": "Metadata for the plugin manifest.", + "type": "object", + "required": [ + "description", + "name" + ], + "properties": { + "description": { + "description": "Description of the plugin.", + "type": "string" + }, + "name": { + "description": "Name of the plugin.", + "type": "string" + } + } + }, + "PluginManifestPermissions": { + "description": "Permissions required by the plugin.", + "type": "object", + "properties": { + "clipboard": { + "description": "Clipboard permissions for the plugin.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestClipboardPermissions" + } + }, + "environment": { + "description": "Environment variables that the plugin can access.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "exec": { + "description": "Execution permissions for the plugin.", + "default": { + "command": [], + "executable": [] + }, + "allOf": [ + { + "$ref": "#/definitions/PluginManifestPermissionsExec" + } + ] + }, + "filesystem": { + "description": "Filesystem permissions for the plugin.", + "default": { + "read": [], + "write": [] + }, + "allOf": [ + { + "$ref": "#/definitions/PluginManifestPermissionsFileSystem" + } + ] + }, + "main_search_bar": { + "description": "Permissions for the main search bar.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestMainSearchBarPermissions" + } + }, + "network": { + "description": "Network domains that the plugin can access.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "system": { + "description": "System permissions for the plugin.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PluginManifestPermissionsExec": { + "description": "Execution permissions for the plugin.", + "type": "object", + "properties": { + "command": { + "description": "Commands that the plugin can execute.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "executable": { + "description": "Executables that the plugin can run.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PluginManifestPermissionsFileSystem": { + "description": "Filesystem permissions for the plugin.", + "type": "object", + "properties": { + "read": { + "description": "Paths that the plugin can read from.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "write": { + "description": "Paths that the plugin can write to.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PluginManifestPreference": { + "description": "User-configurable preference options.", + "oneOf": [ + { + "description": "A numeric preference.", + "type": "object", + "required": [ + "description", + "id", + "name", + "type" + ], + "properties": { + "default": { + "type": [ + "number", + "null" + ], + "format": "double" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "number" + ] + } + } + }, + { + "description": "A string preference.", + "type": "object", + "required": [ + "description", + "id", + "name", + "type" + ], + "properties": { + "default": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "string" + ] + } + } + }, + { + "description": "An enum preference with selectable values.", + "type": "object", + "required": [ + "description", + "enum_values", + "id", + "name", + "type" + ], + "properties": { + "default": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": "string" + }, + "enum_values": { + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestPreferenceEnumValue" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "enum" + ] + } + } + }, + { + "description": "A boolean preference.", + "type": "object", + "required": [ + "description", + "id", + "name", + "type" + ], + "properties": { + "default": { + "type": [ + "boolean", + "null" + ] + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "bool" + ] + } + } + }, + { + "description": "A list of strings preference.", + "type": "object", + "required": [ + "description", + "id", + "name", + "type" + ], + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "list_of_strings" + ] + } + } + }, + { + "description": "A list of numbers preference.", + "type": "object", + "required": [ + "description", + "id", + "name", + "type" + ], + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "list_of_numbers" + ] + } + } + }, + { + "description": "A list of enumerated preference values.", + "type": "object", + "required": [ + "description", + "enum_values", + "id", + "name", + "type" + ], + "properties": { + "description": { + "type": "string" + }, + "enum_values": { + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestPreferenceEnumValue" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "list_of_enums" + ] + } + } + } + ] + }, + "PluginManifestPreferenceEnumValue": { + "description": "An enumerated preference value.", + "type": "object", + "required": [ + "label", + "value" + ], + "properties": { + "label": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "PluginManifestSupportedSystem": { + "oneOf": [ + { + "type": "object", + "required": [ + "os" + ], + "properties": { + "os": { + "type": "string", + "enum": [ + "linux" + ] + } + } + }, + { + "type": "object", + "required": [ + "os" + ], + "properties": { + "os": { + "type": "string", + "enum": [ + "windows" + ] + } + } + }, + { + "type": "object", + "required": [ + "os" + ], + "properties": { + "os": { + "type": "string", + "enum": [ + "macos" + ] + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/rust/manifest_schema/Cargo.toml b/rust/manifest_schema/Cargo.toml new file mode 100644 index 0000000..4090883 --- /dev/null +++ b/rust/manifest_schema/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "gauntlet-manifest-schema" +edition.workspace = true + +[dependencies] +# workspaces +gauntlet-server.workspace = true + +# other +schemars = "0.8" +serde_json = "1.0" + +[features] +release = ["gauntlet-server/release"] diff --git a/rust/manifest_schema/src/main.rs b/rust/manifest_schema/src/main.rs new file mode 100644 index 0000000..7ffbc1c --- /dev/null +++ b/rust/manifest_schema/src/main.rs @@ -0,0 +1,14 @@ +use std::io::Write; + +use gauntlet_server::plugins::plugin_manifest::PluginManifest; +use schemars::schema_for; + +fn main() { + let schema = schema_for!(PluginManifest); + let json = serde_json::to_string_pretty(&schema).unwrap(); + + std::fs::create_dir_all("../../docs/schema").expect("Failed to create directory"); + std::fs::write("../../docs/schema/plugin_manifest.schema.json", json.as_bytes()).expect("Failed to write schema"); + + println!("Schema generated and saved to schema.json"); +} diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index 52349af..fb0b56e 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -41,6 +41,7 @@ url = "2.5" ureq = "2.10" vergen-pretty = "0.3" dark-light = "1.1.1" +schemars = "0.8" [features] release = ["gauntlet-common/release", "gauntlet-plugin-runtime/release"] diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index a7b75f5..6eb0b69 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -35,7 +35,7 @@ use crate::rpc::BackendServerImpl; use crate::search::SearchIndex; pub(crate) mod model; -pub(crate) mod plugins; +pub mod plugins; pub mod rpc; pub(crate) mod search; diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index 2795cba..9d1144d 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -29,7 +29,7 @@ use uuid::Uuid; use crate::model::ActionShortcutKey; use crate::plugins::frecency::FrecencyItemStats; use crate::plugins::frecency::FrecencyMetaParams; -use crate::plugins::loader::PluginManifestActionShortcutKey; +use crate::plugins::plugin_manifest::PluginManifestActionShortcutKey; static MIGRATOR: Migrator = sqlx::migrate!("./db_migrations"); diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index af56075..ddce67a 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -46,6 +46,7 @@ use crate::plugins::data_db_repository::DbWritePlugin; use crate::plugins::data_db_repository::DbWritePluginAssetData; use crate::plugins::data_db_repository::DbWritePluginEntrypoint; use crate::plugins::download_status::DownloadStatusHolder; +use crate::plugins::plugin_manifest::*; pub struct PluginLoader { db_repository: DataDbRepository, @@ -894,496 +895,3 @@ struct PluginDownloadData { pub preferences: HashMap, pub preferences_user_data: HashMap, } - -#[derive(Debug, Deserialize)] -struct PluginManifest { - gauntlet: PluginManifestMetadata, - entrypoint: Vec, - #[serde(default)] - supported_system: Vec, - #[serde(default)] - permissions: PluginManifestPermissions, - #[serde(default)] - preferences: Vec, -} - -#[derive(Debug, Deserialize)] -struct PluginManifestEntrypoint { - id: String, - name: String, - description: String, - #[allow(unused)] // used when building plugin - path: String, - icon: Option, - #[serde(rename = "type")] - entrypoint_type: PluginManifestEntrypointTypes, - #[serde(default)] - preferences: Vec, - #[serde(default)] - actions: Vec, -} - -#[derive(Debug, Deserialize)] -#[serde(tag = "type")] -enum PluginManifestPreference { - #[serde(rename = "number")] - Number { - id: String, - name: String, - default: Option, - description: String, - }, - #[serde(rename = "string")] - String { - id: String, - name: String, - default: Option, - description: String, - }, - #[serde(rename = "enum")] - Enum { - id: String, - name: String, - default: Option, - description: String, - enum_values: Vec, - }, - #[serde(rename = "bool")] - Bool { - id: String, - name: String, - default: Option, - description: String, - }, - #[serde(rename = "list_of_strings")] - ListOfStrings { - id: String, - name: String, - // default: Option>, - description: String, - }, - #[serde(rename = "list_of_numbers")] - ListOfNumbers { - id: String, - name: String, - // default: Option>, - description: String, - }, - #[serde(rename = "list_of_enums")] - ListOfEnums { - id: String, - name: String, - // default: Option>, - enum_values: Vec, - description: String, - }, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct PluginManifestPreferenceEnumValue { - pub label: String, - pub value: String, -} - -#[derive(Debug, Deserialize)] -pub enum PluginManifestEntrypointTypes { - #[serde(rename = "command")] - Command, - #[serde(rename = "view")] - View, - #[serde(rename = "inline-view")] - InlineView, - #[serde(rename = "entrypoint-generator")] - EntrypointGenerator, -} - -#[derive(Debug, Deserialize)] -pub struct PluginManifestAction { - id: String, - description: String, - shortcut: PluginManifestActionShortcut, -} - -#[derive(Debug, Deserialize)] -pub struct PluginManifestActionShortcut { - key: PluginManifestActionShortcutKey, - kind: PluginManifestActionShortcutKind, -} - -// only stuff that is present on 60% keyboard -#[derive(Debug, Deserialize)] -pub enum PluginManifestActionShortcutKey { - #[serde(rename = "0")] - Num0, - #[serde(rename = "1")] - Num1, - #[serde(rename = "2")] - Num2, - #[serde(rename = "3")] - Num3, - #[serde(rename = "4")] - Num4, - #[serde(rename = "5")] - Num5, - #[serde(rename = "6")] - Num6, - #[serde(rename = "7")] - Num7, - #[serde(rename = "8")] - Num8, - #[serde(rename = "9")] - Num9, - - #[serde(rename = "!")] - Exclamation, - #[serde(rename = "@")] - AtSign, - #[serde(rename = "#")] - Hash, - #[serde(rename = "$")] - Dollar, - #[serde(rename = "%")] - Percent, - #[serde(rename = "^")] - Caret, - #[serde(rename = "&")] - Ampersand, - #[serde(rename = "*")] - Star, - #[serde(rename = "(")] - LeftParenthesis, - #[serde(rename = ")")] - RightParenthesis, - - #[serde(rename = "a")] - LowerA, - #[serde(rename = "b")] - LowerB, - #[serde(rename = "c")] - LowerC, - #[serde(rename = "d")] - LowerD, - #[serde(rename = "e")] - LowerE, - #[serde(rename = "f")] - LowerF, - #[serde(rename = "g")] - LowerG, - #[serde(rename = "h")] - LowerH, - #[serde(rename = "i")] - LowerI, - #[serde(rename = "j")] - LowerJ, - #[serde(rename = "k")] - LowerK, - #[serde(rename = "l")] - LowerL, - #[serde(rename = "m")] - LowerM, - #[serde(rename = "n")] - LowerN, - #[serde(rename = "o")] - LowerO, - #[serde(rename = "p")] - LowerP, - #[serde(rename = "q")] - LowerQ, - #[serde(rename = "r")] - LowerR, - #[serde(rename = "s")] - LowerS, - #[serde(rename = "t")] - LowerT, - #[serde(rename = "u")] - LowerU, - #[serde(rename = "v")] - LowerV, - #[serde(rename = "w")] - LowerW, - #[serde(rename = "x")] - LowerX, - #[serde(rename = "y")] - LowerY, - #[serde(rename = "z")] - LowerZ, - - #[serde(rename = "A")] - UpperA, - #[serde(rename = "B")] - UpperB, - #[serde(rename = "C")] - UpperC, - #[serde(rename = "D")] - UpperD, - #[serde(rename = "E")] - UpperE, - #[serde(rename = "F")] - UpperF, - #[serde(rename = "G")] - UpperG, - #[serde(rename = "H")] - UpperH, - #[serde(rename = "I")] - UpperI, - #[serde(rename = "J")] - UpperJ, - #[serde(rename = "K")] - UpperK, - #[serde(rename = "L")] - UpperL, - #[serde(rename = "M")] - UpperM, - #[serde(rename = "N")] - UpperN, - #[serde(rename = "O")] - UpperO, - #[serde(rename = "P")] - UpperP, - #[serde(rename = "Q")] - UpperQ, - #[serde(rename = "R")] - UpperR, - #[serde(rename = "S")] - UpperS, - #[serde(rename = "T")] - UpperT, - #[serde(rename = "U")] - UpperU, - #[serde(rename = "V")] - UpperV, - #[serde(rename = "W")] - UpperW, - #[serde(rename = "X")] - UpperX, - #[serde(rename = "Y")] - UpperY, - #[serde(rename = "Z")] - UpperZ, - - #[serde(rename = "-")] - Minus, - #[serde(rename = "=")] - Equals, - #[serde(rename = ",")] - Comma, - #[serde(rename = ".")] - Dot, - #[serde(rename = "/")] - Slash, - #[serde(rename = "[")] - OpenSquareBracket, - #[serde(rename = "]")] - CloseSquareBracket, - #[serde(rename = ";")] - Semicolon, - #[serde(rename = "'")] - Quote, - #[serde(rename = "\\")] - Backslash, - - #[serde(rename = "_")] - Underscore, - #[serde(rename = "+")] - Plus, - #[serde(rename = "<")] - LessThan, - #[serde(rename = ">")] - GreaterThan, - #[serde(rename = "?")] - QuestionMark, - #[serde(rename = "{")] - LeftBrace, - #[serde(rename = "}")] - RightBrace, - #[serde(rename = ":")] - Colon, - #[serde(rename = "\"")] - DoubleQuotes, - #[serde(rename = "|")] - Pipe, -} - -impl PluginManifestActionShortcutKey { - pub fn to_model(self) -> ActionShortcutKey { - match self { - PluginManifestActionShortcutKey::Num0 => ActionShortcutKey::Num0, - PluginManifestActionShortcutKey::Num1 => ActionShortcutKey::Num1, - PluginManifestActionShortcutKey::Num2 => ActionShortcutKey::Num2, - PluginManifestActionShortcutKey::Num3 => ActionShortcutKey::Num3, - PluginManifestActionShortcutKey::Num4 => ActionShortcutKey::Num4, - PluginManifestActionShortcutKey::Num5 => ActionShortcutKey::Num5, - PluginManifestActionShortcutKey::Num6 => ActionShortcutKey::Num6, - PluginManifestActionShortcutKey::Num7 => ActionShortcutKey::Num7, - PluginManifestActionShortcutKey::Num8 => ActionShortcutKey::Num8, - PluginManifestActionShortcutKey::Num9 => ActionShortcutKey::Num9, - PluginManifestActionShortcutKey::Exclamation => ActionShortcutKey::Exclamation, - PluginManifestActionShortcutKey::AtSign => ActionShortcutKey::AtSign, - PluginManifestActionShortcutKey::Hash => ActionShortcutKey::Hash, - PluginManifestActionShortcutKey::Dollar => ActionShortcutKey::Dollar, - PluginManifestActionShortcutKey::Percent => ActionShortcutKey::Percent, - PluginManifestActionShortcutKey::Caret => ActionShortcutKey::Caret, - PluginManifestActionShortcutKey::Ampersand => ActionShortcutKey::Ampersand, - PluginManifestActionShortcutKey::Star => ActionShortcutKey::Star, - PluginManifestActionShortcutKey::LeftParenthesis => ActionShortcutKey::LeftParenthesis, - PluginManifestActionShortcutKey::RightParenthesis => ActionShortcutKey::RightParenthesis, - PluginManifestActionShortcutKey::LowerA => ActionShortcutKey::LowerA, - PluginManifestActionShortcutKey::LowerB => ActionShortcutKey::LowerB, - PluginManifestActionShortcutKey::LowerC => ActionShortcutKey::LowerC, - PluginManifestActionShortcutKey::LowerD => ActionShortcutKey::LowerD, - PluginManifestActionShortcutKey::LowerE => ActionShortcutKey::LowerE, - PluginManifestActionShortcutKey::LowerF => ActionShortcutKey::LowerF, - PluginManifestActionShortcutKey::LowerG => ActionShortcutKey::LowerG, - PluginManifestActionShortcutKey::LowerH => ActionShortcutKey::LowerH, - PluginManifestActionShortcutKey::LowerI => ActionShortcutKey::LowerI, - PluginManifestActionShortcutKey::LowerJ => ActionShortcutKey::LowerJ, - PluginManifestActionShortcutKey::LowerK => ActionShortcutKey::LowerK, - PluginManifestActionShortcutKey::LowerL => ActionShortcutKey::LowerL, - PluginManifestActionShortcutKey::LowerM => ActionShortcutKey::LowerM, - PluginManifestActionShortcutKey::LowerN => ActionShortcutKey::LowerN, - PluginManifestActionShortcutKey::LowerO => ActionShortcutKey::LowerO, - PluginManifestActionShortcutKey::LowerP => ActionShortcutKey::LowerP, - PluginManifestActionShortcutKey::LowerQ => ActionShortcutKey::LowerQ, - PluginManifestActionShortcutKey::LowerR => ActionShortcutKey::LowerR, - PluginManifestActionShortcutKey::LowerS => ActionShortcutKey::LowerS, - PluginManifestActionShortcutKey::LowerT => ActionShortcutKey::LowerT, - PluginManifestActionShortcutKey::LowerU => ActionShortcutKey::LowerU, - PluginManifestActionShortcutKey::LowerV => ActionShortcutKey::LowerV, - PluginManifestActionShortcutKey::LowerW => ActionShortcutKey::LowerW, - PluginManifestActionShortcutKey::LowerX => ActionShortcutKey::LowerX, - PluginManifestActionShortcutKey::LowerY => ActionShortcutKey::LowerY, - PluginManifestActionShortcutKey::LowerZ => ActionShortcutKey::LowerZ, - PluginManifestActionShortcutKey::UpperA => ActionShortcutKey::UpperA, - PluginManifestActionShortcutKey::UpperB => ActionShortcutKey::UpperB, - PluginManifestActionShortcutKey::UpperC => ActionShortcutKey::UpperC, - PluginManifestActionShortcutKey::UpperD => ActionShortcutKey::UpperD, - PluginManifestActionShortcutKey::UpperE => ActionShortcutKey::UpperE, - PluginManifestActionShortcutKey::UpperF => ActionShortcutKey::UpperF, - PluginManifestActionShortcutKey::UpperG => ActionShortcutKey::UpperG, - PluginManifestActionShortcutKey::UpperH => ActionShortcutKey::UpperH, - PluginManifestActionShortcutKey::UpperI => ActionShortcutKey::UpperI, - PluginManifestActionShortcutKey::UpperJ => ActionShortcutKey::UpperJ, - PluginManifestActionShortcutKey::UpperK => ActionShortcutKey::UpperK, - PluginManifestActionShortcutKey::UpperL => ActionShortcutKey::UpperL, - PluginManifestActionShortcutKey::UpperM => ActionShortcutKey::UpperM, - PluginManifestActionShortcutKey::UpperN => ActionShortcutKey::UpperN, - PluginManifestActionShortcutKey::UpperO => ActionShortcutKey::UpperO, - PluginManifestActionShortcutKey::UpperP => ActionShortcutKey::UpperP, - PluginManifestActionShortcutKey::UpperQ => ActionShortcutKey::UpperQ, - PluginManifestActionShortcutKey::UpperR => ActionShortcutKey::UpperR, - PluginManifestActionShortcutKey::UpperS => ActionShortcutKey::UpperS, - PluginManifestActionShortcutKey::UpperT => ActionShortcutKey::UpperT, - PluginManifestActionShortcutKey::UpperU => ActionShortcutKey::UpperU, - PluginManifestActionShortcutKey::UpperV => ActionShortcutKey::UpperV, - PluginManifestActionShortcutKey::UpperW => ActionShortcutKey::UpperW, - PluginManifestActionShortcutKey::UpperX => ActionShortcutKey::UpperX, - PluginManifestActionShortcutKey::UpperY => ActionShortcutKey::UpperY, - PluginManifestActionShortcutKey::UpperZ => ActionShortcutKey::UpperZ, - PluginManifestActionShortcutKey::Minus => ActionShortcutKey::Minus, - PluginManifestActionShortcutKey::Equals => ActionShortcutKey::Equals, - PluginManifestActionShortcutKey::Comma => ActionShortcutKey::Comma, - PluginManifestActionShortcutKey::Dot => ActionShortcutKey::Dot, - PluginManifestActionShortcutKey::Slash => ActionShortcutKey::Slash, - PluginManifestActionShortcutKey::OpenSquareBracket => ActionShortcutKey::OpenSquareBracket, - PluginManifestActionShortcutKey::CloseSquareBracket => ActionShortcutKey::CloseSquareBracket, - PluginManifestActionShortcutKey::Semicolon => ActionShortcutKey::Semicolon, - PluginManifestActionShortcutKey::Quote => ActionShortcutKey::Quote, - PluginManifestActionShortcutKey::Backslash => ActionShortcutKey::Backslash, - PluginManifestActionShortcutKey::Underscore => ActionShortcutKey::Underscore, - PluginManifestActionShortcutKey::Plus => ActionShortcutKey::Plus, - PluginManifestActionShortcutKey::LessThan => ActionShortcutKey::LessThan, - PluginManifestActionShortcutKey::GreaterThan => ActionShortcutKey::GreaterThan, - PluginManifestActionShortcutKey::QuestionMark => ActionShortcutKey::QuestionMark, - PluginManifestActionShortcutKey::LeftBrace => ActionShortcutKey::LeftBrace, - PluginManifestActionShortcutKey::RightBrace => ActionShortcutKey::RightBrace, - PluginManifestActionShortcutKey::Colon => ActionShortcutKey::Colon, - PluginManifestActionShortcutKey::DoubleQuotes => ActionShortcutKey::DoubleQuotes, - PluginManifestActionShortcutKey::Pipe => ActionShortcutKey::Pipe, - } - } -} - -#[derive(Debug, Deserialize)] -pub enum PluginManifestActionShortcutKind { - #[serde(rename = "main")] - Main, - #[serde(rename = "alternative")] - Alternative, -} - -#[derive(Debug, Deserialize, PartialEq, Eq)] -#[serde(tag = "os")] -pub enum PluginManifestSupportedSystem { - #[serde(rename = "linux")] - Linux, - #[serde(rename = "windows")] - Windows, - #[serde(rename = "macos")] - MacOS, -} - -impl std::fmt::Display for PluginManifestSupportedSystem { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - PluginManifestSupportedSystem::Linux => write!(f, "Linux"), - PluginManifestSupportedSystem::Windows => write!(f, "Windows"), - PluginManifestSupportedSystem::MacOS => write!(f, "MacOS"), - } - } -} - -#[derive(Debug, Deserialize)] -struct PluginManifestMetadata { - name: String, - description: String, -} - -#[derive(Debug, Deserialize, Default)] -pub struct PluginManifestPermissions { - #[serde(default)] - environment: Vec, - #[serde(default)] - network: Vec, - #[serde(default)] - filesystem: PluginManifestPermissionsFileSystem, - #[serde(default)] - exec: PluginManifestPermissionsExec, - #[serde(default)] - system: Vec, - #[serde(default)] - clipboard: Vec, - #[serde(default)] - main_search_bar: Vec, -} - -#[derive(Debug, Deserialize, Default)] -pub struct PluginManifestPermissionsFileSystem { - #[serde(default)] - pub read: Vec, - #[serde(default)] - pub write: Vec, -} - -#[derive(Debug, Deserialize, Default)] -pub struct PluginManifestPermissionsExec { - #[serde(default)] - pub command: Vec, - #[serde(default)] - pub executable: Vec, -} - -#[derive(Debug, Deserialize)] -pub enum PluginManifestClipboardPermissions { - #[serde(rename = "read")] - Read, - #[serde(rename = "write")] - Write, - #[serde(rename = "clear")] - Clear, -} - -#[derive(Debug, Deserialize, Eq, PartialEq)] -pub enum PluginManifestMainSearchBarPermissions { - #[serde(rename = "read")] - Read, -} diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index b2acd60..f0e7b19 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -83,6 +83,7 @@ mod icon_cache; mod image_gatherer; pub mod js; mod loader; +pub mod plugin_manifest; mod run_status; mod runtime; mod settings; diff --git a/rust/server/src/plugins/plugin_manifest.rs b/rust/server/src/plugins/plugin_manifest.rs new file mode 100644 index 0000000..0665ca2 --- /dev/null +++ b/rust/server/src/plugins/plugin_manifest.rs @@ -0,0 +1,553 @@ +use schemars::JsonSchema; +use serde::Deserialize; +use serde::Serialize; + +use crate::model::ActionShortcutKey; + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "Manifest structure for a plugin.")] +pub struct PluginManifest { + #[serde(rename = "$schema")] + schema: Option, + #[schemars(description = "Metadata about the plugin.")] + pub gauntlet: PluginManifestMetadata, + #[schemars(description = "Entrypoints for the plugin.")] + pub entrypoint: Vec, + #[serde(default)] + #[schemars(description = "List of supported operating systems.")] + pub supported_system: Vec, + #[serde(default)] + #[schemars(description = "Permissions required by the plugin.")] + pub permissions: PluginManifestPermissions, + #[serde(default)] + #[schemars(description = "Preferences that can be configured by the user in the settings view.")] + pub preferences: Vec, +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "An entrypoint for the plugin.")] +pub struct PluginManifestEntrypoint { + pub id: String, + pub name: String, + pub description: String, + #[allow(unused)] // Used during plugin build + pub path: String, + pub icon: Option, + #[serde(rename = "type")] + pub entrypoint_type: PluginManifestEntrypointTypes, + #[serde(default)] + pub preferences: Vec, + #[serde(default)] + pub actions: Vec, +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[serde(tag = "type")] +#[schemars(description = "User-configurable preference options.")] +pub enum PluginManifestPreference { + #[serde(rename = "number")] + #[schemars(description = "A numeric preference.")] + Number { + id: String, + name: String, + default: Option, + description: String, + }, + #[serde(rename = "string")] + #[schemars(description = "A string preference.")] + String { + id: String, + name: String, + default: Option, + description: String, + }, + #[serde(rename = "enum")] + #[schemars(description = "An enum preference with selectable values.")] + Enum { + id: String, + name: String, + default: Option, + description: String, + enum_values: Vec, + }, + #[serde(rename = "bool")] + #[schemars(description = "A boolean preference.")] + Bool { + id: String, + name: String, + default: Option, + description: String, + }, + #[serde(rename = "list_of_strings")] + #[schemars(description = "A list of strings preference.")] + ListOfStrings { + id: String, + name: String, + // default: Option>, + description: String, + }, + #[serde(rename = "list_of_numbers")] + #[schemars(description = "A list of numbers preference.")] + ListOfNumbers { + id: String, + name: String, + // default: Option>, + description: String, + }, + #[serde(rename = "list_of_enums")] + #[schemars(description = "A list of enumerated preference values.")] + ListOfEnums { + id: String, + name: String, + // default: Option>, + enum_values: Vec, + description: String, + }, +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "An enumerated preference value.")] +pub struct PluginManifestPreferenceEnumValue { + pub label: String, + pub value: String, +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "Types of plugin entrypoints.")] +pub enum PluginManifestEntrypointTypes { + #[serde(rename = "command")] + #[schemars(description = "A command entrypoint.")] + Command, + #[serde(rename = "view")] + #[schemars(description = "A view-based entrypoint.")] + View, + #[serde(rename = "inline-view")] + #[schemars(description = "An inline view entrypoint.")] + InlineView, + #[serde(rename = "entrypoint-generator")] + #[schemars(description = "Generates new entrypoints dynamically.")] + EntrypointGenerator, +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "Action that can be performed by the plugin.")] +pub struct PluginManifestAction { + #[schemars(description = "Unique identifier for the action.")] + pub id: String, + #[schemars(description = "Description of what the action does.")] + pub description: String, + #[schemars(description = "Keyboard shortcut to trigger the action.")] + pub shortcut: PluginManifestActionShortcut, +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "Keyboard shortcut configuration for a plugin action.")] +pub struct PluginManifestActionShortcut { + #[schemars(description = "The key to be pressed for this shortcut.")] + pub key: PluginManifestActionShortcutKey, + #[schemars(description = "The type of shortcut.")] + pub kind: PluginManifestActionShortcutKind, +} + +// only stuff that is present on 60% keyboard +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +pub enum PluginManifestActionShortcutKey { + #[serde(rename = "0")] + Num0, + #[serde(rename = "1")] + Num1, + #[serde(rename = "2")] + Num2, + #[serde(rename = "3")] + Num3, + #[serde(rename = "4")] + Num4, + #[serde(rename = "5")] + Num5, + #[serde(rename = "6")] + Num6, + #[serde(rename = "7")] + Num7, + #[serde(rename = "8")] + Num8, + #[serde(rename = "9")] + Num9, + + #[serde(rename = "!")] + Exclamation, + #[serde(rename = "@")] + AtSign, + #[serde(rename = "#")] + Hash, + #[serde(rename = "$")] + Dollar, + #[serde(rename = "%")] + Percent, + #[serde(rename = "^")] + Caret, + #[serde(rename = "&")] + Ampersand, + #[serde(rename = "*")] + Star, + #[serde(rename = "(")] + LeftParenthesis, + #[serde(rename = ")")] + RightParenthesis, + + #[serde(rename = "a")] + LowerA, + #[serde(rename = "b")] + LowerB, + #[serde(rename = "c")] + LowerC, + #[serde(rename = "d")] + LowerD, + #[serde(rename = "e")] + LowerE, + #[serde(rename = "f")] + LowerF, + #[serde(rename = "g")] + LowerG, + #[serde(rename = "h")] + LowerH, + #[serde(rename = "i")] + LowerI, + #[serde(rename = "j")] + LowerJ, + #[serde(rename = "k")] + LowerK, + #[serde(rename = "l")] + LowerL, + #[serde(rename = "m")] + LowerM, + #[serde(rename = "n")] + LowerN, + #[serde(rename = "o")] + LowerO, + #[serde(rename = "p")] + LowerP, + #[serde(rename = "q")] + LowerQ, + #[serde(rename = "r")] + LowerR, + #[serde(rename = "s")] + LowerS, + #[serde(rename = "t")] + LowerT, + #[serde(rename = "u")] + LowerU, + #[serde(rename = "v")] + LowerV, + #[serde(rename = "w")] + LowerW, + #[serde(rename = "x")] + LowerX, + #[serde(rename = "y")] + LowerY, + #[serde(rename = "z")] + LowerZ, + + #[serde(rename = "A")] + UpperA, + #[serde(rename = "B")] + UpperB, + #[serde(rename = "C")] + UpperC, + #[serde(rename = "D")] + UpperD, + #[serde(rename = "E")] + UpperE, + #[serde(rename = "F")] + UpperF, + #[serde(rename = "G")] + UpperG, + #[serde(rename = "H")] + UpperH, + #[serde(rename = "I")] + UpperI, + #[serde(rename = "J")] + UpperJ, + #[serde(rename = "K")] + UpperK, + #[serde(rename = "L")] + UpperL, + #[serde(rename = "M")] + UpperM, + #[serde(rename = "N")] + UpperN, + #[serde(rename = "O")] + UpperO, + #[serde(rename = "P")] + UpperP, + #[serde(rename = "Q")] + UpperQ, + #[serde(rename = "R")] + UpperR, + #[serde(rename = "S")] + UpperS, + #[serde(rename = "T")] + UpperT, + #[serde(rename = "U")] + UpperU, + #[serde(rename = "V")] + UpperV, + #[serde(rename = "W")] + UpperW, + #[serde(rename = "X")] + UpperX, + #[serde(rename = "Y")] + UpperY, + #[serde(rename = "Z")] + UpperZ, + + #[serde(rename = "-")] + Minus, + #[serde(rename = "=")] + Equals, + #[serde(rename = ",")] + Comma, + #[serde(rename = ".")] + Dot, + #[serde(rename = "/")] + Slash, + #[serde(rename = "[")] + OpenSquareBracket, + #[serde(rename = "]")] + CloseSquareBracket, + #[serde(rename = ";")] + Semicolon, + #[serde(rename = "'")] + Quote, + #[serde(rename = "\\")] + Backslash, + + #[serde(rename = "_")] + Underscore, + #[serde(rename = "+")] + Plus, + #[serde(rename = "<")] + LessThan, + #[serde(rename = ">")] + GreaterThan, + #[serde(rename = "?")] + QuestionMark, + #[serde(rename = "{")] + LeftBrace, + #[serde(rename = "}")] + RightBrace, + #[serde(rename = ":")] + Colon, + #[serde(rename = "\"")] + DoubleQuotes, + #[serde(rename = "|")] + Pipe, +} + +impl PluginManifestActionShortcutKey { + pub fn to_model(self) -> ActionShortcutKey { + match self { + PluginManifestActionShortcutKey::Num0 => ActionShortcutKey::Num0, + PluginManifestActionShortcutKey::Num1 => ActionShortcutKey::Num1, + PluginManifestActionShortcutKey::Num2 => ActionShortcutKey::Num2, + PluginManifestActionShortcutKey::Num3 => ActionShortcutKey::Num3, + PluginManifestActionShortcutKey::Num4 => ActionShortcutKey::Num4, + PluginManifestActionShortcutKey::Num5 => ActionShortcutKey::Num5, + PluginManifestActionShortcutKey::Num6 => ActionShortcutKey::Num6, + PluginManifestActionShortcutKey::Num7 => ActionShortcutKey::Num7, + PluginManifestActionShortcutKey::Num8 => ActionShortcutKey::Num8, + PluginManifestActionShortcutKey::Num9 => ActionShortcutKey::Num9, + PluginManifestActionShortcutKey::Exclamation => ActionShortcutKey::Exclamation, + PluginManifestActionShortcutKey::AtSign => ActionShortcutKey::AtSign, + PluginManifestActionShortcutKey::Hash => ActionShortcutKey::Hash, + PluginManifestActionShortcutKey::Dollar => ActionShortcutKey::Dollar, + PluginManifestActionShortcutKey::Percent => ActionShortcutKey::Percent, + PluginManifestActionShortcutKey::Caret => ActionShortcutKey::Caret, + PluginManifestActionShortcutKey::Ampersand => ActionShortcutKey::Ampersand, + PluginManifestActionShortcutKey::Star => ActionShortcutKey::Star, + PluginManifestActionShortcutKey::LeftParenthesis => ActionShortcutKey::LeftParenthesis, + PluginManifestActionShortcutKey::RightParenthesis => ActionShortcutKey::RightParenthesis, + PluginManifestActionShortcutKey::LowerA => ActionShortcutKey::LowerA, + PluginManifestActionShortcutKey::LowerB => ActionShortcutKey::LowerB, + PluginManifestActionShortcutKey::LowerC => ActionShortcutKey::LowerC, + PluginManifestActionShortcutKey::LowerD => ActionShortcutKey::LowerD, + PluginManifestActionShortcutKey::LowerE => ActionShortcutKey::LowerE, + PluginManifestActionShortcutKey::LowerF => ActionShortcutKey::LowerF, + PluginManifestActionShortcutKey::LowerG => ActionShortcutKey::LowerG, + PluginManifestActionShortcutKey::LowerH => ActionShortcutKey::LowerH, + PluginManifestActionShortcutKey::LowerI => ActionShortcutKey::LowerI, + PluginManifestActionShortcutKey::LowerJ => ActionShortcutKey::LowerJ, + PluginManifestActionShortcutKey::LowerK => ActionShortcutKey::LowerK, + PluginManifestActionShortcutKey::LowerL => ActionShortcutKey::LowerL, + PluginManifestActionShortcutKey::LowerM => ActionShortcutKey::LowerM, + PluginManifestActionShortcutKey::LowerN => ActionShortcutKey::LowerN, + PluginManifestActionShortcutKey::LowerO => ActionShortcutKey::LowerO, + PluginManifestActionShortcutKey::LowerP => ActionShortcutKey::LowerP, + PluginManifestActionShortcutKey::LowerQ => ActionShortcutKey::LowerQ, + PluginManifestActionShortcutKey::LowerR => ActionShortcutKey::LowerR, + PluginManifestActionShortcutKey::LowerS => ActionShortcutKey::LowerS, + PluginManifestActionShortcutKey::LowerT => ActionShortcutKey::LowerT, + PluginManifestActionShortcutKey::LowerU => ActionShortcutKey::LowerU, + PluginManifestActionShortcutKey::LowerV => ActionShortcutKey::LowerV, + PluginManifestActionShortcutKey::LowerW => ActionShortcutKey::LowerW, + PluginManifestActionShortcutKey::LowerX => ActionShortcutKey::LowerX, + PluginManifestActionShortcutKey::LowerY => ActionShortcutKey::LowerY, + PluginManifestActionShortcutKey::LowerZ => ActionShortcutKey::LowerZ, + PluginManifestActionShortcutKey::UpperA => ActionShortcutKey::UpperA, + PluginManifestActionShortcutKey::UpperB => ActionShortcutKey::UpperB, + PluginManifestActionShortcutKey::UpperC => ActionShortcutKey::UpperC, + PluginManifestActionShortcutKey::UpperD => ActionShortcutKey::UpperD, + PluginManifestActionShortcutKey::UpperE => ActionShortcutKey::UpperE, + PluginManifestActionShortcutKey::UpperF => ActionShortcutKey::UpperF, + PluginManifestActionShortcutKey::UpperG => ActionShortcutKey::UpperG, + PluginManifestActionShortcutKey::UpperH => ActionShortcutKey::UpperH, + PluginManifestActionShortcutKey::UpperI => ActionShortcutKey::UpperI, + PluginManifestActionShortcutKey::UpperJ => ActionShortcutKey::UpperJ, + PluginManifestActionShortcutKey::UpperK => ActionShortcutKey::UpperK, + PluginManifestActionShortcutKey::UpperL => ActionShortcutKey::UpperL, + PluginManifestActionShortcutKey::UpperM => ActionShortcutKey::UpperM, + PluginManifestActionShortcutKey::UpperN => ActionShortcutKey::UpperN, + PluginManifestActionShortcutKey::UpperO => ActionShortcutKey::UpperO, + PluginManifestActionShortcutKey::UpperP => ActionShortcutKey::UpperP, + PluginManifestActionShortcutKey::UpperQ => ActionShortcutKey::UpperQ, + PluginManifestActionShortcutKey::UpperR => ActionShortcutKey::UpperR, + PluginManifestActionShortcutKey::UpperS => ActionShortcutKey::UpperS, + PluginManifestActionShortcutKey::UpperT => ActionShortcutKey::UpperT, + PluginManifestActionShortcutKey::UpperU => ActionShortcutKey::UpperU, + PluginManifestActionShortcutKey::UpperV => ActionShortcutKey::UpperV, + PluginManifestActionShortcutKey::UpperW => ActionShortcutKey::UpperW, + PluginManifestActionShortcutKey::UpperX => ActionShortcutKey::UpperX, + PluginManifestActionShortcutKey::UpperY => ActionShortcutKey::UpperY, + PluginManifestActionShortcutKey::UpperZ => ActionShortcutKey::UpperZ, + PluginManifestActionShortcutKey::Minus => ActionShortcutKey::Minus, + PluginManifestActionShortcutKey::Equals => ActionShortcutKey::Equals, + PluginManifestActionShortcutKey::Comma => ActionShortcutKey::Comma, + PluginManifestActionShortcutKey::Dot => ActionShortcutKey::Dot, + PluginManifestActionShortcutKey::Slash => ActionShortcutKey::Slash, + PluginManifestActionShortcutKey::OpenSquareBracket => ActionShortcutKey::OpenSquareBracket, + PluginManifestActionShortcutKey::CloseSquareBracket => ActionShortcutKey::CloseSquareBracket, + PluginManifestActionShortcutKey::Semicolon => ActionShortcutKey::Semicolon, + PluginManifestActionShortcutKey::Quote => ActionShortcutKey::Quote, + PluginManifestActionShortcutKey::Backslash => ActionShortcutKey::Backslash, + PluginManifestActionShortcutKey::Underscore => ActionShortcutKey::Underscore, + PluginManifestActionShortcutKey::Plus => ActionShortcutKey::Plus, + PluginManifestActionShortcutKey::LessThan => ActionShortcutKey::LessThan, + PluginManifestActionShortcutKey::GreaterThan => ActionShortcutKey::GreaterThan, + PluginManifestActionShortcutKey::QuestionMark => ActionShortcutKey::QuestionMark, + PluginManifestActionShortcutKey::LeftBrace => ActionShortcutKey::LeftBrace, + PluginManifestActionShortcutKey::RightBrace => ActionShortcutKey::RightBrace, + PluginManifestActionShortcutKey::Colon => ActionShortcutKey::Colon, + PluginManifestActionShortcutKey::DoubleQuotes => ActionShortcutKey::DoubleQuotes, + PluginManifestActionShortcutKey::Pipe => ActionShortcutKey::Pipe, + } + } +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "The type of shortcut.")] +pub enum PluginManifestActionShortcutKind { + #[serde(rename = "main")] + #[schemars(description = "Main shortcut for the action (e.g. cmd).")] + Main, + #[serde(rename = "alternative")] + #[schemars(description = "Alternative shortcut for the action (e.g. opt).")] + Alternative, +} + +#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[serde(tag = "os")] +pub enum PluginManifestSupportedSystem { + #[serde(rename = "linux")] + Linux, + #[serde(rename = "windows")] + Windows, + #[serde(rename = "macos")] + MacOS, +} + +impl std::fmt::Display for PluginManifestSupportedSystem { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + PluginManifestSupportedSystem::Linux => write!(f, "Linux"), + PluginManifestSupportedSystem::Windows => write!(f, "Windows"), + PluginManifestSupportedSystem::MacOS => write!(f, "MacOS"), + } + } +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "Metadata for the plugin manifest.")] +pub struct PluginManifestMetadata { + #[schemars(description = "Name of the plugin.")] + pub name: String, + #[schemars(description = "Description of the plugin.")] + pub description: String, +} + +#[derive(Debug, Deserialize, Default, Serialize, JsonSchema)] +#[schemars(description = "Permissions required by the plugin.")] +pub struct PluginManifestPermissions { + #[serde(default)] + #[schemars(description = "Environment variables that the plugin can access.")] + pub environment: Vec, + #[serde(default)] + #[schemars(description = "Network domains that the plugin can access.")] + pub network: Vec, + #[serde(default)] + #[schemars(description = "Filesystem permissions for the plugin.")] + pub filesystem: PluginManifestPermissionsFileSystem, + #[serde(default)] + #[schemars(description = "Execution permissions for the plugin.")] + pub exec: PluginManifestPermissionsExec, + #[serde(default)] + #[schemars(description = "System permissions for the plugin.")] + pub system: Vec, + #[serde(default)] + #[schemars(description = "Clipboard permissions for the plugin.")] + pub clipboard: Vec, + #[serde(default)] + #[schemars(description = "Permissions for the main search bar.")] + pub main_search_bar: Vec, +} + +#[derive(Debug, Deserialize, Default, Serialize, JsonSchema)] +#[schemars(description = "Filesystem permissions for the plugin.")] +pub struct PluginManifestPermissionsFileSystem { + #[serde(default)] + #[schemars(description = "Paths that the plugin can read from.")] + pub read: Vec, + #[serde(default)] + #[schemars(description = "Paths that the plugin can write to.")] + pub write: Vec, +} + +#[derive(Debug, Deserialize, Default, Serialize, JsonSchema)] +#[schemars(description = "Execution permissions for the plugin.")] +pub struct PluginManifestPermissionsExec { + #[serde(default)] + #[schemars(description = "Commands that the plugin can execute.")] + pub command: Vec, + #[serde(default)] + #[schemars(description = "Executables that the plugin can run.")] + pub executable: Vec, +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "Clipboard permissions for the plugin.")] +pub enum PluginManifestClipboardPermissions { + #[serde(rename = "read")] + #[schemars(description = "Allows the plugin to read from the clipboard.")] + Read, + #[serde(rename = "write")] + #[schemars(description = "Allows the plugin to write to the clipboard.")] + Write, + #[serde(rename = "clear")] + #[schemars(description = "Allows the plugin to clear the clipboard contents.")] + Clear, +} + +#[derive(Debug, Deserialize, Eq, PartialEq, Serialize, JsonSchema)] +pub enum PluginManifestMainSearchBarPermissions { + #[serde(rename = "read")] + #[schemars(description = "Allows the plugin to read the main search bar")] + Read, +} From 338540cdec63fb38a4698698bf71c4046a42decf Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:39:21 +0100 Subject: [PATCH 018/170] Fix plugin examples after breaking changes --- rust/scenario_runner/src/frontend_mock.rs | 2 +- .../docs_grid/src/content_code_block.tsx | 2 +- .../plugins/docs_grid/src/content_headers.tsx | 12 +++++----- .../plugins/docs_grid/src/content_image.tsx | 2 +- .../docs_grid/src/content_paragraph.tsx | 2 +- scenarios/plugins/docs_grid/src/main.tsx | 2 +- .../plugins/docs_grid/src/search_bar.tsx | 2 +- scenarios/plugins/docs_grid/src/section.tsx | 22 +++++++++---------- scenarios/plugins/docs_list/src/detail.tsx | 20 ++++++++--------- scenarios/plugins/docs_list/src/main.tsx | 20 ++++++++--------- .../plugins/docs_list/src/search_bar.tsx | 2 +- scenarios/plugins/docs_list/src/section.tsx | 16 +++++++------- 12 files changed, 52 insertions(+), 52 deletions(-) diff --git a/rust/scenario_runner/src/frontend_mock.rs b/rust/scenario_runner/src/frontend_mock.rs index 55d6403..c108972 100644 --- a/rust/scenario_runner/src/frontend_mock.rs +++ b/rust/scenario_runner/src/frontend_mock.rs @@ -160,7 +160,7 @@ async fn request_loop( | UiRequestData::SetTheme { .. } => { unreachable!() } - UiRequestData::SetGlobalShortcut { .. } | UiRequestData::RequestSearchResultUpdate => { + UiRequestData::SetGlobalShortcut { .. } | UiRequestData::SetWindowPositionMode { .. } | UiRequestData::RequestSearchResultUpdate => { // noop } UiRequestData::ReplaceView { diff --git a/scenarios/plugins/docs_grid/src/content_code_block.tsx b/scenarios/plugins/docs_grid/src/content_code_block.tsx index 6e793e9..3127d08 100644 --- a/scenarios/plugins/docs_grid/src/content_code_block.tsx +++ b/scenarios/plugins/docs_grid/src/content_code_block.tsx @@ -14,7 +14,7 @@ export default function Main(): ReactElement { return ( {items.map(value => ( - + {value} diff --git a/scenarios/plugins/docs_grid/src/content_headers.tsx b/scenarios/plugins/docs_grid/src/content_headers.tsx index 1c9b0c7..db65312 100644 --- a/scenarios/plugins/docs_grid/src/content_headers.tsx +++ b/scenarios/plugins/docs_grid/src/content_headers.tsx @@ -4,42 +4,42 @@ import { Grid } from "@project-gauntlet/api/components"; export default function Main(): ReactElement { return ( - + Episode I - + Episode II - + Episode III - + Episode IV - + Episode V - + Episode VI diff --git a/scenarios/plugins/docs_grid/src/content_image.tsx b/scenarios/plugins/docs_grid/src/content_image.tsx index e5a50ac..60e0ac0 100644 --- a/scenarios/plugins/docs_grid/src/content_image.tsx +++ b/scenarios/plugins/docs_grid/src/content_image.tsx @@ -36,7 +36,7 @@ export default function Main(): ReactElement { return ( {items.map(value => ( - + diff --git a/scenarios/plugins/docs_grid/src/content_paragraph.tsx b/scenarios/plugins/docs_grid/src/content_paragraph.tsx index 3c4a339..44d76f4 100644 --- a/scenarios/plugins/docs_grid/src/content_paragraph.tsx +++ b/scenarios/plugins/docs_grid/src/content_paragraph.tsx @@ -14,7 +14,7 @@ export default function Main(): ReactElement { return ( {items.map(value => ( - + {value} diff --git a/scenarios/plugins/docs_grid/src/main.tsx b/scenarios/plugins/docs_grid/src/main.tsx index e5a50ac..60e0ac0 100644 --- a/scenarios/plugins/docs_grid/src/main.tsx +++ b/scenarios/plugins/docs_grid/src/main.tsx @@ -36,7 +36,7 @@ export default function Main(): ReactElement { return ( {items.map(value => ( - + diff --git a/scenarios/plugins/docs_grid/src/search_bar.tsx b/scenarios/plugins/docs_grid/src/search_bar.tsx index ca19caa..ce67753 100644 --- a/scenarios/plugins/docs_grid/src/search_bar.tsx +++ b/scenarios/plugins/docs_grid/src/search_bar.tsx @@ -23,7 +23,7 @@ export default function Main(): ReactElement { {results .filter(value => !searchText ? true : value.toLowerCase().includes(searchText)) .map(value => ( - + {value} diff --git a/scenarios/plugins/docs_grid/src/section.tsx b/scenarios/plugins/docs_grid/src/section.tsx index 55e79ab..2d0ab75 100644 --- a/scenarios/plugins/docs_grid/src/section.tsx +++ b/scenarios/plugins/docs_grid/src/section.tsx @@ -18,59 +18,59 @@ export default function Main(): ReactElement { return ( - + - + - + - + - + - + - + - + - + - + - + diff --git a/scenarios/plugins/docs_list/src/detail.tsx b/scenarios/plugins/docs_list/src/detail.tsx index d5d5854..72d6a6b 100644 --- a/scenarios/plugins/docs_list/src/detail.tsx +++ b/scenarios/plugins/docs_list/src/detail.tsx @@ -4,16 +4,16 @@ import { List } from "@project-gauntlet/api/components"; export default function Main(): ReactElement { return ( - - - - - - - - - - + + + + + + + + + + Sentient diff --git a/scenarios/plugins/docs_list/src/main.tsx b/scenarios/plugins/docs_list/src/main.tsx index 6bdafeb..6458e49 100644 --- a/scenarios/plugins/docs_list/src/main.tsx +++ b/scenarios/plugins/docs_list/src/main.tsx @@ -4,16 +4,16 @@ import { List } from "@project-gauntlet/api/components"; export default function Main(): ReactElement { return ( - - - - - - - - - - + + + + + + + + + + ) } diff --git a/scenarios/plugins/docs_list/src/search_bar.tsx b/scenarios/plugins/docs_list/src/search_bar.tsx index 618f476..a57aeda 100644 --- a/scenarios/plugins/docs_list/src/search_bar.tsx +++ b/scenarios/plugins/docs_list/src/search_bar.tsx @@ -23,7 +23,7 @@ export default function Main(): ReactElement { {results .filter(value => !searchText ? true : value.toLowerCase().includes(searchText)) .map(value => ( - + )) } diff --git a/scenarios/plugins/docs_list/src/section.tsx b/scenarios/plugins/docs_list/src/section.tsx index 3d9db2b..9f23846 100644 --- a/scenarios/plugins/docs_list/src/section.tsx +++ b/scenarios/plugins/docs_list/src/section.tsx @@ -4,17 +4,17 @@ import { List } from "@project-gauntlet/api/components"; export default function Main(): ReactElement { return ( - + - - - + + + - - - - + + + + ) From 053b0a100805faade8d401d1846efc2c3774afc0 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 20 Jan 2025 20:00:27 +0100 Subject: [PATCH 019/170] Tweak window border color of macos dark theme on non-macos platforms --- bundled_themes/macos_dark.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundled_themes/macos_dark.toml b/bundled_themes/macos_dark.toml index 52389ae..e0f8e13 100644 --- a/bundled_themes/macos_dark.toml +++ b/bundled_themes/macos_dark.toml @@ -17,7 +17,7 @@ text = [ [window.border] radius = 8 width = 1 -color = { color = "#646464", alpha = 0.5 } +color = { color = "#383838", alpha = 1 } [content.border] radius = 4.0 From 74f6433beadcdad1fe63ed1534cf1148675b8018 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 20 Jan 2025 20:01:31 +0100 Subject: [PATCH 020/170] Fix screenshot generator --- rust/client/src/ui/mod.rs | 71 +++++++++++++++++---------------- rust/common/src/model.rs | 2 +- rust/scenario_runner/src/lib.rs | 28 +++++++++++++ rust/server/src/lib.rs | 24 +++++++++-- rust/server/src/plugins/mod.rs | 4 +- 5 files changed, 87 insertions(+), 42 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 78c8c60..056acae 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -157,7 +157,7 @@ pub struct AppModel { theme: GauntletComplexTheme, window_position_mode: WindowPositionMode, close_on_unfocus: bool, - window_position_file: PathBuf, + window_position_file: Option, #[cfg(target_os = "linux")] x11_active_window: Option, pending_window_state_reset: bool, @@ -429,8 +429,10 @@ fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { } } -fn open_main_window_non_wayland(minimized: bool, window_position_file: &PathBuf) -> (window::Id, Task) { - let position = fs::read_to_string(window_position_file) +fn open_main_window_non_wayland(minimized: bool, window_position_file: Option<&PathBuf>) -> (window::Id, Task) { + let position = window_position_file + .map(|window_position_file| fs::read_to_string(window_position_file).ok()) + .flatten() .map(|data| { if let Some((x, y)) = data.split_once(":") { match (x.parse(), y.parse()) { @@ -575,14 +577,16 @@ fn new( open_main_window_wayland(id) } } else { - open_main_window_non_wayland(minimized, &setup_data.window_position_file) + open_main_window_non_wayland(minimized, setup_data.window_position_file.as_ref()) }; #[cfg(not(target_os = "linux"))] - let (main_window_id, open_task) = open_main_window_non_wayland(minimized, &setup_data.window_position_file); + let (main_window_id, open_task) = open_main_window_non_wayland(minimized, setup_data.window_position_file.as_ref()); tasks.push(open_task); + let mut client_context = ClientContext::new(); + let global_state = if cfg!(feature = "scenario_runner") { let gen_in = std::env::var("GAUNTLET_SCREENSHOT_GEN_IN").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_IN"); @@ -619,21 +623,20 @@ fn new( } => { let plugin_id = PluginId::from_string("__SCREENSHOT_GEN___"); let entrypoint_id = EntrypointId::from_string(entrypoint_id); + let plugin_name = "Screenshot Plugin".to_string(); + let entrypoint_name = gen_name; let render_location = ui_render_location_from_scenario(render_location); - let msg = AppMsg::RenderPluginUI { - plugin_id: plugin_id.clone(), - plugin_name: "Screenshot Plugin".to_string(), - entrypoint_id: entrypoint_id.clone(), - entrypoint_name: "Screenshot Entrypoint".to_string(), + let _ = client_context.render_ui( render_location, - top_level_view, - container: Arc::new(container), + Arc::new(container), images, - }; - - tasks.push(Task::done(msg)); + &plugin_id, + &plugin_name, + &entrypoint_id, + &entrypoint_name, + ); match render_location { UiRenderLocation::InlineView => GlobalState::new(text_input::Id::unique()), @@ -642,9 +645,9 @@ fn new( PluginViewData { top_level_view, plugin_id, - plugin_name: "Screenshot Gen".to_string(), + plugin_name, entrypoint_id, - entrypoint_name: gen_name, + entrypoint_name, action_shortcuts: Default::default(), }, true, @@ -708,7 +711,7 @@ fn new( // state global_state, - client_context: ClientContext::new(), + client_context, search_results: vec![], loading_bar_state: HashMap::new(), hud_display: None, @@ -878,30 +881,26 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::PromptChanged(mut new_prompt) => { - if cfg!(feature = "scenario_runner") { - Task::none() - } else { - match &mut state.global_state { - GlobalState::MainView { + match &mut state.global_state { + GlobalState::MainView { focused_search_result, sub_state, .. } => { - new_prompt.truncate(100); // search query uses regex so just to be safe truncate the prompt + new_prompt.truncate(100); // search query uses regex so just to be safe truncate the prompt - state.prompt = new_prompt.clone(); + state.prompt = new_prompt.clone(); - focused_search_result.reset(true); + focused_search_result.reset(true); - MainViewState::initial(sub_state); - } - GlobalState::ErrorView { .. } => {} - GlobalState::PluginView { .. } => {} - GlobalState::PendingPluginView { .. } => {} + MainViewState::initial(sub_state); + } + GlobalState::ErrorView { .. } => {} + GlobalState::PluginView { .. } => {} + GlobalState::PendingPluginView { .. } => {} } - state.search(new_prompt, true) - } + state.search(new_prompt, true) } AppMsg::UpdateSearchResults => { match &state.global_state { @@ -1134,8 +1133,10 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { return Task::none(); } - let _ = fs::create_dir_all(state.window_position_file.parent().unwrap()); - let _ = fs::write(&state.window_position_file, format!("{}:{}", point.x, point.y)); + if let Some(window_position_file) = &state.window_position_file { + let _ = fs::create_dir_all(window_position_file.parent().unwrap()); + let _ = fs::write(&window_position_file, format!("{}:{}", point.x, point.y)); + } Task::none() } diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index 0b2217e..330278c 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -214,7 +214,7 @@ pub struct UiTheme { #[derive(Debug)] pub struct UiSetupData { - pub window_position_file: PathBuf, + pub window_position_file: Option, pub theme: UiTheme, pub global_shortcut: Option, pub close_on_unfocus: bool, diff --git a/rust/scenario_runner/src/lib.rs b/rust/scenario_runner/src/lib.rs index 693245f..854a703 100644 --- a/rust/scenario_runner/src/lib.rs +++ b/rust/scenario_runner/src/lib.rs @@ -1,4 +1,7 @@ use gauntlet_common::model::BackendRequestData; +use gauntlet_common::model::UiSetupData; +use gauntlet_common::model::UiTheme; +use gauntlet_common::model::WindowPositionMode; use gauntlet_common::model::BackendResponseData; use gauntlet_common::model::UiRequestData; use gauntlet_common::model::UiResponseData; @@ -16,3 +19,28 @@ pub async fn run_scenario_runner_frontend_mock( Ok(()) } + +pub async fn run_scenario_runner_mock_server( + _request_sender: RequestSender, + mut backend_receiver: RequestReceiver, + theme: UiTheme +) -> anyhow::Result<()> { + + let (_data, responder) = backend_receiver.recv().await; + responder.respond(BackendResponseData::SetupData { + data: UiSetupData { + window_position_file: None, + theme, + global_shortcut: None, + close_on_unfocus: false, + window_position_mode: WindowPositionMode::Static, + }, + }); + + let (_data, responder) = backend_receiver.recv().await; + responder.respond(BackendResponseData::Nothing); + + std::thread::park(); + + Ok(()) +} diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 6eb0b69..4c8b800 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -18,6 +18,7 @@ use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PluginId; use gauntlet_common::model::UiRequestData; use gauntlet_common::model::UiResponseData; +use gauntlet_common::model::UiTheme; use gauntlet_common::rpc::backend_api::BackendApi; use gauntlet_common::rpc::backend_api::BackendApiError; use gauntlet_common::rpc::backend_server::start_backend_server; @@ -119,10 +120,13 @@ fn run_scenario_runner() { let (frontend_sender, frontend_receiver) = channel::(); let (backend_sender, backend_receiver) = channel::(); - start_client(false, frontend_receiver, backend_sender); + std::thread::spawn(|| { + let theme = crate::plugins::theme::BundledThemes::new().unwrap(); - drop(frontend_sender); - drop(backend_receiver); + start_mock_server(frontend_sender, backend_receiver, theme.legacy_theme) + }); + + start_client(false, frontend_receiver, backend_sender); } "scenario_runner" => { let (frontend_sender, frontend_receiver) = channel::(); @@ -130,7 +134,7 @@ fn run_scenario_runner() { std::thread::spawn(|| start_server(frontend_sender, backend_receiver)); - start_frontend_mock(frontend_receiver, backend_sender) + start_frontend_mock(frontend_receiver, backend_sender); } _ => panic!("unknown type"), } @@ -168,6 +172,18 @@ fn start_server( .unwrap(); } +#[cfg(feature = "scenario_runner")] +fn start_mock_server(request_sender: RequestSender, backend_receiver: RequestReceiver, theme: UiTheme) { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("unable to start server tokio runtime") + .block_on(async { + gauntlet_scenario_runner::run_scenario_runner_mock_server(request_sender, backend_receiver, theme).await + }) + .unwrap(); +} + #[cfg(feature = "scenario_runner")] fn start_frontend_mock( request_receiver: RequestReceiver, diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index f0e7b19..b282ee2 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -87,7 +87,7 @@ pub mod plugin_manifest; mod run_status; mod runtime; mod settings; -mod theme; +pub mod theme; static BUNDLED_PLUGINS: [(&str, Dir); 1] = [( "gauntlet", @@ -146,7 +146,7 @@ impl ApplicationManager { let close_on_unfocus = self.config_reader.close_on_unfocus(); Ok(UiSetupData { - window_position_file, + window_position_file: Some(window_position_file), theme, global_shortcut, close_on_unfocus, From 6ccd091c8f6af814860f69c9bc3698626a12bd7c Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 20 Jan 2025 20:23:28 +0100 Subject: [PATCH 021/170] Rename scenarios directory to example_plugins --- {scenarios => example_plugins}/.gitignore | 0 .../plugins/docs_detail/.gitignore | 0 .../plugins/docs_detail/gauntlet.toml | 0 .../plugins/docs_detail/package.json | 0 .../plugins/docs_detail/src/content.tsx | 0 .../docs_detail/src/content_code_block.tsx | 0 .../docs_detail/src/content_header.tsx | 0 .../src/content_horizontal_break.tsx | 0 .../plugins/docs_detail/src/content_image.tsx | 0 .../docs_detail/src/content_paragraph.tsx | 0 .../plugins/docs_detail/src/main.tsx | 0 .../plugins/docs_detail/src/metadata.tsx | 0 .../plugins/docs_detail/src/metadata_icon.tsx | 0 .../plugins/docs_detail/src/metadata_link.tsx | 0 .../docs_detail/src/metadata_separator.tsx | 0 .../docs_detail/src/metadata_tag_list.tsx | 0 .../docs_detail/src/metadata_value.tsx | 0 .../plugins/docs_detail/tsconfig.json | 0 .../plugins/docs_form/.gitignore | 0 .../plugins/docs_form/gauntlet.toml | 0 .../plugins/docs_form/package.json | 0 .../plugins/docs_form/src/checkbox.tsx | 0 .../plugins/docs_form/src/date-picker.tsx | 0 .../plugins/docs_form/src/main.tsx | 0 .../plugins/docs_form/src/password-field.tsx | 0 .../plugins/docs_form/src/select.tsx | 0 .../plugins/docs_form/src/separator.tsx | 0 .../plugins/docs_form/src/text-field.tsx | 0 .../plugins/docs_form/tsconfig.json | 0 .../plugins/docs_grid/.gitignore | 0 .../plugins/docs_grid/gauntlet.toml | 0 .../plugins/docs_grid/package.json | 0 .../docs_grid/src/content_code_block.tsx | 0 .../plugins/docs_grid/src/content_headers.tsx | 0 .../plugins/docs_grid/src/content_image.tsx | 0 .../docs_grid/src/content_paragraph.tsx | 0 .../plugins/docs_grid/src/empty_view.tsx | 0 .../plugins/docs_grid/src/main.tsx | 0 .../plugins/docs_grid/src/search_bar.tsx | 0 .../plugins/docs_grid/src/section.tsx | 0 .../plugins/docs_grid/tsconfig.json | 0 .../plugins/docs_inline_separators/.gitignore | 0 .../docs_inline_separators/gauntlet.toml | 0 .../docs_inline_separators/package.json | 0 .../docs_inline_separators/src/main.tsx | 0 .../docs_inline_separators/tsconfig.json | 0 .../docs_inline_three_sections/.gitignore | 0 .../docs_inline_three_sections/gauntlet.toml | 0 .../docs_inline_three_sections/package.json | 0 .../docs_inline_three_sections/src/main.tsx | 0 .../docs_inline_three_sections/tsconfig.json | 0 .../docs_inline_two_sections/.gitignore | 0 .../docs_inline_two_sections/gauntlet.toml | 0 .../docs_inline_two_sections/package.json | 0 .../docs_inline_two_sections/src/main.tsx | 0 .../docs_inline_two_sections/tsconfig.json | 0 .../plugins/docs_list/.gitignore | 0 .../plugins/docs_list/gauntlet.toml | 0 .../plugins/docs_list/package.json | 0 .../plugins/docs_list/src/detail.tsx | 0 .../plugins/docs_list/src/empty_view.tsx | 0 .../plugins/docs_list/src/main.tsx | 0 .../plugins/docs_list/src/search_bar.tsx | 0 .../plugins/docs_list/src/section.tsx | 0 .../plugins/docs_list/tsconfig.json | 0 .../content-code-block/default.json | 0 .../docs_detail/content-header/default.json | 0 .../content-horizontal-break/default.json | 0 .../docs_detail/content-image/default.json | 0 .../content-paragraph/default.json | 0 .../docs_detail/content/default.json | 0 .../scenarios}/docs_detail/main/default.json | 0 .../docs_detail/metadata-icon/default.json | 0 .../docs_detail/metadata-link/default.json | 0 .../metadata-separator/default.json | 0 .../metadata-tag-list/default.json | 0 .../docs_detail/metadata-value/default.json | 0 .../docs_detail/metadata/default.json | 0 .../docs_form/checkbox/default.json | 0 .../docs_form/date-picker/default.json | 0 .../scenarios}/docs_form/main/default.json | 0 .../docs_form/password-field/default.json | 0 .../scenarios}/docs_form/select/default.json | 0 .../docs_form/separator/default.json | 0 .../docs_form/text-field/default.json | 0 .../docs_grid/content-code-block/default.json | 0 .../docs_grid/content-headers/default.json | 0 .../docs_grid/content-image/default.json | 0 .../docs_grid/content-paragraph/default.json | 0 .../docs_grid/empty-view/default.json | 0 .../scenarios}/docs_grid/main/default.json | 0 .../docs_grid/search-bar/default.json | 0 .../scenarios}/docs_grid/section/default.json | 0 .../main/separator.json | 0 .../main/three-sections.json | 0 .../main/two-sections.json | 0 .../scenarios}/docs_list/detail/default.json | 0 .../docs_list/empty-view/default.json | 0 .../scenarios}/docs_list/main/default.json | 0 .../docs_list/search-bar/default.json | 0 .../scenarios}/docs_list/section/default.json | 0 js/scenario_runner_cli/src/main.ts | 6 +- package-lock.json | 168 ++++++++++++++---- package.json | 4 +- rust/scenario_runner/src/frontend_mock.rs | 2 +- 105 files changed, 137 insertions(+), 43 deletions(-) rename {scenarios => example_plugins}/.gitignore (100%) rename {scenarios => example_plugins}/plugins/docs_detail/.gitignore (100%) rename {scenarios => example_plugins}/plugins/docs_detail/gauntlet.toml (100%) rename {scenarios => example_plugins}/plugins/docs_detail/package.json (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/content.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/content_code_block.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/content_header.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/content_horizontal_break.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/content_image.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/content_paragraph.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/main.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/metadata.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/metadata_icon.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/metadata_link.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/metadata_separator.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/metadata_tag_list.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/src/metadata_value.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_detail/tsconfig.json (100%) rename {scenarios => example_plugins}/plugins/docs_form/.gitignore (100%) rename {scenarios => example_plugins}/plugins/docs_form/gauntlet.toml (100%) rename {scenarios => example_plugins}/plugins/docs_form/package.json (100%) rename {scenarios => example_plugins}/plugins/docs_form/src/checkbox.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_form/src/date-picker.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_form/src/main.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_form/src/password-field.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_form/src/select.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_form/src/separator.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_form/src/text-field.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_form/tsconfig.json (100%) rename {scenarios => example_plugins}/plugins/docs_grid/.gitignore (100%) rename {scenarios => example_plugins}/plugins/docs_grid/gauntlet.toml (100%) rename {scenarios => example_plugins}/plugins/docs_grid/package.json (100%) rename {scenarios => example_plugins}/plugins/docs_grid/src/content_code_block.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_grid/src/content_headers.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_grid/src/content_image.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_grid/src/content_paragraph.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_grid/src/empty_view.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_grid/src/main.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_grid/src/search_bar.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_grid/src/section.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_grid/tsconfig.json (100%) rename {scenarios => example_plugins}/plugins/docs_inline_separators/.gitignore (100%) rename {scenarios => example_plugins}/plugins/docs_inline_separators/gauntlet.toml (100%) rename {scenarios => example_plugins}/plugins/docs_inline_separators/package.json (100%) rename {scenarios => example_plugins}/plugins/docs_inline_separators/src/main.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_inline_separators/tsconfig.json (100%) rename {scenarios => example_plugins}/plugins/docs_inline_three_sections/.gitignore (100%) rename {scenarios => example_plugins}/plugins/docs_inline_three_sections/gauntlet.toml (100%) rename {scenarios => example_plugins}/plugins/docs_inline_three_sections/package.json (100%) rename {scenarios => example_plugins}/plugins/docs_inline_three_sections/src/main.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_inline_three_sections/tsconfig.json (100%) rename {scenarios => example_plugins}/plugins/docs_inline_two_sections/.gitignore (100%) rename {scenarios => example_plugins}/plugins/docs_inline_two_sections/gauntlet.toml (100%) rename {scenarios => example_plugins}/plugins/docs_inline_two_sections/package.json (100%) rename {scenarios => example_plugins}/plugins/docs_inline_two_sections/src/main.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_inline_two_sections/tsconfig.json (100%) rename {scenarios => example_plugins}/plugins/docs_list/.gitignore (100%) rename {scenarios => example_plugins}/plugins/docs_list/gauntlet.toml (100%) rename {scenarios => example_plugins}/plugins/docs_list/package.json (100%) rename {scenarios => example_plugins}/plugins/docs_list/src/detail.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_list/src/empty_view.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_list/src/main.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_list/src/search_bar.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_list/src/section.tsx (100%) rename {scenarios => example_plugins}/plugins/docs_list/tsconfig.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/content-code-block/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/content-header/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/content-horizontal-break/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/content-image/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/content-paragraph/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/content/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/main/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/metadata-icon/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/metadata-link/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/metadata-separator/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/metadata-tag-list/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/metadata-value/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_detail/metadata/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_form/checkbox/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_form/date-picker/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_form/main/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_form/password-field/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_form/select/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_form/separator/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_form/text-field/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_grid/content-code-block/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_grid/content-headers/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_grid/content-image/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_grid/content-paragraph/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_grid/empty-view/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_grid/main/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_grid/search-bar/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_grid/section/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_inline_separators/main/separator.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_inline_three_sections/main/three-sections.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_inline_two_sections/main/two-sections.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_list/detail/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_list/empty-view/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_list/main/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_list/search-bar/default.json (100%) rename {scenarios/data => example_plugins/scenarios}/docs_list/section/default.json (100%) diff --git a/scenarios/.gitignore b/example_plugins/.gitignore similarity index 100% rename from scenarios/.gitignore rename to example_plugins/.gitignore diff --git a/scenarios/plugins/docs_detail/.gitignore b/example_plugins/plugins/docs_detail/.gitignore similarity index 100% rename from scenarios/plugins/docs_detail/.gitignore rename to example_plugins/plugins/docs_detail/.gitignore diff --git a/scenarios/plugins/docs_detail/gauntlet.toml b/example_plugins/plugins/docs_detail/gauntlet.toml similarity index 100% rename from scenarios/plugins/docs_detail/gauntlet.toml rename to example_plugins/plugins/docs_detail/gauntlet.toml diff --git a/scenarios/plugins/docs_detail/package.json b/example_plugins/plugins/docs_detail/package.json similarity index 100% rename from scenarios/plugins/docs_detail/package.json rename to example_plugins/plugins/docs_detail/package.json diff --git a/scenarios/plugins/docs_detail/src/content.tsx b/example_plugins/plugins/docs_detail/src/content.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/content.tsx rename to example_plugins/plugins/docs_detail/src/content.tsx diff --git a/scenarios/plugins/docs_detail/src/content_code_block.tsx b/example_plugins/plugins/docs_detail/src/content_code_block.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/content_code_block.tsx rename to example_plugins/plugins/docs_detail/src/content_code_block.tsx diff --git a/scenarios/plugins/docs_detail/src/content_header.tsx b/example_plugins/plugins/docs_detail/src/content_header.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/content_header.tsx rename to example_plugins/plugins/docs_detail/src/content_header.tsx diff --git a/scenarios/plugins/docs_detail/src/content_horizontal_break.tsx b/example_plugins/plugins/docs_detail/src/content_horizontal_break.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/content_horizontal_break.tsx rename to example_plugins/plugins/docs_detail/src/content_horizontal_break.tsx diff --git a/scenarios/plugins/docs_detail/src/content_image.tsx b/example_plugins/plugins/docs_detail/src/content_image.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/content_image.tsx rename to example_plugins/plugins/docs_detail/src/content_image.tsx diff --git a/scenarios/plugins/docs_detail/src/content_paragraph.tsx b/example_plugins/plugins/docs_detail/src/content_paragraph.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/content_paragraph.tsx rename to example_plugins/plugins/docs_detail/src/content_paragraph.tsx diff --git a/scenarios/plugins/docs_detail/src/main.tsx b/example_plugins/plugins/docs_detail/src/main.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/main.tsx rename to example_plugins/plugins/docs_detail/src/main.tsx diff --git a/scenarios/plugins/docs_detail/src/metadata.tsx b/example_plugins/plugins/docs_detail/src/metadata.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/metadata.tsx rename to example_plugins/plugins/docs_detail/src/metadata.tsx diff --git a/scenarios/plugins/docs_detail/src/metadata_icon.tsx b/example_plugins/plugins/docs_detail/src/metadata_icon.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/metadata_icon.tsx rename to example_plugins/plugins/docs_detail/src/metadata_icon.tsx diff --git a/scenarios/plugins/docs_detail/src/metadata_link.tsx b/example_plugins/plugins/docs_detail/src/metadata_link.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/metadata_link.tsx rename to example_plugins/plugins/docs_detail/src/metadata_link.tsx diff --git a/scenarios/plugins/docs_detail/src/metadata_separator.tsx b/example_plugins/plugins/docs_detail/src/metadata_separator.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/metadata_separator.tsx rename to example_plugins/plugins/docs_detail/src/metadata_separator.tsx diff --git a/scenarios/plugins/docs_detail/src/metadata_tag_list.tsx b/example_plugins/plugins/docs_detail/src/metadata_tag_list.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/metadata_tag_list.tsx rename to example_plugins/plugins/docs_detail/src/metadata_tag_list.tsx diff --git a/scenarios/plugins/docs_detail/src/metadata_value.tsx b/example_plugins/plugins/docs_detail/src/metadata_value.tsx similarity index 100% rename from scenarios/plugins/docs_detail/src/metadata_value.tsx rename to example_plugins/plugins/docs_detail/src/metadata_value.tsx diff --git a/scenarios/plugins/docs_detail/tsconfig.json b/example_plugins/plugins/docs_detail/tsconfig.json similarity index 100% rename from scenarios/plugins/docs_detail/tsconfig.json rename to example_plugins/plugins/docs_detail/tsconfig.json diff --git a/scenarios/plugins/docs_form/.gitignore b/example_plugins/plugins/docs_form/.gitignore similarity index 100% rename from scenarios/plugins/docs_form/.gitignore rename to example_plugins/plugins/docs_form/.gitignore diff --git a/scenarios/plugins/docs_form/gauntlet.toml b/example_plugins/plugins/docs_form/gauntlet.toml similarity index 100% rename from scenarios/plugins/docs_form/gauntlet.toml rename to example_plugins/plugins/docs_form/gauntlet.toml diff --git a/scenarios/plugins/docs_form/package.json b/example_plugins/plugins/docs_form/package.json similarity index 100% rename from scenarios/plugins/docs_form/package.json rename to example_plugins/plugins/docs_form/package.json diff --git a/scenarios/plugins/docs_form/src/checkbox.tsx b/example_plugins/plugins/docs_form/src/checkbox.tsx similarity index 100% rename from scenarios/plugins/docs_form/src/checkbox.tsx rename to example_plugins/plugins/docs_form/src/checkbox.tsx diff --git a/scenarios/plugins/docs_form/src/date-picker.tsx b/example_plugins/plugins/docs_form/src/date-picker.tsx similarity index 100% rename from scenarios/plugins/docs_form/src/date-picker.tsx rename to example_plugins/plugins/docs_form/src/date-picker.tsx diff --git a/scenarios/plugins/docs_form/src/main.tsx b/example_plugins/plugins/docs_form/src/main.tsx similarity index 100% rename from scenarios/plugins/docs_form/src/main.tsx rename to example_plugins/plugins/docs_form/src/main.tsx diff --git a/scenarios/plugins/docs_form/src/password-field.tsx b/example_plugins/plugins/docs_form/src/password-field.tsx similarity index 100% rename from scenarios/plugins/docs_form/src/password-field.tsx rename to example_plugins/plugins/docs_form/src/password-field.tsx diff --git a/scenarios/plugins/docs_form/src/select.tsx b/example_plugins/plugins/docs_form/src/select.tsx similarity index 100% rename from scenarios/plugins/docs_form/src/select.tsx rename to example_plugins/plugins/docs_form/src/select.tsx diff --git a/scenarios/plugins/docs_form/src/separator.tsx b/example_plugins/plugins/docs_form/src/separator.tsx similarity index 100% rename from scenarios/plugins/docs_form/src/separator.tsx rename to example_plugins/plugins/docs_form/src/separator.tsx diff --git a/scenarios/plugins/docs_form/src/text-field.tsx b/example_plugins/plugins/docs_form/src/text-field.tsx similarity index 100% rename from scenarios/plugins/docs_form/src/text-field.tsx rename to example_plugins/plugins/docs_form/src/text-field.tsx diff --git a/scenarios/plugins/docs_form/tsconfig.json b/example_plugins/plugins/docs_form/tsconfig.json similarity index 100% rename from scenarios/plugins/docs_form/tsconfig.json rename to example_plugins/plugins/docs_form/tsconfig.json diff --git a/scenarios/plugins/docs_grid/.gitignore b/example_plugins/plugins/docs_grid/.gitignore similarity index 100% rename from scenarios/plugins/docs_grid/.gitignore rename to example_plugins/plugins/docs_grid/.gitignore diff --git a/scenarios/plugins/docs_grid/gauntlet.toml b/example_plugins/plugins/docs_grid/gauntlet.toml similarity index 100% rename from scenarios/plugins/docs_grid/gauntlet.toml rename to example_plugins/plugins/docs_grid/gauntlet.toml diff --git a/scenarios/plugins/docs_grid/package.json b/example_plugins/plugins/docs_grid/package.json similarity index 100% rename from scenarios/plugins/docs_grid/package.json rename to example_plugins/plugins/docs_grid/package.json diff --git a/scenarios/plugins/docs_grid/src/content_code_block.tsx b/example_plugins/plugins/docs_grid/src/content_code_block.tsx similarity index 100% rename from scenarios/plugins/docs_grid/src/content_code_block.tsx rename to example_plugins/plugins/docs_grid/src/content_code_block.tsx diff --git a/scenarios/plugins/docs_grid/src/content_headers.tsx b/example_plugins/plugins/docs_grid/src/content_headers.tsx similarity index 100% rename from scenarios/plugins/docs_grid/src/content_headers.tsx rename to example_plugins/plugins/docs_grid/src/content_headers.tsx diff --git a/scenarios/plugins/docs_grid/src/content_image.tsx b/example_plugins/plugins/docs_grid/src/content_image.tsx similarity index 100% rename from scenarios/plugins/docs_grid/src/content_image.tsx rename to example_plugins/plugins/docs_grid/src/content_image.tsx diff --git a/scenarios/plugins/docs_grid/src/content_paragraph.tsx b/example_plugins/plugins/docs_grid/src/content_paragraph.tsx similarity index 100% rename from scenarios/plugins/docs_grid/src/content_paragraph.tsx rename to example_plugins/plugins/docs_grid/src/content_paragraph.tsx diff --git a/scenarios/plugins/docs_grid/src/empty_view.tsx b/example_plugins/plugins/docs_grid/src/empty_view.tsx similarity index 100% rename from scenarios/plugins/docs_grid/src/empty_view.tsx rename to example_plugins/plugins/docs_grid/src/empty_view.tsx diff --git a/scenarios/plugins/docs_grid/src/main.tsx b/example_plugins/plugins/docs_grid/src/main.tsx similarity index 100% rename from scenarios/plugins/docs_grid/src/main.tsx rename to example_plugins/plugins/docs_grid/src/main.tsx diff --git a/scenarios/plugins/docs_grid/src/search_bar.tsx b/example_plugins/plugins/docs_grid/src/search_bar.tsx similarity index 100% rename from scenarios/plugins/docs_grid/src/search_bar.tsx rename to example_plugins/plugins/docs_grid/src/search_bar.tsx diff --git a/scenarios/plugins/docs_grid/src/section.tsx b/example_plugins/plugins/docs_grid/src/section.tsx similarity index 100% rename from scenarios/plugins/docs_grid/src/section.tsx rename to example_plugins/plugins/docs_grid/src/section.tsx diff --git a/scenarios/plugins/docs_grid/tsconfig.json b/example_plugins/plugins/docs_grid/tsconfig.json similarity index 100% rename from scenarios/plugins/docs_grid/tsconfig.json rename to example_plugins/plugins/docs_grid/tsconfig.json diff --git a/scenarios/plugins/docs_inline_separators/.gitignore b/example_plugins/plugins/docs_inline_separators/.gitignore similarity index 100% rename from scenarios/plugins/docs_inline_separators/.gitignore rename to example_plugins/plugins/docs_inline_separators/.gitignore diff --git a/scenarios/plugins/docs_inline_separators/gauntlet.toml b/example_plugins/plugins/docs_inline_separators/gauntlet.toml similarity index 100% rename from scenarios/plugins/docs_inline_separators/gauntlet.toml rename to example_plugins/plugins/docs_inline_separators/gauntlet.toml diff --git a/scenarios/plugins/docs_inline_separators/package.json b/example_plugins/plugins/docs_inline_separators/package.json similarity index 100% rename from scenarios/plugins/docs_inline_separators/package.json rename to example_plugins/plugins/docs_inline_separators/package.json diff --git a/scenarios/plugins/docs_inline_separators/src/main.tsx b/example_plugins/plugins/docs_inline_separators/src/main.tsx similarity index 100% rename from scenarios/plugins/docs_inline_separators/src/main.tsx rename to example_plugins/plugins/docs_inline_separators/src/main.tsx diff --git a/scenarios/plugins/docs_inline_separators/tsconfig.json b/example_plugins/plugins/docs_inline_separators/tsconfig.json similarity index 100% rename from scenarios/plugins/docs_inline_separators/tsconfig.json rename to example_plugins/plugins/docs_inline_separators/tsconfig.json diff --git a/scenarios/plugins/docs_inline_three_sections/.gitignore b/example_plugins/plugins/docs_inline_three_sections/.gitignore similarity index 100% rename from scenarios/plugins/docs_inline_three_sections/.gitignore rename to example_plugins/plugins/docs_inline_three_sections/.gitignore diff --git a/scenarios/plugins/docs_inline_three_sections/gauntlet.toml b/example_plugins/plugins/docs_inline_three_sections/gauntlet.toml similarity index 100% rename from scenarios/plugins/docs_inline_three_sections/gauntlet.toml rename to example_plugins/plugins/docs_inline_three_sections/gauntlet.toml diff --git a/scenarios/plugins/docs_inline_three_sections/package.json b/example_plugins/plugins/docs_inline_three_sections/package.json similarity index 100% rename from scenarios/plugins/docs_inline_three_sections/package.json rename to example_plugins/plugins/docs_inline_three_sections/package.json diff --git a/scenarios/plugins/docs_inline_three_sections/src/main.tsx b/example_plugins/plugins/docs_inline_three_sections/src/main.tsx similarity index 100% rename from scenarios/plugins/docs_inline_three_sections/src/main.tsx rename to example_plugins/plugins/docs_inline_three_sections/src/main.tsx diff --git a/scenarios/plugins/docs_inline_three_sections/tsconfig.json b/example_plugins/plugins/docs_inline_three_sections/tsconfig.json similarity index 100% rename from scenarios/plugins/docs_inline_three_sections/tsconfig.json rename to example_plugins/plugins/docs_inline_three_sections/tsconfig.json diff --git a/scenarios/plugins/docs_inline_two_sections/.gitignore b/example_plugins/plugins/docs_inline_two_sections/.gitignore similarity index 100% rename from scenarios/plugins/docs_inline_two_sections/.gitignore rename to example_plugins/plugins/docs_inline_two_sections/.gitignore diff --git a/scenarios/plugins/docs_inline_two_sections/gauntlet.toml b/example_plugins/plugins/docs_inline_two_sections/gauntlet.toml similarity index 100% rename from scenarios/plugins/docs_inline_two_sections/gauntlet.toml rename to example_plugins/plugins/docs_inline_two_sections/gauntlet.toml diff --git a/scenarios/plugins/docs_inline_two_sections/package.json b/example_plugins/plugins/docs_inline_two_sections/package.json similarity index 100% rename from scenarios/plugins/docs_inline_two_sections/package.json rename to example_plugins/plugins/docs_inline_two_sections/package.json diff --git a/scenarios/plugins/docs_inline_two_sections/src/main.tsx b/example_plugins/plugins/docs_inline_two_sections/src/main.tsx similarity index 100% rename from scenarios/plugins/docs_inline_two_sections/src/main.tsx rename to example_plugins/plugins/docs_inline_two_sections/src/main.tsx diff --git a/scenarios/plugins/docs_inline_two_sections/tsconfig.json b/example_plugins/plugins/docs_inline_two_sections/tsconfig.json similarity index 100% rename from scenarios/plugins/docs_inline_two_sections/tsconfig.json rename to example_plugins/plugins/docs_inline_two_sections/tsconfig.json diff --git a/scenarios/plugins/docs_list/.gitignore b/example_plugins/plugins/docs_list/.gitignore similarity index 100% rename from scenarios/plugins/docs_list/.gitignore rename to example_plugins/plugins/docs_list/.gitignore diff --git a/scenarios/plugins/docs_list/gauntlet.toml b/example_plugins/plugins/docs_list/gauntlet.toml similarity index 100% rename from scenarios/plugins/docs_list/gauntlet.toml rename to example_plugins/plugins/docs_list/gauntlet.toml diff --git a/scenarios/plugins/docs_list/package.json b/example_plugins/plugins/docs_list/package.json similarity index 100% rename from scenarios/plugins/docs_list/package.json rename to example_plugins/plugins/docs_list/package.json diff --git a/scenarios/plugins/docs_list/src/detail.tsx b/example_plugins/plugins/docs_list/src/detail.tsx similarity index 100% rename from scenarios/plugins/docs_list/src/detail.tsx rename to example_plugins/plugins/docs_list/src/detail.tsx diff --git a/scenarios/plugins/docs_list/src/empty_view.tsx b/example_plugins/plugins/docs_list/src/empty_view.tsx similarity index 100% rename from scenarios/plugins/docs_list/src/empty_view.tsx rename to example_plugins/plugins/docs_list/src/empty_view.tsx diff --git a/scenarios/plugins/docs_list/src/main.tsx b/example_plugins/plugins/docs_list/src/main.tsx similarity index 100% rename from scenarios/plugins/docs_list/src/main.tsx rename to example_plugins/plugins/docs_list/src/main.tsx diff --git a/scenarios/plugins/docs_list/src/search_bar.tsx b/example_plugins/plugins/docs_list/src/search_bar.tsx similarity index 100% rename from scenarios/plugins/docs_list/src/search_bar.tsx rename to example_plugins/plugins/docs_list/src/search_bar.tsx diff --git a/scenarios/plugins/docs_list/src/section.tsx b/example_plugins/plugins/docs_list/src/section.tsx similarity index 100% rename from scenarios/plugins/docs_list/src/section.tsx rename to example_plugins/plugins/docs_list/src/section.tsx diff --git a/scenarios/plugins/docs_list/tsconfig.json b/example_plugins/plugins/docs_list/tsconfig.json similarity index 100% rename from scenarios/plugins/docs_list/tsconfig.json rename to example_plugins/plugins/docs_list/tsconfig.json diff --git a/scenarios/data/docs_detail/content-code-block/default.json b/example_plugins/scenarios/docs_detail/content-code-block/default.json similarity index 100% rename from scenarios/data/docs_detail/content-code-block/default.json rename to example_plugins/scenarios/docs_detail/content-code-block/default.json diff --git a/scenarios/data/docs_detail/content-header/default.json b/example_plugins/scenarios/docs_detail/content-header/default.json similarity index 100% rename from scenarios/data/docs_detail/content-header/default.json rename to example_plugins/scenarios/docs_detail/content-header/default.json diff --git a/scenarios/data/docs_detail/content-horizontal-break/default.json b/example_plugins/scenarios/docs_detail/content-horizontal-break/default.json similarity index 100% rename from scenarios/data/docs_detail/content-horizontal-break/default.json rename to example_plugins/scenarios/docs_detail/content-horizontal-break/default.json diff --git a/scenarios/data/docs_detail/content-image/default.json b/example_plugins/scenarios/docs_detail/content-image/default.json similarity index 100% rename from scenarios/data/docs_detail/content-image/default.json rename to example_plugins/scenarios/docs_detail/content-image/default.json diff --git a/scenarios/data/docs_detail/content-paragraph/default.json b/example_plugins/scenarios/docs_detail/content-paragraph/default.json similarity index 100% rename from scenarios/data/docs_detail/content-paragraph/default.json rename to example_plugins/scenarios/docs_detail/content-paragraph/default.json diff --git a/scenarios/data/docs_detail/content/default.json b/example_plugins/scenarios/docs_detail/content/default.json similarity index 100% rename from scenarios/data/docs_detail/content/default.json rename to example_plugins/scenarios/docs_detail/content/default.json diff --git a/scenarios/data/docs_detail/main/default.json b/example_plugins/scenarios/docs_detail/main/default.json similarity index 100% rename from scenarios/data/docs_detail/main/default.json rename to example_plugins/scenarios/docs_detail/main/default.json diff --git a/scenarios/data/docs_detail/metadata-icon/default.json b/example_plugins/scenarios/docs_detail/metadata-icon/default.json similarity index 100% rename from scenarios/data/docs_detail/metadata-icon/default.json rename to example_plugins/scenarios/docs_detail/metadata-icon/default.json diff --git a/scenarios/data/docs_detail/metadata-link/default.json b/example_plugins/scenarios/docs_detail/metadata-link/default.json similarity index 100% rename from scenarios/data/docs_detail/metadata-link/default.json rename to example_plugins/scenarios/docs_detail/metadata-link/default.json diff --git a/scenarios/data/docs_detail/metadata-separator/default.json b/example_plugins/scenarios/docs_detail/metadata-separator/default.json similarity index 100% rename from scenarios/data/docs_detail/metadata-separator/default.json rename to example_plugins/scenarios/docs_detail/metadata-separator/default.json diff --git a/scenarios/data/docs_detail/metadata-tag-list/default.json b/example_plugins/scenarios/docs_detail/metadata-tag-list/default.json similarity index 100% rename from scenarios/data/docs_detail/metadata-tag-list/default.json rename to example_plugins/scenarios/docs_detail/metadata-tag-list/default.json diff --git a/scenarios/data/docs_detail/metadata-value/default.json b/example_plugins/scenarios/docs_detail/metadata-value/default.json similarity index 100% rename from scenarios/data/docs_detail/metadata-value/default.json rename to example_plugins/scenarios/docs_detail/metadata-value/default.json diff --git a/scenarios/data/docs_detail/metadata/default.json b/example_plugins/scenarios/docs_detail/metadata/default.json similarity index 100% rename from scenarios/data/docs_detail/metadata/default.json rename to example_plugins/scenarios/docs_detail/metadata/default.json diff --git a/scenarios/data/docs_form/checkbox/default.json b/example_plugins/scenarios/docs_form/checkbox/default.json similarity index 100% rename from scenarios/data/docs_form/checkbox/default.json rename to example_plugins/scenarios/docs_form/checkbox/default.json diff --git a/scenarios/data/docs_form/date-picker/default.json b/example_plugins/scenarios/docs_form/date-picker/default.json similarity index 100% rename from scenarios/data/docs_form/date-picker/default.json rename to example_plugins/scenarios/docs_form/date-picker/default.json diff --git a/scenarios/data/docs_form/main/default.json b/example_plugins/scenarios/docs_form/main/default.json similarity index 100% rename from scenarios/data/docs_form/main/default.json rename to example_plugins/scenarios/docs_form/main/default.json diff --git a/scenarios/data/docs_form/password-field/default.json b/example_plugins/scenarios/docs_form/password-field/default.json similarity index 100% rename from scenarios/data/docs_form/password-field/default.json rename to example_plugins/scenarios/docs_form/password-field/default.json diff --git a/scenarios/data/docs_form/select/default.json b/example_plugins/scenarios/docs_form/select/default.json similarity index 100% rename from scenarios/data/docs_form/select/default.json rename to example_plugins/scenarios/docs_form/select/default.json diff --git a/scenarios/data/docs_form/separator/default.json b/example_plugins/scenarios/docs_form/separator/default.json similarity index 100% rename from scenarios/data/docs_form/separator/default.json rename to example_plugins/scenarios/docs_form/separator/default.json diff --git a/scenarios/data/docs_form/text-field/default.json b/example_plugins/scenarios/docs_form/text-field/default.json similarity index 100% rename from scenarios/data/docs_form/text-field/default.json rename to example_plugins/scenarios/docs_form/text-field/default.json diff --git a/scenarios/data/docs_grid/content-code-block/default.json b/example_plugins/scenarios/docs_grid/content-code-block/default.json similarity index 100% rename from scenarios/data/docs_grid/content-code-block/default.json rename to example_plugins/scenarios/docs_grid/content-code-block/default.json diff --git a/scenarios/data/docs_grid/content-headers/default.json b/example_plugins/scenarios/docs_grid/content-headers/default.json similarity index 100% rename from scenarios/data/docs_grid/content-headers/default.json rename to example_plugins/scenarios/docs_grid/content-headers/default.json diff --git a/scenarios/data/docs_grid/content-image/default.json b/example_plugins/scenarios/docs_grid/content-image/default.json similarity index 100% rename from scenarios/data/docs_grid/content-image/default.json rename to example_plugins/scenarios/docs_grid/content-image/default.json diff --git a/scenarios/data/docs_grid/content-paragraph/default.json b/example_plugins/scenarios/docs_grid/content-paragraph/default.json similarity index 100% rename from scenarios/data/docs_grid/content-paragraph/default.json rename to example_plugins/scenarios/docs_grid/content-paragraph/default.json diff --git a/scenarios/data/docs_grid/empty-view/default.json b/example_plugins/scenarios/docs_grid/empty-view/default.json similarity index 100% rename from scenarios/data/docs_grid/empty-view/default.json rename to example_plugins/scenarios/docs_grid/empty-view/default.json diff --git a/scenarios/data/docs_grid/main/default.json b/example_plugins/scenarios/docs_grid/main/default.json similarity index 100% rename from scenarios/data/docs_grid/main/default.json rename to example_plugins/scenarios/docs_grid/main/default.json diff --git a/scenarios/data/docs_grid/search-bar/default.json b/example_plugins/scenarios/docs_grid/search-bar/default.json similarity index 100% rename from scenarios/data/docs_grid/search-bar/default.json rename to example_plugins/scenarios/docs_grid/search-bar/default.json diff --git a/scenarios/data/docs_grid/section/default.json b/example_plugins/scenarios/docs_grid/section/default.json similarity index 100% rename from scenarios/data/docs_grid/section/default.json rename to example_plugins/scenarios/docs_grid/section/default.json diff --git a/scenarios/data/docs_inline_separators/main/separator.json b/example_plugins/scenarios/docs_inline_separators/main/separator.json similarity index 100% rename from scenarios/data/docs_inline_separators/main/separator.json rename to example_plugins/scenarios/docs_inline_separators/main/separator.json diff --git a/scenarios/data/docs_inline_three_sections/main/three-sections.json b/example_plugins/scenarios/docs_inline_three_sections/main/three-sections.json similarity index 100% rename from scenarios/data/docs_inline_three_sections/main/three-sections.json rename to example_plugins/scenarios/docs_inline_three_sections/main/three-sections.json diff --git a/scenarios/data/docs_inline_two_sections/main/two-sections.json b/example_plugins/scenarios/docs_inline_two_sections/main/two-sections.json similarity index 100% rename from scenarios/data/docs_inline_two_sections/main/two-sections.json rename to example_plugins/scenarios/docs_inline_two_sections/main/two-sections.json diff --git a/scenarios/data/docs_list/detail/default.json b/example_plugins/scenarios/docs_list/detail/default.json similarity index 100% rename from scenarios/data/docs_list/detail/default.json rename to example_plugins/scenarios/docs_list/detail/default.json diff --git a/scenarios/data/docs_list/empty-view/default.json b/example_plugins/scenarios/docs_list/empty-view/default.json similarity index 100% rename from scenarios/data/docs_list/empty-view/default.json rename to example_plugins/scenarios/docs_list/empty-view/default.json diff --git a/scenarios/data/docs_list/main/default.json b/example_plugins/scenarios/docs_list/main/default.json similarity index 100% rename from scenarios/data/docs_list/main/default.json rename to example_plugins/scenarios/docs_list/main/default.json diff --git a/scenarios/data/docs_list/search-bar/default.json b/example_plugins/scenarios/docs_list/search-bar/default.json similarity index 100% rename from scenarios/data/docs_list/search-bar/default.json rename to example_plugins/scenarios/docs_list/search-bar/default.json diff --git a/scenarios/data/docs_list/section/default.json b/example_plugins/scenarios/docs_list/section/default.json similarity index 100% rename from scenarios/data/docs_list/section/default.json rename to example_plugins/scenarios/docs_list/section/default.json diff --git a/js/scenario_runner_cli/src/main.ts b/js/scenario_runner_cli/src/main.ts index 01923e2..abc193e 100644 --- a/js/scenario_runner_cli/src/main.ts +++ b/js/scenario_runner_cli/src/main.ts @@ -31,8 +31,8 @@ async function sleep(ms: number) { async function runScenarios(expectedPlugin: string | undefined) { const projectRoot = path.resolve(process.cwd(), '..', '..'); - const scenarios = path.join(projectRoot, "scenarios"); - const scenariosData = path.join(scenarios, "data"); + const scenarios = path.join(projectRoot, "example_plugins"); + const scenariosData = path.join(scenarios, "scenarios"); const scenariosRun = path.join(scenarios, "run"); console.log("Building server") @@ -78,7 +78,7 @@ async function runScenarios(expectedPlugin: string | undefined) { async function runScreenshotGen(expectedPlugin: string | undefined, expectedEntrypoint: string | undefined) { const projectRoot = path.resolve(process.cwd(), '..', '..'); - const scenarios = path.join(projectRoot, "scenarios"); + const scenarios = path.join(projectRoot, "example_plugins"); const scenariosOut = path.join(scenarios, "out"); for (const plugin of readdirSync(scenariosOut)) { diff --git a/package-lock.json b/package-lock.json index 519073c..be00d1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "workspaces": [ "dev_plugin", "bundled_plugins/*", - "scenarios/plugins/*", + "example_plugins/plugins/*", "js/typings", "js/build", "js/api_build", @@ -48,6 +48,119 @@ "typescript": "^5.7.2" } }, + "example_plugins/js/api": {}, + "example_plugins/plugins/docs_detail": { + "name": "@project-gauntlet/docs-detailt", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_detail/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/docs_form": { + "name": "@project-gauntlet/docs-form", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_form/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/docs_grid": { + "name": "@project-gauntlet/docs-grid", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_grid/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/docs_inline_separators": { + "name": "@project-gauntlet/docs-inline-separators", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_inline_separators/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/docs_inline_three_sections": { + "name": "@project-gauntlet/docs-inline-three-sections", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_inline_three_sections/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/docs_inline_two_sections": { + "name": "@project-gauntlet/docs-inline-two-sections", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_inline_two_sections/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/docs_list": { + "name": "@project-gauntlet/docs-list", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_list/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, "js/api": { "name": "@project-gauntlet/api", "version": "0.0.0", @@ -675,31 +788,31 @@ "link": true }, "node_modules/@project-gauntlet/docs-detailt": { - "resolved": "scenarios/plugins/docs_detail", + "resolved": "example_plugins/plugins/docs_detail", "link": true }, "node_modules/@project-gauntlet/docs-form": { - "resolved": "scenarios/plugins/docs_form", + "resolved": "example_plugins/plugins/docs_form", "link": true }, "node_modules/@project-gauntlet/docs-grid": { - "resolved": "scenarios/plugins/docs_grid", + "resolved": "example_plugins/plugins/docs_grid", "link": true }, "node_modules/@project-gauntlet/docs-inline-separators": { - "resolved": "scenarios/plugins/docs_inline_separators", + "resolved": "example_plugins/plugins/docs_inline_separators", "link": true }, "node_modules/@project-gauntlet/docs-inline-three-sections": { - "resolved": "scenarios/plugins/docs_inline_three_sections", + "resolved": "example_plugins/plugins/docs_inline_three_sections", "link": true }, "node_modules/@project-gauntlet/docs-inline-two-sections": { - "resolved": "scenarios/plugins/docs_inline_two_sections", + "resolved": "example_plugins/plugins/docs_inline_two_sections", "link": true }, "node_modules/@project-gauntlet/docs-list": { - "resolved": "scenarios/plugins/docs_list", + "resolved": "example_plugins/plugins/docs_list", "link": true }, "node_modules/@project-gauntlet/react": { @@ -2829,9 +2942,12 @@ "zod": "^3.18.0" } }, - "scenarios/js/api": {}, + "scenarios/js/api": { + "extraneous": true + }, "scenarios/plugins/docs_detail": { "name": "@project-gauntlet/docs-detailt", + "extraneous": true, "dependencies": { "@project-gauntlet/api": "file:../../js/api" }, @@ -2842,12 +2958,9 @@ "typescript": "*" } }, - "scenarios/plugins/docs_detail/node_modules/@project-gauntlet/api": { - "resolved": "scenarios/js/api", - "link": true - }, "scenarios/plugins/docs_form": { "name": "@project-gauntlet/docs-form", + "extraneous": true, "dependencies": { "@project-gauntlet/api": "file:../../js/api" }, @@ -2858,12 +2971,9 @@ "typescript": "*" } }, - "scenarios/plugins/docs_form/node_modules/@project-gauntlet/api": { - "resolved": "scenarios/js/api", - "link": true - }, "scenarios/plugins/docs_grid": { "name": "@project-gauntlet/docs-grid", + "extraneous": true, "dependencies": { "@project-gauntlet/api": "file:../../js/api" }, @@ -2874,12 +2984,9 @@ "typescript": "*" } }, - "scenarios/plugins/docs_grid/node_modules/@project-gauntlet/api": { - "resolved": "scenarios/js/api", - "link": true - }, "scenarios/plugins/docs_inline_separators": { "name": "@project-gauntlet/docs-inline-separators", + "extraneous": true, "dependencies": { "@project-gauntlet/api": "file:../../js/api" }, @@ -2890,12 +2997,9 @@ "typescript": "*" } }, - "scenarios/plugins/docs_inline_separators/node_modules/@project-gauntlet/api": { - "resolved": "scenarios/js/api", - "link": true - }, "scenarios/plugins/docs_inline_three_sections": { "name": "@project-gauntlet/docs-inline-three-sections", + "extraneous": true, "dependencies": { "@project-gauntlet/api": "file:../../js/api" }, @@ -2906,12 +3010,9 @@ "typescript": "*" } }, - "scenarios/plugins/docs_inline_three_sections/node_modules/@project-gauntlet/api": { - "resolved": "scenarios/js/api", - "link": true - }, "scenarios/plugins/docs_inline_two_sections": { "name": "@project-gauntlet/docs-inline-two-sections", + "extraneous": true, "dependencies": { "@project-gauntlet/api": "file:../../js/api" }, @@ -2922,12 +3023,9 @@ "typescript": "*" } }, - "scenarios/plugins/docs_inline_two_sections/node_modules/@project-gauntlet/api": { - "resolved": "scenarios/js/api", - "link": true - }, "scenarios/plugins/docs_list": { "name": "@project-gauntlet/docs-list", + "extraneous": true, "dependencies": { "@project-gauntlet/api": "file:../../js/api" }, @@ -2937,10 +3035,6 @@ "@types/react": "*", "typescript": "*" } - }, - "scenarios/plugins/docs_list/node_modules/@project-gauntlet/api": { - "resolved": "scenarios/js/api", - "link": true } } } diff --git a/package.json b/package.json index d1e35bf..cf422ab 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "scripts": { "build-all": "npm run build --workspaces --if-present", - "build-scenarios": "npm run build --workspace scenarios --if-present", + "build-scenarios": "npm run build --workspace example_plugins --if-present", "build-dev-plugin": "npm run build --workspace dev_plugin", "build": "npm run build --workspace js --workspace bundled_plugins --if-present", "run-scenarios": "npm run run-scenarios --workspace js/scenario_runner_cli", @@ -12,7 +12,7 @@ "workspaces": [ "dev_plugin", "bundled_plugins/*", - "scenarios/plugins/*", + "example_plugins/plugins/*", "js/typings", "js/build", "js/api_build", diff --git a/rust/scenario_runner/src/frontend_mock.rs b/rust/scenario_runner/src/frontend_mock.rs index c108972..29431e1 100644 --- a/rust/scenario_runner/src/frontend_mock.rs +++ b/rust/scenario_runner/src/frontend_mock.rs @@ -36,7 +36,7 @@ pub async fn start_scenario_runner_frontend( .to_string(); let scenario_data_dir = scenario_dir - .join("data") + .join("scenarios") .join(&plugin_name) .to_str() .expect("scenario_data_dir is invalid UTF-8") From d7d87d0829d8ac48f9a560ad3531529a6991eebd Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:35:45 +0100 Subject: [PATCH 022/170] Rename example plugins from docs_* to ui_* --- example_plugins/plugins/{docs_detail => ui_detail}/.gitignore | 0 example_plugins/plugins/{docs_detail => ui_detail}/gauntlet.toml | 0 example_plugins/plugins/{docs_detail => ui_detail}/package.json | 0 .../plugins/{docs_detail => ui_detail}/src/content.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/content_code_block.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/content_header.tsx | 0 .../{docs_detail => ui_detail}/src/content_horizontal_break.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/content_image.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/content_paragraph.tsx | 0 example_plugins/plugins/{docs_detail => ui_detail}/src/main.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/metadata.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/metadata_icon.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/metadata_link.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/metadata_separator.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/metadata_tag_list.tsx | 0 .../plugins/{docs_detail => ui_detail}/src/metadata_value.tsx | 0 example_plugins/plugins/{docs_detail => ui_detail}/tsconfig.json | 0 example_plugins/plugins/{docs_form => ui_form}/.gitignore | 0 example_plugins/plugins/{docs_form => ui_form}/gauntlet.toml | 0 example_plugins/plugins/{docs_form => ui_form}/package.json | 0 example_plugins/plugins/{docs_form => ui_form}/src/checkbox.tsx | 0 .../plugins/{docs_form => ui_form}/src/date-picker.tsx | 0 example_plugins/plugins/{docs_form => ui_form}/src/main.tsx | 0 .../plugins/{docs_form => ui_form}/src/password-field.tsx | 0 example_plugins/plugins/{docs_form => ui_form}/src/select.tsx | 0 example_plugins/plugins/{docs_form => ui_form}/src/separator.tsx | 0 example_plugins/plugins/{docs_form => ui_form}/src/text-field.tsx | 0 example_plugins/plugins/{docs_form => ui_form}/tsconfig.json | 0 example_plugins/plugins/{docs_grid => ui_grid}/.gitignore | 0 example_plugins/plugins/{docs_grid => ui_grid}/gauntlet.toml | 0 example_plugins/plugins/{docs_grid => ui_grid}/package.json | 0 .../plugins/{docs_grid => ui_grid}/src/content_code_block.tsx | 0 .../plugins/{docs_grid => ui_grid}/src/content_headers.tsx | 0 .../plugins/{docs_grid => ui_grid}/src/content_image.tsx | 0 .../plugins/{docs_grid => ui_grid}/src/content_paragraph.tsx | 0 example_plugins/plugins/{docs_grid => ui_grid}/src/empty_view.tsx | 0 example_plugins/plugins/{docs_grid => ui_grid}/src/main.tsx | 0 example_plugins/plugins/{docs_grid => ui_grid}/src/search_bar.tsx | 0 example_plugins/plugins/{docs_grid => ui_grid}/src/section.tsx | 0 example_plugins/plugins/{docs_grid => ui_grid}/tsconfig.json | 0 .../{docs_inline_separators => ui_inline_separators}/.gitignore | 0 .../gauntlet.toml | 0 .../{docs_inline_separators => ui_inline_separators}/package.json | 0 .../{docs_inline_separators => ui_inline_separators}/src/main.tsx | 0 .../tsconfig.json | 0 .../.gitignore | 0 .../gauntlet.toml | 0 .../package.json | 0 .../src/main.tsx | 0 .../tsconfig.json | 0 .../.gitignore | 0 .../gauntlet.toml | 0 .../package.json | 0 .../src/main.tsx | 0 .../tsconfig.json | 0 example_plugins/plugins/{docs_list => ui_list}/.gitignore | 0 example_plugins/plugins/{docs_list => ui_list}/gauntlet.toml | 0 example_plugins/plugins/{docs_list => ui_list}/package.json | 0 example_plugins/plugins/{docs_list => ui_list}/src/detail.tsx | 0 example_plugins/plugins/{docs_list => ui_list}/src/empty_view.tsx | 0 example_plugins/plugins/{docs_list => ui_list}/src/main.tsx | 0 example_plugins/plugins/{docs_list => ui_list}/src/search_bar.tsx | 0 example_plugins/plugins/{docs_list => ui_list}/src/section.tsx | 0 example_plugins/plugins/{docs_list => ui_list}/tsconfig.json | 0 .../{docs_detail => ui_detail}/content-code-block/default.json | 0 .../{docs_detail => ui_detail}/content-header/default.json | 0 .../content-horizontal-break/default.json | 0 .../{docs_detail => ui_detail}/content-image/default.json | 0 .../{docs_detail => ui_detail}/content-paragraph/default.json | 0 .../scenarios/{docs_detail => ui_detail}/content/default.json | 0 .../scenarios/{docs_detail => ui_detail}/main/default.json | 0 .../{docs_detail => ui_detail}/metadata-icon/default.json | 0 .../{docs_detail => ui_detail}/metadata-link/default.json | 0 .../{docs_detail => ui_detail}/metadata-separator/default.json | 0 .../{docs_detail => ui_detail}/metadata-tag-list/default.json | 0 .../{docs_detail => ui_detail}/metadata-value/default.json | 0 .../scenarios/{docs_detail => ui_detail}/metadata/default.json | 0 .../scenarios/{docs_form => ui_form}/checkbox/default.json | 0 .../scenarios/{docs_form => ui_form}/date-picker/default.json | 0 .../scenarios/{docs_form => ui_form}/main/default.json | 0 .../scenarios/{docs_form => ui_form}/password-field/default.json | 0 .../scenarios/{docs_form => ui_form}/select/default.json | 0 .../scenarios/{docs_form => ui_form}/separator/default.json | 0 .../scenarios/{docs_form => ui_form}/text-field/default.json | 0 .../{docs_grid => ui_grid}/content-code-block/default.json | 0 .../scenarios/{docs_grid => ui_grid}/content-headers/default.json | 0 .../scenarios/{docs_grid => ui_grid}/content-image/default.json | 0 .../{docs_grid => ui_grid}/content-paragraph/default.json | 0 .../scenarios/{docs_grid => ui_grid}/empty-view/default.json | 0 .../scenarios/{docs_grid => ui_grid}/main/default.json | 0 .../scenarios/{docs_grid => ui_grid}/search-bar/default.json | 0 .../scenarios/{docs_grid => ui_grid}/section/default.json | 0 .../main/separator.json | 0 .../main/three-sections.json | 0 .../main/two-sections.json | 0 .../scenarios/{docs_list => ui_list}/detail/default.json | 0 .../scenarios/{docs_list => ui_list}/empty-view/default.json | 0 .../scenarios/{docs_list => ui_list}/main/default.json | 0 .../scenarios/{docs_list => ui_list}/search-bar/default.json | 0 .../scenarios/{docs_list => ui_list}/section/default.json | 0 100 files changed, 0 insertions(+), 0 deletions(-) rename example_plugins/plugins/{docs_detail => ui_detail}/.gitignore (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/gauntlet.toml (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/package.json (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/content.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/content_code_block.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/content_header.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/content_horizontal_break.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/content_image.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/content_paragraph.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/main.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/metadata.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/metadata_icon.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/metadata_link.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/metadata_separator.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/metadata_tag_list.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/src/metadata_value.tsx (100%) rename example_plugins/plugins/{docs_detail => ui_detail}/tsconfig.json (100%) rename example_plugins/plugins/{docs_form => ui_form}/.gitignore (100%) rename example_plugins/plugins/{docs_form => ui_form}/gauntlet.toml (100%) rename example_plugins/plugins/{docs_form => ui_form}/package.json (100%) rename example_plugins/plugins/{docs_form => ui_form}/src/checkbox.tsx (100%) rename example_plugins/plugins/{docs_form => ui_form}/src/date-picker.tsx (100%) rename example_plugins/plugins/{docs_form => ui_form}/src/main.tsx (100%) rename example_plugins/plugins/{docs_form => ui_form}/src/password-field.tsx (100%) rename example_plugins/plugins/{docs_form => ui_form}/src/select.tsx (100%) rename example_plugins/plugins/{docs_form => ui_form}/src/separator.tsx (100%) rename example_plugins/plugins/{docs_form => ui_form}/src/text-field.tsx (100%) rename example_plugins/plugins/{docs_form => ui_form}/tsconfig.json (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/.gitignore (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/gauntlet.toml (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/package.json (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/src/content_code_block.tsx (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/src/content_headers.tsx (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/src/content_image.tsx (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/src/content_paragraph.tsx (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/src/empty_view.tsx (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/src/main.tsx (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/src/search_bar.tsx (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/src/section.tsx (100%) rename example_plugins/plugins/{docs_grid => ui_grid}/tsconfig.json (100%) rename example_plugins/plugins/{docs_inline_separators => ui_inline_separators}/.gitignore (100%) rename example_plugins/plugins/{docs_inline_separators => ui_inline_separators}/gauntlet.toml (100%) rename example_plugins/plugins/{docs_inline_separators => ui_inline_separators}/package.json (100%) rename example_plugins/plugins/{docs_inline_separators => ui_inline_separators}/src/main.tsx (100%) rename example_plugins/plugins/{docs_inline_separators => ui_inline_separators}/tsconfig.json (100%) rename example_plugins/plugins/{docs_inline_three_sections => ui_inline_three_sections}/.gitignore (100%) rename example_plugins/plugins/{docs_inline_three_sections => ui_inline_three_sections}/gauntlet.toml (100%) rename example_plugins/plugins/{docs_inline_three_sections => ui_inline_three_sections}/package.json (100%) rename example_plugins/plugins/{docs_inline_three_sections => ui_inline_three_sections}/src/main.tsx (100%) rename example_plugins/plugins/{docs_inline_three_sections => ui_inline_three_sections}/tsconfig.json (100%) rename example_plugins/plugins/{docs_inline_two_sections => ui_inline_two_sections}/.gitignore (100%) rename example_plugins/plugins/{docs_inline_two_sections => ui_inline_two_sections}/gauntlet.toml (100%) rename example_plugins/plugins/{docs_inline_two_sections => ui_inline_two_sections}/package.json (100%) rename example_plugins/plugins/{docs_inline_two_sections => ui_inline_two_sections}/src/main.tsx (100%) rename example_plugins/plugins/{docs_inline_two_sections => ui_inline_two_sections}/tsconfig.json (100%) rename example_plugins/plugins/{docs_list => ui_list}/.gitignore (100%) rename example_plugins/plugins/{docs_list => ui_list}/gauntlet.toml (100%) rename example_plugins/plugins/{docs_list => ui_list}/package.json (100%) rename example_plugins/plugins/{docs_list => ui_list}/src/detail.tsx (100%) rename example_plugins/plugins/{docs_list => ui_list}/src/empty_view.tsx (100%) rename example_plugins/plugins/{docs_list => ui_list}/src/main.tsx (100%) rename example_plugins/plugins/{docs_list => ui_list}/src/search_bar.tsx (100%) rename example_plugins/plugins/{docs_list => ui_list}/src/section.tsx (100%) rename example_plugins/plugins/{docs_list => ui_list}/tsconfig.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/content-code-block/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/content-header/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/content-horizontal-break/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/content-image/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/content-paragraph/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/content/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/main/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/metadata-icon/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/metadata-link/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/metadata-separator/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/metadata-tag-list/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/metadata-value/default.json (100%) rename example_plugins/scenarios/{docs_detail => ui_detail}/metadata/default.json (100%) rename example_plugins/scenarios/{docs_form => ui_form}/checkbox/default.json (100%) rename example_plugins/scenarios/{docs_form => ui_form}/date-picker/default.json (100%) rename example_plugins/scenarios/{docs_form => ui_form}/main/default.json (100%) rename example_plugins/scenarios/{docs_form => ui_form}/password-field/default.json (100%) rename example_plugins/scenarios/{docs_form => ui_form}/select/default.json (100%) rename example_plugins/scenarios/{docs_form => ui_form}/separator/default.json (100%) rename example_plugins/scenarios/{docs_form => ui_form}/text-field/default.json (100%) rename example_plugins/scenarios/{docs_grid => ui_grid}/content-code-block/default.json (100%) rename example_plugins/scenarios/{docs_grid => ui_grid}/content-headers/default.json (100%) rename example_plugins/scenarios/{docs_grid => ui_grid}/content-image/default.json (100%) rename example_plugins/scenarios/{docs_grid => ui_grid}/content-paragraph/default.json (100%) rename example_plugins/scenarios/{docs_grid => ui_grid}/empty-view/default.json (100%) rename example_plugins/scenarios/{docs_grid => ui_grid}/main/default.json (100%) rename example_plugins/scenarios/{docs_grid => ui_grid}/search-bar/default.json (100%) rename example_plugins/scenarios/{docs_grid => ui_grid}/section/default.json (100%) rename example_plugins/scenarios/{docs_inline_separators => ui_inline_separators}/main/separator.json (100%) rename example_plugins/scenarios/{docs_inline_three_sections => ui_inline_three_sections}/main/three-sections.json (100%) rename example_plugins/scenarios/{docs_inline_two_sections => ui_inline_two_sections}/main/two-sections.json (100%) rename example_plugins/scenarios/{docs_list => ui_list}/detail/default.json (100%) rename example_plugins/scenarios/{docs_list => ui_list}/empty-view/default.json (100%) rename example_plugins/scenarios/{docs_list => ui_list}/main/default.json (100%) rename example_plugins/scenarios/{docs_list => ui_list}/search-bar/default.json (100%) rename example_plugins/scenarios/{docs_list => ui_list}/section/default.json (100%) diff --git a/example_plugins/plugins/docs_detail/.gitignore b/example_plugins/plugins/ui_detail/.gitignore similarity index 100% rename from example_plugins/plugins/docs_detail/.gitignore rename to example_plugins/plugins/ui_detail/.gitignore diff --git a/example_plugins/plugins/docs_detail/gauntlet.toml b/example_plugins/plugins/ui_detail/gauntlet.toml similarity index 100% rename from example_plugins/plugins/docs_detail/gauntlet.toml rename to example_plugins/plugins/ui_detail/gauntlet.toml diff --git a/example_plugins/plugins/docs_detail/package.json b/example_plugins/plugins/ui_detail/package.json similarity index 100% rename from example_plugins/plugins/docs_detail/package.json rename to example_plugins/plugins/ui_detail/package.json diff --git a/example_plugins/plugins/docs_detail/src/content.tsx b/example_plugins/plugins/ui_detail/src/content.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/content.tsx rename to example_plugins/plugins/ui_detail/src/content.tsx diff --git a/example_plugins/plugins/docs_detail/src/content_code_block.tsx b/example_plugins/plugins/ui_detail/src/content_code_block.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/content_code_block.tsx rename to example_plugins/plugins/ui_detail/src/content_code_block.tsx diff --git a/example_plugins/plugins/docs_detail/src/content_header.tsx b/example_plugins/plugins/ui_detail/src/content_header.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/content_header.tsx rename to example_plugins/plugins/ui_detail/src/content_header.tsx diff --git a/example_plugins/plugins/docs_detail/src/content_horizontal_break.tsx b/example_plugins/plugins/ui_detail/src/content_horizontal_break.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/content_horizontal_break.tsx rename to example_plugins/plugins/ui_detail/src/content_horizontal_break.tsx diff --git a/example_plugins/plugins/docs_detail/src/content_image.tsx b/example_plugins/plugins/ui_detail/src/content_image.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/content_image.tsx rename to example_plugins/plugins/ui_detail/src/content_image.tsx diff --git a/example_plugins/plugins/docs_detail/src/content_paragraph.tsx b/example_plugins/plugins/ui_detail/src/content_paragraph.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/content_paragraph.tsx rename to example_plugins/plugins/ui_detail/src/content_paragraph.tsx diff --git a/example_plugins/plugins/docs_detail/src/main.tsx b/example_plugins/plugins/ui_detail/src/main.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/main.tsx rename to example_plugins/plugins/ui_detail/src/main.tsx diff --git a/example_plugins/plugins/docs_detail/src/metadata.tsx b/example_plugins/plugins/ui_detail/src/metadata.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/metadata.tsx rename to example_plugins/plugins/ui_detail/src/metadata.tsx diff --git a/example_plugins/plugins/docs_detail/src/metadata_icon.tsx b/example_plugins/plugins/ui_detail/src/metadata_icon.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/metadata_icon.tsx rename to example_plugins/plugins/ui_detail/src/metadata_icon.tsx diff --git a/example_plugins/plugins/docs_detail/src/metadata_link.tsx b/example_plugins/plugins/ui_detail/src/metadata_link.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/metadata_link.tsx rename to example_plugins/plugins/ui_detail/src/metadata_link.tsx diff --git a/example_plugins/plugins/docs_detail/src/metadata_separator.tsx b/example_plugins/plugins/ui_detail/src/metadata_separator.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/metadata_separator.tsx rename to example_plugins/plugins/ui_detail/src/metadata_separator.tsx diff --git a/example_plugins/plugins/docs_detail/src/metadata_tag_list.tsx b/example_plugins/plugins/ui_detail/src/metadata_tag_list.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/metadata_tag_list.tsx rename to example_plugins/plugins/ui_detail/src/metadata_tag_list.tsx diff --git a/example_plugins/plugins/docs_detail/src/metadata_value.tsx b/example_plugins/plugins/ui_detail/src/metadata_value.tsx similarity index 100% rename from example_plugins/plugins/docs_detail/src/metadata_value.tsx rename to example_plugins/plugins/ui_detail/src/metadata_value.tsx diff --git a/example_plugins/plugins/docs_detail/tsconfig.json b/example_plugins/plugins/ui_detail/tsconfig.json similarity index 100% rename from example_plugins/plugins/docs_detail/tsconfig.json rename to example_plugins/plugins/ui_detail/tsconfig.json diff --git a/example_plugins/plugins/docs_form/.gitignore b/example_plugins/plugins/ui_form/.gitignore similarity index 100% rename from example_plugins/plugins/docs_form/.gitignore rename to example_plugins/plugins/ui_form/.gitignore diff --git a/example_plugins/plugins/docs_form/gauntlet.toml b/example_plugins/plugins/ui_form/gauntlet.toml similarity index 100% rename from example_plugins/plugins/docs_form/gauntlet.toml rename to example_plugins/plugins/ui_form/gauntlet.toml diff --git a/example_plugins/plugins/docs_form/package.json b/example_plugins/plugins/ui_form/package.json similarity index 100% rename from example_plugins/plugins/docs_form/package.json rename to example_plugins/plugins/ui_form/package.json diff --git a/example_plugins/plugins/docs_form/src/checkbox.tsx b/example_plugins/plugins/ui_form/src/checkbox.tsx similarity index 100% rename from example_plugins/plugins/docs_form/src/checkbox.tsx rename to example_plugins/plugins/ui_form/src/checkbox.tsx diff --git a/example_plugins/plugins/docs_form/src/date-picker.tsx b/example_plugins/plugins/ui_form/src/date-picker.tsx similarity index 100% rename from example_plugins/plugins/docs_form/src/date-picker.tsx rename to example_plugins/plugins/ui_form/src/date-picker.tsx diff --git a/example_plugins/plugins/docs_form/src/main.tsx b/example_plugins/plugins/ui_form/src/main.tsx similarity index 100% rename from example_plugins/plugins/docs_form/src/main.tsx rename to example_plugins/plugins/ui_form/src/main.tsx diff --git a/example_plugins/plugins/docs_form/src/password-field.tsx b/example_plugins/plugins/ui_form/src/password-field.tsx similarity index 100% rename from example_plugins/plugins/docs_form/src/password-field.tsx rename to example_plugins/plugins/ui_form/src/password-field.tsx diff --git a/example_plugins/plugins/docs_form/src/select.tsx b/example_plugins/plugins/ui_form/src/select.tsx similarity index 100% rename from example_plugins/plugins/docs_form/src/select.tsx rename to example_plugins/plugins/ui_form/src/select.tsx diff --git a/example_plugins/plugins/docs_form/src/separator.tsx b/example_plugins/plugins/ui_form/src/separator.tsx similarity index 100% rename from example_plugins/plugins/docs_form/src/separator.tsx rename to example_plugins/plugins/ui_form/src/separator.tsx diff --git a/example_plugins/plugins/docs_form/src/text-field.tsx b/example_plugins/plugins/ui_form/src/text-field.tsx similarity index 100% rename from example_plugins/plugins/docs_form/src/text-field.tsx rename to example_plugins/plugins/ui_form/src/text-field.tsx diff --git a/example_plugins/plugins/docs_form/tsconfig.json b/example_plugins/plugins/ui_form/tsconfig.json similarity index 100% rename from example_plugins/plugins/docs_form/tsconfig.json rename to example_plugins/plugins/ui_form/tsconfig.json diff --git a/example_plugins/plugins/docs_grid/.gitignore b/example_plugins/plugins/ui_grid/.gitignore similarity index 100% rename from example_plugins/plugins/docs_grid/.gitignore rename to example_plugins/plugins/ui_grid/.gitignore diff --git a/example_plugins/plugins/docs_grid/gauntlet.toml b/example_plugins/plugins/ui_grid/gauntlet.toml similarity index 100% rename from example_plugins/plugins/docs_grid/gauntlet.toml rename to example_plugins/plugins/ui_grid/gauntlet.toml diff --git a/example_plugins/plugins/docs_grid/package.json b/example_plugins/plugins/ui_grid/package.json similarity index 100% rename from example_plugins/plugins/docs_grid/package.json rename to example_plugins/plugins/ui_grid/package.json diff --git a/example_plugins/plugins/docs_grid/src/content_code_block.tsx b/example_plugins/plugins/ui_grid/src/content_code_block.tsx similarity index 100% rename from example_plugins/plugins/docs_grid/src/content_code_block.tsx rename to example_plugins/plugins/ui_grid/src/content_code_block.tsx diff --git a/example_plugins/plugins/docs_grid/src/content_headers.tsx b/example_plugins/plugins/ui_grid/src/content_headers.tsx similarity index 100% rename from example_plugins/plugins/docs_grid/src/content_headers.tsx rename to example_plugins/plugins/ui_grid/src/content_headers.tsx diff --git a/example_plugins/plugins/docs_grid/src/content_image.tsx b/example_plugins/plugins/ui_grid/src/content_image.tsx similarity index 100% rename from example_plugins/plugins/docs_grid/src/content_image.tsx rename to example_plugins/plugins/ui_grid/src/content_image.tsx diff --git a/example_plugins/plugins/docs_grid/src/content_paragraph.tsx b/example_plugins/plugins/ui_grid/src/content_paragraph.tsx similarity index 100% rename from example_plugins/plugins/docs_grid/src/content_paragraph.tsx rename to example_plugins/plugins/ui_grid/src/content_paragraph.tsx diff --git a/example_plugins/plugins/docs_grid/src/empty_view.tsx b/example_plugins/plugins/ui_grid/src/empty_view.tsx similarity index 100% rename from example_plugins/plugins/docs_grid/src/empty_view.tsx rename to example_plugins/plugins/ui_grid/src/empty_view.tsx diff --git a/example_plugins/plugins/docs_grid/src/main.tsx b/example_plugins/plugins/ui_grid/src/main.tsx similarity index 100% rename from example_plugins/plugins/docs_grid/src/main.tsx rename to example_plugins/plugins/ui_grid/src/main.tsx diff --git a/example_plugins/plugins/docs_grid/src/search_bar.tsx b/example_plugins/plugins/ui_grid/src/search_bar.tsx similarity index 100% rename from example_plugins/plugins/docs_grid/src/search_bar.tsx rename to example_plugins/plugins/ui_grid/src/search_bar.tsx diff --git a/example_plugins/plugins/docs_grid/src/section.tsx b/example_plugins/plugins/ui_grid/src/section.tsx similarity index 100% rename from example_plugins/plugins/docs_grid/src/section.tsx rename to example_plugins/plugins/ui_grid/src/section.tsx diff --git a/example_plugins/plugins/docs_grid/tsconfig.json b/example_plugins/plugins/ui_grid/tsconfig.json similarity index 100% rename from example_plugins/plugins/docs_grid/tsconfig.json rename to example_plugins/plugins/ui_grid/tsconfig.json diff --git a/example_plugins/plugins/docs_inline_separators/.gitignore b/example_plugins/plugins/ui_inline_separators/.gitignore similarity index 100% rename from example_plugins/plugins/docs_inline_separators/.gitignore rename to example_plugins/plugins/ui_inline_separators/.gitignore diff --git a/example_plugins/plugins/docs_inline_separators/gauntlet.toml b/example_plugins/plugins/ui_inline_separators/gauntlet.toml similarity index 100% rename from example_plugins/plugins/docs_inline_separators/gauntlet.toml rename to example_plugins/plugins/ui_inline_separators/gauntlet.toml diff --git a/example_plugins/plugins/docs_inline_separators/package.json b/example_plugins/plugins/ui_inline_separators/package.json similarity index 100% rename from example_plugins/plugins/docs_inline_separators/package.json rename to example_plugins/plugins/ui_inline_separators/package.json diff --git a/example_plugins/plugins/docs_inline_separators/src/main.tsx b/example_plugins/plugins/ui_inline_separators/src/main.tsx similarity index 100% rename from example_plugins/plugins/docs_inline_separators/src/main.tsx rename to example_plugins/plugins/ui_inline_separators/src/main.tsx diff --git a/example_plugins/plugins/docs_inline_separators/tsconfig.json b/example_plugins/plugins/ui_inline_separators/tsconfig.json similarity index 100% rename from example_plugins/plugins/docs_inline_separators/tsconfig.json rename to example_plugins/plugins/ui_inline_separators/tsconfig.json diff --git a/example_plugins/plugins/docs_inline_three_sections/.gitignore b/example_plugins/plugins/ui_inline_three_sections/.gitignore similarity index 100% rename from example_plugins/plugins/docs_inline_three_sections/.gitignore rename to example_plugins/plugins/ui_inline_three_sections/.gitignore diff --git a/example_plugins/plugins/docs_inline_three_sections/gauntlet.toml b/example_plugins/plugins/ui_inline_three_sections/gauntlet.toml similarity index 100% rename from example_plugins/plugins/docs_inline_three_sections/gauntlet.toml rename to example_plugins/plugins/ui_inline_three_sections/gauntlet.toml diff --git a/example_plugins/plugins/docs_inline_three_sections/package.json b/example_plugins/plugins/ui_inline_three_sections/package.json similarity index 100% rename from example_plugins/plugins/docs_inline_three_sections/package.json rename to example_plugins/plugins/ui_inline_three_sections/package.json diff --git a/example_plugins/plugins/docs_inline_three_sections/src/main.tsx b/example_plugins/plugins/ui_inline_three_sections/src/main.tsx similarity index 100% rename from example_plugins/plugins/docs_inline_three_sections/src/main.tsx rename to example_plugins/plugins/ui_inline_three_sections/src/main.tsx diff --git a/example_plugins/plugins/docs_inline_three_sections/tsconfig.json b/example_plugins/plugins/ui_inline_three_sections/tsconfig.json similarity index 100% rename from example_plugins/plugins/docs_inline_three_sections/tsconfig.json rename to example_plugins/plugins/ui_inline_three_sections/tsconfig.json diff --git a/example_plugins/plugins/docs_inline_two_sections/.gitignore b/example_plugins/plugins/ui_inline_two_sections/.gitignore similarity index 100% rename from example_plugins/plugins/docs_inline_two_sections/.gitignore rename to example_plugins/plugins/ui_inline_two_sections/.gitignore diff --git a/example_plugins/plugins/docs_inline_two_sections/gauntlet.toml b/example_plugins/plugins/ui_inline_two_sections/gauntlet.toml similarity index 100% rename from example_plugins/plugins/docs_inline_two_sections/gauntlet.toml rename to example_plugins/plugins/ui_inline_two_sections/gauntlet.toml diff --git a/example_plugins/plugins/docs_inline_two_sections/package.json b/example_plugins/plugins/ui_inline_two_sections/package.json similarity index 100% rename from example_plugins/plugins/docs_inline_two_sections/package.json rename to example_plugins/plugins/ui_inline_two_sections/package.json diff --git a/example_plugins/plugins/docs_inline_two_sections/src/main.tsx b/example_plugins/plugins/ui_inline_two_sections/src/main.tsx similarity index 100% rename from example_plugins/plugins/docs_inline_two_sections/src/main.tsx rename to example_plugins/plugins/ui_inline_two_sections/src/main.tsx diff --git a/example_plugins/plugins/docs_inline_two_sections/tsconfig.json b/example_plugins/plugins/ui_inline_two_sections/tsconfig.json similarity index 100% rename from example_plugins/plugins/docs_inline_two_sections/tsconfig.json rename to example_plugins/plugins/ui_inline_two_sections/tsconfig.json diff --git a/example_plugins/plugins/docs_list/.gitignore b/example_plugins/plugins/ui_list/.gitignore similarity index 100% rename from example_plugins/plugins/docs_list/.gitignore rename to example_plugins/plugins/ui_list/.gitignore diff --git a/example_plugins/plugins/docs_list/gauntlet.toml b/example_plugins/plugins/ui_list/gauntlet.toml similarity index 100% rename from example_plugins/plugins/docs_list/gauntlet.toml rename to example_plugins/plugins/ui_list/gauntlet.toml diff --git a/example_plugins/plugins/docs_list/package.json b/example_plugins/plugins/ui_list/package.json similarity index 100% rename from example_plugins/plugins/docs_list/package.json rename to example_plugins/plugins/ui_list/package.json diff --git a/example_plugins/plugins/docs_list/src/detail.tsx b/example_plugins/plugins/ui_list/src/detail.tsx similarity index 100% rename from example_plugins/plugins/docs_list/src/detail.tsx rename to example_plugins/plugins/ui_list/src/detail.tsx diff --git a/example_plugins/plugins/docs_list/src/empty_view.tsx b/example_plugins/plugins/ui_list/src/empty_view.tsx similarity index 100% rename from example_plugins/plugins/docs_list/src/empty_view.tsx rename to example_plugins/plugins/ui_list/src/empty_view.tsx diff --git a/example_plugins/plugins/docs_list/src/main.tsx b/example_plugins/plugins/ui_list/src/main.tsx similarity index 100% rename from example_plugins/plugins/docs_list/src/main.tsx rename to example_plugins/plugins/ui_list/src/main.tsx diff --git a/example_plugins/plugins/docs_list/src/search_bar.tsx b/example_plugins/plugins/ui_list/src/search_bar.tsx similarity index 100% rename from example_plugins/plugins/docs_list/src/search_bar.tsx rename to example_plugins/plugins/ui_list/src/search_bar.tsx diff --git a/example_plugins/plugins/docs_list/src/section.tsx b/example_plugins/plugins/ui_list/src/section.tsx similarity index 100% rename from example_plugins/plugins/docs_list/src/section.tsx rename to example_plugins/plugins/ui_list/src/section.tsx diff --git a/example_plugins/plugins/docs_list/tsconfig.json b/example_plugins/plugins/ui_list/tsconfig.json similarity index 100% rename from example_plugins/plugins/docs_list/tsconfig.json rename to example_plugins/plugins/ui_list/tsconfig.json diff --git a/example_plugins/scenarios/docs_detail/content-code-block/default.json b/example_plugins/scenarios/ui_detail/content-code-block/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/content-code-block/default.json rename to example_plugins/scenarios/ui_detail/content-code-block/default.json diff --git a/example_plugins/scenarios/docs_detail/content-header/default.json b/example_plugins/scenarios/ui_detail/content-header/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/content-header/default.json rename to example_plugins/scenarios/ui_detail/content-header/default.json diff --git a/example_plugins/scenarios/docs_detail/content-horizontal-break/default.json b/example_plugins/scenarios/ui_detail/content-horizontal-break/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/content-horizontal-break/default.json rename to example_plugins/scenarios/ui_detail/content-horizontal-break/default.json diff --git a/example_plugins/scenarios/docs_detail/content-image/default.json b/example_plugins/scenarios/ui_detail/content-image/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/content-image/default.json rename to example_plugins/scenarios/ui_detail/content-image/default.json diff --git a/example_plugins/scenarios/docs_detail/content-paragraph/default.json b/example_plugins/scenarios/ui_detail/content-paragraph/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/content-paragraph/default.json rename to example_plugins/scenarios/ui_detail/content-paragraph/default.json diff --git a/example_plugins/scenarios/docs_detail/content/default.json b/example_plugins/scenarios/ui_detail/content/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/content/default.json rename to example_plugins/scenarios/ui_detail/content/default.json diff --git a/example_plugins/scenarios/docs_detail/main/default.json b/example_plugins/scenarios/ui_detail/main/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/main/default.json rename to example_plugins/scenarios/ui_detail/main/default.json diff --git a/example_plugins/scenarios/docs_detail/metadata-icon/default.json b/example_plugins/scenarios/ui_detail/metadata-icon/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/metadata-icon/default.json rename to example_plugins/scenarios/ui_detail/metadata-icon/default.json diff --git a/example_plugins/scenarios/docs_detail/metadata-link/default.json b/example_plugins/scenarios/ui_detail/metadata-link/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/metadata-link/default.json rename to example_plugins/scenarios/ui_detail/metadata-link/default.json diff --git a/example_plugins/scenarios/docs_detail/metadata-separator/default.json b/example_plugins/scenarios/ui_detail/metadata-separator/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/metadata-separator/default.json rename to example_plugins/scenarios/ui_detail/metadata-separator/default.json diff --git a/example_plugins/scenarios/docs_detail/metadata-tag-list/default.json b/example_plugins/scenarios/ui_detail/metadata-tag-list/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/metadata-tag-list/default.json rename to example_plugins/scenarios/ui_detail/metadata-tag-list/default.json diff --git a/example_plugins/scenarios/docs_detail/metadata-value/default.json b/example_plugins/scenarios/ui_detail/metadata-value/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/metadata-value/default.json rename to example_plugins/scenarios/ui_detail/metadata-value/default.json diff --git a/example_plugins/scenarios/docs_detail/metadata/default.json b/example_plugins/scenarios/ui_detail/metadata/default.json similarity index 100% rename from example_plugins/scenarios/docs_detail/metadata/default.json rename to example_plugins/scenarios/ui_detail/metadata/default.json diff --git a/example_plugins/scenarios/docs_form/checkbox/default.json b/example_plugins/scenarios/ui_form/checkbox/default.json similarity index 100% rename from example_plugins/scenarios/docs_form/checkbox/default.json rename to example_plugins/scenarios/ui_form/checkbox/default.json diff --git a/example_plugins/scenarios/docs_form/date-picker/default.json b/example_plugins/scenarios/ui_form/date-picker/default.json similarity index 100% rename from example_plugins/scenarios/docs_form/date-picker/default.json rename to example_plugins/scenarios/ui_form/date-picker/default.json diff --git a/example_plugins/scenarios/docs_form/main/default.json b/example_plugins/scenarios/ui_form/main/default.json similarity index 100% rename from example_plugins/scenarios/docs_form/main/default.json rename to example_plugins/scenarios/ui_form/main/default.json diff --git a/example_plugins/scenarios/docs_form/password-field/default.json b/example_plugins/scenarios/ui_form/password-field/default.json similarity index 100% rename from example_plugins/scenarios/docs_form/password-field/default.json rename to example_plugins/scenarios/ui_form/password-field/default.json diff --git a/example_plugins/scenarios/docs_form/select/default.json b/example_plugins/scenarios/ui_form/select/default.json similarity index 100% rename from example_plugins/scenarios/docs_form/select/default.json rename to example_plugins/scenarios/ui_form/select/default.json diff --git a/example_plugins/scenarios/docs_form/separator/default.json b/example_plugins/scenarios/ui_form/separator/default.json similarity index 100% rename from example_plugins/scenarios/docs_form/separator/default.json rename to example_plugins/scenarios/ui_form/separator/default.json diff --git a/example_plugins/scenarios/docs_form/text-field/default.json b/example_plugins/scenarios/ui_form/text-field/default.json similarity index 100% rename from example_plugins/scenarios/docs_form/text-field/default.json rename to example_plugins/scenarios/ui_form/text-field/default.json diff --git a/example_plugins/scenarios/docs_grid/content-code-block/default.json b/example_plugins/scenarios/ui_grid/content-code-block/default.json similarity index 100% rename from example_plugins/scenarios/docs_grid/content-code-block/default.json rename to example_plugins/scenarios/ui_grid/content-code-block/default.json diff --git a/example_plugins/scenarios/docs_grid/content-headers/default.json b/example_plugins/scenarios/ui_grid/content-headers/default.json similarity index 100% rename from example_plugins/scenarios/docs_grid/content-headers/default.json rename to example_plugins/scenarios/ui_grid/content-headers/default.json diff --git a/example_plugins/scenarios/docs_grid/content-image/default.json b/example_plugins/scenarios/ui_grid/content-image/default.json similarity index 100% rename from example_plugins/scenarios/docs_grid/content-image/default.json rename to example_plugins/scenarios/ui_grid/content-image/default.json diff --git a/example_plugins/scenarios/docs_grid/content-paragraph/default.json b/example_plugins/scenarios/ui_grid/content-paragraph/default.json similarity index 100% rename from example_plugins/scenarios/docs_grid/content-paragraph/default.json rename to example_plugins/scenarios/ui_grid/content-paragraph/default.json diff --git a/example_plugins/scenarios/docs_grid/empty-view/default.json b/example_plugins/scenarios/ui_grid/empty-view/default.json similarity index 100% rename from example_plugins/scenarios/docs_grid/empty-view/default.json rename to example_plugins/scenarios/ui_grid/empty-view/default.json diff --git a/example_plugins/scenarios/docs_grid/main/default.json b/example_plugins/scenarios/ui_grid/main/default.json similarity index 100% rename from example_plugins/scenarios/docs_grid/main/default.json rename to example_plugins/scenarios/ui_grid/main/default.json diff --git a/example_plugins/scenarios/docs_grid/search-bar/default.json b/example_plugins/scenarios/ui_grid/search-bar/default.json similarity index 100% rename from example_plugins/scenarios/docs_grid/search-bar/default.json rename to example_plugins/scenarios/ui_grid/search-bar/default.json diff --git a/example_plugins/scenarios/docs_grid/section/default.json b/example_plugins/scenarios/ui_grid/section/default.json similarity index 100% rename from example_plugins/scenarios/docs_grid/section/default.json rename to example_plugins/scenarios/ui_grid/section/default.json diff --git a/example_plugins/scenarios/docs_inline_separators/main/separator.json b/example_plugins/scenarios/ui_inline_separators/main/separator.json similarity index 100% rename from example_plugins/scenarios/docs_inline_separators/main/separator.json rename to example_plugins/scenarios/ui_inline_separators/main/separator.json diff --git a/example_plugins/scenarios/docs_inline_three_sections/main/three-sections.json b/example_plugins/scenarios/ui_inline_three_sections/main/three-sections.json similarity index 100% rename from example_plugins/scenarios/docs_inline_three_sections/main/three-sections.json rename to example_plugins/scenarios/ui_inline_three_sections/main/three-sections.json diff --git a/example_plugins/scenarios/docs_inline_two_sections/main/two-sections.json b/example_plugins/scenarios/ui_inline_two_sections/main/two-sections.json similarity index 100% rename from example_plugins/scenarios/docs_inline_two_sections/main/two-sections.json rename to example_plugins/scenarios/ui_inline_two_sections/main/two-sections.json diff --git a/example_plugins/scenarios/docs_list/detail/default.json b/example_plugins/scenarios/ui_list/detail/default.json similarity index 100% rename from example_plugins/scenarios/docs_list/detail/default.json rename to example_plugins/scenarios/ui_list/detail/default.json diff --git a/example_plugins/scenarios/docs_list/empty-view/default.json b/example_plugins/scenarios/ui_list/empty-view/default.json similarity index 100% rename from example_plugins/scenarios/docs_list/empty-view/default.json rename to example_plugins/scenarios/ui_list/empty-view/default.json diff --git a/example_plugins/scenarios/docs_list/main/default.json b/example_plugins/scenarios/ui_list/main/default.json similarity index 100% rename from example_plugins/scenarios/docs_list/main/default.json rename to example_plugins/scenarios/ui_list/main/default.json diff --git a/example_plugins/scenarios/docs_list/search-bar/default.json b/example_plugins/scenarios/ui_list/search-bar/default.json similarity index 100% rename from example_plugins/scenarios/docs_list/search-bar/default.json rename to example_plugins/scenarios/ui_list/search-bar/default.json diff --git a/example_plugins/scenarios/docs_list/section/default.json b/example_plugins/scenarios/ui_list/section/default.json similarity index 100% rename from example_plugins/scenarios/docs_list/section/default.json rename to example_plugins/scenarios/ui_list/section/default.json From fc4df0efa14651a5444e507dc900ea2d7c97e687 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:41:59 +0100 Subject: [PATCH 023/170] Rename out-screenshot to out_screenshot --- example_plugins/.gitignore | 2 +- js/scenario_runner_cli/src/main.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example_plugins/.gitignore b/example_plugins/.gitignore index 6990142..5ba1e87 100644 --- a/example_plugins/.gitignore +++ b/example_plugins/.gitignore @@ -1,3 +1,3 @@ run -out-screenshot +out_screenshot out \ No newline at end of file diff --git a/js/scenario_runner_cli/src/main.ts b/js/scenario_runner_cli/src/main.ts index abc193e..4d72330 100644 --- a/js/scenario_runner_cli/src/main.ts +++ b/js/scenario_runner_cli/src/main.ts @@ -121,7 +121,7 @@ async function runScreenshotGen(expectedPlugin: string | undefined, expectedEntr RUST_LOG: "gauntlet-client=INFO", GAUNTLET_SCENARIO_RUNNER_TYPE: "screenshot_gen", GAUNTLET_SCREENSHOT_GEN_IN: scenarioFile, - GAUNTLET_SCREENSHOT_GEN_OUT: path.join(scenarios, "out-screenshot", plugin, entrypoint, scenarioName + ".png"), + GAUNTLET_SCREENSHOT_GEN_OUT: path.join(scenarios, "out_screenshot", plugin, entrypoint, scenarioName + ".png"), GAUNTLET_SCREENSHOT_GEN_NAME: scenarioNameTitle, }) }); From 77d20bcd6c653cd8124831c0f4765a14d4ec277a Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 21 Jan 2025 22:46:25 +0100 Subject: [PATCH 024/170] More ui_list plugin examples --- example_plugins/plugins/ui_list/gauntlet.toml | 126 ++++++++++++++++++ .../plugins/ui_list/src/content.tsx | 47 +++++++ .../ui_list/src/content_code_block.tsx | 23 ++++ .../plugins/ui_list/src/content_headers.tsx | 29 ++++ .../ui_list/src/content_horizontal_break.tsx | 40 ++++++ .../plugins/ui_list/src/content_image.tsx | 17 +++ .../plugins/ui_list/src/content_paragraph.tsx | 32 +++++ .../plugins/ui_list/src/detail.tsx | 1 + example_plugins/plugins/ui_list/src/item.tsx | 13 ++ example_plugins/plugins/ui_list/src/main.tsx | 97 ++++++++++++-- .../plugins/ui_list/src/metadata.tsx | 41 ++++++ .../plugins/ui_list/src/metadata_icon.tsx | 16 +++ .../plugins/ui_list/src/metadata_link.tsx | 15 +++ .../ui_list/src/metadata_separator.tsx | 19 +++ .../plugins/ui_list/src/metadata_tag_list.tsx | 23 ++++ .../plugins/ui_list/src/metadata_value.tsx | 15 +++ .../ui_list/content-code-block/default.json | 3 + .../ui_list/content-headers/default.json | 3 + .../content-horizontal-break/default.json | 3 + .../ui_list/content-image/default.json | 3 + .../ui_list/content-paragraph/default.json | 3 + .../scenarios/ui_list/content/default.json | 3 + .../scenarios/ui_list/item/default.json | 3 + .../ui_list/metadata-icon/default.json | 3 + .../ui_list/metadata-link/default.json | 3 + .../ui_list/metadata-separator/default.json | 3 + .../ui_list/metadata-tag-list/default.json | 3 + .../ui_list/metadata-value/default.json | 3 + .../scenarios/ui_list/metadata/default.json | 3 + 29 files changed, 580 insertions(+), 13 deletions(-) create mode 100644 example_plugins/plugins/ui_list/src/content.tsx create mode 100644 example_plugins/plugins/ui_list/src/content_code_block.tsx create mode 100644 example_plugins/plugins/ui_list/src/content_headers.tsx create mode 100644 example_plugins/plugins/ui_list/src/content_horizontal_break.tsx create mode 100644 example_plugins/plugins/ui_list/src/content_image.tsx create mode 100644 example_plugins/plugins/ui_list/src/content_paragraph.tsx create mode 100644 example_plugins/plugins/ui_list/src/item.tsx create mode 100644 example_plugins/plugins/ui_list/src/metadata.tsx create mode 100644 example_plugins/plugins/ui_list/src/metadata_icon.tsx create mode 100644 example_plugins/plugins/ui_list/src/metadata_link.tsx create mode 100644 example_plugins/plugins/ui_list/src/metadata_separator.tsx create mode 100644 example_plugins/plugins/ui_list/src/metadata_tag_list.tsx create mode 100644 example_plugins/plugins/ui_list/src/metadata_value.tsx create mode 100644 example_plugins/scenarios/ui_list/content-code-block/default.json create mode 100644 example_plugins/scenarios/ui_list/content-headers/default.json create mode 100644 example_plugins/scenarios/ui_list/content-horizontal-break/default.json create mode 100644 example_plugins/scenarios/ui_list/content-image/default.json create mode 100644 example_plugins/scenarios/ui_list/content-paragraph/default.json create mode 100644 example_plugins/scenarios/ui_list/content/default.json create mode 100644 example_plugins/scenarios/ui_list/item/default.json create mode 100644 example_plugins/scenarios/ui_list/metadata-icon/default.json create mode 100644 example_plugins/scenarios/ui_list/metadata-link/default.json create mode 100644 example_plugins/scenarios/ui_list/metadata-separator/default.json create mode 100644 example_plugins/scenarios/ui_list/metadata-tag-list/default.json create mode 100644 example_plugins/scenarios/ui_list/metadata-value/default.json create mode 100644 example_plugins/scenarios/ui_list/metadata/default.json diff --git a/example_plugins/plugins/ui_list/gauntlet.toml b/example_plugins/plugins/ui_list/gauntlet.toml index 45fe43f..c0e7845 100644 --- a/example_plugins/plugins/ui_list/gauntlet.toml +++ b/example_plugins/plugins/ui_list/gauntlet.toml @@ -48,6 +48,132 @@ type = 'view' description = '' # docs-code-segment:end +# docs-code-segment:start item +[[entrypoint]] +id = 'item' +name = 'List Item' +path = 'src/item.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start content +[[entrypoint]] +id = 'content' +name = 'List Content' +path = 'src/content.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start content-code-block +[[entrypoint]] +id = 'content-code-block' +name = 'List Content Code Block' +path = 'src/content_code_block.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start content-headers +[[entrypoint]] +id = 'content-headers' +name = 'List Content Headers' +path = 'src/content_headers.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start content-horizontal-break +[[entrypoint]] +id = 'content-horizontal-break' +name = 'List Content Horizontal Break' +path = 'src/content_horizontal_break.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start content-image +[[entrypoint]] +id = 'content-image' +name = 'List Content Image' +path = 'src/content_image.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start content-paragraph +[[entrypoint]] +id = 'content-paragraph' +name = 'List Content Paragraph' +path = 'src/content_paragraph.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start metadata +[[entrypoint]] +id = 'metadata' +name = 'List Metadata' +path = 'src/metadata.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start metadata-icon +[[entrypoint]] +id = 'metadata-icon' +name = 'List Metadata Icon' +path = 'src/metadata_icon.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start metadata-icon +[[entrypoint]] +id = 'metadata-icon' +name = 'List Metadata Icon' +path = 'src/metadata_icon.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start metadata-link +[[entrypoint]] +id = 'metadata-link' +name = 'List Metadata Link' +path = 'src/metadata_link.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start metadata-separator +[[entrypoint]] +id = 'metadata-separator' +name = 'List Metadata Separator' +path = 'src/metadata_separator.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start metadata-tag-list +[[entrypoint]] +id = 'metadata-tag-list' +name = 'List Metadata Tag List' +path = 'src/metadata_tag_list.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start metadata-value +[[entrypoint]] +id = 'metadata-value' +name = 'List Metadata Value' +path = 'src/metadata_value.tsx' +type = 'view' +description = '' +# docs-code-segment:end + [permissions] network = ["static.wikia.nocookie.net"] diff --git a/example_plugins/plugins/ui_list/src/content.tsx b/example_plugins/plugins/ui_list/src/content.tsx new file mode 100644 index 0000000..72c7ecd --- /dev/null +++ b/example_plugins/plugins/ui_list/src/content.tsx @@ -0,0 +1,47 @@ +import React, { ReactElement } from "react"; +import { Action, ActionPanel, List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + {}}/> + + } + > + + + + + + + + + + + + + + The Ezaraa were a species of warmongering carnivorous sentients that were native to the the planet + Ezaraa. + They intended to overthrow the Galactic Empire, only to replace it with their own dominion and feed + on the other species, which they deemed as lesser to them. + To arm their revolution, the dominion sent Ezaraa to take advantage of opportunities such as the + Auction of Rur. + + + Society and culture + + + "Bring the Dominion of the Ezaraa across the stars! And consume the flesh of all the lesser + species!" + + + ―An Ezaraa, to Luke Skywalker + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/content_code_block.tsx b/example_plugins/plugins/ui_list/src/content_code_block.tsx new file mode 100644 index 0000000..11a9a02 --- /dev/null +++ b/example_plugins/plugins/ui_list/src/content_code_block.tsx @@ -0,0 +1,23 @@ +import { ReactElement } from "react"; +import { List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + + + + + Hah! Why pay when one can slay? Then retrieve the bauble from its smoking chassis! It is the Ezaraa way!" + "For the glory of the Ezaraa Dominion!" + [The Ezaraas take a single causality] + "Retreat!" + + + ―Ezaraa warriors during the Rur Crystal incident + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/content_headers.tsx b/example_plugins/plugins/ui_list/src/content_headers.tsx new file mode 100644 index 0000000..a811601 --- /dev/null +++ b/example_plugins/plugins/ui_list/src/content_headers.tsx @@ -0,0 +1,29 @@ +import { ReactElement } from "react"; +import { List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + + + + + + + + + + + + + + Behind the scenes + + + The Ezaraa species first appeared in the canon crossover comic The Screaming Citadel 1, written by Kieron Gillen, illustrated by Marco Checchetto and released on May 10, 2017. + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/content_horizontal_break.tsx b/example_plugins/plugins/ui_list/src/content_horizontal_break.tsx new file mode 100644 index 0000000..7690bf2 --- /dev/null +++ b/example_plugins/plugins/ui_list/src/content_horizontal_break.tsx @@ -0,0 +1,40 @@ +import { ReactElement } from "react"; +import { List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + + + + + + + + + + + + + + Quotes + + + "Hah! Why pay when one can slay? Then retrieve the bauble from its smoking chassis! It is the Ezaraa way!" + "For the glory of the Ezaraa Dominion!" + [The Ezaraas take a single causality] + "Retreat!" + + + + "The Ezaraa will rule the universe. It is our destiny. For delivering the Rur sentience to us, you will receive 0.00001% of our Imperial revenue, for you and your next ten descendants." + + + + The Ezaraa species first appeared in the canon crossover comic The Screaming Citadel 1, written by Kieron Gillen, illustrated by Marco Checchetto and released on May 10, 2017. + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/content_image.tsx b/example_plugins/plugins/ui_list/src/content_image.tsx new file mode 100644 index 0000000..ed3ca2f --- /dev/null +++ b/example_plugins/plugins/ui_list/src/content_image.tsx @@ -0,0 +1,17 @@ +import { ReactElement } from "react"; +import { List } from "@project-gauntlet/api/components"; + +const imgUrl = "https://static.wikia.nocookie.net/starwars/images/e/e4/Ezaraa.png/revision/latest/scale-to-width-down/200?cb=20170511082800" + +export default function Main(): ReactElement { + return ( + + + + + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/content_paragraph.tsx b/example_plugins/plugins/ui_list/src/content_paragraph.tsx new file mode 100644 index 0000000..29b1f8d --- /dev/null +++ b/example_plugins/plugins/ui_list/src/content_paragraph.tsx @@ -0,0 +1,32 @@ +import { ReactElement } from "react"; +import { List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + + + + + + + + + + + + + + Hah! Why pay when one can slay? Then retrieve the bauble from its smoking chassis! It is the Ezaraa way!" + "For the glory of the Ezaraa Dominion!" + [The Ezaraas take a single causality] + "Retreat!" + + + ―Ezaraa warriors during the Rur Crystal incident + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/detail.tsx b/example_plugins/plugins/ui_list/src/detail.tsx index 72d6a6b..c5c3685 100644 --- a/example_plugins/plugins/ui_list/src/detail.tsx +++ b/example_plugins/plugins/ui_list/src/detail.tsx @@ -16,6 +16,7 @@ export default function Main(): ReactElement { + Ezaraa Sentient Humanoid Ezaraa diff --git a/example_plugins/plugins/ui_list/src/item.tsx b/example_plugins/plugins/ui_list/src/item.tsx new file mode 100644 index 0000000..9b94484 --- /dev/null +++ b/example_plugins/plugins/ui_list/src/item.tsx @@ -0,0 +1,13 @@ +import { ReactElement } from "react"; +import { IconAccessory, Icons, List, TextAccessory } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + ]}/> + ]}/> + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/main.tsx b/example_plugins/plugins/ui_list/src/main.tsx index 6458e49..4fc65b8 100644 --- a/example_plugins/plugins/ui_list/src/main.tsx +++ b/example_plugins/plugins/ui_list/src/main.tsx @@ -1,19 +1,90 @@ -import { ReactElement } from "react"; -import { List } from "@project-gauntlet/api/components"; +import React, { ReactElement } from "react"; +import { Action, ActionPanel, IconAccessory, Icons, List, TextAccessory } from "@project-gauntlet/api/components"; export default function Main(): ReactElement { return ( - - - - - - - - - - - + + {}}/> + + } + > + , + , + ]} + /> + , + , + ]} + /> + , + , + ]} + /> + , + , + ]} + /> + , + , + ]} + /> + , + , + , + ]} + /> + , + , + ]} + /> + ) } diff --git a/example_plugins/plugins/ui_list/src/metadata.tsx b/example_plugins/plugins/ui_list/src/metadata.tsx new file mode 100644 index 0000000..7d06e3a --- /dev/null +++ b/example_plugins/plugins/ui_list/src/metadata.tsx @@ -0,0 +1,41 @@ +import React, { ReactElement } from "react"; +import { Action, ActionPanel, List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + {}}/> + + } + > + + + + + + + + + + + + + Ezaraa + Sentient + Humanoid + Ezaraa + Carnivorous + + The Screaming Citadel 1 + Doctor Aphra (2016) 9 + Doctor Aphra (2016) 10 + Doctor Aphra (2016) 11 + Doctor Aphra (2016) 12 + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/metadata_icon.tsx b/example_plugins/plugins/ui_list/src/metadata_icon.tsx new file mode 100644 index 0000000..e559f6f --- /dev/null +++ b/example_plugins/plugins/ui_list/src/metadata_icon.tsx @@ -0,0 +1,16 @@ +import { ReactElement } from "react"; +import { Icons, List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + + + + + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/metadata_link.tsx b/example_plugins/plugins/ui_list/src/metadata_link.tsx new file mode 100644 index 0000000..bc99323 --- /dev/null +++ b/example_plugins/plugins/ui_list/src/metadata_link.tsx @@ -0,0 +1,15 @@ +import { ReactElement } from "react"; +import { Icons, List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + + + + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/metadata_separator.tsx b/example_plugins/plugins/ui_list/src/metadata_separator.tsx new file mode 100644 index 0000000..0cf8e38 --- /dev/null +++ b/example_plugins/plugins/ui_list/src/metadata_separator.tsx @@ -0,0 +1,19 @@ +import { ReactElement } from "react"; +import { Icons, List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + + + + Sentient + Humanoid + + Ezaraa + Carnivorous + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/metadata_tag_list.tsx b/example_plugins/plugins/ui_list/src/metadata_tag_list.tsx new file mode 100644 index 0000000..89316aa --- /dev/null +++ b/example_plugins/plugins/ui_list/src/metadata_tag_list.tsx @@ -0,0 +1,23 @@ +import { ReactElement } from "react"; +import { Icons, List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + + + + + Bounty Hunters 14 + Bounty Hunters 20 + Bounty Hunters 23 + Bounty Hunters 24 + Bounty Hunters 35 + "Tall Tales" — Revelations (2023) 1 + Bounty Hunters 42 + + + + + ) +} diff --git a/example_plugins/plugins/ui_list/src/metadata_value.tsx b/example_plugins/plugins/ui_list/src/metadata_value.tsx new file mode 100644 index 0000000..a922c7a --- /dev/null +++ b/example_plugins/plugins/ui_list/src/metadata_value.tsx @@ -0,0 +1,15 @@ +import { ReactElement } from "react"; +import { Icons, List } from "@project-gauntlet/api/components"; + +export default function Main(): ReactElement { + return ( + + + + + Sentient + + + + ) +} diff --git a/example_plugins/scenarios/ui_list/content-code-block/default.json b/example_plugins/scenarios/ui_list/content-code-block/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/content-code-block/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/content-headers/default.json b/example_plugins/scenarios/ui_list/content-headers/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/content-headers/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/content-horizontal-break/default.json b/example_plugins/scenarios/ui_list/content-horizontal-break/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/content-horizontal-break/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/content-image/default.json b/example_plugins/scenarios/ui_list/content-image/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/content-image/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/content-paragraph/default.json b/example_plugins/scenarios/ui_list/content-paragraph/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/content-paragraph/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/content/default.json b/example_plugins/scenarios/ui_list/content/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/content/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/item/default.json b/example_plugins/scenarios/ui_list/item/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/item/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/metadata-icon/default.json b/example_plugins/scenarios/ui_list/metadata-icon/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/metadata-icon/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/metadata-link/default.json b/example_plugins/scenarios/ui_list/metadata-link/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/metadata-link/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/metadata-separator/default.json b/example_plugins/scenarios/ui_list/metadata-separator/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/metadata-separator/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/metadata-tag-list/default.json b/example_plugins/scenarios/ui_list/metadata-tag-list/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/metadata-tag-list/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/metadata-value/default.json b/example_plugins/scenarios/ui_list/metadata-value/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/metadata-value/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/metadata/default.json b/example_plugins/scenarios/ui_list/metadata/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/metadata/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file From c08e33c48c5e6ad2cee3cc821b4dfa3599a877ab Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:15:43 +0100 Subject: [PATCH 025/170] Remove links from docs markdown for now --- docs/js/components/action/props/id.md | 2 +- docs/js/components/content/description.md | 2 +- docs/js/components/detail/description.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/js/components/action/props/id.md b/docs/js/components/action/props/id.md index 17dbcd3..8c9761b 100644 --- a/docs/js/components/action/props/id.md +++ b/docs/js/components/action/props/id.md @@ -1 +1 @@ -The identifier of an action. Referenced in [plugin manifest]($LINK$__PLUGIN_MANIFEST__) to assign shortcut to button in UI \ No newline at end of file +The identifier of an action. Referenced in plugin manifest to assign shortcut to button in UI \ No newline at end of file diff --git a/docs/js/components/content/description.md b/docs/js/components/content/description.md index ee6dab1..77668fe 100644 --- a/docs/js/components/content/description.md +++ b/docs/js/components/content/description.md @@ -1,3 +1,3 @@ A container for a set of non-interactable components. -Used in a variety of places like [Detail]($COMPONENT$__DETAIL__), [Inline]($COMPONENT$__INLINE__) and [GridItem]($COMPONENT$__GRID_ITEM__). +Used in a variety of places like , and . By utilizing the power of React the content can also be made dynamic \ No newline at end of file diff --git a/docs/js/components/detail/description.md b/docs/js/components/detail/description.md index 18f2574..2524727 100644 --- a/docs/js/components/detail/description.md +++ b/docs/js/components/detail/description.md @@ -1 +1 @@ -Detail is a root component that contains a possibly dynamic, non-interactable [Content]($COMPONENT$__CONTENT__) and an optional side panel \ No newline at end of file +Detail is a root component that contains a possibly dynamic, non-interactable component and an optional side panel \ No newline at end of file From 345ab751434950ba38eacec19208fe86b3809d1d Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:16:09 +0100 Subject: [PATCH 026/170] Add script to generate component model from root --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index cf422ab..a9e2bc9 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "build-scenarios": "npm run build --workspace example_plugins --if-present", "build-dev-plugin": "npm run build --workspace dev_plugin", "build": "npm run build --workspace js --workspace bundled_plugins --if-present", + "run-component-model-gen": "npm run generate-json --workspace js/api_build", "run-scenarios": "npm run run-scenarios --workspace js/scenario_runner_cli", "run-screenshot-gen": "npm run run-screenshot-gen --workspace js/scenario_runner_cli" }, From 67c7234b411b1f184092f1341d77e4508cc9ad8f Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:36:10 +0100 Subject: [PATCH 027/170] Replace _ with - in plugin examples file names --- .../plugins/ui_detail/gauntlet.toml | 20 ++++++------ ..._code_block.tsx => content-code-block.tsx} | 4 +-- ...{content_header.tsx => content-header.tsx} | 4 +-- ...break.tsx => content-horizontal-break.tsx} | 4 +-- .../{content_image.tsx => content-image.tsx} | 6 ++-- ...nt_paragraph.tsx => content-paragraph.tsx} | 4 +-- .../plugins/ui_detail/src/content.tsx | 4 +-- .../plugins/ui_detail/src/main.tsx | 10 ++++-- .../{metadata_icon.tsx => metadata-icon.tsx} | 4 +-- .../{metadata_link.tsx => metadata-link.tsx} | 4 +-- ...a_separator.tsx => metadata-separator.tsx} | 4 +-- .../ui_detail/src/metadata-tag-list.tsx | 31 +++++++++++++++++++ ...{metadata_value.tsx => metadata-value.tsx} | 4 +-- .../plugins/ui_detail/src/metadata.tsx | 4 +-- .../ui_detail/src/metadata_tag_list.tsx | 18 ----------- .../plugins/ui_form/src/checkbox.tsx | 2 +- .../plugins/ui_form/src/date-picker.tsx | 2 +- example_plugins/plugins/ui_form/src/main.tsx | 2 +- .../plugins/ui_form/src/password-field.tsx | 2 +- .../plugins/ui_form/src/select.tsx | 2 +- .../plugins/ui_form/src/separator.tsx | 2 +- .../plugins/ui_form/src/text-field.tsx | 2 +- example_plugins/plugins/ui_grid/gauntlet.toml | 12 +++---- ..._code_block.tsx => content-code-block.tsx} | 2 +- ...ontent_headers.tsx => content-headers.tsx} | 2 +- .../{content_image.tsx => content-image.tsx} | 2 +- ...nt_paragraph.tsx => content-paragraph.tsx} | 2 +- .../src/{empty_view.tsx => empty-view.tsx} | 2 +- example_plugins/plugins/ui_grid/src/main.tsx | 2 +- .../src/{search_bar.tsx => search-bar.tsx} | 2 +- .../plugins/ui_grid/src/section.tsx | 2 +- example_plugins/plugins/ui_list/gauntlet.toml | 26 ++++++++-------- ..._code_block.tsx => content-code-block.tsx} | 2 +- ...ontent_headers.tsx => content-headers.tsx} | 2 +- ...break.tsx => content-horizontal-break.tsx} | 2 +- .../{content_image.tsx => content-image.tsx} | 2 +- ...nt_paragraph.tsx => content-paragraph.tsx} | 2 +- .../plugins/ui_list/src/content.tsx | 4 +-- .../plugins/ui_list/src/detail.tsx | 2 +- .../src/{empty_view.tsx => empty-view.tsx} | 2 +- example_plugins/plugins/ui_list/src/item.tsx | 2 +- example_plugins/plugins/ui_list/src/main.tsx | 4 +-- .../{metadata_icon.tsx => metadata-icon.tsx} | 2 +- .../{metadata_link.tsx => metadata-link.tsx} | 4 +-- ...a_separator.tsx => metadata-separator.tsx} | 4 +-- ...ata_tag_list.tsx => metadata-tag-list.tsx} | 4 +-- ...{metadata_value.tsx => metadata-value.tsx} | 4 +-- .../plugins/ui_list/src/metadata.tsx | 4 +-- .../src/{search_bar.tsx => search-bar.tsx} | 2 +- .../plugins/ui_list/src/section.tsx | 2 +- 50 files changed, 131 insertions(+), 112 deletions(-) rename example_plugins/plugins/ui_detail/src/{content_code_block.tsx => content-code-block.tsx} (88%) rename example_plugins/plugins/ui_detail/src/{content_header.tsx => content-header.tsx} (93%) rename example_plugins/plugins/ui_detail/src/{content_horizontal_break.tsx => content-horizontal-break.tsx} (90%) rename example_plugins/plugins/ui_detail/src/{content_image.tsx => content-image.tsx} (83%) rename example_plugins/plugins/ui_detail/src/{content_paragraph.tsx => content-paragraph.tsx} (92%) rename example_plugins/plugins/ui_detail/src/{metadata_icon.tsx => metadata-icon.tsx} (84%) rename example_plugins/plugins/ui_detail/src/{metadata_link.tsx => metadata-link.tsx} (86%) rename example_plugins/plugins/ui_detail/src/{metadata_separator.tsx => metadata-separator.tsx} (88%) create mode 100644 example_plugins/plugins/ui_detail/src/metadata-tag-list.tsx rename example_plugins/plugins/ui_detail/src/{metadata_value.tsx => metadata-value.tsx} (85%) delete mode 100644 example_plugins/plugins/ui_detail/src/metadata_tag_list.tsx rename example_plugins/plugins/ui_grid/src/{content_code_block.tsx => content-code-block.tsx} (90%) rename example_plugins/plugins/ui_grid/src/{content_headers.tsx => content-headers.tsx} (96%) rename example_plugins/plugins/ui_grid/src/{content_image.tsx => content-image.tsx} (96%) rename example_plugins/plugins/ui_grid/src/{content_paragraph.tsx => content-paragraph.tsx} (90%) rename example_plugins/plugins/ui_grid/src/{empty_view.tsx => empty-view.tsx} (87%) rename example_plugins/plugins/ui_grid/src/{search_bar.tsx => search-bar.tsx} (95%) rename example_plugins/plugins/ui_list/src/{content_code_block.tsx => content-code-block.tsx} (93%) rename example_plugins/plugins/ui_list/src/{content_headers.tsx => content-headers.tsx} (95%) rename example_plugins/plugins/ui_list/src/{content_horizontal_break.tsx => content-horizontal-break.tsx} (96%) rename example_plugins/plugins/ui_list/src/{content_image.tsx => content-image.tsx} (89%) rename example_plugins/plugins/ui_list/src/{content_paragraph.tsx => content-paragraph.tsx} (95%) rename example_plugins/plugins/ui_list/src/{empty_view.tsx => empty-view.tsx} (87%) rename example_plugins/plugins/ui_list/src/{metadata_icon.tsx => metadata-icon.tsx} (89%) rename example_plugins/plugins/ui_list/src/{metadata_link.tsx => metadata-link.tsx} (76%) rename example_plugins/plugins/ui_list/src/{metadata_separator.tsx => metadata-separator.tsx} (85%) rename example_plugins/plugins/ui_list/src/{metadata_tag_list.tsx => metadata-tag-list.tsx} (90%) rename example_plugins/plugins/ui_list/src/{metadata_value.tsx => metadata-value.tsx} (76%) rename example_plugins/plugins/ui_list/src/{search_bar.tsx => search-bar.tsx} (93%) diff --git a/example_plugins/plugins/ui_detail/gauntlet.toml b/example_plugins/plugins/ui_detail/gauntlet.toml index f950b31..b787a2e 100644 --- a/example_plugins/plugins/ui_detail/gauntlet.toml +++ b/example_plugins/plugins/ui_detail/gauntlet.toml @@ -15,7 +15,7 @@ description = '' [[entrypoint]] id = 'content-code-block' name = 'Content Code Block' -path = 'src/content_code_block.tsx' +path = 'src/content-code-block.tsx' type = 'view' description = '' # docs-code-segment:end @@ -24,7 +24,7 @@ description = '' [[entrypoint]] id = 'content-header' name = 'Content Header' -path = 'src/content_header.tsx' +path = 'src/content-header.tsx' type = 'view' description = '' # docs-code-segment:end @@ -33,7 +33,7 @@ description = '' [[entrypoint]] id = 'content-horizontal-break' name = 'Content Horizontal Break' -path = 'src/content_horizontal_break.tsx' +path = 'src/content-horizontal-break.tsx' type = 'view' description = '' # docs-code-segment:end @@ -42,7 +42,7 @@ description = '' [[entrypoint]] id = 'content-image' name = 'Content Image' -path = 'src/content_image.tsx' +path = 'src/content-image.tsx' type = 'view' description = '' # docs-code-segment:end @@ -51,7 +51,7 @@ description = '' [[entrypoint]] id = 'content-paragraph' name = 'Content Paragraph' -path = 'src/content_paragraph.tsx' +path = 'src/content-paragraph.tsx' type = 'view' description = '' # docs-code-segment:end @@ -78,7 +78,7 @@ description = '' [[entrypoint]] id = 'metadata-icon' name = 'Metadata Icon' -path = 'src/metadata_icon.tsx' +path = 'src/metadata-icon.tsx' type = 'view' description = '' # docs-code-segment:end @@ -87,7 +87,7 @@ description = '' [[entrypoint]] id = 'metadata-link' name = 'Metadata Link' -path = 'src/metadata_link.tsx' +path = 'src/metadata-link.tsx' type = 'view' description = '' # docs-code-segment:end @@ -96,7 +96,7 @@ description = '' [[entrypoint]] id = 'metadata-separator' name = 'Metadata Separator' -path = 'src/metadata_separator.tsx' +path = 'src/metadata-separator.tsx' type = 'view' description = '' # docs-code-segment:end @@ -105,7 +105,7 @@ description = '' [[entrypoint]] id = 'metadata-tag-list' name = 'Metadata Tag List' -path = 'src/metadata_tag_list.tsx' +path = 'src/metadata-tag-list.tsx' type = 'view' description = '' # docs-code-segment:end @@ -114,7 +114,7 @@ description = '' [[entrypoint]] id = 'metadata-value' name = 'Metadata Value' -path = 'src/metadata_value.tsx' +path = 'src/metadata-value.tsx' type = 'view' description = '' # docs-code-segment:end diff --git a/example_plugins/plugins/ui_detail/src/content_code_block.tsx b/example_plugins/plugins/ui_detail/src/content-code-block.tsx similarity index 88% rename from example_plugins/plugins/ui_detail/src/content_code_block.tsx rename to example_plugins/plugins/ui_detail/src/content-code-block.tsx index 5326068..5ade758 100644 --- a/example_plugins/plugins/ui_detail/src/content_code_block.tsx +++ b/example_plugins/plugins/ui_detail/src/content-code-block.tsx @@ -1,5 +1,5 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; const code = `\ fib :: Integer -> Integer @@ -8,7 +8,7 @@ fib 1 = 1 fib n = fib (n-1) + fib (n-2) ` -export default function Main(): ReactNode { +export default function ContentCodeBlock(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/content_header.tsx b/example_plugins/plugins/ui_detail/src/content-header.tsx similarity index 93% rename from example_plugins/plugins/ui_detail/src/content_header.tsx rename to example_plugins/plugins/ui_detail/src/content-header.tsx index 299defd..03bb711 100644 --- a/example_plugins/plugins/ui_detail/src/content_header.tsx +++ b/example_plugins/plugins/ui_detail/src/content-header.tsx @@ -1,7 +1,7 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; -export default function Main(): ReactNode { +export default function ContentHeader(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/content_horizontal_break.tsx b/example_plugins/plugins/ui_detail/src/content-horizontal-break.tsx similarity index 90% rename from example_plugins/plugins/ui_detail/src/content_horizontal_break.tsx rename to example_plugins/plugins/ui_detail/src/content-horizontal-break.tsx index b5f0a3d..bc86170 100644 --- a/example_plugins/plugins/ui_detail/src/content_horizontal_break.tsx +++ b/example_plugins/plugins/ui_detail/src/content-horizontal-break.tsx @@ -1,7 +1,7 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; -export default function Main(): ReactNode { +export default function ContentHorizontalBreak(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/content_image.tsx b/example_plugins/plugins/ui_detail/src/content-image.tsx similarity index 83% rename from example_plugins/plugins/ui_detail/src/content_image.tsx rename to example_plugins/plugins/ui_detail/src/content-image.tsx index 2109e1c..31b0c5c 100644 --- a/example_plugins/plugins/ui_detail/src/content_image.tsx +++ b/example_plugins/plugins/ui_detail/src/content-image.tsx @@ -1,9 +1,9 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; -const imgUrl = "https://static.wikia.nocookie.net/starwars/images/a/ae/The_Whills_Strike_Back.png/revision/latest/scale-to-width-down/400?cb=20201006180053" +const imgUrl = "https://static.wikia.nocookie.net/starwars/images/a/ae/The_Whills_Strike_Back.png/revision/latest/scale-to-width-down/250?cb=20201006180053" -export default function Main(): ReactNode { +export default function ContentImage(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/content_paragraph.tsx b/example_plugins/plugins/ui_detail/src/content-paragraph.tsx similarity index 92% rename from example_plugins/plugins/ui_detail/src/content_paragraph.tsx rename to example_plugins/plugins/ui_detail/src/content-paragraph.tsx index e7bfdf4..f219348 100644 --- a/example_plugins/plugins/ui_detail/src/content_paragraph.tsx +++ b/example_plugins/plugins/ui_detail/src/content-paragraph.tsx @@ -1,7 +1,7 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; -export default function Main(): ReactNode { +export default function ContentParagraph(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/content.tsx b/example_plugins/plugins/ui_detail/src/content.tsx index 3229f11..d89110f 100644 --- a/example_plugins/plugins/ui_detail/src/content.tsx +++ b/example_plugins/plugins/ui_detail/src/content.tsx @@ -1,7 +1,7 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; -export default function Main(): ReactNode { +export default function Content(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/main.tsx b/example_plugins/plugins/ui_detail/src/main.tsx index 8073b8f..e250d91 100644 --- a/example_plugins/plugins/ui_detail/src/main.tsx +++ b/example_plugins/plugins/ui_detail/src/main.tsx @@ -1,9 +1,15 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Action, ActionPanel, Detail } from "@project-gauntlet/api/components"; export default function Main(): ReactNode { return ( - + + {/* */}}/> + + } + > Sentient Humanoid diff --git a/example_plugins/plugins/ui_detail/src/metadata_icon.tsx b/example_plugins/plugins/ui_detail/src/metadata-icon.tsx similarity index 84% rename from example_plugins/plugins/ui_detail/src/metadata_icon.tsx rename to example_plugins/plugins/ui_detail/src/metadata-icon.tsx index 48b8b41..5011693 100644 --- a/example_plugins/plugins/ui_detail/src/metadata_icon.tsx +++ b/example_plugins/plugins/ui_detail/src/metadata-icon.tsx @@ -1,7 +1,7 @@ -import { Detail, Icons } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail, Icons } from "@project-gauntlet/api/components"; -export default function Main(): ReactNode { +export default function MetadataIcon(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/metadata_link.tsx b/example_plugins/plugins/ui_detail/src/metadata-link.tsx similarity index 86% rename from example_plugins/plugins/ui_detail/src/metadata_link.tsx rename to example_plugins/plugins/ui_detail/src/metadata-link.tsx index bbaef8c..7f7d8cf 100644 --- a/example_plugins/plugins/ui_detail/src/metadata_link.tsx +++ b/example_plugins/plugins/ui_detail/src/metadata-link.tsx @@ -1,7 +1,7 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; -export default function Main(): ReactNode { +export default function MetadataLink(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/metadata_separator.tsx b/example_plugins/plugins/ui_detail/src/metadata-separator.tsx similarity index 88% rename from example_plugins/plugins/ui_detail/src/metadata_separator.tsx rename to example_plugins/plugins/ui_detail/src/metadata-separator.tsx index 1b0921f..a4dca12 100644 --- a/example_plugins/plugins/ui_detail/src/metadata_separator.tsx +++ b/example_plugins/plugins/ui_detail/src/metadata-separator.tsx @@ -1,7 +1,7 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; -export default function Main(): ReactNode { +export default function MetadataSeparator(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/metadata-tag-list.tsx b/example_plugins/plugins/ui_detail/src/metadata-tag-list.tsx new file mode 100644 index 0000000..8292e75 --- /dev/null +++ b/example_plugins/plugins/ui_detail/src/metadata-tag-list.tsx @@ -0,0 +1,31 @@ +import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; +import { showHud } from "@project-gauntlet/api/helpers"; + +const items = [ + "The Screaming Citadel 1", + "Doctor Aphra (2016) 9", + "Doctor Aphra (2016) 10", + "Doctor Aphra (2016) 11", + "Doctor Aphra (2016) 12" +] + +export default function MetadataTagList(): ReactNode { + return ( + + + + { + items.map(value => { + return ( + showHud(value)}> + {value} + + ) + }) + } + + + + ) +} diff --git a/example_plugins/plugins/ui_detail/src/metadata_value.tsx b/example_plugins/plugins/ui_detail/src/metadata-value.tsx similarity index 85% rename from example_plugins/plugins/ui_detail/src/metadata_value.tsx rename to example_plugins/plugins/ui_detail/src/metadata-value.tsx index 0f4c7b4..e3d90d8 100644 --- a/example_plugins/plugins/ui_detail/src/metadata_value.tsx +++ b/example_plugins/plugins/ui_detail/src/metadata-value.tsx @@ -1,7 +1,7 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; -export default function Main(): ReactNode { +export default function MetadataValue(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/metadata.tsx b/example_plugins/plugins/ui_detail/src/metadata.tsx index 5ea91c9..9bba77d 100644 --- a/example_plugins/plugins/ui_detail/src/metadata.tsx +++ b/example_plugins/plugins/ui_detail/src/metadata.tsx @@ -1,7 +1,7 @@ -import { Detail } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; -export default function Main(): ReactNode { +export default function Metadata(): ReactNode { return ( diff --git a/example_plugins/plugins/ui_detail/src/metadata_tag_list.tsx b/example_plugins/plugins/ui_detail/src/metadata_tag_list.tsx deleted file mode 100644 index 858a784..0000000 --- a/example_plugins/plugins/ui_detail/src/metadata_tag_list.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Detail } from "@project-gauntlet/api/components"; -import { ReactNode } from "react"; - -export default function Main(): ReactNode { - return ( - - - - The Screaming Citadel 1 - Doctor Aphra (2016) 9 - Doctor Aphra (2016) 10 - Doctor Aphra (2016) 11 - Doctor Aphra (2016) 12 - - - - ) -} diff --git a/example_plugins/plugins/ui_form/src/checkbox.tsx b/example_plugins/plugins/ui_form/src/checkbox.tsx index e545fa4..5cd0c18 100644 --- a/example_plugins/plugins/ui_form/src/checkbox.tsx +++ b/example_plugins/plugins/ui_form/src/checkbox.tsx @@ -1,7 +1,7 @@ import { ReactElement } from 'react'; import { Form } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function CheckboxExample(): ReactElement { return (
diff --git a/example_plugins/plugins/ui_form/src/select.tsx b/example_plugins/plugins/ui_form/src/select.tsx index 891975a..ef9cb99 100644 --- a/example_plugins/plugins/ui_form/src/select.tsx +++ b/example_plugins/plugins/ui_form/src/select.tsx @@ -1,7 +1,7 @@ import { ReactElement } from 'react'; import { Form } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function SelectExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_form/src/separator.tsx b/example_plugins/plugins/ui_form/src/separator.tsx index 4e39930..d3622c5 100644 --- a/example_plugins/plugins/ui_form/src/separator.tsx +++ b/example_plugins/plugins/ui_form/src/separator.tsx @@ -1,7 +1,7 @@ import { ReactElement } from 'react'; import { Form } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function SeparatorExample(): ReactElement { return ( {items.map(value => ( diff --git a/example_plugins/plugins/ui_grid/src/content_headers.tsx b/example_plugins/plugins/ui_grid/src/content-headers.tsx similarity index 96% rename from example_plugins/plugins/ui_grid/src/content_headers.tsx rename to example_plugins/plugins/ui_grid/src/content-headers.tsx index db65312..0d2b070 100644 --- a/example_plugins/plugins/ui_grid/src/content_headers.tsx +++ b/example_plugins/plugins/ui_grid/src/content-headers.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; import { Grid } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function ContentHeadersExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_grid/src/content_image.tsx b/example_plugins/plugins/ui_grid/src/content-image.tsx similarity index 96% rename from example_plugins/plugins/ui_grid/src/content_image.tsx rename to example_plugins/plugins/ui_grid/src/content-image.tsx index 60e0ac0..fd71682 100644 --- a/example_plugins/plugins/ui_grid/src/content_image.tsx +++ b/example_plugins/plugins/ui_grid/src/content-image.tsx @@ -32,7 +32,7 @@ const items = [ }, ] -export default function Main(): ReactElement { +export default function ContentImageExample(): ReactElement { return ( {items.map(value => ( diff --git a/example_plugins/plugins/ui_grid/src/content_paragraph.tsx b/example_plugins/plugins/ui_grid/src/content-paragraph.tsx similarity index 90% rename from example_plugins/plugins/ui_grid/src/content_paragraph.tsx rename to example_plugins/plugins/ui_grid/src/content-paragraph.tsx index 44d76f4..9bc0019 100644 --- a/example_plugins/plugins/ui_grid/src/content_paragraph.tsx +++ b/example_plugins/plugins/ui_grid/src/content-paragraph.tsx @@ -10,7 +10,7 @@ const items = [ "C1-10P", ] -export default function Main(): ReactElement { +export default function ContentParagraphExample(): ReactElement { return ( {items.map(value => ( diff --git a/example_plugins/plugins/ui_grid/src/empty_view.tsx b/example_plugins/plugins/ui_grid/src/empty-view.tsx similarity index 87% rename from example_plugins/plugins/ui_grid/src/empty_view.tsx rename to example_plugins/plugins/ui_grid/src/empty-view.tsx index 418fed3..5aeb75f 100644 --- a/example_plugins/plugins/ui_grid/src/empty_view.tsx +++ b/example_plugins/plugins/ui_grid/src/empty-view.tsx @@ -3,7 +3,7 @@ import { Grid } from "@project-gauntlet/api/components"; const alderaanImage = "https://static.wikia.nocookie.net/starwars/images/4/4a/Alderaan.jpg/revision/latest?cb=20061211013805" -export default function Main(): ReactElement { +export default function EmptyViewExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_grid/src/main.tsx b/example_plugins/plugins/ui_grid/src/main.tsx index 60e0ac0..07ca131 100644 --- a/example_plugins/plugins/ui_grid/src/main.tsx +++ b/example_plugins/plugins/ui_grid/src/main.tsx @@ -32,7 +32,7 @@ const items = [ }, ] -export default function Main(): ReactElement { +export default function MainExample(): ReactElement { return ( {items.map(value => ( diff --git a/example_plugins/plugins/ui_grid/src/search_bar.tsx b/example_plugins/plugins/ui_grid/src/search-bar.tsx similarity index 95% rename from example_plugins/plugins/ui_grid/src/search_bar.tsx rename to example_plugins/plugins/ui_grid/src/search-bar.tsx index ce67753..3fffa9c 100644 --- a/example_plugins/plugins/ui_grid/src/search_bar.tsx +++ b/example_plugins/plugins/ui_grid/src/search-bar.tsx @@ -11,7 +11,7 @@ const results = [ "Mandalorian Culture" ] -export default function Main(): ReactElement { +export default function SearchBarExample(): ReactElement { const [searchText, setSearchText] = useState(""); return ( diff --git a/example_plugins/plugins/ui_grid/src/section.tsx b/example_plugins/plugins/ui_grid/src/section.tsx index 2d0ab75..2ec9c59 100644 --- a/example_plugins/plugins/ui_grid/src/section.tsx +++ b/example_plugins/plugins/ui_grid/src/section.tsx @@ -14,7 +14,7 @@ const vader5 = "https://static.wikia.nocookie.net/starwars/images/a/ab/Darthvade const vader6 = "https://static.wikia.nocookie.net/starwars/images/2/20/DarthVader-DLotS--Solicitation.jpg/revision/latest/scale-to-width-down/150?cb=20171001165404" const vader7 = "https://static.wikia.nocookie.net/starwars/images/f/fa/DarthVader2017-7.jpg/revision/latest/scale-to-width-down/150?cb=20190226233333" -export default function Main(): ReactElement { +export default function SectionExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/gauntlet.toml b/example_plugins/plugins/ui_list/gauntlet.toml index c0e7845..5c61845 100644 --- a/example_plugins/plugins/ui_list/gauntlet.toml +++ b/example_plugins/plugins/ui_list/gauntlet.toml @@ -16,7 +16,7 @@ description = '' [[entrypoint]] id = 'empty-view' name = 'Empty View' -path = 'src/empty_view.tsx' +path = 'src/empty-view.tsx' type = 'view' description = '' # docs-code-segment:end @@ -43,7 +43,7 @@ description = '' [[entrypoint]] id = 'search-bar' name = 'List Search bar' -path = 'src/search_bar.tsx' +path = 'src/search-bar.tsx' type = 'view' description = '' # docs-code-segment:end @@ -70,7 +70,7 @@ description = '' [[entrypoint]] id = 'content-code-block' name = 'List Content Code Block' -path = 'src/content_code_block.tsx' +path = 'src/content-code-block.tsx' type = 'view' description = '' # docs-code-segment:end @@ -79,7 +79,7 @@ description = '' [[entrypoint]] id = 'content-headers' name = 'List Content Headers' -path = 'src/content_headers.tsx' +path = 'src/content-headers.tsx' type = 'view' description = '' # docs-code-segment:end @@ -88,7 +88,7 @@ description = '' [[entrypoint]] id = 'content-horizontal-break' name = 'List Content Horizontal Break' -path = 'src/content_horizontal_break.tsx' +path = 'src/content-horizontal-break.tsx' type = 'view' description = '' # docs-code-segment:end @@ -97,7 +97,7 @@ description = '' [[entrypoint]] id = 'content-image' name = 'List Content Image' -path = 'src/content_image.tsx' +path = 'src/content-image.tsx' type = 'view' description = '' # docs-code-segment:end @@ -106,7 +106,7 @@ description = '' [[entrypoint]] id = 'content-paragraph' name = 'List Content Paragraph' -path = 'src/content_paragraph.tsx' +path = 'src/content-paragraph.tsx' type = 'view' description = '' # docs-code-segment:end @@ -124,7 +124,7 @@ description = '' [[entrypoint]] id = 'metadata-icon' name = 'List Metadata Icon' -path = 'src/metadata_icon.tsx' +path = 'src/metadata-icon.tsx' type = 'view' description = '' # docs-code-segment:end @@ -133,7 +133,7 @@ description = '' [[entrypoint]] id = 'metadata-icon' name = 'List Metadata Icon' -path = 'src/metadata_icon.tsx' +path = 'src/metadata-icon.tsx' type = 'view' description = '' # docs-code-segment:end @@ -142,7 +142,7 @@ description = '' [[entrypoint]] id = 'metadata-link' name = 'List Metadata Link' -path = 'src/metadata_link.tsx' +path = 'src/metadata-link.tsx' type = 'view' description = '' # docs-code-segment:end @@ -151,7 +151,7 @@ description = '' [[entrypoint]] id = 'metadata-separator' name = 'List Metadata Separator' -path = 'src/metadata_separator.tsx' +path = 'src/metadata-separator.tsx' type = 'view' description = '' # docs-code-segment:end @@ -160,7 +160,7 @@ description = '' [[entrypoint]] id = 'metadata-tag-list' name = 'List Metadata Tag List' -path = 'src/metadata_tag_list.tsx' +path = 'src/metadata-tag-list.tsx' type = 'view' description = '' # docs-code-segment:end @@ -169,7 +169,7 @@ description = '' [[entrypoint]] id = 'metadata-value' name = 'List Metadata Value' -path = 'src/metadata_value.tsx' +path = 'src/metadata-value.tsx' type = 'view' description = '' # docs-code-segment:end diff --git a/example_plugins/plugins/ui_list/src/content_code_block.tsx b/example_plugins/plugins/ui_list/src/content-code-block.tsx similarity index 93% rename from example_plugins/plugins/ui_list/src/content_code_block.tsx rename to example_plugins/plugins/ui_list/src/content-code-block.tsx index 11a9a02..6b609c5 100644 --- a/example_plugins/plugins/ui_list/src/content_code_block.tsx +++ b/example_plugins/plugins/ui_list/src/content-code-block.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; import { List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function ContentCodeBlockExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/content_headers.tsx b/example_plugins/plugins/ui_list/src/content-headers.tsx similarity index 95% rename from example_plugins/plugins/ui_list/src/content_headers.tsx rename to example_plugins/plugins/ui_list/src/content-headers.tsx index a811601..aac9a0b 100644 --- a/example_plugins/plugins/ui_list/src/content_headers.tsx +++ b/example_plugins/plugins/ui_list/src/content-headers.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; import { List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function ContentHeadersExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/content_horizontal_break.tsx b/example_plugins/plugins/ui_list/src/content-horizontal-break.tsx similarity index 96% rename from example_plugins/plugins/ui_list/src/content_horizontal_break.tsx rename to example_plugins/plugins/ui_list/src/content-horizontal-break.tsx index 7690bf2..e180569 100644 --- a/example_plugins/plugins/ui_list/src/content_horizontal_break.tsx +++ b/example_plugins/plugins/ui_list/src/content-horizontal-break.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; import { List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function ContentHorizontalBreakExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/content_image.tsx b/example_plugins/plugins/ui_list/src/content-image.tsx similarity index 89% rename from example_plugins/plugins/ui_list/src/content_image.tsx rename to example_plugins/plugins/ui_list/src/content-image.tsx index ed3ca2f..edc46c9 100644 --- a/example_plugins/plugins/ui_list/src/content_image.tsx +++ b/example_plugins/plugins/ui_list/src/content-image.tsx @@ -3,7 +3,7 @@ import { List } from "@project-gauntlet/api/components"; const imgUrl = "https://static.wikia.nocookie.net/starwars/images/e/e4/Ezaraa.png/revision/latest/scale-to-width-down/200?cb=20170511082800" -export default function Main(): ReactElement { +export default function ContentImageExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/content_paragraph.tsx b/example_plugins/plugins/ui_list/src/content-paragraph.tsx similarity index 95% rename from example_plugins/plugins/ui_list/src/content_paragraph.tsx rename to example_plugins/plugins/ui_list/src/content-paragraph.tsx index 29b1f8d..a297048 100644 --- a/example_plugins/plugins/ui_list/src/content_paragraph.tsx +++ b/example_plugins/plugins/ui_list/src/content-paragraph.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; import { List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function ContentParagraphExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/content.tsx b/example_plugins/plugins/ui_list/src/content.tsx index 72c7ecd..c1f2435 100644 --- a/example_plugins/plugins/ui_list/src/content.tsx +++ b/example_plugins/plugins/ui_list/src/content.tsx @@ -1,7 +1,7 @@ -import React, { ReactElement } from "react"; +import { ReactElement } from "react"; import { Action, ActionPanel, List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function ContentExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/empty_view.tsx b/example_plugins/plugins/ui_list/src/empty-view.tsx similarity index 87% rename from example_plugins/plugins/ui_list/src/empty_view.tsx rename to example_plugins/plugins/ui_list/src/empty-view.tsx index 0a41444..c487984 100644 --- a/example_plugins/plugins/ui_list/src/empty_view.tsx +++ b/example_plugins/plugins/ui_list/src/empty-view.tsx @@ -3,7 +3,7 @@ import { List } from "@project-gauntlet/api/components"; const alderaanImage = "https://static.wikia.nocookie.net/starwars/images/4/4a/Alderaan.jpg/revision/latest?cb=20061211013805" -export default function Main(): ReactElement { +export default function EmptyViewExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/item.tsx b/example_plugins/plugins/ui_list/src/item.tsx index 9b94484..8a19abd 100644 --- a/example_plugins/plugins/ui_list/src/item.tsx +++ b/example_plugins/plugins/ui_list/src/item.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; import { IconAccessory, Icons, List, TextAccessory } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function ItemExample(): ReactElement { return ( ]}/> diff --git a/example_plugins/plugins/ui_list/src/main.tsx b/example_plugins/plugins/ui_list/src/main.tsx index 4fc65b8..6c82394 100644 --- a/example_plugins/plugins/ui_list/src/main.tsx +++ b/example_plugins/plugins/ui_list/src/main.tsx @@ -1,7 +1,7 @@ -import React, { ReactElement } from "react"; +import { ReactElement } from "react"; import { Action, ActionPanel, IconAccessory, Icons, List, TextAccessory } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function MainExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/metadata_link.tsx b/example_plugins/plugins/ui_list/src/metadata-link.tsx similarity index 76% rename from example_plugins/plugins/ui_list/src/metadata_link.tsx rename to example_plugins/plugins/ui_list/src/metadata-link.tsx index bc99323..c9f0ff6 100644 --- a/example_plugins/plugins/ui_list/src/metadata_link.tsx +++ b/example_plugins/plugins/ui_list/src/metadata-link.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; -import { Icons, List } from "@project-gauntlet/api/components"; +import { List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function MetadataLinkExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/metadata_separator.tsx b/example_plugins/plugins/ui_list/src/metadata-separator.tsx similarity index 85% rename from example_plugins/plugins/ui_list/src/metadata_separator.tsx rename to example_plugins/plugins/ui_list/src/metadata-separator.tsx index 0cf8e38..62072b7 100644 --- a/example_plugins/plugins/ui_list/src/metadata_separator.tsx +++ b/example_plugins/plugins/ui_list/src/metadata-separator.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; -import { Icons, List } from "@project-gauntlet/api/components"; +import { List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function MetadataSeparatorExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/metadata_tag_list.tsx b/example_plugins/plugins/ui_list/src/metadata-tag-list.tsx similarity index 90% rename from example_plugins/plugins/ui_list/src/metadata_tag_list.tsx rename to example_plugins/plugins/ui_list/src/metadata-tag-list.tsx index 89316aa..c9a83d7 100644 --- a/example_plugins/plugins/ui_list/src/metadata_tag_list.tsx +++ b/example_plugins/plugins/ui_list/src/metadata-tag-list.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; -import { Icons, List } from "@project-gauntlet/api/components"; +import { List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function MetadataTagListExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/metadata_value.tsx b/example_plugins/plugins/ui_list/src/metadata-value.tsx similarity index 76% rename from example_plugins/plugins/ui_list/src/metadata_value.tsx rename to example_plugins/plugins/ui_list/src/metadata-value.tsx index a922c7a..c44eca0 100644 --- a/example_plugins/plugins/ui_list/src/metadata_value.tsx +++ b/example_plugins/plugins/ui_list/src/metadata-value.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; -import { Icons, List } from "@project-gauntlet/api/components"; +import { List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function MetadataValueExample(): ReactElement { return ( diff --git a/example_plugins/plugins/ui_list/src/metadata.tsx b/example_plugins/plugins/ui_list/src/metadata.tsx index 7d06e3a..dc4a5ea 100644 --- a/example_plugins/plugins/ui_list/src/metadata.tsx +++ b/example_plugins/plugins/ui_list/src/metadata.tsx @@ -1,7 +1,7 @@ -import React, { ReactElement } from "react"; +import { ReactElement } from "react"; import { Action, ActionPanel, List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function MetadataExample(): ReactElement { return ( (""); return ( diff --git a/example_plugins/plugins/ui_list/src/section.tsx b/example_plugins/plugins/ui_list/src/section.tsx index 9f23846..ded6510 100644 --- a/example_plugins/plugins/ui_list/src/section.tsx +++ b/example_plugins/plugins/ui_list/src/section.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; import { List } from "@project-gauntlet/api/components"; -export default function Main(): ReactElement { +export default function SectionExample(): ReactElement { return ( From 2e2864e88a46fbdd52d0b2655f131f2fc38da72a Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:38:27 +0100 Subject: [PATCH 028/170] Replace ` with " in docs markdown files --- docs/js/components/checkbox/description.md | 2 +- docs/js/components/date_picker/description.md | 2 +- docs/js/components/detail/props/isLoading.md | 2 +- docs/js/components/form/props/isLoading.md | 2 +- docs/js/components/grid/props/isLoading.md | 2 +- docs/js/components/image/props/source.md | 2 +- docs/js/components/inline/description.md | 2 +- docs/js/components/list/props/isLoading.md | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/js/components/checkbox/description.md b/docs/js/components/checkbox/description.md index 5a364d6..5dd522a 100644 --- a/docs/js/components/checkbox/description.md +++ b/docs/js/components/checkbox/description.md @@ -1 +1 @@ -Checkbox is a type of form input that produces boolean value, either `true` or `false` \ No newline at end of file +Checkbox is a type of form input that produces boolean value, either "true" or "false" \ No newline at end of file diff --git a/docs/js/components/date_picker/description.md b/docs/js/components/date_picker/description.md index b9745ec..de8191c 100644 --- a/docs/js/components/date_picker/description.md +++ b/docs/js/components/date_picker/description.md @@ -1 +1 @@ -Date Picker is a type of form input that produces date value represented as a string in `YYYY-MM-DD` format \ No newline at end of file +Date Picker is a type of form input that produces date value represented as a string in "YYYY-MM-DD" format \ No newline at end of file diff --git a/docs/js/components/detail/props/isLoading.md b/docs/js/components/detail/props/isLoading.md index d1ebe77..fa5cc2d 100644 --- a/docs/js/components/detail/props/isLoading.md +++ b/docs/js/components/detail/props/isLoading.md @@ -1 +1 @@ -If `true` loading bar is shown above content \ No newline at end of file +If "true" loading bar is shown above content \ No newline at end of file diff --git a/docs/js/components/form/props/isLoading.md b/docs/js/components/form/props/isLoading.md index d1ebe77..fa5cc2d 100644 --- a/docs/js/components/form/props/isLoading.md +++ b/docs/js/components/form/props/isLoading.md @@ -1 +1 @@ -If `true` loading bar is shown above content \ No newline at end of file +If "true" loading bar is shown above content \ No newline at end of file diff --git a/docs/js/components/grid/props/isLoading.md b/docs/js/components/grid/props/isLoading.md index d1ebe77..fa5cc2d 100644 --- a/docs/js/components/grid/props/isLoading.md +++ b/docs/js/components/grid/props/isLoading.md @@ -1 +1 @@ -If `true` loading bar is shown above content \ No newline at end of file +If "true" loading bar is shown above content \ No newline at end of file diff --git a/docs/js/components/image/props/source.md b/docs/js/components/image/props/source.md index f5c0a66..a126898 100644 --- a/docs/js/components/image/props/source.md +++ b/docs/js/components/image/props/source.md @@ -1 +1 @@ -Image data. Supported formats: `png`, `gif`, `jpg`, `webp`, `tiff` \ No newline at end of file +Image data. Supported formats: "png", "gif', "jpg", "webp", "tiff" \ No newline at end of file diff --git a/docs/js/components/inline/description.md b/docs/js/components/inline/description.md index 7031f28..3e2c3ca 100644 --- a/docs/js/components/inline/description.md +++ b/docs/js/components/inline/description.md @@ -1,2 +1,2 @@ -Inline is a root component used with `inline-view` entrypoint type. +Inline is a root component used with "inline-view" entrypoint type. Displayed right under search bar in main view \ No newline at end of file diff --git a/docs/js/components/list/props/isLoading.md b/docs/js/components/list/props/isLoading.md index d1ebe77..fa5cc2d 100644 --- a/docs/js/components/list/props/isLoading.md +++ b/docs/js/components/list/props/isLoading.md @@ -1 +1 @@ -If `true` loading bar is shown above content \ No newline at end of file +If "true" loading bar is shown above content \ No newline at end of file From 8fc21934dee3eaf3ddbb93ad1859fe85d8e4f514 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:23:00 +0100 Subject: [PATCH 029/170] Improve ui form plugin examples --- .../plugins/ui_form/src/checkbox.tsx | 3 +- example_plugins/plugins/ui_form/src/main.tsx | 72 +++++++++---------- .../plugins/ui_form/src/password-field.tsx | 2 +- .../plugins/ui_form/src/select.tsx | 14 ++-- .../plugins/ui_form/src/separator.tsx | 16 ++--- .../plugins/ui_form/src/text-field.tsx | 4 +- 6 files changed, 48 insertions(+), 63 deletions(-) diff --git a/example_plugins/plugins/ui_form/src/checkbox.tsx b/example_plugins/plugins/ui_form/src/checkbox.tsx index 5cd0c18..3f5c365 100644 --- a/example_plugins/plugins/ui_form/src/checkbox.tsx +++ b/example_plugins/plugins/ui_form/src/checkbox.tsx @@ -5,7 +5,8 @@ export default function CheckboxExample(): ReactElement { return ( { console.log(`value: ${value}`) diff --git a/example_plugins/plugins/ui_form/src/main.tsx b/example_plugins/plugins/ui_form/src/main.tsx index 3335f37..6afb36f 100644 --- a/example_plugins/plugins/ui_form/src/main.tsx +++ b/example_plugins/plugins/ui_form/src/main.tsx @@ -1,50 +1,35 @@ import { ReactElement } from 'react'; -import { Form } from "@project-gauntlet/api/components"; - -// TODO remake into starwars themed +import { Action, ActionPanel, Form } from "@project-gauntlet/api/components"; export default function MainExample(): ReactElement { return ( - + + {}}/> + + } + > + { + console.log(`First Name: ${value}`) + }}/> + { + console.log(`Last Name: ${value}`) + }}/> - Burger + label="Species" + value="human" + onChange={value => { + console.log(`Last Name: ${value}`) + }}> + Human + Jawa + Hutt + Twi'lek - { - console.log(`value: ${value}`) - }} - /> - { - console.log(`value: ${value}`) - }} - /> - { - console.log(`value: ${value}`) - }} - /> - { - console.log(`value: ${value}`) - }} - /> - { - console.log(`value: ${value}`) - }} - /> + + + { + console.log(`terms of service: ${value}`) + }} + /> ); }; diff --git a/example_plugins/plugins/ui_form/src/password-field.tsx b/example_plugins/plugins/ui_form/src/password-field.tsx index 5ec40d5..b4042fe 100644 --- a/example_plugins/plugins/ui_form/src/password-field.tsx +++ b/example_plugins/plugins/ui_form/src/password-field.tsx @@ -4,7 +4,7 @@ import { Form } from "@project-gauntlet/api/components"; export default function PasswordFieldExample(): ReactElement { return (
- + ); }; diff --git a/example_plugins/plugins/ui_form/src/select.tsx b/example_plugins/plugins/ui_form/src/select.tsx index ef9cb99..6753d63 100644 --- a/example_plugins/plugins/ui_form/src/select.tsx +++ b/example_plugins/plugins/ui_form/src/select.tsx @@ -4,13 +4,13 @@ import { Form } from "@project-gauntlet/api/components"; export default function SelectExample(): ReactElement { return (
- - Burger - Hot Dog - Croissant - Cookies - Steak - Seafood + + Human + Jawa + Hutt + Twi'lek + Wookie + Kaminoan ); diff --git a/example_plugins/plugins/ui_form/src/separator.tsx b/example_plugins/plugins/ui_form/src/separator.tsx index d3622c5..2090282 100644 --- a/example_plugins/plugins/ui_form/src/separator.tsx +++ b/example_plugins/plugins/ui_form/src/separator.tsx @@ -4,19 +4,11 @@ import { Form } from "@project-gauntlet/api/components"; export default function SeparatorExample(): ReactElement { return (
- { - console.log(`value: ${value}`) - }} - /> + + - { - console.log(`value: ${value}`) - }} - /> + + ); }; diff --git a/example_plugins/plugins/ui_form/src/text-field.tsx b/example_plugins/plugins/ui_form/src/text-field.tsx index e35eb21..d50930c 100644 --- a/example_plugins/plugins/ui_form/src/text-field.tsx +++ b/example_plugins/plugins/ui_form/src/text-field.tsx @@ -5,9 +5,9 @@ export default function TextFieldExample(): ReactElement { return (
{ - console.log(`value: ${value}`) + console.log(`homeworld: ${value}`) }} /> From 6e04702903cf3be66cf21fd641665a812e4afaa4 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 23 Jan 2025 19:56:08 +0100 Subject: [PATCH 030/170] Add 3 more grid examples. Tweak others --- docs/js/components/content/description.md | 2 +- docs/js/components/search_bar/description.md | 2 +- example_plugins/plugins/ui_grid/gauntlet.toml | 28 +++++++++++ .../ui_grid/src/content-horizontal-break.tsx | 20 ++++++++ .../plugins/ui_grid/src/content-image.tsx | 37 +------------- .../plugins/ui_grid/src/more-columns.tsx | 29 +++++++++++ .../src/search-bar-set-search-text.tsx | 28 +++++++++++ .../plugins/ui_grid/src/section.tsx | 48 ------------------- .../content-horizontal-break/default.json | 3 ++ .../ui_grid/more-columns/default.json | 3 ++ 10 files changed, 115 insertions(+), 85 deletions(-) create mode 100644 example_plugins/plugins/ui_grid/src/content-horizontal-break.tsx create mode 100644 example_plugins/plugins/ui_grid/src/more-columns.tsx create mode 100644 example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx create mode 100644 example_plugins/scenarios/ui_grid/content-horizontal-break/default.json create mode 100644 example_plugins/scenarios/ui_grid/more-columns/default.json diff --git a/docs/js/components/content/description.md b/docs/js/components/content/description.md index 77668fe..ddce9f9 100644 --- a/docs/js/components/content/description.md +++ b/docs/js/components/content/description.md @@ -1,3 +1,3 @@ -A container for a set of non-interactable components. +Content is a container for a set of non-interactable components. Used in a variety of places like , and . By utilizing the power of React the content can also be made dynamic \ No newline at end of file diff --git a/docs/js/components/search_bar/description.md b/docs/js/components/search_bar/description.md index 1ecfae1..dfce5b5 100644 --- a/docs/js/components/search_bar/description.md +++ b/docs/js/components/search_bar/description.md @@ -1 +1 @@ -Adds search bar above the content \ No newline at end of file +Adds search bar above the content. Text in search bar can be read and set \ No newline at end of file diff --git a/example_plugins/plugins/ui_grid/gauntlet.toml b/example_plugins/plugins/ui_grid/gauntlet.toml index a4d408f..56c6b83 100644 --- a/example_plugins/plugins/ui_grid/gauntlet.toml +++ b/example_plugins/plugins/ui_grid/gauntlet.toml @@ -21,6 +21,24 @@ type = 'view' description = '' # docs-code-segment:end +# docs-code-segment:start more-columns +[[entrypoint]] +id = 'more-columns' +name = 'Grid More Columns' +path = 'src/more-columns.tsx' +type = 'view' +description = '' +# docs-code-segment:end + +# docs-code-segment:start search-bar-set-search-text +[[entrypoint]] +id = 'search-bar-set-search-text' +name = 'Grid Search Bar Set Search Text' +path = 'src/search-bar-set-search-text.tsx' +type = 'view' +description = '' +# docs-code-segment:end + # docs-code-segment:start section [[entrypoint]] id = 'section' @@ -75,5 +93,15 @@ type = 'view' description = '' # docs-code-segment:end + +# docs-code-segment:start content-horizontal-break +[[entrypoint]] +id = 'content-horizontal-break' +name = 'Grid Content Horizontal Break' +path = 'src/content-horizontal-break.tsx' +type = 'view' +description = '' +# docs-code-segment:end + [permissions] network = ["static.wikia.nocookie.net"] diff --git a/example_plugins/plugins/ui_grid/src/content-horizontal-break.tsx b/example_plugins/plugins/ui_grid/src/content-horizontal-break.tsx new file mode 100644 index 0000000..7a40967 --- /dev/null +++ b/example_plugins/plugins/ui_grid/src/content-horizontal-break.tsx @@ -0,0 +1,20 @@ +import { ReactElement } from "react"; +import { Grid } from "@project-gauntlet/api/components"; + +export default function ContentHorizontalBreak(): ReactElement { + return ( + + + + + C-3PO + + + + BB-8 + + + + + ) +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_grid/src/content-image.tsx b/example_plugins/plugins/ui_grid/src/content-image.tsx index fd71682..b07b68a 100644 --- a/example_plugins/plugins/ui_grid/src/content-image.tsx +++ b/example_plugins/plugins/ui_grid/src/content-image.tsx @@ -1,47 +1,14 @@ import { ReactElement } from "react"; import { Grid } from "@project-gauntlet/api/components"; -const items = [ - { - title: "Naboo", - image: "https://static.wikia.nocookie.net/star-wars-canon/images/2/24/NabooFull-SW.png/revision/latest/scale-to-width-down/150?cb=20151218205422" - }, - { - title: "Ryloth", - image: "https://static.wikia.nocookie.net/star-wars-canon/images/b/b7/Ryloth_Rebels.png/revision/latest/scale-to-width-down/150?cb=20161103040944" - }, - { - title: "Tatooine", - image: "https://static.wikia.nocookie.net/star-wars-canon/images/b/b0/Tatooine_TPM.png/revision/latest/scale-to-width-down/150?cb=20151124205032" - }, - { - title: "Dagobah", - image: "https://static.wikia.nocookie.net/star-wars-canon/images/4/48/Dagobah_ep3.jpg/revision/latest/scale-to-width-down/150?cb=20161103221846" - }, - { - title: "Endor", - image: "https://static.wikia.nocookie.net/star-wars-canon/images/9/96/Endor-DB.png/revision/latest/scale-to-width-down/150?cb=20160711234205" - }, - { - title: "Dathomir", - image: "https://static.wikia.nocookie.net/starwars/images/3/34/DathomirJFO.jpg/revision/latest/scale-to-width-down/150?cb=20200222032237" - }, - { - title: "Dantooine", - image: "https://static.wikia.nocookie.net/starwars/images/a/a5/Dantooine_Resistance.jpg/revision/latest/scale-to-width-down/150?cb=20200120190043" - }, -] - export default function ContentImageExample(): ReactElement { return ( - {items.map(value => ( - + - + - ))} ) } \ No newline at end of file diff --git a/example_plugins/plugins/ui_grid/src/more-columns.tsx b/example_plugins/plugins/ui_grid/src/more-columns.tsx new file mode 100644 index 0000000..c3f8958 --- /dev/null +++ b/example_plugins/plugins/ui_grid/src/more-columns.tsx @@ -0,0 +1,29 @@ +import { ReactElement } from "react"; +import { Grid } from "@project-gauntlet/api/components"; + +const items = [ + "🥹", + "🤣", + "🥵", + "🤕", + "🫥", + "🤬", + "🥱", + "🤮", + "🙄", + "🤠" +] + +export default function MoreColumnsExample(): ReactElement { + return ( + + {items.map(value => ( + + + {value} + + + ))} + + ) +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx b/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx new file mode 100644 index 0000000..ac42644 --- /dev/null +++ b/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx @@ -0,0 +1,28 @@ +import { ReactElement, useState } from "react"; +import { Action, ActionPanel, Grid } from "@project-gauntlet/api/components"; + +export default function SearchBarSetSearchTextExample(): ReactElement { + const [searchText, setSearchText] = useState(""); + + return ( + + setSearchText(id)}/> + + } + > + + + + + Click me! + + + + + ) +} diff --git a/example_plugins/plugins/ui_grid/src/section.tsx b/example_plugins/plugins/ui_grid/src/section.tsx index 2ec9c59..9687bdc 100644 --- a/example_plugins/plugins/ui_grid/src/section.tsx +++ b/example_plugins/plugins/ui_grid/src/section.tsx @@ -3,16 +3,8 @@ import { Grid } from "@project-gauntlet/api/components"; const theBlade1 = "https://static.wikia.nocookie.net/starwars/images/a/a4/The-Blade-1-final-cover.jpg/revision/latest/scale-to-width-down/150?cb=20221215195606" const theBlade2 = "https://static.wikia.nocookie.net/starwars/images/f/fd/The-Blade-2-Final-Cover.jpg/revision/latest/scale-to-width-down/150?cb=20230120033002" -const theBlade3 = "https://static.wikia.nocookie.net/starwars/images/0/02/The-Blade-3-Final-Cover.jpg/revision/latest/scale-to-width-down/150?cb=20230227203337" -const theBlade4 = "https://static.wikia.nocookie.net/starwars/images/6/6c/The-Blade-4-Final-Cover.jpg/revision/latest/scale-to-width-down/150?cb=20230321223753" const vader1 = "https://static.wikia.nocookie.net/starwars/images/9/9a/Darth_VaderDark_Lord_of_the_Sith.jpg/revision/latest/scale-to-width-down/150?cb=20190223230434" -const vader2 = "https://static.wikia.nocookie.net/starwars/images/2/2e/Darth_Vader_2_cover_art.jpg/revision/latest/scale-to-width-down/150?cb=20190223234228" -const vader3 = "https://static.wikia.nocookie.net/starwars/images/d/df/DarthVader2017-3.jpg/revision/latest/scale-to-width-down/150?cb=20190224013414" -const vader4 = "https://static.wikia.nocookie.net/starwars/images/c/c9/Darthvader-dlots-4-final.jpg/revision/latest/scale-to-width-down/150?cb=20190226024707" -const vader5 = "https://static.wikia.nocookie.net/starwars/images/a/ab/Darthvader-dlots-5.jpg/revision/latest/scale-to-width-down/150?cb=20170826121053" -const vader6 = "https://static.wikia.nocookie.net/starwars/images/2/20/DarthVader-DLotS--Solicitation.jpg/revision/latest/scale-to-width-down/150?cb=20171001165404" -const vader7 = "https://static.wikia.nocookie.net/starwars/images/f/fa/DarthVader2017-7.jpg/revision/latest/scale-to-width-down/150?cb=20190226233333" export default function SectionExample(): ReactElement { return ( @@ -28,16 +20,6 @@ export default function SectionExample(): ReactElement {
- - - - - - - - - - @@ -45,36 +27,6 @@ export default function SectionExample(): ReactElement {
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
) diff --git a/example_plugins/scenarios/ui_grid/content-horizontal-break/default.json b/example_plugins/scenarios/ui_grid/content-horizontal-break/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_grid/content-horizontal-break/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_grid/more-columns/default.json b/example_plugins/scenarios/ui_grid/more-columns/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_grid/more-columns/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file From 71284cbca8b285819ada8c1175ea31908aa6501a Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 23 Jan 2025 20:13:51 +0100 Subject: [PATCH 031/170] Add search-bar-set-search-text list example --- .../src/search-bar-set-search-text.tsx | 5 +---- example_plugins/plugins/ui_list/gauntlet.toml | 9 +++++++++ .../src/search-bar-set-search-text.tsx | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 example_plugins/plugins/ui_list/src/search-bar-set-search-text.tsx diff --git a/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx b/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx index ac42644..94853c1 100644 --- a/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx +++ b/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx @@ -12,10 +12,7 @@ export default function SearchBarSetSearchTextExample(): ReactElement { } > - + diff --git a/example_plugins/plugins/ui_list/gauntlet.toml b/example_plugins/plugins/ui_list/gauntlet.toml index 5c61845..9ca4bf4 100644 --- a/example_plugins/plugins/ui_list/gauntlet.toml +++ b/example_plugins/plugins/ui_list/gauntlet.toml @@ -48,6 +48,15 @@ type = 'view' description = '' # docs-code-segment:end +# docs-code-segment:start search-bar-set-search-text +[[entrypoint]] +id = 'search-bar-set-search-text' +name = 'List Search Bar Set Search Text' +path = 'src/search-bar-set-search-text.tsx' +type = 'view' +description = '' +# docs-code-segment:end + # docs-code-segment:start item [[entrypoint]] id = 'item' diff --git a/example_plugins/plugins/ui_list/src/search-bar-set-search-text.tsx b/example_plugins/plugins/ui_list/src/search-bar-set-search-text.tsx new file mode 100644 index 0000000..6670bc7 --- /dev/null +++ b/example_plugins/plugins/ui_list/src/search-bar-set-search-text.tsx @@ -0,0 +1,19 @@ +import { ReactElement, useState } from "react"; +import { Action, ActionPanel, List } from "@project-gauntlet/api/components"; + +export default function SearchBarSetSearchTextExample(): ReactElement { + const [searchText, setSearchText] = useState(""); + + return ( + + setSearchText(id)}/> + + } + > + + + + ) +} From 90957f214abb2890e814cf4dddd24ae25f978208 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 26 Jan 2025 13:06:04 +0100 Subject: [PATCH 032/170] Use pull_request instead of pull_request_target --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e9364a7..d5b20cf 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -2,7 +2,7 @@ name: build on: push: - pull_request_target: + pull_request: branches: - main From d721bda736a88874643bd2951b69f44571ad95bb Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 28 Jan 2025 21:44:05 +0100 Subject: [PATCH 033/170] Inline view examples --- dev_plugin/src/hooks-view.tsx | 7 ++--- .../plugins/ui_detail/package.json | 2 +- .../plugins/ui_grid/src/content-image.tsx | 11 ++++--- .../.gitignore | 0 .../gauntlet.toml | 0 .../package.json | 2 +- .../plugins/ui_inline_code_block/src/main.tsx | 19 ++++++++++++ .../tsconfig.json | 0 .../plugins/ui_inline_header/.gitignore | 2 ++ .../plugins/ui_inline_header/gauntlet.toml | 16 ++++++++++ .../plugins/ui_inline_header/package.json | 17 ++++++++++ .../plugins/ui_inline_header/src/main.tsx | 19 ++++++++++++ .../plugins/ui_inline_header/tsconfig.json | 11 +++++++ .../ui_inline_horizontal_break/.gitignore | 2 ++ .../ui_inline_horizontal_break/gauntlet.toml | 16 ++++++++++ .../ui_inline_horizontal_break/package.json | 17 ++++++++++ .../ui_inline_horizontal_break/src/main.tsx | 23 ++++++++++++++ .../ui_inline_horizontal_break/tsconfig.json | 11 +++++++ .../plugins/ui_inline_image/.gitignore | 2 ++ .../plugins/ui_inline_image/gauntlet.toml | 16 ++++++++++ .../plugins/ui_inline_image/package.json | 17 ++++++++++ .../plugins/ui_inline_image/src/main.tsx | 17 ++++++++++ .../plugins/ui_inline_image/tsconfig.json | 11 +++++++ .../plugins/ui_inline_main/.gitignore | 2 ++ .../plugins/ui_inline_main/gauntlet.toml | 16 ++++++++++ .../plugins/ui_inline_main/package.json | 17 ++++++++++ .../plugins/ui_inline_main/src/main.tsx | 31 +++++++++++++++++++ .../plugins/ui_inline_main/tsconfig.json | 11 +++++++ .../plugins/ui_inline_paragraph/.gitignore | 2 ++ .../plugins/ui_inline_paragraph/gauntlet.toml | 16 ++++++++++ .../plugins/ui_inline_paragraph/package.json | 17 ++++++++++ .../plugins/ui_inline_paragraph/src/main.tsx | 19 ++++++++++++ .../plugins/ui_inline_paragraph/tsconfig.json | 11 +++++++ .../plugins/ui_inline_separator/.gitignore | 2 ++ .../plugins/ui_inline_separator/gauntlet.toml | 16 ++++++++++ .../plugins/ui_inline_separator/package.json | 17 ++++++++++ .../src/main.tsx | 7 ++++- .../plugins/ui_inline_separator/tsconfig.json | 11 +++++++ .../ui_inline_three_sections/src/main.tsx | 9 ++++-- .../main/default.json} | 2 +- .../ui_inline_header/main/default.json | 4 +++ .../main/default.json | 4 +++ .../ui_inline_image/main/default.json | 4 +++ .../ui_inline_main/main/default.json | 4 +++ .../ui_inline_paragraph/main/default.json | 4 +++ .../ui_inline_separator/main/default.json | 4 +++ .../main/default.json | 4 +++ .../main/three-sections.json | 4 --- .../ui_inline_two_sections/main/default.json | 4 +++ .../main/two-sections.json | 4 --- 50 files changed, 459 insertions(+), 25 deletions(-) rename example_plugins/plugins/{ui_inline_separators => ui_inline_code_block}/.gitignore (100%) rename example_plugins/plugins/{ui_inline_separators => ui_inline_code_block}/gauntlet.toml (100%) rename example_plugins/plugins/{ui_inline_separators => ui_inline_code_block}/package.json (85%) create mode 100644 example_plugins/plugins/ui_inline_code_block/src/main.tsx rename example_plugins/plugins/{ui_inline_separators => ui_inline_code_block}/tsconfig.json (100%) create mode 100644 example_plugins/plugins/ui_inline_header/.gitignore create mode 100644 example_plugins/plugins/ui_inline_header/gauntlet.toml create mode 100644 example_plugins/plugins/ui_inline_header/package.json create mode 100644 example_plugins/plugins/ui_inline_header/src/main.tsx create mode 100644 example_plugins/plugins/ui_inline_header/tsconfig.json create mode 100644 example_plugins/plugins/ui_inline_horizontal_break/.gitignore create mode 100644 example_plugins/plugins/ui_inline_horizontal_break/gauntlet.toml create mode 100644 example_plugins/plugins/ui_inline_horizontal_break/package.json create mode 100644 example_plugins/plugins/ui_inline_horizontal_break/src/main.tsx create mode 100644 example_plugins/plugins/ui_inline_horizontal_break/tsconfig.json create mode 100644 example_plugins/plugins/ui_inline_image/.gitignore create mode 100644 example_plugins/plugins/ui_inline_image/gauntlet.toml create mode 100644 example_plugins/plugins/ui_inline_image/package.json create mode 100644 example_plugins/plugins/ui_inline_image/src/main.tsx create mode 100644 example_plugins/plugins/ui_inline_image/tsconfig.json create mode 100644 example_plugins/plugins/ui_inline_main/.gitignore create mode 100644 example_plugins/plugins/ui_inline_main/gauntlet.toml create mode 100644 example_plugins/plugins/ui_inline_main/package.json create mode 100644 example_plugins/plugins/ui_inline_main/src/main.tsx create mode 100644 example_plugins/plugins/ui_inline_main/tsconfig.json create mode 100644 example_plugins/plugins/ui_inline_paragraph/.gitignore create mode 100644 example_plugins/plugins/ui_inline_paragraph/gauntlet.toml create mode 100644 example_plugins/plugins/ui_inline_paragraph/package.json create mode 100644 example_plugins/plugins/ui_inline_paragraph/src/main.tsx create mode 100644 example_plugins/plugins/ui_inline_paragraph/tsconfig.json create mode 100644 example_plugins/plugins/ui_inline_separator/.gitignore create mode 100644 example_plugins/plugins/ui_inline_separator/gauntlet.toml create mode 100644 example_plugins/plugins/ui_inline_separator/package.json rename example_plugins/plugins/{ui_inline_separators => ui_inline_separator}/src/main.tsx (89%) create mode 100644 example_plugins/plugins/ui_inline_separator/tsconfig.json rename example_plugins/scenarios/{ui_inline_separators/main/separator.json => ui_inline_code_block/main/default.json} (51%) create mode 100644 example_plugins/scenarios/ui_inline_header/main/default.json create mode 100644 example_plugins/scenarios/ui_inline_horizontal_break/main/default.json create mode 100644 example_plugins/scenarios/ui_inline_image/main/default.json create mode 100644 example_plugins/scenarios/ui_inline_main/main/default.json create mode 100644 example_plugins/scenarios/ui_inline_paragraph/main/default.json create mode 100644 example_plugins/scenarios/ui_inline_separator/main/default.json create mode 100644 example_plugins/scenarios/ui_inline_three_sections/main/default.json delete mode 100644 example_plugins/scenarios/ui_inline_three_sections/main/three-sections.json create mode 100644 example_plugins/scenarios/ui_inline_two_sections/main/default.json delete mode 100644 example_plugins/scenarios/ui_inline_two_sections/main/two-sections.json diff --git a/dev_plugin/src/hooks-view.tsx b/dev_plugin/src/hooks-view.tsx index 9596f9e..3112ac1 100644 --- a/dev_plugin/src/hooks-view.tsx +++ b/dev_plugin/src/hooks-view.tsx @@ -1,20 +1,17 @@ import { Action, ActionPanel, Element, Icons, List } from "@project-gauntlet/api/components"; -import React, { ReactElement, ReactNode, useRef, useState } from "react"; +import React, { ReactElement, ReactNode, useRef } from "react"; import { useCachedPromise, useFetch, useNavigation, usePromise } from "@project-gauntlet/api/hooks"; export default function ListView(): ReactElement { const { pushView } = useNavigation(); - const [id, setId] = useState(undefined); - return ( - pushPrimaryAction(id, pushView)}/> + pushPrimaryAction(id, pushView)}/> } - onItemFocusChange={itemId => setId(itemId)} > diff --git a/example_plugins/plugins/ui_detail/package.json b/example_plugins/plugins/ui_detail/package.json index 694c863..2953d0c 100644 --- a/example_plugins/plugins/ui_detail/package.json +++ b/example_plugins/plugins/ui_detail/package.json @@ -1,5 +1,5 @@ { - "name": "@project-gauntlet/docs-detailt", + "name": "@project-gauntlet/docs-detail", "private": true, "scripts": { "build": "gauntlet build", diff --git a/example_plugins/plugins/ui_grid/src/content-image.tsx b/example_plugins/plugins/ui_grid/src/content-image.tsx index b07b68a..2e14906 100644 --- a/example_plugins/plugins/ui_grid/src/content-image.tsx +++ b/example_plugins/plugins/ui_grid/src/content-image.tsx @@ -4,11 +4,12 @@ import { Grid } from "@project-gauntlet/api/components"; export default function ContentImageExample(): ReactElement { return ( - - - - - + + + + + ) } \ No newline at end of file diff --git a/example_plugins/plugins/ui_inline_separators/.gitignore b/example_plugins/plugins/ui_inline_code_block/.gitignore similarity index 100% rename from example_plugins/plugins/ui_inline_separators/.gitignore rename to example_plugins/plugins/ui_inline_code_block/.gitignore diff --git a/example_plugins/plugins/ui_inline_separators/gauntlet.toml b/example_plugins/plugins/ui_inline_code_block/gauntlet.toml similarity index 100% rename from example_plugins/plugins/ui_inline_separators/gauntlet.toml rename to example_plugins/plugins/ui_inline_code_block/gauntlet.toml diff --git a/example_plugins/plugins/ui_inline_separators/package.json b/example_plugins/plugins/ui_inline_code_block/package.json similarity index 85% rename from example_plugins/plugins/ui_inline_separators/package.json rename to example_plugins/plugins/ui_inline_code_block/package.json index 7049d90..e485da9 100644 --- a/example_plugins/plugins/ui_inline_separators/package.json +++ b/example_plugins/plugins/ui_inline_code_block/package.json @@ -1,5 +1,5 @@ { - "name": "@project-gauntlet/docs-inline-separators", + "name": "@project-gauntlet/docs-inline-code-block", "private": true, "scripts": { "build": "gauntlet build", diff --git a/example_plugins/plugins/ui_inline_code_block/src/main.tsx b/example_plugins/plugins/ui_inline_code_block/src/main.tsx new file mode 100644 index 0000000..456298c --- /dev/null +++ b/example_plugins/plugins/ui_inline_code_block/src/main.tsx @@ -0,0 +1,19 @@ +import { ReactElement } from "react"; +import { Inline } from "@project-gauntlet/api/components"; + +export default function Main({ text }: { text: string }): ReactElement | null { + + if (text != "example") { + return null + } + + return ( + + + + {"() => void"} + + + + ) +} diff --git a/example_plugins/plugins/ui_inline_separators/tsconfig.json b/example_plugins/plugins/ui_inline_code_block/tsconfig.json similarity index 100% rename from example_plugins/plugins/ui_inline_separators/tsconfig.json rename to example_plugins/plugins/ui_inline_code_block/tsconfig.json diff --git a/example_plugins/plugins/ui_inline_header/.gitignore b/example_plugins/plugins/ui_inline_header/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/ui_inline_header/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/ui_inline_header/gauntlet.toml b/example_plugins/plugins/ui_inline_header/gauntlet.toml new file mode 100644 index 0000000..2521a6c --- /dev/null +++ b/example_plugins/plugins/ui_inline_header/gauntlet.toml @@ -0,0 +1,16 @@ +[gauntlet] +name = 'Docs Inline' +description = '' + +# docs-code-segment:start main +[[entrypoint]] +id = 'main' +name = 'Main' +path = 'src/main.tsx' +type = 'inline-view' +description = '' +# docs-code-segment:end + +[permissions] +main_search_bar = ["read"] + diff --git a/example_plugins/plugins/ui_inline_header/package.json b/example_plugins/plugins/ui_inline_header/package.json new file mode 100644 index 0000000..e338daa --- /dev/null +++ b/example_plugins/plugins/ui_inline_header/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-inline-header", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/ui_inline_header/src/main.tsx b/example_plugins/plugins/ui_inline_header/src/main.tsx new file mode 100644 index 0000000..8a20b04 --- /dev/null +++ b/example_plugins/plugins/ui_inline_header/src/main.tsx @@ -0,0 +1,19 @@ +import { ReactElement } from "react"; +import { Inline } from "@project-gauntlet/api/components"; + +export default function Main({ text }: { text: string }): ReactElement | null { + + if (text != "example") { + return null + } + + return ( + + + + Header Text + + + + ) +} diff --git a/example_plugins/plugins/ui_inline_header/tsconfig.json b/example_plugins/plugins/ui_inline_header/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/ui_inline_header/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_inline_horizontal_break/.gitignore b/example_plugins/plugins/ui_inline_horizontal_break/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/ui_inline_horizontal_break/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/ui_inline_horizontal_break/gauntlet.toml b/example_plugins/plugins/ui_inline_horizontal_break/gauntlet.toml new file mode 100644 index 0000000..2521a6c --- /dev/null +++ b/example_plugins/plugins/ui_inline_horizontal_break/gauntlet.toml @@ -0,0 +1,16 @@ +[gauntlet] +name = 'Docs Inline' +description = '' + +# docs-code-segment:start main +[[entrypoint]] +id = 'main' +name = 'Main' +path = 'src/main.tsx' +type = 'inline-view' +description = '' +# docs-code-segment:end + +[permissions] +main_search_bar = ["read"] + diff --git a/example_plugins/plugins/ui_inline_horizontal_break/package.json b/example_plugins/plugins/ui_inline_horizontal_break/package.json new file mode 100644 index 0000000..3d6e2e1 --- /dev/null +++ b/example_plugins/plugins/ui_inline_horizontal_break/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-inline-horizontal-break", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/ui_inline_horizontal_break/src/main.tsx b/example_plugins/plugins/ui_inline_horizontal_break/src/main.tsx new file mode 100644 index 0000000..13258db --- /dev/null +++ b/example_plugins/plugins/ui_inline_horizontal_break/src/main.tsx @@ -0,0 +1,23 @@ +import { ReactElement } from "react"; +import { Inline } from "@project-gauntlet/api/components"; + +export default function Main({ text }: { text: string }): ReactElement | null { + + if (text != "example") { + return null + } + + return ( + + + + Above + + + + Below + + + + ) +} diff --git a/example_plugins/plugins/ui_inline_horizontal_break/tsconfig.json b/example_plugins/plugins/ui_inline_horizontal_break/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/ui_inline_horizontal_break/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_inline_image/.gitignore b/example_plugins/plugins/ui_inline_image/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/ui_inline_image/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/ui_inline_image/gauntlet.toml b/example_plugins/plugins/ui_inline_image/gauntlet.toml new file mode 100644 index 0000000..2521a6c --- /dev/null +++ b/example_plugins/plugins/ui_inline_image/gauntlet.toml @@ -0,0 +1,16 @@ +[gauntlet] +name = 'Docs Inline' +description = '' + +# docs-code-segment:start main +[[entrypoint]] +id = 'main' +name = 'Main' +path = 'src/main.tsx' +type = 'inline-view' +description = '' +# docs-code-segment:end + +[permissions] +main_search_bar = ["read"] + diff --git a/example_plugins/plugins/ui_inline_image/package.json b/example_plugins/plugins/ui_inline_image/package.json new file mode 100644 index 0000000..62e21b9 --- /dev/null +++ b/example_plugins/plugins/ui_inline_image/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-inline-image", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/ui_inline_image/src/main.tsx b/example_plugins/plugins/ui_inline_image/src/main.tsx new file mode 100644 index 0000000..ae29c63 --- /dev/null +++ b/example_plugins/plugins/ui_inline_image/src/main.tsx @@ -0,0 +1,17 @@ +import { ReactElement } from "react"; +import { Icons, Inline } from "@project-gauntlet/api/components"; + +export default function Main({ text }: { text: string }): ReactElement | null { + + if (text != "example") { + return null + } + + return ( + + + + + + ) +} diff --git a/example_plugins/plugins/ui_inline_image/tsconfig.json b/example_plugins/plugins/ui_inline_image/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/ui_inline_image/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_inline_main/.gitignore b/example_plugins/plugins/ui_inline_main/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/ui_inline_main/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/ui_inline_main/gauntlet.toml b/example_plugins/plugins/ui_inline_main/gauntlet.toml new file mode 100644 index 0000000..2521a6c --- /dev/null +++ b/example_plugins/plugins/ui_inline_main/gauntlet.toml @@ -0,0 +1,16 @@ +[gauntlet] +name = 'Docs Inline' +description = '' + +# docs-code-segment:start main +[[entrypoint]] +id = 'main' +name = 'Main' +path = 'src/main.tsx' +type = 'inline-view' +description = '' +# docs-code-segment:end + +[permissions] +main_search_bar = ["read"] + diff --git a/example_plugins/plugins/ui_inline_main/package.json b/example_plugins/plugins/ui_inline_main/package.json new file mode 100644 index 0000000..c8eacb7 --- /dev/null +++ b/example_plugins/plugins/ui_inline_main/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-inline-main", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/ui_inline_main/src/main.tsx b/example_plugins/plugins/ui_inline_main/src/main.tsx new file mode 100644 index 0000000..2901b87 --- /dev/null +++ b/example_plugins/plugins/ui_inline_main/src/main.tsx @@ -0,0 +1,31 @@ +import { ReactElement } from "react"; +import { Inline, Icons, ActionPanel, Action } from "@project-gauntlet/api/components"; + +export default function Main({ text }: { text: string }): ReactElement | null { + + if (text != "1 + 2") { + return null + } + + return ( + + {/* */}}/> + + } + > + + + 1 + 2 + + + + + + 3 + + + + ) +} diff --git a/example_plugins/plugins/ui_inline_main/tsconfig.json b/example_plugins/plugins/ui_inline_main/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/ui_inline_main/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_inline_paragraph/.gitignore b/example_plugins/plugins/ui_inline_paragraph/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/ui_inline_paragraph/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/ui_inline_paragraph/gauntlet.toml b/example_plugins/plugins/ui_inline_paragraph/gauntlet.toml new file mode 100644 index 0000000..2521a6c --- /dev/null +++ b/example_plugins/plugins/ui_inline_paragraph/gauntlet.toml @@ -0,0 +1,16 @@ +[gauntlet] +name = 'Docs Inline' +description = '' + +# docs-code-segment:start main +[[entrypoint]] +id = 'main' +name = 'Main' +path = 'src/main.tsx' +type = 'inline-view' +description = '' +# docs-code-segment:end + +[permissions] +main_search_bar = ["read"] + diff --git a/example_plugins/plugins/ui_inline_paragraph/package.json b/example_plugins/plugins/ui_inline_paragraph/package.json new file mode 100644 index 0000000..c4aed9e --- /dev/null +++ b/example_plugins/plugins/ui_inline_paragraph/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-inline-paragraph", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/ui_inline_paragraph/src/main.tsx b/example_plugins/plugins/ui_inline_paragraph/src/main.tsx new file mode 100644 index 0000000..72da13e --- /dev/null +++ b/example_plugins/plugins/ui_inline_paragraph/src/main.tsx @@ -0,0 +1,19 @@ +import { ReactElement } from "react"; +import { Inline } from "@project-gauntlet/api/components"; + +export default function Main({ text }: { text: string }): ReactElement | null { + + if (text != "example") { + return null + } + + return ( + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + + + + ) +} diff --git a/example_plugins/plugins/ui_inline_paragraph/tsconfig.json b/example_plugins/plugins/ui_inline_paragraph/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/ui_inline_paragraph/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_inline_separator/.gitignore b/example_plugins/plugins/ui_inline_separator/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/ui_inline_separator/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/ui_inline_separator/gauntlet.toml b/example_plugins/plugins/ui_inline_separator/gauntlet.toml new file mode 100644 index 0000000..2521a6c --- /dev/null +++ b/example_plugins/plugins/ui_inline_separator/gauntlet.toml @@ -0,0 +1,16 @@ +[gauntlet] +name = 'Docs Inline' +description = '' + +# docs-code-segment:start main +[[entrypoint]] +id = 'main' +name = 'Main' +path = 'src/main.tsx' +type = 'inline-view' +description = '' +# docs-code-segment:end + +[permissions] +main_search_bar = ["read"] + diff --git a/example_plugins/plugins/ui_inline_separator/package.json b/example_plugins/plugins/ui_inline_separator/package.json new file mode 100644 index 0000000..555ed2d --- /dev/null +++ b/example_plugins/plugins/ui_inline_separator/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-inline-separator", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/ui_inline_separators/src/main.tsx b/example_plugins/plugins/ui_inline_separator/src/main.tsx similarity index 89% rename from example_plugins/plugins/ui_inline_separators/src/main.tsx rename to example_plugins/plugins/ui_inline_separator/src/main.tsx index c2b4bb9..7570e4a 100644 --- a/example_plugins/plugins/ui_inline_separators/src/main.tsx +++ b/example_plugins/plugins/ui_inline_separator/src/main.tsx @@ -1,7 +1,12 @@ import { ReactElement } from "react"; import { Inline, Icons } from "@project-gauntlet/api/components"; -export default function Main({ text }: { text: string }): ReactElement { +export default function Main({ text }: { text: string }): ReactElement | null { + + if (text != "example") { + return null + } + return ( diff --git a/example_plugins/plugins/ui_inline_separator/tsconfig.json b/example_plugins/plugins/ui_inline_separator/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/ui_inline_separator/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_inline_three_sections/src/main.tsx b/example_plugins/plugins/ui_inline_three_sections/src/main.tsx index a0ff3fa..71d7bc3 100644 --- a/example_plugins/plugins/ui_inline_three_sections/src/main.tsx +++ b/example_plugins/plugins/ui_inline_three_sections/src/main.tsx @@ -1,8 +1,11 @@ import { ReactElement } from "react"; import { Inline } from "@project-gauntlet/api/components"; -export default function Main(props: { text: string }): ReactElement { - const type = props.text; +export default function Main({ text }: { text: string }): ReactElement | null { + + if (text != "example") { + return null + } return ( @@ -13,7 +16,7 @@ export default function Main(props: { text: string }): ReactElement { - Center: {type} + Center diff --git a/example_plugins/scenarios/ui_inline_separators/main/separator.json b/example_plugins/scenarios/ui_inline_code_block/main/default.json similarity index 51% rename from example_plugins/scenarios/ui_inline_separators/main/separator.json rename to example_plugins/scenarios/ui_inline_code_block/main/default.json index 056de6b..d880710 100644 --- a/example_plugins/scenarios/ui_inline_separators/main/separator.json +++ b/example_plugins/scenarios/ui_inline_code_block/main/default.json @@ -1,4 +1,4 @@ { "type": "Search", - "text": "separator" + "text": "example" } \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_header/main/default.json b/example_plugins/scenarios/ui_inline_header/main/default.json new file mode 100644 index 0000000..d880710 --- /dev/null +++ b/example_plugins/scenarios/ui_inline_header/main/default.json @@ -0,0 +1,4 @@ +{ + "type": "Search", + "text": "example" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_horizontal_break/main/default.json b/example_plugins/scenarios/ui_inline_horizontal_break/main/default.json new file mode 100644 index 0000000..d880710 --- /dev/null +++ b/example_plugins/scenarios/ui_inline_horizontal_break/main/default.json @@ -0,0 +1,4 @@ +{ + "type": "Search", + "text": "example" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_image/main/default.json b/example_plugins/scenarios/ui_inline_image/main/default.json new file mode 100644 index 0000000..d880710 --- /dev/null +++ b/example_plugins/scenarios/ui_inline_image/main/default.json @@ -0,0 +1,4 @@ +{ + "type": "Search", + "text": "example" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_main/main/default.json b/example_plugins/scenarios/ui_inline_main/main/default.json new file mode 100644 index 0000000..b9bad38 --- /dev/null +++ b/example_plugins/scenarios/ui_inline_main/main/default.json @@ -0,0 +1,4 @@ +{ + "type": "Search", + "text": "1 + 2" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_paragraph/main/default.json b/example_plugins/scenarios/ui_inline_paragraph/main/default.json new file mode 100644 index 0000000..d880710 --- /dev/null +++ b/example_plugins/scenarios/ui_inline_paragraph/main/default.json @@ -0,0 +1,4 @@ +{ + "type": "Search", + "text": "example" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_separator/main/default.json b/example_plugins/scenarios/ui_inline_separator/main/default.json new file mode 100644 index 0000000..d880710 --- /dev/null +++ b/example_plugins/scenarios/ui_inline_separator/main/default.json @@ -0,0 +1,4 @@ +{ + "type": "Search", + "text": "example" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_three_sections/main/default.json b/example_plugins/scenarios/ui_inline_three_sections/main/default.json new file mode 100644 index 0000000..d880710 --- /dev/null +++ b/example_plugins/scenarios/ui_inline_three_sections/main/default.json @@ -0,0 +1,4 @@ +{ + "type": "Search", + "text": "example" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_three_sections/main/three-sections.json b/example_plugins/scenarios/ui_inline_three_sections/main/three-sections.json deleted file mode 100644 index a01b23a..0000000 --- a/example_plugins/scenarios/ui_inline_three_sections/main/three-sections.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Search", - "text": "three-sections" -} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_two_sections/main/default.json b/example_plugins/scenarios/ui_inline_two_sections/main/default.json new file mode 100644 index 0000000..d880710 --- /dev/null +++ b/example_plugins/scenarios/ui_inline_two_sections/main/default.json @@ -0,0 +1,4 @@ +{ + "type": "Search", + "text": "example" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_two_sections/main/two-sections.json b/example_plugins/scenarios/ui_inline_two_sections/main/two-sections.json deleted file mode 100644 index 38eaee1..0000000 --- a/example_plugins/scenarios/ui_inline_two_sections/main/two-sections.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Search", - "text": "two-sections" -} \ No newline at end of file From 709f8337d4a115e21761a9151c94484c97c70475 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 15 Mar 2025 20:05:17 +0100 Subject: [PATCH 034/170] Fix scenario runner --- rust/client/src/ui/mod.rs | 45 ++++++++++------------- rust/scenario_runner/src/frontend_mock.rs | 8 +++- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 056acae..9e66585 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -883,10 +883,10 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::PromptChanged(mut new_prompt) => { match &mut state.global_state { GlobalState::MainView { - focused_search_result, - sub_state, - .. - } => { + focused_search_result, + sub_state, + .. + } => { new_prompt.truncate(100); // search query uses regex so just to be safe truncate the prompt state.prompt = new_prompt.clone(); @@ -897,8 +897,8 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } GlobalState::ErrorView { .. } => {} GlobalState::PluginView { .. } => {} - GlobalState::PendingPluginView { .. } => {} - } + GlobalState::PendingPluginView { .. } => {} + } state.search(new_prompt, true) } @@ -1242,30 +1242,23 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::ScreenshotDone { save_path, screenshot } => { println!("Saving screenshot at: {}", save_path); - Task::perform( - async move { - tokio::task::spawn_blocking(move || { - let save_dir = Path::new(&save_path); + let save_dir = Path::new(&save_path); - let save_parent_dir = save_dir.parent().expect("save_path has no parent"); + let save_parent_dir = save_dir.parent().expect("save_path has no parent"); - fs::create_dir_all(save_parent_dir).expect("unable to create save_parent_dir"); + fs::create_dir_all(save_parent_dir).expect("unable to create save_parent_dir"); - image::save_buffer_with_format( - &save_path, - &screenshot.bytes, - screenshot.size.width, - screenshot.size.height, - image::ColorType::Rgba8, - image::ImageFormat::Png, - ) - }) - .await - .expect("Unable to save screenshot") - }, - |_| (), + image::save_buffer_with_format( + &save_path, + &screenshot.bytes, + screenshot.size.width, + screenshot.size.height, + image::ColorType::Rgba8, + image::ImageFormat::Png, ) - .then(|_| iced::exit()) + .expect("Unable to save screenshot"); + + std::process::exit(0); } AppMsg::ToggleActionPanel { keyboard } => { match &mut state.global_state { diff --git a/rust/scenario_runner/src/frontend_mock.rs b/rust/scenario_runner/src/frontend_mock.rs index 29431e1..e3d2d19 100644 --- a/rust/scenario_runner/src/frontend_mock.rs +++ b/rust/scenario_runner/src/frontend_mock.rs @@ -157,10 +157,14 @@ async fn request_loop( | UiRequestData::ShowWindow | UiRequestData::HideWindow | UiRequestData::ClearInlineView { .. } - | UiRequestData::SetTheme { .. } => { + | UiRequestData::SetTheme { .. } + | UiRequestData::ShowPluginView { .. } + | UiRequestData::ShowGeneratedPluginView { .. } => { unreachable!() } - UiRequestData::SetGlobalShortcut { .. } | UiRequestData::SetWindowPositionMode { .. } | UiRequestData::RequestSearchResultUpdate => { + UiRequestData::SetGlobalShortcut { .. } + | UiRequestData::SetWindowPositionMode { .. } + | UiRequestData::RequestSearchResultUpdate => { // noop } UiRequestData::ReplaceView { From 99f7ecec116cdccb28c2acb6c266573e266579b2 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 15 Mar 2025 20:16:19 +0100 Subject: [PATCH 035/170] Fix formatting --- rust/scenario_runner/src/lib.rs | 9 ++++----- rust/server/src/lib.rs | 6 +++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/rust/scenario_runner/src/lib.rs b/rust/scenario_runner/src/lib.rs index 854a703..7df44f0 100644 --- a/rust/scenario_runner/src/lib.rs +++ b/rust/scenario_runner/src/lib.rs @@ -1,10 +1,10 @@ use gauntlet_common::model::BackendRequestData; -use gauntlet_common::model::UiSetupData; -use gauntlet_common::model::UiTheme; -use gauntlet_common::model::WindowPositionMode; use gauntlet_common::model::BackendResponseData; use gauntlet_common::model::UiRequestData; use gauntlet_common::model::UiResponseData; +use gauntlet_common::model::UiSetupData; +use gauntlet_common::model::UiTheme; +use gauntlet_common::model::WindowPositionMode; use gauntlet_utils::channel::RequestReceiver; use gauntlet_utils::channel::RequestSender; @@ -23,9 +23,8 @@ pub async fn run_scenario_runner_frontend_mock( pub async fn run_scenario_runner_mock_server( _request_sender: RequestSender, mut backend_receiver: RequestReceiver, - theme: UiTheme + theme: UiTheme, ) -> anyhow::Result<()> { - let (_data, responder) = backend_receiver.recv().await; responder.respond(BackendResponseData::SetupData { data: UiSetupData { diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 4c8b800..842e724 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -173,7 +173,11 @@ fn start_server( } #[cfg(feature = "scenario_runner")] -fn start_mock_server(request_sender: RequestSender, backend_receiver: RequestReceiver, theme: UiTheme) { +fn start_mock_server( + request_sender: RequestSender, + backend_receiver: RequestReceiver, + theme: UiTheme, +) { tokio::runtime::Builder::new_multi_thread() .enable_all() .build() From c39ac08178fc66bd605dbf5445605df3dfa2e45f Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 15 Mar 2025 20:22:34 +0100 Subject: [PATCH 036/170] Update package-lock.json --- package-lock.json | 257 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 234 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index be00d1a..3575bb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ "example_plugins/js/api": {}, "example_plugins/plugins/docs_detail": { "name": "@project-gauntlet/docs-detailt", + "extraneous": true, "dependencies": { "@project-gauntlet/api": "file:../../js/api" }, @@ -61,11 +62,101 @@ "typescript": "*" } }, - "example_plugins/plugins/docs_detail/node_modules/@project-gauntlet/api": { + "example_plugins/plugins/docs_form": { + "name": "@project-gauntlet/docs-form", + "extraneous": true, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_grid": { + "name": "@project-gauntlet/docs-grid", + "extraneous": true, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_inline_separators": { + "name": "@project-gauntlet/docs-inline-separators", + "extraneous": true, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_inline_three_sections": { + "name": "@project-gauntlet/docs-inline-three-sections", + "extraneous": true, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_inline_two_sections": { + "name": "@project-gauntlet/docs-inline-two-sections", + "extraneous": true, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/docs_list": { + "name": "@project-gauntlet/docs-list", + "extraneous": true, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/ui_detail": { + "name": "@project-gauntlet/docs-detail", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/ui_detail/node_modules/@project-gauntlet/api": { "resolved": "example_plugins/js/api", "link": true }, - "example_plugins/plugins/docs_form": { + "example_plugins/plugins/ui_form": { "name": "@project-gauntlet/docs-form", "dependencies": { "@project-gauntlet/api": "file:../../js/api" @@ -77,11 +168,11 @@ "typescript": "*" } }, - "example_plugins/plugins/docs_form/node_modules/@project-gauntlet/api": { + "example_plugins/plugins/ui_form/node_modules/@project-gauntlet/api": { "resolved": "example_plugins/js/api", "link": true }, - "example_plugins/plugins/docs_grid": { + "example_plugins/plugins/ui_grid": { "name": "@project-gauntlet/docs-grid", "dependencies": { "@project-gauntlet/api": "file:../../js/api" @@ -93,12 +184,12 @@ "typescript": "*" } }, - "example_plugins/plugins/docs_grid/node_modules/@project-gauntlet/api": { + "example_plugins/plugins/ui_grid/node_modules/@project-gauntlet/api": { "resolved": "example_plugins/js/api", "link": true }, - "example_plugins/plugins/docs_inline_separators": { - "name": "@project-gauntlet/docs-inline-separators", + "example_plugins/plugins/ui_inline_code_block": { + "name": "@project-gauntlet/docs-inline-code-block", "dependencies": { "@project-gauntlet/api": "file:../../js/api" }, @@ -109,11 +200,107 @@ "typescript": "*" } }, - "example_plugins/plugins/docs_inline_separators/node_modules/@project-gauntlet/api": { + "example_plugins/plugins/ui_inline_code_block/node_modules/@project-gauntlet/api": { "resolved": "example_plugins/js/api", "link": true }, - "example_plugins/plugins/docs_inline_three_sections": { + "example_plugins/plugins/ui_inline_header": { + "name": "@project-gauntlet/docs-inline-header", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/ui_inline_header/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/ui_inline_horizontal_break": { + "name": "@project-gauntlet/docs-inline-horizontal-break", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/ui_inline_horizontal_break/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/ui_inline_image": { + "name": "@project-gauntlet/docs-inline-image", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/ui_inline_image/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/ui_inline_main": { + "name": "@project-gauntlet/docs-inline-main", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/ui_inline_main/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/ui_inline_paragraph": { + "name": "@project-gauntlet/docs-inline-paragraph", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/ui_inline_paragraph/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/ui_inline_separator": { + "name": "@project-gauntlet/docs-inline-separator", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/ui_inline_separator/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/ui_inline_three_sections": { "name": "@project-gauntlet/docs-inline-three-sections", "dependencies": { "@project-gauntlet/api": "file:../../js/api" @@ -125,11 +312,11 @@ "typescript": "*" } }, - "example_plugins/plugins/docs_inline_three_sections/node_modules/@project-gauntlet/api": { + "example_plugins/plugins/ui_inline_three_sections/node_modules/@project-gauntlet/api": { "resolved": "example_plugins/js/api", "link": true }, - "example_plugins/plugins/docs_inline_two_sections": { + "example_plugins/plugins/ui_inline_two_sections": { "name": "@project-gauntlet/docs-inline-two-sections", "dependencies": { "@project-gauntlet/api": "file:../../js/api" @@ -141,11 +328,11 @@ "typescript": "*" } }, - "example_plugins/plugins/docs_inline_two_sections/node_modules/@project-gauntlet/api": { + "example_plugins/plugins/ui_inline_two_sections/node_modules/@project-gauntlet/api": { "resolved": "example_plugins/js/api", "link": true }, - "example_plugins/plugins/docs_list": { + "example_plugins/plugins/ui_list": { "name": "@project-gauntlet/docs-list", "dependencies": { "@project-gauntlet/api": "file:../../js/api" @@ -157,7 +344,7 @@ "typescript": "*" } }, - "example_plugins/plugins/docs_list/node_modules/@project-gauntlet/api": { + "example_plugins/plugins/ui_list/node_modules/@project-gauntlet/api": { "resolved": "example_plugins/js/api", "link": true }, @@ -787,32 +974,56 @@ "resolved": "dev_plugin", "link": true }, - "node_modules/@project-gauntlet/docs-detailt": { - "resolved": "example_plugins/plugins/docs_detail", + "node_modules/@project-gauntlet/docs-detail": { + "resolved": "example_plugins/plugins/ui_detail", "link": true }, "node_modules/@project-gauntlet/docs-form": { - "resolved": "example_plugins/plugins/docs_form", + "resolved": "example_plugins/plugins/ui_form", "link": true }, "node_modules/@project-gauntlet/docs-grid": { - "resolved": "example_plugins/plugins/docs_grid", + "resolved": "example_plugins/plugins/ui_grid", "link": true }, - "node_modules/@project-gauntlet/docs-inline-separators": { - "resolved": "example_plugins/plugins/docs_inline_separators", + "node_modules/@project-gauntlet/docs-inline-code-block": { + "resolved": "example_plugins/plugins/ui_inline_code_block", + "link": true + }, + "node_modules/@project-gauntlet/docs-inline-header": { + "resolved": "example_plugins/plugins/ui_inline_header", + "link": true + }, + "node_modules/@project-gauntlet/docs-inline-horizontal-break": { + "resolved": "example_plugins/plugins/ui_inline_horizontal_break", + "link": true + }, + "node_modules/@project-gauntlet/docs-inline-image": { + "resolved": "example_plugins/plugins/ui_inline_image", + "link": true + }, + "node_modules/@project-gauntlet/docs-inline-main": { + "resolved": "example_plugins/plugins/ui_inline_main", + "link": true + }, + "node_modules/@project-gauntlet/docs-inline-paragraph": { + "resolved": "example_plugins/plugins/ui_inline_paragraph", + "link": true + }, + "node_modules/@project-gauntlet/docs-inline-separator": { + "resolved": "example_plugins/plugins/ui_inline_separator", "link": true }, "node_modules/@project-gauntlet/docs-inline-three-sections": { - "resolved": "example_plugins/plugins/docs_inline_three_sections", + "resolved": "example_plugins/plugins/ui_inline_three_sections", "link": true }, "node_modules/@project-gauntlet/docs-inline-two-sections": { - "resolved": "example_plugins/plugins/docs_inline_two_sections", + "resolved": "example_plugins/plugins/ui_inline_two_sections", "link": true }, "node_modules/@project-gauntlet/docs-list": { - "resolved": "example_plugins/plugins/docs_list", + "resolved": "example_plugins/plugins/ui_list", "link": true }, "node_modules/@project-gauntlet/react": { From 6d37684fc894207dd273538590c5d0cfe2f2b1b3 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 16 Mar 2025 11:05:37 +0100 Subject: [PATCH 037/170] Tweak descriptions of fields in plugin manifest schema --- docs/schema/plugin_manifest.schema.json | 147 +++++++++++++-------- rust/manifest_schema/src/main.rs | 10 +- rust/server/src/plugins/plugin_manifest.rs | 144 ++++++++++++-------- 3 files changed, 193 insertions(+), 108 deletions(-) diff --git a/docs/schema/plugin_manifest.schema.json b/docs/schema/plugin_manifest.schema.json index a6cacdb..11f9fbd 100644 --- a/docs/schema/plugin_manifest.schema.json +++ b/docs/schema/plugin_manifest.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "PluginManifest", - "description": "Manifest structure for a plugin.", + "description": "Plugin Manifest definition", "type": "object", "required": [ "entrypoint", @@ -15,14 +15,14 @@ ] }, "entrypoint": { - "description": "Entrypoints for the plugin.", + "description": "Plugin entrypoints, all plugin will have at least one entrypoint", "type": "array", "items": { "$ref": "#/definitions/PluginManifestEntrypoint" } }, "gauntlet": { - "description": "Metadata about the plugin.", + "description": "General plugin metadata", "allOf": [ { "$ref": "#/definitions/PluginManifestMetadata" @@ -30,7 +30,7 @@ ] }, "permissions": { - "description": "Permissions required by the plugin.", + "description": "Permissions required by the plugin", "default": { "environment": [], "network": [], @@ -53,7 +53,7 @@ ] }, "preferences": { - "description": "Preferences that can be configured by the user in the settings view.", + "description": "Preferences that can be configured by the user in the settings view", "default": [], "type": "array", "items": { @@ -61,7 +61,7 @@ } }, "supported_system": { - "description": "List of supported operating systems.", + "description": "List of supported operating systems", "default": [], "type": "array", "items": { @@ -71,7 +71,7 @@ }, "definitions": { "PluginManifestAction": { - "description": "Action that can be performed by the plugin.", + "description": "Action definition", "type": "object", "required": [ "description", @@ -80,15 +80,15 @@ ], "properties": { "description": { - "description": "Description of what the action does.", + "description": "Description of what the action does", "type": "string" }, "id": { - "description": "Unique identifier for the action.", + "description": "Unique identifier for the action, can only contain letters and numbers", "type": "string" }, "shortcut": { - "description": "Keyboard shortcut to trigger the action.", + "description": "Default keyboard shortcut to trigger the action", "allOf": [ { "$ref": "#/definitions/PluginManifestActionShortcut" @@ -98,7 +98,7 @@ } }, "PluginManifestActionShortcut": { - "description": "Keyboard shortcut configuration for a plugin action.", + "description": "Keyboard shortcut for a plugin action", "type": "object", "required": [ "key", @@ -106,7 +106,7 @@ ], "properties": { "key": { - "description": "The key to be pressed for this shortcut.", + "description": "The main key to be pressed for this shortcut", "allOf": [ { "$ref": "#/definitions/PluginManifestActionShortcutKey" @@ -114,7 +114,7 @@ ] }, "kind": { - "description": "The type of shortcut.", + "description": "The kind of shortcut, defines required modifiers", "allOf": [ { "$ref": "#/definitions/PluginManifestActionShortcutKind" @@ -221,17 +221,17 @@ ] }, "PluginManifestActionShortcutKind": { - "description": "The type of shortcut.", + "description": "The kind of shortcut", "oneOf": [ { - "description": "Main shortcut for the action (e.g. cmd).", + "description": "Main kind shortcuts require Ctrl modifier on Windows/Linux or Cmd on macOS", "type": "string", "enum": [ "main" ] }, { - "description": "Alternative shortcut for the action (e.g. opt).", + "description": "Alternative kind shortcuts require Alt modifier on Windows/Linux or Opt on macOS", "type": "string", "enum": [ "alternative" @@ -240,24 +240,24 @@ ] }, "PluginManifestClipboardPermissions": { - "description": "Clipboard permissions for the plugin.", + "description": "Clipboard permissions for the plugin", "oneOf": [ { - "description": "Allows the plugin to read from the clipboard.", + "description": "Allows the plugin to read from the clipboard", "type": "string", "enum": [ "read" ] }, { - "description": "Allows the plugin to write to the clipboard.", + "description": "Allows the plugin to write to the clipboard", "type": "string", "enum": [ "write" ] }, { - "description": "Allows the plugin to clear the clipboard contents.", + "description": "Allows the plugin to clear the clipboard contents", "type": "string", "enum": [ "clear" @@ -266,7 +266,7 @@ ] }, "PluginManifestEntrypoint": { - "description": "An entrypoint for the plugin.", + "description": "Plugin entrypoint definition", "type": "object", "required": [ "description", @@ -277,6 +277,7 @@ ], "properties": { "actions": { + "description": "List of definitions of plugin actions", "default": [], "type": "array", "items": { @@ -284,24 +285,30 @@ } }, "description": { + "description": "Entrypoint description", "type": "string" }, "icon": { + "description": "Entrypoint icon, path to file in assets relative to it", "type": [ "string", "null" ] }, "id": { + "description": "Unique identifier of the entrypoint, can only contain small letters, numbers and dash", "type": "string" }, "name": { + "description": "Entrypoint name", "type": "string" }, "path": { + "description": "Path to TypeScript file relative to package directory", "type": "string" }, "preferences": { + "description": "List of definitions of plugin preferences", "default": [], "type": "array", "items": { @@ -309,36 +316,41 @@ } }, "type": { - "$ref": "#/definitions/PluginManifestEntrypointTypes" + "description": "Type of the entrypoint", + "allOf": [ + { + "$ref": "#/definitions/PluginManifestEntrypointTypes" + } + ] } } }, "PluginManifestEntrypointTypes": { - "description": "Types of plugin entrypoints.", + "description": "Types of plugin entrypoints", "oneOf": [ { - "description": "A command entrypoint.", + "description": "A function-based entrypoint", "type": "string", "enum": [ "command" ] }, { - "description": "A view-based entrypoint.", + "description": "A view-based entrypoint", "type": "string", "enum": [ "view" ] }, { - "description": "An inline view entrypoint.", + "description": "A view-based entrypoint displayed under main search bar", "type": "string", "enum": [ "inline-view" ] }, { - "description": "Generates new entrypoints dynamically.", + "description": "Entrypoint that dynamically generatepointsd", "type": "string", "enum": [ "entrypoint-generator" @@ -358,7 +370,7 @@ ] }, "PluginManifestMetadata": { - "description": "Metadata for the plugin manifest.", + "description": "General plugin metadata", "type": "object", "required": [ "description", @@ -366,21 +378,21 @@ ], "properties": { "description": { - "description": "Description of the plugin.", + "description": "Description of the plugin", "type": "string" }, "name": { - "description": "Name of the plugin.", + "description": "Name of the plugin", "type": "string" } } }, "PluginManifestPermissions": { - "description": "Permissions required by the plugin.", + "description": "Permissions required by the plugin", "type": "object", "properties": { "clipboard": { - "description": "Clipboard permissions for the plugin.", + "description": "Clipboard permissions for the plugin", "default": [], "type": "array", "items": { @@ -388,7 +400,7 @@ } }, "environment": { - "description": "Environment variables that the plugin can access.", + "description": "Environment variables that the plugin can access", "default": [], "type": "array", "items": { @@ -396,7 +408,7 @@ } }, "exec": { - "description": "Execution permissions for the plugin.", + "description": "Execution permissions for the plugin", "default": { "command": [], "executable": [] @@ -408,7 +420,7 @@ ] }, "filesystem": { - "description": "Filesystem permissions for the plugin.", + "description": "Filesystem permissions for the plugin", "default": { "read": [], "write": [] @@ -420,7 +432,7 @@ ] }, "main_search_bar": { - "description": "Permissions for the main search bar.", + "description": "Permissions for the main search bar", "default": [], "type": "array", "items": { @@ -428,7 +440,7 @@ } }, "network": { - "description": "Network domains that the plugin can access.", + "description": "Network address (domain or ip address + optional port) that the plugin can access", "default": [], "type": "array", "items": { @@ -436,7 +448,7 @@ } }, "system": { - "description": "System permissions for the plugin.", + "description": "Deno system permissions for the plugin", "default": [], "type": "array", "items": { @@ -446,11 +458,11 @@ } }, "PluginManifestPermissionsExec": { - "description": "Execution permissions for the plugin.", + "description": "Execution permissions for the plugin", "type": "object", "properties": { "command": { - "description": "Commands that the plugin can execute.", + "description": "List of commands on PATH that the plugin can execute", "default": [], "type": "array", "items": { @@ -458,7 +470,7 @@ } }, "executable": { - "description": "Executables that the plugin can run.", + "description": "List of paths to executables that the plugin can run", "default": [], "type": "array", "items": { @@ -468,11 +480,11 @@ } }, "PluginManifestPermissionsFileSystem": { - "description": "Filesystem permissions for the plugin.", + "description": "Filesystem permissions for the plugin", "type": "object", "properties": { "read": { - "description": "Paths that the plugin can read from.", + "description": "Paths that the plugin can read from", "default": [], "type": "array", "items": { @@ -480,7 +492,7 @@ } }, "write": { - "description": "Paths that the plugin can write to.", + "description": "Paths that the plugin can write to", "default": [], "type": "array", "items": { @@ -490,10 +502,10 @@ } }, "PluginManifestPreference": { - "description": "User-configurable preference options.", + "description": "User-configurable preference options", "oneOf": [ { - "description": "A numeric preference.", + "description": "A numeric preference", "type": "object", "required": [ "description", @@ -503,6 +515,7 @@ ], "properties": { "default": { + "description": "Default value", "type": [ "number", "null" @@ -510,12 +523,15 @@ "format": "double" }, "description": { + "description": "Description of the preference", "type": "string" }, "id": { + "description": "Unique identifier of the preference, can only contain letters and numbers", "type": "string" }, "name": { + "description": "Display name of the preference", "type": "string" }, "type": { @@ -527,7 +543,7 @@ } }, { - "description": "A string preference.", + "description": "A string preference", "type": "object", "required": [ "description", @@ -537,18 +553,22 @@ ], "properties": { "default": { + "description": "Default value", "type": [ "string", "null" ] }, "description": { + "description": "Description of the preference", "type": "string" }, "id": { + "description": "Unique identifier of the preference, can only contain letters and numbers", "type": "string" }, "name": { + "description": "Display name of the preference", "type": "string" }, "type": { @@ -560,7 +580,7 @@ } }, { - "description": "An enum preference with selectable values.", + "description": "An enum preference with selectable values", "type": "object", "required": [ "description", @@ -571,24 +591,29 @@ ], "properties": { "default": { + "description": "Default value", "type": [ "string", "null" ] }, "description": { + "description": "Description of the preference", "type": "string" }, "enum_values": { + "description": "List of allowed enum values", "type": "array", "items": { "$ref": "#/definitions/PluginManifestPreferenceEnumValue" } }, "id": { + "description": "Unique identifier of the preference, can only contain letters and numbers", "type": "string" }, "name": { + "description": "Display name of the preference", "type": "string" }, "type": { @@ -600,7 +625,7 @@ } }, { - "description": "A boolean preference.", + "description": "A boolean preference", "type": "object", "required": [ "description", @@ -610,18 +635,22 @@ ], "properties": { "default": { + "description": "Default value", "type": [ "boolean", "null" ] }, "description": { + "description": "Description of the preference", "type": "string" }, "id": { + "description": "Unique identifier of the preference, can only contain letters and numbers", "type": "string" }, "name": { + "description": "Display name of the preference", "type": "string" }, "type": { @@ -633,7 +662,7 @@ } }, { - "description": "A list of strings preference.", + "description": "A list of strings preference", "type": "object", "required": [ "description", @@ -643,12 +672,15 @@ ], "properties": { "description": { + "description": "Description of the preference", "type": "string" }, "id": { + "description": "Unique identifier of the preference, can only contain letters and numbers", "type": "string" }, "name": { + "description": "Display name of the preference", "type": "string" }, "type": { @@ -660,7 +692,7 @@ } }, { - "description": "A list of numbers preference.", + "description": "A list of numbers preference", "type": "object", "required": [ "description", @@ -670,12 +702,15 @@ ], "properties": { "description": { + "description": "Description of the preference", "type": "string" }, "id": { + "description": "Unique identifier of the preference, can only contain letters and numbers", "type": "string" }, "name": { + "description": "Display name of the preference", "type": "string" }, "type": { @@ -687,7 +722,7 @@ } }, { - "description": "A list of enumerated preference values.", + "description": "A list of enumerated preference values", "type": "object", "required": [ "description", @@ -698,18 +733,22 @@ ], "properties": { "description": { + "description": "Description of the preference", "type": "string" }, "enum_values": { + "description": "List of allowed enum values", "type": "array", "items": { "$ref": "#/definitions/PluginManifestPreferenceEnumValue" } }, "id": { + "description": "Unique identifier of the preference, can only contain letters and numbers", "type": "string" }, "name": { + "description": "Display name of the preference", "type": "string" }, "type": { @@ -723,7 +762,7 @@ ] }, "PluginManifestPreferenceEnumValue": { - "description": "An enumerated preference value.", + "description": "Definition of the values available in enumerated preference", "type": "object", "required": [ "label", @@ -731,9 +770,11 @@ ], "properties": { "label": { + "description": "Displayed name", "type": "string" }, "value": { + "description": "Internal enum value", "type": "string" } } diff --git a/rust/manifest_schema/src/main.rs b/rust/manifest_schema/src/main.rs index 7ffbc1c..1ec6401 100644 --- a/rust/manifest_schema/src/main.rs +++ b/rust/manifest_schema/src/main.rs @@ -1,4 +1,5 @@ use std::io::Write; +use std::path::PathBuf; use gauntlet_server::plugins::plugin_manifest::PluginManifest; use schemars::schema_for; @@ -7,8 +8,13 @@ fn main() { let schema = schema_for!(PluginManifest); let json = serde_json::to_string_pretty(&schema).unwrap(); - std::fs::create_dir_all("../../docs/schema").expect("Failed to create directory"); - std::fs::write("../../docs/schema/plugin_manifest.schema.json", json.as_bytes()).expect("Failed to write schema"); + let schema_path = PathBuf::from(concat!( + env!("CARGO_MANIFEST_DIR"), + "../../../docs/schema/plugin_manifest.schema.json" + )); + + std::fs::create_dir_all(schema_path.parent().unwrap()).expect("Failed to create directory"); + std::fs::write(schema_path, json.as_bytes()).expect("Failed to write schema"); println!("Schema generated and saved to schema.json"); } diff --git a/rust/server/src/plugins/plugin_manifest.rs b/rust/server/src/plugins/plugin_manifest.rs index 0665ca2..6e8726e 100644 --- a/rust/server/src/plugins/plugin_manifest.rs +++ b/rust/server/src/plugins/plugin_manifest.rs @@ -5,147 +5,185 @@ use serde::Serialize; use crate::model::ActionShortcutKey; #[derive(Debug, Deserialize, Serialize, JsonSchema)] -#[schemars(description = "Manifest structure for a plugin.")] +#[schemars(description = "Plugin Manifest definition")] pub struct PluginManifest { #[serde(rename = "$schema")] + #[allow(unused)] schema: Option, - #[schemars(description = "Metadata about the plugin.")] + #[schemars(description = "General plugin metadata")] pub gauntlet: PluginManifestMetadata, - #[schemars(description = "Entrypoints for the plugin.")] + #[schemars(description = "Plugin entrypoints, all plugin will have at least one entrypoint")] pub entrypoint: Vec, #[serde(default)] - #[schemars(description = "List of supported operating systems.")] + #[schemars(description = "List of supported operating systems")] pub supported_system: Vec, #[serde(default)] - #[schemars(description = "Permissions required by the plugin.")] + #[schemars(description = "Permissions required by the plugin")] pub permissions: PluginManifestPermissions, #[serde(default)] - #[schemars(description = "Preferences that can be configured by the user in the settings view.")] + #[schemars(description = "Preferences that can be configured by the user in the settings view")] pub preferences: Vec, } #[derive(Debug, Deserialize, Serialize, JsonSchema)] -#[schemars(description = "An entrypoint for the plugin.")] +#[schemars(description = "Plugin entrypoint definition")] pub struct PluginManifestEntrypoint { + #[schemars(description = "Unique identifier of the entrypoint, can only contain small letters, numbers and dash")] pub id: String, + #[schemars(description = "Entrypoint name")] pub name: String, + #[schemars(description = "Entrypoint description")] pub description: String, #[allow(unused)] // Used during plugin build - pub path: String, + #[schemars(description = "Path to TypeScript file relative to package directory")] + path: String, + #[schemars(description = "Entrypoint icon, path to file in assets relative to it")] pub icon: Option, #[serde(rename = "type")] + #[schemars(description = "Type of the entrypoint")] pub entrypoint_type: PluginManifestEntrypointTypes, #[serde(default)] + #[schemars(description = "List of definitions of plugin preferences")] pub preferences: Vec, #[serde(default)] + #[schemars(description = "List of definitions of plugin actions")] pub actions: Vec, } #[derive(Debug, Deserialize, Serialize, JsonSchema)] #[serde(tag = "type")] -#[schemars(description = "User-configurable preference options.")] +#[schemars(description = "User-configurable preference options")] pub enum PluginManifestPreference { #[serde(rename = "number")] - #[schemars(description = "A numeric preference.")] + #[schemars(description = "A numeric preference")] Number { + #[schemars(description = "Unique identifier of the preference, can only contain letters and numbers")] id: String, + #[schemars(description = "Display name of the preference")] name: String, + #[schemars(description = "Default value")] default: Option, + #[schemars(description = "Description of the preference")] description: String, }, #[serde(rename = "string")] - #[schemars(description = "A string preference.")] + #[schemars(description = "A string preference")] String { + #[schemars(description = "Unique identifier of the preference, can only contain letters and numbers")] id: String, + #[schemars(description = "Display name of the preference")] name: String, + #[schemars(description = "Default value")] default: Option, + #[schemars(description = "Description of the preference")] description: String, }, #[serde(rename = "enum")] - #[schemars(description = "An enum preference with selectable values.")] + #[schemars(description = "An enum preference with selectable values")] Enum { + #[schemars(description = "Unique identifier of the preference, can only contain letters and numbers")] id: String, + #[schemars(description = "Display name of the preference")] name: String, + #[schemars(description = "Default value")] default: Option, + #[schemars(description = "Description of the preference")] description: String, + #[schemars(description = "List of allowed enum values")] enum_values: Vec, }, #[serde(rename = "bool")] - #[schemars(description = "A boolean preference.")] + #[schemars(description = "A boolean preference")] Bool { + #[schemars(description = "Unique identifier of the preference, can only contain letters and numbers")] id: String, + #[schemars(description = "Display name of the preference")] name: String, + #[schemars(description = "Default value")] default: Option, + #[schemars(description = "Description of the preference")] description: String, }, #[serde(rename = "list_of_strings")] - #[schemars(description = "A list of strings preference.")] + #[schemars(description = "A list of strings preference")] ListOfStrings { + #[schemars(description = "Unique identifier of the preference, can only contain letters and numbers")] id: String, + #[schemars(description = "Display name of the preference")] name: String, // default: Option>, + #[schemars(description = "Description of the preference")] description: String, }, #[serde(rename = "list_of_numbers")] - #[schemars(description = "A list of numbers preference.")] + #[schemars(description = "A list of numbers preference")] ListOfNumbers { + #[schemars(description = "Unique identifier of the preference, can only contain letters and numbers")] id: String, + #[schemars(description = "Display name of the preference")] name: String, // default: Option>, + #[schemars(description = "Description of the preference")] description: String, }, #[serde(rename = "list_of_enums")] - #[schemars(description = "A list of enumerated preference values.")] + #[schemars(description = "A list of enumerated preference values")] ListOfEnums { + #[schemars(description = "Unique identifier of the preference, can only contain letters and numbers")] id: String, + #[schemars(description = "Display name of the preference")] name: String, // default: Option>, + #[schemars(description = "List of allowed enum values")] enum_values: Vec, + #[schemars(description = "Description of the preference")] description: String, }, } #[derive(Debug, Deserialize, Serialize, JsonSchema)] -#[schemars(description = "An enumerated preference value.")] +#[schemars(description = "Definition of the values available in enumerated preference")] pub struct PluginManifestPreferenceEnumValue { + #[schemars(description = "Displayed name")] pub label: String, + #[schemars(description = "Internal enum value")] pub value: String, } #[derive(Debug, Deserialize, Serialize, JsonSchema)] -#[schemars(description = "Types of plugin entrypoints.")] +#[schemars(description = "Types of plugin entrypoints")] pub enum PluginManifestEntrypointTypes { #[serde(rename = "command")] - #[schemars(description = "A command entrypoint.")] + #[schemars(description = "A function-based entrypoint")] Command, #[serde(rename = "view")] - #[schemars(description = "A view-based entrypoint.")] + #[schemars(description = "A view-based entrypoint")] View, #[serde(rename = "inline-view")] - #[schemars(description = "An inline view entrypoint.")] + #[schemars(description = "A view-based entrypoint displayed under main search bar")] InlineView, #[serde(rename = "entrypoint-generator")] - #[schemars(description = "Generates new entrypoints dynamically.")] + #[schemars(description = "Entrypoint that dynamically generatepointsd")] EntrypointGenerator, } #[derive(Debug, Deserialize, Serialize, JsonSchema)] -#[schemars(description = "Action that can be performed by the plugin.")] +#[schemars(description = "Action definition")] pub struct PluginManifestAction { - #[schemars(description = "Unique identifier for the action.")] + #[schemars(description = "Unique identifier for the action, can only contain letters and numbers")] pub id: String, - #[schemars(description = "Description of what the action does.")] + #[schemars(description = "Description of what the action does")] pub description: String, - #[schemars(description = "Keyboard shortcut to trigger the action.")] + #[schemars(description = "Default keyboard shortcut to trigger the action")] pub shortcut: PluginManifestActionShortcut, } #[derive(Debug, Deserialize, Serialize, JsonSchema)] -#[schemars(description = "Keyboard shortcut configuration for a plugin action.")] +#[schemars(description = "Keyboard shortcut for a plugin action")] pub struct PluginManifestActionShortcut { - #[schemars(description = "The key to be pressed for this shortcut.")] + #[schemars(description = "The main key to be pressed for this shortcut")] pub key: PluginManifestActionShortcutKey, - #[schemars(description = "The type of shortcut.")] + #[schemars(description = "The kind of shortcut, defines required modifiers")] pub kind: PluginManifestActionShortcutKind, } @@ -443,13 +481,13 @@ impl PluginManifestActionShortcutKey { } #[derive(Debug, Deserialize, Serialize, JsonSchema)] -#[schemars(description = "The type of shortcut.")] +#[schemars(description = "The kind of shortcut")] pub enum PluginManifestActionShortcutKind { #[serde(rename = "main")] - #[schemars(description = "Main shortcut for the action (e.g. cmd).")] + #[schemars(description = "Main kind shortcuts require Ctrl modifier on Windows/Linux or Cmd on macOS")] Main, #[serde(rename = "alternative")] - #[schemars(description = "Alternative shortcut for the action (e.g. opt).")] + #[schemars(description = "Alternative kind shortcuts require Alt modifier on Windows/Linux or Opt on macOS")] Alternative, } @@ -475,73 +513,73 @@ impl std::fmt::Display for PluginManifestSupportedSystem { } #[derive(Debug, Deserialize, Serialize, JsonSchema)] -#[schemars(description = "Metadata for the plugin manifest.")] +#[schemars(description = "General plugin metadata")] pub struct PluginManifestMetadata { - #[schemars(description = "Name of the plugin.")] + #[schemars(description = "Name of the plugin")] pub name: String, - #[schemars(description = "Description of the plugin.")] + #[schemars(description = "Description of the plugin")] pub description: String, } #[derive(Debug, Deserialize, Default, Serialize, JsonSchema)] -#[schemars(description = "Permissions required by the plugin.")] +#[schemars(description = "Permissions required by the plugin")] pub struct PluginManifestPermissions { #[serde(default)] - #[schemars(description = "Environment variables that the plugin can access.")] + #[schemars(description = "Environment variables that the plugin can access")] pub environment: Vec, #[serde(default)] - #[schemars(description = "Network domains that the plugin can access.")] + #[schemars(description = "Network address (domain or ip address + optional port) that the plugin can access")] pub network: Vec, #[serde(default)] - #[schemars(description = "Filesystem permissions for the plugin.")] + #[schemars(description = "Filesystem permissions for the plugin")] pub filesystem: PluginManifestPermissionsFileSystem, #[serde(default)] - #[schemars(description = "Execution permissions for the plugin.")] + #[schemars(description = "Execution permissions for the plugin")] pub exec: PluginManifestPermissionsExec, #[serde(default)] - #[schemars(description = "System permissions for the plugin.")] + #[schemars(description = "Deno system permissions for the plugin")] pub system: Vec, #[serde(default)] - #[schemars(description = "Clipboard permissions for the plugin.")] + #[schemars(description = "Clipboard permissions for the plugin")] pub clipboard: Vec, #[serde(default)] - #[schemars(description = "Permissions for the main search bar.")] + #[schemars(description = "Permissions for the main search bar")] pub main_search_bar: Vec, } #[derive(Debug, Deserialize, Default, Serialize, JsonSchema)] -#[schemars(description = "Filesystem permissions for the plugin.")] +#[schemars(description = "Filesystem permissions for the plugin")] pub struct PluginManifestPermissionsFileSystem { #[serde(default)] - #[schemars(description = "Paths that the plugin can read from.")] + #[schemars(description = "Paths that the plugin can read from")] pub read: Vec, #[serde(default)] - #[schemars(description = "Paths that the plugin can write to.")] + #[schemars(description = "Paths that the plugin can write to")] pub write: Vec, } #[derive(Debug, Deserialize, Default, Serialize, JsonSchema)] -#[schemars(description = "Execution permissions for the plugin.")] +#[schemars(description = "Execution permissions for the plugin")] pub struct PluginManifestPermissionsExec { #[serde(default)] - #[schemars(description = "Commands that the plugin can execute.")] + #[schemars(description = "List of commands on PATH that the plugin can execute")] pub command: Vec, #[serde(default)] - #[schemars(description = "Executables that the plugin can run.")] + #[schemars(description = "List of paths to executables that the plugin can run")] pub executable: Vec, } #[derive(Debug, Deserialize, Serialize, JsonSchema)] -#[schemars(description = "Clipboard permissions for the plugin.")] +#[schemars(description = "Clipboard permissions for the plugin")] pub enum PluginManifestClipboardPermissions { #[serde(rename = "read")] - #[schemars(description = "Allows the plugin to read from the clipboard.")] + #[schemars(description = "Allows the plugin to read from the clipboard")] Read, #[serde(rename = "write")] - #[schemars(description = "Allows the plugin to write to the clipboard.")] + #[schemars(description = "Allows the plugin to write to the clipboard")] Write, #[serde(rename = "clear")] - #[schemars(description = "Allows the plugin to clear the clipboard contents.")] + #[schemars(description = "Allows the plugin to clear the clipboard contents")] Clear, } From 3d962f34050de0eae247f5edaa802d76804eb80f Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 16 Mar 2025 11:56:28 +0100 Subject: [PATCH 038/170] Add authors field to plugin manifest --- rust/server/src/plugins/plugin_manifest.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rust/server/src/plugins/plugin_manifest.rs b/rust/server/src/plugins/plugin_manifest.rs index 6e8726e..00f9af0 100644 --- a/rust/server/src/plugins/plugin_manifest.rs +++ b/rust/server/src/plugins/plugin_manifest.rs @@ -519,6 +519,21 @@ pub struct PluginManifestMetadata { pub name: String, #[schemars(description = "Description of the plugin")] pub description: String, + #[schemars(description = "Description of the plugin")] + #[serde(default)] + pub authors: Vec, +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[schemars(description = "Plugin author")] +pub struct PluginManifestMetadataAuthor { + #[schemars(description = "Author name")] + pub name: String, + #[schemars( + description = "URIs that identify the author. Can be a link to social media page or an email (if email it should begin with mailto: schema)" + )] + #[serde(default)] + pub uris: Vec, } #[derive(Debug, Deserialize, Default, Serialize, JsonSchema)] From b47906fbad6ccc6b8df42a689fb8120b170f113d Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 16 Mar 2025 12:08:16 +0100 Subject: [PATCH 039/170] Fix author field schema description --- docs/schema/plugin_manifest.schema.json | 29 ++++++++++++++++++++++ rust/server/src/plugins/plugin_manifest.rs | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/schema/plugin_manifest.schema.json b/docs/schema/plugin_manifest.schema.json index 11f9fbd..7189275 100644 --- a/docs/schema/plugin_manifest.schema.json +++ b/docs/schema/plugin_manifest.schema.json @@ -377,6 +377,14 @@ "name" ], "properties": { + "authors": { + "description": "List of plugin authors", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/PluginManifestMetadataAuthor" + } + }, "description": { "description": "Description of the plugin", "type": "string" @@ -387,6 +395,27 @@ } } }, + "PluginManifestMetadataAuthor": { + "description": "Plugin author", + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "Author name", + "type": "string" + }, + "uris": { + "description": "URIs that identify the author. Can be a link to social media page or an email (if email it should begin with mailto: schema)", + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, "PluginManifestPermissions": { "description": "Permissions required by the plugin", "type": "object", diff --git a/rust/server/src/plugins/plugin_manifest.rs b/rust/server/src/plugins/plugin_manifest.rs index 00f9af0..133bb25 100644 --- a/rust/server/src/plugins/plugin_manifest.rs +++ b/rust/server/src/plugins/plugin_manifest.rs @@ -519,7 +519,7 @@ pub struct PluginManifestMetadata { pub name: String, #[schemars(description = "Description of the plugin")] pub description: String, - #[schemars(description = "Description of the plugin")] + #[schemars(description = "List of plugin authors")] #[serde(default)] pub authors: Vec, } From aacf78a377593c11c93732e7356c22f0ab378a99 Mon Sep 17 00:00:00 2001 From: DaRacci Date: Thu, 20 Mar 2025 23:20:00 +1100 Subject: [PATCH 040/170] fix(flake): update npmDepsHash --- nix/overlay.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/overlay.nix b/nix/overlay.nix index 0918940..37cf374 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -6,7 +6,7 @@ flake.overlays.default = final: _: let # These must be updated following the instructions in ./nix/README.md when dependencies are updated or the version bumped version = "v17"; - npmDepsHash = "sha256-rk5aLdXoSpqMNcSVIRJTKN1KqtddVPiKkZ1YWZ+n5m8="; + npmDepsHash = "sha256-BOnKpFS0ofIZbmXcyEzHzHgvbgeg3QUXnC79BwKO6P8="; RUSTY_V8_ARCHIVE = fetchRustyV8 "130.0.2" { aarch64-darwin = "sha256-aWZ/4Q4Wttx37xOdBmTCPGP+eYGhr4CM1UkYq8pC7Qs="; aarch64-linux = "sha256-p9+tHmKIM5wBABubHIAstpwfzO19ypPzOuaV4b6loCU="; From ae0f833f15e22b3757a63f303aef6158445faf8d Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 30 Mar 2025 19:25:02 +0200 Subject: [PATCH 041/170] Rework shortcut selector ui in settings --- rust/client/src/ui/widget_container.rs | 2 +- .../src/components/shortcut_selector.rs | 363 +++++++++++++----- rust/management_client/src/ui.rs | 30 +- rust/management_client/src/views/general.rs | 136 +++---- rust/management_client/src/views/plugins.rs | 156 ++++---- .../src/views/plugins/table.rs | 19 +- 6 files changed, 416 insertions(+), 290 deletions(-) diff --git a/rust/client/src/ui/widget_container.rs b/rust/client/src/ui/widget_container.rs index 3aa92f0..8416711 100644 --- a/rust/client/src/ui/widget_container.rs +++ b/rust/client/src/ui/widget_container.rs @@ -76,7 +76,7 @@ impl PluginWidgetContainer { self.images = images; // use new state with values from old state but only widget ids which exists in new state - // so we this way we use already existing values but remove state for removed widgets + // so this way we use already existing values but remove state for removed widgets let old_state = mem::replace(&mut self.state, create_state(&container)); for (key, value) in old_state.into_iter() { diff --git a/rust/management_client/src/components/shortcut_selector.rs b/rust/management_client/src/components/shortcut_selector.rs index df54fe5..f46dcdc 100644 --- a/rust/management_client/src/components/shortcut_selector.rs +++ b/rust/management_client/src/components/shortcut_selector.rs @@ -1,10 +1,13 @@ +use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalShortcut; +use gauntlet_common::model::PluginId; use gauntlet_common_ui::physical_key_model; use gauntlet_common_ui::shortcut_to_text; use iced::advanced::graphics::core::event; use iced::advanced::graphics::core::keyboard; use iced::advanced::layout; use iced::advanced::mouse; +use iced::advanced::overlay; use iced::advanced::renderer; use iced::advanced::widget::tree; use iced::advanced::widget::Tree; @@ -12,95 +15,123 @@ use iced::advanced::Clipboard; use iced::advanced::Layout; use iced::advanced::Shell; use iced::advanced::Widget; -use iced::alignment; use iced::keyboard::key::Physical; use iced::mouse::Button; +use iced::widget::column; use iced::widget::container; use iced::widget::container::draw_background; -use iced::widget::container::layout; use iced::widget::row; use iced::widget::text; -use iced::Element; +use iced::Alignment; use iced::Event; use iced::Length; use iced::Padding; +use iced::Point; use iced::Rectangle; use iced::Renderer; use iced::Size; +use iced::Vector; -pub struct ShortcutSelector<'a, Message, Theme> -where - Theme: Catalog + text::Catalog + container::Catalog, -{ - padding: Padding, - width: Length, - height: Length, - max_width: f32, - max_height: f32, - horizontal_alignment: alignment::Horizontal, - vertical_alignment: alignment::Vertical, +use crate::theme::text::TextStyle; +use crate::theme::Element; +use crate::theme::GauntletSettingsTheme; - on_shortcut_captured: Box) -> Message + 'a>, - on_capturing_change: Box Message + 'a>, - - content: Element<'a, Message, Theme>, +pub struct ShortcutData { + pub shortcut: Option, + pub error: Option, } -impl<'a, Message: 'a, Theme> ShortcutSelector<'a, Message, Theme> +pub fn shortcut_selector<'a, 'b: 'a, 'c, Message: 'a, Id: 'a, F>( + shortcut_id: Id, + current_shortcut: &'b ShortcutData, + on_shortcut_captured: F, + overlay_class: ::Class<'a>, +) -> Element<'a, Message> where - Theme: Catalog + text::Catalog + container::Catalog + 'a, + F: 'a + Fn(Id, Option) -> Message, + Id: Clone, { - pub fn new( - current_shortcut: &Option, + Element::new(ShortcutSelector::new::( + shortcut_id, + current_shortcut, + on_shortcut_captured, + overlay_class, + )) +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum ShortcutId { + Global, + Entrypoint { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + }, +} + +pub struct ShortcutSelector<'a, 'b, Message, Id> +where + Id: Clone, +{ + on_shortcut_captured: Box) -> Message + 'a>, + + shortcut_id: Id, + current_shortcut: &'b ShortcutData, + + content: Element<'a, Message>, + popup: Element<'a, Message>, + overlay_class: ::Class<'a>, +} + +impl<'a, 'b, 'c, Message: 'a, Id> ShortcutSelector<'a, 'b, Message, Id> +where + Id: Clone, +{ + pub fn new( + shortcut_id: Id, + current_shortcut: &'b ShortcutData, on_shortcut_captured: F, - on_capturing_change: F2, + overlay_class: ::Class<'a>, ) -> Self where - F: 'a + Fn(Option) -> Message, - F2: 'a + Fn(bool) -> Message, + F: 'a + Fn(Id, Option) -> Message, { - let mut content: Vec> = vec![]; + let content = render_shortcut(¤t_shortcut.shortcut); - if let Some(current_shortcut) = current_shortcut { - let (key_name, alt_modifier_text, meta_modifier_text, control_modifier_text, shift_modifier_text) = - shortcut_to_text(current_shortcut); + let content = container(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x(Length::Fill) + .center_y(Length::Fill) + .into(); - if let Some(meta_modifier_text) = meta_modifier_text { - content.push(meta_modifier_text); - } + let recording_text: Element<_> = text("Recording shortcut...").into(); - if let Some(control_modifier_text) = control_modifier_text { - content.push(control_modifier_text); - } + let backspace_test: Element<_> = text("Backspace - Unset Shortcut").class(TextStyle::Subtitle).into(); - if let Some(shift_modifier_text) = shift_modifier_text { - content.push(shift_modifier_text); - } + let escape_test: Element<_> = text("Escape - Stop Capturing").class(TextStyle::Subtitle).into(); - if let Some(alt_modifier_text) = alt_modifier_text { - content.push(alt_modifier_text); - } + let popup: Element<_> = column(vec![recording_text, backspace_test, escape_test]) + .align_x(Alignment::Center) + .into(); - content.push(key_name); - } - - let content: Element<_, _> = row(content).spacing(8.0).into(); - - let content = container(content).into(); + let popup = container(popup) + .max_height(80) + .center_x(Length::Fill) + .center_y(Length::Fill) + .max_width(300) + .width(Length::Fill) + .height(Length::Fill) + .into(); Self { - padding: Padding::ZERO, - width: Length::Fill, - height: Length::Fill, - max_width: f32::INFINITY, - max_height: f32::INFINITY, - horizontal_alignment: alignment::Horizontal::Center, - vertical_alignment: alignment::Vertical::Center, - on_shortcut_captured: Box::new(on_shortcut_captured), - on_capturing_change: Box::new(on_capturing_change), + shortcut_id, + current_shortcut, content, + popup, + + overlay_class, } } } @@ -110,36 +141,26 @@ struct State { is_capturing: bool, } -impl<'a, Message, Theme> Widget for ShortcutSelector<'a, Message, Theme> +impl<'a, 'b, Message: 'a, Id> Widget for ShortcutSelector<'a, 'b, Message, Id> where - Theme: Catalog + text::Catalog + container::Catalog, + Id: Clone, { fn size(&self) -> Size { Size { - width: self.width, - height: self.height, + width: Length::Fill, + height: Length::Fill, } } fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) -> layout::Node { - layout( - limits, - self.width, - self.height, - self.max_width, - self.max_height, - self.padding, - self.horizontal_alignment, - self.vertical_alignment, - |limits| self.content.as_widget().layout(tree, renderer, limits), - ) + self.content.as_widget().layout(&mut tree.children[0], renderer, limits) } fn draw( &self, tree: &Tree, renderer: &mut Renderer, - theme: &Theme, + theme: &GauntletSettingsTheme, renderer_style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, @@ -153,18 +174,16 @@ where Status::Active }; - let style = Catalog::style(theme, &::default(), style); + let style = Catalog::style(theme, &::default(), style); draw_background(renderer, &style, layout.bounds()); self.content.as_widget().draw( - tree, + &tree.children[0], renderer, theme, - &renderer::Style { - text_color: renderer_style.text_color, - }, - layout.children().next().unwrap(), + renderer_style, + layout, cursor, viewport, ); @@ -179,11 +198,11 @@ where } fn children(&self) -> Vec { - self.content.as_widget().children() + vec![Tree::new(&self.content), Tree::new(&self.popup)] } fn diff(&self, tree: &mut Tree) { - self.content.as_widget().diff(tree); + tree.diff_children(&[self.content.as_widget(), self.popup.as_widget()]); } fn on_event( @@ -211,21 +230,18 @@ where match physical_key { Physical::Code(code) => { match code { - keyboard::key::Code::Backspace => { + keyboard::key::Code::Backspace if modifiers.is_empty() => { state.is_capturing = false; - let message = (self.on_capturing_change)(false); - shell.publish(message); - - let message = (self.on_shortcut_captured)(None); + let message = (self.on_shortcut_captured)(self.shortcut_id.clone(), None); shell.publish(message); event::Status::Ignored } - keyboard::key::Code::Escape => { + keyboard::key::Code::Escape if modifiers.is_empty() => { state.is_capturing = false; - let message = (self.on_capturing_change)(false); + let message = (self.on_shortcut_captured)(self.shortcut_id.clone(), None); shell.publish(message); event::Status::Ignored @@ -236,10 +252,10 @@ where Some(shortcut) => { state.is_capturing = false; - let message = (self.on_capturing_change)(false); - shell.publish(message); - - let message = (self.on_shortcut_captured)(Some(shortcut)); + let message = (self.on_shortcut_captured)( + self.shortcut_id.clone(), + Some(shortcut), + ); shell.publish(message); event::Status::Captured @@ -263,16 +279,10 @@ where if cursor.is_over(layout.bounds()) { state.is_capturing = true; - let message = (self.on_capturing_change)(true); - shell.publish(message); - event::Status::Captured } else { state.is_capturing = false; - let message = (self.on_capturing_change)(false); - shell.publish(message); - event::Status::Ignored } } @@ -297,15 +307,40 @@ where mouse::Interaction::default() } } -} -impl<'a, Message, Theme> From> for Element<'a, Message, Theme> -where - Message: 'a, - Theme: Catalog + text::Catalog + container::Catalog + 'a, -{ - fn from(shortcut_selector: ShortcutSelector<'a, Message, Theme>) -> Self { - Self::new(shortcut_selector) + fn overlay<'c>( + &'c mut self, + tree: &'c mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + translation: Vector, + ) -> Option> { + let state = tree.state.downcast_ref::(); + + let mut children = tree.children.iter_mut(); + + let content = self + .content + .as_widget_mut() + .overlay(children.next().unwrap(), layout, renderer, translation); + + let popup = if state.is_capturing { + Some(overlay::Element::new(Box::new(Overlay { + position: layout.position() + translation, + popup: &self.popup, + state: children.next().unwrap(), + content_bounds: layout.bounds(), + class: &self.overlay_class, + }))) + } else { + None + }; + + if content.is_some() || popup.is_some() { + Some(overlay::Group::with_children(content.into_iter().chain(popup).collect()).overlay()) + } else { + None + } } } @@ -322,3 +357,121 @@ pub trait Catalog { fn style(&self, class: &Self::Class<'_>, status: Status) -> container::Style; } + +pub fn render_shortcut<'a, Message: 'a>(shortcut: &Option) -> Element<'a, Message> { + let mut content: Vec> = vec![]; + + if let Some(current_shortcut) = shortcut { + let (key_name, alt_modifier_text, meta_modifier_text, control_modifier_text, shift_modifier_text) = + shortcut_to_text(current_shortcut); + + if let Some(meta_modifier_text) = meta_modifier_text { + content.push(meta_modifier_text); + } + + if let Some(control_modifier_text) = control_modifier_text { + content.push(control_modifier_text); + } + + if let Some(shift_modifier_text) = shift_modifier_text { + content.push(shift_modifier_text); + } + + if let Some(alt_modifier_text) = alt_modifier_text { + content.push(alt_modifier_text); + } + + content.push(key_name); + } + + let content: Element = row(content).spacing(8.0).into(); + + content +} + +struct Overlay<'a, 'b, Message> { + position: Point, + popup: &'b Element<'a, Message>, + state: &'b mut Tree, + content_bounds: Rectangle, + class: &'b ::Class<'a>, +} + +impl<'a, 'b, Message> overlay::Overlay for Overlay<'a, 'b, Message> { + fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node { + let padding = 2.0; + let gap = 10.0; + + let viewport = Rectangle::with_size(bounds); + + let popup_layout = self.popup.as_widget().layout( + self.state, + renderer, + &layout::Limits::new(Size::ZERO, viewport.size()).shrink(Padding::new(padding)), + ); + + let text_bounds = popup_layout.bounds(); + let x_center = self.position.x + (self.content_bounds.width - text_bounds.width) / 2.0; + + let mut tooltip_bounds = { + let offset = Vector::new(x_center, self.position.y + self.content_bounds.height + gap + padding); + + Rectangle { + x: offset.x - padding, + y: offset.y - padding, + width: text_bounds.width + padding * 2.0, + height: text_bounds.height + padding * 2.0, + } + }; + + // snap_within_viewport + if tooltip_bounds.x < viewport.x { + tooltip_bounds.x = viewport.x; + } else if viewport.x + viewport.width < tooltip_bounds.x + tooltip_bounds.width { + tooltip_bounds.x = viewport.x + viewport.width - tooltip_bounds.width; + } + + if tooltip_bounds.y < viewport.y { + tooltip_bounds.y = viewport.y; + } else if viewport.y + viewport.height < tooltip_bounds.y + tooltip_bounds.height { + tooltip_bounds.y = viewport.y + viewport.height - tooltip_bounds.height; + } + + layout::Node::with_children( + tooltip_bounds.size(), + vec![popup_layout.translate(Vector::new(padding, padding))], + ) + .translate(Vector::new(tooltip_bounds.x, tooltip_bounds.y)) + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &GauntletSettingsTheme, + inherited_style: &renderer::Style, + layout: Layout<'_>, + cursor_position: mouse::Cursor, + ) { + let style = ::style(theme, self.class); + + draw_background(renderer, &style, layout.bounds()); + + let defaults = renderer::Style { + text_color: style.text_color.unwrap_or(inherited_style.text_color), + }; + + self.popup.as_widget().draw( + self.state, + renderer, + theme, + &defaults, + layout.children().next().unwrap(), + cursor_position, + &Rectangle::with_size(Size::INFINITY), + ); + } + + fn is_over(&self, _layout: Layout<'_>, _renderer: &Renderer, _cursor_position: Point) -> bool { + false + } +} diff --git a/rust/management_client/src/ui.rs b/rust/management_client/src/ui.rs index 7d6fdcf..79436ba 100644 --- a/rust/management_client/src/ui.rs +++ b/rust/management_client/src/ui.rs @@ -79,7 +79,7 @@ struct ManagementAppModel { } #[derive(Debug, Clone)] -enum ManagementAppMsg { +pub enum ManagementAppMsg { FontLoaded(Result<(), font::Error>), General(ManagementAppGeneralMsgIn), Plugin(ManagementAppPluginMsgIn), @@ -191,34 +191,16 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task { state.plugins_state.update(message).map(|msg| { match msg { - ManagementAppPluginMsgOut::PluginsReloaded(plugins) => { - ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::PluginsFetched(plugins)) - } - ManagementAppPluginMsgOut::Noop => ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::Noop), - ManagementAppPluginMsgOut::DownloadPlugin { plugin_id } => { - ManagementAppMsg::DownloadPlugin { plugin_id } - } - ManagementAppPluginMsgOut::SelectedItem(selected_item) => { - ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::SelectItem(selected_item)) - } - ManagementAppPluginMsgOut::HandleBackendError(err) => ManagementAppMsg::HandleBackendError(err), + ManagementAppPluginMsgOut::Inner(msg) => ManagementAppMsg::Plugin(msg), + ManagementAppPluginMsgOut::Outer(msg) => msg, } }) } ManagementAppMsg::General(message) => { state.general_state.update(message).map(|msg| { match msg { - ManagementAppGeneralMsgOut::Noop => ManagementAppMsg::General(ManagementAppGeneralMsgIn::Noop), - ManagementAppGeneralMsgOut::HandleBackendError(err) => ManagementAppMsg::HandleBackendError(err), - ManagementAppGeneralMsgOut::SetGlobalShortcutResponse { - shortcut, - shortcut_error, - } => { - ManagementAppMsg::General(ManagementAppGeneralMsgIn::SetGlobalShortcutResponse { - shortcut, - shortcut_error, - }) - } + ManagementAppGeneralMsgOut::Inner(msg) => ManagementAppMsg::General(msg), + ManagementAppGeneralMsgOut::Outer(msg) => msg, } }) } @@ -266,7 +248,7 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task, theme: SettingsTheme, window_position_mode: WindowPositionMode, - current_shortcut: Option, - current_shortcut_error: Option, - currently_capturing: bool, + current_shortcut: ShortcutData, } #[derive(Debug, Clone)] pub enum ManagementAppGeneralMsgIn { - ShortcutCaptured(Option), - CapturingChanged(bool), + ShortcutCaptured(ShortcutId, Option), ThemeChanged(SettingsTheme), WindowPositionModeChanged(WindowPositionMode), - SetGlobalShortcutResponse { + HandleShortcutResponse { + id: ShortcutId, shortcut: Option, shortcut_error: Option, }, @@ -57,12 +58,8 @@ pub enum ManagementAppGeneralMsgIn { #[derive(Debug, Clone)] pub enum ManagementAppGeneralMsgOut { - Noop, - SetGlobalShortcutResponse { - shortcut: Option, - shortcut_error: Option, - }, - HandleBackendError(BackendApiError), + Inner(ManagementAppGeneralMsgIn), + Outer(ManagementAppMsg), } impl ManagementAppGeneralState { @@ -71,9 +68,10 @@ impl ManagementAppGeneralState { backend_api, theme: SettingsTheme::AutoDetect, window_position_mode: WindowPositionMode::Static, - current_shortcut: None, - current_shortcut_error: None, - currently_capturing: false, + current_shortcut: ShortcutData { + shortcut: None, + error: None, + }, } } @@ -84,7 +82,7 @@ impl ManagementAppGeneralState { }; match message { - ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut) => { + ManagementAppGeneralMsgIn::ShortcutCaptured(id, shortcut) => { let mut backend_api = backend_api.clone(); Task::perform( @@ -99,12 +97,14 @@ impl ManagementAppGeneralState { }, move |result| { let shortcut = shortcut.clone(); + let id = id.clone(); handle_backend_error(result, move |shortcut_error| { - ManagementAppGeneralMsgOut::SetGlobalShortcutResponse { + ManagementAppGeneralMsgOut::Inner(ManagementAppGeneralMsgIn::HandleShortcutResponse { + id, shortcut, shortcut_error, - } + }) }) }, ) @@ -118,13 +118,10 @@ impl ManagementAppGeneralState { } => { self.theme = theme; self.window_position_mode = window_position_mode; - self.current_shortcut = shortcut; - self.current_shortcut_error = shortcut_error; - - Task::done(ManagementAppGeneralMsgOut::Noop) - } - ManagementAppGeneralMsgIn::CapturingChanged(capturing) => { - self.currently_capturing = capturing; + self.current_shortcut = ShortcutData { + shortcut, + error: shortcut_error, + }; Task::none() } @@ -139,7 +136,9 @@ impl ManagementAppGeneralState { Ok(()) }, - |result| handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Noop), + |result| { + handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::Noop)) + }, ) } ManagementAppGeneralMsgIn::WindowPositionModeChanged(mode) => { @@ -153,15 +152,20 @@ impl ManagementAppGeneralState { Ok(()) }, - |result| handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Noop), + |result| { + handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::Noop)) + }, ) } - ManagementAppGeneralMsgIn::SetGlobalShortcutResponse { + ManagementAppGeneralMsgIn::HandleShortcutResponse { + id, shortcut, shortcut_error, } => { - self.current_shortcut = shortcut; - self.current_shortcut_error = shortcut_error; + self.current_shortcut = ShortcutData { + shortcut, + error: shortcut_error, + }; Task::none() } @@ -169,12 +173,12 @@ impl ManagementAppGeneralState { } pub fn view(&self) -> Element { - let global_shortcut_selector: Element<_> = ShortcutSelector::new( + let global_shortcut_selector = shortcut_selector( + ShortcutId::Global, &self.current_shortcut, - move |value| ManagementAppGeneralMsgIn::ShortcutCaptured(value), - move |value| ManagementAppGeneralMsgIn::CapturingChanged(value), - ) - .into(); + move |id, shortcut| ManagementAppGeneralMsgIn::ShortcutCaptured(id, shortcut), + ContainerStyle::Box, + ); let global_shortcut_field: Element<_> = container(global_shortcut_selector) .width(Length::Fill) @@ -200,8 +204,6 @@ impl ManagementAppGeneralState { let content: Element<_> = container(content).width(Length::Fill).into(); - let content: Element<_> = container(content).width(Length::Fill).into(); - content } @@ -290,59 +292,41 @@ impl ManagementAppGeneralState { } fn shortcut_capture_after(&self) -> Element { - if self.currently_capturing { - let hint1: Element<_> = text("Backspace - Unset Shortcut") - .width(Length::Fill) - .class(TextStyle::Subtitle) + if let Some(current_shortcut_error) = &self.current_shortcut.error { + let error_icon: Element<_> = value(Bootstrap::ExclamationTriangleFill) + .font(BOOTSTRAP_FONT) + .class(TextStyle::Destructive) .into(); - let hint2: Element<_> = text("Escape - Stop Capturing") - .width(Length::Fill) - .class(TextStyle::Subtitle) + let error_text: Element<_> = text(current_shortcut_error).class(TextStyle::Destructive).into(); + + let error_text: Element<_> = container(error_text) + .padding(16.0) + .max_width(300) + .class(ContainerStyle::Box) .into(); - column(vec![hint1, hint2]) + let tooltip: Element<_> = tooltip(error_icon, error_text, Position::Bottom).into(); + + let content = container(tooltip) .width(Length::FillPortion(3)) - .align_x(Alignment::Center) + .align_y(alignment::Vertical::Center) .padding(Padding::from([0.0, 8.0])) - .into() + .into(); + + content } else { - if let Some(current_shortcut_error) = &self.current_shortcut_error { - let error_icon: Element<_> = value(Bootstrap::ExclamationTriangleFill) - .font(BOOTSTRAP_FONT) - .class(TextStyle::Destructive) - .into(); - - let error_text: Element<_> = text(current_shortcut_error).class(TextStyle::Destructive).into(); - - let error_text: Element<_> = container(error_text) - .padding(16.0) - .max_width(300) - .class(ContainerStyle::Box) - .into(); - - let tooltip: Element<_> = tooltip(error_icon, error_text, Position::Bottom).into(); - - let content = container(tooltip) - .width(Length::FillPortion(3)) - .align_y(alignment::Vertical::Center) - .padding(Padding::from([0.0, 8.0])) - .into(); - - content - } else { - Space::with_width(Length::FillPortion(3)).into() - } + Space::with_width(Length::FillPortion(3)).into() } } } -pub fn handle_backend_error( +fn handle_backend_error( result: Result, convert: impl FnOnce(T) -> ManagementAppGeneralMsgOut, ) -> ManagementAppGeneralMsgOut { match result { Ok(val) => convert(val), - Err(err) => ManagementAppGeneralMsgOut::HandleBackendError(err), + Err(err) => ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::HandleBackendError(err)), } } diff --git a/rust/management_client/src/views/plugins.rs b/rust/management_client/src/views/plugins.rs index 9147edb..5cf806a 100644 --- a/rust/management_client/src/views/plugins.rs +++ b/rust/management_client/src/views/plugins.rs @@ -32,13 +32,13 @@ use iced_fonts::BOOTSTRAP_FONT; use crate::theme::button::ButtonStyle; use crate::theme::text::TextStyle; use crate::theme::Element; +use crate::ui::ManagementAppMsg; use crate::views::plugins::preferences::preferences_ui; use crate::views::plugins::preferences::PluginPreferencesMsg; use crate::views::plugins::preferences::SelectItem; use crate::views::plugins::table::PluginTableMsgIn; use crate::views::plugins::table::PluginTableMsgOut; use crate::views::plugins::table::PluginTableState; -use crate::views::plugins::table::PluginTableUpdateResult; mod preferences; mod table; @@ -48,19 +48,17 @@ pub enum ManagementAppPluginMsgIn { PluginTableMsg(PluginTableMsgIn), PluginPreferenceMsg(PluginPreferencesMsg), FetchPlugins, - PluginsFetched(HashMap), + PluginsReloaded(HashMap), RemovePlugin { plugin_id: PluginId }, + ToggleShowEntrypoint { plugin_id: PluginId }, DownloadPlugin { plugin_id: PluginId }, SelectItem(SelectedItem), Noop, } pub enum ManagementAppPluginMsgOut { - PluginsReloaded(HashMap), - SelectedItem(SelectedItem), - DownloadPlugin { plugin_id: PluginId }, - HandleBackendError(BackendApiError), - Noop, + Inner(ManagementAppPluginMsgIn), + Outer(ManagementAppMsg), } pub struct ManagementAppPluginsState { @@ -99,7 +97,7 @@ impl ManagementAppPluginsState { tracing::debug!("Opening selected item: {:?}", select_item); Self { - backend_api, + backend_api: backend_api.clone(), plugin_data: Rc::new(RefCell::new(PluginDataContainer::new())), preference_user_data: HashMap::new(), selected_item: select_item, @@ -115,71 +113,77 @@ impl ManagementAppPluginsState { match message { ManagementAppPluginMsgIn::PluginTableMsg(message) => { - match self.table_state.update(message) { - PluginTableUpdateResult::Command(command) => command.map(|_| ManagementAppPluginMsgOut::Noop), - PluginTableUpdateResult::Value(msg) => { - match msg { - PluginTableMsgOut::SetPluginState { enabled, plugin_id } => { - let mut backend_client = backend_api.clone(); + self.table_state.update(message).then(move |msg| { + match msg { + PluginTableMsgOut::SetPluginState { enabled, plugin_id } => { + let mut backend_client = backend_api.clone(); - Task::perform( - async move { - backend_client.set_plugin_state(plugin_id, enabled).await?; + Task::perform( + async move { + backend_client.set_plugin_state(plugin_id, enabled).await?; - let plugins = backend_client.plugins().await?; + let plugins = backend_client.plugins().await?; - Ok(plugins) - }, - |result| { - handle_backend_error(result, |plugins| { - ManagementAppPluginMsgOut::PluginsReloaded(plugins) - }) - }, - ) - } - PluginTableMsgOut::SetEntrypointState { - enabled, - plugin_id, - entrypoint_id, - } => { - let mut backend_client = backend_api.clone(); + Ok(plugins) + }, + |result| { + handle_backend_error(result, |plugins| { + ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( + plugins, + )) + }) + }, + ) + } + PluginTableMsgOut::SetEntrypointState { + enabled, + plugin_id, + entrypoint_id, + } => { + let mut backend_client = backend_api.clone(); - Task::perform( - async move { - backend_client - .set_entrypoint_state(plugin_id, entrypoint_id, enabled) - .await?; + Task::perform( + async move { + backend_client + .set_entrypoint_state(plugin_id, entrypoint_id, enabled) + .await?; - let plugins = backend_client.plugins().await?; + let plugins = backend_client.plugins().await?; - Ok(plugins) - }, - |result| { - handle_backend_error(result, |plugins| { - ManagementAppPluginMsgOut::PluginsReloaded(plugins) - }) - }, - ) - } - PluginTableMsgOut::SelectItem(selected_item) => { - Task::done(ManagementAppPluginMsgOut::SelectedItem(selected_item)) - } - PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } => { - let plugins = { - let mut plugin_data = self.plugin_data.borrow_mut(); - let settings_plugin_data = plugin_data.plugins_state.get_mut(&plugin_id).unwrap(); - settings_plugin_data.show_entrypoints = !settings_plugin_data.show_entrypoints; - - plugin_data.plugins.clone() - }; - - self.apply_plugin_fetch(plugins); - - Task::none() - } + Ok(plugins) + }, + |result| { + handle_backend_error(result, |plugins| { + ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( + plugins, + )) + }) + }, + ) + } + PluginTableMsgOut::SelectItem(selected_item) => { + Task::done(ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::SelectItem( + selected_item, + ))) + } + PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } => { + Task::done(ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::Noop)) } } - } + }) + } + ManagementAppPluginMsgIn::ToggleShowEntrypoint { plugin_id } => { + let plugins = { + let mut plugin_data = self.plugin_data.borrow_mut(); + let settings_plugin_data = plugin_data.plugins_state.get_mut(&plugin_id).unwrap(); + settings_plugin_data.show_entrypoints = !settings_plugin_data.show_entrypoints; + + plugin_data.plugins.clone() + }; + + self.apply_plugin_fetch(plugins); + + Task::none() } ManagementAppPluginMsgIn::PluginPreferenceMsg(msg) => { match msg { @@ -204,7 +208,11 @@ impl ManagementAppPluginsState { Ok(()) }, - |result| handle_backend_error(result, |()| ManagementAppPluginMsgOut::Noop), + |result| { + handle_backend_error(result, |()| { + ManagementAppPluginMsgOut::Outer(ManagementAppMsg::Noop) + }) + }, ) } } @@ -219,11 +227,13 @@ impl ManagementAppPluginsState { Ok(plugins) }, |result| { - handle_backend_error(result, |plugins| ManagementAppPluginMsgOut::PluginsReloaded(plugins)) + handle_backend_error(result, |plugins| { + ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(plugins)) + }) }, ) } - ManagementAppPluginMsgIn::PluginsFetched(plugins) => { + ManagementAppPluginMsgIn::PluginsReloaded(plugins) => { self.apply_plugin_fetch(plugins); Task::none() @@ -242,12 +252,16 @@ impl ManagementAppPluginsState { Ok(plugins) }, |result| { - handle_backend_error(result, |plugins| ManagementAppPluginMsgOut::PluginsReloaded(plugins)) + handle_backend_error(result, |plugins| { + ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(plugins)) + }) }, ) } ManagementAppPluginMsgIn::DownloadPlugin { plugin_id } => { - Task::done(ManagementAppPluginMsgOut::DownloadPlugin { plugin_id }) + Task::done(ManagementAppPluginMsgOut::Outer(ManagementAppMsg::DownloadPlugin { + plugin_id, + })) } ManagementAppPluginMsgIn::SelectItem(selected_item) => { self.selected_item = selected_item; @@ -696,6 +710,6 @@ pub fn handle_backend_error( ) -> ManagementAppPluginMsgOut { match result { Ok(val) => convert(val), - Err(err) => ManagementAppPluginMsgOut::HandleBackendError(err), + Err(err) => ManagementAppPluginMsgOut::Outer(ManagementAppMsg::HandleBackendError(err)), } } diff --git a/rust/management_client/src/views/plugins/table.rs b/rust/management_client/src/views/plugins/table.rs index bb35d10..99438f7 100644 --- a/rust/management_client/src/views/plugins/table.rs +++ b/rust/management_client/src/views/plugins/table.rs @@ -62,11 +62,6 @@ pub struct PluginTableState { body: Id, } -pub enum PluginTableUpdateResult { - Command(Task<()>), - Value(PluginTableMsgOut), -} - impl PluginTableState { pub fn new() -> Self { Self { @@ -82,22 +77,20 @@ impl PluginTableState { } } - pub fn update(&mut self, message: PluginTableMsgIn) -> PluginTableUpdateResult { + pub fn update(&mut self, message: PluginTableMsgIn) -> Task { match message { - PluginTableMsgIn::TableSyncHeader(offset) => { - PluginTableUpdateResult::Command(scrollable::scroll_to(self.header.clone(), offset)) - } + PluginTableMsgIn::TableSyncHeader(offset) => scrollable::scroll_to(self.header.clone(), offset), PluginTableMsgIn::EnabledToggleItem(item) => { match item { EnabledItem::Plugin { enabled, plugin_id } => { - PluginTableUpdateResult::Value(PluginTableMsgOut::SetPluginState { enabled, plugin_id }) + Task::done(PluginTableMsgOut::SetPluginState { enabled, plugin_id }) } EnabledItem::Entrypoint { enabled, plugin_id, entrypoint_id, } => { - PluginTableUpdateResult::Value(PluginTableMsgOut::SetEntrypointState { + Task::done(PluginTableMsgOut::SetEntrypointState { enabled, plugin_id, entrypoint_id, @@ -105,9 +98,9 @@ impl PluginTableState { } } } - PluginTableMsgIn::SelectItem(item) => PluginTableUpdateResult::Value(PluginTableMsgOut::SelectItem(item)), + PluginTableMsgIn::SelectItem(item) => Task::done(PluginTableMsgOut::SelectItem(item)), PluginTableMsgIn::ToggleShowEntrypoints { plugin_id } => { - PluginTableUpdateResult::Value(PluginTableMsgOut::ToggleShowEntrypoints { plugin_id }) + Task::done(PluginTableMsgOut::ToggleShowEntrypoints { plugin_id }) } } } From 9f057c09dcf7484a8591ca6605dcdb907e76cdf0 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 3 Apr 2025 19:37:57 +0200 Subject: [PATCH 042/170] Show generated entrypoints in settings view --- Cargo.toml | 2 +- rust/common/src/model.rs | 7 + rust/common/src/rpc/backend_api.rs | 13 + rust/common/src/rpc/backend_server.rs | 14 + rust/management_client/src/views/plugins.rs | 152 ++++++++++- .../src/views/plugins/table.rs | 241 ++++++++++++++---- rust/server/src/plugins/js.rs | 15 +- rust/server/src/plugins/mod.rs | 55 +++- rust/server/src/search.rs | 17 +- schema/backend.proto | 5 + 10 files changed, 435 insertions(+), 86 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ad4095d..52021b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition = "2021" [workspace.dependencies] # iced #iced = { version = "0.13.99", features = ["tiny-skia", "wgpu", "tokio", "lazy", "advanced", "image"] } -iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13", default-features = false, features = ["tiny-skia", "wgpu", "tokio", "advanced", "image"] } +iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13", default-features = false, features = ["tiny-skia", "wgpu", "tokio", "advanced", "image", "web-colors"] } #iced_aw = { version = "0.11.99", features = ["date_picker", "wrap", "number_input", "grid", "spinner"] } iced_aw = { git = "https://github.com/project-gauntlet/iced_aw.git", branch = "gauntlet-0.13", default-features = false, features = ["date_picker", "wrap", "number_input", "grid", "spinner"] } #iced_table = "0.13.99" diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index 330278c..a95ec26 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -676,6 +676,13 @@ pub struct SettingsEntrypoint { pub enabled: bool, pub preferences: HashMap, pub preferences_user_data: HashMap, + pub generated_entrypoints: HashMap, +} + +#[derive(Debug, Clone)] +pub struct SettingsGeneratedEntrypoint { + pub entrypoint_id: EntrypointId, + pub entrypoint_name: String, } #[derive(Debug, Clone)] diff --git a/rust/common/src/rpc/backend_api.rs b/rust/common/src/rpc/backend_api.rs index ddcd5bd..a006288 100644 --- a/rust/common/src/rpc/backend_api.rs +++ b/rust/common/src/rpc/backend_api.rs @@ -20,6 +20,7 @@ use crate::model::PluginPreferenceUserData; use crate::model::SearchResult; use crate::model::SettingsEntrypoint; use crate::model::SettingsEntrypointType; +use crate::model::SettingsGeneratedEntrypoint; use crate::model::SettingsPlugin; use crate::model::SettingsTheme; use crate::model::UiPropertyValue; @@ -418,6 +419,18 @@ impl BackendApi { .into_iter() .map(|(key, value)| (key, plugin_preference_user_data_from_rpc(value))) .collect(), + generated_entrypoints: entrypoint + .generated_entrypoints + .into_iter() + .map(|(entrypoint_id, data)| { + let generated_entrypoint = SettingsGeneratedEntrypoint { + entrypoint_id: EntrypointId::from_string(data.entrypoint_id), + entrypoint_name: data.entrypoint_name, + }; + + (EntrypointId::from_string(entrypoint_id), generated_entrypoint) + }) + .collect(), }; (id, entrypoint) }) diff --git a/rust/common/src/rpc/backend_server.rs b/rust/common/src/rpc/backend_server.rs index f03cfc3..9da185c 100644 --- a/rust/common/src/rpc/backend_server.rs +++ b/rust/common/src/rpc/backend_server.rs @@ -29,6 +29,7 @@ use crate::rpc::grpc::RpcDownloadStatusResponse; use crate::rpc::grpc::RpcDownloadStatusValue; use crate::rpc::grpc::RpcEntrypoint; use crate::rpc::grpc::RpcEntrypointTypeSettings; +use crate::rpc::grpc::RpcGeneratedEntrypoint; use crate::rpc::grpc::RpcGetGlobalShortcutRequest; use crate::rpc::grpc::RpcGetGlobalShortcutResponse; use crate::rpc::grpc::RpcGetThemeRequest; @@ -238,6 +239,19 @@ impl RpcBackend for RpcBackendServerImpl { .into_iter() .map(|(key, value)| (key, plugin_preference_user_data_to_rpc(value))) .collect(), + generated_entrypoints: entrypoint + .generated_entrypoints + .into_iter() + .map(|(entrypoint_id, data)| { + ( + entrypoint_id.to_string(), + RpcGeneratedEntrypoint { + entrypoint_id: data.entrypoint_id.to_string(), + entrypoint_name: data.entrypoint_name, + }, + ) + }) + .collect(), } }) .collect(); diff --git a/rust/management_client/src/views/plugins.rs b/rust/management_client/src/views/plugins.rs index 5cf806a..ba3e620 100644 --- a/rust/management_client/src/views/plugins.rs +++ b/rust/management_client/src/views/plugins.rs @@ -5,16 +5,19 @@ use std::rc::Rc; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PluginId; use gauntlet_common::model::PluginPreferenceUserData; +use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; use gauntlet_common::rpc::backend_api::BackendApi; use gauntlet_common::rpc::backend_api::BackendApiError; use gauntlet_common::settings_env_data_from_string; use gauntlet_common::SettingsEnvData; use gauntlet_common::SETTINGS_ENV; +use iced::alignment; use iced::padding; use iced::widget::button; use iced::widget::column; use iced::widget::container; +use iced::widget::pane_grid::Edge; use iced::widget::row; use iced::widget::scrollable; use iced::widget::text; @@ -49,9 +52,19 @@ pub enum ManagementAppPluginMsgIn { PluginPreferenceMsg(PluginPreferencesMsg), FetchPlugins, PluginsReloaded(HashMap), - RemovePlugin { plugin_id: PluginId }, - ToggleShowEntrypoint { plugin_id: PluginId }, - DownloadPlugin { plugin_id: PluginId }, + RemovePlugin { + plugin_id: PluginId, + }, + ToggleShowEntrypoint { + plugin_id: PluginId, + }, + ToggleShowGeneratedEntrypoint { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + }, + DownloadPlugin { + plugin_id: PluginId, + }, SelectItem(SelectedItem), Noop, } @@ -167,7 +180,20 @@ impl ManagementAppPluginsState { ))) } PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } => { - Task::done(ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::Noop)) + Task::done(ManagementAppPluginMsgOut::Inner( + ManagementAppPluginMsgIn::ToggleShowEntrypoint { plugin_id }, + )) + } + PluginTableMsgOut::ToggleShowGeneratedEntrypoints { + plugin_id, + entrypoint_id, + } => { + Task::done(ManagementAppPluginMsgOut::Inner( + ManagementAppPluginMsgIn::ToggleShowGeneratedEntrypoint { + plugin_id, + entrypoint_id, + }, + )) } } }) @@ -185,6 +211,27 @@ impl ManagementAppPluginsState { Task::none() } + ManagementAppPluginMsgIn::ToggleShowGeneratedEntrypoint { + plugin_id, + entrypoint_id, + } => { + let plugins = { + let mut plugin_data = self.plugin_data.borrow_mut(); + let settings_plugin_data = plugin_data.plugins_state.get_mut(&plugin_id).unwrap(); + let settings_entrypoint_data = settings_plugin_data + .generator_entrypoint_state + .get_mut(&entrypoint_id) + .unwrap(); + + settings_entrypoint_data.show_entrypoints = !settings_entrypoint_data.show_entrypoints; + + plugin_data.plugins.clone() + }; + + self.apply_plugin_fetch(plugins); + + Task::none() + } ManagementAppPluginMsgIn::PluginPreferenceMsg(msg) => { match msg { PluginPreferencesMsg::UpdatePreferenceValue { @@ -303,14 +350,37 @@ impl ManagementAppPluginsState { plugin_data.plugins_state = plugins .iter() - .map(|(id, _plugin)| { - let show_entrypoints = plugin_data - .plugins_state - .get(&id) - .map(|data| data.show_entrypoints) - .unwrap_or(true); + .map(|(id, plugin)| { + let plugin_data = plugin_data.plugins_state.get(&id); - (id.clone(), SettingsPluginData { show_entrypoints }) + let show_entrypoints = plugin_data.map(|data| data.show_entrypoints).unwrap_or(true); + + let mut generator_entrypoint_state_old = plugin_data + .map(|data| data.generator_entrypoint_state.clone()) + .unwrap_or_default(); + + let generator_entrypoint_state = plugin + .entrypoints + .iter() + .filter(|(_, entrypoint)| { + matches!(entrypoint.entrypoint_type, SettingsEntrypointType::EntrypointGenerator) + }) + .map(|(_, entrypoint)| { + let generator_data = generator_entrypoint_state_old + .remove(&entrypoint.entrypoint_id) + .unwrap_or(SettingsGeneratorData { show_entrypoints: true }); + + (entrypoint.entrypoint_id.clone(), generator_data) + }) + .collect(); + + ( + id.clone(), + SettingsPluginData { + show_entrypoints, + generator_entrypoint_state, + }, + ) }) .collect(); @@ -377,7 +447,7 @@ impl ManagementAppPluginsState { .class(TextStyle::Subtitle) .into(); - let id = container(id).padding(padding::bottom(8.0)).into(); + let id = container(id).padding(padding::all(8.0).top(0)).into(); let mut column_content = vec![name, id]; @@ -385,7 +455,8 @@ impl ManagementAppPluginsState { let description_label: Element<_> = text("Description").size(14).class(TextStyle::Subtitle).into(); - let description_label = container(description_label).padding(padding::bottom(8.0)).into(); + let description_label = + container(description_label).padding(padding::all(8.0).top(0)).into(); let description = text(plugin.plugin_description.to_string()).shaping(Shaping::Advanced); @@ -485,7 +556,8 @@ impl ManagementAppPluginsState { let description_label: Element<_> = text("Description").size(14).class(TextStyle::Subtitle).into(); - let description_label = container(description_label).padding(padding::bottom(8.0)).into(); + let description_label = + container(description_label).padding(padding::all(8.0).top(0)).into(); let description = container(text(entrypoint.entrypoint_description.to_string())) .padding(Padding::new(8.0)) @@ -542,6 +614,47 @@ impl ManagementAppPluginsState { .align_x(Alignment::Center) .into() } + SelectedItem::GeneratedEntrypoint { + plugin_id, + generated_entrypoint_id, + generator_entrypoint_id, + } => { + let plugin_data = self.plugin_data.borrow(); + + let entrypoint = plugin_data + .plugins + .get(&plugin_id) + .map(|plugin| plugin.entrypoints.get(generator_entrypoint_id)) + .flatten() + .map(|entrypoint| entrypoint.generated_entrypoints.get(generated_entrypoint_id)) + .flatten(); + + match entrypoint { + None => { + let loading_text: Element<_> = text("Loading...").into(); + + container(loading_text) + .align_y(Alignment::Center) + .align_x(Alignment::Center) + .height(Length::Fill) + .width(Length::Fill) + .into() + } + Some(entrypoint) => { + let name: Element<_> = text(entrypoint.entrypoint_name.to_string()) + .shaping(Shaping::Advanced) + .into(); + + let name: Element<_> = container(name).padding(padding::all(8.0)).into(); + + container(name) + .padding(Padding::from([4.0, 0.0])) + .width(Length::Fill) + .height(Length::Fill) + .into() + } + } + } }; let plugin_url = if let SelectedItem::NewPlugin { repository_url } = &self.selected_item { @@ -630,11 +743,22 @@ pub enum SelectedItem { plugin_id: PluginId, entrypoint_id: EntrypointId, }, + GeneratedEntrypoint { + plugin_id: PluginId, + generator_entrypoint_id: EntrypointId, + generated_entrypoint_id: EntrypointId, + }, } #[derive(Debug, Clone)] struct SettingsPluginData { show_entrypoints: bool, + generator_entrypoint_state: HashMap, +} + +#[derive(Debug, Clone)] +struct SettingsGeneratorData { + show_entrypoints: bool, } #[derive(Debug, Clone)] diff --git a/rust/management_client/src/views/plugins/table.rs b/rust/management_client/src/views/plugins/table.rs index 99438f7..c2c2bee 100644 --- a/rust/management_client/src/views/plugins/table.rs +++ b/rust/management_client/src/views/plugins/table.rs @@ -6,6 +6,7 @@ use gauntlet_common::model::PluginId; use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; use iced::advanced::text::Shaping; +use iced::padding; use iced::widget::button; use iced::widget::checkbox; use iced::widget::container; @@ -36,7 +37,13 @@ pub enum PluginTableMsgIn { TableSyncHeader(scrollable::AbsoluteOffset), SelectItem(SelectedItem), EnabledToggleItem(EnabledItem), - ToggleShowEntrypoints { plugin_id: PluginId }, + ToggleShowEntrypoints { + plugin_id: PluginId, + }, + ToggleShowGeneratedEntrypoints { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + }, } pub enum PluginTableMsgOut { @@ -53,6 +60,10 @@ pub enum PluginTableMsgOut { ToggleShowEntrypoints { plugin_id: PluginId, }, + ToggleShowGeneratedEntrypoints { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + }, } pub struct PluginTableState { @@ -66,7 +77,6 @@ impl PluginTableState { pub fn new() -> Self { Self { columns: vec![ - Column::new(ColumnKind::ShowEntrypointsToggle), Column::new(ColumnKind::Name), Column::new(ColumnKind::Type), Column::new(ColumnKind::EnableToggle), @@ -102,6 +112,15 @@ impl PluginTableState { PluginTableMsgIn::ToggleShowEntrypoints { plugin_id } => { Task::done(PluginTableMsgOut::ToggleShowEntrypoints { plugin_id }) } + PluginTableMsgIn::ToggleShowGeneratedEntrypoints { + plugin_id, + entrypoint_id, + } => { + Task::done(PluginTableMsgOut::ToggleShowGeneratedEntrypoints { + plugin_id, + entrypoint_id, + }) + } } } @@ -125,18 +144,42 @@ impl PluginTableState { entrypoints.sort_by_key(|entrypoint| &entrypoint.entrypoint_name); - let mut entrypoints: Vec<_> = entrypoints - .iter() - .map(|entrypoint| { - Row::Entrypoint { - plugin_data: plugin_data.clone(), - plugin_id: plugin.plugin_id.clone(), - entrypoint_id: entrypoint.entrypoint_id.clone(), - } - }) - .collect(); + for entrypoint in entrypoints { + let entrypoint_row = Row::Entrypoint { + plugin_data: plugin_data.clone(), + plugin_id: plugin.plugin_id.clone(), + entrypoint_id: entrypoint.entrypoint_id.clone(), + }; - result.append(&mut entrypoints); + result.push(entrypoint_row); + + let show_generated_entrypoints = plugin_state + .generator_entrypoint_state + .get(&entrypoint.entrypoint_id) + .map(|data| data.show_entrypoints) + .unwrap_or(true); + + if show_generated_entrypoints { + let mut generated_entrypoints: Vec<_> = entrypoint + .generated_entrypoints + .iter() + .map(|(_, entrypoint)| entrypoint) + .collect(); + + generated_entrypoints.sort_by_key(|entrypoint| &entrypoint.entrypoint_name); + + for data in generated_entrypoints { + let generated_entrypoint_row = Row::GeneratedEntrypoint { + plugin_data: plugin_data.clone(), + plugin_id: plugin.plugin_id.clone(), + generator_entrypoint_id: entrypoint.entrypoint_id.clone(), + generated_entrypoint_id: data.entrypoint_id.clone(), + }; + + result.push(generated_entrypoint_row); + } + } + } } result @@ -180,10 +223,15 @@ enum Row { plugin_id: PluginId, entrypoint_id: EntrypointId, }, + GeneratedEntrypoint { + plugin_data: Rc>, + plugin_id: PluginId, + generator_entrypoint_id: EntrypointId, + generated_entrypoint_id: EntrypointId, + }, } enum ColumnKind { - ShowEntrypointsToggle, Name, Type, EnableToggle, @@ -204,7 +252,6 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo fn header(&'a self, _col_index: usize) -> Element<'a, PluginTableMsgIn> { match self.kind { - ColumnKind::ShowEntrypointsToggle => horizontal_space().into(), ColumnKind::Name => { container(text("Name")) .height(Length::Fixed(30.0)) @@ -228,8 +275,8 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo fn cell(&'a self, _col_index: usize, _row_index: usize, row_entry: &'a Self::Row) -> Element<'a, PluginTableMsgIn> { match self.kind { - ColumnKind::ShowEntrypointsToggle => { - match row_entry { + ColumnKind::Name => { + let toggle = match row_entry { Row::Plugin { plugin_data, plugin_id } => { let plugin_data = plugin_data.borrow(); let plugin_data = plugin_data.plugins_state.get(&plugin_id).unwrap(); @@ -246,16 +293,58 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo .on_press(PluginTableMsgIn::ToggleShowEntrypoints { plugin_id: plugin_id.clone(), }) - .width(Length::Fill) + .width(Length::Shrink) .height(Length::Fixed(40.0)) .padding(8.0) .class(ButtonStyle::TableRow) .into() } - Row::Entrypoint { .. } => horizontal_space().into(), - } - } - ColumnKind::Name => { + Row::GeneratedEntrypoint { .. } => horizontal_space().width(Length::Shrink).into(), + Row::Entrypoint { + plugin_data, + plugin_id, + entrypoint_id, + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let plugin_data = plugin_data.plugins_state.get(&plugin_id).unwrap(); + + let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); + + if matches!(entrypoint.entrypoint_type, SettingsEntrypointType::EntrypointGenerator) { + let icon = if plugin_data + .generator_entrypoint_state + .get(&entrypoint_id) + .unwrap() + .show_entrypoints + { + Bootstrap::CaretDown + } else { + Bootstrap::CaretRight + }; + + let icon: Element<_> = value(icon).font(BOOTSTRAP_FONT).into(); + + let content = button(icon) + .on_press(PluginTableMsgIn::ToggleShowGeneratedEntrypoints { + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + }) + .width(Length::Shrink) + .height(Length::Fixed(40.0)) + .padding(8.0) + .class(ButtonStyle::TableRow) + .into(); + + let space: Element<_> = Space::with_width(Length::Fixed(10.0)).into(); + + row(vec![space, content]).into() + } else { + horizontal_space().width(Length::Shrink).into() + } + } + }; + let content: Element<_> = match row_entry { Row::Plugin { plugin_data, plugin_id } => { let plugin_data = plugin_data.borrow(); @@ -278,44 +367,80 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo .shaping(Shaping::Advanced) .into(); - let text: Element<_> = row(vec![Space::with_width(Length::Fixed(30.0)).into(), text]).into(); + let space: Element<_> = + if let SettingsEntrypointType::EntrypointGenerator = entrypoint.entrypoint_type { + Space::with_width(Length::Fixed(4.0)).into() + } else { + Space::with_width(Length::Fixed(45.0)).into() + }; + + let text: Element<_> = row(vec![space, text]).into(); + + container(text).align_y(Alignment::Center).into() + } + Row::GeneratedEntrypoint { + plugin_data, + plugin_id, + generator_entrypoint_id, + generated_entrypoint_id, + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let entrypoint = plugin.entrypoints.get(&generator_entrypoint_id).unwrap(); + let generated_entrypoint = + entrypoint.generated_entrypoints.get(&generated_entrypoint_id).unwrap(); + + let text: Element<_> = text(generated_entrypoint.entrypoint_name.to_string()) + .shaping(Shaping::Advanced) + .into(); + + let space: Element<_> = Space::with_width(Length::Fixed(65.0)).into(); + + let text: Element<_> = row(vec![space, text]).into(); container(text).align_y(Alignment::Center).into() } }; let msg = match &row_entry { - Row::Plugin { plugin_data, plugin_id } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - + Row::Plugin { plugin_id, .. } => { SelectedItem::Plugin { - plugin_id: plugin.plugin_id.clone(), + plugin_id: plugin_id.clone(), } } Row::Entrypoint { - plugin_data, entrypoint_id, plugin_id, + .. } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); - SelectedItem::Entrypoint { - plugin_id: plugin.plugin_id.clone(), - entrypoint_id: entrypoint.entrypoint_id.clone(), + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + } + } + Row::GeneratedEntrypoint { + plugin_id, + generator_entrypoint_id, + generated_entrypoint_id, + .. + } => { + SelectedItem::GeneratedEntrypoint { + plugin_id: plugin_id.clone(), + generator_entrypoint_id: generator_entrypoint_id.clone(), + generated_entrypoint_id: generated_entrypoint_id.clone(), } } }; - button(content) + let content = button(content) .class(ButtonStyle::TableRow) .on_press(PluginTableMsgIn::SelectItem(msg)) .width(Length::Fill) .height(Length::Fixed(40.0)) - .padding(8.0) - .into() + .padding(padding::all(8).left(0)) + .into(); + + row(vec![toggle, content]).into() } ColumnKind::Type => { let content: Element<_> = match row_entry { @@ -332,37 +457,43 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo let entrypoint_type = match entrypoint.entrypoint_type { SettingsEntrypointType::Command => "Command", SettingsEntrypointType::View => "View", - SettingsEntrypointType::InlineView => "Inline View", - SettingsEntrypointType::EntrypointGenerator => "Entrypoint Generator", + SettingsEntrypointType::InlineView => "Inline", + SettingsEntrypointType::EntrypointGenerator => "Generator", }; container(text(entrypoint_type.to_string())) .align_y(Alignment::Center) .into() } + Row::GeneratedEntrypoint { .. } => horizontal_space().into(), }; let msg = match &row_entry { - Row::Plugin { plugin_data, plugin_id } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - + Row::Plugin { plugin_id, .. } => { SelectedItem::Plugin { - plugin_id: plugin.plugin_id.clone(), + plugin_id: plugin_id.clone(), } } Row::Entrypoint { - plugin_data, entrypoint_id, plugin_id, + .. } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); - SelectedItem::Entrypoint { - plugin_id: plugin.plugin_id.clone(), - entrypoint_id: entrypoint.entrypoint_id.clone(), + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + } + } + Row::GeneratedEntrypoint { + plugin_id, + generated_entrypoint_id, + generator_entrypoint_id, + .. + } => { + SelectedItem::GeneratedEntrypoint { + plugin_id: plugin_id.clone(), + generator_entrypoint_id: generator_entrypoint_id.clone(), + generated_entrypoint_id: generated_entrypoint_id.clone(), } } }; @@ -399,6 +530,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo Some(entrypoint.entrypoint_id.clone()), ) } + Row::GeneratedEntrypoint { .. } => return horizontal_space().into(), }; let on_toggle = if show_checkbox { @@ -438,9 +570,8 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo fn width(&self) -> f32 { match self.kind { - ColumnKind::ShowEntrypointsToggle => 35.0, - ColumnKind::Name => 350.0, - ColumnKind::Type => 200.0, + ColumnKind::Name => 300.0, + ColumnKind::Type => 300.0, ColumnKind::EnableToggle => 75.0, } } diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 2615a0f..472f48d 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -888,9 +888,12 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { }) .collect(); - let entrypoint_generator_name = generator_names - .get(&item.generator_entrypoint_id) - .map(|name| name.to_string()); + let entrypoint_generator = generator_names.get(&item.generator_entrypoint_id).map(|name| { + ( + EntrypointId::from_string(item.generator_entrypoint_id), + name.to_string(), + ) + }); Ok(SearchIndexItem { entrypoint_type: SearchResultEntrypointType::Generated, @@ -900,7 +903,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { entrypoint_frecency, entrypoint_actions, entrypoint_accessories, - entrypoint_generator_name, + entrypoint_generator, }) }) .collect::>>()?; @@ -946,7 +949,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { Ok(Some(SearchIndexItem { entrypoint_type: SearchResultEntrypointType::Command, entrypoint_name: entrypoint.name, - entrypoint_generator_name: None, + entrypoint_generator: None, entrypoint_id, entrypoint_icon, entrypoint_frecency, @@ -958,7 +961,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { Ok(Some(SearchIndexItem { entrypoint_type: SearchResultEntrypointType::View, entrypoint_name: entrypoint.name, - entrypoint_generator_name: None, + entrypoint_generator: None, entrypoint_id, entrypoint_icon, entrypoint_frecency, diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index b282ee2..1d193f2 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -23,6 +23,7 @@ use gauntlet_common::model::SearchResult; use gauntlet_common::model::SearchResultEntrypointType; use gauntlet_common::model::SettingsEntrypoint; use gauntlet_common::model::SettingsEntrypointType; +use gauntlet_common::model::SettingsGeneratedEntrypoint; use gauntlet_common::model::SettingsPlugin; use gauntlet_common::model::SettingsTheme; use gauntlet_common::model::UiPropertyValue; @@ -43,6 +44,7 @@ use gauntlet_plugin_runtime::JsPluginPermissionsMainSearchBar; use gauntlet_utils::channel::RequestSender; use include_dir::include_dir; use include_dir::Dir; +use itertools::Itertools; use tokio::runtime::Handle; use crate::model::ActionShortcutKey; @@ -196,7 +198,7 @@ impl ApplicationManager { entrypoint_id: EntrypointId, action_id: String, ) -> anyhow::Result<()> { - let data = self.search_index.plugin_entrypoint_actions(); + let data = self.search_index.plugin_entrypoint_data(); let Some(data) = data.get(&plugin_id) else { return Err(anyhow!("Unable to find plugin with id: {}", plugin_id)); @@ -213,9 +215,9 @@ impl ApplicationManager { let EntrypointDataView { entrypoint_name, + entrypoint_generator: _, entrypoint_type, actions, - .. } = entrypoint_data; match action_id.as_str() { @@ -381,12 +383,46 @@ impl ApplicationManager { } pub async fn plugins(&self) -> anyhow::Result> { + let mut generator_data: HashMap<_, _> = self + .search_index + .plugin_entrypoint_data() + .into_iter() + .flat_map(|(plugin_id, plugin_data)| { + let generator_data: HashMap<_, HashMap<_, _>> = plugin_data + .entrypoints + .into_iter() + .filter_map(|(entrypoint_id, entrypoint_data)| { + match &entrypoint_data.entrypoint_generator { + None => None, + Some((generator_entrypoint_id, _)) => { + Some((generator_entrypoint_id.clone(), entrypoint_id, entrypoint_data)) + } + } + }) + .chunk_by(|(generator_entrypoint_id, entrypoint_id, entrypoint_data)| { + generator_entrypoint_id.clone() + }) + .into_iter() + .map(|(generator_id, data)| { + let data: HashMap<_, _> = data + .map(|(_, entrypoint_id, entrypoint_data)| (entrypoint_id, entrypoint_data)) + .collect(); + ((plugin_id.clone(), generator_id.clone()), data) + }) + .collect(); + + generator_data + }) + .collect(); + let result = self .db_repository .list_plugins_and_entrypoints() .await? .into_iter() .map(|(plugin, entrypoints)| { + let plugin_id = PluginId::from_string(plugin.id); + let entrypoints = entrypoints .into_iter() .map(|entrypoint| { @@ -419,6 +455,19 @@ impl ApplicationManager { .into_iter() .map(|(key, value)| (key, plugin_preference_user_data_from_db(value))) .collect(), + generated_entrypoints: generator_data + .remove(&(plugin_id.clone(), entrypoint_id.clone())) + .unwrap_or_default() + .into_iter() + .map(|(entrypoint_id, data)| { + let generated_entrypoint = SettingsGeneratedEntrypoint { + entrypoint_id: entrypoint_id.clone(), + entrypoint_name: data.entrypoint_name, + }; + + (entrypoint_id, generated_entrypoint) + }) + .collect(), }; (entrypoint_id, entrypoint) @@ -426,7 +475,7 @@ impl ApplicationManager { .collect(); SettingsPlugin { - plugin_id: PluginId::from_string(plugin.id), + plugin_id, plugin_name: plugin.name, plugin_description: plugin.description, enabled: plugin.enabled, diff --git a/rust/server/src/search.rs b/rust/server/src/search.rs index 95219bb..df5d1d2 100644 --- a/rust/server/src/search.rs +++ b/rust/server/src/search.rs @@ -51,7 +51,7 @@ struct PluginData { struct EntrypointData { entrypoint_name: String, - entrypoint_generator_name: Option, + entrypoint_generator: Option<(EntrypointId, String)>, entrypoint_type: SearchResultEntrypointType, icon: Option, frecency: f64, @@ -79,7 +79,7 @@ pub struct PluginDataView { pub struct EntrypointDataView { pub entrypoint_name: String, - pub entrypoint_generator_name: Option, + pub entrypoint_generator: Option<(EntrypointId, String)>, pub entrypoint_type: SearchResultEntrypointType, pub actions: Vec, } @@ -95,7 +95,7 @@ pub struct EntrypointActionDataView { pub struct SearchIndexItem { pub entrypoint_type: SearchResultEntrypointType, pub entrypoint_name: String, - pub entrypoint_generator_name: Option, + pub entrypoint_generator: Option<(EntrypointId, String)>, pub entrypoint_id: EntrypointId, pub entrypoint_icon: Option, pub entrypoint_frecency: f64, @@ -228,7 +228,7 @@ impl SearchIndex { let data = EntrypointData { entrypoint_name: item.entrypoint_name, - entrypoint_generator_name: item.entrypoint_generator_name, + entrypoint_generator: item.entrypoint_generator, entrypoint_type: item.entrypoint_type, icon: item.entrypoint_icon, frecency: item.entrypoint_frecency, @@ -267,7 +267,7 @@ impl SearchIndex { Ok(()) } - pub fn plugin_entrypoint_actions(&self) -> HashMap { + pub fn plugin_entrypoint_data(&self) -> HashMap { let entrypoint_data = self.entrypoint_data.lock().expect("lock is poisoned"); entrypoint_data @@ -294,7 +294,7 @@ impl SearchIndex { entrypoint_id.clone(), EntrypointDataView { entrypoint_name: data.entrypoint_name.clone(), - entrypoint_generator_name: data.entrypoint_generator_name.clone(), + entrypoint_generator: data.entrypoint_generator.clone(), entrypoint_type: data.entrypoint_type.clone(), actions, }, @@ -425,7 +425,10 @@ impl SearchIndex { let result_item = SearchResult { entrypoint_type: entrypoint_data.entrypoint_type.clone(), entrypoint_name, - entrypoint_generator_name: entrypoint_data.entrypoint_generator_name.clone(), + entrypoint_generator_name: entrypoint_data + .entrypoint_generator + .as_ref() + .map(|(_, name)| name.clone()), entrypoint_id, entrypoint_icon: entrypoint_data.icon.clone(), plugin_name, diff --git a/schema/backend.proto b/schema/backend.proto index 1404a2f..1ea2351 100644 --- a/schema/backend.proto +++ b/schema/backend.proto @@ -210,8 +210,13 @@ message RpcEntrypoint { RpcEntrypointTypeSettings entrypoint_type = 5; map preferences = 6; map preferences_user_data = 7; + map generated_entrypoints = 8; } +message RpcGeneratedEntrypoint { + string entrypoint_id = 1; + string entrypoint_name = 2; +} message RpcEventRenderView { string entrypoint_id = 1; From a258bdf6743c1590bd52324992577f5a155f9aff Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 3 Apr 2025 19:54:01 +0200 Subject: [PATCH 043/170] Do not panic when global shortcut unregistration fails --- rust/client/src/ui/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 9e66585..3143d3a 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -2220,7 +2220,9 @@ fn assign_global_shortcut( let mut hotkey_guard = current_hotkey.lock().expect("lock is poisoned"); if let Some(current_hotkey) = *hotkey_guard { - global_hotkey_manager.unregister(current_hotkey)?; + if let Err(err) = global_hotkey_manager.unregister(current_hotkey) { + tracing::warn!("error occurred when unregistering global shortcut {:?}: {:?}", current_hotkey, err) + } } if let Some(shortcut) = shortcut { From 2bb69f8add17717445961cc1fd9132b22853ef30 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 3 Apr 2025 20:38:03 +0200 Subject: [PATCH 044/170] Fix formatting --- rust/client/src/ui/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 3143d3a..03e4853 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -2221,7 +2221,11 @@ fn assign_global_shortcut( if let Some(current_hotkey) = *hotkey_guard { if let Err(err) = global_hotkey_manager.unregister(current_hotkey) { - tracing::warn!("error occurred when unregistering global shortcut {:?}: {:?}", current_hotkey, err) + tracing::warn!( + "error occurred when unregistering global shortcut {:?}: {:?}", + current_hotkey, + err + ) } } From d8b6010ad1b02afdff10c1fd1b871e177817ec5b Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 8 Apr 2025 21:45:49 +0200 Subject: [PATCH 045/170] Global shortcuts for entrypoints --- README.md | 1 - rust/client/src/global_shortcut.rs | 2 +- rust/client/src/ui/mod.rs | 205 ++++++++++++--- rust/common/src/model.rs | 11 + rust/common/src/rpc/backend_api.rs | 88 ++++++- rust/common/src/rpc/backend_server.rs | 78 ++++++ rust/common/src/rpc/frontend_api.rs | 24 ++ .../src/components/shortcut_selector.rs | 98 +++---- .../src/theme/shortcut_selector.rs | 25 +- rust/management_client/src/ui.rs | 55 ++-- rust/management_client/src/views/general.rs | 13 +- rust/management_client/src/views/plugins.rs | 95 +++++-- .../src/views/plugins/table.rs | 109 +++++++- rust/scenario_runner/src/frontend_mock.rs | 1 + .../13_remove_old_global_shortcut.sql | 1 + rust/server/src/lib.rs | 19 +- rust/server/src/plugins/data_db_repository.rs | 242 ++++++++++-------- rust/server/src/plugins/mod.rs | 41 ++- rust/server/src/plugins/settings.rs | 222 +++++++++++++--- rust/server/src/rpc.rs | 18 ++ schema/backend.proto | 27 ++ 21 files changed, 1083 insertions(+), 292 deletions(-) create mode 100644 rust/server/db_migrations/13_remove_old_global_shortcut.sql diff --git a/README.md b/README.md index 33efdc7..e0b7428 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ Web-first cross-platform application launcher with React-based plugins > [!WARNING] > Launcher is being developed by single developer in their free time. > Changes may be few and far between. -> If you face any issues they will likely not get fixed unless they become a problem for that developer > > There will probably be breaking changes which will be documented in [changelog](CHANGELOG.md). diff --git a/rust/client/src/global_shortcut.rs b/rust/client/src/global_shortcut.rs index 7d253b1..e0ee697 100644 --- a/rust/client/src/global_shortcut.rs +++ b/rust/client/src/global_shortcut.rs @@ -17,7 +17,7 @@ pub fn register_listener(msg_sender: Sender) { if let global_hotkey::HotKeyState::Pressed = e.state() { handle.spawn(async move { - if let Err(err) = msg_sender.send(AppMsg::ToggleWindow).await { + if let Err(err) = msg_sender.send(AppMsg::HandleGlobalShortcut(e.id)).await { tracing::warn!(target = "rpc", "error occurred when receiving shortcut event {:?}", err) } }); diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 03e4853..5cff3b4 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -92,6 +92,7 @@ use iced::Subscription; use iced::Task; use iced_fonts::BOOTSTRAP_FONT_BYTES; use serde::Deserialize; +use serde_json::map::Entry; use tokio::runtime::Handle; use tokio::sync::oneshot; use tokio::sync::Mutex as TokioMutex; @@ -145,8 +146,9 @@ use crate::ui::widget_container::PluginWidgetContainer; pub struct AppModel { // logic backend_api: BackendForFrontendApi, - global_hotkey_manager: Arc>, - current_hotkey: Arc>>, + global_hotkey_manager: GlobalHotKeyManager, + current_global_hotkey: Option, + current_entrypoint_global_hotkeys: HashMap<(PluginId, EntrypointId), HotKey>, frontend_receiver: Arc>>, main_window_id: window::Id, focused: bool, @@ -254,6 +256,7 @@ pub enum AppMsg { ShowWindow, HideWindow, ToggleWindow, + HandleGlobalShortcut(u32), ToggleActionPanel { keyboard: bool, }, @@ -332,6 +335,12 @@ pub enum AppMsg { shortcut: Option, responder: Arc>>>, }, + SetGlobalEntrypointShortcut { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + responder: Arc>>>, + }, UpdateLoadingBar { plugin_id: PluginId, entrypoint_id: EntrypointId, @@ -357,6 +366,10 @@ pub enum AppMsg { X11ActiveWindowChanged { window: u32, }, + RunEntrypoint { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + }, } #[cfg(target_os = "linux")] @@ -554,15 +567,30 @@ fn new( GauntletComplexTheme::set_global(theme.clone()); - let current_hotkey = Arc::new(StdMutex::new(None)); - let global_hotkey_manager = GlobalHotKeyManager::new().expect("unable to create global hot key manager"); - let assignment_result = assign_global_shortcut(&global_hotkey_manager, ¤t_hotkey, setup_data.global_shortcut); + // let current_global_hotkey = None; + // let global_assignment_result = anyhow::Ok(()); + let (current_global_hotkey, global_assignment_result) = + assign_global_shortcut(&global_hotkey_manager, None, setup_data.global_shortcut); - futures::executor::block_on( - backend_api.setup_response(assignment_result.map_err(|err| format!("{:#}", err)).err()), - ) + let mut global_entrypoint_assignment_results = HashMap::new(); + let mut current_entrypoint_global_hotkeys = HashMap::new(); + for ((plugin_id, entrypoint_id), shortcut) in setup_data.global_entrypoint_shortcuts { + let (global_hotkey, result) = assign_global_shortcut(&global_hotkey_manager, None, Some(shortcut)); + if let Some(global_hotkey) = global_hotkey { + current_entrypoint_global_hotkeys.insert((plugin_id.clone(), entrypoint_id.clone()), global_hotkey); + } + global_entrypoint_assignment_results.insert( + (plugin_id, entrypoint_id), + result.map_err(|err| format!("{:#}", err)).err(), + ); + } + + futures::executor::block_on(backend_api.setup_response( + global_assignment_result.map_err(|err| format!("{:#}", err)).err(), + global_entrypoint_assignment_results, + )) .expect("Unable to setup frontend"); let mut tasks = vec![font::load(BOOTSTRAP_FONT_BYTES).map(AppMsg::FontLoaded)]; @@ -689,8 +717,9 @@ fn new( AppModel { // logic backend_api, - global_hotkey_manager: Arc::new(StdRwLock::new(global_hotkey_manager)), - current_hotkey, + global_hotkey_manager, + current_global_hotkey, + current_entrypoint_global_hotkeys, frontend_receiver: Arc::new(TokioRwLock::new(frontend_receiver)), main_window_id, focused: false, @@ -1460,27 +1489,81 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::SetGlobalShortcut { shortcut, responder } => { - tracing::info!("Registering new global shortcut: {:?}", shortcut); - - let run = || { - let global_hotkey_manager = state.global_hotkey_manager.read().expect("lock is poisoned"); - - assign_global_shortcut(&global_hotkey_manager, &state.current_hotkey, shortcut) - }; - - // responder is not clone and send, and we need to consume it - // so we wrap it in arc mutex option let mut responder = responder .lock() .expect("lock is poisoned") .take() .expect("there should always be a responder here"); - match run() { + let (hotkey, error) = assign_global_shortcut( + &state.global_hotkey_manager, + state.current_global_hotkey, + shortcut.clone(), + ); + + state.current_global_hotkey = hotkey; + + match error { Ok(()) => { + tracing::info!("Successfully registered new global shortcut: {:?}", shortcut); responder.respond(UiResponseData::Nothing); } Err(err) => { + tracing::error!("Unable to register new global shortcut {:?}: {:?}", shortcut, err); + responder.respond(UiResponseData::Err(err)); + } + } + + Task::none() + } + AppMsg::SetGlobalEntrypointShortcut { + plugin_id, + entrypoint_id, + shortcut, + responder, + } => { + let mut responder = responder + .lock() + .expect("lock is poisoned") + .take() + .expect("there should always be a responder here"); + + let current_global_hotkey = state + .current_entrypoint_global_hotkeys + .get(&(plugin_id.clone(), entrypoint_id.clone())) + .cloned(); + + let (hotkey, error) = + assign_global_shortcut(&state.global_hotkey_manager, current_global_hotkey, shortcut.clone()); + + if let Some(hotkey) = hotkey { + state + .current_entrypoint_global_hotkeys + .insert((plugin_id.clone(), entrypoint_id.clone()), hotkey); + } else { + state + .current_entrypoint_global_hotkeys + .remove(&(plugin_id.clone(), entrypoint_id.clone())); + }; + + match error { + Ok(()) => { + tracing::info!( + "Successfully registered new global shortcut for plugin '{:?}' and entrypoint '{:?}' : {:?}", + plugin_id, + entrypoint_id, + shortcut + ); + responder.respond(UiResponseData::Nothing); + } + Err(err) => { + tracing::info!( + "Unable to register new global shortcut for plugin '{:?}' and entrypoint '{:?}' - {:?}: {:?}", + plugin_id, + entrypoint_id, + shortcut, + err + ); responder.respond(UiResponseData::Err(err)); } } @@ -1608,6 +1691,43 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } } + AppMsg::HandleGlobalShortcut(id) => { + if let Some(hotkey) = state.current_global_hotkey { + if hotkey.id == id { + return Task::done(AppMsg::ToggleWindow); + } + } + + let ids = state + .current_entrypoint_global_hotkeys + .iter() + .find(|(_, hotkey)| hotkey.id == id) + .map(|(ids, _)| ids); + + if let Some((plugin_id, entrypoint_id)) = ids { + return Task::done(AppMsg::RunEntrypoint { + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + }); + }; + + Task::none() + } + AppMsg::RunEntrypoint { + plugin_id, + entrypoint_id, + } => { + let mut backend_client = state.backend_api.clone(); + + Task::perform( + async move { + let result = backend_client.run_entrypoint(plugin_id, entrypoint_id).await?; + + Ok(result) + }, + |result| handle_backend_error(result, |action_shortcuts| AppMsg::Noop), + ) + } } } @@ -2214,13 +2334,11 @@ fn subscription(state: &AppModel) -> Subscription { fn assign_global_shortcut( global_hotkey_manager: &GlobalHotKeyManager, - current_hotkey: &Arc>>, - shortcut: Option, -) -> anyhow::Result<()> { - let mut hotkey_guard = current_hotkey.lock().expect("lock is poisoned"); - - if let Some(current_hotkey) = *hotkey_guard { - if let Err(err) = global_hotkey_manager.unregister(current_hotkey) { + current_hotkey: Option, + new_shortcut: Option, +) -> (Option, anyhow::Result<()>) { + if let Some(current_hotkey) = current_hotkey { + if let Err(err) = global_hotkey_manager.unregister(current_hotkey.clone()) { tracing::warn!( "error occurred when unregistering global shortcut {:?}: {:?}", current_hotkey, @@ -2229,22 +2347,15 @@ fn assign_global_shortcut( } } - if let Some(shortcut) = shortcut { - let hotkey = convert_physical_shortcut_to_hotkey(shortcut); - + if let Some(new_shortcut) = new_shortcut { + let hotkey = convert_physical_shortcut_to_hotkey(new_shortcut); match global_hotkey_manager.register(hotkey) { - Ok(()) => { - *hotkey_guard = Some(hotkey); - } - Err(err) => { - *hotkey_guard = None; - - Err(err)? - } + Ok(()) => (Some(hotkey), Ok(())), + Err(err) => (None, Err(anyhow!(err))), } + } else { + (None, Ok(())) } - - Ok(()) } impl AppModel { @@ -2959,6 +3070,18 @@ async fn request_loop( responder: Arc::new(Mutex::new(Some(responder))), } } + UiRequestData::SetGlobalEntrypointShortcut { + plugin_id, + entrypoint_id, + shortcut, + } => { + AppMsg::SetGlobalEntrypointShortcut { + plugin_id, + entrypoint_id, + shortcut, + responder: Arc::new(Mutex::new(Some(responder))), + } + } UiRequestData::UpdateLoadingBar { plugin_id, entrypoint_id, diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index a95ec26..2c6c2e6 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -217,6 +217,7 @@ pub struct UiSetupData { pub window_position_file: Option, pub theme: UiTheme, pub global_shortcut: Option, + pub global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), PhysicalShortcut>, pub close_on_unfocus: bool, pub window_position_mode: WindowPositionMode, } @@ -272,6 +273,11 @@ pub enum UiRequestData { SetGlobalShortcut { shortcut: Option, }, + SetGlobalEntrypointShortcut { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + }, SetTheme { theme: UiTheme, }, @@ -361,6 +367,11 @@ pub enum BackendRequestData { InlineViewShortcuts, SetupResponse { global_shortcut_error: Option, + global_entrypoint_shortcuts_errors: HashMap<(PluginId, EntrypointId), Option>, + }, + RunEntrypoint { + plugin_id: PluginId, + entrypoint_id: EntrypointId, }, } diff --git a/rust/common/src/rpc/backend_api.rs b/rust/common/src/rpc/backend_api.rs index a006288..aa9359f 100644 --- a/rust/common/src/rpc/backend_api.rs +++ b/rust/common/src/rpc/backend_api.rs @@ -32,6 +32,7 @@ use crate::rpc::grpc::RpcDownloadPluginRequest; use crate::rpc::grpc::RpcDownloadStatus; use crate::rpc::grpc::RpcDownloadStatusRequest; use crate::rpc::grpc::RpcEntrypointTypeSettings; +use crate::rpc::grpc::RpcGetGlobalEntrypointShortcutRequest; use crate::rpc::grpc::RpcGetGlobalShortcutRequest; use crate::rpc::grpc::RpcGetThemeRequest; use crate::rpc::grpc::RpcGetWindowPositionModeRequest; @@ -41,6 +42,7 @@ use crate::rpc::grpc::RpcRemovePluginRequest; use crate::rpc::grpc::RpcRunActionRequest; use crate::rpc::grpc::RpcSaveLocalPluginRequest; use crate::rpc::grpc::RpcSetEntrypointStateRequest; +use crate::rpc::grpc::RpcSetGlobalEntrypointShortcutRequest; use crate::rpc::grpc::RpcSetGlobalShortcutRequest; use crate::rpc::grpc::RpcSetPluginStateRequest; use crate::rpc::grpc::RpcSetPreferenceValueRequest; @@ -79,6 +81,8 @@ pub struct BackendForFrontendApi { backend_sender: RequestSender, } +impl BackendForFrontendApi {} + impl BackendForFrontendApi { pub fn new(backend_sender: RequestSender) -> Self { Self { backend_sender } @@ -97,8 +101,12 @@ impl BackendForFrontendApi { pub async fn setup_response( &mut self, global_shortcut_error: Option, + global_entrypoint_shortcuts_errors: HashMap<(PluginId, EntrypointId), Option>, ) -> Result<(), BackendForFrontendApiError> { - let request = BackendRequestData::SetupResponse { global_shortcut_error }; + let request = BackendRequestData::SetupResponse { + global_shortcut_error, + global_entrypoint_shortcuts_errors, + }; let BackendResponseData::Nothing = self.backend_sender.send_receive(request).await? else { unreachable!() @@ -291,6 +299,23 @@ impl BackendForFrontendApi { Ok(shortcuts) } + + pub async fn run_entrypoint( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + ) -> Result<(), BackendForFrontendApiError> { + let request = BackendRequestData::RunEntrypoint { + plugin_id, + entrypoint_id, + }; + + let BackendResponseData::Nothing = self.backend_sender.send_receive(request).await? else { + unreachable!() + }; + + Ok(()) + } } #[derive(Error, Debug, Clone)] @@ -538,6 +563,67 @@ impl BackendApi { )) } + pub async fn set_global_entrypoint_shortcut( + &mut self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> Result, BackendApiError> { + let request = RpcSetGlobalEntrypointShortcutRequest { + plugin_id: plugin_id.to_string(), + entrypoint_id: entrypoint_id.to_string(), + shortcut: shortcut.map(|shortcut| { + RpcShortcut { + physical_key: shortcut.physical_key.to_value(), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + } + }), + }; + + let error = self + .client + .set_global_entrypoint_shortcut(request) + .await? + .into_inner() + .error; + + Ok(error) + } + + pub async fn get_global_entrypoint_shortcuts( + &mut self, + ) -> Result)>, BackendApiError> { + let response = self + .client + .get_global_entrypoint_shortcut(Request::new(RpcGetGlobalEntrypointShortcutRequest::default())) + .await?; + + let response = response + .into_inner() + .shortcuts + .into_iter() + .map(|data| { + let plugin_id = PluginId::from_string(data.plugin_id); + let entrypoint_id = EntrypointId::from_string(data.entrypoint_id); + let shortcut = data.shortcut.unwrap(); + let shortcut = PhysicalShortcut { + physical_key: PhysicalKey::from_value(shortcut.physical_key), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + }; + + ((plugin_id, entrypoint_id), (shortcut, data.error)) + }) + .collect(); + + Ok(response) + } + pub async fn set_theme(&mut self, theme: SettingsTheme) -> Result<(), BackendApiError> { let theme = match theme { SettingsTheme::AutoDetect => "AutoDetect", diff --git a/rust/common/src/rpc/backend_server.rs b/rust/common/src/rpc/backend_server.rs index 9da185c..9553cc4 100644 --- a/rust/common/src/rpc/backend_server.rs +++ b/rust/common/src/rpc/backend_server.rs @@ -30,6 +30,9 @@ use crate::rpc::grpc::RpcDownloadStatusValue; use crate::rpc::grpc::RpcEntrypoint; use crate::rpc::grpc::RpcEntrypointTypeSettings; use crate::rpc::grpc::RpcGeneratedEntrypoint; +use crate::rpc::grpc::RpcGetGlobalEntrypointShortcut; +use crate::rpc::grpc::RpcGetGlobalEntrypointShortcutRequest; +use crate::rpc::grpc::RpcGetGlobalEntrypointShortcutResponse; use crate::rpc::grpc::RpcGetGlobalShortcutRequest; use crate::rpc::grpc::RpcGetGlobalShortcutResponse; use crate::rpc::grpc::RpcGetThemeRequest; @@ -49,6 +52,8 @@ use crate::rpc::grpc::RpcSaveLocalPluginRequest; use crate::rpc::grpc::RpcSaveLocalPluginResponse; use crate::rpc::grpc::RpcSetEntrypointStateRequest; use crate::rpc::grpc::RpcSetEntrypointStateResponse; +use crate::rpc::grpc::RpcSetGlobalEntrypointShortcutRequest; +use crate::rpc::grpc::RpcSetGlobalEntrypointShortcutResponse; use crate::rpc::grpc::RpcSetGlobalShortcutRequest; use crate::rpc::grpc::RpcSetGlobalShortcutResponse; use crate::rpc::grpc::RpcSetPluginStateRequest; @@ -128,6 +133,17 @@ pub trait BackendServer { async fn get_global_shortcut(&self) -> anyhow::Result<(Option, Option)>; + async fn set_global_entrypoint_shortcut( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> anyhow::Result<()>; + + async fn get_global_entrypoint_shortcuts( + &self, + ) -> anyhow::Result)>>; + async fn set_theme(&self, theme: SettingsTheme) -> anyhow::Result<()>; async fn get_theme(&self) -> anyhow::Result; @@ -403,6 +419,68 @@ impl RpcBackend for RpcBackendServerImpl { })) } + async fn set_global_entrypoint_shortcut( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + + let plugin_id = request.plugin_id; + let entrypoint_id = request.entrypoint_id; + let shortcut = request.shortcut.map(|shortcut| { + PhysicalShortcut { + physical_key: PhysicalKey::from_value(shortcut.physical_key), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + } + }); + + let plugin_id = PluginId::from_string(plugin_id); + let entrypoint_id = EntrypointId::from_string(entrypoint_id); + + let error = self + .server + .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) + .await + .map_err(|err| format!("{:#}", err)) + .err(); + + Ok(Response::new(RpcSetGlobalEntrypointShortcutResponse { error })) + } + + async fn get_global_entrypoint_shortcut( + &self, + _request: Request, + ) -> Result, Status> { + let shortcuts = self + .server + .get_global_entrypoint_shortcuts() + .await + .map_err(|err| Status::internal(format!("{:#}", err)))?; + + let shortcuts = shortcuts + .into_iter() + .map(|((plugin_id, entrypoint_id), (shortcut, error))| { + RpcGetGlobalEntrypointShortcut { + plugin_id: plugin_id.to_string(), + entrypoint_id: entrypoint_id.to_string(), + shortcut: Some(RpcShortcut { + physical_key: shortcut.physical_key.to_value(), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + }), + error, + } + }) + .collect(); + + Ok(Response::new(RpcGetGlobalEntrypointShortcutResponse { shortcuts })) + } + async fn set_theme(&self, request: Request) -> Result, Status> { let theme = request.into_inner().theme; diff --git a/rust/common/src/rpc/frontend_api.rs b/rust/common/src/rpc/frontend_api.rs index 3c65ea2..975b83e 100644 --- a/rust/common/src/rpc/frontend_api.rs +++ b/rust/common/src/rpc/frontend_api.rs @@ -191,6 +191,30 @@ impl FrontendApi { } } + pub async fn set_global_entrypoint_shortcut( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> anyhow::Result<()> { + let request = UiRequestData::SetGlobalEntrypointShortcut { + plugin_id, + entrypoint_id, + shortcut, + }; + + let data = self + .frontend_sender + .send_receive(request) + .await + .map_err(|err| anyhow!("error: {:?}", err))?; + + match data { + UiResponseData::Nothing => Ok(()), + UiResponseData::Err(err) => Err(err), + } + } + pub async fn set_theme(&self, theme: UiTheme) -> anyhow::Result<()> { let request = UiRequestData::SetTheme { theme }; diff --git a/rust/management_client/src/components/shortcut_selector.rs b/rust/management_client/src/components/shortcut_selector.rs index f46dcdc..d7e2488 100644 --- a/rust/management_client/src/components/shortcut_selector.rs +++ b/rust/management_client/src/components/shortcut_selector.rs @@ -1,6 +1,4 @@ -use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalShortcut; -use gauntlet_common::model::PluginId; use gauntlet_common_ui::physical_key_model; use gauntlet_common_ui::shortcut_to_text; use iced::advanced::graphics::core::event; @@ -41,68 +39,56 @@ pub struct ShortcutData { pub error: Option, } -pub fn shortcut_selector<'a, 'b: 'a, 'c, Message: 'a, Id: 'a, F>( - shortcut_id: Id, +pub fn shortcut_selector<'a, 'b: 'a, 'c, Message: 'a, F>( current_shortcut: &'b ShortcutData, on_shortcut_captured: F, overlay_class: ::Class<'a>, + in_table: bool, ) -> Element<'a, Message> where - F: 'a + Fn(Id, Option) -> Message, - Id: Clone, + F: 'a + Fn(Option) -> Message, { Element::new(ShortcutSelector::new::( - shortcut_id, current_shortcut, on_shortcut_captured, overlay_class, + in_table, )) } -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum ShortcutId { - Global, - Entrypoint { - plugin_id: PluginId, - entrypoint_id: EntrypointId, - }, -} +pub struct ShortcutSelector<'a, 'b, Message> { + on_shortcut_captured: Box) -> Message + 'a>, -pub struct ShortcutSelector<'a, 'b, Message, Id> -where - Id: Clone, -{ - on_shortcut_captured: Box) -> Message + 'a>, - - shortcut_id: Id, current_shortcut: &'b ShortcutData, content: Element<'a, Message>, popup: Element<'a, Message>, overlay_class: ::Class<'a>, + in_table: bool, } -impl<'a, 'b, 'c, Message: 'a, Id> ShortcutSelector<'a, 'b, Message, Id> -where - Id: Clone, -{ +impl<'a, 'b, 'c, Message: 'a> ShortcutSelector<'a, 'b, Message> { pub fn new( - shortcut_id: Id, current_shortcut: &'b ShortcutData, on_shortcut_captured: F, overlay_class: ::Class<'a>, + in_table: bool, ) -> Self where - F: 'a + Fn(Id, Option) -> Message, + F: 'a + Fn(Option) -> Message, { - let content = render_shortcut(¤t_shortcut.shortcut); + let content = render_shortcut(¤t_shortcut.shortcut, in_table); - let content = container(content) + let mut content = container(content) .width(Length::Fill) .height(Length::Fill) - .center_x(Length::Fill) - .center_y(Length::Fill) - .into(); + .center_y(Length::Fill); + + if !in_table { + content = content.center_x(Length::Fill); + } + + let content = content.into(); let recording_text: Element<_> = text("Recording shortcut...").into(); @@ -126,12 +112,12 @@ where Self { on_shortcut_captured: Box::new(on_shortcut_captured), - shortcut_id, current_shortcut, content, popup, overlay_class, + in_table, } } } @@ -139,12 +125,10 @@ where #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] struct State { is_capturing: bool, + is_hovering: bool, } -impl<'a, 'b, Message: 'a, Id> Widget for ShortcutSelector<'a, 'b, Message, Id> -where - Id: Clone, -{ +impl<'a, 'b, Message: 'a> Widget for ShortcutSelector<'a, 'b, Message> { fn size(&self) -> Size { Size { width: Length::Fill, @@ -171,10 +155,19 @@ where let style = if state.is_capturing { Status::Capturing } else { - Status::Active + if state.is_hovering { + Status::Hovered + } else { + Status::Active + } }; - let style = Catalog::style(theme, &::default(), style); + let style = Catalog::style( + theme, + &::default(), + style, + self.in_table, + ); draw_background(renderer, &style, layout.bounds()); @@ -233,7 +226,7 @@ where keyboard::key::Code::Backspace if modifiers.is_empty() => { state.is_capturing = false; - let message = (self.on_shortcut_captured)(self.shortcut_id.clone(), None); + let message = (self.on_shortcut_captured)(None); shell.publish(message); event::Status::Ignored @@ -241,7 +234,7 @@ where keyboard::key::Code::Escape if modifiers.is_empty() => { state.is_capturing = false; - let message = (self.on_shortcut_captured)(self.shortcut_id.clone(), None); + let message = (self.on_shortcut_captured)(None); shell.publish(message); event::Status::Ignored @@ -252,10 +245,7 @@ where Some(shortcut) => { state.is_capturing = false; - let message = (self.on_shortcut_captured)( - self.shortcut_id.clone(), - Some(shortcut), - ); + let message = (self.on_shortcut_captured)(Some(shortcut)); shell.publish(message); event::Status::Captured @@ -286,6 +276,11 @@ where event::Status::Ignored } } + mouse::Event::CursorMoved { .. } => { + state.is_hovering = cursor.is_over(layout.bounds()); + + event::Status::Captured + } _ => event::Status::Ignored, } } @@ -347,6 +342,7 @@ where #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Status { Active, + Hovered, Capturing, } @@ -355,10 +351,10 @@ pub trait Catalog { fn default<'a>() -> Self::Class<'a>; - fn style(&self, class: &Self::Class<'_>, status: Status) -> container::Style; + fn style(&self, class: &Self::Class<'_>, status: Status, transparent_background: bool) -> container::Style; } -pub fn render_shortcut<'a, Message: 'a>(shortcut: &Option) -> Element<'a, Message> { +pub fn render_shortcut<'a, Message: 'a>(shortcut: &Option, in_table: bool) -> Element<'a, Message> { let mut content: Vec> = vec![]; if let Some(current_shortcut) = shortcut { @@ -382,9 +378,13 @@ pub fn render_shortcut<'a, Message: 'a>(shortcut: &Option) -> } content.push(key_name); + } else { + if in_table { + content.push(text("Record Shortcut").class(TextStyle::Subtitle).into()); + } } - let content: Element = row(content).spacing(8.0).into(); + let content: Element = row(content).padding(8.0).spacing(8.0).into(); content } diff --git a/rust/management_client/src/theme/shortcut_selector.rs b/rust/management_client/src/theme/shortcut_selector.rs index a5a6adc..68a227b 100644 --- a/rust/management_client/src/theme/shortcut_selector.rs +++ b/rust/management_client/src/theme/shortcut_selector.rs @@ -5,8 +5,10 @@ use crate::components::shortcut_selector; use crate::components::shortcut_selector::Status; use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; +use crate::theme::BACKGROUND_LIGHTER; use crate::theme::BUTTON_BORDER_RADIUS; use crate::theme::PRIMARY; +use crate::theme::TRANSPARENT; impl shortcut_selector::Catalog for GauntletSettingsTheme { type Class<'a> = (); @@ -15,11 +17,17 @@ impl shortcut_selector::Catalog for GauntletSettingsTheme { () } - fn style(&self, _class: &Self::Class<'_>, status: Status) -> Style { + fn style(&self, _class: &Self::Class<'_>, status: Status, transparent_background: bool) -> Style { + let background = if transparent_background { + TRANSPARENT.to_iced().into() + } else { + BACKGROUND_DARKER.to_iced().into() + }; + match status { Status::Active => { Style { - background: Some(BACKGROUND_DARKER.to_iced().into()), + background: Some(background), border: Border { radius: BUTTON_BORDER_RADIUS.into(), ..Default::default() @@ -29,7 +37,7 @@ impl shortcut_selector::Catalog for GauntletSettingsTheme { } Status::Capturing => { Style { - background: Some(BACKGROUND_DARKER.to_iced().into()), + background: Some(background), border: Border { radius: BUTTON_BORDER_RADIUS.into(), width: 2.0, @@ -38,6 +46,17 @@ impl shortcut_selector::Catalog for GauntletSettingsTheme { ..Default::default() } } + Status::Hovered => { + Style { + background: Some(background), + border: Border { + radius: BUTTON_BORDER_RADIUS.into(), + width: 2.0, + color: BACKGROUND_LIGHTER.to_iced(), + }, + ..Default::default() + } + } } } } diff --git a/rust/management_client/src/ui.rs b/rust/management_client/src/ui.rs index 79436ba..c311415 100644 --- a/rust/management_client/src/ui.rs +++ b/rust/management_client/src/ui.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::time::Duration; use gauntlet_common::model::DownloadStatus; +use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::SettingsTheme; @@ -129,32 +130,35 @@ fn new() -> (ManagementAppModel, Task) { Task::batch([ font::load(BOOTSTRAP_FONT_BYTES).map(ManagementAppMsg::FontLoaded), Task::done(ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::FetchPlugins)), - Task::perform( - async { - match backend_api { - Some(backend_api) => Some(init_data(backend_api).await), - None => None, - } - }, - |shortcut| { - match shortcut { - None => ManagementAppMsg::General(ManagementAppGeneralMsgIn::Noop), - Some(init) => { - match init { - Ok(init) => { - ManagementAppMsg::General(ManagementAppGeneralMsgIn::InitSetting { + Task::future(async { + match backend_api { + Some(backend_api) => Some(init_data(backend_api).await), + None => None, + } + }) + .then(|init_data| { + match init_data { + None => Task::done(ManagementAppMsg::General(ManagementAppGeneralMsgIn::Noop)), + Some(init) => { + match init { + Ok(init) => { + Task::batch([ + Task::done(ManagementAppMsg::General(ManagementAppGeneralMsgIn::InitSetting { theme: init.theme, window_position_mode: init.window_position_mode, shortcut: init.global_shortcut, shortcut_error: init.global_shortcut_error, - }) - } - Err(err) => ManagementAppMsg::HandleBackendError(err), + })), + Task::done(ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::InitSetting { + global_entrypoint_shortcuts: init.global_entrypoint_shortcuts, + })), + ]) } + Err(err) => Task::done(ManagementAppMsg::HandleBackendError(err)), } } - }, - ), + } + }), ]), ) } @@ -164,10 +168,12 @@ struct InitSettingsData { global_shortcut_error: Option, theme: SettingsTheme, window_position_mode: WindowPositionMode, + global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, } async fn init_data(mut backend_api: BackendApi) -> Result { let (global_shortcut, global_shortcut_error) = backend_api.get_global_shortcut().await?; + let global_entrypoint_shortcuts = backend_api.get_global_entrypoint_shortcuts().await?; let theme = backend_api.get_theme().await?; @@ -176,6 +182,7 @@ async fn init_data(mut backend_api: BackendApi) -> Result Task), + ShortcutCaptured(Option), ThemeChanged(SettingsTheme), WindowPositionModeChanged(WindowPositionMode), HandleShortcutResponse { - id: ShortcutId, shortcut: Option, shortcut_error: Option, }, @@ -82,7 +80,7 @@ impl ManagementAppGeneralState { }; match message { - ManagementAppGeneralMsgIn::ShortcutCaptured(id, shortcut) => { + ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut) => { let mut backend_api = backend_api.clone(); Task::perform( @@ -97,11 +95,9 @@ impl ManagementAppGeneralState { }, move |result| { let shortcut = shortcut.clone(); - let id = id.clone(); handle_backend_error(result, move |shortcut_error| { ManagementAppGeneralMsgOut::Inner(ManagementAppGeneralMsgIn::HandleShortcutResponse { - id, shortcut, shortcut_error, }) @@ -158,7 +154,6 @@ impl ManagementAppGeneralState { ) } ManagementAppGeneralMsgIn::HandleShortcutResponse { - id, shortcut, shortcut_error, } => { @@ -174,10 +169,10 @@ impl ManagementAppGeneralState { pub fn view(&self) -> Element { let global_shortcut_selector = shortcut_selector( - ShortcutId::Global, &self.current_shortcut, - move |id, shortcut| ManagementAppGeneralMsgIn::ShortcutCaptured(id, shortcut), + move |shortcut| ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut), ContainerStyle::Box, + false, ); let global_shortcut_field: Element<_> = container(global_shortcut_selector) diff --git a/rust/management_client/src/views/plugins.rs b/rust/management_client/src/views/plugins.rs index ba3e620..3d67f7c 100644 --- a/rust/management_client/src/views/plugins.rs +++ b/rust/management_client/src/views/plugins.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::rc::Rc; use gauntlet_common::model::EntrypointId; +use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::PluginPreferenceUserData; use gauntlet_common::model::SettingsEntrypointType; @@ -48,10 +49,16 @@ mod table; #[derive(Debug, Clone)] pub enum ManagementAppPluginMsgIn { + InitSetting { + global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, + }, PluginTableMsg(PluginTableMsgIn), PluginPreferenceMsg(PluginPreferencesMsg), FetchPlugins, - PluginsReloaded(HashMap), + PluginsReloaded( + HashMap, + HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, + ), RemovePlugin { plugin_id: PluginId, }, @@ -80,6 +87,7 @@ pub struct ManagementAppPluginsState { plugin_data: Rc>, preference_user_data: HashMap<(PluginId, Option, String), PluginPreferenceUserDataState>, selected_item: SelectedItem, + global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, } impl ManagementAppPluginsState { @@ -115,6 +123,7 @@ impl ManagementAppPluginsState { preference_user_data: HashMap::new(), selected_item: select_item, table_state: PluginTableState::new(), + global_entrypoint_shortcuts: HashMap::new(), } } @@ -125,6 +134,13 @@ impl ManagementAppPluginsState { }; match message { + ManagementAppPluginMsgIn::InitSetting { + global_entrypoint_shortcuts, + } => { + self.global_entrypoint_shortcuts = global_entrypoint_shortcuts; + + Task::none() + } ManagementAppPluginMsgIn::PluginTableMsg(message) => { self.table_state.update(message).then(move |msg| { match msg { @@ -136,13 +152,16 @@ impl ManagementAppPluginsState { backend_client.set_plugin_state(plugin_id, enabled).await?; let plugins = backend_client.plugins().await?; + let global_entrypoint_shortcuts = + backend_client.get_global_entrypoint_shortcuts().await?; - Ok(plugins) + Ok((plugins, global_entrypoint_shortcuts)) }, |result| { - handle_backend_error(result, |plugins| { + handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| { ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( plugins, + global_entrypoint_shortcuts, )) }) }, @@ -162,13 +181,16 @@ impl ManagementAppPluginsState { .await?; let plugins = backend_client.plugins().await?; + let global_entrypoint_shortcuts = + backend_client.get_global_entrypoint_shortcuts().await?; - Ok(plugins) + Ok((plugins, global_entrypoint_shortcuts)) }, |result| { - handle_backend_error(result, |plugins| { + handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| { ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( plugins, + global_entrypoint_shortcuts, )) }) }, @@ -195,6 +217,31 @@ impl ManagementAppPluginsState { }, )) } + PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => { + let mut backend_client = backend_api.clone(); + + Task::perform( + async move { + backend_client + .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) + .await?; + + let plugins = backend_client.plugins().await?; + let global_entrypoint_shortcuts = + backend_client.get_global_entrypoint_shortcuts().await?; + + Ok((plugins, global_entrypoint_shortcuts)) + }, + |result| { + handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| { + ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + )) + }) + }, + ) + } } }) } @@ -207,7 +254,7 @@ impl ManagementAppPluginsState { plugin_data.plugins.clone() }; - self.apply_plugin_fetch(plugins); + self.apply_plugin_fetch(plugins, self.global_entrypoint_shortcuts.clone()); Task::none() } @@ -228,7 +275,7 @@ impl ManagementAppPluginsState { plugin_data.plugins.clone() }; - self.apply_plugin_fetch(plugins); + self.apply_plugin_fetch(plugins, self.global_entrypoint_shortcuts.clone()); Task::none() } @@ -270,18 +317,22 @@ impl ManagementAppPluginsState { Task::perform( async move { let plugins = backend_api.plugins().await?; + let global_entrypoint_shortcuts = backend_api.get_global_entrypoint_shortcuts().await?; - Ok(plugins) + Ok((plugins, global_entrypoint_shortcuts)) }, |result| { - handle_backend_error(result, |plugins| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(plugins)) + handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| { + ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + )) }) }, ) } - ManagementAppPluginMsgIn::PluginsReloaded(plugins) => { - self.apply_plugin_fetch(plugins); + ManagementAppPluginMsgIn::PluginsReloaded(plugins, shortcuts) => { + self.apply_plugin_fetch(plugins, shortcuts); Task::none() } @@ -295,12 +346,16 @@ impl ManagementAppPluginsState { backend_client.remove_plugin(plugin_id).await?; let plugins = backend_client.plugins().await?; + let global_entrypoint_shortcuts = backend_client.get_global_entrypoint_shortcuts().await?; - Ok(plugins) + Ok((plugins, global_entrypoint_shortcuts)) }, |result| { - handle_backend_error(result, |plugins| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(plugins)) + handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| { + ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + )) }) }, ) @@ -319,7 +374,13 @@ impl ManagementAppPluginsState { } } - fn apply_plugin_fetch(&mut self, plugins: HashMap) { + fn apply_plugin_fetch( + &mut self, + plugins: HashMap, + global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, + ) { + self.global_entrypoint_shortcuts = global_entrypoint_shortcuts.clone(); + self.preference_user_data = plugins .iter() .map(|(plugin_id, plugin)| { @@ -395,7 +456,7 @@ impl ManagementAppPluginsState { plugin_refs.sort_by_key(|(plugin, _)| &plugin.plugin_name); self.table_state - .apply_plugin_reload(self.plugin_data.clone(), plugin_refs) + .apply_plugin_reload(self.plugin_data.clone(), plugin_refs, global_entrypoint_shortcuts) } pub fn view(&self) -> Element { diff --git a/rust/management_client/src/views/plugins/table.rs b/rust/management_client/src/views/plugins/table.rs index c2c2bee..aa325fe 100644 --- a/rust/management_client/src/views/plugins/table.rs +++ b/rust/management_client/src/views/plugins/table.rs @@ -1,7 +1,9 @@ use std::cell::RefCell; +use std::collections::HashMap; use std::rc::Rc; use gauntlet_common::model::EntrypointId; +use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; @@ -25,7 +27,10 @@ use iced_fonts::Bootstrap; use iced_fonts::BOOTSTRAP_FONT; use iced_table::table; +use crate::components::shortcut_selector::shortcut_selector; +use crate::components::shortcut_selector::ShortcutData; use crate::theme::button::ButtonStyle; +use crate::theme::container::ContainerStyle; use crate::theme::Element; use crate::theme::GauntletSettingsTheme; use crate::views::plugins::PluginDataContainer; @@ -44,6 +49,7 @@ pub enum PluginTableMsgIn { plugin_id: PluginId, entrypoint_id: EntrypointId, }, + ShortcutCaptured(PluginId, EntrypointId, Option), } pub enum PluginTableMsgOut { @@ -64,6 +70,7 @@ pub enum PluginTableMsgOut { plugin_id: PluginId, entrypoint_id: EntrypointId, }, + ShortcutCaptured(PluginId, EntrypointId, Option), } pub struct PluginTableState { @@ -79,6 +86,7 @@ impl PluginTableState { columns: vec![ Column::new(ColumnKind::Name), Column::new(ColumnKind::Type), + Column::new(ColumnKind::Shortcut), Column::new(ColumnKind::EnableToggle), ], rows: vec![], @@ -121,6 +129,9 @@ impl PluginTableState { entrypoint_id, }) } + PluginTableMsgIn::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => { + Task::done(PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut)) + } } } @@ -128,6 +139,7 @@ impl PluginTableState { &mut self, plugin_data: Rc>, plugin_refs: Vec<(&SettingsPlugin, &SettingsPluginData)>, + global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, ) { self.rows = plugin_refs .iter() @@ -145,10 +157,16 @@ impl PluginTableState { entrypoints.sort_by_key(|entrypoint| &entrypoint.entrypoint_name); for entrypoint in entrypoints { + let global_entrypoint_shortcut = global_entrypoint_shortcuts + .get(&(plugin.plugin_id.clone(), entrypoint.entrypoint_id.clone())); + let shortcut = global_entrypoint_shortcut.map(|(shortcut, _)| shortcut).cloned(); + let error = global_entrypoint_shortcut.map(|(_, error)| error).cloned().flatten(); + let entrypoint_row = Row::Entrypoint { plugin_data: plugin_data.clone(), plugin_id: plugin.plugin_id.clone(), entrypoint_id: entrypoint.entrypoint_id.clone(), + shortcut_data: ShortcutData { shortcut, error }, }; result.push(entrypoint_row); @@ -169,11 +187,17 @@ impl PluginTableState { generated_entrypoints.sort_by_key(|entrypoint| &entrypoint.entrypoint_name); for data in generated_entrypoints { + let global_entrypoint_shortcut = global_entrypoint_shortcuts + .get(&(plugin.plugin_id.clone(), data.entrypoint_id.clone())); + let shortcut = global_entrypoint_shortcut.map(|(shortcut, _)| shortcut).cloned(); + let error = global_entrypoint_shortcut.map(|(_, error)| error).cloned().flatten(); + let generated_entrypoint_row = Row::GeneratedEntrypoint { plugin_data: plugin_data.clone(), plugin_id: plugin.plugin_id.clone(), generator_entrypoint_id: entrypoint.entrypoint_id.clone(), generated_entrypoint_id: data.entrypoint_id.clone(), + shortcut_data: ShortcutData { shortcut, error }, }; result.push(generated_entrypoint_row); @@ -222,18 +246,21 @@ enum Row { plugin_data: Rc>, plugin_id: PluginId, entrypoint_id: EntrypointId, + shortcut_data: ShortcutData, }, GeneratedEntrypoint { plugin_data: Rc>, plugin_id: PluginId, generator_entrypoint_id: EntrypointId, generated_entrypoint_id: EntrypointId, + shortcut_data: ShortcutData, }, } enum ColumnKind { Name, Type, + Shortcut, EnableToggle, } @@ -256,6 +283,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo container(text("Name")) .height(Length::Fixed(30.0)) .align_y(Alignment::Center) + .padding(padding::left(8.0)) .into() } ColumnKind::Type => { @@ -264,6 +292,12 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo .align_y(Alignment::Center) .into() } + ColumnKind::Shortcut => { + container(text("Shortcut")) + .height(Length::Fixed(30.0)) + .align_y(Alignment::Center) + .into() + } ColumnKind::EnableToggle => { container(text("Enabled")) .height(Length::Fixed(30.0)) @@ -304,6 +338,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo plugin_data, plugin_id, entrypoint_id, + .. } => { let plugin_data = plugin_data.borrow(); let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); @@ -358,6 +393,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo plugin_data, plugin_id, entrypoint_id, + .. } => { let plugin_data = plugin_data.borrow(); let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); @@ -383,6 +419,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo plugin_id, generator_entrypoint_id, generated_entrypoint_id, + .. } => { let plugin_data = plugin_data.borrow(); let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); @@ -444,11 +481,12 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo } ColumnKind::Type => { let content: Element<_> = match row_entry { - Row::Plugin { .. } => horizontal_space().into(), + Row::Plugin { .. } => container(text("Plugin")).align_y(Alignment::Center).into(), Row::Entrypoint { plugin_data, plugin_id, entrypoint_id, + .. } => { let plugin_data = plugin_data.borrow(); let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); @@ -465,7 +503,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo .align_y(Alignment::Center) .into() } - Row::GeneratedEntrypoint { .. } => horizontal_space().into(), + Row::GeneratedEntrypoint { .. } => container(text("Generated")).align_y(Alignment::Center).into(), }; let msg = match &row_entry { @@ -506,6 +544,69 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo .padding(8.0) .into() } + ColumnKind::Shortcut => { + match row_entry { + Row::Plugin { .. } => horizontal_space().into(), + Row::Entrypoint { + plugin_data, + plugin_id, + entrypoint_id, + shortcut_data, + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); + + if let SettingsEntrypointType::View | SettingsEntrypointType::Command = + entrypoint.entrypoint_type + { + let shortcut_selector = shortcut_selector( + shortcut_data, + move |shortcut| { + PluginTableMsgIn::ShortcutCaptured( + plugin_id.clone(), + entrypoint_id.clone(), + shortcut, + ) + }, + ContainerStyle::Box, + true, + ); + + container(shortcut_selector) + .height(Length::Fixed(40.0)) + .width(Length::Fill) + .into() + } else { + horizontal_space().into() + } + } + Row::GeneratedEntrypoint { + plugin_id, + generated_entrypoint_id, + shortcut_data, + .. + } => { + let shortcut_selector = shortcut_selector( + shortcut_data, + move |shortcut| { + PluginTableMsgIn::ShortcutCaptured( + plugin_id.clone(), + generated_entrypoint_id.clone(), + shortcut, + ) + }, + ContainerStyle::Box, + true, + ); + + container(shortcut_selector) + .height(Length::Fixed(40.0)) + .width(Length::Fill) + .into() + } + } + } ColumnKind::EnableToggle => { let (enabled, show_checkbox, plugin_id, entrypoint_id) = match &row_entry { Row::Plugin { plugin_data, plugin_id } => { @@ -518,6 +619,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo plugin_data, entrypoint_id, plugin_id, + .. } => { let plugin_data = plugin_data.borrow(); let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); @@ -571,7 +673,8 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo fn width(&self) -> f32 { match self.kind { ColumnKind::Name => 300.0, - ColumnKind::Type => 300.0, + ColumnKind::Type => 100.0, + ColumnKind::Shortcut => 200.0, ColumnKind::EnableToggle => 75.0, } } diff --git a/rust/scenario_runner/src/frontend_mock.rs b/rust/scenario_runner/src/frontend_mock.rs index e3d2d19..8923324 100644 --- a/rust/scenario_runner/src/frontend_mock.rs +++ b/rust/scenario_runner/src/frontend_mock.rs @@ -163,6 +163,7 @@ async fn request_loop( unreachable!() } UiRequestData::SetGlobalShortcut { .. } + | UiRequestData::SetGlobalEntrypointShortcut { .. } | UiRequestData::SetWindowPositionMode { .. } | UiRequestData::RequestSearchResultUpdate => { // noop diff --git a/rust/server/db_migrations/13_remove_old_global_shortcut.sql b/rust/server/db_migrations/13_remove_old_global_shortcut.sql new file mode 100644 index 0000000..c90ac04 --- /dev/null +++ b/rust/server/db_migrations/13_remove_old_global_shortcut.sql @@ -0,0 +1 @@ +ALTER TABLE settings_data DROP COLUMN global_shortcut; diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 842e724..d70cfb0 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -256,8 +256,13 @@ async fn handle_request( BackendResponseData::SetupData { data } } - BackendRequestData::SetupResponse { global_shortcut_error } => { - application_manager.setup_response(global_shortcut_error).await?; + BackendRequestData::SetupResponse { + global_shortcut_error, + global_entrypoint_shortcuts_errors, + } => { + application_manager + .setup_response(global_shortcut_error, global_entrypoint_shortcuts_errors) + .await?; BackendResponseData::Nothing } @@ -359,6 +364,16 @@ async fn handle_request( BackendResponseData::InlineViewShortcuts { shortcuts } } + BackendRequestData::RunEntrypoint { + plugin_id, + entrypoint_id, + } => { + application_manager + .run_action(plugin_id, entrypoint_id, ":primary".to_string()) + .await?; + + BackendResponseData::Nothing + } }; Ok(response_data) diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index 9d1144d..c91388c 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -8,6 +8,7 @@ use futures::future::join_all; use futures::StreamExt; use futures::TryStreamExt; use gauntlet_common::dirs::Dirs; +use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; @@ -215,31 +216,73 @@ pub struct DbPluginActionUserData { #[derive(sqlx::FromRow)] struct DbSettingsDataContainer { - #[sqlx(json)] - pub global_shortcut: DbSettingsGlobalShortcutData, // separate field because legacy // #[sqlx(json)] // https://github.com/launchbadge/sqlx/issues/2849 pub settings: Option>, } #[derive(Debug, Deserialize, Serialize)] -pub struct DbSettingsGlobalShortcutData { +pub struct DbSettingsShortcut { pub physical_key: String, pub modifier_shift: bool, pub modifier_control: bool, pub modifier_alt: bool, pub modifier_meta: bool, - #[serde(default)] - pub unset: bool, - #[serde(default)] +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct DbSettingsGlobalShortcutData { + pub shortcut: DbSettingsShortcut, pub error: Option, } -#[derive(Debug, Default, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize)] +pub struct DbSettingsGlobalEntrypointShortcutData { + pub plugin_id: String, + pub entrypoint_id: String, + pub shortcut: DbSettingsGlobalShortcutData, +} + +#[derive(Debug, Deserialize, Serialize)] pub struct DbSettings { // none means auto-detect pub theme: Option, - // none is static + // none is static mode pub window_position_mode: Option, + // none is unset, if whole settings object is unset, it is likely a first start and default shortcut will be used + pub global_shortcut: Option, + pub global_entrypoint_shortcuts: Option>, +} + +impl Default for DbSettings { + fn default() -> Self { + let default_global_shortcut = if cfg!(target_os = "windows") { + DbSettingsShortcut { + physical_key: PhysicalKey::Space.to_value(), + modifier_shift: false, + modifier_control: false, + modifier_alt: true, + modifier_meta: false, + } + } else { + DbSettingsShortcut { + physical_key: PhysicalKey::Space.to_value(), + modifier_shift: false, + modifier_control: false, + modifier_alt: false, + modifier_meta: true, + } + }; + + DbSettings { + theme: None, + window_position_mode: None, + global_shortcut: Some(DbSettingsGlobalShortcutData { + shortcut: default_global_shortcut, + error: None, + }), + global_entrypoint_shortcuts: None, + } + } } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -358,17 +401,28 @@ impl DataDbRepository { .await .context("Unable to open database connection")?; - // TODO backup before migration? up to 5 backups? - MIGRATOR.run(&pool).await.context("Unable apply database migration")?; - let db_repository = Self { pool }; + let _ = db_repository.migrate_global_shortcut_to_settings().await; + + db_repository.run_migrations().await?; + db_repository.apply_uuid_default_value().await?; db_repository.remove_legacy_bundled_plugins().await?; Ok(db_repository) } + async fn run_migrations(&self) -> anyhow::Result<()> { + // TODO backup before migration? up to 5 backups? + MIGRATOR + .run(&self.pool) + .await + .context("Unable apply database migration")?; + + Ok(()) + } + async fn apply_uuid_default_value(&self) -> anyhow::Result<()> { // language=SQLite let mut stream = self.pool.fetch(sqlx::query("SELECT id FROM plugin WHERE uuid IS NULL")); @@ -411,6 +465,71 @@ impl DataDbRepository { Ok(()) } + async fn migrate_global_shortcut_to_settings(&self) -> anyhow::Result<()> { + #[derive(sqlx::FromRow)] + struct DbSettingsDataOldContainer { + #[sqlx(json)] + pub global_shortcut: DbSettingsGlobalShortcutOldData, + pub settings: Option>, + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct DbSettingsGlobalShortcutOldData { + pub physical_key: String, + pub modifier_shift: bool, + pub modifier_control: bool, + pub modifier_alt: bool, + pub modifier_meta: bool, + #[serde(default)] + pub unset: bool, + #[serde(default)] + pub error: Option, + } + + // language=SQLite + let mut data = sqlx::query_as::<_, DbSettingsDataOldContainer>("SELECT * FROM settings_data") + .fetch_one(&self.pool) + .await?; + + let shortcut_data = &data.global_shortcut; + + let shortcut = if shortcut_data.unset { + None + } else { + Some(DbSettingsGlobalShortcutData { + shortcut: DbSettingsShortcut { + physical_key: shortcut_data.physical_key.clone(), + modifier_shift: shortcut_data.modifier_shift, + modifier_control: shortcut_data.modifier_control, + modifier_alt: shortcut_data.modifier_alt, + modifier_meta: shortcut_data.modifier_meta, + }, + error: None, + }) + }; + + if let Some(settings) = &mut data.settings { + settings.global_shortcut = shortcut; + } + + // language=SQLite + let sql = r#" + INSERT INTO settings_data (id, global_shortcut, settings) + VALUES(?1, ?2, ?3) + ON CONFLICT (id) + DO UPDATE SET settings = ?3 + "#; + + sqlx::query(sql) + .bind(SETTINGS_DATA_ID) + .bind(Json(data.global_shortcut)) + .bind(data.settings) + .execute(&self.pool) + .await?; + + Ok(()) + } + pub async fn list_plugins(&self) -> anyhow::Result> { // language=SQLite let plugins = sqlx::query_as::<_, DbReadPlugin>("SELECT * FROM plugin") @@ -932,85 +1051,9 @@ impl DataDbRepository { Ok(()) } - pub async fn set_global_shortcut( - &self, - shortcut: Option, - error: Option, - ) -> anyhow::Result<()> { - // language=SQLite - let sql = r#" - INSERT INTO settings_data (id, global_shortcut) - VALUES(?1, ?2) - ON CONFLICT (id) - DO UPDATE SET global_shortcut = ?2 - "#; - - let shortcut_data = match shortcut { - None => { - DbSettingsGlobalShortcutData { - physical_key: "".to_string(), - modifier_shift: false, - modifier_control: false, - modifier_alt: false, - modifier_meta: false, - unset: true, - error, - } - } - Some(shortcut) => { - DbSettingsGlobalShortcutData { - physical_key: shortcut.physical_key.to_value(), - modifier_shift: shortcut.modifier_shift, - modifier_control: shortcut.modifier_control, - modifier_alt: shortcut.modifier_alt, - modifier_meta: shortcut.modifier_meta, - unset: false, - error, - } - } - }; - - sqlx::query(sql) - .bind(SETTINGS_DATA_ID) - .bind(Json(shortcut_data)) - .execute(&self.pool) - .await?; - - Ok(()) - } - - pub async fn get_global_shortcut(&self) -> anyhow::Result, Option)>> { - // language=SQLite - let data = sqlx::query_as::<_, DbSettingsDataContainer>("SELECT * FROM settings_data") - .fetch_optional(&self.pool) - .await; - - match data { - Ok(Some(data)) => { - let shortcut_data = data.global_shortcut; - - let shortcut = if shortcut_data.unset { - None - } else { - Some(PhysicalShortcut { - physical_key: PhysicalKey::from_value(shortcut_data.physical_key), - modifier_shift: shortcut_data.modifier_shift, - modifier_control: shortcut_data.modifier_control, - modifier_alt: shortcut_data.modifier_alt, - modifier_meta: shortcut_data.modifier_meta, - }) - }; - - Ok(Some((shortcut, shortcut_data.error))) - } - Ok(None) => Ok(None), - Err(err) => Err(anyhow!("Unable to get global shortcut from db: {:?}", err)), - } - } - pub async fn get_settings(&self) -> anyhow::Result { // language=SQLite - let settings = sqlx::query_as::<_, DbSettingsDataContainer>("SELECT * FROM settings_data") + let settings = sqlx::query_as::<_, DbSettingsDataContainer>("SELECT settings FROM settings_data") .fetch_optional(&self.pool) .await?; @@ -1022,31 +1065,14 @@ impl DataDbRepository { pub async fn set_settings(&self, value: DbSettings) -> anyhow::Result<()> { // language=SQLite let sql = r#" - INSERT INTO settings_data (id, global_shortcut, settings) - VALUES(?1, ?2, ?3) + INSERT INTO settings_data (id, settings) + VALUES(?1, ?2) ON CONFLICT (id) - DO UPDATE SET settings = ?3 + DO UPDATE SET settings = ?2 "#; - let data = sqlx::query_as::<_, DbSettingsDataContainer>("SELECT * FROM settings_data") - .fetch_optional(&self.pool) - .await? - .unwrap_or(DbSettingsDataContainer { - global_shortcut: DbSettingsGlobalShortcutData { - physical_key: "".to_string(), - modifier_shift: false, - modifier_control: false, - modifier_alt: false, - modifier_meta: false, - unset: true, - error: None, - }, - settings: None, - }); - sqlx::query(sql) .bind(SETTINGS_DATA_ID) - .bind(Json(data.global_shortcut)) .bind(Json(value)) .execute(&self.pool) .await?; diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 1d193f2..7929b09 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -143,7 +143,14 @@ impl ApplicationManager { pub async fn setup_data(&self) -> anyhow::Result { let window_position_file = self.dirs.window_position(); let theme = self.settings.effective_theme().await?; - let global_shortcut = self.settings.effective_global_shortcut().await?; + let global_shortcut = self.settings.global_shortcut().await?.map(|(shortcut, _)| shortcut); + let global_entrypoint_shortcuts = self + .settings + .global_entrypoint_shortcuts() + .await? + .into_iter() + .map(|((plugin_id, entrypoint_id), (shortcut, _))| ((plugin_id, entrypoint_id), shortcut)) + .collect(); let window_position_mode = self.settings.window_position_mode_setting().await?; let close_on_unfocus = self.config_reader.close_on_unfocus(); @@ -151,14 +158,25 @@ impl ApplicationManager { window_position_file: Some(window_position_file), theme, global_shortcut, + global_entrypoint_shortcuts, close_on_unfocus, window_position_mode, }) } - pub async fn setup_response(&self, global_shortcut_error: Option) -> anyhow::Result<()> { + pub async fn setup_response( + &self, + global_shortcut_error: Option, + global_entrypoint_shortcuts_errors: HashMap<(PluginId, EntrypointId), Option>, + ) -> anyhow::Result<()> { self.settings.set_global_shortcut_error(global_shortcut_error).await?; + for ((plugin_id, entrypoint_id), error) in global_entrypoint_shortcuts_errors { + self.settings + .set_global_entrypoint_shortcut_error(plugin_id, entrypoint_id, error) + .await?; + } + Ok(()) } @@ -571,10 +589,27 @@ impl ApplicationManager { self.settings.set_global_shortcut(shortcut).await } - pub async fn get_global_shortcut(&self) -> anyhow::Result, Option)>> { + pub async fn get_global_shortcut(&self) -> anyhow::Result)>> { self.settings.global_shortcut().await } + pub async fn set_global_entrypoint_shortcut( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> anyhow::Result<()> { + self.settings + .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) + .await + } + + pub async fn get_global_entrypoint_shortcut( + &self, + ) -> anyhow::Result)>> { + self.settings.global_entrypoint_shortcuts().await + } + pub async fn set_theme(&self, theme: SettingsTheme) -> anyhow::Result<()> { self.settings.set_theme_setting(theme).await } diff --git a/rust/server/src/plugins/settings.rs b/rust/server/src/plugins/settings.rs index 1ec4d33..7ec00b2 100644 --- a/rust/server/src/plugins/settings.rs +++ b/rust/server/src/plugins/settings.rs @@ -1,16 +1,22 @@ -use std::env::consts::OS; +use std::collections::hash_map::Entry; +use std::collections::HashMap; use anyhow::anyhow; use dark_light::Mode; use gauntlet_common::dirs::Dirs; +use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; +use gauntlet_common::model::PluginId; use gauntlet_common::model::SettingsTheme; use gauntlet_common::model::UiTheme; use gauntlet_common::model::WindowPositionMode; use gauntlet_common::rpc::frontend_api::FrontendApi; use crate::plugins::data_db_repository::DataDbRepository; +use crate::plugins::data_db_repository::DbSettingsGlobalEntrypointShortcutData; +use crate::plugins::data_db_repository::DbSettingsGlobalShortcutData; +use crate::plugins::data_db_repository::DbSettingsShortcut; use crate::plugins::data_db_repository::DbTheme; use crate::plugins::data_db_repository::DbWindowPositionMode; use crate::plugins::theme::read_theme_file; @@ -33,33 +39,22 @@ impl Settings { }) } - pub async fn effective_global_shortcut(&self) -> anyhow::Result> { - match self.global_shortcut().await? { - None => { - if cfg!(target_os = "windows") { - Ok(Some(PhysicalShortcut { - physical_key: PhysicalKey::Space, - modifier_shift: false, - modifier_control: false, - modifier_alt: true, - modifier_meta: false, - })) - } else { - Ok(Some(PhysicalShortcut { - physical_key: PhysicalKey::Space, - modifier_shift: false, - modifier_control: false, - modifier_alt: false, - modifier_meta: true, - })) - } - } - Some((shortcut, _)) => Ok(shortcut), - } - } + pub async fn global_shortcut(&self) -> anyhow::Result)>> { + let settings = self.repository.get_settings().await?; - pub async fn global_shortcut(&self) -> anyhow::Result, Option)>> { - self.repository.get_global_shortcut().await + let data = settings.global_shortcut.map(|data| { + let shortcut = PhysicalShortcut { + physical_key: PhysicalKey::from_value(data.shortcut.physical_key), + modifier_shift: data.shortcut.modifier_shift, + modifier_control: data.shortcut.modifier_control, + modifier_alt: data.shortcut.modifier_alt, + modifier_meta: data.shortcut.modifier_meta, + }; + + (shortcut, data.error) + }); + + Ok(data) } pub async fn set_global_shortcut(&self, shortcut: Option) -> anyhow::Result<()> { @@ -67,19 +62,182 @@ impl Settings { let db_err = err.as_ref().map_err(|err| format!("{:#}", err)).err(); - self.repository.set_global_shortcut(shortcut, db_err).await?; + let mut settings = self.repository.get_settings().await?; + + settings.global_shortcut = shortcut.map(|shortcut| { + DbSettingsGlobalShortcutData { + shortcut: DbSettingsShortcut { + physical_key: shortcut.physical_key.to_value(), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + }, + error: db_err, + } + }); + + self.repository.set_settings(settings).await?; err } pub async fn set_global_shortcut_error(&self, error: Option) -> anyhow::Result<()> { - match self.repository.get_global_shortcut().await? { - None => {} - Some((shortcut, _)) => { - self.repository.set_global_shortcut(shortcut, error).await?; + let mut settings = self.repository.get_settings().await?; + + if let Some(data) = &mut settings.global_shortcut { + data.error = error + } + + self.repository.set_settings(settings).await?; + + Ok(()) + } + + pub async fn global_entrypoint_shortcuts( + &self, + ) -> anyhow::Result)>> { + let settings = self.repository.get_settings().await?; + let data: HashMap<_, _> = settings + .global_entrypoint_shortcuts + .unwrap_or_default() + .into_iter() + .map(|data| { + let shortcut = data.shortcut.shortcut; + let error = data.shortcut.error; + + let shortcut = PhysicalShortcut { + physical_key: PhysicalKey::from_value(shortcut.physical_key), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + }; + + ( + ( + PluginId::from_string(data.plugin_id), + EntrypointId::from_string(data.entrypoint_id), + ), + (shortcut, error), + ) + }) + .collect(); + + Ok(data) + } + + pub async fn set_global_entrypoint_shortcut( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> anyhow::Result<()> { + let err = self + .frontend_api + .set_global_entrypoint_shortcut(plugin_id.clone(), entrypoint_id.clone(), shortcut.clone()) + .await; + + let db_err = err.as_ref().map_err(|err| format!("{:#}", err)).err(); + + let mut settings = self.repository.get_settings().await?; + + let mut shortcuts: HashMap<_, _> = settings + .global_entrypoint_shortcuts + .unwrap_or_default() + .into_iter() + .map(|data| { + ( + ( + PluginId::from_string(data.plugin_id), + EntrypointId::from_string(data.entrypoint_id), + ), + data.shortcut, + ) + }) + .collect(); + + match shortcut { + None => { + shortcuts.remove(&(plugin_id, entrypoint_id)); } + Some(shortcut) => { + shortcuts.insert( + (plugin_id, entrypoint_id), + DbSettingsGlobalShortcutData { + shortcut: DbSettingsShortcut { + physical_key: shortcut.physical_key.to_value(), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + }, + error: db_err, + }, + ); + } + } + + let global_entrypoint_shortcuts = shortcuts + .into_iter() + .map(|((plugin_id, entrypoint_id), data)| { + DbSettingsGlobalEntrypointShortcutData { + plugin_id: plugin_id.to_string(), + entrypoint_id: entrypoint_id.to_string(), + shortcut: data, + } + }) + .collect(); + + settings.global_entrypoint_shortcuts = Some(global_entrypoint_shortcuts); + + self.repository.set_settings(settings).await?; + + err + } + + pub async fn set_global_entrypoint_shortcut_error( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + error: Option, + ) -> anyhow::Result<()> { + let mut settings = self.repository.get_settings().await?; + + let mut shortcuts: HashMap<_, _> = settings + .global_entrypoint_shortcuts + .unwrap_or_default() + .into_iter() + .map(|data| { + ( + ( + PluginId::from_string(data.plugin_id), + EntrypointId::from_string(data.entrypoint_id), + ), + data.shortcut, + ) + }) + .collect(); + + if let Some(data) = shortcuts.get_mut(&(plugin_id, entrypoint_id)) { + data.error = error; }; + let global_entrypoint_shortcuts = shortcuts + .into_iter() + .map(|((plugin_id, entrypoint_id), data)| { + DbSettingsGlobalEntrypointShortcutData { + plugin_id: plugin_id.to_string(), + entrypoint_id: entrypoint_id.to_string(), + shortcut: data, + } + }) + .collect(); + + settings.global_entrypoint_shortcuts = Some(global_entrypoint_shortcuts); + + self.repository.set_settings(settings).await?; + Ok(()) } diff --git a/rust/server/src/rpc.rs b/rust/server/src/rpc.rs index 179c635..e5390cf 100644 --- a/rust/server/src/rpc.rs +++ b/rust/server/src/rpc.rs @@ -126,11 +126,29 @@ impl BackendServer for BackendServerImpl { .application_manager .get_global_shortcut() .await? + .map(|(shortcut, error)| (Some(shortcut), error)) .unwrap_or((None, None)); Ok(result) } + async fn set_global_entrypoint_shortcut( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> anyhow::Result<()> { + self.application_manager + .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) + .await + } + + async fn get_global_entrypoint_shortcuts( + &self, + ) -> anyhow::Result)>> { + self.application_manager.get_global_entrypoint_shortcut().await + } + async fn set_theme(&self, theme: SettingsTheme) -> anyhow::Result<()> { self.application_manager.set_theme(theme).await } diff --git a/schema/backend.proto b/schema/backend.proto index 1ea2351..ab8154a 100644 --- a/schema/backend.proto +++ b/schema/backend.proto @@ -23,6 +23,9 @@ service RpcBackend { rpc SetGlobalShortcut (RpcSetGlobalShortcutRequest) returns (RpcSetGlobalShortcutResponse); rpc GetGlobalShortcut (RpcGetGlobalShortcutRequest) returns (RpcGetGlobalShortcutResponse); + rpc SetGlobalEntrypointShortcut (RpcSetGlobalEntrypointShortcutRequest) returns (RpcSetGlobalEntrypointShortcutResponse); + rpc GetGlobalEntrypointShortcut (RpcGetGlobalEntrypointShortcutRequest) returns (RpcGetGlobalEntrypointShortcutResponse); + rpc SetTheme (RpcSetThemeRequest) returns (RpcSetThemeResponse); rpc GetTheme (RpcGetThemeRequest) returns (RpcGetThemeResponse); @@ -107,6 +110,30 @@ message RpcGetGlobalShortcutResponse { optional string error = 2; } +message RpcSetGlobalEntrypointShortcutRequest { + string plugin_id = 1; + string entrypoint_id = 2; + optional RpcShortcut shortcut = 3; +} + +message RpcSetGlobalEntrypointShortcutResponse { + optional string error = 1; +} + +message RpcGetGlobalEntrypointShortcutRequest { +} + +message RpcGetGlobalEntrypointShortcutResponse { + repeated RpcGetGlobalEntrypointShortcut shortcuts = 1; +} + +message RpcGetGlobalEntrypointShortcut { + string plugin_id = 1; + string entrypoint_id = 2; + RpcShortcut shortcut = 3; + optional string error = 4; +} + message RpcSetThemeRequest { string theme = 1; } From 02f9d366fe8d75dfdd03548b6bbfe38bc565c012 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 10 Apr 2025 20:32:41 +0200 Subject: [PATCH 046/170] Pin wix toolkit version in github actions --- .github/workflows/setup-windows.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/setup-windows.yaml b/.github/workflows/setup-windows.yaml index e71f519..7da7923 100644 --- a/.github/workflows/setup-windows.yaml +++ b/.github/workflows/setup-windows.yaml @@ -27,7 +27,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: choco install protoc - - run: dotnet tool install --global wix + - run: dotnet tool install --global wix --version 5.0.2 - run: wix extension add -g WixToolset.Util.wixext/5.0.2 - uses: Swatinem/rust-cache@v2 From 6fafe236584d442bf0816135345eab561c8d52a4 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 11 Apr 2025 18:02:10 +0200 Subject: [PATCH 047/170] Fix window sometimes not being hidden on x11 --- rust/client/src/ui/mod.rs | 13 +++++++++++-- rust/client/src/ui/platform/linux.rs | 8 ++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 5cff3b4..6bee303 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -365,6 +365,7 @@ pub enum AppMsg { #[cfg(target_os = "linux")] X11ActiveWindowChanged { window: u32, + wm_name: Option, }, RunEntrypoint { plugin_id: PluginId, @@ -1683,10 +1684,18 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { ]) } #[cfg(target_os = "linux")] - AppMsg::X11ActiveWindowChanged { window } => { + AppMsg::X11ActiveWindowChanged { window, wm_name } => { if state.x11_active_window != Some(window) { state.x11_active_window = Some(window); - Task::done(AppMsg::HideWindow) + if let Some(wm_name) = &wm_name { + if wm_name != "gauntlet" { + Task::done(AppMsg::HideWindow) + } else { + Task::none() + } + } else { + Task::none() + } } else { Task::none() } diff --git a/rust/client/src/ui/platform/linux.rs b/rust/client/src/ui/platform/linux.rs index ea66203..0948866 100644 --- a/rust/client/src/ui/platform/linux.rs +++ b/rust/client/src/ui/platform/linux.rs @@ -31,14 +31,10 @@ pub fn listen_on_x11_active_window_change(sender: Sender, handle: Handle continue; }; - if let Ok(wm_name) = fetch_app_wm_name(&conn, window) { - if wm_name == "gauntlet" { - continue; - } - } + let wm_name = fetch_app_wm_name(&conn, window).ok(); let mut sender = sender.clone(); - handle.spawn(async move { sender.send(AppMsg::X11ActiveWindowChanged { window }).await }); + handle.spawn(async move { sender.send(AppMsg::X11ActiveWindowChanged { window, wm_name }).await }); } } } From 8439f96430011a01418f751f415efb1a8fd01d35 Mon Sep 17 00:00:00 2001 From: Benno <107504783+BennoCrafter@users.noreply.github.com> Date: Sun, 13 Apr 2025 18:29:29 +0200 Subject: [PATCH 048/170] fix: ensure window state resets correctly (#65) --- rust/client/src/ui/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 6bee303..a957b28 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -2441,6 +2441,10 @@ impl AppModel { GlobalState::PendingPluginView { .. } => {} } + if self.pending_window_state_reset { + commands.push(self.reset_window_state()); + } + Task::batch(commands) } @@ -2473,11 +2477,7 @@ impl AppModel { window::change_mode(self.main_window_id, Mode::Windowed), ]); - let mut commands = vec![open_task]; - - if self.pending_window_state_reset { - commands.push(self.reset_window_state()); - } + let commands = vec![open_task]; Task::batch(commands) } From 4a99160427a59cee3f60699512cc7f3760b28474 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 13 Apr 2025 18:59:48 +0200 Subject: [PATCH 049/170] Fix plugin view being empty if the window is closed by toggling global shortcut --- rust/client/src/ui/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index a957b28..9d78025 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -2430,7 +2430,9 @@ impl AppModel { plugin_view_data: PluginViewData { plugin_id, .. }, .. } => { - commands.push(self.close_plugin_view(plugin_id.clone())); + if reset_state { + commands.push(self.close_plugin_view(plugin_id.clone())); + } } GlobalState::MainView { focused_search_result, .. @@ -2477,9 +2479,7 @@ impl AppModel { window::change_mode(self.main_window_id, Mode::Windowed), ]); - let commands = vec![open_task]; - - Task::batch(commands) + open_task } fn reset_window_state(&mut self) -> Task { From bdda05f579ff1cc028cb59ddbfd84d3b2bf2d154 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 13 Apr 2025 19:32:35 +0200 Subject: [PATCH 050/170] Update CHANGELOG.md --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8090ebf..449f537 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,30 @@ For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://git ## [Unreleased] +- Entrypoints can now be assigned a global shortcut in Settings UI + - On Linux X11 there is a known bug which will cause a crash and prevent server startup if attempted to register global shortcut that is already registered by another application +- Generated shortcuts are now listed in the Settings UI + +### Plugins +- Added authors field to Plugin Manifest + - `gauntlet.authors.*.name` - String + - `gauntlet.authors.*.uris` - List of strings. URIs that identify the author. Can be a link to social media page or an email (if email it should begin with `mailto:` schema) +- Added `$schema` field to Plugin Manifest which takes URL to the JSON Schema file + - Some editors use it to validate the content of the file + - Currently, the schema file is located inside the repository at path `https://raw.githubusercontent.com/project-gauntlet/gauntlet/refs/heads/main/docs/schema/plugin_manifest.schema.json` but at some point this will change + +### Themes +- Tweaked window border color of macOS Dark theme on non-macos platforms not as bright + +### UI/UX improvements +- Reworked shortcut selector widget in Setting UI + +### Fixes +- Fixed window sometimes not being hidden on X11 +- Main view search list is now refreshed when window being hidden instead of when it is shown, to avoid the list being changed after window was opened +- Fixed plugin view being empty if the window is closed by toggling global shortcut +- Fixes plugin view not being opened properly if it is created using global shortcut or cli command + ## [17] - 2025-03-15 - Fixed crash when typing/clicking fast in React-created plugin ui - Fixed events sometimes overwriting other parallel events when typing/clicking fast in React-created plugin ui From 25a10d573ce4508e5a7efc6c9dde01349a157509 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 13 Apr 2025 19:36:16 +0200 Subject: [PATCH 051/170] Bump nix version --- nix/overlay.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/overlay.nix b/nix/overlay.nix index 37cf374..401929d 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -5,7 +5,7 @@ }: { flake.overlays.default = final: _: let # These must be updated following the instructions in ./nix/README.md when dependencies are updated or the version bumped - version = "v17"; + version = "v18"; npmDepsHash = "sha256-BOnKpFS0ofIZbmXcyEzHzHgvbgeg3QUXnC79BwKO6P8="; RUSTY_V8_ARCHIVE = fetchRustyV8 "130.0.2" { aarch64-darwin = "sha256-aWZ/4Q4Wttx37xOdBmTCPGP+eYGhr4CM1UkYq8pC7Qs="; From 7143ed2471a1ab5d86226ad0e5bd23dadf9989dd Mon Sep 17 00:00:00 2001 From: Exidex <16986685+Exidex@users.noreply.github.com> Date: Sun, 13 Apr 2025 17:41:09 +0000 Subject: [PATCH 052/170] Prepare for v18 release --- CHANGELOG.md | 2 ++ VERSION | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 449f537..4a66fe1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://git ## [Unreleased] +## [18] - 2025-04-13 + - Entrypoints can now be assigned a global shortcut in Settings UI - On Linux X11 there is a known bug which will cause a crash and prevent server startup if attempted to register global shortcut that is already registered by another application - Generated shortcuts are now listed in the Settings UI diff --git a/VERSION b/VERSION index 8e2afd3..25bf17f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -17 \ No newline at end of file +18 \ No newline at end of file From 61eca05f4253e71ae6769db7f60f8866ddcc55a0 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 13 Apr 2025 19:52:35 +0200 Subject: [PATCH 053/170] Add contributions to CHANGELOG.md --- CHANGELOG.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a66fe1..1d21e91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,11 @@ For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://git ## [18] - 2025-04-13 -- Entrypoints can now be assigned a global shortcut in Settings UI +### General + +- Entrypoints can now be run or opened using global shortcut set Settings UI - On Linux X11 there is a known bug which will cause a crash and prevent server startup if attempted to register global shortcut that is already registered by another application -- Generated shortcuts are now listed in the Settings UI +- Generated Entrypoints are now listed in the Settings UI ### Plugins - Added authors field to Plugin Manifest @@ -24,16 +26,16 @@ For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://git - Currently, the schema file is located inside the repository at path `https://raw.githubusercontent.com/project-gauntlet/gauntlet/refs/heads/main/docs/schema/plugin_manifest.schema.json` but at some point this will change ### Themes -- Tweaked window border color of macOS Dark theme on non-macos platforms not as bright +- Tweaked window border color of macOS Dark theme on non-macos platforms to be not as bright ### UI/UX improvements - Reworked shortcut selector widget in Setting UI ### Fixes - Fixed window sometimes not being hidden on X11 -- Main view search list is now refreshed when window being hidden instead of when it is shown, to avoid the list being changed after window was opened +- Main view search list is now refreshed when window being hidden instead of when it is shown, to avoid the list being changed after window was opened (contributed by @BennoCrafter) - Fixed plugin view being empty if the window is closed by toggling global shortcut -- Fixes plugin view not being opened properly if it is created using global shortcut or cli command +- Fixes plugin view not being opened properly if it is created using global shortcut or cli command (contributed by @BennoCrafter) ## [17] - 2025-03-15 - Fixed crash when typing/clicking fast in React-created plugin ui From b8f07d502818668df246d266da58c4e98d87dfd4 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 20 Apr 2025 16:15:08 +0200 Subject: [PATCH 054/170] Unify Vec usage to ArrayBuffer in js. Fixes icon in EntrypointGenerator requiring number[] --- bundled_plugins/gauntlet/gauntlet.toml | 2 +- .../gauntlet/src/window/shared.tsx | 7 ++-- js/api/src/helpers.ts | 8 ++-- js/react_renderer/src/renderer.ts | 6 +-- js/typings/index.d.ts | 8 ++-- rust/plugin_runtime/src/assets.rs | 2 +- rust/plugin_runtime/src/clipboard.rs | 22 ++++------ rust/plugin_runtime/src/model.rs | 40 ++++++++++++++++++- .../src/plugins/applications.rs | 11 ++--- .../src/plugins/applications/linux/mod.rs | 4 +- .../src/plugins/applications/macos.rs | 5 ++- .../src/plugins/applications/windows.rs | 5 ++- rust/plugin_runtime/src/search.rs | 20 +++++++++- 13 files changed, 94 insertions(+), 46 deletions(-) diff --git a/bundled_plugins/gauntlet/gauntlet.toml b/bundled_plugins/gauntlet/gauntlet.toml index 28bc064..1859162 100644 --- a/bundled_plugins/gauntlet/gauntlet.toml +++ b/bundled_plugins/gauntlet/gauntlet.toml @@ -29,7 +29,7 @@ enum_values = [ [[entrypoint]] id = 'windows' -name = 'All Open Windows' +name = 'Open Windows' path = 'src/windows.tsx' type = 'view' description = 'Show all open windows' diff --git a/bundled_plugins/gauntlet/src/window/shared.tsx b/bundled_plugins/gauntlet/src/window/shared.tsx index 136d25d..eecb927 100644 --- a/bundled_plugins/gauntlet/src/window/shared.tsx +++ b/bundled_plugins/gauntlet/src/window/shared.tsx @@ -18,7 +18,6 @@ export function ListOfWindows({ windows, focus }: { windows: OpenWindowData[], f onAction={(id: string | undefined) => { if (id) { focus(id) - console.log("focus: " + id) return { close: true } } }} @@ -123,9 +122,9 @@ export function applicationAccessories(id: string, experimentalWindowTracking: b if (appWindows.length == 0) { return [] } else if (appWindows.length == 1) { - return [{ text: "1 window open" }] + return [{ text: "1 window" }] } else if (appWindows.length > 1) { - return [{ text: `${appWindows.length} windows open` }] + return [{ text: `${appWindows.length} windows` }] } else { return [] } @@ -182,4 +181,4 @@ export function openLinuxApplication(appId: string) { return () => { linux_open_application(appId) } -} \ No newline at end of file +} diff --git a/js/api/src/helpers.ts b/js/api/src/helpers.ts index fa9c3b4..b662267 100644 --- a/js/api/src/helpers.ts +++ b/js/api/src/helpers.ts @@ -84,7 +84,7 @@ export const Clipboard: Clipboard = { } if (data.png_data) { - result["image/png"] = new Uint8Array(data.png_data).buffer; + result["image/png"] = data.png_data; } return result @@ -92,18 +92,18 @@ export const Clipboard: Clipboard = { readText: async function (): Promise { return await clipboard_read_text() }, - write: async function (data: { "text/plain"?: string | undefined; "image/png"?: ArrayBuffer | undefined; }): Promise { + write: async function (data: { "text/plain"?: string | undefined; "image/png"?: Uint8Array | undefined; }): Promise { const text_data = data["text/plain"]; const png_data = data["image/png"]; - const write_data: { text_data?: string, png_data?: number[] } = {}; + const write_data: { text_data?: string, png_data?: ArrayBuffer } = {}; if (text_data) { write_data.text_data = text_data; } if (png_data) { - write_data.png_data = Array.from(new Uint8Array(png_data)); + write_data.png_data = png_data; } return await clipboard_write(write_data) diff --git a/js/react_renderer/src/renderer.ts b/js/react_renderer/src/renderer.ts index 0ac0984..6e8fb7a 100644 --- a/js/react_renderer/src/renderer.ts +++ b/js/react_renderer/src/renderer.ts @@ -117,13 +117,11 @@ export function useGauntletContext() { } export async function getAssetData(path: string): Promise { - const vecU8 = await asset_data(path); - return new Uint8Array(vecU8).buffer; // FIXME move array creation into rust if possible + return await asset_data(path); } export function getAssetDataSync(path: string): ArrayBuffer { - const vecU8 = asset_data_blocking(path); - return new Uint8Array(vecU8).buffer; + return asset_data_blocking(path); } export function getPluginPreferences(): Record { diff --git a/js/typings/index.d.ts b/js/typings/index.d.ts index 4f8652a..66c0542 100644 --- a/js/typings/index.d.ts +++ b/js/typings/index.d.ts @@ -219,8 +219,8 @@ declare module "ext:core/ops" { function op_log_error(target: string, message: string): void; function op_component_model(): Record; - function asset_data(path: string): Promise; - function asset_data_blocking(path: string): number[]; + function asset_data(path: string): Promise; + function asset_data_blocking(path: string): ArrayBuffer; function op_inline_view_entrypoint_id(): string | null; function op_entrypoint_names(): Record; @@ -246,9 +246,9 @@ declare module "ext:core/ops" { function fetch_action_id_for_shortcut(entrypointId: string, key: string, modifierShift: boolean, modifierControl: boolean, modifierAlt: boolean, modifierMeta: boolean): Promise; - function clipboard_read(): Promise<{ text_data?: string, png_data?: number[] }>; + function clipboard_read(): Promise<{ text_data?: string, png_data?: ArrayBuffer }>; function clipboard_read_text(): Promise; - function clipboard_write(data: { text_data?: string, png_data?: number[] }): Promise; + function clipboard_write(data: { text_data?: string, png_data?: ArrayBuffer }): Promise; function clipboard_write_text(data: string): Promise; function clipboard_clear(): Promise; diff --git a/rust/plugin_runtime/src/assets.rs b/rust/plugin_runtime/src/assets.rs index cfa8aee..89a2090 100644 --- a/rust/plugin_runtime/src/assets.rs +++ b/rust/plugin_runtime/src/assets.rs @@ -43,6 +43,6 @@ pub fn asset_data_blocking(state: Rc>, #[string] path: String) outer_handle.block_on(async { let data = api.get_asset_data(&path).await?; - Ok(data) + Ok(data.into()) }) } diff --git a/rust/plugin_runtime/src/clipboard.rs b/rust/plugin_runtime/src/clipboard.rs index 22e8298..cad0bb4 100644 --- a/rust/plugin_runtime/src/clipboard.rs +++ b/rust/plugin_runtime/src/clipboard.rs @@ -3,22 +3,16 @@ use std::rc::Rc; use deno_core::op2; use deno_core::OpState; -use serde::Deserialize; -use serde::Serialize; use crate::api::BackendForPluginRuntimeApi; use crate::api::BackendForPluginRuntimeApiProxy; -use crate::model::JsClipboardData; - -#[derive(Debug, Serialize, Deserialize)] -struct JSClipboardData { - text_data: Option, - png_data: Option>, -} +use crate::DenoInClipboardData; +use crate::DenoOutClipboardData; +use crate::JsClipboardData; #[op2(async)] #[serde] -pub async fn clipboard_read(state: Rc>) -> anyhow::Result { +pub async fn clipboard_read(state: Rc>) -> anyhow::Result { let api = { let state = state.borrow(); @@ -29,9 +23,9 @@ pub async fn clipboard_read(state: Rc>) -> anyhow::Result>) -> anyhow::Result< } #[op2(async)] -pub async fn clipboard_write(state: Rc>, #[serde] data: JSClipboardData) -> anyhow::Result<()> { +pub async fn clipboard_write(state: Rc>, #[serde] data: DenoInClipboardData) -> anyhow::Result<()> { let api = { let state = state.borrow(); @@ -61,7 +55,7 @@ pub async fn clipboard_write(state: Rc>, #[serde] data: JSClipb let clipboard_data = JsClipboardData { text_data: data.text_data, - png_data: data.png_data, + png_data: data.png_data.map(|buffer| buffer.to_vec()), }; api.clipboard_write(clipboard_data).await diff --git a/rust/plugin_runtime/src/model.rs b/rust/plugin_runtime/src/model.rs index 1294429..108dacf 100644 --- a/rust/plugin_runtime/src/model.rs +++ b/rust/plugin_runtime/src/model.rs @@ -3,6 +3,8 @@ use std::fmt; use bincode::Decode; use bincode::Encode; +use deno_core::JsBuffer; +use deno_core::ToJsBuffer; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::Icons; use gauntlet_common::model::PluginId; @@ -174,7 +176,7 @@ pub enum JsRequest { }, } -#[derive(Deserialize, Serialize, Encode, Decode)] +#[derive(Encode, Decode)] pub struct JsGeneratedSearchItem { pub entrypoint_name: String, pub generator_entrypoint_id: String, @@ -199,6 +201,28 @@ impl fmt::Debug for JsGeneratedSearchItem { } } +#[derive(Serialize)] +pub struct DenoOutGeneratedSearchItem { + pub entrypoint_name: String, + pub generator_entrypoint_id: String, + pub entrypoint_id: String, + pub entrypoint_uuid: String, + pub entrypoint_icon: Option, + pub entrypoint_actions: Vec, + pub entrypoint_accessories: Vec, +} + +#[derive(Deserialize)] +pub struct DenoInGeneratedSearchItem { + pub entrypoint_name: String, + pub generator_entrypoint_id: String, + pub entrypoint_id: String, + pub entrypoint_uuid: String, + pub entrypoint_icon: Option, + pub entrypoint_actions: Vec, + pub entrypoint_accessories: Vec, +} + #[derive(Debug, Deserialize, Serialize, Encode, Decode)] pub struct JsGeneratedSearchItemAction { pub id: Option, @@ -236,8 +260,20 @@ pub enum JsGeneratedSearchItemAccessory { }, } -#[derive(Debug, Serialize, Deserialize, Encode, Decode)] +#[derive(Debug, Encode, Decode)] pub struct JsClipboardData { pub text_data: Option, pub png_data: Option>, } + +#[derive(Serialize)] +pub struct DenoOutClipboardData { + pub text_data: Option, + pub png_data: Option, +} + +#[derive(Deserialize)] +pub struct DenoInClipboardData { + pub text_data: Option, + pub png_data: Option, +} diff --git a/rust/plugin_runtime/src/plugins/applications.rs b/rust/plugin_runtime/src/plugins/applications.rs index 264622b..0d7dd23 100644 --- a/rust/plugin_runtime/src/plugins/applications.rs +++ b/rust/plugin_runtime/src/plugins/applications.rs @@ -5,6 +5,7 @@ use std::rc::Rc; use anyhow::anyhow; use deno_core::op2; use deno_core::OpState; +use deno_core::ToJsBuffer; use gauntlet_common::detached_process::CommandExt; use image::imageops::FilterType; use image::ImageFormat; @@ -46,7 +47,7 @@ pub enum DesktopPathAction { pub struct DesktopApplication { name: String, desktop_file_path: String, - icon: Option>, + icon: Option, startup_wm_class: Option, } @@ -55,7 +56,7 @@ pub struct DesktopApplication { pub struct DesktopApplication { name: String, path: String, - icon: Option>, + icon: Option, } #[cfg(target_os = "windows")] @@ -63,7 +64,7 @@ pub struct DesktopApplication { pub struct DesktopApplication { name: String, path: String, - icon: Option>, + icon: Option, } #[cfg(target_os = "macos")] @@ -71,7 +72,7 @@ pub struct DesktopApplication { pub struct DesktopSettingsPre13Data { name: String, path: String, - icon: Option>, + icon: Option, } #[cfg(target_os = "macos")] @@ -79,7 +80,7 @@ pub struct DesktopSettingsPre13Data { pub struct DesktopSettings13AndPostData { name: String, preferences_id: String, - icon: Option>, + icon: Option, } #[op2] diff --git a/rust/plugin_runtime/src/plugins/applications/linux/mod.rs b/rust/plugin_runtime/src/plugins/applications/linux/mod.rs index 2e1f195..7b64a1b 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/mod.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/mod.rs @@ -8,6 +8,7 @@ use std::rc::Rc; use deno_core::op2; use deno_core::OpState; +use deno_core::ToJsBuffer; use freedesktop_entry_parser::parse_entry; use freedesktop_icons::lookup; use gauntlet_common::detached_process::CommandExt; @@ -267,7 +268,8 @@ fn create_app_entry(desktop_file_path: &Path) -> Option { res.inspect_err(|err| tracing::warn!("error processing icon of {:?}: {:?}", desktop_file_path, err)) .ok() }) - .flatten(); + .flatten() + .map(|buffer| buffer.into()); Some(DesktopApplication { name: name.to_string(), diff --git a/rust/plugin_runtime/src/plugins/applications/macos.rs b/rust/plugin_runtime/src/plugins/applications/macos.rs index 9db37c3..59af25b 100644 --- a/rust/plugin_runtime/src/plugins/applications/macos.rs +++ b/rust/plugin_runtime/src/plugins/applications/macos.rs @@ -11,6 +11,7 @@ use cacao::filesystem::FileManager; use cacao::filesystem::SearchPathDirectory; use cacao::filesystem::SearchPathDomainMask; use cacao::url::Url; +use deno_core::ToJsBuffer; use objc2::ClassType; use objc2_app_kit::NSBitmapImageRep; use objc2_app_kit::NSCalibratedWhiteColorSpace; @@ -464,7 +465,7 @@ unsafe fn resize_ns_image(source_image: &NSImage, width: NSInteger, height: NSIn Some(data.bytes().to_vec()) } -fn get_application_icon(app_path: &Path) -> anyhow::Result> { +fn get_application_icon(app_path: &Path) -> anyhow::Result { unsafe { let workspace = NSWorkspace::sharedWorkspace(); @@ -478,7 +479,7 @@ fn get_application_icon(app_path: &Path) -> anyhow::Result> { let bytes = resize_ns_image(&image, 40, 40).ok_or(anyhow!("Unable to resize the image"))?; - Ok(bytes) + Ok(bytes.into()) } } diff --git a/rust/plugin_runtime/src/plugins/applications/windows.rs b/rust/plugin_runtime/src/plugins/applications/windows.rs index febb913..9d6a25c 100644 --- a/rust/plugin_runtime/src/plugins/applications/windows.rs +++ b/rust/plugin_runtime/src/plugins/applications/windows.rs @@ -7,6 +7,7 @@ use std::ptr; use anyhow::anyhow; use anyhow::Context; use deno_core::op2; +use deno_core::ToJsBuffer; use image::RgbaImage; use tokio::task::spawn_blocking; use windows::core::GUID; @@ -89,7 +90,7 @@ fn windows_app_from_path_blocking(file_path: String) -> anyhow::Result anyhow::Result> { +fn extract_icon(file_path: &str) -> anyhow::Result { unsafe { let mut shfileinfow = Shell::SHFILEINFOW::default(); @@ -188,7 +189,7 @@ fn extract_icon(file_path: &str) -> anyhow::Result> { let data = resize_icon(data)?; - Ok(data) + Ok(data.into()) } } diff --git a/rust/plugin_runtime/src/search.rs b/rust/plugin_runtime/src/search.rs index aa0f560..0c999f8 100644 --- a/rust/plugin_runtime/src/search.rs +++ b/rust/plugin_runtime/src/search.rs @@ -6,12 +6,13 @@ use deno_core::OpState; use crate::api::BackendForPluginRuntimeApi; use crate::api::BackendForPluginRuntimeApiProxy; -use crate::model::JsGeneratedSearchItem; +use crate::DenoInGeneratedSearchItem; +use crate::JsGeneratedSearchItem; #[op2(async)] pub async fn reload_search_index( state: Rc>, - #[serde] generated_entrypoints: Vec, + #[serde] generated_entrypoints: Vec, refresh_search_list: bool, ) -> anyhow::Result<()> { let api = { @@ -22,6 +23,21 @@ pub async fn reload_search_index( api }; + let generated_entrypoints = generated_entrypoints + .into_iter() + .map(|item| { + JsGeneratedSearchItem { + entrypoint_name: item.entrypoint_name, + generator_entrypoint_id: item.generator_entrypoint_id, + entrypoint_id: item.entrypoint_id, + entrypoint_uuid: item.entrypoint_uuid, + entrypoint_icon: item.entrypoint_icon.map(|buffer| buffer.to_vec()), + entrypoint_actions: item.entrypoint_actions, + entrypoint_accessories: item.entrypoint_accessories, + } + }) + .collect(); + api.reload_search_index(generated_entrypoints, refresh_search_list) .await?; From a72329aeb7b971ea95af646d2c02792d711eeaf1 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 20 Apr 2025 17:16:00 +0200 Subject: [PATCH 055/170] Add a bunch of plugin examples for entrypoint generators --- example_plugins/plugins/command/.gitignore | 2 + example_plugins/plugins/command/gauntlet.toml | 35 ++++++++++++++++ example_plugins/plugins/command/package.json | 17 ++++++++ .../plugins/command/src/preferences.tsx | 8 ++++ .../plugins/command/src/simple.tsx | 3 ++ example_plugins/plugins/command/tsconfig.json | 11 +++++ .../.gitignore | 2 + .../gauntlet.toml | 12 ++++++ .../package.json | 17 ++++++++ .../src/accessories.tsx | 17 ++++++++ .../tsconfig.json | 11 +++++ .../.gitignore | 2 + .../gauntlet.toml | 17 ++++++++ .../package.json | 17 ++++++++ .../src/action-shortcut.tsx | 30 ++++++++++++++ .../tsconfig.json | 11 +++++ .../entrypoint_generator_fs_events/.gitignore | 2 + .../gauntlet.toml | 19 +++++++++ .../package.json | 17 ++++++++ .../src/fs-events.tsx | 40 +++++++++++++++++++ .../tsconfig.json | 11 +++++ .../entrypoint_generator_icons/.gitignore | 2 + .../entrypoint_generator_icons/gauntlet.toml | 15 +++++++ .../entrypoint_generator_icons/package.json | 17 ++++++++ .../entrypoint_generator_icons/src/icons.tsx | 20 ++++++++++ .../entrypoint_generator_icons/tsconfig.json | 11 +++++ .../.gitignore | 2 + .../gauntlet.toml | 26 ++++++++++++ .../package.json | 17 ++++++++ .../src/preferences.tsx | 17 ++++++++ .../tsconfig.json | 11 +++++ .../entrypoint_generator_simple/.gitignore | 2 + .../entrypoint_generator_simple/gauntlet.toml | 12 ++++++ .../entrypoint_generator_simple/package.json | 17 ++++++++ .../src/simple.tsx | 35 ++++++++++++++++ .../entrypoint_generator_simple/tsconfig.json | 11 +++++ 36 files changed, 516 insertions(+) create mode 100644 example_plugins/plugins/command/.gitignore create mode 100644 example_plugins/plugins/command/gauntlet.toml create mode 100644 example_plugins/plugins/command/package.json create mode 100644 example_plugins/plugins/command/src/preferences.tsx create mode 100644 example_plugins/plugins/command/src/simple.tsx create mode 100644 example_plugins/plugins/command/tsconfig.json create mode 100644 example_plugins/plugins/entrypoint_generator_accessories/.gitignore create mode 100644 example_plugins/plugins/entrypoint_generator_accessories/gauntlet.toml create mode 100644 example_plugins/plugins/entrypoint_generator_accessories/package.json create mode 100644 example_plugins/plugins/entrypoint_generator_accessories/src/accessories.tsx create mode 100644 example_plugins/plugins/entrypoint_generator_accessories/tsconfig.json create mode 100644 example_plugins/plugins/entrypoint_generator_action_shortcut/.gitignore create mode 100644 example_plugins/plugins/entrypoint_generator_action_shortcut/gauntlet.toml create mode 100644 example_plugins/plugins/entrypoint_generator_action_shortcut/package.json create mode 100644 example_plugins/plugins/entrypoint_generator_action_shortcut/src/action-shortcut.tsx create mode 100644 example_plugins/plugins/entrypoint_generator_action_shortcut/tsconfig.json create mode 100644 example_plugins/plugins/entrypoint_generator_fs_events/.gitignore create mode 100644 example_plugins/plugins/entrypoint_generator_fs_events/gauntlet.toml create mode 100644 example_plugins/plugins/entrypoint_generator_fs_events/package.json create mode 100644 example_plugins/plugins/entrypoint_generator_fs_events/src/fs-events.tsx create mode 100644 example_plugins/plugins/entrypoint_generator_fs_events/tsconfig.json create mode 100644 example_plugins/plugins/entrypoint_generator_icons/.gitignore create mode 100644 example_plugins/plugins/entrypoint_generator_icons/gauntlet.toml create mode 100644 example_plugins/plugins/entrypoint_generator_icons/package.json create mode 100644 example_plugins/plugins/entrypoint_generator_icons/src/icons.tsx create mode 100644 example_plugins/plugins/entrypoint_generator_icons/tsconfig.json create mode 100644 example_plugins/plugins/entrypoint_generator_preferences/.gitignore create mode 100644 example_plugins/plugins/entrypoint_generator_preferences/gauntlet.toml create mode 100644 example_plugins/plugins/entrypoint_generator_preferences/package.json create mode 100644 example_plugins/plugins/entrypoint_generator_preferences/src/preferences.tsx create mode 100644 example_plugins/plugins/entrypoint_generator_preferences/tsconfig.json create mode 100644 example_plugins/plugins/entrypoint_generator_simple/.gitignore create mode 100644 example_plugins/plugins/entrypoint_generator_simple/gauntlet.toml create mode 100644 example_plugins/plugins/entrypoint_generator_simple/package.json create mode 100644 example_plugins/plugins/entrypoint_generator_simple/src/simple.tsx create mode 100644 example_plugins/plugins/entrypoint_generator_simple/tsconfig.json diff --git a/example_plugins/plugins/command/.gitignore b/example_plugins/plugins/command/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/command/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/command/gauntlet.toml b/example_plugins/plugins/command/gauntlet.toml new file mode 100644 index 0000000..9355b6b --- /dev/null +++ b/example_plugins/plugins/command/gauntlet.toml @@ -0,0 +1,35 @@ +[gauntlet] +name = 'Docs Command' +description = '' + +# docs-code-segment:start simple +[[entrypoint]] +id = 'simple' +name = 'Simple' +path = 'src/simple.tsx' +type = 'command' +description = '' +# docs-code-segment:end + +# docs-code-segment:start preferences +[[preferences]] +id = 'testBool' +name = 'Test Boolean Preference' +type = 'bool' +default = true +description = "" + +[[entrypoint]] +id = 'preferences' +name = 'Preferences' +path = 'src/preferences.tsx' +type = 'command' +description = '' + +[[entrypoint.preferences]] +id = 'testStr' +name = 'Test String Preference' +type = 'string' +default = 'test_value' +description = "" +# docs-code-segment:end diff --git a/example_plugins/plugins/command/package.json b/example_plugins/plugins/command/package.json new file mode 100644 index 0000000..b5bdf4e --- /dev/null +++ b/example_plugins/plugins/command/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-command", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/command/src/preferences.tsx b/example_plugins/plugins/command/src/preferences.tsx new file mode 100644 index 0000000..b8d6a6e --- /dev/null +++ b/example_plugins/plugins/command/src/preferences.tsx @@ -0,0 +1,8 @@ +import { CommandContext } from "@project-gauntlet/api/helpers"; + +type PluginCommandContext = CommandContext<{ testBool: boolean }, {testStr: string }>; + +export default async function Command({ pluginPreferences, entrypointPreferences }: PluginCommandContext) { + console.log(pluginPreferences.testBool); + console.log(entrypointPreferences.testStr); +} diff --git a/example_plugins/plugins/command/src/simple.tsx b/example_plugins/plugins/command/src/simple.tsx new file mode 100644 index 0000000..d682be1 --- /dev/null +++ b/example_plugins/plugins/command/src/simple.tsx @@ -0,0 +1,3 @@ +export default async function Command() { + console.log("Running the Gauntlet...") +} diff --git a/example_plugins/plugins/command/tsconfig.json b/example_plugins/plugins/command/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/command/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/entrypoint_generator_accessories/.gitignore b/example_plugins/plugins/entrypoint_generator_accessories/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_accessories/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/entrypoint_generator_accessories/gauntlet.toml b/example_plugins/plugins/entrypoint_generator_accessories/gauntlet.toml new file mode 100644 index 0000000..9ffef6a --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_accessories/gauntlet.toml @@ -0,0 +1,12 @@ +[gauntlet] +name = 'Docs Entrypoint Generator Accessories' +description = '' + +# docs-code-segment:start accessories +[[entrypoint]] +id = 'accessories' +name = 'Accessories' +path = 'src/accessories.tsx' +type = 'entrypoint-generator' +description = '' +# docs-code-segment:end diff --git a/example_plugins/plugins/entrypoint_generator_accessories/package.json b/example_plugins/plugins/entrypoint_generator_accessories/package.json new file mode 100644 index 0000000..f646518 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_accessories/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-entrypoint-generator-accessories", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/entrypoint_generator_accessories/src/accessories.tsx b/example_plugins/plugins/entrypoint_generator_accessories/src/accessories.tsx new file mode 100644 index 0000000..784207b --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_accessories/src/accessories.tsx @@ -0,0 +1,17 @@ +import { GeneratorContext } from "@project-gauntlet/api/helpers"; +import { Icons } from "@project-gauntlet/api/components"; + +export default function EntrypointGenerator({ add }: GeneratorContext): void { + add('generated', { + name: 'Generated Command', + actions: [ + { + label: "Run the Gauntlet", + run: () => { + console.log('Running the Gauntlet...') + } + } + ], + accessories: [{ icon: Icons.Battery, text: "100 %" }] + }) +} diff --git a/example_plugins/plugins/entrypoint_generator_accessories/tsconfig.json b/example_plugins/plugins/entrypoint_generator_accessories/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_accessories/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/entrypoint_generator_action_shortcut/.gitignore b/example_plugins/plugins/entrypoint_generator_action_shortcut/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_action_shortcut/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/entrypoint_generator_action_shortcut/gauntlet.toml b/example_plugins/plugins/entrypoint_generator_action_shortcut/gauntlet.toml new file mode 100644 index 0000000..d3455c3 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_action_shortcut/gauntlet.toml @@ -0,0 +1,17 @@ +[gauntlet] +name = 'Docs Entrypoint Generator Action Shortcut' +description = '' + +# docs-code-segment:start action-shortcut +[[entrypoint]] +id = 'action-shortcut' +name = 'Action Shortcut' +path = 'src/action-shortcut.tsx' +type = 'entrypoint-generator' +description = '' + +[[entrypoint.actions]] +id = 'otherAction' +description = '' +shortcut = { key = 'g', kind = 'main'} +# docs-code-segment:end diff --git a/example_plugins/plugins/entrypoint_generator_action_shortcut/package.json b/example_plugins/plugins/entrypoint_generator_action_shortcut/package.json new file mode 100644 index 0000000..8727092 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_action_shortcut/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-entrypoint-generator-action-shortcuts", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/entrypoint_generator_action_shortcut/src/action-shortcut.tsx b/example_plugins/plugins/entrypoint_generator_action_shortcut/src/action-shortcut.tsx new file mode 100644 index 0000000..51fcbf4 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_action_shortcut/src/action-shortcut.tsx @@ -0,0 +1,30 @@ +import { GeneratorContext } from "@project-gauntlet/api/helpers"; + +type PluginGeneratorContext = GeneratorContext<{ testBool: boolean }, { testStr: string }>; + +export default function EntrypointGenerator({ add }: PluginGeneratorContext): void { + add('generated', { + name: 'Generated Command', + actions: [ + { + label: "Primary Action", // Executed when Enter is pressed + run: () => { + console.log('Running the Gauntlet... - Primary Action') + } + }, + { + label: "Secondary Action", // Executed when Shift+Enter is pressed + run: () => { + console.log('Running the Gauntlet... - Secondary Action') + } + }, + { + ref: "otherAction", // Executed when pressing shortcut specified in Plugin Manifest + label: "Other Action", + run: () => { + console.log('Running the Gauntlet... - Other Action') + } + } + ], + }); +} diff --git a/example_plugins/plugins/entrypoint_generator_action_shortcut/tsconfig.json b/example_plugins/plugins/entrypoint_generator_action_shortcut/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_action_shortcut/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/entrypoint_generator_fs_events/.gitignore b/example_plugins/plugins/entrypoint_generator_fs_events/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_fs_events/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/entrypoint_generator_fs_events/gauntlet.toml b/example_plugins/plugins/entrypoint_generator_fs_events/gauntlet.toml new file mode 100644 index 0000000..6329476 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_fs_events/gauntlet.toml @@ -0,0 +1,19 @@ +[gauntlet] +name = 'Docs Entrypoint Generator FS Events' +description = '' + +# docs-code-segment:start fs-events +[[entrypoint]] +id = 'fs-events' +name = 'FS Events' +path = 'src/fs-events.tsx' +type = 'entrypoint-generator' +description = '' + +[permissions.filesystem] +read = ["/tmp/gauntlet-example"] +write = ["/tmp/gauntlet-example"] + +[[supported_system]] +os = 'linux' +# docs-code-segment:end diff --git a/example_plugins/plugins/entrypoint_generator_fs_events/package.json b/example_plugins/plugins/entrypoint_generator_fs_events/package.json new file mode 100644 index 0000000..0c69da1 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_fs_events/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-entrypoint-generator-fs-events", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/entrypoint_generator_fs_events/src/fs-events.tsx b/example_plugins/plugins/entrypoint_generator_fs_events/src/fs-events.tsx new file mode 100644 index 0000000..7f6dcaf --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_fs_events/src/fs-events.tsx @@ -0,0 +1,40 @@ +import { GeneratorContext } from "@project-gauntlet/api/helpers"; + +export default async function EntrypointGenerator({ add, remove }: GeneratorContext): Promise void)> { + const id = "generated"; + const path = "/tmp/gauntlet-example"; + + await Deno.create(path) + add(id, generatedItem("default")) + + const watcher = Deno.watchFs(path); + + (async () => { + for await (const _event of watcher) { + try { + const value = await Deno.readTextFile(path); + add(id, generatedItem(value)) + } catch (err) { + remove(id) + } + } + })(); + + return () => { + watcher.close() + } +} + +function generatedItem(value: string) { + return { + name: `Generated Command - ${value}`, + actions: [ + { + label: "Run the Gauntlet", + run: () => { + console.log('Running the Gauntlet...') + } + } + ] + }; +} diff --git a/example_plugins/plugins/entrypoint_generator_fs_events/tsconfig.json b/example_plugins/plugins/entrypoint_generator_fs_events/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_fs_events/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/entrypoint_generator_icons/.gitignore b/example_plugins/plugins/entrypoint_generator_icons/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_icons/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/entrypoint_generator_icons/gauntlet.toml b/example_plugins/plugins/entrypoint_generator_icons/gauntlet.toml new file mode 100644 index 0000000..c5cb3aa --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_icons/gauntlet.toml @@ -0,0 +1,15 @@ +[gauntlet] +name = 'Docs Entrypoint Generator Icons' +description = '' + +# docs-code-segment:start icons +[[entrypoint]] +id = 'icons' +name = 'Icons' +path = 'src/icons.tsx' +type = 'entrypoint-generator' +description = '' + +[permissions] +network = ["img.icons8.com"] +# docs-code-segment:end diff --git a/example_plugins/plugins/entrypoint_generator_icons/package.json b/example_plugins/plugins/entrypoint_generator_icons/package.json new file mode 100644 index 0000000..9848338 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_icons/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-entrypoint-generator", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/entrypoint_generator_icons/src/icons.tsx b/example_plugins/plugins/entrypoint_generator_icons/src/icons.tsx new file mode 100644 index 0000000..50e8478 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_icons/src/icons.tsx @@ -0,0 +1,20 @@ +import { GeneratorContext } from "@project-gauntlet/api/helpers"; + +export default async function EntrypointGenerator({ add }: GeneratorContext): Promise { + + const response = await fetch("https://img.icons8.com/?size=32&id=21276&format=png"); + const arrayBuffer = await response.bytes(); + + add('generated', { + name: 'Generated Command', + actions: [ + { + label: "Run the Gauntlet", + run: () => { + console.log('Running the Gauntlet...') + } + } + ], + icon: arrayBuffer + }); +} diff --git a/example_plugins/plugins/entrypoint_generator_icons/tsconfig.json b/example_plugins/plugins/entrypoint_generator_icons/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_icons/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/entrypoint_generator_preferences/.gitignore b/example_plugins/plugins/entrypoint_generator_preferences/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_preferences/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/entrypoint_generator_preferences/gauntlet.toml b/example_plugins/plugins/entrypoint_generator_preferences/gauntlet.toml new file mode 100644 index 0000000..e834c8c --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_preferences/gauntlet.toml @@ -0,0 +1,26 @@ +[gauntlet] +name = 'Docs Entrypoint Generator Preferences' +description = '' + +# docs-code-segment:start preferences +[[preferences]] +id = 'testBool' +name = 'Test Boolean Preference' +type = 'bool' +default = true +description = "" + +[[entrypoint]] +id = 'preferences' +name = 'Preferences' +path = 'src/preferences.tsx' +type = 'entrypoint-generator' +description = '' + +[[entrypoint.preferences]] +id = 'testStr' +name = 'Test String Preference' +type = 'string' +default = 'test_value' +description = "" +# docs-code-segment:end diff --git a/example_plugins/plugins/entrypoint_generator_preferences/package.json b/example_plugins/plugins/entrypoint_generator_preferences/package.json new file mode 100644 index 0000000..834ca3c --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_preferences/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-entrypoint-generator-preferences", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/entrypoint_generator_preferences/src/preferences.tsx b/example_plugins/plugins/entrypoint_generator_preferences/src/preferences.tsx new file mode 100644 index 0000000..becf895 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_preferences/src/preferences.tsx @@ -0,0 +1,17 @@ +import { GeneratorContext } from "@project-gauntlet/api/helpers"; + +type PluginGeneratorContext = GeneratorContext<{ testBool: boolean }, { testStr: string }>; + +export default function EntrypointGenerator({ add, pluginPreferences, entrypointPreferences }: PluginGeneratorContext): void { + add('generated', { + name: 'Generated Command - ' + entrypointPreferences.testStr, + actions: [ + { + label: "Run the Gauntlet", + run: () => { + console.log('Running the Gauntlet... ' + pluginPreferences.testBool) + } + } + ], + }); +} diff --git a/example_plugins/plugins/entrypoint_generator_preferences/tsconfig.json b/example_plugins/plugins/entrypoint_generator_preferences/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_preferences/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/entrypoint_generator_simple/.gitignore b/example_plugins/plugins/entrypoint_generator_simple/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_simple/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/entrypoint_generator_simple/gauntlet.toml b/example_plugins/plugins/entrypoint_generator_simple/gauntlet.toml new file mode 100644 index 0000000..8be1da9 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_simple/gauntlet.toml @@ -0,0 +1,12 @@ +[gauntlet] +name = 'Docs Entrypoint Generator Simple' +description = '' + +# docs-code-segment:start simple +[[entrypoint]] +id = 'simple' +name = 'Simple' +path = 'src/simple.tsx' +type = 'entrypoint-generator' +description = '' +# docs-code-segment:end diff --git a/example_plugins/plugins/entrypoint_generator_simple/package.json b/example_plugins/plugins/entrypoint_generator_simple/package.json new file mode 100644 index 0000000..3b313df --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_simple/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-entrypoint-generator-simple", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/entrypoint_generator_simple/src/simple.tsx b/example_plugins/plugins/entrypoint_generator_simple/src/simple.tsx new file mode 100644 index 0000000..f43c7fc --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_simple/src/simple.tsx @@ -0,0 +1,35 @@ +import { GeneratorContext } from "@project-gauntlet/api/helpers"; +import { ReactElement } from "react"; +import { List } from "@project-gauntlet/api/components"; + +function ListView(): ReactElement { + return ( + + + + ) +} + +export default function EntrypointGenerator({ add }: GeneratorContext): void { + add('generated', { + name: 'Generated Command', + actions: [ + { + label: "Run the Gauntlet", + run: () => { + console.log('Running the Gauntlet...') + } + } + ] + }) + + add('generated', { + name: 'Generated View', + actions: [ + { + label: "Open generated view", + view: () => + } + ] + }) +} diff --git a/example_plugins/plugins/entrypoint_generator_simple/tsconfig.json b/example_plugins/plugins/entrypoint_generator_simple/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/entrypoint_generator_simple/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file From 87d6b1abb8cbf67d4c0ef03c1b9445e817bfb1a7 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 20 Apr 2025 17:27:21 +0200 Subject: [PATCH 056/170] Fix typo in plugin manifest json schema --- docs/schema/plugin_manifest.schema.json | 2 +- package-lock.json | 140 +++++++++++++++++++++ rust/server/src/plugins/plugin_manifest.rs | 2 +- 3 files changed, 142 insertions(+), 2 deletions(-) diff --git a/docs/schema/plugin_manifest.schema.json b/docs/schema/plugin_manifest.schema.json index 7189275..0b12326 100644 --- a/docs/schema/plugin_manifest.schema.json +++ b/docs/schema/plugin_manifest.schema.json @@ -350,7 +350,7 @@ ] }, { - "description": "Entrypoint that dynamically generatepointsd", + "description": "Entrypoint that can dynamically generates endpoints", "type": "string", "enum": [ "entrypoint-generator" diff --git a/package-lock.json b/package-lock.json index 3575bb3..69ee1ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,22 @@ } }, "example_plugins/js/api": {}, + "example_plugins/plugins/command": { + "name": "@project-gauntlet/docs-command", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/command/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, "example_plugins/plugins/docs_detail": { "name": "@project-gauntlet/docs-detailt", "extraneous": true, @@ -140,6 +156,102 @@ "typescript": "*" } }, + "example_plugins/plugins/entrypoint_generator_accessories": { + "name": "@project-gauntlet/docs-entrypoint-generator-accessories", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/entrypoint_generator_accessories/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/entrypoint_generator_action_shortcut": { + "name": "@project-gauntlet/docs-entrypoint-generator-action-shortcuts", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/entrypoint_generator_action_shortcut/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/entrypoint_generator_fs_events": { + "name": "@project-gauntlet/docs-entrypoint-generator-fs-events", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/entrypoint_generator_fs_events/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/entrypoint_generator_icons": { + "name": "@project-gauntlet/docs-entrypoint-generator", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/entrypoint_generator_icons/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/entrypoint_generator_preferences": { + "name": "@project-gauntlet/docs-entrypoint-generator-preferences", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/entrypoint_generator_preferences/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, + "example_plugins/plugins/entrypoint_generator_simple": { + "name": "@project-gauntlet/docs-entrypoint-generator-simple", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/entrypoint_generator_simple/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, "example_plugins/plugins/ui_detail": { "name": "@project-gauntlet/docs-detail", "dependencies": { @@ -974,10 +1086,38 @@ "resolved": "dev_plugin", "link": true }, + "node_modules/@project-gauntlet/docs-command": { + "resolved": "example_plugins/plugins/command", + "link": true + }, "node_modules/@project-gauntlet/docs-detail": { "resolved": "example_plugins/plugins/ui_detail", "link": true }, + "node_modules/@project-gauntlet/docs-entrypoint-generator": { + "resolved": "example_plugins/plugins/entrypoint_generator_icons", + "link": true + }, + "node_modules/@project-gauntlet/docs-entrypoint-generator-accessories": { + "resolved": "example_plugins/plugins/entrypoint_generator_accessories", + "link": true + }, + "node_modules/@project-gauntlet/docs-entrypoint-generator-action-shortcuts": { + "resolved": "example_plugins/plugins/entrypoint_generator_action_shortcut", + "link": true + }, + "node_modules/@project-gauntlet/docs-entrypoint-generator-fs-events": { + "resolved": "example_plugins/plugins/entrypoint_generator_fs_events", + "link": true + }, + "node_modules/@project-gauntlet/docs-entrypoint-generator-preferences": { + "resolved": "example_plugins/plugins/entrypoint_generator_preferences", + "link": true + }, + "node_modules/@project-gauntlet/docs-entrypoint-generator-simple": { + "resolved": "example_plugins/plugins/entrypoint_generator_simple", + "link": true + }, "node_modules/@project-gauntlet/docs-form": { "resolved": "example_plugins/plugins/ui_form", "link": true diff --git a/rust/server/src/plugins/plugin_manifest.rs b/rust/server/src/plugins/plugin_manifest.rs index 133bb25..f311b0b 100644 --- a/rust/server/src/plugins/plugin_manifest.rs +++ b/rust/server/src/plugins/plugin_manifest.rs @@ -163,7 +163,7 @@ pub enum PluginManifestEntrypointTypes { #[schemars(description = "A view-based entrypoint displayed under main search bar")] InlineView, #[serde(rename = "entrypoint-generator")] - #[schemars(description = "Entrypoint that dynamically generatepointsd")] + #[schemars(description = "Entrypoint that can dynamically generates endpoints")] EntrypointGenerator, } From 1a1c86297318b95ec2705c54959e8eaa800750e8 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 24 Apr 2025 21:13:43 +0200 Subject: [PATCH 057/170] More plugin examples --- docs/js/components/action/props/onAction.md | 2 +- .../plugins/command/src/preferences.tsx | 2 +- .../gauntlet.toml | 2 +- .../src/action-shortcut.tsx | 6 +- .../plugins/ui_action_panel/.gitignore | 2 + .../plugins/ui_action_panel/gauntlet.toml | 26 +++++++++ .../plugins/ui_action_panel/package.json | 17 ++++++ .../plugins/ui_action_panel/src/action.tsx | 31 ++++++++++ .../plugins/ui_action_panel/src/section.tsx | 33 +++++++++++ .../plugins/ui_action_panel/tsconfig.json | 11 ++++ .../plugins/ui_icons_and_images/.gitignore | 2 + .../ui_icons_and_images/assets/logo.png | Bin 0 -> 18959 bytes .../plugins/ui_icons_and_images/gauntlet.toml | 33 +++++++++++ .../plugins/ui_icons_and_images/package.json | 17 ++++++ .../plugins/ui_icons_and_images/src/asset.tsx | 12 ++++ .../plugins/ui_icons_and_images/src/icon.tsx | 12 ++++ .../plugins/ui_icons_and_images/src/url.tsx | 12 ++++ .../plugins/ui_icons_and_images/tsconfig.json | 11 ++++ .../plugins/ui_navigation/.gitignore | 2 + .../plugins/ui_navigation/gauntlet.toml | 12 ++++ .../plugins/ui_navigation/package.json | 17 ++++++ .../plugins/ui_navigation/src/navigation.tsx | 53 ++++++++++++++++++ .../plugins/ui_navigation/tsconfig.json | 11 ++++ .../plugins/ui_preferences/.gitignore | 2 + .../plugins/ui_preferences/gauntlet.toml | 26 +++++++++ .../plugins/ui_preferences/package.json | 17 ++++++ .../ui_preferences/src/preferences.tsx | 23 ++++++++ .../plugins/ui_preferences/tsconfig.json | 11 ++++ .../ui_action_panel/action/default.json | 3 + .../ui_action_panel/section/default.json | 3 + .../ui_icons_and_images/asset/default.json | 3 + .../ui_icons_and_images/icon/default.json | 3 + .../ui_icons_and_images/url/default.json | 3 + js/scenario_runner_cli/src/main.ts | 5 ++ rust/client/src/ui/mod.rs | 9 +++ rust/scenario_runner/src/lib.rs | 1 + 36 files changed, 429 insertions(+), 6 deletions(-) create mode 100644 example_plugins/plugins/ui_action_panel/.gitignore create mode 100644 example_plugins/plugins/ui_action_panel/gauntlet.toml create mode 100644 example_plugins/plugins/ui_action_panel/package.json create mode 100644 example_plugins/plugins/ui_action_panel/src/action.tsx create mode 100644 example_plugins/plugins/ui_action_panel/src/section.tsx create mode 100644 example_plugins/plugins/ui_action_panel/tsconfig.json create mode 100644 example_plugins/plugins/ui_icons_and_images/.gitignore create mode 100644 example_plugins/plugins/ui_icons_and_images/assets/logo.png create mode 100644 example_plugins/plugins/ui_icons_and_images/gauntlet.toml create mode 100644 example_plugins/plugins/ui_icons_and_images/package.json create mode 100644 example_plugins/plugins/ui_icons_and_images/src/asset.tsx create mode 100644 example_plugins/plugins/ui_icons_and_images/src/icon.tsx create mode 100644 example_plugins/plugins/ui_icons_and_images/src/url.tsx create mode 100644 example_plugins/plugins/ui_icons_and_images/tsconfig.json create mode 100644 example_plugins/plugins/ui_navigation/.gitignore create mode 100644 example_plugins/plugins/ui_navigation/gauntlet.toml create mode 100644 example_plugins/plugins/ui_navigation/package.json create mode 100644 example_plugins/plugins/ui_navigation/src/navigation.tsx create mode 100644 example_plugins/plugins/ui_navigation/tsconfig.json create mode 100644 example_plugins/plugins/ui_preferences/.gitignore create mode 100644 example_plugins/plugins/ui_preferences/gauntlet.toml create mode 100644 example_plugins/plugins/ui_preferences/package.json create mode 100644 example_plugins/plugins/ui_preferences/src/preferences.tsx create mode 100644 example_plugins/plugins/ui_preferences/tsconfig.json create mode 100644 example_plugins/scenarios/ui_action_panel/action/default.json create mode 100644 example_plugins/scenarios/ui_action_panel/section/default.json create mode 100644 example_plugins/scenarios/ui_icons_and_images/asset/default.json create mode 100644 example_plugins/scenarios/ui_icons_and_images/icon/default.json create mode 100644 example_plugins/scenarios/ui_icons_and_images/url/default.json diff --git a/docs/js/components/action/props/onAction.md b/docs/js/components/action/props/onAction.md index 2346de8..fdad01a 100644 --- a/docs/js/components/action/props/onAction.md +++ b/docs/js/components/action/props/onAction.md @@ -1 +1 @@ -Function that is called when action button is clicked or the shortcut is pressed (including primary and secondary shortcuts). ID parameter is an id of currently focused grid or list item \ No newline at end of file +Function that is called when action button is clicked or the shortcut is pressed (including primary and secondary shortcuts). ID parameter is an id of currently focused grid or list item. Returning `{ close: true }` object from the action will close the window diff --git a/example_plugins/plugins/command/src/preferences.tsx b/example_plugins/plugins/command/src/preferences.tsx index b8d6a6e..9257eac 100644 --- a/example_plugins/plugins/command/src/preferences.tsx +++ b/example_plugins/plugins/command/src/preferences.tsx @@ -1,6 +1,6 @@ import { CommandContext } from "@project-gauntlet/api/helpers"; -type PluginCommandContext = CommandContext<{ testBool: boolean }, {testStr: string }>; +type PluginCommandContext = CommandContext<{ testBool: boolean }, { testStr: string }>; export default async function Command({ pluginPreferences, entrypointPreferences }: PluginCommandContext) { console.log(pluginPreferences.testBool); diff --git a/example_plugins/plugins/entrypoint_generator_action_shortcut/gauntlet.toml b/example_plugins/plugins/entrypoint_generator_action_shortcut/gauntlet.toml index d3455c3..1c9bb2d 100644 --- a/example_plugins/plugins/entrypoint_generator_action_shortcut/gauntlet.toml +++ b/example_plugins/plugins/entrypoint_generator_action_shortcut/gauntlet.toml @@ -11,7 +11,7 @@ type = 'entrypoint-generator' description = '' [[entrypoint.actions]] -id = 'otherAction' +id = 'anotherAction' description = '' shortcut = { key = 'g', kind = 'main'} # docs-code-segment:end diff --git a/example_plugins/plugins/entrypoint_generator_action_shortcut/src/action-shortcut.tsx b/example_plugins/plugins/entrypoint_generator_action_shortcut/src/action-shortcut.tsx index 51fcbf4..68df383 100644 --- a/example_plugins/plugins/entrypoint_generator_action_shortcut/src/action-shortcut.tsx +++ b/example_plugins/plugins/entrypoint_generator_action_shortcut/src/action-shortcut.tsx @@ -19,10 +19,10 @@ export default function EntrypointGenerator({ add }: PluginGeneratorContext): vo } }, { - ref: "otherAction", // Executed when pressing shortcut specified in Plugin Manifest - label: "Other Action", + ref: "anotherAction", // Executed when pressing shortcut specified in Plugin Manifest + label: "Another Action", run: () => { - console.log('Running the Gauntlet... - Other Action') + console.log('Running the Gauntlet... - Another Action') } } ], diff --git a/example_plugins/plugins/ui_action_panel/.gitignore b/example_plugins/plugins/ui_action_panel/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/ui_action_panel/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/ui_action_panel/gauntlet.toml b/example_plugins/plugins/ui_action_panel/gauntlet.toml new file mode 100644 index 0000000..d53558b --- /dev/null +++ b/example_plugins/plugins/ui_action_panel/gauntlet.toml @@ -0,0 +1,26 @@ +[gauntlet] +name = 'Docs Action Panel' +description = '' + +# docs-code-segment:start action +[[entrypoint]] +id = 'action' +name = 'Action' +path = 'src/action.tsx' +type = 'view' +description = '' + +[[entrypoint.actions]] +id = 'actionId' +description = '' +shortcut = { key = 'g', kind = 'main'} +# docs-code-segment:end + +# docs-code-segment:start section +[[entrypoint]] +id = 'section' +name = 'Section' +path = 'src/section.tsx' +type = 'view' +description = '' +# docs-code-segment:end diff --git a/example_plugins/plugins/ui_action_panel/package.json b/example_plugins/plugins/ui_action_panel/package.json new file mode 100644 index 0000000..775e96f --- /dev/null +++ b/example_plugins/plugins/ui_action_panel/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/ui-action-panel", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/ui_action_panel/src/action.tsx b/example_plugins/plugins/ui_action_panel/src/action.tsx new file mode 100644 index 0000000..8dafdf3 --- /dev/null +++ b/example_plugins/plugins/ui_action_panel/src/action.tsx @@ -0,0 +1,31 @@ +import { ReactElement } from "react"; +import { ActionPanel, List } from "@project-gauntlet/api/components"; + +export default function View(): ReactElement { + return ( + + { + console.log(`Primary action for item with id '${id}' was executed`) + // returning object with close property set to true will close the window + return { close: true } + }} + /> + console.log(`Secondary action for item with id '${id}' was executed`)} + /> + console.log(`Another action for item with id '${id}' was executed`)} + /> + + }> + + + ) +} + diff --git a/example_plugins/plugins/ui_action_panel/src/section.tsx b/example_plugins/plugins/ui_action_panel/src/section.tsx new file mode 100644 index 0000000..7990070 --- /dev/null +++ b/example_plugins/plugins/ui_action_panel/src/section.tsx @@ -0,0 +1,33 @@ +import { ReactElement } from "react"; +import { ActionPanel, List } from "@project-gauntlet/api/components"; + +export default function View(): ReactElement { + return ( + + console.log(`Primary action for item with id '${id}' was executed`)} + /> + + console.log(`Secondary action for item with id '${id}' was executed`)} + /> + console.log(`Another action for item with id '${id}' was executed`)} + /> + + + console.log(`Yet another action for item with id '${id}' was executed`)} + /> + + + }> + + + ) +} diff --git a/example_plugins/plugins/ui_action_panel/tsconfig.json b/example_plugins/plugins/ui_action_panel/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/ui_action_panel/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_icons_and_images/.gitignore b/example_plugins/plugins/ui_icons_and_images/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/ui_icons_and_images/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/ui_icons_and_images/assets/logo.png b/example_plugins/plugins/ui_icons_and_images/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f677191ee3a6fc167b08c4a0a390a6e4ecdc86c6 GIT binary patch literal 18959 zcmeIZWmI0vvM5S$39iAN1b3I<4#6D)A0O`S1P>k{xCM6z?(XjH5Fof)ki1FO+GnqQ z?;YcfGse5`*8wJT&hF~!>gww1>Z+Qd9~2~!5%CcrARv&XrNoqh``yb04-5S7oTNhn zZa=}A(k9Zjfae#Omka+D%%6J%@J#Uc z^H<>c?cXpjZCGAGLLdY8G~i+dp0R*?F>slO{MA<*@ci-v|L3(#e^Qv~_#-y?wf$MZ`MT(zGS0lNLo zMMXbIi;9xkIoO(*TY(`U+|s;b1f;s(6ZRUYh);$Eyb&X0Ri$Yz{0u4+L8lGW&QgPm zlWqSdEgHME^|k?r@KLjrw}JccV1pLkpM-P_MInq)dFywqNH`v9)WiA7+V%+%DLbuP zee>2Q8*X*pk#sBcF&_l$XhI?kP=(F{(F`04IeGC}W$mtVt6_t=|g zqnyK@kK}_K)@X3Fq$fX*htkkDk)Bh`t7Wcp;|k!o<(q-0E_=u;RgXT_251dVg%*V7 zq}Y6D`*N+d-O{U7F{62y@fZ}-Qdu?PoRTADgf9DqOjsNlthzxeQ6d}`$$m@&`ACsr zK%FR4&Wgu_^C1YO_cGvj=c&zc*Gz6EmNdjFQ~3i6?9~zEXw#t&Nwdu*G`tX$hQzXg zHAHo0?)n1?{2QM%1{8G$BPWlJL$x?t^6Ob-ZTL&;H zJ0m+IGlRIBxeF_~AR;NhgRu#(vY5o*D1bKsax*6zLvRt5lq!O`8u$XjXb2}$?zv#2J zlb8Q5^fr!va{=H7lbfL(6AL3VleIO|KU+9DiMs$u{wC=E*uqiO-44v840g12b^w9J zUBEU@6#qnF4EnG3cFqn~e}-cWVgg%%tpQL+pjVdv=<;P9{#T0^7MPk_+x=+;F#A6s zfx&{r-TqD9%ljWdM<-(@Y2bw&XaN8)u`)ArGcdC~e>3|(#`aS4haLYM z2!QOr==u-nfAQ`QFaRYl&nspNa()?}w3q<-3;n#twjgt3-an5ZurV74GqVW;8z(mh z13N2-oxu>q%EQ3P3^oC?a&WU5aU1^wm9&kclc5a={DKNV&S(zc;Wjkp;xRNbWZ+^o z0#LAnnHh|@dAJ$4j5&;WjCi;>xWL^1K%wYh4oJ13)jz3vL1he}0&%c2v+)2}IE`41 z7}&WCxfpoZnK>B%^z58QEIjO7%*=mK8H0EwY#po(0e+iX8=8Wd>}*W`RJ=?WukZ(H z0diKxzd-+#e6TWfG65P0kjt6dIJ^BDRMp%Xtm0(&!c7(~W=cN zv;Ct+>;F^(@S0cD0c_}G>!50DYb8MbGQ>Z7&7a<0s^tL+j18R(#SEQ*A^m&zQT^BM zL&wa*`}a)zfwwg_H*x=egMLvCr2GIpAd*t%jzItJe~SLn7%E`jA{(lfv7DFB|J2xO&Mr_8c4D2Rs z%naPzoW=}XoXj9#$~n2&nK}M&;lHpu+L}1I8ajZ5O@YY+cm*tUW-zfkcr{j;CHApfDdfhK>I0b2sF!!Z5V z7V|e90GIx6{`@Tg|2MlJCHMq!`5Sm)~!#1#v(Lf}NC>BLoDN{LA&~>rf;U zPzdKFEiVrD6A}vRH6$GQ0Z>N@AuT4X>b7*;YU-qFnsWNE-0Xld^oH^a8V~;Kq>s#< zizh25X_q5ue5SkQ&10>H8O~#RhiM2U1`4cQqehLiZ76b%v=9$I7r((gUI zE-4zk9}+YqnVsKzQfS8i(UyZnmS}}- zRhKA<{{rjQo-M+k^+l?<2Lo}_EVlf88jfN_iM*kLE)r%`bkrRcOR5~R7K<5GTMcK( zqKb7Aa!T`!z@}!b>3T!wPC}Aq021`=(DCyxQFr>}&6biaSmu2W`cy*#qEoTQ8UMin z+<9ZR_AbG1%K}qBxUT^Ibhd5`TUKeQAB)7Eth(}AH!1OGpc`Y#(Y94_reasG-adMk z^-ad4eBbbw1~*EIu0Tix0Mudt>^(_o*lUnHe&DDIT+T)Z3M5jP%EpX(3Av`;5(m%= zx2ezo+>W3$&`s|h;?c^XJxMiQK7HcVg+K2MnC z$+KNV7rrjMF7qZ{S=o0!Lk>isvU{tc#=jmzmek9Z6v{FKS(Wfwe{nVzNM!2M|eA87-ZN`(}BNVzgZiGEM>*L0zmx*F4 z)rqFaW^F4&i*KLrcvQ}GBHk;%lDD@Dfk=Kxee~K|TOrXu*c^w-61ua@Vujk)dn7rs zJU!^wHqKx&2w^Z8p&9YwezIfuUTVHlqnf+2)a&GK_i2u~jGXlJ;m_y*prnes#Bt$Y zYlpVGw|v3@vu7h z%ug8zgDa<;MxEU5o}IKs2>Mm4!XfB$uYh#^(N!aRpgT&+;p|P@jJMgO zbW}EKK>>UGk9%;|xBM)|9eDu?#4eY8cJ%L{8IyE>EOI>lJU4RCgN48n-+9_5YVrE! zXvIJh(LIiUBo64CxXzo8K4rf;e_{Pl2)&!bF0 zT-|T=<`vS~2_}HW&IAYlu5XvzYImk=9$W70upXa#9gAqX-vI-1wk8SRdfq0gFlgJx;h2@N(6N;aLt&h-r7a=BHoM565P zJxAplz=@!g1Pd9!>)Oq0addYT%wv6=$b(dK9wqZ>%0u^6eZ#f)kp)?i*6)LnYLpHD zrr~ZD6iRL;7mY5OIazGayOSIMIwwXFscS*;k=p6m+HY zGS-f7WGEu_X^=h3HA9%F0VZ>MIV6XN;D9NHF^UmG#ysyv$13AIc(G(wnt;{$E;1dM zc7#vRUOs9#C3_}h(nZHvt2BIf+iOwwo(g%3zYJRq)HY=tRe|A;^)lq`1~Qbnki`tq z`R+;aRS;c799|B&5(HmAe_-dK_1plEp^H+;dbxs#FPr!>sbgt%Q0u-wHQ26X>eT6R zmEL+j+J9AAVg~lC*8~CTTX4}>%sK3{LJSEX+?*BJ_{L$Wd+x!DA$!^N9+FJ5ia@XQ zrLf5)0_p=ZCpfY(NnPP2?DiYVb|Pt+^s?Ng-PIe&$LN5}ao1SkAPaf5e9pE4d6eBZ z6Gal1At@#~(z{r&pt6M@i+F1SxZ2)2Slq9H#DDOryefRp-C412vmxj0B$95b7GtlO zt}7Bl$x+4vpu5W=*BGxSVU(G7@PzuKNOuwz0{P}+i3PS;^?Ye0Stv9Be3T}*Z|;`z zb#^j{+VI-l9KBQn!CiLs0}_r;48Rh`kR6iuGW0g&wXUAU+yV3*QDBl~&B_vB{ir4n)mmCzpXPIhMAILi$kDy7A_e_|*qBy7P0WG5o# zI$yjynwTm3d$XCClBx@#`QEJkS|OPVBB6FwaOJbjwOGI0MKszSWx+(&&O`J%Ext6h zTAk0rP#s7w?k0sk!$g?4@0Vw3!=A`ST-rM=-nIub^plfd*z>?H%Li?@(;8o8gG6)j z^w7AtgIQ80u?u$ zQK!gqLWS_Mw}lf~eyX;}XV^x_2DlYc5&Y6=@aW|+e;Ys5iI_6@AxpiE_!t9smvbZ~ z_Wov}Qnrmn$C?IOu+z;dCO|S_Cp(5J+dfFcg{%6|>%DH$@)zq_vRY&49oC>Rshqy ziSs7aUJ1<_zfYP>HWN^n1Twb=3UBx_RNXIg##nrPs_sJjiyElze*Gd;A5jS9!*8!e zDYeF)^JF$NPU4|ryM44Ov(+5|VM3A=Y(3A3Hqw{5ys~>v$;^+M`O=)5J+-CyIh-B- zB-5r7Or&iI>^w`m%oRtrB)EHTp#_P1Tgei8)f6D2^iH<=byBB_3X6ww4?m?;c9al_ z{(5-NJ$U$_bGJ8tV16DMO-@t^OJZtyVp~2Aw$H;<3lb>cz3a|Uoma`@`>h(Q+&T&( zH$ARX{c-S)iO}ohzBYlm-M8I+B~ls(l0?$WS`S6mguA&pqwf5G%=XuBqg6{gtyt!L zDiNbMK00%wuGzCIJPo_3R12yWYX)4Ed@L(T@&Qt68e$1waS1EbSo{3#2NcT+Y!E5T-H7+R7!3$k{B@D z=fb$`wR^^wFtucM_AB(hq}yoCVq!Ne;r(FpTq-nA)$ByFr&$p#zPP5(u*u0Dkn>w! zpc}|3p4g98TE^N^wf;n*(cKeA`}?*)7Q*aO0-+C<(ZcL{t%;?K`n-BAmtS&xZ+=%f zVZMF#1hK~**lJJHZxjc-;Q*|&wd{b70FE|8GmuD(spW~wWreg18E+F00|?u&HjGqi z#^p^|bLkbhJs#eV=k>wU7M0Zy7U*kQBM4TG^d7it;W>Gix$tM%-5KFBou1-N&wdoy zxW#LgF!E^2*rFGp7PUxU!IrVhr4D4uCXK0*x!+s=hsFat(Nsz|Q;r&Pozs#?qb9FzM--MF&^#Ov<)!Q%p zI#*1w5+XbE(X$N2jyJ)Pt7fU~h|Pd>;JeU;z3{l2QJ_>iSnf(Ix~{(UBHeCP&C{8G zYD^|6Xk!o&cXgS2M107_xXXS=ouQq)e8}~4KZ<1Er5|c?aF$jZzJ&lmlexF2>Wxc2 zt_~}TYOP1k3!=jsTQQ5ExYb1!uKBI{Z8^O!vzJv6${B%bq7TrDZj}UJ!FJw|`{|>n z8}u}Qf&{f!+SUiDEI=A0Rs1n;}fqT48!v(CntLq8nFx2urt)wuRe zf->AGJ;oE?re#Vq_crpL(lKx9J?#?WLlSp$X>xBUv7|3LLs9Ao&lL_hC*zbi@U@Iq zt>|P+(y{iE@cK$`1?kIcq6<50CB{bs(A<=j%#VI<<||fT?m?wv+O%r@n7U}3ZB)h4 z@$BU#t!1+>XY2@X*6G#EAEdSm*WazAQozyR`Q?2O4Gl~Yx7@{N`9^q&CK#I;a%opa z7y+$qMI`>=a#Q(QXE74)oKX5smD@&5f%Qn~^;^IBgV2ll0BbK*OcCZ#XhzwypX6{Q zdthYCo6CUrxCdt@hpsp)!p1%@K)ea?Ep(LF?<8ZA_Oq zJXqTjM$#P#tq#(WczmW2PqwP?^xbOSKI@i5bUS)(X=Xf09Vz=BxnSxHgSj6v^Yw_Y zA3hr)zI)F$uLfr?$<5f$d*#^&wyY;{-uSYcM)u*ej@sr_B*gEl3l9C2^>;l2TJ&z> zVM7KcJlrab?W{^fr>;&#NE7% zZmpjpEAJSHB)?5p36gFM1x)vt?Vk@HFYmUk_+~iC=QZKIA{^P?fKOEE^4zIc>U2V> zi&GF=24bBQqs_Ntt!@lnqclsuuf9#_DEN{5iSN`}a_hx>z6iP&mr}MseJUY9y#M{| zMYPV1_r|NatinxXXF!~Z>BN$T5EI9<3sD!+1eZXIXIL04ed>Mqo?95?8AumN7Nv`D zkuHfi=!}1|f4;+g#qXZ!xL|p{REKw-8>T>^lweDI^>CvL&uVIcds&e8E2{C>qwQAe z)$d0Ss6m#}bBMO;=SduMgyL z$_iiIgQUd5PSo0ELp6>bcy^&KP1`n^f3yBQRvwrKPTQ|t^b}es0^#{d+`I$!V!xkl z=zfoazmX0nX*XeM-|ha=^=Zs;9(gKn-%)dvO8*$vOTJ>m>+(tCD@NRLO5w-RtM3}Ik%+NkC?xp84$uUulewa}s z8Mm<{o-_5b$xDU9HHMm4Al__mck-SXB)E4$_Wd2IOejFVwWgB(Xmh(@_CP27-MuXU zIlTHI7n%k;emk+*&D{3kvo)UrCwn6d)$dbHp@A@60U@`VFL|?LP96048r)u!!$`JV zm3z7nwo<~eAMG1MPx*BRRvh?v%ObeY_RfMBhc4uD9FBMtponb)3A%swEly(Z`7J6K zYjv5-k-w8>)({)W+U7$HN$O?9+roFh*wMN7w>~*%%N{BkOV*KbX)Jt}4 zZRqo0gK0~2N}hsd0!+S_b8UQu4LxNEUuH~T z9?em0Bl{px+!p@&vY5+j^f@LwFnhKMCB3RvMyM#R*PHc1zrwC7FHzp853!&hR@NUU z)XV5f?HUE8oCnLgl64FGc4kosB|>E*(NA_z;v|9<86D3XYhk>NC_1HLi9(j$H&@lQ zy^TI~2jbiXH*yNrHj(N{&+dnx7o?B2xD3RVBgRVYQ2K&Y(1ArjDV?3qGJv#9u;@Id z$e)}-QEL>89B%Iq|cfjk{f0Few$`Hae9hK=jGNZ znISq&)GXz#;O>{47ta}c4n2P&+?<^p1Pcf-I#NE5hjWb*R(u@br?@eppeYYsw7}Hg zprd8*@{oVkbdtK!bqyipljBz+U?Wl@yqikkpw-R{7obl~C|`Blt^N!$FD~&p|Xf?(uL@h+Ez~R@gjb1dxV!}3z+(rR8=_r=g z%a5EENh&rVn`DFvAr}q$0%5xIAhkvQR@QdKz?Md~A(D05tqRGO!0j%g%rnS}k@?Hn zNO8PohTBP5y+L`8Y&>2A& zd2i}EX3-_pEax)8#M=-%H{>d z6QBEW2`ZJRsdh}-F741~!E^c#OMn~pHrn-U*j3x?tS1PcCZI!}Hg&#%Mc1)JC4`*f zuE+#tSz48oGJ0uMoMaviTs()bT52k&5#6|64Q2QaX|TpxBC(W!v6e7q$6V4Q#+s3v z#m08N7|Cj&lQ_Ja$J2U&rHVafaC>>lMZlwriJ@Zc9-7V+DN|ia|vCk4X;rl_Gf z`i}FgRT6N%Nj3ghd!M%6vO17~6o9I`v%oG#R~7Ng9cM3B_wd(i29Takgd4kpnzYOh zoSXz0Z1t4ot{Si=)an~KhYkPC{5mvu1@8*Dtlq-B;+~9$-tN%VGG*Vt9EvlA(m7hzBLQa@n`B%$n@Qz_