From 9a8de58ae5b44bbef3961c841cb6955181313bf7 Mon Sep 17 00:00:00 2001
From: Dario <77837029+dve00@users.noreply.github.com>
Date: Wed, 1 Apr 2026 23:44:04 +0200
Subject: [PATCH] zed: Open About window as standalone window (#52523)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Context
Replaces the native system prompt used for "About Zed" with a custom
standalone GPUI window which can open without the need for opening a Zed
editor window of none are currently open.
Closes #49157
## How to Review
Single file change in `crates/zed/src/zed.rs` — the `about` function is
replaced by `open_about_window`. The new window deduplicates itself,
matches the old layout (title, version info, Ok + Copy buttons), and
closes on either button or Escape.
## Screenshots
### Old Behavior
Opening the about window when no other Zed window is open will open a
new empty zed window and display the about window on top:
### New Behavior
If there is no open Zed window we just open an instance of the new About
window:
If there is at least one open Zed window, we display the About window on
top of it:
In bright mode:
Additionally, the ESC button will now act like clicking the Ok button
and close the about window without copying its content.
## Self-Review Checklist
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- no new tests have been implemented
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Improved the About Zed dialog to open as a standalone window instead
of a native system prompt
---------
Co-authored-by: Danilo Leal
---
crates/zed/src/zed.rs | 261 +++++++++++++++++++++++++++++++++++-------
1 file changed, 217 insertions(+), 44 deletions(-)
diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs
index 01e2354849f3a70399c680c44bd1a3cfbeb64dc4..75fe04feff794f21ff3fdd0e763084e4887a040b 100644
--- a/crates/zed/src/zed.rs
+++ b/crates/zed/src/zed.rs
@@ -33,10 +33,11 @@ use git_ui::commit_view::CommitViewToolbar;
use git_ui::git_panel::GitPanel;
use git_ui::project_diff::{BranchDiffToolbar, ProjectDiffToolbar};
use gpui::{
- Action, App, AppContext as _, AsyncWindowContext, Context, DismissEvent, Element, Entity,
- Focusable, KeyBinding, ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString,
- Task, TitlebarOptions, UpdateGlobal, WeakEntity, Window, WindowHandle, WindowKind,
- WindowOptions, actions, image_cache, point, px, retain_all,
+ Action, App, AppContext as _, AsyncWindowContext, ClipboardItem, Context, DismissEvent,
+ Element, Entity, FocusHandle, Focusable, Image, ImageFormat, KeyBinding, ParentElement,
+ PathPromptOptions, PromptLevel, ReadGlobal, SharedString, Size, Task, TitlebarOptions,
+ UpdateGlobal, WeakEntity, Window, WindowBounds, WindowHandle, WindowKind, WindowOptions,
+ actions, image_cache, img, point, px, retain_all,
};
use image_viewer::ImageInfo;
use language::Capability;
@@ -78,7 +79,7 @@ use std::{
use terminal_view::terminal_panel::{self, TerminalPanel};
use theme::{ActiveTheme, SystemAppearance, ThemeRegistry, deserialize_icon_theme};
use theme_settings::{ThemeSettings, load_user_theme};
-use ui::{PopoverMenuHandle, prelude::*};
+use ui::{Navigable, NavigableEntry, PopoverMenuHandle, TintColor, prelude::*};
use util::markdown::MarkdownString;
use util::rel_path::RelPath;
use util::{ResultExt, asset_str, maybe};
@@ -96,8 +97,8 @@ use workspace::{
};
use workspace::{Pane, notifications::DetachAndPromptErr};
use zed_actions::{
- OpenAccountSettings, OpenBrowser, OpenDocs, OpenServerSettings, OpenSettingsFile, OpenZedUrl,
- Quit,
+ About, OpenAccountSettings, OpenBrowser, OpenDocs, OpenServerSettings, OpenSettingsFile,
+ OpenZedUrl, Quit,
};
actions!(
@@ -277,10 +278,8 @@ pub fn init(cx: &mut App) {
);
});
})
- .on_action(|_: &zed_actions::About, cx| {
- with_active_or_new_workspace(cx, |workspace, window, cx| {
- about(workspace, window, cx);
- });
+ .on_action(|_: &About, cx| {
+ open_about_window(cx);
});
}
@@ -1249,44 +1248,218 @@ fn initialize_pane(
});
}
-fn about(_: &mut Workspace, window: &mut Window, cx: &mut Context) {
- use std::fmt::Write;
- let release_channel = ReleaseChannel::global(cx).display_name();
- let full_version = AppVersion::global(cx);
- let version = env!("CARGO_PKG_VERSION");
- let debug = if cfg!(debug_assertions) {
- "(debug)"
- } else {
- ""
- };
- let message = format!("{release_channel} {version} {debug}");
+fn open_about_window(cx: &mut App) {
+ fn about_window_icon(release_channel: ReleaseChannel) -> Arc {
+ let bytes = match release_channel {
+ ReleaseChannel::Dev => include_bytes!("../resources/app-icon-dev.png").as_slice(),
+ ReleaseChannel::Nightly => {
+ include_bytes!("../resources/app-icon-nightly.png").as_slice()
+ }
+ ReleaseChannel::Preview => {
+ include_bytes!("../resources/app-icon-preview.png").as_slice()
+ }
+ ReleaseChannel::Stable => include_bytes!("../resources/app-icon.png").as_slice(),
+ };
- let mut detail = AppCommitSha::try_global(cx)
- .map(|sha| sha.full())
- .unwrap_or_default();
- if !detail.is_empty() {
- detail.push('\n');
+ Arc::new(Image::from_bytes(ImageFormat::Png, bytes.to_vec()))
}
- _ = write!(&mut detail, "\n{full_version}");
- let detail = Some(detail);
+ struct AboutWindow {
+ focus_handle: FocusHandle,
+ ok_entry: NavigableEntry,
+ copy_entry: NavigableEntry,
+ app_icon: Arc,
+ message: SharedString,
+ commit: Option,
+ full_version: SharedString,
+ }
- let prompt = window.prompt(
- PromptLevel::Info,
- &message,
- detail.as_deref(),
- &["Copy", "OK"],
- cx,
- );
- cx.spawn(async move |_, cx| {
- if let Ok(0) = prompt.await {
- let content = format!("{}\n{}", message, detail.as_deref().unwrap_or(""));
- cx.update(|cx| {
- cx.write_to_clipboard(gpui::ClipboardItem::new_string(content));
- });
+ impl AboutWindow {
+ fn new(cx: &mut Context) -> Self {
+ let release_channel = ReleaseChannel::global(cx);
+ let release_channel_name = release_channel.display_name();
+ let full_version: SharedString = AppVersion::global(cx).to_string().into();
+ let version = env!("CARGO_PKG_VERSION");
+
+ let debug = if cfg!(debug_assertions) {
+ "(debug)"
+ } else {
+ ""
+ };
+ let message: SharedString = format!("{release_channel_name} {version} {debug}").into();
+ let commit = AppCommitSha::try_global(cx)
+ .map(|sha| sha.full())
+ .filter(|commit| !commit.is_empty())
+ .map(SharedString::from);
+
+ Self {
+ focus_handle: cx.focus_handle(),
+ ok_entry: NavigableEntry::focusable(cx),
+ copy_entry: NavigableEntry::focusable(cx),
+ app_icon: about_window_icon(release_channel),
+ message,
+ commit,
+ full_version,
+ }
}
- })
- .detach();
+
+ fn copy_details(&self, window: &mut Window, cx: &mut Context) {
+ let content = match self.commit.as_ref() {
+ Some(commit) => {
+ format!(
+ "{}\nCommit: {}\nVersion: {}",
+ self.message, commit, self.full_version
+ )
+ }
+ None => format!("{}\nVersion: {}", self.message, self.full_version),
+ };
+ cx.write_to_clipboard(ClipboardItem::new_string(content));
+ window.remove_window();
+ }
+ }
+
+ impl Render for AboutWindow {
+ fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement {
+ let ok_is_focused = self.ok_entry.focus_handle.contains_focused(window, cx);
+ let copy_is_focused = self.copy_entry.focus_handle.contains_focused(window, cx);
+
+ Navigable::new(
+ v_flex()
+ .id("about-window")
+ .track_focus(&self.focus_handle)
+ .on_action(cx.listener(|_, _: &menu::Cancel, window, _cx| {
+ window.remove_window();
+ }))
+ .min_w_0()
+ .size_full()
+ .bg(cx.theme().colors().editor_background)
+ .text_color(cx.theme().colors().text)
+ .p_4()
+ .when(cfg!(target_os = "macos"), |this| this.pt_10())
+ .gap_4()
+ .text_center()
+ .justify_between()
+ .child(
+ v_flex()
+ .w_full()
+ .gap_2()
+ .items_center()
+ .child(img(self.app_icon.clone()).size_16().flex_none())
+ .child(Headline::new(self.message.clone()))
+ .when_some(self.commit.clone(), |this, commit| {
+ this.child(
+ Label::new("Commit")
+ .color(Color::Muted)
+ .size(LabelSize::XSmall),
+ )
+ .child(Label::new(commit).size(LabelSize::Small))
+ })
+ .child(
+ Label::new("Version")
+ .color(Color::Muted)
+ .size(LabelSize::XSmall),
+ )
+ .child(Label::new(self.full_version.clone()).size(LabelSize::Small)),
+ )
+ .child(
+ h_flex()
+ .w_full()
+ .gap_1()
+ .child(
+ div()
+ .flex_1()
+ .track_focus(&self.ok_entry.focus_handle)
+ .on_action(cx.listener(|_, _: &menu::Confirm, window, _cx| {
+ window.remove_window();
+ }))
+ .child(
+ Button::new("ok", "Ok")
+ .full_width()
+ .style(ButtonStyle::OutlinedGhost)
+ .toggle_state(ok_is_focused)
+ .selected_style(ButtonStyle::Tinted(TintColor::Accent))
+ .on_click(cx.listener(|_, _, window, _cx| {
+ window.remove_window();
+ })),
+ ),
+ )
+ .child(
+ div()
+ .flex_1()
+ .track_focus(&self.copy_entry.focus_handle)
+ .on_action(cx.listener(
+ |this, _: &menu::Confirm, window, cx| {
+ this.copy_details(window, cx);
+ },
+ ))
+ .child(
+ Button::new("copy", "Copy")
+ .full_width()
+ .style(ButtonStyle::Tinted(TintColor::Accent))
+ .toggle_state(copy_is_focused)
+ .selected_style(ButtonStyle::Tinted(TintColor::Accent))
+ .on_click(cx.listener(|this, _event, window, cx| {
+ this.copy_details(window, cx);
+ })),
+ ),
+ ),
+ )
+ .into_any_element(),
+ )
+ .entry(self.ok_entry.clone())
+ .entry(self.copy_entry.clone())
+ }
+ }
+
+ impl Focusable for AboutWindow {
+ fn focus_handle(&self, _cx: &App) -> FocusHandle {
+ self.ok_entry.focus_handle.clone()
+ }
+ }
+
+ // Don't open about window twice
+ if let Some(existing) = cx
+ .windows()
+ .into_iter()
+ .find_map(|w| w.downcast::())
+ {
+ existing
+ .update(cx, |about_window, window, cx| {
+ window.activate_window();
+ about_window.ok_entry.focus_handle.focus(window, cx);
+ })
+ .log_err();
+ return;
+ }
+
+ let window_size = Size {
+ width: px(440.),
+ height: px(300.),
+ };
+
+ cx.open_window(
+ WindowOptions {
+ titlebar: Some(TitlebarOptions {
+ title: Some("About Zed".into()),
+ appears_transparent: true,
+ traffic_light_position: Some(point(px(12.), px(12.))),
+ }),
+ window_bounds: Some(WindowBounds::centered(window_size, cx)),
+ is_resizable: false,
+ is_minimizable: false,
+ kind: WindowKind::Normal,
+ app_id: Some(ReleaseChannel::global(cx).app_id().to_owned()),
+ ..Default::default()
+ },
+ |window, cx| {
+ let about_window = cx.new(AboutWindow::new);
+ let focus_handle = about_window.read(cx).ok_entry.focus_handle.clone();
+ window.activate_window();
+ focus_handle.focus(window, cx);
+ about_window
+ },
+ )
+ .log_err();
}
#[cfg(not(target_os = "windows"))]