feat: create repository #3

Merged
reya merged 2 commits from feat/create-repo into master 2026-08-26 13:51:48 +00:00
5 changed files with 180 additions and 61 deletions
Showing only changes of commit df8b8a9598 - Show all commits
Generated
+30 -29
View File
@@ -1259,7 +1259,7 @@ dependencies = [
[[package]]
name = "collections"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"gpui_util",
"indexmap",
@@ -1725,7 +1725,7 @@ dependencies = [
[[package]]
name = "derive_refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"proc-macro2",
"quote",
@@ -3480,7 +3480,7 @@ dependencies = [
[[package]]
name = "gpui"
version = "0.2.2"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"accesskit",
"anyhow",
@@ -3551,7 +3551,7 @@ dependencies = [
[[package]]
name = "gpui-base"
version = "0.5.2"
source = "git+https://github.com/longbridge/gpui-component#d5821f270317754f2a311a0bb148ec32cbb0ced4"
source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79"
dependencies = [
"aho-corasick",
"anyhow",
@@ -3583,7 +3583,7 @@ dependencies = [
[[package]]
name = "gpui-component"
version = "0.5.2"
source = "git+https://github.com/longbridge/gpui-component#d5821f270317754f2a311a0bb148ec32cbb0ced4"
source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79"
dependencies = [
"anyhow",
"chrono",
@@ -3663,7 +3663,7 @@ dependencies = [
[[package]]
name = "gpui-component-assets"
version = "0.5.1"
source = "git+https://github.com/longbridge/gpui-component#d5821f270317754f2a311a0bb148ec32cbb0ced4"
source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79"
dependencies = [
"anyhow",
"gpui",
@@ -3677,7 +3677,7 @@ dependencies = [
[[package]]
name = "gpui-component-macros"
version = "0.5.1"
source = "git+https://github.com/longbridge/gpui-component#d5821f270317754f2a311a0bb148ec32cbb0ced4"
source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79"
dependencies = [
"proc-macro2",
"quote",
@@ -3687,7 +3687,7 @@ dependencies = [
[[package]]
name = "gpui_apple"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"anyhow",
"block",
@@ -3710,7 +3710,7 @@ dependencies = [
[[package]]
name = "gpui_linux"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"accesskit",
"accesskit_unix",
@@ -3756,7 +3756,7 @@ dependencies = [
[[package]]
name = "gpui_macos"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"accesskit",
"accesskit_macos",
@@ -3802,7 +3802,7 @@ dependencies = [
[[package]]
name = "gpui_macros"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -3813,7 +3813,7 @@ dependencies = [
[[package]]
name = "gpui_platform"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"console_error_panic_hook",
"gpui",
@@ -3826,7 +3826,7 @@ dependencies = [
[[package]]
name = "gpui_shared_string"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"schemars",
"serde",
@@ -3836,7 +3836,7 @@ dependencies = [
[[package]]
name = "gpui_util"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"anyhow",
"log",
@@ -3846,7 +3846,7 @@ dependencies = [
[[package]]
name = "gpui_web"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"anyhow",
"console_error_panic_hook",
@@ -3870,7 +3870,7 @@ dependencies = [
[[package]]
name = "gpui_wgpu"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"anyhow",
"bytemuck",
@@ -3896,7 +3896,7 @@ dependencies = [
[[package]]
name = "gpui_windows"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"accesskit",
"accesskit_windows",
@@ -4209,7 +4209,7 @@ dependencies = [
[[package]]
name = "http_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"anyhow",
"async-compression",
@@ -4229,7 +4229,7 @@ dependencies = [
[[package]]
name = "http_client_tls"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"rustls",
"rustls-platform-verifier 0.5.3",
@@ -5205,7 +5205,7 @@ dependencies = [
[[package]]
name = "media"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"anyhow",
"bindgen",
@@ -6243,7 +6243,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "perf"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"collections",
"serde",
@@ -7031,7 +7031,7 @@ dependencies = [
[[package]]
name = "refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"derive_refineable",
]
@@ -7114,7 +7114,7 @@ dependencies = [
[[package]]
name = "reqwest_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"anyhow",
"bytes",
@@ -7526,7 +7526,7 @@ dependencies = [
[[package]]
name = "scheduler"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"async-task",
"backtrace",
@@ -8219,7 +8219,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "sum_tree"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"heapless 0.9.3",
"log",
@@ -9592,7 +9592,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "util_macros"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"perf",
"quote",
@@ -10877,6 +10877,7 @@ dependencies = [
"gpui-component",
"log",
"nostr",
"paths",
"signed_core",
"signed_git",
"signed_state",
@@ -11390,7 +11391,7 @@ checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12"
[[package]]
name = "zlog"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"anyhow",
"chrono",
@@ -11407,7 +11408,7 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
[[package]]
name = "ztracing"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
dependencies = [
"tracing",
"tracing-subscriber",
@@ -11418,7 +11419,7 @@ dependencies = [
[[package]]
name = "ztracing_macro"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#55007f518bc1d49e6b3291c5eaa1aabf649b36fd"
source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8"
[[package]]
name = "zune-core"
+6
View File
@@ -35,6 +35,12 @@ pub fn home_dir() -> PathBuf {
dirs::home_dir().expect("failed to determine home directory")
}
/// Returns the current user's Desktop folder, falling back to the home
/// directory (or an empty path) when it can't be determined.
pub fn desktop_dir() -> PathBuf {
dirs::desktop_dir().unwrap_or_else(|| dirs::home_dir().unwrap_or_default())
}
/// Sets a custom directory for all user data, overriding the default data
/// directory. Must be called before any other path operation. The directory
/// is created if it doesn't exist and canonicalized to an absolute path.
+1
View File
@@ -7,6 +7,7 @@ publish.workspace = true
[dependencies]
assets = { path = "../assets" }
dock = { workspace = true }
paths = { path = "../paths" }
signed_core = { path = "../signed_core" }
signed_git = { path = "../signed_git" }
signed_state = { path = "../signed_state" }
@@ -1,12 +1,11 @@
use dock::{DockArea, DockPlacement, panel_handle};
use gpui::prelude::*;
use gpui::{App, Entity, SharedString, WeakEntity, Window, div, px};
use gpui::{App, Entity, PathPromptOptions, SharedString, WeakEntity, Window, div, px};
use gpui_base::input::TextareaState;
use gpui_component::button::{Button, ButtonVariants};
use gpui_component::button::{Button, ButtonVariants, Toggle, ToggleVariants};
use gpui_component::dialog::{DialogDescription, DialogFooter, DialogHeader, DialogTitle};
use gpui_component::form::{field, v_form};
use gpui_component::input::{Input, InputState, Textarea};
use gpui_component::tag::Tag;
use gpui_component::{ActiveTheme, Disableable, IconName, Sizable, WindowExt, h_flex, v_flex};
use nostr::prelude::*;
use signed_core::{Announcement, filters};
@@ -29,6 +28,8 @@ pub struct CreateRepoState {
pub loading_servers: bool,
pub error: Option<SharedString>,
pub grasp_servers: Vec<RelayUrl>,
/// Whether the grasp server section is shown; defaults to shown.
pub servers_enabled: bool,
}
impl CreateRepoState {
@@ -36,6 +37,7 @@ impl CreateRepoState {
fn new_default() -> Self {
Self {
loading_servers: true,
servers_enabled: false,
grasp_servers: DEFAULT_GRASP_SERVERS
.iter()
.filter_map(|url| RelayUrl::parse(url).ok())
@@ -57,6 +59,10 @@ pub fn open(dock_area: WeakEntity<DockArea>, window: &mut Window, cx: &mut App)
.auto_grow(3, 5)
.placeholder("Short description")
});
let folder_input = cx.new(|cx| {
InputState::new(window, cx)
.default_value(paths::desktop_dir().to_string_lossy().to_string())
});
let relay_input = cx.new(|cx| {
InputState::new(window, cx).placeholder("wss://relay.example.com or relay.example.com")
});
@@ -65,8 +71,14 @@ pub fn open(dock_area: WeakEntity<DockArea>, window: &mut Window, cx: &mut App)
load_user_grasp_servers(state.clone(), window, cx);
window.open_dialog(cx, move |dialog, _window, _cx| {
const DESC: &str = "Publish a new repository to your grasp servers.";
const FOLDER_NOTE: &str = "Where the repository is stored, defaults to your Desktop";
const SERVER_NOTE: &str =
"Where the repository is hosted, the initial push goes to each server";
let name_input = name_input.clone();
let desc_input = desc_input.clone();
let folder_input = folder_input.clone();
let relay_input = relay_input.clone();
let state = state.clone();
let dock_area = dock_area.clone();
@@ -79,15 +91,13 @@ pub fn open(dock_area: WeakEntity<DockArea>, window: &mut Window, cx: &mut App)
let error = state.read(cx).error.clone();
let servers = state.read(cx).grasp_servers.clone();
let loading_servers = state.read(cx).loading_servers;
let servers_enabled = state.read(cx).servers_enabled;
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Create repository"))
.child(
DialogDescription::new()
.child("Publish a new repository to your grasp servers."),
),
.child(DialogDescription::new().child(DESC)),
)
.child(
v_form()
@@ -101,34 +111,86 @@ pub fn open(dock_area: WeakEntity<DockArea>, window: &mut Window, cx: &mut App)
.child(
field()
.label("Description")
.description("Optional")
.child(Textarea::new(&desc_input)),
)
.child(
field().label("Folder").description(FOLDER_NOTE).child(
h_flex()
.gap_1()
.items_center()
.child(
div()
.flex_1()
.child(Input::new(&folder_input).disabled(true)),
)
.child(
Button::new("choose-folder")
.icon(IconName::FolderOpen)
.ghost()
.tooltip("Choose folder")
.on_click({
let folder_input = folder_input.clone();
move |_ev, window, cx| {
choose_folder(&folder_input, window, cx);
}
}),
),
),
)
.child(
field()
.label("Grasp servers")
.description(
"Where the repository is hosted; the initial push goes to each server",
)
.child(
v_flex()
.gap_1()
.children(
servers
.iter()
.enumerate()
.map(|(ix, relay)| {
render_server_row(ix, relay, state.clone())
}),
)
.child(
h_flex().gap_1().items_center().child(
.label_fn({
let state = state.clone();
move |_window, cx| {
let enabled = state.read(cx).servers_enabled;
h_flex()
.w_full()
.justify_between()
.items_center()
.gap_1()
.child(
Toggle::new("grasp-servers-toggle")
.xsmall()
.ghost()
.icon({
if enabled {
IconName::ChevronDown
} else {
IconName::ChevronUp
}
})
.checked(enabled)
.on_click({
let state = state.clone();
move |checked, _window, cx| {
state.update(cx, |state, cx| {
state.servers_enabled =
*checked;
cx.notify();
});
}
}),
)
.child(div().child("Grasp servers"))
}
})
.when(servers_enabled, |this| this.description(SERVER_NOTE))
.child(v_flex().gap_1().when(servers_enabled, |this| {
this.children(servers.iter().enumerate().map(
|(ix, relay)| {
render_server_row(ix, relay, state.clone(), cx)
},
))
.child(
h_flex()
.gap_1()
.items_center()
.child(
div().flex_1().child(Input::new(&relay_input)),
)
.child(
Button::new("add-relay")
.icon(IconName::Plus)
.small()
.ghost()
.tooltip("Add grasp server")
.on_click({
@@ -144,16 +206,19 @@ pub fn open(dock_area: WeakEntity<DockArea>, window: &mut Window, cx: &mut App)
}
}),
),
)
.when(loading_servers, |this| {
)
.when(
loading_servers,
|this| {
this.child(
div()
.text_xs()
.text_color(cx.theme().muted_foreground)
.child("Loading your grasp servers…"),
)
}),
),
},
)
})),
),
)
.children(error.map(|message| {
@@ -164,6 +229,7 @@ pub fn open(dock_area: WeakEntity<DockArea>, window: &mut Window, cx: &mut App)
Button::new("create")
.primary()
.label("Create repository")
.icon(IconName::ArrowRight)
.tooltip("Create repository")
.loading(busy)
.disabled(busy)
@@ -195,16 +261,26 @@ fn render_server_row(
ix: usize,
relay: &RelayUrl,
state: Entity<CreateRepoState>,
cx: &App,
) -> impl IntoElement {
h_flex()
.w_full()
.gap_1()
.items_center()
.child(Tag::new().flex_1().child(display_server(relay)))
.child(
h_flex()
.h_8()
.w_full()
.px_2()
.bg(cx.theme().muted)
.text_color(cx.theme().muted_foreground)
.text_sm()
.rounded(cx.theme().radius)
.child(display_server(relay)),
)
.child(
Button::new(format!("remove-relay:{ix}"))
.icon(IconName::Close)
.small()
.ghost()
.flex_shrink_0()
.tooltip("Remove")
@@ -227,6 +303,34 @@ fn display_server(relay: &RelayUrl) -> SharedString {
.unwrap_or_else(|| SharedString::from(relay.to_string()))
}
/// Prompt the user to pick the folder the repository will be stored in, using
/// the platform's native folder picker, and show the result in the disabled
/// folder input.
fn choose_folder(folder_input: &Entity<InputState>, window: &mut Window, cx: &mut App) {
let handle = window.window_handle();
let folder_input = folder_input.clone();
let prompt = cx.prompt_for_paths(PathPromptOptions {
files: false,
directories: true,
multiple: false,
prompt: Some("Choose folder".into()),
});
cx.spawn(async move |cx| {
if let Ok(Ok(Some(mut paths))) = prompt.await
&& let Some(path) = paths.pop()
{
let path = path.to_string_lossy().to_string();
cx.update_window(handle, |_, window, cx| {
folder_input.update(cx, |input, cx| input.set_value(path, window, cx));
})
.ok();
}
})
.detach();
}
/// Parse the relay input (accepting a bare host) and append it to the list.
fn add_relay(
state: &Entity<CreateRepoState>,
+7
View File
@@ -0,0 +1,7 @@
# TODO
## Create repository dialog
- [ ] Persist the user's preferred local repository folder (the one picked in
the create-repository dialog, defaulting to Desktop) and use it as the
default next time the dialog opens.