Vectrum
Sign In

Give each thread its own right panel, so one thread's device doesn't show in another

JjohannSocommitted 1 parent f693a4acommit27257db

Showing 6 changed files with 80 additions and 35 deletions.

+17−9src/ui/workspace.rsView File
@@ -224,9 +224,13 @@ pub struct Workspace {
224224 search: Entity<InputState>,
225225 sidebar_open: bool,
226226 panel_open: bool,
227- /// What the right panel shows, in the project panel's place: a surface
228- /// (subagents, browser, terminal) or the list to pick one from.
229- right_panel: Option<surfaces::Surface>,
227+ /// What each thread's right panel shows, in the project panel's place: a
228+ /// surface (subagents, browser, device, terminal) or the list to pick one
229+ /// from. Each thread has its own, so another thread's device or browser
230+ /// doesn't follow you around.
231+ right_panels: HashMap<EntityId, surfaces::Surface>,
232+ /// The right panel while no thread is open.
233+ idle_right_panel: Option<surfaces::Surface>,
230234 /// The surface picker's focus, for its keys.
231235 surface_focus: FocusHandle,
232236 settled_open: bool,
@@ -385,7 +389,8 @@ impl Workspace {
385389 search,
386390 sidebar_open: true,
387391 panel_open: true,
388- right_panel: None,
392+ right_panels: HashMap::new(),
393+ idle_right_panel: None,
389394 surface_focus: cx.focus_handle(),
390395 settled_open: false,
391396 snoozed_open: false,
@@ -1549,6 +1554,7 @@ impl Workspace {
15491554 let id = session.entity_id();
15501555 self.chats.remove(&id);
15511556 self.browsers.remove(&id);
1557+ self.right_panels.remove(&id);
15521558 let thread_id = session.read(cx).id.clone();
15531559 self.forget_thread_devices(&thread_id, cx);
15541560 self.drop_terminals(id);
@@ -1785,8 +1791,8 @@ impl Workspace {
17851791
17861792 fn toggle_project_panel(&mut self, cx: &mut Context<Self>) {
17871793 // With a surface in its place, the project panel comes back.
1788- if self.right_panel.is_some() {
1789- self.right_panel = None;
1794+ if self.right_panel().is_some() {
1795+ self.set_right_panel(None);
17901796 self.panel_open = true;
17911797 } else {
17921798 self.panel_open = !self.panel_open;
@@ -1805,9 +1811,11 @@ impl Workspace {
18051811 }
18061812 let thread_id = session.read(cx).id.clone();
18071813 let panel = cx.new(|cx| BrowserPanel::new(&thread_id, window, cx));
1808- let subscription = cx.subscribe(&panel, |this, _, event: &BrowserPanelEvent, cx| match event {
1814+ let subscription = cx.subscribe(&panel, move |this, _, event: &BrowserPanelEvent, cx| match event {
18091815 BrowserPanelEvent::Close => {
1810- this.right_panel = None;
1816+ if this.right_panels.get(&id) == Some(&surfaces::Surface::Browser) {
1817+ this.right_panels.remove(&id);
1818+ }
18111819 cx.notify();
18121820 }
18131821 });
@@ -2111,7 +2119,7 @@ impl Render for Workspace {
21112119 let docked_terminal =
21122120 if self.surface_open(surfaces::Surface::Terminal) { self.render_terminal_drawer(true, window, cx) } else { None };
21132121 let panel_visible = self.panel_open
2114- && self.right_panel.is_none()
2122+ && self.right_panel().is_none()
21152123 && self.active_project().is_some()
21162124 && window.viewport_size().width >= px(PANEL_MIN_WINDOW);
21172125 // The browser gives way before the conversation gets too narrow.
+16−12src/ui/workspace/devices.rsView File
@@ -113,9 +113,15 @@ impl Workspace {
113113 let active = self.active.as_ref().is_some_and(|session| session.read(cx).id == thread);
114114 if active && !float {
115115 self.open_surface(Surface::Device, window, cx);
116- } else if active && self.surface_open(Surface::Device) {
117- // Floating closes the panel, as the browser's floating preview does.
118- self.right_panel = None;
116+ } else if let Some(session) = self.session_by_id(thread, cx) {
117+ let id = session.entity_id();
118+ if !float {
119+ // It's there when you open the thread.
120+ self.set_thread_surface(id, Some(Surface::Device));
121+ } else if self.thread_surface(id) == Some(Surface::Device) {
122+ // Floating closes the panel, as the browser's floating preview does.
123+ self.set_thread_surface(id, None);
124+ }
119125 }
120126 cx.notify();
121127 }
@@ -236,21 +242,19 @@ impl Workspace {
236242 let thread_id = thread.to_string();
237243 cx.subscribe_in(&panel, window, move |this, _, event: &DevicePanelEvent, window, cx| {
238244 let active = this.active.as_ref().is_some_and(|session| session.read(cx).id == thread_id);
245+ let session = this.session_by_id(&thread_id, cx).map(|session| session.entity_id());
239246 match event {
240- DevicePanelEvent::Close => {
241- if active && this.surface_open(Surface::Device) {
242- this.right_panel = None;
243- }
244- }
245247 // Floating closes the panel, as the browser's floating preview does.
246- DevicePanelEvent::Floated => {
247- if active && this.surface_open(Surface::Device) {
248- this.right_panel = None;
248+ DevicePanelEvent::Close | DevicePanelEvent::Floated => {
249+ if let Some(session) = session.filter(|session| this.thread_surface(*session) == Some(Surface::Device)) {
250+ this.set_thread_surface(session, None);
249251 }
250252 }
251253 DevicePanelEvent::Docked => {
252254 if active {
253255 this.open_surface(Surface::Device, window, cx);
256+ } else if let Some(session) = session {
257+ this.set_thread_surface(session, Some(Surface::Device));
254258 }
255259 }
256260 }
@@ -291,7 +295,7 @@ impl Workspace {
291295 Some(move |_: &mut Window, cx: &mut App| {
292296 let _ = workspace.update(cx, |this, cx| {
293297 if this.surface_open(Surface::Device) {
294- this.right_panel = None;
298+ this.set_right_panel(None);
295299 cx.notify();
296300 }
297301 });
+1−1src/ui/workspace/header.rsView File
@@ -280,7 +280,7 @@ impl Workspace {
280280 .ghost()
281281 .mini()
282282 .icon(IconName::PanelRight)
283- .selected(self.right_panel.is_some())
283+ .selected(self.right_panel().is_some())
284284 .tooltip("Toggle right panel (Ctrl+Alt+B)")
285285 .on_click(cx.listener(|this, _, window, cx| this.toggle_right_panel(window, cx))),
286286 )
+1−1src/ui/workspace/subagents.rsView File
@@ -145,7 +145,7 @@ impl Workspace {
145145 .icon(IconName::Close)
146146 .tooltip("Close subagents")
147147 .on_click(cx.listener(|this, _, _, cx| {
148- this.right_panel = None;
148+ this.set_right_panel(None);
149149 cx.notify();
150150 })),
151151 );
+41−8src/ui/workspace/surfaces.rsView File
@@ -33,16 +33,49 @@ const SURFACES: [(Surface, &str, &str); 4] = [
3333 pub(super) const TERMINAL_PANEL_WIDTH: f32 = 520.;
3434
3535 impl Workspace {
36+ /// What the open thread's right panel shows.
37+ pub(super) fn right_panel(&self) -> Option<Surface> {
38+ match &self.active {
39+ Some(session) => self.right_panels.get(&session.entity_id()).copied(),
40+ None => self.idle_right_panel,
41+ }
42+ }
43+
44+ /// Sets what the open thread's right panel shows. Other threads keep theirs.
45+ pub(super) fn set_right_panel(&mut self, surface: Option<Surface>) {
46+ match self.active.as_ref().map(|session| session.entity_id()) {
47+ Some(id) => self.set_thread_surface(id, surface),
48+ None => self.idle_right_panel = surface,
49+ }
50+ }
51+
52+ /// What a thread's right panel shows, open or not.
53+ pub(super) fn thread_surface(&self, thread: EntityId) -> Option<Surface> {
54+ self.right_panels.get(&thread).copied()
55+ }
56+
57+ /// Sets what a thread's right panel shows, open or not.
58+ pub(super) fn set_thread_surface(&mut self, thread: EntityId, surface: Option<Surface>) {
59+ match surface {
60+ Some(surface) => {
61+ self.right_panels.insert(thread, surface);
62+ }
63+ None => {
64+ self.right_panels.remove(&thread);
65+ }
66+ }
67+ }
68+
3669 pub(super) fn surface_open(&self, surface: Surface) -> bool {
37- self.right_panel == Some(surface)
70+ self.right_panel() == Some(surface)
3871 }
3972
4073 /// The right panel's button: opens "Open a surface", or closes the panel.
4174 pub(super) fn toggle_right_panel(&mut self, window: &mut Window, cx: &mut Context<Self>) {
42- if self.right_panel.is_some() {
43- self.right_panel = None;
75+ if self.right_panel().is_some() {
76+ self.set_right_panel(None);
4477 } else {
45- self.right_panel = Some(Surface::Picker);
78+ self.set_right_panel(Some(Surface::Picker));
4679 self.surface_focus.focus(window, cx);
4780 }
4881 cx.notify();
@@ -51,8 +84,8 @@ impl Workspace {
5184 /// Shows a surface in the right panel, or closes the panel if it's
5285 /// showing it already.
5386 pub(super) fn toggle_surface(&mut self, surface: Surface, window: &mut Window, cx: &mut Context<Self>) {
54- if self.right_panel == Some(surface) {
55- self.right_panel = None;
87+ if self.right_panel() == Some(surface) {
88+ self.set_right_panel(None);
5689 cx.notify();
5790 return;
5891 }
@@ -67,7 +100,7 @@ impl Workspace {
67100 // The terminals move over from the drawer, if it's open.
68101 self.dock_terminal(window, cx);
69102 }
70- self.right_panel = Some(surface);
103+ self.set_right_panel(Some(surface));
71104 if surface == Surface::Picker {
72105 self.surface_focus.focus(window, cx);
73106 }
@@ -81,7 +114,7 @@ impl Workspace {
81114 return;
82115 }
83116 if keystroke.key == "escape" {
84- self.right_panel = None;
117+ self.set_right_panel(None);
85118 cx.notify();
86119 return;
87120 }
+4−4src/ui/workspace/terminal_drawer.rsView File
@@ -190,7 +190,7 @@ impl Workspace {
190190 }
191191 // In the right panel, it hides from there.
192192 if self.surface_open(Surface::Terminal) {
193- self.right_panel = None;
193+ self.set_right_panel(None);
194194 self.focus_active_composer(window, cx);
195195 cx.notify();
196196 return;
@@ -224,7 +224,7 @@ impl Workspace {
224224 let drawer = self.terminals.entry(session).or_default();
225225 drawer.open = false;
226226 let empty = drawer.layout.is_empty();
227- self.right_panel = Some(Surface::Terminal);
227+ self.set_right_panel(Some(Surface::Terminal));
228228 if empty {
229229 self.new_terminal(window, cx);
230230 } else {
@@ -339,8 +339,8 @@ impl Workspace {
339339 let empty = drawer.layout.is_empty();
340340 if empty {
341341 self.terminals.remove(&session);
342- if self.surface_open(Surface::Terminal) && self.active.as_ref().is_some_and(|active| active.entity_id() == session) {
343- self.right_panel = None;
342+ if self.thread_surface(session) == Some(Surface::Terminal) {
343+ self.set_thread_surface(session, None);
344344 }
345345 }
346346 if self.active.as_ref().is_some_and(|active| active.entity_id() == session) {