Compare commits

...

33 Commits

Author SHA1 Message Date
Nathan Sobo
e3b714a4ab zed 0.78.1 2023-03-14 19:44:40 -06:00
Nathan Sobo
03d5eebf08 Merge pull request #2288 from zed-industries/cut-off-collaborator-avatars
Fix collaborator avatars being clipped and not centered
2023-03-14 19:44:21 -06:00
Max Brunsfeld
8ea16a11b0 v0.78.x preview 2023-03-14 12:49:21 -07:00
Max Brunsfeld
2042188f5a Merge pull request #2286 from zed-industries/discoverable-sign-in
Make sign-in more discoverable
2023-03-14 11:08:18 -07:00
Max Brunsfeld
0bbb4b22c6 Rename 'Authenticate' action to 'Sign In'
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-14 11:01:23 -07:00
Max Brunsfeld
75901f1c33 Show sign in button directly in titlebar when not signed in
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-14 10:59:39 -07:00
Antonio Scandurra
a6ebc9bd26 collab 0.8.0 2023-03-14 18:21:16 +01:00
Antonio Scandurra
9e3085b0c4 Merge pull request #2284 from zed-industries/automatic-user-creation
Create user record automatically when someone logs in on the website
2023-03-14 18:20:13 +01:00
Mikayla Maki
7af9dda869 Merge pull request #2285 from zed-industries/fix-failed-theme-setting
Fix failed theme setting
2023-03-14 10:10:39 -07:00
Mikayla Maki
2a5ac4f203 Merge pull request #2283 from zed-industries/fix-code-fold-indicator-scaling
Change fold icon width to scale with font size
2023-03-14 10:07:25 -07:00
Mikayla Maki
d8a3f16891 Refactor load into a seperate function 2023-03-14 09:56:27 -07:00
Mikayla Maki
99257a8213 Fix failed initialization of setting file in welcome experience 2023-03-14 09:46:28 -07:00
Antonio Scandurra
0f429243d7 Fix seed binary 2023-03-14 12:37:56 +01:00
Antonio Scandurra
cba41ef7c5 Create user record automatically when someone logs in on the website
Now that we are moving out of the private alpha, we should let everyone
in when they try to log into zed.dev.
2023-03-14 12:25:04 +01:00
Mikayla Maki
2ba38b2fca Change icon width to scale with font size 2023-03-14 00:42:15 -07:00
Petros Amoiridis
e7f78c4f74 Merge pull request #2281 from zed-industries/petros/z-298-the-terminal-button-is-not-shown-for
Remove check for staffmode
2023-03-14 00:12:13 -07:00
Petros Amoiridis
8980df1f5d Remove feature flag
It wasn't working in production anyway
2023-03-14 09:05:04 +02:00
Nathan Sobo
2db8ac4a6f Merge pull request #2280 from zed-industries/terms
Add terms of use to DMG in the bundle script
2023-03-13 16:08:07 -06:00
Nathan Sobo
818a514110 Add terms of use to DMG in the bundle script
Co-Authored-By: Max Brunsfeld <max@zed.dev>
2023-03-13 15:47:59 -06:00
Mikayla Maki
1b4f783b97 Merge pull request #2279 from zed-industries/remove-invite-link
Remove UI for invite link in prep for beta launch
2023-03-13 14:35:52 -07:00
Mikayla Maki
88599add56 Remove UI for invite link in prep for beta launch 2023-03-13 12:54:20 -07:00
Max Brunsfeld
05f6747132 v0.78.x dev 2023-03-13 12:00:56 -07:00
Max Brunsfeld
1096720b41 Merge pull request #2244 from zed-industries/less-surprising-defaults
Clean up some default settings
2023-03-13 11:54:46 -07:00
Julia
5c7c4dd4dd Clean up some default settings 2023-03-13 14:54:28 -04:00
Max Brunsfeld
da35202bbf Merge pull request #2278 from zed-industries/screenshares-from-before-joining
Fix failure to see screenshare tracks that were started prior to join…
2023-03-13 11:22:40 -07:00
Max Brunsfeld
f5c4a2a0dd Fix failure to see screenshare tracks that were started prior to joining a call
Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2023-03-13 11:15:22 -07:00
Mikayla Maki
77a63c6598 Merge pull request #2277 from zed-industries/switch-invite-person-icon
Update Collab + icon
2023-03-13 10:54:33 -07:00
Nate Butler
edd925f77b Format 2023-03-13 13:49:00 -04:00
Nate Butler
6d0f8290a4 Merge pull request #2276 from zed-industries/improve-picker-state-contrast
Improve picker state contrast
2023-03-13 13:48:04 -04:00
Nate Butler
6497ca8ccb Update icon 2023-03-13 13:47:58 -04:00
Nate Butler
e64fe6d660 Improve contrast of project panel active state in light themes 2023-03-13 13:10:46 -04:00
Nate Butler
7df2440757 Format 2023-03-13 12:59:23 -04:00
Nate Butler
6fd4e28813 Update picker active and hover styles
Thanks for pointing this out @ForLoveOfCats

Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com>
2023-03-13 12:59:17 -04:00
41 changed files with 1970 additions and 253 deletions

4
Cargo.lock generated
View File

@@ -1188,7 +1188,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.7.2"
version = "0.8.0"
dependencies = [
"anyhow",
"async-tungstenite",
@@ -8409,7 +8409,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zed"
version = "0.77.0"
version = "0.78.1"
dependencies = [
"activity_indicator",
"anyhow",

View File

@@ -1,3 +1,5 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.2 6.00001C5.52563 6.00001 6.6 4.92545 6.6 3.60001C6.6 2.27457 5.52563 1.20001 4.2 1.20001C2.87438 1.20001 1.8 2.27457 1.8 3.60001C1.8 4.92545 2.87438 6.00001 4.2 6.00001ZM5.15063 6.90001H3.24938C1.45519 6.90001 0 8.35501 0 10.1494C0 10.5094 0.291 10.8 0.649875 10.8H7.7505C8.10938 10.8 8.4 10.5094 8.4 10.1494C8.4 8.35501 6.945 6.90001 5.15063 6.90001ZM11.55 4.95001H10.65V4.05001C10.65 3.80251 10.4494 3.60001 10.2 3.60001C9.95063 3.60001 9.75 3.80157 9.75 4.05001V4.95001H8.85C8.6025 4.95001 8.4 5.15251 8.4 5.40001C8.4 5.64751 8.60156 5.85001 8.85 5.85001H9.75V6.75001C9.75 6.99939 9.9525 7.20001 10.2 7.20001C10.4475 7.20001 10.65 6.99845 10.65 6.75001V5.85001H11.55C11.7994 5.85001 12 5.64939 12 5.40001C12 5.15064 11.7994 4.95001 11.55 4.95001Z" fill="white"/>
<path d="M5.75062 7.09998H3.24938C1.45519 7.09998 0 8.55498 0 10.3493C0 10.7093 0.291 11 0.649875 11H8.3505C8.70938 11 9 10.7093 9 10.3493C9 8.55498 7.545 7.09998 5.75062 7.09998Z" fill="white"/>
<path d="M7 3.5C7 4.82544 5.82562 6 4.5 6C3.17438 6 2 4.82544 2 3.5C2 2.17456 3.17438 1 4.5 1C5.82562 1 7 2.17456 7 3.5Z" fill="white"/>
<path d="M9.5 3.75V5.5M9.5 7.25V5.5M9.5 5.5H11.25M9.5 5.5H7.75" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 564 B

View File

@@ -1,3 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.9 8.00002C7.44656 8.00002 8.7 6.74637 8.7 5.20002C8.7 3.65368 7.44656 2.40002 5.9 2.40002C4.35344 2.40002 3.1 3.65368 3.1 5.20002C3.1 6.74637 4.35344 8.00002 5.9 8.00002ZM7.00906 9.05002H4.79094C2.69772 9.05002 1 10.7475 1 12.841C1 13.261 1.3395 13.6 1.75819 13.6H10.0422C10.4609 13.6 10.8 13.261 10.8 12.841C10.8 10.7475 9.1025 9.05002 7.00906 9.05002ZM14.475 6.77502H13.425V5.72502C13.425 5.43627 13.1909 5.20002 12.9 5.20002C12.6091 5.20002 12.375 5.43518 12.375 5.72502V6.77502H11.325C11.0363 6.77502 10.8 7.01127 10.8 7.30002C10.8 7.58877 11.0352 7.82502 11.325 7.82502H12.375V8.87502C12.375 9.16596 12.6112 9.40002 12.9 9.40002C13.1887 9.40002 13.425 9.16487 13.425 8.87502V7.82502H14.475C14.7659 7.82502 15 7.59096 15 7.30002C15 7.00909 14.7659 6.77502 14.475 6.77502Z" fill="white"/>
<path d="M7.00906 8.99999H4.79094C2.69772 8.99999 1 11.1475 1 13.2409C1 13.6609 1.3395 14 1.75819 14H10.0422C10.4609 14 10.8 13.6609 10.8 13.2409C10.8 11.1475 9.1025 8.99999 7.00906 8.99999Z" fill="white"/>
<path d="M9 5C9 6.54634 7.44657 7.99998 5.90001 7.99998C4.35344 7.99998 3 6.54634 3 5C3 3.45366 4.45344 2 6 2C7.54656 2 9 3.45366 9 5Z" fill="white"/>
<path d="M13.025 6H14.475C14.7659 6 15 6.20906 15 6.5C15 6.79094 14.7659 7 14.475 7H13V8.49995C13 8.7898 12.7638 9.02495 12.475 9.02495C12.1863 9.02495 11.95 8.79089 11.95 8.49995V7H10.525C10.2352 7 10 6.78875 10 6.5C10 6.21125 10.2362 6 10.525 6H11.975V4.525C11.975 4.23516 12.2091 4 12.5 4C12.7909 4 13.025 4.23625 13.025 4.525V6Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 907 B

After

Width:  |  Height:  |  Size: 810 B

View File

@@ -22,11 +22,6 @@
"show_completions_on_input": true,
// Whether the screen sharing icon is showed in the os status bar.
"show_call_status_icon": true,
// Whether new projects should start out 'online'. Online projects
// appear in the contacts panel under your name, so that your contacts
// can see which projects you are working on. Regardless of this
// setting, projects keep their last online status when you reopen them.
"projects_online_by_default": true,
// Whether to use language servers to provide code intelligence.
"enable_language_server": true,
// When to automatically save edited buffers. This setting can
@@ -202,12 +197,6 @@
"Plain Text": {
"soft_wrap": "preferred_line_length"
},
"C": {
"tab_size": 2
},
"C++": {
"tab_size": 2
},
"Elixir": {
"tab_size": 2
},
@@ -218,9 +207,6 @@
"Markdown": {
"soft_wrap": "preferred_line_length"
},
"Rust": {
"tab_size": 4
},
"JavaScript": {
"tab_size": 2
},

View File

@@ -626,7 +626,7 @@ impl Room {
if let Some(live_kit) = this.live_kit.as_ref() {
let tracks =
live_kit.room.remote_video_tracks(&peer_id.to_string());
live_kit.room.remote_video_tracks(&user.id.to_string());
for track in tracks {
this.remote_video_track_updated(
RemoteVideoTrackUpdate::Subscribed(track),

View File

@@ -66,12 +66,12 @@ pub const ZED_SECRET_CLIENT_TOKEN: &str = "618033988749894";
pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100);
pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
actions!(client, [Authenticate, SignOut]);
actions!(client, [SignIn, SignOut]);
pub fn init(client: Arc<Client>, cx: &mut MutableAppContext) {
cx.add_global_action({
let client = client.clone();
move |_: &Authenticate, cx| {
move |_: &SignIn, cx| {
let client = client.clone();
cx.spawn(
|cx| async move { client.authenticate_and_connect(true, &cx).log_err().await },

View File

@@ -183,6 +183,11 @@ impl UserStore {
}
}
#[cfg(feature = "test-support")]
pub fn clear_cache(&mut self) {
self.users.clear();
}
async fn handle_update_invite_info(
this: ModelHandle<Self>,
message: TypedEnvelope<proto::UpdateInviteInfo>,

View File

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
default-run = "collab"
edition = "2021"
name = "collab"
version = "0.7.2"
version = "0.8.0"
publish = false
[[bin]]

View File

@@ -78,6 +78,7 @@ pub async fn validate_api_token<B>(req: Request<B>, next: Next<B>) -> impl IntoR
struct AuthenticatedUserParams {
github_user_id: Option<i32>,
github_login: String,
github_email: Option<String>,
}
#[derive(Debug, Serialize)]
@@ -92,7 +93,11 @@ async fn get_authenticated_user(
) -> Result<Json<AuthenticatedUserResponse>> {
let user = app
.db
.get_user_by_github_account(&params.github_login, params.github_user_id)
.get_or_create_user_by_github_account(
&params.github_login,
params.github_user_id,
params.github_email.as_deref(),
)
.await?
.ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "user not found".into()))?;
let metrics_id = app.db.get_user_metrics_id(user.id).await?;
@@ -297,11 +302,7 @@ async fn create_access_token(
let mut user_id = user.id;
if let Some(impersonate) = params.impersonate {
if user.admin {
if let Some(impersonated_user) = app
.db
.get_user_by_github_account(&impersonate, None)
.await?
{
if let Some(impersonated_user) = app.db.get_user_by_github_login(&impersonate).await? {
user_id = impersonated_user.id;
} else {
return Err(Error::Http(

View File

@@ -59,7 +59,7 @@ async fn main() {
for (github_user, admin) in zed_users {
if db
.get_user_by_github_account(&github_user.login, Some(github_user.id))
.get_user_by_github_login(&github_user.login)
.await
.expect("failed to fetch user")
.is_none()

View File

@@ -295,10 +295,21 @@ impl Database {
.await
}
pub async fn get_user_by_github_account(
pub async fn get_user_by_github_login(&self, github_login: &str) -> Result<Option<User>> {
self.transaction(|tx| async move {
Ok(user::Entity::find()
.filter(user::Column::GithubLogin.eq(github_login))
.one(&*tx)
.await?)
})
.await
}
pub async fn get_or_create_user_by_github_account(
&self,
github_login: &str,
github_user_id: Option<i32>,
github_email: Option<&str>,
) -> Result<Option<User>> {
self.transaction(|tx| async move {
let tx = &*tx;
@@ -320,7 +331,19 @@ impl Database {
user_by_github_login.github_user_id = ActiveValue::set(Some(github_user_id));
Ok(Some(user_by_github_login.update(tx).await?))
} else {
Ok(None)
let user = user::Entity::insert(user::ActiveModel {
email_address: ActiveValue::set(github_email.map(|email| email.into())),
github_login: ActiveValue::set(github_login.into()),
github_user_id: ActiveValue::set(Some(github_user_id)),
admin: ActiveValue::set(false),
invite_count: ActiveValue::set(0),
invite_code: ActiveValue::set(None),
metrics_id: ActiveValue::set(Uuid::new_v4()),
..Default::default()
})
.exec_with_returning(&*tx)
.await?;
Ok(Some(user))
}
} else {
Ok(user::Entity::find()

View File

@@ -92,8 +92,8 @@ test_both_dbs!(
);
test_both_dbs!(
test_get_user_by_github_account_postgres,
test_get_user_by_github_account_sqlite,
test_get_or_create_user_by_github_account_postgres,
test_get_or_create_user_by_github_account_sqlite,
db,
{
let user_id1 = db
@@ -124,7 +124,7 @@ test_both_dbs!(
.user_id;
let user = db
.get_user_by_github_account("login1", None)
.get_or_create_user_by_github_account("login1", None, None)
.await
.unwrap()
.unwrap();
@@ -133,19 +133,28 @@ test_both_dbs!(
assert_eq!(user.github_user_id, Some(101));
assert!(db
.get_user_by_github_account("non-existent-login", None)
.get_or_create_user_by_github_account("non-existent-login", None, None)
.await
.unwrap()
.is_none());
let user = db
.get_user_by_github_account("the-new-login2", Some(102))
.get_or_create_user_by_github_account("the-new-login2", Some(102), None)
.await
.unwrap()
.unwrap();
assert_eq!(user.id, user_id2);
assert_eq!(&user.github_login, "the-new-login2");
assert_eq!(user.github_user_id, Some(102));
let user = db
.get_or_create_user_by_github_account("login3", Some(103), Some("user3@example.com"))
.await
.unwrap()
.unwrap();
assert_eq!(&user.github_login, "login3");
assert_eq!(user.github_user_id, Some(103));
assert_eq!(user.email_address, Some("user3@example.com".into()));
}
);

View File

@@ -1875,7 +1875,7 @@ async fn fuzzy_search_users(
1 | 2 => session
.db()
.await
.get_user_by_github_account(&query, None)
.get_user_by_github_login(&query)
.await?
.into_iter()
.collect(),

View File

@@ -104,11 +104,7 @@ impl TestServer {
});
let http = FakeHttpClient::with_404_response();
let user_id = if let Ok(Some(user)) = self
.app_state
.db
.get_user_by_github_account(name, None)
.await
let user_id = if let Ok(Some(user)) = self.app_state.db.get_user_by_github_login(name).await
{
user.id
} else {

View File

@@ -6292,6 +6292,99 @@ async fn test_basic_following(
);
}
#[gpui::test(iterations = 10)]
async fn test_join_call_after_screen_was_shared(
deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
let mut server = TestServer::start(&deterministic).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
.make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)])
.await;
let active_call_a = cx_a.read(ActiveCall::global);
let active_call_b = cx_b.read(ActiveCall::global);
// Call users B and C from client A.
active_call_a
.update(cx_a, |call, cx| {
call.invite(client_b.user_id().unwrap(), None, cx)
})
.await
.unwrap();
let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone());
deterministic.run_until_parked();
assert_eq!(
room_participants(&room_a, cx_a),
RoomParticipants {
remote: Default::default(),
pending: vec!["user_b".to_string()]
}
);
// User B receives the call.
let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
let call_b = incoming_call_b.next().await.unwrap().unwrap();
assert_eq!(call_b.calling_user.github_login, "user_a");
// User A shares their screen
let display = MacOSDisplay::new();
active_call_a
.update(cx_a, |call, cx| {
call.room().unwrap().update(cx, |room, cx| {
room.set_display_sources(vec![display.clone()]);
room.share_screen(cx)
})
})
.await
.unwrap();
client_b.user_store.update(cx_b, |user_store, _| {
user_store.clear_cache();
});
// User B joins the room
active_call_b
.update(cx_b, |call, cx| call.accept_incoming(cx))
.await
.unwrap();
let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone());
assert!(incoming_call_b.next().await.unwrap().is_none());
deterministic.run_until_parked();
assert_eq!(
room_participants(&room_a, cx_a),
RoomParticipants {
remote: vec!["user_b".to_string()],
pending: vec![],
}
);
assert_eq!(
room_participants(&room_b, cx_b),
RoomParticipants {
remote: vec!["user_a".to_string()],
pending: vec![],
}
);
// Ensure User B sees User A's screenshare.
room_b.read_with(cx_b, |room, _| {
assert_eq!(
room.remote_participants()
.get(&client_a.user_id().unwrap())
.unwrap()
.tracks
.len(),
1
);
});
}
#[gpui::test]
async fn test_following_tab_order(
deterministic: Arc<Deterministic>,

View File

@@ -4,7 +4,7 @@ use crate::{
ToggleScreenSharing,
};
use call::{ActiveCall, ParticipantLocation, Room};
use client::{proto::PeerId, Authenticate, ContactEventKind, SignOut, User, UserStore};
use client::{proto::PeerId, ContactEventKind, SignIn, SignOut, User, UserStore};
use clock::ReplicaId;
use contacts_popover::ContactsPopover;
use context_menu::{ContextMenu, ContextMenuItem};
@@ -119,12 +119,12 @@ impl View for CollabTitlebarItem {
let status = &*status.borrow();
if matches!(status, client::Status::Connected { .. }) {
right_container.add_child(self.render_toggle_contacts_button(&theme, cx));
right_container.add_child(self.render_user_menu_button(&theme, cx));
} else {
right_container.add_children(self.render_connection_status(status, cx));
right_container.add_child(self.render_sign_in_button(&theme, cx));
}
right_container.add_child(self.render_user_menu_button(&theme, cx));
Stack::new()
.with_child(left_container.boxed())
.with_child(right_container.aligned().right().boxed())
@@ -313,7 +313,7 @@ impl CollabTitlebarItem {
vec![
ContextMenuItem::Item {
label: "Sign in".into(),
action: Box::new(Authenticate),
action: Box::new(SignIn),
},
ContextMenuItem::Item {
label: "Give Feedback".into(),
@@ -379,7 +379,7 @@ impl CollabTitlebarItem {
let style = titlebar
.toggle_contacts_button
.style_for(state, self.contacts_popover.is_some());
Svg::new("icons/plus_8.svg")
Svg::new("icons/user_plus_16.svg")
.with_color(style.color)
.constrained()
.with_width(style.icon_width)
@@ -554,6 +554,22 @@ impl CollabTitlebarItem {
.boxed()
}
fn render_sign_in_button(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> ElementBox {
let titlebar = &theme.workspace.titlebar;
MouseEventHandler::<SignIn>::new(0, cx, |state, _| {
let style = titlebar.sign_in_prompt.style_for(state, false);
Label::new("Sign In", style.text.clone())
.contained()
.with_style(style.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(SignIn);
})
.boxed()
}
fn render_contacts_popover_host<'a>(
&'a self,
theme: &'a theme::Titlebar,

View File

@@ -1,8 +1,8 @@
use crate::{contact_finder::ContactFinder, contact_list::ContactList, ToggleContactsMenu};
use client::UserStore;
use gpui::{
actions, elements::*, ClipboardItem, CursorStyle, Entity, ModelHandle, MouseButton,
MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
actions, elements::*, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, View,
ViewContext, ViewHandle,
};
use project::Project;
use settings::Settings;
@@ -98,61 +98,9 @@ impl View for ContactsPopover {
Child::ContactFinder(child) => ChildView::new(child, cx),
};
MouseEventHandler::<ContactsPopover>::new(0, cx, |_, cx| {
MouseEventHandler::<ContactsPopover>::new(0, cx, |_, _| {
Flex::column()
.with_child(child.flex(1., true).boxed())
.with_children(
self.user_store
.read(cx)
.invite_info()
.cloned()
.and_then(|info| {
enum InviteLink {}
if info.count > 0 {
Some(
MouseEventHandler::<InviteLink>::new(0, cx, |state, cx| {
let style = theme
.contacts_popover
.invite_row
.style_for(state, false)
.clone();
let copied =
cx.read_from_clipboard().map_or(false, |item| {
item.text().as_str() == info.url.as_ref()
});
Label::new(
format!(
"{} invite link ({} left)",
if copied { "Copied" } else { "Copy" },
info.count
),
style.label.clone(),
)
.aligned()
.left()
.constrained()
.with_height(theme.contacts_popover.invite_row_height)
.contained()
.with_style(style.container)
.boxed()
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.write_to_clipboard(ClipboardItem::new(
info.url.to_string(),
));
cx.notify();
})
.boxed(),
)
} else {
None
}
}),
)
.contained()
.with_style(theme.contacts_popover.container)
.constrained()

View File

@@ -2746,7 +2746,7 @@ impl Editor {
.color,
)
.constrained()
.with_width(style.icon_width)
.with_width(gutter_margin * style.icon_margin_scale)
.aligned()
.constrained()
.with_height(line_height)

View File

@@ -31,4 +31,4 @@ theme = { path = "../theme" }
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
urlencoding = "2.1.2"
util = { path = "../util" }
workspace = { path = "../workspace" }
workspace = { path = "../workspace" }

View File

@@ -104,6 +104,15 @@ impl TestServer {
room_name
))
} else {
for track in &room.tracks {
client_room
.0
.lock()
.video_track_updates
.0
.try_broadcast(RemoteVideoTrackUpdate::Subscribed(track.clone()))
.unwrap();
}
room.client_rooms.insert(identity, client_room);
Ok(())
}
@@ -167,11 +176,13 @@ impl TestServer {
.get_mut(&*room_name)
.ok_or_else(|| anyhow!("room {} does not exist", room_name))?;
let update = RemoteVideoTrackUpdate::Subscribed(Arc::new(RemoteVideoTrack {
let track = Arc::new(RemoteVideoTrack {
sid: nanoid::nanoid!(17),
publisher_id: identity.clone(),
frames_rx: local_track.frames_rx.clone(),
}));
});
room.tracks.push(track.clone());
for (id, client_room) in &room.client_rooms {
if *id != identity {
@@ -180,18 +191,30 @@ impl TestServer {
.lock()
.video_track_updates
.0
.try_broadcast(update.clone())
.try_broadcast(RemoteVideoTrackUpdate::Subscribed(track.clone()))
.unwrap();
}
}
Ok(())
}
fn video_tracks(&self, token: String) -> Result<Vec<Arc<RemoteVideoTrack>>> {
let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
let room_name = claims.video.room.unwrap();
let mut server_rooms = self.rooms.lock();
let room = server_rooms
.get_mut(&*room_name)
.ok_or_else(|| anyhow!("room {} does not exist", room_name))?;
Ok(room.tracks.clone())
}
}
#[derive(Default)]
struct TestServerRoom {
client_rooms: HashMap<Sid, Arc<Room>>,
tracks: Vec<Arc<RemoteVideoTrack>>,
}
impl TestServerRoom {}
@@ -307,8 +330,17 @@ impl Room {
pub fn unpublish_track(&self, _: LocalTrackPublication) {}
pub fn remote_video_tracks(&self, _: &str) -> Vec<Arc<RemoteVideoTrack>> {
Default::default()
pub fn remote_video_tracks(&self, publisher_id: &str) -> Vec<Arc<RemoteVideoTrack>> {
if !self.is_connected() {
return Vec::new();
}
self.test_server()
.video_tracks(self.token())
.unwrap()
.into_iter()
.filter(|track| track.publisher_id() == publisher_id)
.collect()
}
pub fn remote_video_track_updates(&self) -> impl Stream<Item = RemoteVideoTrackUpdate> {
@@ -332,6 +364,13 @@ impl Room {
ConnectionState::Connected { token, .. } => token,
}
}
fn is_connected(&self) -> bool {
match *self.0.lock().connection.1.borrow() {
ConnectionState::Disconnected => false,
ConnectionState::Connected { .. } => true,
}
}
}
impl Drop for Room {

View File

@@ -327,8 +327,6 @@ impl Column for DockAnchor {
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct SettingsFileContent {
#[serde(default)]
pub projects_online_by_default: Option<bool>,
#[serde(default)]
pub buffer_font_family: Option<String>,
#[serde(default)]

View File

@@ -1,8 +1,9 @@
use crate::{update_settings_file, watched_json::WatchedJsonFile, SettingsFileContent};
use anyhow::Result;
use assets::Assets;
use fs::Fs;
use gpui::MutableAppContext;
use std::{path::Path, sync::Arc};
use gpui::{AssetSource, MutableAppContext};
use std::{io::ErrorKind, path::Path, sync::Arc};
// TODO: Switch SettingsFile to open a worktree and buffer for synchronization
// And instant updates in the Zed editor
@@ -26,6 +27,27 @@ impl SettingsFile {
}
}
async fn load_settings(path: &Path, fs: &Arc<dyn Fs>) -> Result<String> {
match fs.load(path).await {
result @ Ok(_) => result,
Err(err) => {
if let Some(e) = err.downcast_ref::<std::io::Error>() {
if e.kind() == ErrorKind::NotFound {
return Ok(std::str::from_utf8(
Assets
.load("settings/initial_user_settings.json")
.unwrap()
.as_ref(),
)
.unwrap()
.to_string());
}
}
return Err(err);
}
}
}
pub fn update(
cx: &mut MutableAppContext,
update: impl 'static + Send + FnOnce(&mut SettingsFileContent),
@@ -39,7 +61,7 @@ impl SettingsFile {
cx.background()
.spawn(async move {
let old_text = fs.load(path).await?;
let old_text = SettingsFile::load_settings(path, &fs).await?;
let new_text = update_settings_file(old_text, current_file_content, update);

View File

@@ -15,4 +15,4 @@ thread_local = "1.1.4"
lazy_static = "1.4"
parking_lot = "0.11.1"
futures = "0.3"
uuid = { version = "1.1.2", features = ["v4"] }
uuid = { version = "1.1.2", features = ["v4"] }

View File

@@ -15,4 +15,4 @@ quote = "1.0"
proc-macro2 = "1.0"
lazy_static = "1.4"
sqlez = { path = "../sqlez" }
sqlformat = "0.2"
sqlformat = "0.2"

View File

@@ -121,8 +121,6 @@ pub struct ContactsPopover {
pub container: ContainerStyle,
pub height: f32,
pub width: f32,
pub invite_row_height: f32,
pub invite_row: Interactive<ContainedLabel>,
}
#[derive(Deserialize, Default)]
@@ -666,7 +664,7 @@ pub struct Folds {
pub indicator: Interactive<InteractiveColor>,
pub ellipses: FoldEllipses,
pub fold_background: Color,
pub icon_width: f32,
pub icon_margin_scale: f32,
pub folded_icon: String,
pub foldable_icon: String,
}

View File

@@ -24,4 +24,4 @@ theme = { path = "../theme" }
theme_selector = { path = "../theme_selector" }
util = { path = "../util" }
picker = { path = "../picker" }
workspace = { path = "../workspace" }
workspace = { path = "../workspace" }

View File

@@ -57,4 +57,4 @@ gpui = { path = "../gpui", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
fs = { path = "../fs", features = ["test-support"] }
db = { path = "../db", features = ["test-support"] }
db = { path = "../db", features = ["test-support"] }

View File

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
version = "0.77.0"
version = "0.78.1"
publish = false
[lib]

View File

@@ -1 +1 @@
dev
preview

View File

@@ -350,9 +350,7 @@ pub fn initialize_workspace(
workspace.status_bar().update(cx, |status_bar, cx| {
status_bar.add_left_item(diagnostic_summary, cx);
status_bar.add_left_item(activity_indicator, cx);
if **cx.default_global::<StaffMode>() {
status_bar.add_right_item(toggle_terminal, cx);
}
status_bar.add_right_item(toggle_terminal, cx);
status_bar.add_right_item(feedback_button, cx);
status_bar.add_right_item(active_buffer_language, cx);
status_bar.add_right_item(cursor_position, cx);

View File

@@ -8,12 +8,12 @@ open_result=false
# If -o option is specified, the folder of the resulting dmg will be opened in finder
# If -d is specified, Zed will be compiled in debug mode and the application's path printed
# If -od or -do is specified Zed will be bundled in debug and the application will be run.
# If -od or -do is specified Zed will be bundled in debug and the application will be run.
while getopts 'od' flag
do
case "${flag}" in
o) open_result=true;;
d)
d)
build_flag="";
target_dir="debug"
;;
@@ -119,12 +119,16 @@ hdiutil create -volname Zed -srcfolder "${dmg_source_directory}" -ov -format UDZ
# This symlink causes CPU issues with Zed if the Zed codebase is the project being worked on, so we simply remove it for now.
rm ${dmg_source_directory}/Applications
echo "Adding license agreement to DMG"
npm install --global dmg-license minimist
dmg-license script/terms/terms-of-use.json "${dmg_file_path}"
if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTARIZATION_USERNAME && -n $APPLE_NOTARIZATION_PASSWORD ]]; then
echo "Notarizing DMG with Apple"
npm install -g notarize-cli
npx notarize-cli --file ${dmg_file_path} --bundle-id dev.zed.Zed --username "$APPLE_NOTARIZATION_USERNAME" --password "$APPLE_NOTARIZATION_PASSWORD"
npx notarize-cli --file "${dmg_file_path}" --bundle-id dev.zed.Zed --username "$APPLE_NOTARIZATION_USERNAME" --password "$APPLE_NOTARIZATION_PASSWORD"
fi
if [ "$open_result" = true ]; then
open $dmg_target_directory
fi
fi

View File

@@ -0,0 +1,9 @@
{
"body": [
{
"lang": "en-US",
"type": "rtf",
"file": "terms-of-use.rtf"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -42,7 +42,7 @@ export default function contactsPanel(colorScheme: ColorScheme) {
return {
background: background(layer),
padding: { top: 12, bottom: 0 },
padding: { top: 12 },
userQueryEditor: {
background: background(layer, "on"),
cornerRadius: 6,

View File

@@ -7,23 +7,11 @@ export default function contactsPopover(colorScheme: ColorScheme) {
return {
background: background(layer),
cornerRadius: 6,
padding: { top: 6 },
padding: { top: 6, bottom: 6 },
margin: { top: -6 },
shadow: colorScheme.popoverShadow,
border: border(layer),
width: 300,
height: 400,
inviteRowHeight: 28,
inviteRow: {
padding: {
left: sidePadding,
right: sidePadding,
},
border: border(layer, { top: true }),
text: text(layer, "sans", "variant", { size: "sm" }),
hover: {
text: text(layer, "sans", "hovered", { size: "sm" }),
},
},
}
}

View File

@@ -60,7 +60,7 @@ export default function editor(colorScheme: ColorScheme) {
verticalScale: 0.55,
},
folds: {
iconWidth: 8,
iconMarginScale: 2.5,
foldedIcon: "icons/chevron_right_8.svg",
foldableIcon: "icons/chevron_down_8.svg",
indicator: {

View File

@@ -1,4 +1,5 @@
import { ColorScheme } from "../themes/common/colorScheme"
import { withOpacity } from "../utils/color"
import { background, border, text } from "./components"
export default function picker(colorScheme: ColorScheme) {
@@ -53,14 +54,17 @@ export default function picker(colorScheme: ColorScheme) {
text: text(layer, "sans", "variant"),
highlightText: text(layer, "sans", "accent", { weight: "bold" }),
active: {
background: background(layer, "base", "active"),
background: withOpacity(
background(layer, "base", "active"),
0.5
),
text: text(layer, "sans", "base", "active"),
highlightText: text(layer, "sans", "accent", {
weight: "bold",
}),
},
hover: {
background: background(layer, "hovered"),
background: withOpacity(background(layer, "hovered"), 0.5),
},
},
inputEditor,

View File

@@ -3,80 +3,80 @@ import { withOpacity } from "../utils/color"
import { background, border, foreground, text } from "./components"
export default function projectPanel(colorScheme: ColorScheme) {
let layer = colorScheme.middle
let layer = colorScheme.middle
let baseEntry = {
height: 24,
iconColor: foreground(layer, "variant"),
iconSize: 8,
iconSpacing: 8,
}
let baseEntry = {
height: 24,
iconColor: foreground(layer, "variant"),
iconSize: 8,
iconSpacing: 8,
}
let entry = {
...baseEntry,
text: text(layer, "mono", "variant", { size: "sm" }),
hover: {
background: background(layer, "variant", "hovered"),
},
active: {
background: background(layer, "active"),
text: text(layer, "mono", "active", { size: "sm" }),
},
activeHover: {
background: background(layer, "active"),
text: text(layer, "mono", "active", { size: "sm" }),
},
}
let entry = {
...baseEntry,
text: text(layer, "mono", "variant", { size: "sm" }),
hover: {
background: background(layer, "variant", "hovered"),
},
active: {
background: colorScheme.isLight ? withOpacity(background(layer, "active"), 0.5) : background(layer, "active"),
text: text(layer, "mono", "active", { size: "sm" }),
},
activeHover: {
background: background(layer, "active"),
text: text(layer, "mono", "active", { size: "sm" }),
},
}
return {
openProjectButton: {
background: background(layer),
border: border(layer, "active"),
cornerRadius: 4,
margin: {
top: 16,
left: 16,
right: 16,
},
padding: {
top: 3,
bottom: 3,
left: 7,
right: 7,
},
...text(layer, "sans", "default", { size: "sm" }),
hover: {
...text(layer, "sans", "default", { size: "sm" }),
background: background(layer, "hovered"),
border: border(layer, "active"),
},
},
background: background(layer),
padding: { left: 12, right: 12, top: 6, bottom: 6 },
indentWidth: 8,
entry,
draggedEntry: {
...baseEntry,
text: text(layer, "mono", "on", { size: "sm" }),
background: withOpacity(background(layer, "on"), 0.9),
border: border(layer),
},
ignoredEntry: {
...entry,
text: text(layer, "mono", "disabled"),
},
cutEntry: {
...entry,
text: text(layer, "mono", "disabled"),
active: {
background: background(layer, "active"),
text: text(layer, "mono", "disabled", { size: "sm" }),
},
},
filenameEditor: {
background: background(layer, "on"),
text: text(layer, "mono", "on", { size: "sm" }),
selection: colorScheme.players[0],
},
}
return {
openProjectButton: {
background: background(layer),
border: border(layer, "active"),
cornerRadius: 4,
margin: {
top: 16,
left: 16,
right: 16,
},
padding: {
top: 3,
bottom: 3,
left: 7,
right: 7,
},
...text(layer, "sans", "default", { size: "sm" }),
hover: {
...text(layer, "sans", "default", { size: "sm" }),
background: background(layer, "hovered"),
border: border(layer, "active"),
},
},
background: background(layer),
padding: { left: 12, right: 12, top: 6, bottom: 6 },
indentWidth: 8,
entry,
draggedEntry: {
...baseEntry,
text: text(layer, "mono", "on", { size: "sm" }),
background: withOpacity(background(layer, "on"), 0.9),
border: border(layer),
},
ignoredEntry: {
...entry,
text: text(layer, "mono", "disabled"),
},
cutEntry: {
...entry,
text: text(layer, "mono", "disabled"),
active: {
background: background(layer, "active"),
text: text(layer, "mono", "disabled", { size: "sm" }),
},
},
filenameEditor: {
background: background(layer, "on"),
text: text(layer, "mono", "on", { size: "sm" }),
selection: colorScheme.players[0],
},
}
}

View File

@@ -29,8 +29,8 @@ export default function statusBar(colorScheme: ColorScheme) {
padding: { left: 6, right: 6 },
...text(layer, "sans", "variant"),
hover: {
...text(layer, "sans", "on")
}
...text(layer, "sans", "on"),
},
},
autoUpdateProgressMessage: text(layer, "sans", "variant"),
autoUpdateDoneMessage: text(layer, "sans", "variant"),

View File

@@ -1,11 +1,15 @@
import { ColorScheme } from "../themes/common/colorScheme";
import { withOpacity } from "../utils/color";
import { border, background, foreground, text, TextProperties } from "./components";
import { ColorScheme } from "../themes/common/colorScheme"
import { withOpacity } from "../utils/color"
import {
border,
background,
foreground,
text,
TextProperties,
} from "./components"
export default function welcome(colorScheme: ColorScheme) {
let layer = colorScheme.highest;
let layer = colorScheme.highest
let checkboxBase = {
cornerRadius: 4,
@@ -20,9 +24,9 @@ export default function welcome(colorScheme: ColorScheme) {
margin: {
right: 8,
top: 5,
bottom: 5
bottom: 5,
},
};
}
let interactive_text_size: TextProperties = { size: "sm" }
@@ -34,7 +38,7 @@ export default function welcome(colorScheme: ColorScheme) {
dimensions: {
width: 64,
height: 64,
}
},
},
logoSubheading: {
...text(layer, "sans", "variant", { size: "md" }),
@@ -46,13 +50,13 @@ export default function welcome(colorScheme: ColorScheme) {
buttonGroup: {
margin: {
top: 8,
bottom: 16
bottom: 16,
},
},
headingGroup: {
margin: {
top: 8,
bottom: 12
bottom: 12,
},
},
checkboxGroup: {
@@ -62,7 +66,7 @@ export default function welcome(colorScheme: ColorScheme) {
padding: {
left: 12,
top: 2,
bottom: 2
bottom: 2,
},
},
button: {
@@ -71,7 +75,7 @@ export default function welcome(colorScheme: ColorScheme) {
cornerRadius: 4,
margin: {
top: 4,
bottom: 4
bottom: 4,
},
padding: {
top: 3,
@@ -90,8 +94,7 @@ export default function welcome(colorScheme: ColorScheme) {
...text(layer, "sans", "variant", { size: "2xs" }),
padding: {
top: -4,
}
},
},
checkboxContainer: {
margin: {
@@ -99,7 +102,7 @@ export default function welcome(colorScheme: ColorScheme) {
},
padding: {
bottom: 8,
}
},
},
checkbox: {
label: {
@@ -112,28 +115,28 @@ export default function welcome(colorScheme: ColorScheme) {
dimensions: {
width: 12,
height: 12,
}
},
},
default: {
...checkboxBase,
background: background(layer, "default"),
border: border(layer, "active")
border: border(layer, "active"),
},
checked: {
...checkboxBase,
background: background(layer, "hovered"),
border: border(layer, "active")
border: border(layer, "active"),
},
hovered: {
...checkboxBase,
background: background(layer, "hovered"),
border: border(layer, "active")
border: border(layer, "active"),
},
hoveredAndChecked: {
...checkboxBase,
background: background(layer, "hovered"),
border: border(layer, "active")
}
}
border: border(layer, "active"),
},
},
}
}

View File

@@ -55,7 +55,12 @@ export default function workspace(colorScheme: ColorScheme) {
},
},
logoShadow: {
color: withOpacity(colorScheme.isLight ? "#FFFFFF" : colorScheme.lowest.base.default.background, colorScheme.isLight ? 1 : 0.6),
color: withOpacity(
colorScheme.isLight
? "#FFFFFF"
: colorScheme.lowest.base.default.background,
colorScheme.isLight ? 1 : 0.6
),
icon: "icons/logo_96.svg",
dimensions: {
width: 256,
@@ -74,12 +79,12 @@ export default function workspace(colorScheme: ColorScheme) {
top: 3,
left: 8,
right: 8,
bottom: 3
bottom: 3,
},
cornerRadius: 8,
hover: {
...text(layer, "sans", "active", { size: "sm" }),
}
},
},
keyboardHintWidth: 320,
},
@@ -121,9 +126,9 @@ export default function workspace(colorScheme: ColorScheme) {
titlebar: {
itemSpacing,
facePileSpacing: 2,
height: 33, // 32px + 1px for overlaid border
height: 33, // 32px + 1px border. It's important the content area of the titlebar is evenly sized to vertically center avatar images.
background: background(layer),
border: border(layer, { bottom: true, overlay: true }),
border: border(layer, { bottom: true }),
padding: {
left: 80,
right: itemSpacing,
@@ -155,8 +160,8 @@ export default function workspace(colorScheme: ColorScheme) {
padding: {
left: 2,
right: 2,
top: 4,
bottom: 4,
top: 2,
bottom: 2,
},
cornerRadius: 6,
},
@@ -212,7 +217,7 @@ export default function workspace(colorScheme: ColorScheme) {
margin: { left: itemSpacing },
cornerRadius: 6,
color: foreground(layer, "variant"),
iconWidth: 8,
iconWidth: 14,
buttonWidth: 20,
active: {
background: background(layer, "variant", "active"),