@@ -47,7 +47,11 @@ pub struct App {
4747}
4848
4949impl App {
50- pub fn new ( context : Context , receiver : Receiver < ContextNotification > ) -> Self {
50+ pub fn new (
51+ context : Context ,
52+ receiver : Receiver < ContextNotification > ,
53+ project_descriptions : Vec < ProjectDescription > ,
54+ ) -> Self {
5155 Self {
5256 context,
5357 receiver,
@@ -56,20 +60,28 @@ impl App {
5660 events : HashMap :: new ( ) ,
5761 selected_sidebar_tab : SidebarTab :: Projects ,
5862 selected_event : None ,
59- project_descriptions : Vec :: new ( ) ,
63+ project_descriptions,
6064 }
6165 }
6266
6367 fn handle_notifications ( & mut self ) -> bool {
6468 let mut has_new_events = false ;
6569 while let Ok ( notification) = self . receiver . try_recv ( ) {
66- if matches ! ( notification, ContextNotification :: Lsp ( _) ) {
67- continue ;
68- }
70+ // Order is important here. New projects came in
6971 if let ContextNotification :: ProjectDescriptions ( project_descriptions) = notification {
7072 self . project_descriptions = project_descriptions;
73+ has_new_events = true ;
74+ continue ;
75+ }
76+
77+ // If its not a new project notification, request projects
78+ self . context . request_project_descriptions ( ) ;
79+
80+ // If its a lsp, ignore because there's a lot of them
81+ if matches ! ( notification, ContextNotification :: Lsp ( _) ) {
7182 continue ;
7283 }
84+ // Otherwise, we have a new event
7385 has_new_events = true ;
7486 tracing:: debug!( "Received notification: {:?}" , notification) ;
7587 let project_path = notification. notification_path ( ) ;
@@ -83,12 +95,12 @@ impl App {
8395 . entry ( project_name)
8496 . or_default ( )
8597 . push ( timestamped_event) ;
86- self . context . request_project_descriptions ( ) ;
8798 }
8899 has_new_events
89100 }
90101
91102 fn draw_left_sidebar ( & mut self , ui : & mut Ui , project_descriptions : & [ ProjectDescription ] ) {
103+ ui. add_space ( 10.0 ) ;
92104 ui. columns ( 2 , |columns| {
93105 columns[ 0 ] . selectable_value (
94106 & mut self . selected_sidebar_tab ,
@@ -193,6 +205,7 @@ impl App {
193205
194206 fn draw_main_area ( & mut self , ui : & mut Ui , project_descriptions : & [ ProjectDescription ] ) {
195207 if let Some ( selected_root) = & self . selected_project {
208+ let config_path = PathBuf :: from ( selected_root) . join ( ".cursor/mcp.json" ) ;
196209 if let Some ( project) = project_descriptions
197210 . iter ( )
198211 . find ( |p| p. root == * selected_root)
@@ -220,6 +233,17 @@ impl App {
220233 tracing:: error!( "Failed to open project: {}" , e) ;
221234 }
222235 }
236+ if !config_path. exists ( )
237+ && ui
238+ . button ( "Install mcp.json" )
239+ . on_hover_text ( "Create a .cursor/mcp.json file in the project root" )
240+ . clicked ( )
241+ {
242+ let config = self . context . mcp_configuration ( ) ;
243+ if let Err ( e) = create_mcp_configuration_file ( & project. root , config) {
244+ tracing:: error!( "Failed to create mcp.json: {}" , e) ;
245+ }
246+ }
223247 ui. add_space ( 10.0 ) ;
224248 if project. is_indexing_lsp {
225249 ui. add ( egui:: Spinner :: new ( ) ) ;
@@ -237,7 +261,8 @@ impl App {
237261 ui. allocate_ui ( remaining_space, |ui| {
238262 // Show the dark frame within the allocated space
239263 egui:: Frame :: dark_canvas ( ui. style ( ) )
240- . inner_margin ( egui:: Margin :: same ( 4 ) ) // Optional: Add padding inside the frame
264+ . fill ( Color32 :: from_black_alpha ( 128 ) )
265+ . inner_margin ( egui:: Margin :: same ( 4 ) )
241266 . show ( ui, |ui| {
242267 // Make the ScrollArea fill the frame
243268 ScrollArea :: vertical ( )
@@ -297,7 +322,8 @@ impl App {
297322 }
298323 } else {
299324 ui. centered_and_justified ( |ui| {
300- ui. label ( "Select a project from the left sidebar" ) ;
325+ ui. label ( "Select or add a project" ) ;
326+ ui. label ( "Added projects first need to be indexed for LSP and Docs before they can be used." ) ;
301327 } ) ;
302328 if self . selected_event . is_some ( ) {
303329 self . selected_event = None ;
@@ -445,6 +471,7 @@ impl<'a> ListCell<'a> {
445471 // Create a Label widget and set its sense to Hover only,
446472 // so it doesn't steal clicks from the parent response.
447473 let label = egui:: Label :: new ( RichText :: new ( self . text ) . color ( text_color) )
474+ . selectable ( false )
448475 . sense ( egui:: Sense :: hover ( ) ) ;
449476 ui. add ( label) ;
450477
@@ -474,3 +501,10 @@ fn find_root_project(mut path: &Path, projects: &[ProjectDescription]) -> Option
474501
475502 None
476503}
504+
505+ fn create_mcp_configuration_file ( path : & Path , contents : String ) -> anyhow:: Result < ( ) > {
506+ let config_path = PathBuf :: from ( path) . join ( ".cursor/mcp.json" ) ;
507+ std:: fs:: create_dir_all ( & config_path) ?;
508+ std:: fs:: write ( config_path, contents) ?;
509+ Ok ( ( ) )
510+ }
0 commit comments