gwen: Add background image picker to onboarding

Add a Background section to the welcome page: a file picker writes
background.path into user settings, a Clear button removes it, and the
chosen image is previewed in place. No default background is set; fit,
position and opacity remain in the Settings page.
This commit is contained in:
yenru0 committed 2026-09-29 08:19:52 +09:00
1 parent 4e15e1e0ed
commit faa11dea4c
1 file changed
+96 -2
+96 -2
View File
@@ -1,3 +1,4 @@
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
@@ -5,7 +6,10 @@ use client::{Client, TelemetrySettings, UserStore, zed_urls};
use cloud_api_types::Plan;
use collections::HashMap;
use fs::Fs;
use gpui::{Action, Animation, AnimationExt, App, Entity, IntoElement, TaskExt, pulsating_between};
use gpui::{
Action, Animation, AnimationExt, App, Entity, ImageSource, IntoElement, ObjectFit,
PathPromptOptions, Resource, TaskExt, img, pulsating_between,
};
use project::agent_server_store::AllAgentServersSettings;
use project::project_settings::ProjectSettings;
use project::{AgentRegistryStore, RegistryAgent};
@@ -15,7 +19,7 @@ use settings::{
use theme::{Appearance, SystemAppearance, ThemeRegistry};
use theme_settings::{ThemeAppearanceMode, ThemeName, ThemeSelection, ThemeSettings};
use ui::{
AgentSetupButton, Divider, StatefulInteractiveElement, SwitchField, TintColor,
AgentSetupButton, Button, Divider, StatefulInteractiveElement, SwitchField, TintColor,
ToggleButtonGroup, ToggleButtonGroupSize, ToggleButtonSimple, ToggleButtonWithIcon, Tooltip,
prelude::*,
};
@@ -242,6 +246,95 @@ fn render_theme_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement
}
}
fn render_background_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement {
let current_path = cx
.try_global::<SettingsStore>()
.and_then(|store| store.merged_settings().background.clone())
.and_then(|background| background.path)
.filter(|path| !path.is_empty());
let has_background = current_path.is_some();
let colors = cx.theme().colors();
let fs = <dyn Fs>::global(cx);
let select_button = {
let fs = fs.clone();
Button::new("onboarding-background-select", "Select Image…")
.tab_index({
*tab_index += 1;
*tab_index - 1
})
.on_click(move |_, _window, cx| {
let receiver = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: false,
multiple: false,
prompt: None,
});
let fs = fs.clone();
cx.spawn(async move |cx| {
let Ok(Ok(Some(paths))) = receiver.await else {
return;
};
let Some(path) = paths.into_iter().next() else {
return;
};
let path = path.to_string_lossy().to_string();
cx.update(|cx| {
update_settings_file(fs, cx, move |settings, _cx| {
settings.background.get_or_insert_default().path = Some(path);
});
telemetry::event!("Welcome Background Changed", set = true);
});
})
.detach();
})
};
let clear_button = {
let fs = fs.clone();
Button::new("onboarding-background-clear", "Clear")
.tab_index({
*tab_index += 1;
*tab_index - 1
})
.on_click(move |_, _, cx| {
update_settings_file(fs.clone(), cx, |settings, _cx| {
settings.background.get_or_insert_default().path = None;
});
telemetry::event!("Welcome Background Changed", set = false);
})
};
let preview = match current_path {
Some(path) => img(ImageSource::Resource(Resource::Path(Arc::from(Path::new(&path)))))
.w_full()
.h(rems_from_px(120.0_f32))
.object_fit(ObjectFit::Cover)
.rounded_md()
.border_1()
.border_color(colors.border)
.into_any_element(),
None => Label::new("No background image yet — pick one to show it behind the editor.")
.color(Color::Muted)
.into_any_element(),
};
v_flex()
.gap_2()
.child(
h_flex()
.justify_between()
.child(Label::new("Background"))
.child(
h_flex()
.gap_2()
.child(select_button)
.when(has_background, |this| this.child(clear_button)),
),
)
.child(preview)
}
fn render_telemetry_section(tab_index: &mut isize, cx: &App) -> impl IntoElement {
let fs = <dyn Fs>::global(cx);
@@ -720,6 +813,7 @@ pub(crate) fn render_basics_page(user_store: &Entity<UserStore>, cx: &mut App) -
.id("basics-page")
.gap_6()
.child(render_theme_section(&mut tab_index, cx))
.child(render_background_section(&mut tab_index, cx))
.child(render_base_keymap_section(&mut tab_index, cx))
.child(render_ai_section(user_store, cx))
.child(render_import_settings_section(&mut tab_index, cx))