//! The Storage page: where new worktrees go, after T3 Code's, and the
//! folders the app keeps its settings and threads in.
use std::{
path::{Path, PathBuf},
process::{Command, Stdio},
};
use gpui_kit::component::IconName;
use gpui_kit::*;
use super::SettingsView;
use super::widgets::{Section, SettingRow, reset, text_field};
use crate::settings;
use crate::ui::chat::display_path;
use crate::ui::metal::MetalButton;
use crate::ui::workspace;
impl SettingsView {
pub(super) fn render_storage(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Vec<AnyElement> {
let view = cx.weak_entity();
let data = settings::data_dir();
let default_worktrees = data.as_ref().map(|data| data.join("worktrees"));
let worktrees = Section::new("Worktrees")
.id("storage-worktrees")
.row(
SettingRow::new("Worktree location")
.id("storage-worktrees-location")
.description(
"Folder where new worktrees are created, on any drive, such as ~/worktrees. Existing worktrees stay where they are. Leave empty to use the app's data folder.",
)
.status(match &default_worktrees {
Some(path) if settings::get(|settings| settings.worktrees_dir.trim().is_empty()) => {
format!("Default: {}", display_path(path))
}
_ => String::new(),
})
.reset(reset(&view, |s| s.worktrees_dir.clone(), |s, dir| s.worktrees_dir = dir))
.control(text_field(&self.worktrees_dir, 288., window, cx)),
)
.render(cx);
let settings_dir = settings::path().as_deref().and_then(Path::parent).map(Path::to_path_buf);
let folder_row = |title: &'static str, id: &'static str, path: Option<PathBuf>| {
SettingRow::new(title)
.description(path.as_deref().map(display_path).unwrap_or_else(|| "Unavailable".into()))
.control(
MetalButton::new(id)
.compact()
.icon(IconName::FolderOpen)
.label("Open")
.disabled(path.as_ref().is_none_or(|path| !path.exists()))
.on_click(move |_, _, _| {
if let Some(path) = &path {
open_folder(path);
}
}),
)
.into_any_element()
};
let files = Section::new("Files")
.id("storage-files")
.row(folder_row("Settings", "open-settings-dir", settings_dir))
.row(folder_row("Threads", "open-threads-dir", data.as_ref().map(|data| data.join("threads"))))
.row(folder_row(
"Worktrees",
"open-worktrees-dir",
Some(settings::get(|settings| settings.worktrees_dir.clone()))
.filter(|dir| !dir.trim().is_empty())
.map(|dir| settings::expand_home(dir.trim()))
.or(default_worktrees),
))
.row(folder_row("Threads without a project", "open-scratch-dir", workspace::scratch_dir()))
.render(cx);
vec![worktrees, files]
}
}
pub(super) fn open_folder(path: &Path) {
let Some(program) = workspace::find_program(&["xdg-open"]) else { return };
let spawned = Command::new(program).arg(path).stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null()).spawn();
if let Ok(mut child) = spawned {
std::thread::spawn(move || child.wait());
}
}