Add hidden `prompt_to_focus` field to `OpenPromptLibrary` action (#29062)

Michael Sloan created

Release Notes:

- N/A

Change summary

Cargo.lock                                  |  1 +
crates/agent/src/active_thread.rs           | 14 ++++++++++++--
crates/agent/src/assistant_panel.rs         |  9 +++++----
crates/agent/src/thread_store.rs            |  8 ++++++--
crates/assistant/src/assistant_panel.rs     | 12 ++++++++----
crates/prompt_library/Cargo.toml            |  2 +-
crates/prompt_library/src/prompt_library.rs | 16 +++++++++++++---
crates/prompt_store/src/prompts.rs          |  3 +++
crates/zed_actions/Cargo.toml               |  1 +
crates/zed_actions/src/lib.rs               | 12 ++++++++++--
10 files changed, 60 insertions(+), 18 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -18322,6 +18322,7 @@ dependencies = [
  "gpui",
  "schemars",
  "serde",
+ "uuid",
  "workspace-hack",
 ]
 

crates/agent/src/active_thread.rs 🔗

@@ -2965,6 +2965,11 @@ impl ActiveThread {
             ))
         };
 
+        let first_default_user_rules_id = project_context
+            .default_user_rules
+            .first()
+            .map(|user_rules| user_rules.uuid);
+
         let rules_files = project_context
             .worktrees
             .iter()
@@ -3015,8 +3020,13 @@ impl ActiveThread {
                                     .icon_color(Color::Ignored)
                                     // TODO: Figure out a way to pass focus handle here so we can display the `OpenPromptLibrary`  keybinding
                                     .tooltip(Tooltip::text("View User Rules"))
-                                    .on_click(|_event, window, cx| {
-                                        window.dispatch_action(Box::new(OpenPromptLibrary), cx)
+                                    .on_click(move |_event, window, cx| {
+                                        window.dispatch_action(
+                                            Box::new(OpenPromptLibrary {
+                                                prompt_to_focus: first_default_user_rules_id,
+                                            }),
+                                            cx,
+                                        )
                                     }),
                             ),
                     )

crates/agent/src/assistant_panel.rs 🔗

@@ -25,7 +25,7 @@ use language_model::{LanguageModelProviderTosView, LanguageModelRegistry};
 use language_model_selector::ToggleModelSelector;
 use project::Project;
 use prompt_library::{PromptLibrary, open_prompt_library};
-use prompt_store::PromptBuilder;
+use prompt_store::{PromptBuilder, PromptId};
 use proto::Plan;
 use settings::{Settings, update_settings_file};
 use time::UtcOffset;
@@ -83,7 +83,7 @@ pub fn init(cx: &mut App) {
                     if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
                         workspace.focus_panel::<AssistantPanel>(window, cx);
                         panel.update(cx, |panel, cx| {
-                            panel.deploy_prompt_library(&OpenPromptLibrary, window, cx)
+                            panel.deploy_prompt_library(&OpenPromptLibrary::default(), window, cx)
                         });
                     }
                 })
@@ -488,7 +488,7 @@ impl AssistantPanel {
 
     fn deploy_prompt_library(
         &mut self,
-        _: &OpenPromptLibrary,
+        action: &OpenPromptLibrary,
         _window: &mut Window,
         cx: &mut Context<Self>,
     ) {
@@ -502,6 +502,7 @@ impl AssistantPanel {
                     None,
                 ))
             }),
+            action.prompt_to_focus.map(|uuid| PromptId::User { uuid }),
             cx,
         )
         .detach_and_log_err(cx);
@@ -1119,7 +1120,7 @@ impl AssistantPanel {
                                                     "New Text Thread",
                                                     NewTextThread.boxed_clone(),
                                                 )
-                                                .action("Prompt Library", Box::new(OpenPromptLibrary))
+                                                .action("Prompt Library", Box::new(OpenPromptLibrary::default()))
                                                 .action("Settings", Box::new(OpenConfiguration))
                                                 .separator()
                                                 .action(

crates/agent/src/thread_store.rs 🔗

@@ -24,8 +24,8 @@ use heed::types::SerdeBincode;
 use language_model::{LanguageModelToolUseId, Role, TokenUsage};
 use project::{Project, Worktree};
 use prompt_store::{
-    DefaultUserRulesContext, ProjectContext, PromptBuilder, PromptStore, PromptsUpdatedEvent,
-    RulesFileContext, WorktreeContext,
+    DefaultUserRulesContext, ProjectContext, PromptBuilder, PromptId, PromptStore,
+    PromptsUpdatedEvent, RulesFileContext, WorktreeContext,
 };
 use serde::{Deserialize, Serialize};
 use settings::{Settings as _, SettingsStore};
@@ -246,6 +246,10 @@ impl ThreadStore {
                 .into_iter()
                 .flat_map(|(contents, prompt_metadata)| match contents {
                     Ok(contents) => Some(DefaultUserRulesContext {
+                        uuid: match prompt_metadata.id {
+                            PromptId::User { uuid } => uuid,
+                            PromptId::EditWorkflow => return None,
+                        },
                         title: prompt_metadata.title.map(|title| title.to_string()),
                         contents,
                     }),

crates/assistant/src/assistant_panel.rs 🔗

@@ -27,7 +27,7 @@ use language_model::{
 };
 use project::Project;
 use prompt_library::{PromptLibrary, open_prompt_library};
-use prompt_store::PromptBuilder;
+use prompt_store::{PromptBuilder, PromptId};
 
 use search::{BufferSearchBar, buffer_search::DivRegistrar};
 use settings::{Settings, update_settings_file};
@@ -62,7 +62,7 @@ pub fn init(cx: &mut App) {
                     if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
                         workspace.focus_panel::<AssistantPanel>(window, cx);
                         panel.update(cx, |panel, cx| {
-                            panel.deploy_prompt_library(&OpenPromptLibrary, window, cx)
+                            panel.deploy_prompt_library(&OpenPromptLibrary::default(), window, cx)
                         });
                     }
                 });
@@ -272,7 +272,10 @@ impl AssistantPanel {
                                     menu.context(focus_handle.clone())
                                         .action("New Chat", Box::new(NewChat))
                                         .action("History", Box::new(DeployHistory))
-                                        .action("Prompt Library", Box::new(OpenPromptLibrary))
+                                        .action(
+                                            "Prompt Library",
+                                            Box::new(OpenPromptLibrary::default()),
+                                        )
                                         .action("Configure", Box::new(ShowConfiguration))
                                         .action(zoom_label, Box::new(ToggleZoom))
                                 }))
@@ -1043,7 +1046,7 @@ impl AssistantPanel {
 
     fn deploy_prompt_library(
         &mut self,
-        _: &OpenPromptLibrary,
+        action: &OpenPromptLibrary,
         _window: &mut Window,
         cx: &mut Context<Self>,
     ) {
@@ -1057,6 +1060,7 @@ impl AssistantPanel {
                     None,
                 ))
             }),
+            action.prompt_to_focus.map(|uuid| PromptId::User { uuid }),
             cx,
         )
         .detach_and_log_err(cx);

crates/prompt_library/Cargo.toml 🔗

@@ -29,6 +29,6 @@ settings.workspace = true
 theme.workspace = true
 ui.workspace = true
 util.workspace = true
+workspace-hack.workspace = true
 workspace.workspace = true
 zed_actions.workspace = true
-workspace-hack.workspace = true

crates/prompt_library/src/prompt_library.rs 🔗

@@ -75,6 +75,7 @@ pub fn open_prompt_library(
     language_registry: Arc<LanguageRegistry>,
     inline_assist_delegate: Box<dyn InlineAssistDelegate>,
     make_completion_provider: Arc<dyn Fn() -> Box<dyn CompletionProvider>>,
+    prompt_to_focus: Option<PromptId>,
     cx: &mut App,
 ) -> Task<Result<WindowHandle<PromptLibrary>>> {
     let store = PromptStore::global(cx);
@@ -88,7 +89,12 @@ pub fn open_prompt_library(
                     .find_map(|window| window.downcast::<PromptLibrary>());
                 if let Some(existing_window) = existing_window {
                     existing_window
-                        .update(cx, |_, window, _| window.activate_window())
+                        .update(cx, |prompt_library, window, cx| {
+                            if let Some(prompt_to_focus) = prompt_to_focus {
+                                prompt_library.load_prompt(prompt_to_focus, true, window, cx);
+                            }
+                            window.activate_window()
+                        })
                         .ok();
 
                     Some(existing_window)
@@ -120,14 +126,18 @@ pub fn open_prompt_library(
                 },
                 |window, cx| {
                     cx.new(|cx| {
-                        PromptLibrary::new(
+                        let mut prompt_library = PromptLibrary::new(
                             store,
                             language_registry,
                             inline_assist_delegate,
                             make_completion_provider,
                             window,
                             cx,
-                        )
+                        );
+                        if let Some(prompt_to_focus) = prompt_to_focus {
+                            prompt_library.load_prompt(prompt_to_focus, true, window, cx);
+                        }
+                        prompt_library
                     })
                 },
             )

crates/prompt_store/src/prompts.rs 🔗

@@ -15,6 +15,7 @@ use std::{
 };
 use text::LineEnding;
 use util::{ResultExt, get_system_shell};
+use uuid::Uuid;
 
 #[derive(Debug, Clone, Serialize)]
 pub struct ProjectContext {
@@ -51,6 +52,7 @@ impl ProjectContext {
 
 #[derive(Debug, Clone, Serialize)]
 pub struct DefaultUserRulesContext {
+    pub uuid: Uuid,
     pub title: Option<String>,
     pub contents: String,
 }
@@ -409,6 +411,7 @@ mod test {
             }),
         }];
         let default_user_rules = vec![DefaultUserRulesContext {
+            uuid: Uuid::nil(),
             title: Some("Rules title".into()),
             contents: "Rules contents".into(),
         }];

crates/zed_actions/Cargo.toml 🔗

@@ -13,3 +13,4 @@ gpui.workspace = true
 schemars.workspace = true
 serde.workspace = true
 workspace-hack.workspace = true
+uuid.workspace = true

crates/zed_actions/src/lib.rs 🔗

@@ -190,13 +190,21 @@ pub mod agent {
 }
 
 pub mod assistant {
-    use gpui::{action_with_deprecated_aliases, actions, impl_actions};
+    use gpui::{actions, impl_action_with_deprecated_aliases, impl_actions};
     use schemars::JsonSchema;
     use serde::Deserialize;
+    use uuid::Uuid;
 
     actions!(assistant, [ToggleFocus, ShowConfiguration]);
 
-    action_with_deprecated_aliases!(
+    #[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema)]
+    #[serde(deny_unknown_fields)]
+    pub struct OpenPromptLibrary {
+        #[serde(skip)]
+        pub prompt_to_focus: Option<Uuid>,
+    }
+
+    impl_action_with_deprecated_aliases!(
         assistant,
         OpenPromptLibrary,
         ["assistant::DeployPromptLibrary"]