Skip to content

Commit 0fec4a4

Browse files
committed
Create macOS main menu, bind actions
1 parent b700f88 commit 0fec4a4

File tree

1 file changed

+103
-62
lines changed
  • crates/code_assistant/src/ui/gpui

1 file changed

+103
-62
lines changed

crates/code_assistant/src/ui/gpui/mod.rs

Lines changed: 103 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ use crate::ui::gpui::{
2525
use crate::ui::{async_trait, DisplayFragment, StreamingState, UIError, UiEvent, UserInterface};
2626
use assets::Assets;
2727
use async_channel;
28-
use gpui::{actions, px, AppContext, AsyncApp, Entity, Global, Point};
28+
use gpui::{
29+
actions, px, App, AppContext, AsyncApp, Entity, Global, KeyBinding, Menu, MenuItem, Point,
30+
SharedString,
31+
};
2932
use gpui_component::input::InputState;
3033
use gpui_component::Root;
3134
pub use memory::MemoryView;
@@ -38,7 +41,7 @@ use tracing::{debug, error, trace, warn};
3841

3942
use elements::MessageContainer;
4043

41-
actions!(code_assistant, [CloseWindow]);
44+
actions!(code_assistant, [Quit, CloseWindow]);
4245

4346
// Global UI event sender for chat components
4447
#[derive(Clone)]
@@ -104,6 +107,38 @@ pub struct Gpui {
104107
chat_sidebar: Arc<Mutex<Option<Entity<chat_sidebar::ChatSidebar>>>>,
105108
}
106109

110+
fn init(cx: &mut App) {
111+
cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
112+
113+
cx.on_action(|_: &Quit, cx: &mut App| {
114+
cx.quit();
115+
});
116+
117+
use gpui_component::input::{Copy, Cut, Paste, Redo, Undo};
118+
cx.set_menus(vec![
119+
Menu {
120+
name: "GPUI App".into(),
121+
items: vec![MenuItem::action("Quit", Quit)],
122+
},
123+
Menu {
124+
name: "Edit".into(),
125+
items: vec![
126+
MenuItem::os_action("Undo", Undo, gpui::OsAction::Undo),
127+
MenuItem::os_action("Redo", Redo, gpui::OsAction::Redo),
128+
MenuItem::separator(),
129+
MenuItem::os_action("Cut", Cut, gpui::OsAction::Cut),
130+
MenuItem::os_action("Copy", Copy, gpui::OsAction::Copy),
131+
MenuItem::os_action("Paste", Paste, gpui::OsAction::Paste),
132+
],
133+
},
134+
Menu {
135+
name: "Window".into(),
136+
items: vec![],
137+
},
138+
]);
139+
cx.activate(true);
140+
}
141+
107142
// Implement Global trait for Gpui
108143
impl Global for Gpui {}
109144

@@ -208,6 +243,8 @@ impl Gpui {
208243
// Apply our custom theme colors
209244
theme::init_themes(cx);
210245

246+
init(cx);
247+
211248
// Spawn task to receive UiEvents
212249
let rx = gpui_clone.event_receiver.lock().unwrap().clone();
213250
let async_gpui_clone = gpui_clone.clone();
@@ -279,68 +316,70 @@ impl Gpui {
279316
let bounds =
280317
gpui::Bounds::centered(None, gpui::size(gpui::px(1400.0), gpui::px(700.0)), cx);
281318
// Open window with titlebar
282-
let window_result = cx.open_window(
283-
gpui::WindowOptions {
284-
window_bounds: Some(gpui::WindowBounds::Windowed(bounds)),
285-
titlebar: Some(gpui::TitlebarOptions {
286-
title: Some(gpui::SharedString::from("Code Assistant")),
287-
appears_transparent: true,
288-
traffic_light_position: Some(Point {
289-
x: px(16.),
290-
y: px(16.),
319+
let window = cx
320+
.open_window(
321+
gpui::WindowOptions {
322+
window_bounds: Some(gpui::WindowBounds::Windowed(bounds)),
323+
titlebar: Some(gpui::TitlebarOptions {
324+
title: Some(gpui::SharedString::from("Code Assistant")),
325+
appears_transparent: true,
326+
traffic_light_position: Some(Point {
327+
x: px(16.),
328+
y: px(16.),
329+
}),
291330
}),
292-
}),
293-
..Default::default()
294-
},
295-
|window, cx| {
296-
// Create TextInput with multi-line support
297-
let text_input = cx.new(|cx| {
298-
InputState::new(window, cx)
299-
.multi_line()
300-
.auto_grow(1, 8)
301-
.placeholder("Type your message...")
302-
});
303-
304-
// Create MessagesView
305-
let messages_view = cx.new(|cx| MessagesView::new(message_queue.clone(), cx));
306-
307-
// Create ChatSidebar and store it in Gpui
308-
let chat_sidebar = cx.new(|cx| chat_sidebar::ChatSidebar::new(cx));
309-
*gpui_clone.chat_sidebar.lock().unwrap() = Some(chat_sidebar.clone());
310-
311-
// Create RootView
312-
let root_view = cx.new(|cx| {
313-
RootView::new(
314-
text_input,
315-
memory_view.clone(),
316-
messages_view,
317-
chat_sidebar.clone(),
318-
cx,
319-
input_value.clone(),
320-
input_requested.clone(),
321-
gpui_clone.streaming_state.clone(),
322-
)
323-
});
324-
325-
// Wrap in Root component
326-
cx.new(|cx| Root::new(root_view.into(), window, cx))
327-
},
328-
);
331+
..Default::default()
332+
},
333+
|window, cx| {
334+
// Create TextInput with multi-line support
335+
let text_input = cx.new(|cx| {
336+
InputState::new(window, cx)
337+
.multi_line()
338+
.auto_grow(1, 8)
339+
.placeholder("Type your message...")
340+
});
341+
342+
// Create MessagesView
343+
let messages_view =
344+
cx.new(|cx| MessagesView::new(message_queue.clone(), cx));
345+
346+
// Create ChatSidebar and store it in Gpui
347+
let chat_sidebar = cx.new(|cx| chat_sidebar::ChatSidebar::new(cx));
348+
*gpui_clone.chat_sidebar.lock().unwrap() = Some(chat_sidebar.clone());
349+
350+
// Create RootView
351+
let root_view = cx.new(|cx| {
352+
RootView::new(
353+
text_input,
354+
memory_view.clone(),
355+
messages_view,
356+
chat_sidebar.clone(),
357+
cx,
358+
input_value.clone(),
359+
input_requested.clone(),
360+
gpui_clone.streaming_state.clone(),
361+
)
362+
});
363+
364+
// Wrap in Root component
365+
cx.new(|cx| Root::new(root_view.into(), window, cx))
366+
},
367+
)
368+
.expect("failed to open window");
329369

330370
// Focus the TextInput if window was created successfully
331-
if let Ok(window_handle) = window_result {
332-
window_handle
333-
.update(cx, |_root, window, cx| {
334-
// Get the MessageView from the Root
335-
if let Some(_view) =
336-
window.root::<gpui_component::Root>().and_then(|root| root)
337-
{
338-
// Activate window
339-
cx.activate(true);
340-
}
341-
})
342-
.ok();
343-
}
371+
window
372+
.update(cx, |_root, window, cx| {
373+
window.activate_window();
374+
window.set_window_title(&SharedString::from("Code Assistant"));
375+
// Get the MessageView from the Root
376+
if let Some(_view) = window.root::<gpui_component::Root>().and_then(|root| root)
377+
{
378+
// Activate window
379+
cx.activate(true);
380+
}
381+
})
382+
.expect("failed to update window");
344383
});
345384
}
346385

@@ -771,9 +810,9 @@ impl Gpui {
771810

772811
// Handle backend responses
773812
fn handle_backend_response(&self, response: BackendResponse, _cx: &mut AsyncApp) {
774-
debug!("UI: Received chat management response: {:?}", response);
775813
match response {
776814
BackendResponse::SessionCreated { session_id } => {
815+
debug!("Received BackendResponse::SessionCreated");
777816
*self.current_session_id.lock().unwrap() = Some(session_id.clone());
778817
// Refresh the session list
779818
if let Some(sender) = self.backend_event_sender.lock().unwrap().as_ref() {
@@ -783,12 +822,14 @@ impl Gpui {
783822
}
784823
}
785824
BackendResponse::SessionDeleted { session_id: _ } => {
825+
debug!("Received BackendResponse::SessionDeleted");
786826
// Refresh the session list
787827
if let Some(sender) = self.backend_event_sender.lock().unwrap().as_ref() {
788828
let _ = sender.try_send(BackendEvent::ListSessions);
789829
}
790830
}
791831
BackendResponse::SessionsListed { sessions } => {
832+
debug!("Received BackendResponse::SessionsListed");
792833
*self.chat_sessions.lock().unwrap() = sessions.clone();
793834
self.push_event(UiEvent::UpdateChatList { sessions });
794835
}

0 commit comments

Comments
 (0)