Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a2e1cb8
refactor(files): separate explorer rendering from editor layout
jsgrrchg Sep 12, 2026
07aaa41
feat(shell): add an independent right files panel
jsgrrchg Sep 12, 2026
5fed757
fix(layout): coordinate files panel resizing and expanded mode
jsgrrchg Sep 12, 2026
e39a75c
feat(files): synchronize explorer selection with file tabs
jsgrrchg Sep 12, 2026
be87e9c
fix(files): match sidebar frost and blur floating explorer
jsgrrchg Sep 12, 2026
a4f6dca
fix(layout): align surface tabs with the panel beside Files
jsgrrchg Sep 12, 2026
dad1c86
fix(shell): keep the surface picker clear of the Files overlay
jsgrrchg Sep 12, 2026
9033ff4
fix(layout): keep the explorer and editor in separate columns
jsgrrchg Sep 13, 2026
822433e
Merge upstream main into right sidebar file tree
jsgrrchg Sep 13, 2026
8e3a4c9
Merge remote-tracking branch 'upstream/main' into zeron/right-sidebar…
jsgrrchg Sep 13, 2026
86c27d7
fix(ui): draw the Files sidebar divider only once
jsgrrchg Sep 13, 2026
2012bf2
fix(ui): remove Files from the central surface menus
jsgrrchg Sep 13, 2026
5166d41
test(browser): keep blur sampling inside compact menu
jsgrrchg Sep 13, 2026
d797a04
Merge remote-tracking branch 'upstream/main' into zeron/right-sidebar…
jsgrrchg Sep 13, 2026
067bf84
Merge remote-tracking branch 'upstream/main' into zeron/right-sidebar…
jsgrrchg Sep 13, 2026
f53a33b
fix(files): reveal directories activated from search
jsgrrchg Sep 13, 2026
3ebe2d8
fix(ui): match Files background and scroll Markdown padding
jsgrrchg Sep 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/ui/examples/browser-fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ fn main() -> anyhow::Result<()> {
#[cfg(target_os = "macos")]
{
cx.update(|cx|appearance::set_surface(zeron_theme::SurfacePreference::Frosted,cx));
first.read_with(cx, |b,_| b.fixture_eval("(() => {let grid=document.createElement('div'); grid.id='browser-blur-grid'; grid.style='height:140px;background:repeating-conic-gradient(#172f25 0% 25%,#f5f0df 0% 50%) 0 0/16px 16px'; document.body.prepend(grid);})()"));
first.read_with(cx, |b,_| b.fixture_eval("(() => {let grid=document.createElement('div'); grid.id='browser-blur-grid'; grid.style='height:140px;background:repeating-conic-gradient(#172f25 0% 25%,#f5f0df 0% 50%) 0 0/16px 16px'; document.body.style.paddingTop='0'; document.body.prepend(grid);})()"));
pause(cx,300).await;
let mut layout_video = std::process::Command::new("/usr/sbin/screencapture").args(["-v","-V","30","-C","-k","-D","1"]).arg(output.join("browser-layout.mov")).spawn()?;
pause(cx,800).await;
Expand Down
6 changes: 4 additions & 2 deletions crates/ui/examples/browser-fixture/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ pub async fn exercise(
output: &std::path::Path,
cx: &mut AsyncApp,
) -> anyhow::Result<()> {
eval(&page,"document.body.insertAdjacentHTML('afterbegin', `<div id='browser-blur-grid' style='height:140px;background:repeating-conic-gradient(#172f25 0% 25%,#f5f0df 0% 50%) 0 0/16px 16px'></div><input id='browser-input' style='height:32px;width:200px' placeholder='Browser input'><button id='browser-button' onclick='window.browserClicks=(window.browserClicks||0)+1'>Click</button>`); window.browserClicks=0; document.body.addEventListener('pointerdown',()=>window.pagePresses=(window.pagePresses||0)+1); let live=document.createElement('div');live.style='position:fixed;bottom:12px;right:12px;background:#29483b;color:white;padding:8px;border-radius:6px;font:12px monospace';document.body.append(live);window.browserFrames=0;function frame(){live.textContent='LIVE '+(++window.browserFrames);requestAnimationFrame(frame)}frame();true",cx).await?;
eval(&page,"document.body.style.paddingTop='0'; document.body.insertAdjacentHTML('afterbegin', `<div id='browser-blur-grid' style='height:140px;background:repeating-conic-gradient(#172f25 0% 25%,#f5f0df 0% 50%) 0 0/16px 16px'></div><input id='browser-input' style='height:32px;width:200px' placeholder='Browser input'><button id='browser-button' onclick='window.browserClicks=(window.browserClicks||0)+1'>Click</button>`); window.browserClicks=0; document.body.addEventListener('pointerdown',()=>window.pagePresses=(window.pagePresses||0)+1); let live=document.createElement('div');live.style='position:fixed;bottom:12px;right:12px;background:#29483b;color:white;padding:8px;border-radius:6px;font:12px monospace';document.body.append(live);window.browserFrames=0;function frame(){live.textContent='LIVE '+(++window.browserFrames);requestAnimationFrame(frame)}frame();true",cx).await?;
pause(cx, 400).await;
let bounds = page.read_with(cx, |b, _| b.fixture_linux_bounds());
let input=eval(&page,"(()=>{let r=document.getElementById('browser-input').getBoundingClientRect();return [r.x+20,r.y+15]})()",cx).await?;
Expand Down Expand Up @@ -510,7 +510,9 @@ pub async fn exercise(
AnyWindowHandle::from(window).update(cx, |_, w, _| f32::from(w.viewport_size().width))?;
super::validate_blur(
output,
(f32::from(bounds.origin.x) as f64 + 124., 42., 168., 112.),
// Browser and Terminal are the two permanent rows. Keep this in sync
// with the compact menu so the lower-right sample stays inside it.
(f32::from(bounds.origin.x) as f64 + 124., 42., 168., 78.),
viewport,
)?;
window.update(cx, |s, _, cx| s.fixture_browser_menu(false, cx))?;
Expand Down
13 changes: 13 additions & 0 deletions crates/ui/src/browser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub struct BrowserSurface {
presentation: Presentation,
#[cfg(target_os = "macos")]
resize_inset: gpui::Pixels,
#[cfg(target_os = "macos")]
right_occlusion: gpui::Pixels,
_input_sub: Subscription,
#[cfg(any(target_os = "macos", target_os = "linux"))]
native: Option<native::NativePage>,
Expand Down Expand Up @@ -168,6 +170,8 @@ impl BrowserSurface {
presentation: Presentation::Hidden,
#[cfg(target_os = "macos")]
resize_inset: gpui::px(0.0),
#[cfg(target_os = "macos")]
right_occlusion: gpui::px(0.0),
_input_sub: input_sub,
#[cfg(any(target_os = "macos", target_os = "linux"))]
native: None,
Expand Down Expand Up @@ -221,6 +225,15 @@ impl BrowserSurface {
}
}

/// Crop a GPUI overlay out of both native painting and native hit testing.
#[cfg(target_os = "macos")]
pub fn set_right_occlusion(&mut self, width: gpui::Pixels, cx: &mut Context<Self>) {
if self.right_occlusion != width {
self.right_occlusion = width;
cx.notify();
}
}

pub fn set_presentation(&mut self, presentation: Presentation, cx: &mut Context<Self>) {
if self.presentation == presentation {
return;
Expand Down
11 changes: 9 additions & 2 deletions crates/ui/src/browser/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@ impl BrowserSurface {
};
content = content.child(
div()
.id(gpui::SharedString::from(format!("preview-row-{}", service.id)))
.id(gpui::SharedString::from(format!(
"preview-row-{}",
service.id
)))
.w_full()
.h(px(56.0))
.px(px(14.0))
Expand Down Expand Up @@ -520,12 +523,16 @@ impl Render for BrowserSurface {
} else {
let native = native.handle();
let resize_inset = self.resize_inset;
let right_occlusion = self.right_occlusion;
body.child(
gpui::canvas(
|_, _, _| (),
move |bounds, _, window, cx| {
let native = std::rc::Rc::downgrade(&native);
let mask = window.content_mask().bounds;
let mut mask = window.content_mask().bounds;
let right =
(window.viewport_size().width - right_occlusion).max(mask.left());
mask.size.width = mask.size.width.min(right - mask.left());
let dragging = cx.has_active_drag();
window.on_present(move || {
if let Some(native) = native.upgrade() {
Expand Down
16 changes: 11 additions & 5 deletions crates/ui/src/files/markdown_preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const MAX_MEDIA_BYTES: usize = 64 * 1024 * 1024;
const MAX_MEDIA_ENTRIES: usize = 32;
const MAX_MARKDOWN_BYTES: usize = 2 * 1024 * 1024;
const MAX_PREVIEW_CONTENT_WIDTH: f32 = 900.0;
const PREVIEW_VERTICAL_PADDING: f32 = 16.0;

/// A visual block cites its first source line. Notes on inner lines (for
/// example list items or fenced code) remain attached to that containing block.
Expand Down Expand Up @@ -936,7 +937,10 @@ impl MarkdownPreview {

#[cfg(test)]
pub(super) fn test_block_bounds(&self, ix: usize) -> gpui::Bounds<gpui::Pixels> {
self.list.bounds_for_item(ix).unwrap()
let mut bounds = self.list.bounds_for_item(ix).unwrap();
// GPUI's bounds_for_item omits the list padding applied during paint.
bounds.origin.y += px(PREVIEW_VERTICAL_PADDING);
bounds
}

fn render_row(&mut self, ix: usize, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
Expand Down Expand Up @@ -1236,7 +1240,6 @@ impl Render for MarkdownPreview {
.min_h_0()
.flex()
.flex_col()
.py(px(16.0))
.font_family(theme.font_sans.clone())
.text_color(theme.text)
.track_focus(&self.focus)
Expand Down Expand Up @@ -1287,6 +1290,9 @@ impl Render for MarkdownPreview {
list(self.list.clone(), cx.processor(Self::render_row))
.flex_1()
.min_h_0()
// Scroll the breathing room with the document so content
// clips at the viewport edge, directly below the toolbar.
.py(px(PREVIEW_VERTICAL_PADDING))
.with_sizing_behavior(ListSizingBehavior::Auto),
);
if let Some(preview) = &self.preview_image {
Expand Down Expand Up @@ -1520,7 +1526,7 @@ mod layout_tests {
cx.update_window(window.into(), |_, window, cx| {
window.refresh();
let _ = window.draw(cx);
let bounds = preview.read(cx).list.bounds_for_item(0).unwrap();
let bounds = preview.read(cx).test_block_bounds(0);
let gutter =
((bounds.size.width - px(MAX_PREVIEW_CONTENT_WIDTH)) / 2.0).max(px(24.0));
let position =
Expand Down Expand Up @@ -1684,7 +1690,7 @@ mod layout_tests {
}).unwrap();
let view = window.entity(cx).unwrap();
cx.update_window(window.into(), |_, window, cx| { window.refresh(); let _ = window.draw(cx); }).unwrap();
let bounds = view.read(cx).list.bounds_for_item(0).unwrap();
let bounds = view.read(cx).test_block_bounds(0);
assert!(bounds.size.height > px(100.0));
let position = bounds.center();
cx.update_window(window.into(), |_, window, cx| {
Expand Down Expand Up @@ -1752,7 +1758,7 @@ mod layout_tests {
})
.unwrap();

let bounds = view.read(cx).list.bounds_for_item(0).unwrap();
let bounds = view.read(cx).test_block_bounds(0);
let image_position =
gpui::point(bounds.center().x, bounds.top() + px(28.0 + 80.0));
cx.update_window(window.into(), |_, window, cx| {
Expand Down
Loading
Loading