Polish prompt library UX (#12647)
This could still use some improvement UI-wise but the user experience should be a lot better. - [x] Show in "Window" application menu - [x] Load prompt as it's selected in the picker - [x] Refocus picker on `esc` - [x] When creating a new prompt, if a new prompt already exists and is unedited, activate it instead - [x] Add `/default` command - [x] Evaluate /commands on prompt insertion - [x] Autocomplete /commands (but don't evaluate) during prompt editing - [x] Show token count using the settings model, right-aligned in the editor - [x] Picker - [x] Sorted alpha - [x] 2 sublists - Default - Empty state: Star a prompt to add it to your default prompt - Otherwise show prompts with star on hover - All - Move prompts with star on hover Release Notes: - N/A
This commit is contained in:
parent
e4bb666eab
commit
c5b22eee2d
22 changed files with 716 additions and 358 deletions
|
@ -96,6 +96,7 @@ impl SlashCommand for ActiveSlashCommand {
|
|||
.into_any_element()
|
||||
}),
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
})
|
||||
});
|
||||
|
|
81
crates/assistant/src/slash_command/default_command.rs
Normal file
81
crates/assistant/src/slash_command/default_command.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
use super::{prompt_command::PromptPlaceholder, SlashCommand, SlashCommandOutput};
|
||||
use crate::prompt_library::PromptStore;
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::SlashCommandOutputSection;
|
||||
use gpui::{AppContext, Task, WeakView};
|
||||
use language::LspAdapterDelegate;
|
||||
use std::{
|
||||
fmt::Write,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use ui::prelude::*;
|
||||
use workspace::Workspace;
|
||||
|
||||
pub(crate) struct DefaultSlashCommand;
|
||||
|
||||
impl SlashCommand for DefaultSlashCommand {
|
||||
fn name(&self) -> String {
|
||||
"default".into()
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
"insert default prompt".into()
|
||||
}
|
||||
|
||||
fn menu_text(&self) -> String {
|
||||
"Insert Default Prompt".into()
|
||||
}
|
||||
|
||||
fn requires_argument(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn complete_argument(
|
||||
&self,
|
||||
_query: String,
|
||||
_cancellation_flag: Arc<AtomicBool>,
|
||||
_workspace: WeakView<Workspace>,
|
||||
_cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<String>>> {
|
||||
Task::ready(Err(anyhow!("this command does not require argument")))
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
_argument: Option<&str>,
|
||||
_workspace: WeakView<Workspace>,
|
||||
_delegate: Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<Result<SlashCommandOutput>> {
|
||||
let store = PromptStore::global(cx);
|
||||
cx.background_executor().spawn(async move {
|
||||
let store = store.await?;
|
||||
let prompts = store.default_prompt_metadata();
|
||||
|
||||
let mut text = String::new();
|
||||
writeln!(text, "Default Prompt:").unwrap();
|
||||
for prompt in prompts {
|
||||
if let Some(title) = prompt.title {
|
||||
writeln!(text, "/prompt {}", title).unwrap();
|
||||
}
|
||||
}
|
||||
text.pop();
|
||||
|
||||
Ok(SlashCommandOutput {
|
||||
sections: vec![SlashCommandOutputSection {
|
||||
range: 0..text.len(),
|
||||
render_placeholder: Arc::new(move |id, unfold, _cx| {
|
||||
PromptPlaceholder {
|
||||
title: "Default".into(),
|
||||
id,
|
||||
unfold,
|
||||
}
|
||||
.into_any_element()
|
||||
}),
|
||||
}],
|
||||
text,
|
||||
run_commands_in_text: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
|
@ -107,6 +107,7 @@ impl SlashCommand for FetchSlashCommand {
|
|||
.into_any_element()
|
||||
}),
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -187,6 +187,7 @@ impl SlashCommand for FileSlashCommand {
|
|||
.into_any_element()
|
||||
}),
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ impl SlashCommand for ProjectSlashCommand {
|
|||
.into_any_element()
|
||||
}),
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
})
|
||||
});
|
||||
|
|
|
@ -8,15 +8,7 @@ use std::sync::{atomic::AtomicBool, Arc};
|
|||
use ui::{prelude::*, ButtonLike, ElevationIndex};
|
||||
use workspace::Workspace;
|
||||
|
||||
pub(crate) struct PromptSlashCommand {
|
||||
store: Arc<PromptStore>,
|
||||
}
|
||||
|
||||
impl PromptSlashCommand {
|
||||
pub fn new(store: Arc<PromptStore>) -> Self {
|
||||
Self { store }
|
||||
}
|
||||
}
|
||||
pub(crate) struct PromptSlashCommand;
|
||||
|
||||
impl SlashCommand for PromptSlashCommand {
|
||||
fn name(&self) -> String {
|
||||
|
@ -42,9 +34,9 @@ impl SlashCommand for PromptSlashCommand {
|
|||
_workspace: WeakView<Workspace>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<String>>> {
|
||||
let store = self.store.clone();
|
||||
let store = PromptStore::global(cx);
|
||||
cx.background_executor().spawn(async move {
|
||||
let prompts = store.search(query).await;
|
||||
let prompts = store.await?.search(query).await;
|
||||
Ok(prompts
|
||||
.into_iter()
|
||||
.filter_map(|prompt| Some(prompt.title?.to_string()))
|
||||
|
@ -63,11 +55,12 @@ impl SlashCommand for PromptSlashCommand {
|
|||
return Task::ready(Err(anyhow!("missing prompt name")));
|
||||
};
|
||||
|
||||
let store = self.store.clone();
|
||||
let store = PromptStore::global(cx);
|
||||
let title = SharedString::from(title.to_string());
|
||||
let prompt = cx.background_executor().spawn({
|
||||
let title = title.clone();
|
||||
async move {
|
||||
let store = store.await?;
|
||||
let prompt_id = store
|
||||
.id_for_title(&title)
|
||||
.with_context(|| format!("no prompt found with title {:?}", title))?;
|
||||
|
@ -91,6 +84,7 @@ impl SlashCommand for PromptSlashCommand {
|
|||
.into_any_element()
|
||||
}),
|
||||
}],
|
||||
run_commands_in_text: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -192,6 +192,7 @@ impl SlashCommand for RustdocSlashCommand {
|
|||
.into_any_element()
|
||||
}),
|
||||
}],
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -181,7 +181,11 @@ impl SlashCommand for SearchSlashCommand {
|
|||
}),
|
||||
});
|
||||
|
||||
SlashCommandOutput { text, sections }
|
||||
SlashCommandOutput {
|
||||
text,
|
||||
sections,
|
||||
run_commands_in_text: false,
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
|
|
|
@ -109,7 +109,11 @@ impl SlashCommand for TabsSlashCommand {
|
|||
});
|
||||
}
|
||||
|
||||
Ok(SlashCommandOutput { text, sections })
|
||||
Ok(SlashCommandOutput {
|
||||
text,
|
||||
sections,
|
||||
run_commands_in_text: false,
|
||||
})
|
||||
}),
|
||||
Err(error) => Task::ready(Err(error)),
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue