mirror of
https://github.com/project-gauntlet/gauntlet.git
synced 2025-12-23 10:35:53 +00:00
Implement entrypoint search aliases
This commit is contained in:
parent
3064f7f297
commit
40fb6f2617
17 changed files with 565 additions and 99 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -9,17 +9,23 @@ For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://git
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### General
|
||||
- It is now possible to assign custom alias to entrypoints which is used for search
|
||||
- Windows in "Opened windows" view entrypoint are now sorted following "most recently focused on the top" order
|
||||
|
||||
### Plugins
|
||||
- Plugin manifest property `entrypoint.*.actions.*.shortcut` is now optional
|
||||
- Add `<Content.Svg/>` component to display SVG images
|
||||
- **BREAKING CHANGE**: Renamed TS types: `ImageSource` to `DataSource`, `ImageSourceUrl` to `DataSourceUrl`, `ImageSourceAsset` to `DataSourceAsset`
|
||||
- Added `<Content.Svg/>` component to display SVG images
|
||||
- **BREAKING CHANGE**: Renamed TypeScript types: `ImageSource` to `DataSource`, `ImageSourceUrl` to `DataSourceUrl`, `ImageSourceAsset` to `DataSourceAsset`
|
||||
|
||||
### UI/UX improvements
|
||||
- Made font size in the Settings UI a little smaller
|
||||
|
||||
### Fixes
|
||||
- Fixed crash when closing inline view due to Action being run
|
||||
- Fixed crash when closing inline view due to Action being run, again...
|
||||
- Fixed shortcut assignment error not being shown for global entrypoint shortcuts
|
||||
- Fixed crash on X11 when trying to assign shortcut that is already used by another application
|
||||
- Unified `Vec<u8>` usage to `ArrayBuffer` in JS
|
||||
- Fixes `icon` in EntrypointGenerator requiring `number[]`
|
||||
- Fixed `icon` in `EntrypointGenerator` requiring `number[]` instead of declared `ArrayBuffer`
|
||||
- Fixed text selection not being visible when selecting text in form view text fields
|
||||
- Fixed plugin runtime crash when using `assetDataSync()` function
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use gauntlet_common::model::SearchResultAccessory;
|
|||
use gauntlet_common::model::SearchResultEntrypointType;
|
||||
use gauntlet_common::model::TextAccessoryWidget;
|
||||
use iced::advanced::image::Handle;
|
||||
use iced::color;
|
||||
use iced::widget::button;
|
||||
use iced::widget::column;
|
||||
use iced::widget::container;
|
||||
|
|
@ -14,7 +15,9 @@ use iced::widget::horizontal_space;
|
|||
use iced::widget::row;
|
||||
use iced::widget::text;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::text_input;
|
||||
use iced::Alignment;
|
||||
use iced::Font;
|
||||
use iced::Length;
|
||||
|
||||
use crate::ui::scroll_handle::ScrollHandle;
|
||||
|
|
@ -67,6 +70,15 @@ pub fn search_list<'a>(
|
|||
|
||||
button_content.push(entrypoint_name);
|
||||
button_content.push(plugin_name_text);
|
||||
|
||||
if let Some(alias) = &search_result.entrypoint_alias {
|
||||
let alias: Element<_> = text(alias.clone()).shaping(Shaping::Advanced).into();
|
||||
|
||||
let alias: Element<_> = container(alias).themed(ContainerStyle::MainListItemAlias).into();
|
||||
|
||||
button_content.push(alias);
|
||||
}
|
||||
|
||||
button_content.push(spacer);
|
||||
|
||||
if search_result.entrypoint_accessories.len() > 0 {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ pub enum ContainerStyle {
|
|||
Main,
|
||||
MainList,
|
||||
MainListInner,
|
||||
MainListItemAlias,
|
||||
MainListItemIcon,
|
||||
MainListItemSubText,
|
||||
MainListItemText,
|
||||
|
|
@ -81,6 +82,7 @@ pub enum ContainerStyleInner {
|
|||
ActionShortcutModifier,
|
||||
ContentCodeBlockText,
|
||||
Main,
|
||||
MainListItemAlias,
|
||||
Root,
|
||||
ContentImage,
|
||||
RootBottomPanel,
|
||||
|
|
@ -257,6 +259,20 @@ impl container::Catalog for GauntletComplexTheme {
|
|||
shadow: Default::default(),
|
||||
}
|
||||
}
|
||||
ContainerStyleInner::MainListItemAlias => {
|
||||
let theme = &self.main_list_item_alias;
|
||||
|
||||
Style {
|
||||
text_color: None,
|
||||
background: None,
|
||||
border: Border {
|
||||
color: theme.background_color.clone().into(),
|
||||
width: 2.0,
|
||||
radius: theme.border_radius.into(),
|
||||
},
|
||||
shadow: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -334,6 +350,10 @@ impl<'a, Message: 'a> ThemableWidget<'a, Message> for Container<'a, Message, Gau
|
|||
ContainerStyle::MainListItemText => self.padding(theme.main_list_item_text.padding.to_iced()),
|
||||
ContainerStyle::MainListItemSubText => self.padding(theme.main_list_item_sub_text.padding.to_iced()),
|
||||
ContainerStyle::MainListItemIcon => self.padding(theme.main_list_item_icon.padding.to_iced()),
|
||||
ContainerStyle::MainListItemAlias => {
|
||||
self.padding(theme.main_list_item_alias.padding.to_iced())
|
||||
.class(ContainerStyleInner::MainListItemAlias)
|
||||
}
|
||||
ContainerStyle::MainList => self.padding(theme.main_list.padding.to_iced()),
|
||||
ContainerStyle::MainListInner => self.padding(theme.main_list_inner.padding.to_iced()),
|
||||
ContainerStyle::MainSearchBar => self.padding(theme.main_search_bar.padding.to_iced()),
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ pub struct GauntletComplexTheme {
|
|||
main_list: ThemePaddingOnly,
|
||||
main_list_inner: ThemePaddingOnly,
|
||||
main_list_item: ThemeButton,
|
||||
main_list_item_alias: ThemeEntrypointAlias,
|
||||
main_list_item_icon: ThemePaddingOnly,
|
||||
main_list_item_sub_text: ThemePaddingTextColor,
|
||||
main_list_item_text: ThemePaddingOnly,
|
||||
|
|
@ -450,6 +451,11 @@ impl GauntletComplexTheme {
|
|||
border_width: 0.0,
|
||||
border_color: Color::TRANSPARENT,
|
||||
},
|
||||
main_list_item_alias: ThemeEntrypointAlias {
|
||||
padding: padding_axis(4.0, 6.0),
|
||||
background_color: background_100,
|
||||
border_radius: content.border.radius,
|
||||
},
|
||||
main_list_item_text: ThemePaddingOnly {
|
||||
padding: padding_all(4.0),
|
||||
},
|
||||
|
|
@ -768,6 +774,13 @@ pub struct ThemePaddingTextColor {
|
|||
text_color: Color,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ThemeEntrypointAlias {
|
||||
padding: ThemePadding,
|
||||
background_color: Color,
|
||||
border_radius: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ThemePaddingTextColorSize {
|
||||
padding: ThemePadding,
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ pub struct SearchResult {
|
|||
pub entrypoint_type: SearchResultEntrypointType,
|
||||
pub entrypoint_actions: Vec<SearchResultEntrypointAction>,
|
||||
pub entrypoint_accessories: Vec<SearchResultAccessory>,
|
||||
pub entrypoint_alias: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -142,6 +142,15 @@ pub trait BackendForSettingsApi {
|
|||
&self,
|
||||
) -> RequestResult<HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option<String>)>>;
|
||||
|
||||
async fn set_entrypoint_search_alias(
|
||||
&self,
|
||||
plugin_id: PluginId,
|
||||
entrypoint_id: EntrypointId,
|
||||
alias: Option<String>,
|
||||
) -> RequestResult<()>;
|
||||
|
||||
async fn get_entrypoint_search_aliases(&self) -> RequestResult<HashMap<(PluginId, EntrypointId), String>>;
|
||||
|
||||
async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()>;
|
||||
|
||||
async fn get_theme(&self) -> RequestResult<SettingsTheme>;
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ pub fn render_shortcut<'a, Message: 'a>(shortcut: &ShortcutData, in_table: bool)
|
|||
}
|
||||
} else {
|
||||
if in_table {
|
||||
content.push(text("Record Shortcut").class(TextStyle::Subtitle).into());
|
||||
content.push(text("Record Shortcut").size(14).class(TextStyle::Subtitle).into());
|
||||
|
||||
if let Some(error) = &shortcut.error {
|
||||
content.push(horizontal_space().width(Length::Fill).into());
|
||||
|
|
|
|||
|
|
@ -6,13 +6,16 @@ use iced::Border;
|
|||
|
||||
use crate::theme::GauntletSettingsTheme;
|
||||
use crate::theme::BACKGROUND_DARKER;
|
||||
use crate::theme::BACKGROUND_LIGHTER;
|
||||
use crate::theme::BACKGROUND_LIGHTEST;
|
||||
use crate::theme::BUTTON_BORDER_RADIUS;
|
||||
use crate::theme::TEXT_DARKER;
|
||||
use crate::theme::TEXT_LIGHTEST;
|
||||
use crate::theme::TRANSPARENT;
|
||||
|
||||
pub enum TextInputStyle {
|
||||
FormInput,
|
||||
EntrypointAlias,
|
||||
}
|
||||
|
||||
impl text_input::Catalog for GauntletSettingsTheme {
|
||||
|
|
@ -22,7 +25,31 @@ impl text_input::Catalog for GauntletSettingsTheme {
|
|||
TextInputStyle::FormInput
|
||||
}
|
||||
|
||||
fn style(&self, _class: &Self::Class<'_>, status: Status) -> Style {
|
||||
fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
|
||||
match class {
|
||||
TextInputStyle::EntrypointAlias => {
|
||||
let border = if let Status::Focused | Status::Hovered = status {
|
||||
Border {
|
||||
radius: BUTTON_BORDER_RADIUS.into(),
|
||||
width: 2.0,
|
||||
color: BACKGROUND_LIGHTER.to_iced(),
|
||||
}
|
||||
} else {
|
||||
Border::default()
|
||||
};
|
||||
|
||||
return Style {
|
||||
background: Background::Color(TRANSPARENT.to_iced().into()),
|
||||
border,
|
||||
icon: TEXT_LIGHTEST.to_iced(),
|
||||
placeholder: TEXT_DARKER.to_iced(),
|
||||
value: TEXT_LIGHTEST.to_iced(),
|
||||
selection: BACKGROUND_LIGHTEST.to_iced(),
|
||||
};
|
||||
}
|
||||
TextInputStyle::FormInput => {}
|
||||
}
|
||||
|
||||
let active = Style {
|
||||
background: Background::Color(TRANSPARENT.to_iced().into()),
|
||||
border: Border {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ pub fn run() {
|
|||
view,
|
||||
)
|
||||
.window(window::Settings {
|
||||
size: Size::new(1000.0, 600.0),
|
||||
size: Size::new(1150.0, 700.0),
|
||||
..Default::default()
|
||||
})
|
||||
.subscription(subscription)
|
||||
|
|
@ -261,16 +261,21 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task<Man
|
|||
async move {
|
||||
let plugins = backend_api.plugins().await?;
|
||||
let global_entrypoint_shortcuts = backend_api.get_global_entrypoint_shortcuts().await?;
|
||||
let entrypoint_search_aliases = backend_api.get_entrypoint_search_aliases().await?;
|
||||
|
||||
Ok((plugins, global_entrypoint_shortcuts))
|
||||
Ok((plugins, global_entrypoint_shortcuts, entrypoint_search_aliases))
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| {
|
||||
ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
))
|
||||
})
|
||||
handle_backend_error(
|
||||
result,
|
||||
|(plugins, global_entrypoint_shortcuts, entrypoint_search_aliases)| {
|
||||
ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
entrypoint_search_aliases,
|
||||
))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ pub enum ManagementAppPluginMsgIn {
|
|||
PluginsReloaded(
|
||||
HashMap<PluginId, SettingsPlugin>,
|
||||
HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option<String>)>,
|
||||
HashMap<(PluginId, EntrypointId), String>,
|
||||
),
|
||||
RemovePlugin {
|
||||
plugin_id: PluginId,
|
||||
|
|
@ -91,6 +92,7 @@ pub struct ManagementAppPluginsState {
|
|||
preference_user_data: HashMap<(PluginId, Option<EntrypointId>, String), PluginPreferenceUserDataState>,
|
||||
selected_item: SelectedItem,
|
||||
global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option<String>)>,
|
||||
entrypoint_search_aliases: HashMap<(PluginId, EntrypointId), String>,
|
||||
}
|
||||
|
||||
impl ManagementAppPluginsState {
|
||||
|
|
@ -127,6 +129,7 @@ impl ManagementAppPluginsState {
|
|||
selected_item: select_item,
|
||||
table_state: PluginTableState::new(),
|
||||
global_entrypoint_shortcuts: HashMap::new(),
|
||||
entrypoint_search_aliases: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,16 +160,21 @@ impl ManagementAppPluginsState {
|
|||
let plugins = backend_client.plugins().await?;
|
||||
let global_entrypoint_shortcuts =
|
||||
backend_client.get_global_entrypoint_shortcuts().await?;
|
||||
let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?;
|
||||
|
||||
Ok((plugins, global_entrypoint_shortcuts))
|
||||
Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases))
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
))
|
||||
})
|
||||
handle_backend_error(
|
||||
result,
|
||||
|(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
entrypoint_aliases,
|
||||
))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -186,16 +194,21 @@ impl ManagementAppPluginsState {
|
|||
let plugins = backend_client.plugins().await?;
|
||||
let global_entrypoint_shortcuts =
|
||||
backend_client.get_global_entrypoint_shortcuts().await?;
|
||||
let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?;
|
||||
|
||||
Ok((plugins, global_entrypoint_shortcuts))
|
||||
Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases))
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
))
|
||||
})
|
||||
handle_backend_error(
|
||||
result,
|
||||
|(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
entrypoint_aliases,
|
||||
))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -232,16 +245,51 @@ impl ManagementAppPluginsState {
|
|||
let plugins = backend_client.plugins().await?;
|
||||
let global_entrypoint_shortcuts =
|
||||
backend_client.get_global_entrypoint_shortcuts().await?;
|
||||
let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?;
|
||||
|
||||
Ok((plugins, global_entrypoint_shortcuts))
|
||||
Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases))
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
))
|
||||
})
|
||||
handle_backend_error(
|
||||
result,
|
||||
|(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
entrypoint_aliases,
|
||||
))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, shortcut) => {
|
||||
let mut backend_client = backend_api.clone();
|
||||
|
||||
Task::perform(
|
||||
async move {
|
||||
backend_client
|
||||
.set_entrypoint_search_alias(plugin_id, entrypoint_id, shortcut)
|
||||
.await?;
|
||||
|
||||
let plugins = backend_client.plugins().await?;
|
||||
let global_entrypoint_shortcuts =
|
||||
backend_client.get_global_entrypoint_shortcuts().await?;
|
||||
let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?;
|
||||
|
||||
Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases))
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(
|
||||
result,
|
||||
|(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
entrypoint_aliases,
|
||||
))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -257,7 +305,11 @@ impl ManagementAppPluginsState {
|
|||
plugin_data.plugins.clone()
|
||||
};
|
||||
|
||||
self.apply_plugin_fetch(plugins, self.global_entrypoint_shortcuts.clone());
|
||||
self.apply_plugin_fetch(
|
||||
plugins,
|
||||
self.global_entrypoint_shortcuts.clone(),
|
||||
self.entrypoint_search_aliases.clone(),
|
||||
);
|
||||
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -278,7 +330,11 @@ impl ManagementAppPluginsState {
|
|||
plugin_data.plugins.clone()
|
||||
};
|
||||
|
||||
self.apply_plugin_fetch(plugins, self.global_entrypoint_shortcuts.clone());
|
||||
self.apply_plugin_fetch(
|
||||
plugins,
|
||||
self.global_entrypoint_shortcuts.clone(),
|
||||
self.entrypoint_search_aliases.clone(),
|
||||
);
|
||||
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -321,21 +377,23 @@ impl ManagementAppPluginsState {
|
|||
async move {
|
||||
let plugins = backend_api.plugins().await?;
|
||||
let global_entrypoint_shortcuts = backend_api.get_global_entrypoint_shortcuts().await?;
|
||||
let entrypoint_aliases = backend_api.get_entrypoint_search_aliases().await?;
|
||||
|
||||
Ok((plugins, global_entrypoint_shortcuts))
|
||||
Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases))
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| {
|
||||
handle_backend_error(result, |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
entrypoint_aliases,
|
||||
))
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
ManagementAppPluginMsgIn::PluginsReloaded(plugins, shortcuts) => {
|
||||
self.apply_plugin_fetch(plugins, shortcuts);
|
||||
ManagementAppPluginMsgIn::PluginsReloaded(plugins, shortcuts, entrypoint_aliases) => {
|
||||
self.apply_plugin_fetch(plugins, shortcuts, entrypoint_aliases);
|
||||
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -350,14 +408,16 @@ impl ManagementAppPluginsState {
|
|||
|
||||
let plugins = backend_client.plugins().await?;
|
||||
let global_entrypoint_shortcuts = backend_client.get_global_entrypoint_shortcuts().await?;
|
||||
let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?;
|
||||
|
||||
Ok((plugins, global_entrypoint_shortcuts))
|
||||
Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases))
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |(plugins, global_entrypoint_shortcuts)| {
|
||||
handle_backend_error(result, |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
global_entrypoint_shortcuts,
|
||||
entrypoint_aliases,
|
||||
))
|
||||
})
|
||||
},
|
||||
|
|
@ -381,6 +441,7 @@ impl ManagementAppPluginsState {
|
|||
&mut self,
|
||||
plugins: HashMap<PluginId, SettingsPlugin>,
|
||||
global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option<String>)>,
|
||||
entrypoint_search_aliases: HashMap<(PluginId, EntrypointId), String>,
|
||||
) {
|
||||
self.global_entrypoint_shortcuts = global_entrypoint_shortcuts.clone();
|
||||
|
||||
|
|
@ -458,8 +519,12 @@ impl ManagementAppPluginsState {
|
|||
|
||||
plugin_refs.sort_by_key(|(plugin, _)| &plugin.plugin_name);
|
||||
|
||||
self.table_state
|
||||
.apply_plugin_reload(self.plugin_data.clone(), plugin_refs, global_entrypoint_shortcuts)
|
||||
self.table_state.apply_plugin_reload(
|
||||
self.plugin_data.clone(),
|
||||
plugin_refs,
|
||||
global_entrypoint_shortcuts,
|
||||
entrypoint_search_aliases,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Element<ManagementAppPluginMsgIn> {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use iced::widget::row;
|
|||
use iced::widget::scrollable;
|
||||
use iced::widget::scrollable::Id;
|
||||
use iced::widget::text;
|
||||
use iced::widget::text_input;
|
||||
use iced::widget::value;
|
||||
use iced::widget::Space;
|
||||
use iced::Alignment;
|
||||
|
|
@ -31,6 +32,8 @@ 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::text::TextStyle;
|
||||
use crate::theme::text_input::TextInputStyle;
|
||||
use crate::theme::Element;
|
||||
use crate::theme::GauntletSettingsTheme;
|
||||
use crate::views::plugins::PluginDataContainer;
|
||||
|
|
@ -50,6 +53,7 @@ pub enum PluginTableMsgIn {
|
|||
entrypoint_id: EntrypointId,
|
||||
},
|
||||
ShortcutCaptured(PluginId, EntrypointId, Option<PhysicalShortcut>),
|
||||
AliasChanged(PluginId, EntrypointId, String),
|
||||
}
|
||||
|
||||
pub enum PluginTableMsgOut {
|
||||
|
|
@ -71,6 +75,7 @@ pub enum PluginTableMsgOut {
|
|||
entrypoint_id: EntrypointId,
|
||||
},
|
||||
ShortcutCaptured(PluginId, EntrypointId, Option<PhysicalShortcut>),
|
||||
AliasChanged(PluginId, EntrypointId, Option<String>),
|
||||
}
|
||||
|
||||
pub struct PluginTableState {
|
||||
|
|
@ -86,6 +91,7 @@ impl PluginTableState {
|
|||
columns: vec![
|
||||
Column::new(ColumnKind::Name),
|
||||
Column::new(ColumnKind::Type),
|
||||
Column::new(ColumnKind::Alias),
|
||||
Column::new(ColumnKind::Shortcut),
|
||||
Column::new(ColumnKind::EnableToggle),
|
||||
],
|
||||
|
|
@ -132,6 +138,12 @@ impl PluginTableState {
|
|||
PluginTableMsgIn::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => {
|
||||
Task::done(PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut))
|
||||
}
|
||||
PluginTableMsgIn::AliasChanged(plugin_id, entrypoint_id, alias) => {
|
||||
let alias = alias.trim().to_owned();
|
||||
let alias = if alias.is_empty() { None } else { Some(alias) };
|
||||
|
||||
Task::done(PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, alias))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +152,7 @@ impl PluginTableState {
|
|||
plugin_data: Rc<RefCell<PluginDataContainer>>,
|
||||
plugin_refs: Vec<(&SettingsPlugin, &SettingsPluginData)>,
|
||||
global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option<String>)>,
|
||||
entrypoint_search_aliases: HashMap<(PluginId, EntrypointId), String>,
|
||||
) {
|
||||
self.rows = plugin_refs
|
||||
.iter()
|
||||
|
|
@ -162,11 +175,16 @@ impl PluginTableState {
|
|||
let shortcut = global_entrypoint_shortcut.map(|(shortcut, _)| shortcut).cloned();
|
||||
let error = global_entrypoint_shortcut.map(|(_, error)| error).cloned().flatten();
|
||||
|
||||
let search_alias = entrypoint_search_aliases
|
||||
.get(&(plugin.plugin_id.clone(), entrypoint.entrypoint_id.clone()))
|
||||
.cloned();
|
||||
|
||||
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 },
|
||||
search_alias,
|
||||
};
|
||||
|
||||
result.push(entrypoint_row);
|
||||
|
|
@ -192,12 +210,17 @@ impl PluginTableState {
|
|||
let shortcut = global_entrypoint_shortcut.map(|(shortcut, _)| shortcut).cloned();
|
||||
let error = global_entrypoint_shortcut.map(|(_, error)| error).cloned().flatten();
|
||||
|
||||
let search_alias = entrypoint_search_aliases
|
||||
.get(&(plugin.plugin_id.clone(), data.entrypoint_id.clone()))
|
||||
.cloned();
|
||||
|
||||
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 },
|
||||
search_alias,
|
||||
};
|
||||
|
||||
result.push(generated_entrypoint_row);
|
||||
|
|
@ -247,6 +270,7 @@ enum Row {
|
|||
plugin_id: PluginId,
|
||||
entrypoint_id: EntrypointId,
|
||||
shortcut_data: ShortcutData,
|
||||
search_alias: Option<String>,
|
||||
},
|
||||
GeneratedEntrypoint {
|
||||
plugin_data: Rc<RefCell<PluginDataContainer>>,
|
||||
|
|
@ -254,6 +278,7 @@ enum Row {
|
|||
generator_entrypoint_id: EntrypointId,
|
||||
generated_entrypoint_id: EntrypointId,
|
||||
shortcut_data: ShortcutData,
|
||||
search_alias: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -261,6 +286,7 @@ enum ColumnKind {
|
|||
Name,
|
||||
Type,
|
||||
Shortcut,
|
||||
Alias,
|
||||
EnableToggle,
|
||||
}
|
||||
|
||||
|
|
@ -292,6 +318,12 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo
|
|||
.align_y(Alignment::Center)
|
||||
.into()
|
||||
}
|
||||
ColumnKind::Alias => {
|
||||
container(text("Alias"))
|
||||
.height(Length::Fixed(30.0))
|
||||
.align_y(Alignment::Center)
|
||||
.into()
|
||||
}
|
||||
ColumnKind::Shortcut => {
|
||||
container(text("Shortcut"))
|
||||
.height(Length::Fixed(30.0))
|
||||
|
|
@ -385,7 +417,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo
|
|||
let plugin_data = plugin_data.borrow();
|
||||
let plugin = plugin_data.plugins.get(&plugin_id).unwrap();
|
||||
|
||||
let plugin_name = text(plugin.plugin_name.to_string()).shaping(Shaping::Advanced);
|
||||
let plugin_name = text(plugin.plugin_name.to_string()).shaping(Shaping::Advanced).size(14);
|
||||
|
||||
container(plugin_name).align_y(Alignment::Center).into()
|
||||
}
|
||||
|
|
@ -401,6 +433,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo
|
|||
|
||||
let text: Element<_> = text(entrypoint.entrypoint_name.to_string())
|
||||
.shaping(Shaping::Advanced)
|
||||
.size(14)
|
||||
.into();
|
||||
|
||||
let space: Element<_> =
|
||||
|
|
@ -429,6 +462,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo
|
|||
|
||||
let text: Element<_> = text(generated_entrypoint.entrypoint_name.to_string())
|
||||
.shaping(Shaping::Advanced)
|
||||
.size(14)
|
||||
.into();
|
||||
|
||||
let space: Element<_> = Space::with_width(Length::Fixed(65.0)).into();
|
||||
|
|
@ -481,7 +515,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo
|
|||
}
|
||||
ColumnKind::Type => {
|
||||
let content: Element<_> = match row_entry {
|
||||
Row::Plugin { .. } => container(text("Plugin")).align_y(Alignment::Center).into(),
|
||||
Row::Plugin { .. } => container(text("Plugin").size(14)).align_y(Alignment::Center).into(),
|
||||
Row::Entrypoint {
|
||||
plugin_data,
|
||||
plugin_id,
|
||||
|
|
@ -499,11 +533,13 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo
|
|||
SettingsEntrypointType::EntrypointGenerator => "Generator",
|
||||
};
|
||||
|
||||
container(text(entrypoint_type.to_string()))
|
||||
container(text(entrypoint_type.to_string()).size(14))
|
||||
.align_y(Alignment::Center)
|
||||
.into()
|
||||
}
|
||||
Row::GeneratedEntrypoint { .. } => container(text("Generated")).align_y(Alignment::Center).into(),
|
||||
Row::GeneratedEntrypoint { .. } => {
|
||||
container(text("Generated").size(14)).align_y(Alignment::Center).into()
|
||||
}
|
||||
};
|
||||
|
||||
let msg = match &row_entry {
|
||||
|
|
@ -552,6 +588,7 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo
|
|||
plugin_id,
|
||||
entrypoint_id,
|
||||
shortcut_data,
|
||||
..
|
||||
} => {
|
||||
let plugin_data = plugin_data.borrow();
|
||||
let plugin = plugin_data.plugins.get(&plugin_id).unwrap();
|
||||
|
|
@ -607,6 +644,58 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo
|
|||
}
|
||||
}
|
||||
}
|
||||
ColumnKind::Alias => {
|
||||
match row_entry {
|
||||
Row::Plugin { .. } => horizontal_space().into(),
|
||||
Row::Entrypoint {
|
||||
plugin_data,
|
||||
plugin_id,
|
||||
entrypoint_id,
|
||||
search_alias,
|
||||
..
|
||||
} => {
|
||||
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 input = text_input("Add Alias", search_alias.as_deref().unwrap_or(""))
|
||||
.class(TextInputStyle::EntrypointAlias)
|
||||
.padding(padding::all(12.0).left(7.0))
|
||||
.size(14)
|
||||
.on_input(move |alias| {
|
||||
PluginTableMsgIn::AliasChanged(plugin_id.clone(), entrypoint_id.clone(), alias)
|
||||
});
|
||||
|
||||
container(input).height(Length::Fixed(40.0)).width(Length::Fill).into()
|
||||
} else {
|
||||
horizontal_space().into()
|
||||
}
|
||||
}
|
||||
Row::GeneratedEntrypoint {
|
||||
plugin_id,
|
||||
generated_entrypoint_id,
|
||||
search_alias,
|
||||
..
|
||||
} => {
|
||||
let input = text_input("Add Alias", search_alias.as_deref().unwrap_or(""))
|
||||
.class(TextInputStyle::EntrypointAlias)
|
||||
.padding(padding::all(12.0).left(7.0))
|
||||
.size(14)
|
||||
.on_input(move |alias| {
|
||||
PluginTableMsgIn::AliasChanged(
|
||||
plugin_id.clone(),
|
||||
generated_entrypoint_id.clone(),
|
||||
alias,
|
||||
)
|
||||
});
|
||||
|
||||
container(input).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 } => {
|
||||
|
|
@ -674,7 +763,8 @@ impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> fo
|
|||
match self.kind {
|
||||
ColumnKind::Name => 300.0,
|
||||
ColumnKind::Type => 100.0,
|
||||
ColumnKind::Shortcut => 200.0,
|
||||
ColumnKind::Shortcut => 190.0,
|
||||
ColumnKind::Alias => 120.0,
|
||||
ColumnKind::EnableToggle => 75.0,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,6 +242,13 @@ pub struct DbSettingsGlobalEntrypointShortcutData {
|
|||
pub shortcut: DbSettingsGlobalShortcutData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct DbSettingsEntrypointSearchAliasData {
|
||||
pub plugin_id: String,
|
||||
pub entrypoint_id: String,
|
||||
pub alias: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct DbSettings {
|
||||
// none means auto-detect
|
||||
|
|
@ -251,6 +258,7 @@ pub struct DbSettings {
|
|||
// 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<DbSettingsGlobalShortcutData>,
|
||||
pub global_entrypoint_shortcuts: Option<Vec<DbSettingsGlobalEntrypointShortcutData>>,
|
||||
pub entrypoint_search_aliases: Option<Vec<DbSettingsEntrypointSearchAliasData>>,
|
||||
}
|
||||
|
||||
impl Default for DbSettings {
|
||||
|
|
@ -281,6 +289,7 @@ impl Default for DbSettings {
|
|||
error: None,
|
||||
}),
|
||||
global_entrypoint_shortcuts: None,
|
||||
entrypoint_search_aliases: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -827,6 +827,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl {
|
|||
generated_search_items,
|
||||
refresh_search_list,
|
||||
)
|
||||
.await
|
||||
.context("error when updating search index")?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ mod loader;
|
|||
pub mod plugin_manifest;
|
||||
mod run_status;
|
||||
mod runtime;
|
||||
mod settings;
|
||||
pub mod settings;
|
||||
pub mod theme;
|
||||
|
||||
static BUNDLED_PLUGINS: [(&str, Dir); 1] = [(
|
||||
|
|
@ -124,9 +124,9 @@ impl ApplicationManager {
|
|||
let config_reader = ConfigReader::new(dirs.clone(), db_repository.clone());
|
||||
let icon_cache = IconCache::new(dirs.clone());
|
||||
let run_status_holder = RunStatusHolder::new();
|
||||
let search_index = SearchIndex::create_index(frontend_api.clone())?;
|
||||
let clipboard = Clipboard::new()?;
|
||||
let settings = Settings::new(dirs.clone(), db_repository.clone(), frontend_api.clone())?;
|
||||
let search_index = SearchIndex::create_index(frontend_api.clone(), settings.clone())?;
|
||||
|
||||
let (command_broadcaster, _) = tokio::sync::broadcast::channel::<PluginCommand>(100);
|
||||
|
||||
|
|
@ -617,6 +617,27 @@ impl ApplicationManager {
|
|||
self.settings.global_entrypoint_shortcuts().await
|
||||
}
|
||||
|
||||
pub async fn set_entrypoint_search_alias(
|
||||
&self,
|
||||
plugin_id: PluginId,
|
||||
entrypoint_id: EntrypointId,
|
||||
alias: Option<String>,
|
||||
) -> anyhow::Result<()> {
|
||||
self.settings
|
||||
.set_entrypoint_search_alias(plugin_id.clone(), entrypoint_id.clone(), alias.clone())
|
||||
.await?;
|
||||
|
||||
self.search_index
|
||||
.set_entrypoint_search_alias(plugin_id, entrypoint_id, alias)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_entrypoint_search_aliases(&self) -> anyhow::Result<HashMap<(PluginId, EntrypointId), String>> {
|
||||
self.settings.entrypoint_search_aliases().await
|
||||
}
|
||||
|
||||
pub async fn set_theme(&self, theme: SettingsTheme) -> anyhow::Result<()> {
|
||||
self.settings.set_theme_setting(theme).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use dark_light::Mode;
|
||||
|
|
@ -15,6 +17,7 @@ use gauntlet_common::rpc::frontend_api::FrontendApi;
|
|||
use gauntlet_common::rpc::frontend_api::FrontendApiProxy;
|
||||
|
||||
use crate::plugins::data_db_repository::DataDbRepository;
|
||||
use crate::plugins::data_db_repository::DbSettingsEntrypointSearchAliasData;
|
||||
use crate::plugins::data_db_repository::DbSettingsGlobalEntrypointShortcutData;
|
||||
use crate::plugins::data_db_repository::DbSettingsGlobalShortcutData;
|
||||
use crate::plugins::data_db_repository::DbSettingsShortcut;
|
||||
|
|
@ -23,11 +26,12 @@ use crate::plugins::data_db_repository::DbWindowPositionMode;
|
|||
use crate::plugins::theme::read_theme_file;
|
||||
use crate::plugins::theme::BundledThemes;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Settings {
|
||||
dirs: Dirs,
|
||||
repository: DataDbRepository,
|
||||
frontend_api: FrontendApiProxy,
|
||||
themes: BundledThemes,
|
||||
themes: Arc<BundledThemes>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
|
|
@ -36,7 +40,7 @@ impl Settings {
|
|||
dirs,
|
||||
repository,
|
||||
frontend_api,
|
||||
themes: BundledThemes::new()?,
|
||||
themes: Arc::new(BundledThemes::new()?),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -242,6 +246,73 @@ impl Settings {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn entrypoint_search_aliases(&self) -> anyhow::Result<HashMap<(PluginId, EntrypointId), String>> {
|
||||
let mut settings = self.repository.get_settings().await?;
|
||||
|
||||
let data: HashMap<_, _> = settings
|
||||
.entrypoint_search_aliases
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|data| {
|
||||
(
|
||||
(
|
||||
PluginId::from_string(data.plugin_id),
|
||||
EntrypointId::from_string(data.entrypoint_id),
|
||||
),
|
||||
data.alias,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub async fn set_entrypoint_search_alias(
|
||||
&self,
|
||||
plugin_id: PluginId,
|
||||
entrypoint_id: EntrypointId,
|
||||
alias: Option<String>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut settings = self.repository.get_settings().await?;
|
||||
|
||||
let mut alias_data: HashMap<_, _> = settings
|
||||
.entrypoint_search_aliases
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|data| {
|
||||
(
|
||||
(
|
||||
PluginId::from_string(data.plugin_id),
|
||||
EntrypointId::from_string(data.entrypoint_id),
|
||||
),
|
||||
data.alias,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
match alias {
|
||||
None => alias_data.remove(&(plugin_id, entrypoint_id)),
|
||||
Some(alias) => alias_data.insert((plugin_id, entrypoint_id), alias),
|
||||
};
|
||||
|
||||
let alias_data = alias_data
|
||||
.into_iter()
|
||||
.map(|((plugin_id, entrypoint_id), alias)| {
|
||||
DbSettingsEntrypointSearchAliasData {
|
||||
plugin_id: plugin_id.to_string(),
|
||||
entrypoint_id: entrypoint_id.to_string(),
|
||||
alias,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
settings.entrypoint_search_aliases = Some(alias_data);
|
||||
|
||||
self.repository.set_settings(settings).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn effective_theme(&self) -> anyhow::Result<UiTheme> {
|
||||
if let Some(theme) = read_theme_file(self.dirs.theme_file()) {
|
||||
return Ok(theme);
|
||||
|
|
|
|||
|
|
@ -173,6 +173,25 @@ impl BackendForSettingsApi for BackendServerImpl {
|
|||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn set_entrypoint_search_alias(
|
||||
&self,
|
||||
plugin_id: PluginId,
|
||||
entrypoint_id: EntrypointId,
|
||||
alias: Option<String>,
|
||||
) -> RequestResult<()> {
|
||||
self.application_manager
|
||||
.set_entrypoint_search_alias(plugin_id, entrypoint_id, alias)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn get_entrypoint_search_aliases(&self) -> RequestResult<HashMap<(PluginId, EntrypointId), String>> {
|
||||
self.application_manager
|
||||
.get_entrypoint_search_aliases()
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()> {
|
||||
self.application_manager.set_theme(theme).await.map_err(Into::into)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use std::sync::Arc;
|
|||
use std::sync::Mutex;
|
||||
use std::sync::MutexGuard;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use gauntlet_common::model::EntrypointId;
|
||||
use gauntlet_common::model::PhysicalShortcut;
|
||||
use gauntlet_common::model::PluginId;
|
||||
|
|
@ -30,9 +31,12 @@ use tantivy::IndexWriter;
|
|||
use tantivy::ReloadPolicy;
|
||||
use tantivy::Searcher;
|
||||
|
||||
use crate::plugins::settings::Settings;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SearchIndex {
|
||||
frontend_api: FrontendApiProxy,
|
||||
settings: Settings,
|
||||
index: Index,
|
||||
index_reader: IndexReader,
|
||||
index_writer_mutex: Arc<Mutex<()>>,
|
||||
|
|
@ -43,6 +47,7 @@ pub struct SearchIndex {
|
|||
entrypoint_id: Field,
|
||||
plugin_name: Field,
|
||||
plugin_id: Field,
|
||||
entrypoint_alias: Field,
|
||||
}
|
||||
|
||||
struct PluginData {
|
||||
|
|
@ -58,6 +63,7 @@ struct EntrypointData {
|
|||
frecency: f64,
|
||||
actions: Vec<EntrypointActionData>,
|
||||
accessories: Vec<SearchResultAccessory>,
|
||||
search_alias: Option<String>,
|
||||
}
|
||||
|
||||
struct EntrypointActionData {
|
||||
|
|
@ -119,7 +125,7 @@ pub enum SearchIndexItemActionActionType {
|
|||
}
|
||||
|
||||
impl SearchIndex {
|
||||
pub fn create_index(frontend_api: FrontendApiProxy) -> tantivy::Result<Self> {
|
||||
pub fn create_index(frontend_api: FrontendApiProxy, settings: Settings) -> tantivy::Result<Self> {
|
||||
let schema = {
|
||||
let mut schema_builder = Schema::builder();
|
||||
|
||||
|
|
@ -127,6 +133,7 @@ impl SearchIndex {
|
|||
schema_builder.add_text_field("entrypoint_id", STRING | STORED);
|
||||
schema_builder.add_text_field("plugin_name", TEXT | STORED);
|
||||
schema_builder.add_text_field("plugin_id", STRING | STORED);
|
||||
schema_builder.add_text_field("entrypoint_alias", TEXT | STORED);
|
||||
|
||||
schema_builder.build()
|
||||
};
|
||||
|
|
@ -139,6 +146,9 @@ impl SearchIndex {
|
|||
.expect("entrypoint_id field should exist");
|
||||
let plugin_name = schema.get_field("plugin_name").expect("plugin_name field should exist");
|
||||
let plugin_id = schema.get_field("plugin_id").expect("plugin_id field should exist");
|
||||
let entrypoint_alias = schema
|
||||
.get_field("entrypoint_alias")
|
||||
.expect("plugin_id field should exist");
|
||||
|
||||
let index = Index::create_in_ram(schema.clone());
|
||||
|
||||
|
|
@ -146,6 +156,7 @@ impl SearchIndex {
|
|||
|
||||
Ok(Self {
|
||||
frontend_api,
|
||||
settings,
|
||||
index,
|
||||
index_reader,
|
||||
index_writer_mutex: Arc::new(Mutex::new(())),
|
||||
|
|
@ -154,6 +165,7 @@ impl SearchIndex {
|
|||
entrypoint_id,
|
||||
plugin_name,
|
||||
plugin_id,
|
||||
entrypoint_alias,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -176,37 +188,81 @@ impl SearchIndex {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save_for_plugin(
|
||||
pub async fn set_entrypoint_search_alias(
|
||||
&self,
|
||||
plugin_id: PluginId,
|
||||
entrypoint_id: EntrypointId,
|
||||
alias: Option<String>,
|
||||
) -> anyhow::Result<()> {
|
||||
tracing::debug!(
|
||||
"Updating the entrypoint search alias in search index for plugin {:?} - {:?}",
|
||||
plugin_id,
|
||||
entrypoint_id
|
||||
);
|
||||
|
||||
// writer panics if another writer exists
|
||||
let _guard = self.index_writer_mutex.lock().expect("lock is poisoned");
|
||||
let mut plugins = self.entrypoint_data.lock().expect("lock is poisoned");
|
||||
|
||||
let Some(plugin_data) = plugins.get_mut(&plugin_id) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(entrypoint_data) = plugin_data.entrypoints.get_mut(&entrypoint_id) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
entrypoint_data.search_alias = alias;
|
||||
|
||||
let mut index_writer = self.index.writer::<TantivyDocument>(15_000_000)?;
|
||||
let query = Box::new(BooleanQuery::union(vec![
|
||||
Box::new(TermQuery::new(
|
||||
Term::from_field_text(self.plugin_id, &plugin_id.to_string()),
|
||||
IndexRecordOption::Basic,
|
||||
)),
|
||||
Box::new(TermQuery::new(
|
||||
Term::from_field_text(self.entrypoint_id, &entrypoint_id.to_string()),
|
||||
IndexRecordOption::Basic,
|
||||
)),
|
||||
]));
|
||||
|
||||
index_writer.delete_query(query)?;
|
||||
|
||||
let mut document = doc!(
|
||||
self.entrypoint_name => entrypoint_data.entrypoint_name.clone(),
|
||||
self.entrypoint_id => entrypoint_id.to_string(),
|
||||
self.plugin_name => plugin_data.plugin_name.clone(),
|
||||
self.plugin_id => plugin_id.to_string(),
|
||||
);
|
||||
|
||||
if let Some(alias) = &entrypoint_data.search_alias {
|
||||
document.add_field_value(self.entrypoint_alias, alias.clone())
|
||||
}
|
||||
|
||||
index_writer.add_document(document)?;
|
||||
|
||||
index_writer.commit()?;
|
||||
self.index_reader.reload()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn save_for_plugin(
|
||||
&self,
|
||||
plugin_id: PluginId,
|
||||
plugin_name: String,
|
||||
search_items: Vec<SearchIndexItem>,
|
||||
refresh_search_list: bool,
|
||||
) -> tantivy::Result<()> {
|
||||
) -> anyhow::Result<()> {
|
||||
tracing::debug!("Reloading search index for plugin {:?}", plugin_id);
|
||||
|
||||
let aliases = self.settings.entrypoint_search_aliases().await?;
|
||||
|
||||
// writer panics if another writer exists
|
||||
let _guard = self.index_writer_mutex.lock().expect("lock is poisoned");
|
||||
let mut entrypoint_data = self.entrypoint_data.lock().expect("lock is poisoned");
|
||||
|
||||
let mut index_writer = self.index.writer::<TantivyDocument>(15_000_000)?;
|
||||
|
||||
index_writer.delete_query(Box::new(TermQuery::new(
|
||||
Term::from_field_text(self.plugin_id, &plugin_id.to_string()),
|
||||
IndexRecordOption::Basic,
|
||||
)))?;
|
||||
|
||||
for search_item in &search_items {
|
||||
index_writer.add_document(doc!(
|
||||
self.entrypoint_name => search_item.entrypoint_name.clone(),
|
||||
self.entrypoint_id => search_item.entrypoint_id.to_string(),
|
||||
self.plugin_name => plugin_name.clone(),
|
||||
self.plugin_id => plugin_id.to_string(),
|
||||
))?;
|
||||
}
|
||||
|
||||
index_writer.commit()?;
|
||||
self.index_reader.reload()?;
|
||||
let entrypoint_ids: Vec<_> = search_items.iter().map(|item| item.entrypoint_id.clone()).collect();
|
||||
|
||||
let data = search_items
|
||||
.into_iter()
|
||||
|
|
@ -235,6 +291,7 @@ impl SearchIndex {
|
|||
frecency: item.entrypoint_frecency,
|
||||
actions,
|
||||
accessories: item.entrypoint_accessories,
|
||||
search_alias: aliases.get(&(plugin_id.clone(), item.entrypoint_id.clone())).cloned(),
|
||||
};
|
||||
|
||||
(item.entrypoint_id.clone(), data)
|
||||
|
|
@ -249,6 +306,34 @@ impl SearchIndex {
|
|||
},
|
||||
);
|
||||
|
||||
let mut index_writer = self.index.writer::<TantivyDocument>(15_000_000)?;
|
||||
|
||||
index_writer.delete_query(Box::new(TermQuery::new(
|
||||
Term::from_field_text(self.plugin_id, &plugin_id.to_string()),
|
||||
IndexRecordOption::Basic,
|
||||
)))?;
|
||||
|
||||
for entrypoint_id in entrypoint_ids {
|
||||
let plugin_data = entrypoint_data.get(&plugin_id).unwrap();
|
||||
let entrypoint_data = plugin_data.entrypoints.get(&entrypoint_id).unwrap();
|
||||
|
||||
let mut document = doc!(
|
||||
self.entrypoint_name => entrypoint_data.entrypoint_name.clone(),
|
||||
self.entrypoint_id => entrypoint_id.to_string(),
|
||||
self.plugin_name => plugin_data.plugin_name.clone(),
|
||||
self.plugin_id => plugin_id.to_string(),
|
||||
);
|
||||
|
||||
if let Some(alias) = &entrypoint_data.search_alias {
|
||||
document.add_field_value(self.entrypoint_alias, alias.clone())
|
||||
}
|
||||
|
||||
index_writer.add_document(document)?;
|
||||
}
|
||||
|
||||
index_writer.commit()?;
|
||||
self.index_reader.reload()?;
|
||||
|
||||
if refresh_search_list {
|
||||
let mut frontend_api = self.frontend_api.clone();
|
||||
tokio::spawn(async move {
|
||||
|
|
@ -319,7 +404,12 @@ impl SearchIndex {
|
|||
|
||||
let searcher = self.index_reader.searcher();
|
||||
|
||||
let query_parser = QueryParser::new(self.index.tokenizers().clone(), self.entrypoint_name, self.plugin_name);
|
||||
let query_parser = QueryParser::new(
|
||||
self.index.tokenizers().clone(),
|
||||
self.entrypoint_name,
|
||||
self.plugin_name,
|
||||
self.entrypoint_alias,
|
||||
);
|
||||
|
||||
let query = query_parser.create_query(query);
|
||||
|
||||
|
|
@ -367,23 +457,12 @@ impl SearchIndex {
|
|||
collector: TopDocs,
|
||||
searcher: &Searcher,
|
||||
) -> anyhow::Result<Vec<(SearchResult, f64)>> {
|
||||
let get_str_field = |retrieved_doc: &TantivyDocument, field: Field| -> String {
|
||||
let get_str_field = |retrieved_doc: &TantivyDocument, field: Field| -> Option<String> {
|
||||
retrieved_doc
|
||||
.get_first(field)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"there should be a field with name {:?}",
|
||||
searcher.schema().get_field_name(field)
|
||||
)
|
||||
})
|
||||
.as_str()
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"field with name {:?} should contain string",
|
||||
searcher.schema().get_field_name(field)
|
||||
)
|
||||
})
|
||||
.to_owned()
|
||||
.map(|value| value.as_str())
|
||||
.flatten()
|
||||
.map(|value| value.to_owned())
|
||||
};
|
||||
|
||||
let result = searcher
|
||||
|
|
@ -394,10 +473,18 @@ impl SearchIndex {
|
|||
.doc::<TantivyDocument>(doc_address)
|
||||
.expect("index should contain just searched results");
|
||||
|
||||
let entrypoint_id = EntrypointId::from_string(get_str_field(&retrieved_doc, self.entrypoint_id));
|
||||
let plugin_id = PluginId::from_string(get_str_field(&retrieved_doc, self.plugin_id));
|
||||
let entrypoint_name = get_str_field(&retrieved_doc, self.entrypoint_name);
|
||||
let plugin_name = get_str_field(&retrieved_doc, self.plugin_name);
|
||||
let entrypoint_id = get_str_field(&retrieved_doc, self.entrypoint_id)
|
||||
.ok_or(anyhow!("document must contain entrypoint id"))?;
|
||||
let plugin_id =
|
||||
get_str_field(&retrieved_doc, self.plugin_id).ok_or(anyhow!("document must contain plugin id"))?;
|
||||
let entrypoint_name = get_str_field(&retrieved_doc, self.entrypoint_name)
|
||||
.ok_or(anyhow!("document must contain entrypoint name"))?;
|
||||
let plugin_name = get_str_field(&retrieved_doc, self.plugin_name)
|
||||
.ok_or(anyhow!("document must contain plugin name"))?;
|
||||
let entrypoint_alias = get_str_field(&retrieved_doc, self.entrypoint_alias);
|
||||
|
||||
let entrypoint_id = EntrypointId::from_string(entrypoint_id);
|
||||
let plugin_id = PluginId::from_string(plugin_id);
|
||||
|
||||
let entrypoint_data = entrypoint_data
|
||||
.get(&plugin_id)
|
||||
|
|
@ -436,11 +523,12 @@ impl SearchIndex {
|
|||
plugin_id,
|
||||
entrypoint_actions,
|
||||
entrypoint_accessories,
|
||||
entrypoint_alias,
|
||||
};
|
||||
|
||||
(result_item, entrypoint_data.frecency)
|
||||
Ok((result_item, entrypoint_data.frecency))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<anyhow::Result<Vec<_>>>()?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
|
@ -450,14 +538,21 @@ struct QueryParser {
|
|||
tokenizer_manager: TokenizerManager,
|
||||
entrypoint_name: Field,
|
||||
plugin_name: Field,
|
||||
entrypoint_alias: Field,
|
||||
}
|
||||
|
||||
impl QueryParser {
|
||||
fn new(tokenizer_manager: TokenizerManager, entrypoint_name: Field, plugin_name: Field) -> Self {
|
||||
fn new(
|
||||
tokenizer_manager: TokenizerManager,
|
||||
entrypoint_name: Field,
|
||||
plugin_name: Field,
|
||||
entrypoint_alias: Field,
|
||||
) -> Self {
|
||||
Self {
|
||||
tokenizer_manager,
|
||||
entrypoint_name,
|
||||
plugin_name,
|
||||
entrypoint_alias,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -486,10 +581,12 @@ impl QueryParser {
|
|||
|
||||
let entrypoint_name_terms = terms_fn(self.entrypoint_name);
|
||||
let plugin_name_terms = terms_fn(self.plugin_name);
|
||||
let entrypoint_alias_terms = terms_fn(self.entrypoint_alias);
|
||||
|
||||
Box::new(BooleanQuery::union(vec![
|
||||
Box::new(entrypoint_name_terms),
|
||||
Box::new(plugin_name_terms),
|
||||
Box::new(entrypoint_alias_terms),
|
||||
]))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue