From aac71fdcfef70557cfb186441cfb88e77b3c4232 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 9 Jun 2023 08:42:16 -0700 Subject: [PATCH 01/63] Remove border from sign in button Co-authored-by: Piotr --- styles/src/styleTree/workspace.ts | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index ae8de178f8..bf0c5d3af3 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -42,6 +42,33 @@ export default function workspace(colorScheme: ColorScheme) { border: border(layer, "variant", "active"), }, } + const signInButton = { + cornerRadius: 6, + padding: { + top: 1, + bottom: 1, + left: 8, + right: 8, + }, + ...text(layer, "sans", "variant", { size: "xs" }), + background: background(layer, "variant"), + //border: border(layer), + hover: { + ...text(layer, "sans", "variant", "hovered", { size: "xs" }), + background: background(layer, "variant", "hovered"), + //border: border(layer, "variant", "hovered"), + }, + clicked: { + ...text(layer, "sans", "variant", "pressed", { size: "xs" }), + background: background(layer, "variant", "pressed"), + //border: border(layer, "variant", "pressed"), + }, + active: { + ...text(layer, "sans", "variant", "active", { size: "xs" }), + background: background(layer, "variant", "active"), + //border: border(layer, "variant", "active"), + }, + } const avatarWidth = 18 const avatarOuterWidth = avatarWidth + 4 const followerAvatarWidth = 14 @@ -205,7 +232,7 @@ export default function workspace(colorScheme: ColorScheme) { margin: { left: itemSpacing, }, - ...titlebarButton, + ...signInButton, }, // Offline Indicator From 47ef800dc6e5f6391e0716717a5927d1d44c62cc Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 9 Jun 2023 09:00:38 -0700 Subject: [PATCH 02/63] WIP: Add face to right hand menu --- crates/collab_ui/src/collab_titlebar_item.rs | 33 ++++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 720a73f477..cffdb2a18f 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -92,10 +92,10 @@ impl View for CollabTitlebarItem { let status = workspace.read(cx).client().status(); 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)); + let avatar = user.as_ref().map(|user| user.avatar.clone()).flatten(); + right_container.add_child(self.render_user_menu_button(&theme, avatar, cx)); } else { right_container.add_children(self.render_connection_status(status, cx)); right_container.add_child(self.render_sign_in_button(&theme, cx)); @@ -504,24 +504,31 @@ impl CollabTitlebarItem { fn render_user_menu_button( &self, theme: &Theme, + avatar: Option>, cx: &mut ViewContext, ) -> AnyElement { let titlebar = &theme.workspace.titlebar; - + let avatar_style = &theme.workspace.titlebar.leader_avatar; Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar.call_control.style_for(state, false); - Svg::new("icons/ellipsis_14.svg") - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) - .contained() - .with_style(style.container) + + if let Some(avatar_img) = avatar { + Self::render_face(avatar_img, *avatar_style, Color::transparent_black()) + } else { + Svg::new("icons/ellipsis_14.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + .into_any() + } }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, this, cx| { From 5f7fdd25eb98e64c7d57de68b77bd74dbcc1c6a1 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 9 Jun 2023 18:05:23 +0200 Subject: [PATCH 03/63] Fix compile error (use of moved value) --- crates/collab_ui/src/collab_titlebar_item.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index cffdb2a18f..405eb20dec 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -78,6 +78,7 @@ impl View for CollabTitlebarItem { let user = self.user_store.read(cx).current_user(); let peer_id = self.client.peer_id(); if let Some(((user, peer_id), room)) = user + .as_ref() .zip(peer_id) .zip(ActiveCall::global(cx).read(cx).room().cloned()) { From 0fe65b945f6de2c66a6ab2f2371c82f79083a833 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sat, 10 Jun 2023 01:35:18 +0200 Subject: [PATCH 04/63] Remove user avatar from dropdown menu Add new options in context menu --- Cargo.lock | 1 + crates/collab_ui/Cargo.toml | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 38 +++++++------------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24fd67f90e..9bf504bc15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1325,6 +1325,7 @@ dependencies = [ "serde_derive", "settings", "theme", + "theme_selector", "util", "workspace", ] diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index 16cc6b5277..9865f8a9c8 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -37,6 +37,7 @@ picker = { path = "../picker" } project = { path = "../project" } settings = { path = "../settings" } theme = { path = "../theme" } +theme_selector = { path = "../theme_selector" } util = { path = "../util" } workspace = { path = "../workspace" } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 405eb20dec..d9127e707f 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -99,7 +99,7 @@ impl View for CollabTitlebarItem { right_container.add_child(self.render_user_menu_button(&theme, avatar, 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, None, cx)); } Stack::new() @@ -298,42 +298,28 @@ impl CollabTitlebarItem { } pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext) { - let theme = theme::current(cx).clone(); - let avatar_style = theme.workspace.titlebar.leader_avatar.clone(); - let item_style = theme.context_menu.item.disabled_style().clone(); self.user_menu.update(cx, |user_menu, cx| { - let items = if let Some(user) = self.user_store.read(cx).current_user() { + let items = if let Some(_) = self.user_store.read(cx).current_user() { vec![ - ContextMenuItem::Static(Box::new(move |_| { - Flex::row() - .with_children(user.avatar.clone().map(|avatar| { - Self::render_face( - avatar, - avatar_style.clone(), - Color::transparent_black(), - ) - })) - .with_child(Label::new( - user.github_login.clone(), - item_style.label.clone(), - )) - .contained() - .with_style(item_style.container) - .into_any() - })), - ContextMenuItem::action("Sign out", SignOut), + ContextMenuItem::action("Settings", SignIn), + ContextMenuItem::action("Theme", theme_selector::Toggle), + ContextMenuItem::separator(), ContextMenuItem::action( - "Send Feedback", + "Share Feedback", feedback::feedback_editor::GiveFeedback, ), + ContextMenuItem::action("Sign out", SignOut), ] } else { vec![ - ContextMenuItem::action("Sign in", SignIn), + ContextMenuItem::action("Settings", SignIn), + ContextMenuItem::action("Theme", theme_selector::Toggle), + ContextMenuItem::separator(), ContextMenuItem::action( - "Send Feedback", + "Share Feedback", feedback::feedback_editor::GiveFeedback, ), + ContextMenuItem::action("Sign in", SignIn), ] }; From 4b87ce8952a2954055e702ac830139baf2b7e3d2 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 12 Jun 2023 11:51:39 +0200 Subject: [PATCH 05/63] add zed-actions crate --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + crates/zed-actions/Cargo.toml | 9 +++++++++ crates/zed-actions/src/lib.rs | 14 ++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 crates/zed-actions/Cargo.toml create mode 100644 crates/zed-actions/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 9bf504bc15..13d194440c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8909,6 +8909,13 @@ dependencies = [ "workspace", ] +[[package]] +name = "zed-actions" +version = "0.1.0" +dependencies = [ + "gpui", +] + [[package]] name = "zeroize" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index fca7355964..34533da51c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ members = [ "crates/workspace", "crates/welcome", "crates/zed", + "crates/zed-actions" ] default-members = ["crates/zed"] resolver = "2" diff --git a/crates/zed-actions/Cargo.toml b/crates/zed-actions/Cargo.toml new file mode 100644 index 0000000000..c6868b1546 --- /dev/null +++ b/crates/zed-actions/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "zed-actions" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +gpui = { path = "../gpui" } diff --git a/crates/zed-actions/src/lib.rs b/crates/zed-actions/src/lib.rs new file mode 100644 index 0000000000..7d12d9af81 --- /dev/null +++ b/crates/zed-actions/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 092cf93dae709025421b296f5178963cb5d96e01 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 12 Jun 2023 13:00:08 +0200 Subject: [PATCH 06/63] Move zed actions to zed-actions --- Cargo.lock | 2 + crates/collab_ui/Cargo.toml | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 4 +- crates/zed-actions/src/lib.rs | 40 +++++++++++++------- crates/zed/Cargo.toml | 2 +- crates/zed/src/main.rs | 2 +- crates/zed/src/zed.rs | 28 +------------- 7 files changed, 35 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13d194440c..6d953d8dc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1328,6 +1328,7 @@ dependencies = [ "theme_selector", "util", "workspace", + "zed-actions", ] [[package]] @@ -8907,6 +8908,7 @@ dependencies = [ "vim", "welcome", "workspace", + "zed-actions", ] [[package]] diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index 9865f8a9c8..ee410ccba7 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -40,6 +40,7 @@ theme = { path = "../theme" } theme_selector = { path = "../theme_selector" } util = { path = "../util" } workspace = { path = "../workspace" } +zed-actions = {path = "../zed-actions"} anyhow.workspace = true futures.workspace = true diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index d9127e707f..d2a9dda480 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -301,7 +301,7 @@ impl CollabTitlebarItem { self.user_menu.update(cx, |user_menu, cx| { let items = if let Some(_) = self.user_store.read(cx).current_user() { vec![ - ContextMenuItem::action("Settings", SignIn), + ContextMenuItem::action("Settings", zed_actions::OpenSettings), ContextMenuItem::action("Theme", theme_selector::Toggle), ContextMenuItem::separator(), ContextMenuItem::action( @@ -312,7 +312,7 @@ impl CollabTitlebarItem { ] } else { vec![ - ContextMenuItem::action("Settings", SignIn), + ContextMenuItem::action("Settings", zed_actions::OpenSettings), ContextMenuItem::action("Theme", theme_selector::Toggle), ContextMenuItem::separator(), ContextMenuItem::action( diff --git a/crates/zed-actions/src/lib.rs b/crates/zed-actions/src/lib.rs index 7d12d9af81..bcd086924d 100644 --- a/crates/zed-actions/src/lib.rs +++ b/crates/zed-actions/src/lib.rs @@ -1,14 +1,28 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +use gpui::actions; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +actions!( + zed, + [ + About, + Hide, + HideOthers, + ShowAll, + Minimize, + Zoom, + ToggleFullScreen, + Quit, + DebugElements, + OpenLog, + OpenLicenses, + OpenTelemetryLog, + OpenKeymap, + OpenSettings, + OpenLocalSettings, + OpenDefaultSettings, + OpenDefaultKeymap, + IncreaseBufferFontSize, + DecreaseBufferFontSize, + ResetBufferFontSize, + ResetDatabase, + ] +); diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d8e47d1c3e..e97005ec7e 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -67,7 +67,7 @@ util = { path = "../util" } vim = { path = "../vim" } workspace = { path = "../workspace" } welcome = { path = "../welcome" } - +zed-actions = {path = "../zed-actions"} anyhow.workspace = true async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] } async-tar = "0.4.2" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index dcdf5c1ea5..5d013a6849 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -871,6 +871,6 @@ pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] { ("Go to file", &file_finder::Toggle), ("Open command palette", &command_palette::Toggle), ("Open recent projects", &recent_projects::OpenRecent), - ("Change your settings", &zed::OpenSettings), + ("Change your settings", &zed_actions::OpenSettings), ] } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index bcdfe57a46..9f3b145793 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -50,6 +50,7 @@ use workspace::{ notifications::simple_message_notification::MessageNotification, open_new, AppState, NewFile, NewWindow, Workspace, WorkspaceSettings, }; +use zed_actions::*; #[derive(Deserialize, Clone, PartialEq)] pub struct OpenBrowser { @@ -58,33 +59,6 @@ pub struct OpenBrowser { impl_actions!(zed, [OpenBrowser]); -actions!( - zed, - [ - About, - Hide, - HideOthers, - ShowAll, - Minimize, - Zoom, - ToggleFullScreen, - Quit, - DebugElements, - OpenLog, - OpenLicenses, - OpenTelemetryLog, - OpenKeymap, - OpenSettings, - OpenLocalSettings, - OpenDefaultSettings, - OpenDefaultKeymap, - IncreaseBufferFontSize, - DecreaseBufferFontSize, - ResetBufferFontSize, - ResetDatabase, - ] -); - pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { cx.add_action(about); cx.add_global_action(|_: &Hide, cx: &mut gpui::AppContext| { From 433c5d30f57c6f973b24f87ad4bd7ce1511558b1 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 12 Jun 2023 19:38:01 +0200 Subject: [PATCH 07/63] Add Sign in button for an unregistered user --- crates/collab_ui/src/collab_titlebar_item.rs | 2 +- crates/zed-actions/Cargo.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index d2a9dda480..54caf9b26f 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -99,6 +99,7 @@ impl View for CollabTitlebarItem { right_container.add_child(self.render_user_menu_button(&theme, avatar, 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, None, cx)); } @@ -319,7 +320,6 @@ impl CollabTitlebarItem { "Share Feedback", feedback::feedback_editor::GiveFeedback, ), - ContextMenuItem::action("Sign in", SignIn), ] }; diff --git a/crates/zed-actions/Cargo.toml b/crates/zed-actions/Cargo.toml index c6868b1546..b3fe3cbb53 100644 --- a/crates/zed-actions/Cargo.toml +++ b/crates/zed-actions/Cargo.toml @@ -2,6 +2,7 @@ name = "zed-actions" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 83c1bb07bbb1824419fa3428279558ee8ea77c6c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 12 Jun 2023 19:55:52 +0200 Subject: [PATCH 08/63] Add background to user avatar --- crates/collab_ui/src/collab_titlebar_item.rs | 27 +++++++++++--------- crates/zed/src/zed.rs | 5 ++-- styles/src/styleTree/workspace.ts | 4 +++ 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 54caf9b26f..de4bb3e1b5 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -495,27 +495,30 @@ impl CollabTitlebarItem { cx: &mut ViewContext, ) -> AnyElement { let titlebar = &theme.workspace.titlebar; - let avatar_style = &theme.workspace.titlebar.leader_avatar; + let avatar_style = &theme.workspace.titlebar.follower_avatar; + let active = self.user_menu.read(cx).visible(); Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar.call_control.style_for(state, false); + let style = titlebar.call_control.style_for(state, active); - if let Some(avatar_img) = avatar { + let img = if let Some(avatar_img) = avatar { Self::render_face(avatar_img, *avatar_style, Color::transparent_black()) } else { Svg::new("icons/ellipsis_14.svg") .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) - .contained() - .with_style(style.container) .into_any() - } + }; + + img.constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + .into_any() }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, this, cx| { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 9f3b145793..8dbc81f62f 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -20,7 +20,6 @@ use feedback::{ }; use futures::{channel::mpsc, StreamExt}; use gpui::{ - actions, anyhow::{self, Result}, geometry::vector::vec2f, impl_actions, @@ -715,8 +714,8 @@ mod tests { use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor}; use fs::{FakeFs, Fs}; use gpui::{ - elements::Empty, executor::Deterministic, Action, AnyElement, AppContext, AssetSource, - Element, Entity, TestAppContext, View, ViewHandle, + actions, elements::Empty, executor::Deterministic, Action, AnyElement, AppContext, + AssetSource, Element, Entity, TestAppContext, View, ViewHandle, }; use language::LanguageRegistry; use node_runtime::NodeRuntime; diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index bf0c5d3af3..15bcbcc27c 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -270,6 +270,10 @@ export default function workspace(colorScheme: ColorScheme) { background: background(layer, "variant", "hovered"), color: foreground(layer, "variant", "hovered"), }, + active: { + background: background(layer, "variant", "active"), + color: foreground(layer, "variant", "active"), + }, }, toggleContactsButton: { margin: { left: itemSpacing }, From d079a0eb2090650aff716e2f7cf4d7808032e2c6 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 12 Jun 2023 20:19:09 +0200 Subject: [PATCH 09/63] Render user avatar only if an user is not in a call --- crates/collab_ui/src/collab_titlebar_item.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index de4bb3e1b5..2100160a6f 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -85,9 +85,9 @@ impl View for CollabTitlebarItem { left_container .add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx)); - right_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx)); - right_container + left_container .add_child(self.render_current_user(&workspace, &theme, &user, peer_id, cx)); + left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx)); right_container.add_child(self.render_toggle_screen_sharing_button(&theme, &room, cx)); } @@ -95,7 +95,13 @@ 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)); - let avatar = user.as_ref().map(|user| user.avatar.clone()).flatten(); + let avatar = ActiveCall::global(cx) + .read(cx) + .room() + .is_none() + .then(|| user.as_ref()) + .flatten() + .and_then(|user| user.avatar.clone()); right_container.add_child(self.render_user_menu_button(&theme, avatar, cx)); } else { right_container.add_children(self.render_connection_status(status, cx)); From ae68be64c05903c6231197322876a396f76b619b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 13 Jun 2023 12:51:11 +0200 Subject: [PATCH 10/63] Move share button to the right hand side --- crates/collab_ui/src/collab_titlebar_item.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 2100160a6f..797a4f7066 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -82,7 +82,7 @@ impl View for CollabTitlebarItem { .zip(peer_id) .zip(ActiveCall::global(cx).read(cx).room().cloned()) { - left_container + right_container .add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx)); left_container From dedc117cca57dd2b52d1171b28435759de2ae4f3 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 16 Jun 2023 10:13:27 -0700 Subject: [PATCH 11/63] Add audio APIs to Zed Share mic on joining room --- crates/call/src/participant.rs | 4 +- crates/call/src/room.rs | 199 ++++++++++++++++--- crates/collab/src/tests/integration_tests.rs | 6 +- crates/collab_ui/src/contact_list.rs | 4 +- crates/live_kit_client/src/test.rs | 1 + crates/workspace/src/workspace.rs | 2 +- 6 files changed, 186 insertions(+), 30 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index 4b9e7ba034..90f256489d 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -3,6 +3,7 @@ use client::{proto, User}; use collections::HashMap; use gpui::WeakModelHandle; pub use live_kit_client::Frame; +use live_kit_client::RemoteAudioTrack; use project::Project; use std::{fmt, sync::Arc}; @@ -42,7 +43,8 @@ pub struct RemoteParticipant { pub peer_id: proto::PeerId, pub projects: Vec, pub location: ParticipantLocation, - pub tracks: HashMap>, + pub video_tracks: HashMap>, + pub audio_tracks: HashMap>, } #[derive(Clone)] diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 775342359f..257a612f08 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -12,7 +12,9 @@ use fs::Fs; use futures::{FutureExt, StreamExt}; use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle}; use language::LanguageRegistry; -use live_kit_client::{LocalTrackPublication, LocalVideoTrack, RemoteVideoTrackUpdate}; +use live_kit_client::{ + LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, RemoteVideoTrackUpdate, RemoteAudioTrackUpdate, +}; use postage::stream::Stream; use project::Project; use std::{future::Future, mem, pin::Pin, sync::Arc, time::Duration}; @@ -28,6 +30,9 @@ pub enum Event { RemoteVideoTracksChanged { participant_id: proto::PeerId, }, + RemoteAudioTracksChanged { + participant_id: proto::PeerId, + }, RemoteProjectShared { owner: Arc, project_id: u64, @@ -112,9 +117,9 @@ impl Room { } }); - let mut track_changes = room.remote_video_track_updates(); - let _maintain_tracks = cx.spawn_weak(|this, mut cx| async move { - while let Some(track_change) = track_changes.next().await { + let mut track_video_changes = room.remote_video_track_updates(); + let _maintain_video_tracks = cx.spawn_weak(|this, mut cx| async move { + while let Some(track_change) = track_video_changes.next().await { let this = if let Some(this) = this.upgrade(&cx) { this } else { @@ -127,16 +132,32 @@ impl Room { } }); + let mut track_audio_changes = room.remote_audio_track_updates(); + let _maintain_audio_tracks = cx.spawn_weak(|this, mut cx| async move { + while let Some(track_change) = track_audio_changes.next().await { + let this = if let Some(this) = this.upgrade(&cx) { + this + } else { + break; + }; + + this.update(&mut cx, |this, cx| { + this.remote_audio_track_updated(track_change, cx).log_err() + }); + } + }); + cx.foreground() .spawn(room.connect(&connection_info.server_url, &connection_info.token)) .detach_and_log_err(cx); Some(LiveKitRoom { room, - screen_track: ScreenTrack::None, + screen_track: Track::None, + microphone_track: Track::None, next_publish_id: 0, _maintain_room, - _maintain_tracks, + _maintain_tracks: [_maintain_video_tracks, _maintain_audio_tracks], }) } else { None @@ -197,6 +218,12 @@ impl Room { None }; + let share_mic = room.update(&mut cx, |room, cx| { + room.share_mic(cx) + }); + + cx.background().spawn(share_mic).detach(); + match room .update(&mut cx, |room, cx| { room.leave_when_empty = true; @@ -618,20 +645,26 @@ impl Room { peer_id, projects: participant.projects, location, - tracks: Default::default(), + video_tracks: Default::default(), + audio_tracks: Default::default(), }, ); if let Some(live_kit) = this.live_kit.as_ref() { - let tracks = + let video_tracks = live_kit.room.remote_video_tracks(&user.id.to_string()); - for track in tracks { + let audio_tracks = live_kit.room.remote_audio_tracks(&user.id.to_string()); + for track in video_tracks { this.remote_video_track_updated( RemoteVideoTrackUpdate::Subscribed(track), cx, ) .log_err(); } + for track in audio_tracks { + this.remote_audio_track_updated(RemoteAudioTrackUpdate::Subscribed(track), cx) + .log_err(); + } } } } @@ -706,7 +739,7 @@ impl Room { .remote_participants .get_mut(&user_id) .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; - participant.tracks.insert( + participant.video_tracks.insert( track_id.clone(), Arc::new(RemoteVideoTrack { live_kit_track: track, @@ -725,7 +758,7 @@ impl Room { .remote_participants .get_mut(&user_id) .ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?; - participant.tracks.remove(&track_id); + participant.video_tracks.remove(&track_id); cx.emit(Event::RemoteVideoTracksChanged { participant_id: participant.peer_id, }); @@ -736,6 +769,47 @@ impl Room { Ok(()) } + fn remote_audio_track_updated( + &mut self, + change: RemoteAudioTrackUpdate, + cx: &mut ModelContext, + ) -> Result<()> { + match change { + RemoteAudioTrackUpdate::Subscribed(track) => { + let user_id = track.publisher_id().parse()?; + let track_id = track.sid().to_string(); + let participant = self + .remote_participants + .get_mut(&user_id) + .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; + participant.audio_tracks.insert( + track_id.clone(), + track, + ); + cx.emit(Event::RemoteAudioTracksChanged { + participant_id: participant.peer_id, + }); + } + RemoteAudioTrackUpdate::Unsubscribed { + publisher_id, + track_id, + } => { + let user_id = publisher_id.parse()?; + let participant = self + .remote_participants + .get_mut(&user_id) + .ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?; + participant.audio_tracks.remove(&track_id); + cx.emit(Event::RemoteAudioTracksChanged { + participant_id: participant.peer_id, + }); + } + } + + cx.notify(); + Ok(()) + } + fn check_invariants(&self) { #[cfg(any(test, feature = "test-support"))] { @@ -908,7 +982,85 @@ impl Room { pub fn is_screen_sharing(&self) -> bool { self.live_kit.as_ref().map_or(false, |live_kit| { - !matches!(live_kit.screen_track, ScreenTrack::None) + !matches!(live_kit.screen_track, Track::None) + }) + } + + pub fn is_sharing_mic(&self) -> bool { + self.live_kit.as_ref().map_or(false, |live_kit| { + !matches!(live_kit.microphone_track, Track::None) + }) + } + + pub fn share_mic(&mut self, cx: &mut ModelContext) -> Task> { + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } else if self.is_sharing_mic() { + return Task::ready(Err(anyhow!("microphone was already shared"))); + } + + let publish_id = if let Some(live_kit) = self.live_kit.as_mut() { + let publish_id = post_inc(&mut live_kit.next_publish_id); + live_kit.microphone_track = Track::Pending { publish_id }; + cx.notify(); + publish_id + } else { + return Task::ready(Err(anyhow!("live-kit was not initialized"))); + }; + + cx.spawn_weak(|this, mut cx| async move { + let publish_track = async { + let track = LocalAudioTrack::create(); + this.upgrade(&cx) + .ok_or_else(|| anyhow!("room was dropped"))? + .read_with(&cx, |this, _| { + this.live_kit + .as_ref() + .map(|live_kit| live_kit.room.publish_audio_track(&track)) + }) + .ok_or_else(|| anyhow!("live-kit was not initialized"))? + .await + }; + + let publication = publish_track.await; + this.upgrade(&cx) + .ok_or_else(|| anyhow!("room was dropped"))? + .update(&mut cx, |this, cx| { + let live_kit = this + .live_kit + .as_mut() + .ok_or_else(|| anyhow!("live-kit was not initialized"))?; + + let canceled = if let Track::Pending { + publish_id: cur_publish_id, + } = &live_kit.microphone_track + { + *cur_publish_id != publish_id + } else { + true + }; + + match publication { + Ok(publication) => { + if canceled { + live_kit.room.unpublish_track(publication); + } else { + live_kit.microphone_track = Track::Published(publication); + cx.notify(); + } + Ok(()) + } + Err(error) => { + if canceled { + Ok(()) + } else { + live_kit.microphone_track = Track::None; + cx.notify(); + Err(error) + } + } + } + }) }) } @@ -921,7 +1073,7 @@ impl Room { let (displays, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() { let publish_id = post_inc(&mut live_kit.next_publish_id); - live_kit.screen_track = ScreenTrack::Pending { publish_id }; + live_kit.screen_track = Track::Pending { publish_id }; cx.notify(); (live_kit.room.display_sources(), publish_id) } else { @@ -955,7 +1107,7 @@ impl Room { .as_mut() .ok_or_else(|| anyhow!("live-kit was not initialized"))?; - let canceled = if let ScreenTrack::Pending { + let canceled = if let Track::Pending { publish_id: cur_publish_id, } = &live_kit.screen_track { @@ -969,7 +1121,7 @@ impl Room { if canceled { live_kit.room.unpublish_track(publication); } else { - live_kit.screen_track = ScreenTrack::Published(publication); + live_kit.screen_track = Track::Published(publication); cx.notify(); } Ok(()) @@ -978,7 +1130,7 @@ impl Room { if canceled { Ok(()) } else { - live_kit.screen_track = ScreenTrack::None; + live_kit.screen_track = Track::None; cx.notify(); Err(error) } @@ -998,12 +1150,12 @@ impl Room { .as_mut() .ok_or_else(|| anyhow!("live-kit was not initialized"))?; match mem::take(&mut live_kit.screen_track) { - ScreenTrack::None => Err(anyhow!("screen was not shared")), - ScreenTrack::Pending { .. } => { + Track::None => Err(anyhow!("screen was not shared")), + Track::Pending { .. } => { cx.notify(); Ok(()) } - ScreenTrack::Published(track) => { + Track::Published(track) => { live_kit.room.unpublish_track(track); cx.notify(); Ok(()) @@ -1023,19 +1175,20 @@ impl Room { struct LiveKitRoom { room: Arc, - screen_track: ScreenTrack, + screen_track: Track, + microphone_track: Track, next_publish_id: usize, _maintain_room: Task<()>, - _maintain_tracks: Task<()>, + _maintain_tracks: [Task<()>; 2], } -enum ScreenTrack { +enum Track { None, Pending { publish_id: usize }, Published(LocalTrackPublication), } -impl Default for ScreenTrack { +impl Default for Track { fn default() -> Self { Self::None } diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 92b63478cb..b2c85f268d 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -257,7 +257,7 @@ async fn test_basic_calls( room_b.read_with(cx_b, |room, _| { assert_eq!( room.remote_participants()[&client_a.user_id().unwrap()] - .tracks + .video_tracks .len(), 1 ); @@ -274,7 +274,7 @@ async fn test_basic_calls( room_c.read_with(cx_c, |room, _| { assert_eq!( room.remote_participants()[&client_a.user_id().unwrap()] - .tracks + .video_tracks .len(), 1 ); @@ -6993,7 +6993,7 @@ async fn test_join_call_after_screen_was_shared( room.remote_participants() .get(&client_a.user_id().unwrap()) .unwrap() - .tracks + .video_tracks .len(), 1 ); diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index e8dae210c4..2dc1fe3f1b 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -514,10 +514,10 @@ impl ContactList { project_id: project.id, worktree_root_names: project.worktree_root_names.clone(), host_user_id: participant.user.id, - is_last: projects.peek().is_none() && participant.tracks.is_empty(), + is_last: projects.peek().is_none() && participant.video_tracks.is_empty(), }); } - if !participant.tracks.is_empty() { + if !participant.video_tracks.is_empty() { participant_entries.push(ContactEntry::ParticipantScreen { peer_id: participant.peer_id, is_last: true, diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index 2f89617363..232fc7cf4b 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -517,6 +517,7 @@ impl RemoteVideoTrack { } } +#[derive(Debug)] pub struct RemoteAudioTrack { sid: Sid, publisher_id: Sid, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f813635941..85f2580a9a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2750,7 +2750,7 @@ impl Workspace { let call = self.active_call()?; let room = call.read(cx).room()?.read(cx); let participant = room.remote_participant_for_peer_id(peer_id)?; - let track = participant.tracks.values().next()?.clone(); + let track = participant.video_tracks.values().next()?.clone(); let user = participant.user.clone(); for item in pane.read(cx).items_of_type::() { From 53062e84222a4cc39176c2a958344fd96c9f867c Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 16 Jun 2023 10:44:00 -0700 Subject: [PATCH 12/63] turn mic off by default in dev builds, add enviroment variable for turning it back on --- crates/call/src/room.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 257a612f08..3b76a0bb37 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -13,12 +13,13 @@ use futures::{FutureExt, StreamExt}; use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle}; use language::LanguageRegistry; use live_kit_client::{ - LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, RemoteVideoTrackUpdate, RemoteAudioTrackUpdate, + LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, RemoteAudioTrackUpdate, + RemoteVideoTrackUpdate, }; use postage::stream::Stream; use project::Project; use std::{future::Future, mem, pin::Pin, sync::Arc, time::Duration}; -use util::{post_inc, ResultExt, TryFutureExt}; +use util::{channel::ReleaseChannel, post_inc, ResultExt, TryFutureExt}; pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); @@ -218,11 +219,12 @@ impl Room { None }; - let share_mic = room.update(&mut cx, |room, cx| { - room.share_mic(cx) - }); - - cx.background().spawn(share_mic).detach(); + if option_env!("START_MIC").is_some() + || &*util::channel::RELEASE_CHANNEL != &ReleaseChannel::Dev + { + let share_mic = room.update(&mut cx, |room, cx| room.share_mic(cx)); + cx.background().spawn(share_mic).detach(); + } match room .update(&mut cx, |room, cx| { @@ -653,7 +655,8 @@ impl Room { if let Some(live_kit) = this.live_kit.as_ref() { let video_tracks = live_kit.room.remote_video_tracks(&user.id.to_string()); - let audio_tracks = live_kit.room.remote_audio_tracks(&user.id.to_string()); + let audio_tracks = + live_kit.room.remote_audio_tracks(&user.id.to_string()); for track in video_tracks { this.remote_video_track_updated( RemoteVideoTrackUpdate::Subscribed(track), @@ -662,8 +665,11 @@ impl Room { .log_err(); } for track in audio_tracks { - this.remote_audio_track_updated(RemoteAudioTrackUpdate::Subscribed(track), cx) - .log_err(); + this.remote_audio_track_updated( + RemoteAudioTrackUpdate::Subscribed(track), + cx, + ) + .log_err(); } } } @@ -782,10 +788,7 @@ impl Room { .remote_participants .get_mut(&user_id) .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; - participant.audio_tracks.insert( - track_id.clone(), - track, - ); + participant.audio_tracks.insert(track_id.clone(), track); cx.emit(Event::RemoteAudioTracksChanged { participant_id: participant.peer_id, }); From ecd2129c2f48f71af4ba2da2f7e3f6088d4062e3 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 16 Jun 2023 11:16:36 -0700 Subject: [PATCH 13/63] Add deafen and mute stubs --- crates/call/src/room.rs | 11 +++++++++++ crates/collab_ui/src/collab_ui.rs | 26 +++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 3b76a0bb37..aa0cb1f20e 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1143,6 +1143,17 @@ impl Room { }) } + pub fn toggle_mute(&mut self, cx: &mut ModelContext) -> Task> { + // https://docs.livekit.io/client/publish/ + // Should be acessible from local participant / publication + todo!(); + } + + pub fn toggle_deafen(&mut self, cx: &mut ModelContext) -> Task> { + // iterate through publications and mute (?????) + todo!(); + } + pub fn unshare_screen(&mut self, cx: &mut ModelContext) -> Result<()> { if self.status.is_offline() { return Err(anyhow!("room is offline")); diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index c0734388b1..9a426b0879 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -15,7 +15,7 @@ use gpui::{actions, AppContext, Task}; use std::sync::Arc; use workspace::AppState; -actions!(collab, [ToggleScreenSharing]); +actions!(collab, [ToggleScreenSharing, ToggleMute, ToggleDeafen]); pub fn init(app_state: &Arc, cx: &mut AppContext) { collab_titlebar_item::init(cx); @@ -27,6 +27,8 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { sharing_status_indicator::init(cx); cx.add_global_action(toggle_screen_sharing); + cx.add_global_action(toggle_mute); + cx.add_global_action(toggle_deafen); } pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) { @@ -41,3 +43,25 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) { toggle_screen_sharing.detach_and_log_err(cx); } } + +pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + let toggle_mut = room.update(cx, |room, cx| { + if room.is_sharing_mic() { + room.toggle_mute(cx) + } else { + room.share_mic(cx) + } + }); + toggle_mut.detach_and_log_err(cx); + } +} + +pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + let toggle_deafan = room.update(cx, |room, cx| { + room.toggle_deafen(cx) + }); + toggle_deafan.detach_and_log_err(cx); + } +} From 91f2f31daa16e36febd0880655bd16344bc3ef02 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 19 Jun 2023 12:06:42 +0200 Subject: [PATCH 14/63] Split git/project label into a flex row --- crates/collab_ui/src/collab_titlebar_item.rs | 62 ++++++++------------ 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 797a4f7066..919e712ad1 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -17,7 +17,7 @@ use gpui::{ AppContext, Entity, ImageData, LayoutContext, ModelHandle, SceneBuilder, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; -use project::Project; +use project::{Project, RepositoryEntry}; use std::{ops::Range, sync::Arc}; use theme::{AvatarStyle, Theme}; use util::ResultExt; @@ -193,37 +193,16 @@ impl CollabTitlebarItem { theme: Arc, cx: &ViewContext, ) -> AnyElement { - let names_and_branches = project.visible_worktrees(cx).map(|worktree| { + let mut names_and_branches = project.visible_worktrees(cx).map(|worktree| { let worktree = worktree.read(cx); (worktree.root_name(), worktree.root_git_entry()) }); - fn push_str(buffer: &mut String, index: &mut usize, str: &str) { - buffer.push_str(str); - *index += str.chars().count(); - } - - let mut indices = Vec::new(); - let mut index = 0; - let mut title = String::new(); - let mut names_and_branches = names_and_branches.peekable(); - while let Some((name, entry)) = names_and_branches.next() { - let pre_index = index; - push_str(&mut title, &mut index, name); - indices.extend((pre_index..index).into_iter()); - if let Some(branch) = entry.and_then(|entry| entry.branch()) { - push_str(&mut title, &mut index, "/"); - push_str(&mut title, &mut index, &branch); - } - if names_and_branches.peek().is_some() { - push_str(&mut title, &mut index, ", "); - if index >= MAX_TITLE_LENGTH { - title.push_str(" …"); - break; - } - } - } - + let (name, entry) = names_and_branches.next().unwrap_or(("", None)); + let branch_prepended = entry + .as_ref() + .and_then(RepositoryEntry::branch) + .map(|branch| format!("/{branch}")); let text_style = theme.workspace.titlebar.title.clone(); let item_spacing = theme.workspace.titlebar.item_spacing; @@ -234,14 +213,25 @@ impl CollabTitlebarItem { text: text_style, highlight_text: Some(highlight), }; - - Label::new(title, style) - .with_highlights(indices) - .contained() - .with_margin_right(item_spacing) - .aligned() - .left() - .into_any_named("title-with-git-information") + let mut ret = Flex::row().with_child( + Label::new(name.to_owned(), style.clone()) + .with_highlights((0..name.len()).into_iter().collect()) + .contained() + .aligned() + .left() + .into_any_named("title-project-name"), + ); + if let Some(git_branch) = branch_prepended { + ret = ret.with_child( + Label::new(git_branch, style) + .contained() + .with_margin_right(item_spacing) + .aligned() + .left() + .into_any_named("title-project-branch"), + ) + } + ret.into_any() } fn window_activation_changed(&mut self, active: bool, cx: &mut ViewContext) { From 3e3079b513699964166a00205edc7c1a1fe829d9 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 19 Jun 2023 12:07:02 +0200 Subject: [PATCH 15/63] cargo fmt --- crates/collab_ui/src/collab_ui.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 9a426b0879..d6e62d146f 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -59,9 +59,7 @@ pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) { pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - let toggle_deafan = room.update(cx, |room, cx| { - room.toggle_deafen(cx) - }); + let toggle_deafan = room.update(cx, |room, cx| room.toggle_deafen(cx)); toggle_deafan.detach_and_log_err(cx); } } From e58f0ac72fce79b4e4f45a813e4b31cee3427cf3 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 20 Jun 2023 09:32:16 -0700 Subject: [PATCH 16/63] Add mute toggling controls --- crates/call/src/room.rs | 110 +++++++++++++----- crates/collab_ui/src/collab_ui.rs | 15 +-- .../Sources/LiveKitBridge/LiveKitBridge.swift | 44 ++++--- crates/live_kit_client/src/prod.rs | 67 ++++++++++- crates/live_kit_client/src/test.rs | 14 +++ 5 files changed, 189 insertions(+), 61 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index aa0cb1f20e..b8da95bfb5 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -154,8 +154,8 @@ impl Room { Some(LiveKitRoom { room, - screen_track: Track::None, - microphone_track: Track::None, + screen_track: LocalTrack::None, + microphone_track: LocalTrack::None, next_publish_id: 0, _maintain_room, _maintain_tracks: [_maintain_video_tracks, _maintain_audio_tracks], @@ -985,13 +985,13 @@ impl Room { pub fn is_screen_sharing(&self) -> bool { self.live_kit.as_ref().map_or(false, |live_kit| { - !matches!(live_kit.screen_track, Track::None) + !matches!(live_kit.screen_track, LocalTrack::None) }) } pub fn is_sharing_mic(&self) -> bool { self.live_kit.as_ref().map_or(false, |live_kit| { - !matches!(live_kit.microphone_track, Track::None) + !matches!(live_kit.microphone_track, LocalTrack::None) }) } @@ -1004,7 +1004,10 @@ impl Room { let publish_id = if let Some(live_kit) = self.live_kit.as_mut() { let publish_id = post_inc(&mut live_kit.next_publish_id); - live_kit.microphone_track = Track::Pending { publish_id }; + live_kit.microphone_track = LocalTrack::Pending { + publish_id, + muted: false, + }; cx.notify(); publish_id } else { @@ -1034,13 +1037,14 @@ impl Room { .as_mut() .ok_or_else(|| anyhow!("live-kit was not initialized"))?; - let canceled = if let Track::Pending { + let (canceled, muted) = if let LocalTrack::Pending { publish_id: cur_publish_id, + muted } = &live_kit.microphone_track { - *cur_publish_id != publish_id + (*cur_publish_id != publish_id, *muted) } else { - true + (true, false) }; match publication { @@ -1048,7 +1052,13 @@ impl Room { if canceled { live_kit.room.unpublish_track(publication); } else { - live_kit.microphone_track = Track::Published(publication); + if muted { + cx.background().spawn(publication.mute()).detach(); + } + live_kit.microphone_track = LocalTrack::Published { + track_publication: publication, + muted + }; cx.notify(); } Ok(()) @@ -1057,7 +1067,7 @@ impl Room { if canceled { Ok(()) } else { - live_kit.microphone_track = Track::None; + live_kit.microphone_track = LocalTrack::None; cx.notify(); Err(error) } @@ -1076,7 +1086,10 @@ impl Room { let (displays, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() { let publish_id = post_inc(&mut live_kit.next_publish_id); - live_kit.screen_track = Track::Pending { publish_id }; + live_kit.screen_track = LocalTrack::Pending { + publish_id, + muted: false, + }; cx.notify(); (live_kit.room.display_sources(), publish_id) } else { @@ -1110,13 +1123,14 @@ impl Room { .as_mut() .ok_or_else(|| anyhow!("live-kit was not initialized"))?; - let canceled = if let Track::Pending { + let (canceled, muted) = if let LocalTrack::Pending { publish_id: cur_publish_id, + muted, } = &live_kit.screen_track { - *cur_publish_id != publish_id + (*cur_publish_id != publish_id, *muted) } else { - true + (true, false) }; match publication { @@ -1124,7 +1138,13 @@ impl Room { if canceled { live_kit.room.unpublish_track(publication); } else { - live_kit.screen_track = Track::Published(publication); + if muted { + cx.background().spawn(publication.mute()).detach(); + } + live_kit.screen_track = LocalTrack::Published { + track_publication: publication, + muted, + }; cx.notify(); } Ok(()) @@ -1133,7 +1153,7 @@ impl Room { if canceled { Ok(()) } else { - live_kit.screen_track = Track::None; + live_kit.screen_track = LocalTrack::None; cx.notify(); Err(error) } @@ -1143,13 +1163,33 @@ impl Room { }) } - pub fn toggle_mute(&mut self, cx: &mut ModelContext) -> Task> { - // https://docs.livekit.io/client/publish/ - // Should be acessible from local participant / publication - todo!(); + pub fn toggle_mute(&mut self, cx: &mut ModelContext) -> Result>> { + if let Some(live_kit) = self.live_kit.as_mut() { + match &mut live_kit.microphone_track { + LocalTrack::None => Err(anyhow!("microphone was not shared")), + LocalTrack::Pending { muted, .. } => { + *muted = !*muted; + Ok(Task::Ready(Some(Ok(())))) + } + LocalTrack::Published { + track_publication, + muted, + } => { + *muted = !*muted; + + if *muted { + Ok(cx.background().spawn(track_publication.mute())) + } else { + Ok(cx.background().spawn(track_publication.unmute())) + } + } + } + } else { + Err(anyhow!("LiveKit not started")) + } } - pub fn toggle_deafen(&mut self, cx: &mut ModelContext) -> Task> { + pub fn toggle_deafen(&mut self, _cx: &mut ModelContext) -> Task> { // iterate through publications and mute (?????) todo!(); } @@ -1164,13 +1204,15 @@ impl Room { .as_mut() .ok_or_else(|| anyhow!("live-kit was not initialized"))?; match mem::take(&mut live_kit.screen_track) { - Track::None => Err(anyhow!("screen was not shared")), - Track::Pending { .. } => { + LocalTrack::None => Err(anyhow!("screen was not shared")), + LocalTrack::Pending { .. } => { cx.notify(); Ok(()) } - Track::Published(track) => { - live_kit.room.unpublish_track(track); + LocalTrack::Published { + track_publication, .. + } => { + live_kit.room.unpublish_track(track_publication); cx.notify(); Ok(()) } @@ -1189,20 +1231,26 @@ impl Room { struct LiveKitRoom { room: Arc, - screen_track: Track, - microphone_track: Track, + screen_track: LocalTrack, + microphone_track: LocalTrack, next_publish_id: usize, _maintain_room: Task<()>, _maintain_tracks: [Task<()>; 2], } -enum Track { +enum LocalTrack { None, - Pending { publish_id: usize }, - Published(LocalTrackPublication), + Pending { + publish_id: usize, + muted: bool, + }, + Published { + track_publication: LocalTrackPublication, + muted: bool, + }, } -impl Default for Track { +impl Default for LocalTrack { fn default() -> Self { Self::None } diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index d6e62d146f..9de8cbf9dd 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -9,9 +9,10 @@ mod notifications; mod project_shared_notification; mod sharing_status_indicator; -use call::ActiveCall; +use call::{ActiveCall, Room}; pub use collab_titlebar_item::{CollabTitlebarItem, ToggleContactsMenu}; use gpui::{actions, AppContext, Task}; +use util::ResultExt; use std::sync::Arc; use workspace::AppState; @@ -46,20 +47,12 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) { pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - let toggle_mut = room.update(cx, |room, cx| { - if room.is_sharing_mic() { - room.toggle_mute(cx) - } else { - room.share_mic(cx) - } - }); - toggle_mut.detach_and_log_err(cx); + room.update(cx, Room::toggle_mute).map(Task::detach).log_err(); } } pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - let toggle_deafan = room.update(cx, |room, cx| room.toggle_deafen(cx)); - toggle_deafan.detach_and_log_err(cx); + room.update(cx, Room::toggle_deafen).detach_and_log_err(cx); } } diff --git a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift index 40c1319e8f..d28dc828f1 100644 --- a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift +++ b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift @@ -201,19 +201,6 @@ public func LKCreateScreenShareTrackForDisplay(display: UnsafeMutableRawPointer) return Unmanaged.passRetained(track).toOpaque() } -@_cdecl("LKRemoteAudioTrackStart") -public func LKRemoteAudioTrackStart(track: UnsafeRawPointer, onStart: @escaping @convention(c) (UnsafeRawPointer, Bool) -> Void, callbackData: UnsafeRawPointer) { - let track = Unmanaged.fromOpaque(track).takeUnretainedValue() as! RemoteAudioTrack - - track.start().then { success in - onStart(callbackData, success) - } - .catch { _ in - onStart(callbackData, false) - } -} - - @_cdecl("LKVideoRendererCreate") public func LKVideoRendererCreate(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) -> UnsafeMutableRawPointer { Unmanaged.passRetained(LKVideoRenderer(data: data, onFrame: onFrame, onDrop: onDrop)).toOpaque() @@ -247,3 +234,34 @@ public func LKDisplaySources(data: UnsafeRawPointer, callback: @escaping @conven callback(data, nil, error.localizedDescription as CFString) } } + +@_cdecl("LKLocalTrackPublicationMute") +public func LKLocalTrackPublicationMute( + publication: UnsafeRawPointer, + on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, + callback_data: UnsafeRawPointer +) { + let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() + + publication.mute().then { + on_complete(callback_data, nil) + }.catch { error in + on_complete(callback_data, error.localizedDescription as CFString) + } + +} + +@_cdecl("LKLocalTrackPublicationUnmute") +public func LKLocalTrackPublicationUnmute( + publication: UnsafeRawPointer, + on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, + callback_data: UnsafeRawPointer +) { + let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() + + publication.unmute().then { + on_complete(callback_data, nil) + }.catch { error in + on_complete(callback_data, error.localizedDescription as CFString) + } +} diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index 6ae493d306..46516d49be 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -84,12 +84,6 @@ extern "C" { ) -> *const c_void; fn LKRemoteAudioTrackGetSid(track: *const c_void) -> CFStringRef; - // fn LKRemoteAudioTrackStart( - // track: *const c_void, - // callback: extern "C" fn(*mut c_void, bool), - // callback_data: *mut c_void - // ); - fn LKVideoTrackAddRenderer(track: *const c_void, renderer: *const c_void); fn LKRemoteVideoTrackGetSid(track: *const c_void) -> CFStringRef; @@ -103,6 +97,17 @@ extern "C" { ); fn LKCreateScreenShareTrackForDisplay(display: *const c_void) -> *const c_void; fn LKLocalAudioTrackCreateTrack() -> *const c_void; + + fn LKLocalTrackPublicationMute( + publication: *const c_void, + on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), + callback_data: *mut c_void, + ); + fn LKLocalTrackPublicationUnmute( + publication: *const c_void, + on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), + callback_data: *mut c_void, + ); } pub type Sid = String; @@ -525,6 +530,56 @@ impl Drop for LocalVideoTrack { pub struct LocalTrackPublication(*const c_void); +impl LocalTrackPublication { + pub fn mute(&self) -> impl Future> { + let (tx, rx) = futures::channel::oneshot::channel(); + + extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) { + let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender>) }; + if error.is_null() { + tx.send(Ok(())).ok(); + } else { + let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; + tx.send(Err(anyhow!(error))).ok(); + } + } + + unsafe { + LKLocalTrackPublicationMute( + self.0, + complete_callback, + Box::into_raw(Box::new(tx)) as *mut c_void, + ) + } + + async move { rx.await.unwrap() } + } + + pub fn unmute(&self) -> impl Future> { + let (tx, rx) = futures::channel::oneshot::channel(); + + extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) { + let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender>) }; + if error.is_null() { + tx.send(Ok(())).ok(); + } else { + let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; + tx.send(Err(anyhow!(error))).ok(); + } + } + + unsafe { + LKLocalTrackPublicationUnmute( + self.0, + complete_callback, + Box::into_raw(Box::new(tx)) as *mut c_void, + ) + } + + async move { rx.await.unwrap() } + } +} + impl Drop for LocalTrackPublication { fn drop(&mut self) { unsafe { CFRelease(self.0) } diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index 232fc7cf4b..21211ce473 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -475,6 +475,20 @@ impl Drop for Room { pub struct LocalTrackPublication; +impl LocalTrackPublication { + pub fn mute(&self) -> impl Future> { + async { + Ok(()) + } + } + + pub fn unmute(&self) -> impl Future> { + async { + Ok(()) + } + } +} + #[derive(Clone)] pub struct LocalVideoTrack { frames_rx: async_broadcast::Receiver, From b828a74ad66e6b1367d98263f8cd4e144fbe26c8 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 20 Jun 2023 12:34:32 -0700 Subject: [PATCH 17/63] Add deafen controls --- crates/call/src/room.rs | 49 ++++++-- crates/collab_ui/src/collab_titlebar_item.rs | 2 +- crates/collab_ui/src/collab_ui.rs | 4 +- crates/gpui/src/executor.rs | 6 +- .../Sources/LiveKitBridge/LiveKitBridge.swift | 46 +++++-- crates/live_kit_client/src/prod.rs | 112 +++++++++++++----- crates/live_kit_client/src/test.rs | 43 +++++-- 7 files changed, 197 insertions(+), 65 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index b8da95bfb5..613a52a908 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -157,6 +157,7 @@ impl Room { screen_track: LocalTrack::None, microphone_track: LocalTrack::None, next_publish_id: 0, + deafened: false, _maintain_room, _maintain_tracks: [_maintain_video_tracks, _maintain_audio_tracks], }) @@ -223,7 +224,9 @@ impl Room { || &*util::channel::RELEASE_CHANNEL != &ReleaseChannel::Dev { let share_mic = room.update(&mut cx, |room, cx| room.share_mic(cx)); - cx.background().spawn(share_mic).detach(); + cx.update(|cx| { + cx.background().spawn(share_mic).detach_and_log_err(cx); + }); } match room @@ -1039,7 +1042,7 @@ impl Room { let (canceled, muted) = if let LocalTrack::Pending { publish_id: cur_publish_id, - muted + muted, } = &live_kit.microphone_track { (*cur_publish_id != publish_id, *muted) @@ -1053,11 +1056,11 @@ impl Room { live_kit.room.unpublish_track(publication); } else { if muted { - cx.background().spawn(publication.mute()).detach(); + cx.background().spawn(publication.set_mute(muted)).detach(); } live_kit.microphone_track = LocalTrack::Published { track_publication: publication, - muted + muted, }; cx.notify(); } @@ -1139,7 +1142,7 @@ impl Room { live_kit.room.unpublish_track(publication); } else { if muted { - cx.background().spawn(publication.mute()).detach(); + cx.background().spawn(publication.set_mute(muted)).detach(); } live_kit.screen_track = LocalTrack::Published { track_publication: publication, @@ -1177,11 +1180,7 @@ impl Room { } => { *muted = !*muted; - if *muted { - Ok(cx.background().spawn(track_publication.mute())) - } else { - Ok(cx.background().spawn(track_publication.unmute())) - } + Ok(cx.background().spawn(track_publication.set_mute(*muted))) } } } else { @@ -1189,9 +1188,32 @@ impl Room { } } - pub fn toggle_deafen(&mut self, _cx: &mut ModelContext) -> Task> { - // iterate through publications and mute (?????) - todo!(); + pub fn toggle_deafen(&mut self, cx: &mut ModelContext) -> Result>> { + if let Some(live_kit) = &mut self.live_kit { + (*live_kit).deafened = !live_kit.deafened + } + + if let Some(live_kit) = &self.live_kit { + let mut tasks = Vec::with_capacity(self.remote_participants.len()); + + for participant in self.remote_participants.values() { + for track in live_kit + .room + .remote_audio_track_publications(&participant.user.id.to_string()) + { + tasks.push(cx.background().spawn(track.set_enabled(live_kit.deafened))); + } + } + + Ok(cx.background().spawn(async move { + for task in tasks { + task.await?; + } + Ok(()) + })) + } else { + Err(anyhow!("LiveKit not started")) + } } pub fn unshare_screen(&mut self, cx: &mut ModelContext) -> Result<()> { @@ -1233,6 +1255,7 @@ struct LiveKitRoom { room: Arc, screen_track: LocalTrack, microphone_track: LocalTrack, + deafened: bool, next_publish_id: usize, _maintain_room: Task<()>, _maintain_tracks: [Task<()>; 2], diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 919e712ad1..87fd7470d6 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -23,7 +23,7 @@ use theme::{AvatarStyle, Theme}; use util::ResultExt; use workspace::{FollowNextCollaborator, Workspace}; -const MAX_TITLE_LENGTH: usize = 75; +// const MAX_TITLE_LENGTH: usize = 75; actions!( collab, diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 9de8cbf9dd..940700a526 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -47,12 +47,12 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) { pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - room.update(cx, Room::toggle_mute).map(Task::detach).log_err(); + room.update(cx, Room::toggle_mute).map(|task| task.detach_and_log_err(cx)).log_err(); } } pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - room.update(cx, Room::toggle_deafen).detach_and_log_err(cx); + room.update(cx, Room::toggle_deafen).map(|task| task.detach_and_log_err(cx)).log_err(); } } diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index 365766fb9d..0923826a09 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -12,7 +12,7 @@ use std::{ sync::Arc, task::{Context, Poll}, thread, - time::Duration, + time::Duration, panic::Location, }; use crate::{ @@ -965,10 +965,12 @@ impl Task { } impl Task> { + #[track_caller] pub fn detach_and_log_err(self, cx: &mut AppContext) { cx.spawn(|_| async move { if let Err(err) = self.await { - log::error!("{:#}", err); + let caller = Location::caller(); + log::error!("{}:{}: {:#}", caller.file(), caller.line(), err); } }) .detach(); diff --git a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift index d28dc828f1..666da3d533 100644 --- a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift +++ b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift @@ -169,6 +169,18 @@ public func LKRoomAudioTracksForRemoteParticipant(room: UnsafeRawPointer, partic return nil; } +@_cdecl("LKRoomAudioTrackPublicationsForRemoteParticipant") +public func LKRoomAudioTrackPublicationsForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + + for (_, participant) in room.remoteParticipants { + if participant.identity == participantId as String { + return participant.audioTracks.compactMap { $0 as? RemoteTrackPublication } as CFArray? + } + } + + return nil; +} @_cdecl("LKRoomVideoTracksForRemoteParticipant") public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { @@ -235,33 +247,45 @@ public func LKDisplaySources(data: UnsafeRawPointer, callback: @escaping @conven } } -@_cdecl("LKLocalTrackPublicationMute") -public func LKLocalTrackPublicationMute( +@_cdecl("LKLocalTrackPublicationSetMute") +public func LKLocalTrackPublicationSetMute( publication: UnsafeRawPointer, + muted: Bool, on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, callback_data: UnsafeRawPointer ) { let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - publication.mute().then { - on_complete(callback_data, nil) - }.catch { error in - on_complete(callback_data, error.localizedDescription as CFString) + if muted { + publication.mute().then { + on_complete(callback_data, nil) + }.catch { error in + on_complete(callback_data, error.localizedDescription as CFString) + } + } else { + publication.unmute().then { + on_complete(callback_data, nil) + }.catch { error in + on_complete(callback_data, error.localizedDescription as CFString) + } } - } -@_cdecl("LKLocalTrackPublicationUnmute") -public func LKLocalTrackPublicationUnmute( +@_cdecl("LKRemoteTrackPublicationSetEnabled") +public func LKRemoteTrackPublicationSetEnabled( publication: UnsafeRawPointer, + enabled: Bool, on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, callback_data: UnsafeRawPointer ) { - let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() + let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - publication.unmute().then { + publication.set(enabled: enabled).then { on_complete(callback_data, nil) }.catch { error in on_complete(callback_data, error.localizedDescription as CFString) } } + + + diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index 46516d49be..76f06b562f 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -72,6 +72,11 @@ extern "C" { participant_id: CFStringRef, ) -> CFArrayRef; + fn LKRoomAudioTrackPublicationsForRemoteParticipant( + room: *const c_void, + participant_id: CFStringRef, + ) -> CFArrayRef; + fn LKRoomVideoTracksForRemoteParticipant( room: *const c_void, participant_id: CFStringRef, @@ -98,13 +103,16 @@ extern "C" { fn LKCreateScreenShareTrackForDisplay(display: *const c_void) -> *const c_void; fn LKLocalAudioTrackCreateTrack() -> *const c_void; - fn LKLocalTrackPublicationMute( + fn LKLocalTrackPublicationSetMute( publication: *const c_void, + muted: bool, on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), callback_data: *mut c_void, ); - fn LKLocalTrackPublicationUnmute( + + fn LKRemoteTrackPublicationSetEnabled( publication: *const c_void, + enabled: bool, on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), callback_data: *mut c_void, ); @@ -318,6 +326,29 @@ impl Room { } } + pub fn remote_audio_track_publications(&self, participant_id: &str) -> Vec> { + unsafe { + let tracks = LKRoomAudioTrackPublicationsForRemoteParticipant( + self.native_room, + CFString::new(participant_id).as_concrete_TypeRef(), + ); + + if tracks.is_null() { + Vec::new() + } else { + let tracks = CFArray::wrap_under_get_rule(tracks); + tracks + .into_iter() + .map(|native_track_publication| { + let native_track_publication = *native_track_publication; + Arc::new(RemoteTrackPublication(native_track_publication)) + }) + .collect() + } + } + } + + pub fn remote_audio_track_updates(&self) -> mpsc::UnboundedReceiver { let (tx, rx) = mpsc::unbounded(); self.remote_audio_track_subscribers.lock().push(tx); @@ -531,7 +562,7 @@ impl Drop for LocalVideoTrack { pub struct LocalTrackPublication(*const c_void); impl LocalTrackPublication { - pub fn mute(&self) -> impl Future> { + pub fn set_mute(&self, muted: bool) -> impl Future> { let (tx, rx) = futures::channel::oneshot::channel(); extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) { @@ -545,32 +576,9 @@ impl LocalTrackPublication { } unsafe { - LKLocalTrackPublicationMute( - self.0, - complete_callback, - Box::into_raw(Box::new(tx)) as *mut c_void, - ) - } - - async move { rx.await.unwrap() } - } - - pub fn unmute(&self) -> impl Future> { - let (tx, rx) = futures::channel::oneshot::channel(); - - extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) { - let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender>) }; - if error.is_null() { - tx.send(Ok(())).ok(); - } else { - let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; - tx.send(Err(anyhow!(error))).ok(); - } - } - - unsafe { - LKLocalTrackPublicationUnmute( + LKLocalTrackPublicationSetMute( self.0, + muted, complete_callback, Box::into_raw(Box::new(tx)) as *mut c_void, ) @@ -586,6 +594,42 @@ impl Drop for LocalTrackPublication { } } +pub struct RemoteTrackPublication(*const c_void); + +impl RemoteTrackPublication { + pub fn set_enabled(&self, enabled: bool) -> impl Future> { + let (tx, rx) = futures::channel::oneshot::channel(); + + extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) { + let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender>) }; + if error.is_null() { + tx.send(Ok(())).ok(); + } else { + let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; + tx.send(Err(anyhow!(error))).ok(); + } + } + + unsafe { + LKRemoteTrackPublicationSetEnabled( + self.0, + enabled, + complete_callback, + Box::into_raw(Box::new(tx)) as *mut c_void, + ) + } + + async move { rx.await.unwrap() } + } +} + +impl Drop for RemoteTrackPublication { + fn drop(&mut self) { + unsafe { CFRelease(self.0) } + } +} + + #[derive(Debug)] pub struct RemoteAudioTrack { _native_track: *const c_void, @@ -612,6 +656,18 @@ impl RemoteAudioTrack { pub fn publisher_id(&self) -> &str { &self.publisher_id } + + pub fn enable(&self) -> impl Future> { + async { + Ok(()) + } + } + + pub fn disable(&self) -> impl Future> { + async { + Ok(()) + } + } } #[derive(Debug)] diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index 21211ce473..e8c5247f53 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -410,6 +410,25 @@ impl Room { .collect() } + pub fn remote_audio_track_publications( + &self, + publisher_id: &str, + ) -> Vec> { + if !self.is_connected() { + return Vec::new(); + } + + self.test_server() + .audio_tracks(self.token()) + .unwrap() + .into_iter() + .filter(|track| track.publisher_id() == publisher_id) + .map(|_track| { + Arc::new(RemoteTrackPublication {}) + }) + .collect() + } + pub fn remote_video_tracks(&self, publisher_id: &str) -> Vec> { if !self.is_connected() { return Vec::new(); @@ -476,16 +495,16 @@ impl Drop for Room { pub struct LocalTrackPublication; impl LocalTrackPublication { - pub fn mute(&self) -> impl Future> { - async { - Ok(()) - } + pub fn set_mute(&self, _mute: bool) -> impl Future> { + async { Ok(()) } } +} - pub fn unmute(&self) -> impl Future> { - async { - Ok(()) - } +pub struct RemoteTrackPublication; + +impl RemoteTrackPublication { + pub fn set_enabled(&self, _enabled: bool) -> impl Future> { + async { Ok(()) } } } @@ -545,6 +564,14 @@ impl RemoteAudioTrack { pub fn publisher_id(&self) -> &str { &self.publisher_id } + + pub fn enable(&self) -> impl Future> { + async { Ok(()) } + } + + pub fn disable(&self) -> impl Future> { + async { Ok(()) } + } } #[derive(Clone)] From dbd95e35cfa21f5e01b881eedd2251720aea20f2 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 20 Jun 2023 12:36:36 -0700 Subject: [PATCH 18/63] fmt --- crates/collab_ui/src/collab_ui.rs | 10 +++++++--- crates/gpui/src/executor.rs | 3 ++- crates/live_kit_client/src/prod.rs | 15 ++++++--------- crates/live_kit_client/src/test.rs | 6 ++---- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 940700a526..36ee58242a 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -12,8 +12,8 @@ mod sharing_status_indicator; use call::{ActiveCall, Room}; pub use collab_titlebar_item::{CollabTitlebarItem, ToggleContactsMenu}; use gpui::{actions, AppContext, Task}; -use util::ResultExt; use std::sync::Arc; +use util::ResultExt; use workspace::AppState; actions!(collab, [ToggleScreenSharing, ToggleMute, ToggleDeafen]); @@ -47,12 +47,16 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) { pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - room.update(cx, Room::toggle_mute).map(|task| task.detach_and_log_err(cx)).log_err(); + room.update(cx, Room::toggle_mute) + .map(|task| task.detach_and_log_err(cx)) + .log_err(); } } pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - room.update(cx, Room::toggle_deafen).map(|task| task.detach_and_log_err(cx)).log_err(); + room.update(cx, Room::toggle_deafen) + .map(|task| task.detach_and_log_err(cx)) + .log_err(); } } diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index 0923826a09..fb175d9d5d 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -7,12 +7,13 @@ use std::{ fmt::{self, Display}, marker::PhantomData, mem, + panic::Location, pin::Pin, rc::Rc, sync::Arc, task::{Context, Poll}, thread, - time::Duration, panic::Location, + time::Duration, }; use crate::{ diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index 76f06b562f..ed040fd5cc 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -326,7 +326,10 @@ impl Room { } } - pub fn remote_audio_track_publications(&self, participant_id: &str) -> Vec> { + pub fn remote_audio_track_publications( + &self, + participant_id: &str, + ) -> Vec> { unsafe { let tracks = LKRoomAudioTrackPublicationsForRemoteParticipant( self.native_room, @@ -348,7 +351,6 @@ impl Room { } } - pub fn remote_audio_track_updates(&self) -> mpsc::UnboundedReceiver { let (tx, rx) = mpsc::unbounded(); self.remote_audio_track_subscribers.lock().push(tx); @@ -629,7 +631,6 @@ impl Drop for RemoteTrackPublication { } } - #[derive(Debug)] pub struct RemoteAudioTrack { _native_track: *const c_void, @@ -658,15 +659,11 @@ impl RemoteAudioTrack { } pub fn enable(&self) -> impl Future> { - async { - Ok(()) - } + async { Ok(()) } } pub fn disable(&self) -> impl Future> { - async { - Ok(()) - } + async { Ok(()) } } } diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index e8c5247f53..9db57a3294 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -423,9 +423,7 @@ impl Room { .unwrap() .into_iter() .filter(|track| track.publisher_id() == publisher_id) - .map(|_track| { - Arc::new(RemoteTrackPublication {}) - }) + .map(|_track| Arc::new(RemoteTrackPublication {})) .collect() } @@ -495,7 +493,7 @@ impl Drop for Room { pub struct LocalTrackPublication; impl LocalTrackPublication { - pub fn set_mute(&self, _mute: bool) -> impl Future> { + pub fn set_mute(&self, _mute: bool) -> impl Future> { async { Ok(()) } } } From cf4251fb55f833555dba3dc6a5ef3f260281b8e5 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 20 Jun 2023 15:17:49 -0700 Subject: [PATCH 19/63] Fix deafened -> enabled mistranslation Fix mislocation of caller query in detach_and_log_error Fix incorrect wording on livekit integration Add share_mic action for manually enabling the microphone Make mic sharing wait until the room has been fully established --- crates/call/src/room.rs | 24 +++++++++++++----------- crates/collab_ui/src/collab_ui.rs | 9 ++++++++- crates/gpui/src/executor.rs | 2 +- crates/live_kit_client/src/prod.rs | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 613a52a908..b673a0a874 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -220,15 +220,6 @@ impl Room { None }; - if option_env!("START_MIC").is_some() - || &*util::channel::RELEASE_CHANNEL != &ReleaseChannel::Dev - { - let share_mic = room.update(&mut cx, |room, cx| room.share_mic(cx)); - cx.update(|cx| { - cx.background().spawn(share_mic).detach_and_log_err(cx); - }); - } - match room .update(&mut cx, |room, cx| { room.leave_when_empty = true; @@ -236,7 +227,18 @@ impl Room { }) .await { - Ok(()) => Ok(room), + Ok(()) => { + if option_env!("START_MIC").is_some() + || &*util::channel::RELEASE_CHANNEL != &ReleaseChannel::Dev + { + let share_mic = room.update(&mut cx, |room, cx| room.share_mic(cx)); + cx.update(|cx| { + cx.background().spawn(share_mic).detach_and_log_err(cx); + }); + } + + Ok(room) + }, Err(error) => Err(anyhow!("room creation failed: {:?}", error)), } }) @@ -1201,7 +1203,7 @@ impl Room { .room .remote_audio_track_publications(&participant.user.id.to_string()) { - tasks.push(cx.background().spawn(track.set_enabled(live_kit.deafened))); + tasks.push(cx.background().spawn(track.set_enabled(!live_kit.deafened))); } } diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 36ee58242a..4b5deaa0be 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use util::ResultExt; use workspace::AppState; -actions!(collab, [ToggleScreenSharing, ToggleMute, ToggleDeafen]); +actions!(collab, [ToggleScreenSharing, ToggleMute, ToggleDeafen, ShareMic]); pub fn init(app_state: &Arc, cx: &mut AppContext) { collab_titlebar_item::init(cx); @@ -30,6 +30,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { cx.add_global_action(toggle_screen_sharing); cx.add_global_action(toggle_mute); cx.add_global_action(toggle_deafen); + cx.add_global_action(share_mic); } pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) { @@ -60,3 +61,9 @@ pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) { .log_err(); } } + +pub fn share_mic(_: &ShareMic, cx: &mut AppContext) { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + room.update(cx, Room::share_mic).detach_and_log_err(cx) + } +} diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index fb175d9d5d..712c854488 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -968,9 +968,9 @@ impl Task { impl Task> { #[track_caller] pub fn detach_and_log_err(self, cx: &mut AppContext) { + let caller = Location::caller(); cx.spawn(|_| async move { if let Err(err) = self.await { - let caller = Location::caller(); log::error!("{}:{}: {:#}", caller.file(), caller.line(), err); } }) diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index ed040fd5cc..2ec4f96dd5 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -259,7 +259,7 @@ impl Room { Box::into_raw(Box::new(tx)) as *mut c_void, ); } - async { rx.await.unwrap().context("error publishing video track") } + async { rx.await.unwrap().context("error publishing audio track") } } pub fn unpublish_track(&self, publication: LocalTrackPublication) { From 8273e08d4a064158b28710c79ba87015318628c7 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 20 Jun 2023 15:23:06 -0700 Subject: [PATCH 20/63] Add is_muted and is_deafened --- crates/call/src/room.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index b673a0a874..a2ef659156 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1000,6 +1000,22 @@ impl Room { }) } + pub fn is_muted(&self) -> Option { + self.live_kit.as_ref().and_then(|live_kit| { + match &live_kit.microphone_track { + LocalTrack::None => None, + LocalTrack::Pending { muted, .. } => Some(*muted), + LocalTrack::Published { muted, .. } => Some(*muted), + } + }) + } + + pub fn is_deafened(&self) -> Option { + self.live_kit.as_ref().map(|live_kit| { + live_kit.deafened + }) + } + pub fn share_mic(&mut self, cx: &mut ModelContext) -> Task> { if self.status.is_offline() { return Task::ready(Err(anyhow!("room is offline"))); From 9b77cafd7b4e9a65f31bf1006751097a5b32c3ae Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 20 Jun 2023 15:35:19 -0700 Subject: [PATCH 21/63] Add a mic sharing to room joining --- crates/call/src/room.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index a2ef659156..9f9a64c1ac 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -238,7 +238,7 @@ impl Room { } Ok(room) - }, + } Err(error) => Err(anyhow!("room creation failed: {:?}", error)), } }) @@ -266,6 +266,14 @@ impl Room { room.update(&mut cx, |room, cx| { room.leave_when_empty = true; room.apply_room_update(room_proto, cx)?; + + if option_env!("START_MIC").is_some() + || &*util::channel::RELEASE_CHANNEL != &ReleaseChannel::Dev + { + let share_mic = room.share_mic(cx); + cx.background().spawn(share_mic).detach_and_log_err(cx); + } + anyhow::Ok(()) })?; Ok(room) @@ -1001,19 +1009,17 @@ impl Room { } pub fn is_muted(&self) -> Option { - self.live_kit.as_ref().and_then(|live_kit| { - match &live_kit.microphone_track { + self.live_kit + .as_ref() + .and_then(|live_kit| match &live_kit.microphone_track { LocalTrack::None => None, LocalTrack::Pending { muted, .. } => Some(*muted), LocalTrack::Published { muted, .. } => Some(*muted), - } - }) + }) } pub fn is_deafened(&self) -> Option { - self.live_kit.as_ref().map(|live_kit| { - live_kit.deafened - }) + self.live_kit.as_ref().map(|live_kit| live_kit.deafened) } pub fn share_mic(&mut self, cx: &mut ModelContext) -> Task> { From 5ffe74f47e2a51d0f8e3ede963896157d2c1f215 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 21 Jun 2023 08:50:33 -0700 Subject: [PATCH 22/63] Fix race condition in sharing mic on connection co-authored-by: antonio --- crates/call/src/room.rs | 32 ++++++------------- crates/collab_ui/src/collab_ui.rs | 8 ++--- .../Sources/LiveKitBridge/LiveKitBridge.swift | 9 ++---- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 9f9a64c1ac..22e59ac467 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -148,8 +148,13 @@ impl Room { } }); - cx.foreground() - .spawn(room.connect(&connection_info.server_url, &connection_info.token)) + let connect = room.connect(&connection_info.server_url, &connection_info.token); + cx.spawn(|this, mut cx| async move { + connect.await?; + this.update(&mut cx, |this, cx| this.share_microphone(cx)).await?; + + anyhow::Ok(()) + }) .detach_and_log_err(cx); Some(LiveKitRoom { @@ -228,15 +233,6 @@ impl Room { .await { Ok(()) => { - if option_env!("START_MIC").is_some() - || &*util::channel::RELEASE_CHANNEL != &ReleaseChannel::Dev - { - let share_mic = room.update(&mut cx, |room, cx| room.share_mic(cx)); - cx.update(|cx| { - cx.background().spawn(share_mic).detach_and_log_err(cx); - }); - } - Ok(room) } Err(error) => Err(anyhow!("room creation failed: {:?}", error)), @@ -266,14 +262,6 @@ impl Room { room.update(&mut cx, |room, cx| { room.leave_when_empty = true; room.apply_room_update(room_proto, cx)?; - - if option_env!("START_MIC").is_some() - || &*util::channel::RELEASE_CHANNEL != &ReleaseChannel::Dev - { - let share_mic = room.share_mic(cx); - cx.background().spawn(share_mic).detach_and_log_err(cx); - } - anyhow::Ok(()) })?; Ok(room) @@ -1022,7 +1010,7 @@ impl Room { self.live_kit.as_ref().map(|live_kit| live_kit.deafened) } - pub fn share_mic(&mut self, cx: &mut ModelContext) -> Task> { + pub fn share_microphone(&mut self, cx: &mut ModelContext) -> Task> { if self.status.is_offline() { return Task::ready(Err(anyhow!("room is offline"))); } else if self.is_sharing_mic() { @@ -1225,11 +1213,11 @@ impl Room { .room .remote_audio_track_publications(&participant.user.id.to_string()) { - tasks.push(cx.background().spawn(track.set_enabled(!live_kit.deafened))); + tasks.push(cx.foreground().spawn(track.set_enabled(!live_kit.deafened))); } } - Ok(cx.background().spawn(async move { + Ok(cx.foreground().spawn(async move { for task in tasks { task.await?; } diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 4b5deaa0be..e9c2bcf7f9 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use util::ResultExt; use workspace::AppState; -actions!(collab, [ToggleScreenSharing, ToggleMute, ToggleDeafen, ShareMic]); +actions!(collab, [ToggleScreenSharing, ToggleMute, ToggleDeafen, ShareMicrophone]); pub fn init(app_state: &Arc, cx: &mut AppContext) { collab_titlebar_item::init(cx); @@ -30,7 +30,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { cx.add_global_action(toggle_screen_sharing); cx.add_global_action(toggle_mute); cx.add_global_action(toggle_deafen); - cx.add_global_action(share_mic); + cx.add_global_action(share_microphone); } pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) { @@ -62,8 +62,8 @@ pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) { } } -pub fn share_mic(_: &ShareMic, cx: &mut AppContext) { +pub fn share_microphone(_: &ShareMicrophone, cx: &mut AppContext) { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - room.update(cx, Room::share_mic).detach_and_log_err(cx) + room.update(cx, Room::share_microphone).detach_and_log_err(cx) } } diff --git a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift index 666da3d533..74d43d7865 100644 --- a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift +++ b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift @@ -10,7 +10,7 @@ class LKRoomDelegate: RoomDelegate { var onDidUnsubscribeFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void var onDidSubscribeToRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void var onDidUnsubscribeFromRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void - + init( data: UnsafeRawPointer, onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void, @@ -40,7 +40,7 @@ class LKRoomDelegate: RoomDelegate { self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque()) } } - + func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) { if track.kind == .video { self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString) @@ -279,13 +279,10 @@ public func LKRemoteTrackPublicationSetEnabled( callback_data: UnsafeRawPointer ) { let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - + publication.set(enabled: enabled).then { on_complete(callback_data, nil) }.catch { error in on_complete(callback_data, error.localizedDescription as CFString) } } - - - From 05c97ed355b3c255f4322c5823c05a96c43a1b26 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 21 Jun 2023 09:05:36 -0700 Subject: [PATCH 23/63] Fix segfault with remote track publications co-authored-by: antonio --- crates/live_kit_client/src/prod.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index 2ec4f96dd5..dbd9e1917e 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -219,7 +219,7 @@ impl Room { let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender>) }; if error.is_null() { - let _ = tx.send(Ok(LocalTrackPublication(publication))); + let _ = tx.send(Ok(LocalTrackPublication::new(publication))); } else { let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; let _ = tx.send(Err(anyhow!(error))); @@ -245,7 +245,7 @@ impl Room { let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender>) }; if error.is_null() { - let _ = tx.send(Ok(LocalTrackPublication(publication))); + let _ = tx.send(Ok(LocalTrackPublication::new(publication))); } else { let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; let _ = tx.send(Err(anyhow!(error))); @@ -344,7 +344,7 @@ impl Room { .into_iter() .map(|native_track_publication| { let native_track_publication = *native_track_publication; - Arc::new(RemoteTrackPublication(native_track_publication)) + Arc::new(RemoteTrackPublication::new(native_track_publication)) }) .collect() } @@ -564,6 +564,13 @@ impl Drop for LocalVideoTrack { pub struct LocalTrackPublication(*const c_void); impl LocalTrackPublication { + pub fn new(native_track_publication: *const c_void) -> Self { + unsafe { + CFRetain(native_track_publication); + } + Self(native_track_publication) + } + pub fn set_mute(&self, muted: bool) -> impl Future> { let (tx, rx) = futures::channel::oneshot::channel(); @@ -599,6 +606,13 @@ impl Drop for LocalTrackPublication { pub struct RemoteTrackPublication(*const c_void); impl RemoteTrackPublication { + pub fn new(native_track_publication: *const c_void) -> Self { + unsafe { + CFRetain(native_track_publication); + } + Self(native_track_publication) + } + pub fn set_enabled(&self, enabled: bool) -> impl Future> { let (tx, rx) = futures::channel::oneshot::channel(); From 4583d22ede058bce747142df33fc2f93625fa3ba Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:49:36 +0200 Subject: [PATCH 24/63] cargo fmt --- crates/call/src/room.rs | 15 +++++++-------- crates/collab_ui/src/collab_ui.rs | 13 +++++++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 22e59ac467..77f22ea2ca 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -150,12 +150,13 @@ impl Room { let connect = room.connect(&connection_info.server_url, &connection_info.token); cx.spawn(|this, mut cx| async move { - connect.await?; - this.update(&mut cx, |this, cx| this.share_microphone(cx)).await?; + connect.await?; + this.update(&mut cx, |this, cx| this.share_microphone(cx)) + .await?; - anyhow::Ok(()) - }) - .detach_and_log_err(cx); + anyhow::Ok(()) + }) + .detach_and_log_err(cx); Some(LiveKitRoom { room, @@ -232,9 +233,7 @@ impl Room { }) .await { - Ok(()) => { - Ok(room) - } + Ok(()) => Ok(room), Err(error) => Err(anyhow!("room creation failed: {:?}", error)), } }) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index e9c2bcf7f9..38a39762f4 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -16,7 +16,15 @@ use std::sync::Arc; use util::ResultExt; use workspace::AppState; -actions!(collab, [ToggleScreenSharing, ToggleMute, ToggleDeafen, ShareMicrophone]); +actions!( + collab, + [ + ToggleScreenSharing, + ToggleMute, + ToggleDeafen, + ShareMicrophone + ] +); pub fn init(app_state: &Arc, cx: &mut AppContext) { collab_titlebar_item::init(cx); @@ -64,6 +72,7 @@ pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) { pub fn share_microphone(_: &ShareMicrophone, cx: &mut AppContext) { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - room.update(cx, Room::share_microphone).detach_and_log_err(cx) + room.update(cx, Room::share_microphone) + .detach_and_log_err(cx) } } From 85d1c89d34868b9de3fec5f7e5166e5f368fce53 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:52:21 +0200 Subject: [PATCH 25/63] Remove unused import --- crates/call/src/room.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 77f22ea2ca..0c0855fd6c 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -19,7 +19,7 @@ use live_kit_client::{ use postage::stream::Stream; use project::Project; use std::{future::Future, mem, pin::Pin, sync::Arc, time::Duration}; -use util::{channel::ReleaseChannel, post_inc, ResultExt, TryFutureExt}; +use util::{post_inc, ResultExt, TryFutureExt}; pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); From bdccdaca1c679dcd3bd8cdedf17d65cce8d77fe4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:57:41 +0200 Subject: [PATCH 26/63] Remove unused variable --- crates/collab_ui/src/collab_titlebar_item.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 80af71f06e..740af0254d 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -505,7 +505,6 @@ impl CollabTitlebarItem { ) -> AnyElement { let titlebar = &theme.workspace.titlebar; let avatar_style = &theme.workspace.titlebar.follower_avatar; - let active = self.user_menu.read(cx).visible(); Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, _| { From bee6c591546d8a86220fa92a9a56e6bd8724b4cf Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 22 Jun 2023 16:28:05 +0200 Subject: [PATCH 27/63] Add caret next to user avatar --- crates/collab_ui/src/collab_titlebar_item.rs | 27 ++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 740af0254d..1d7884fcb7 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -510,19 +510,26 @@ impl CollabTitlebarItem { MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar.call_control.style_for(state); - let img = if let Some(avatar_img) = avatar { - Self::render_face(avatar_img, *avatar_style, Color::transparent_black()) - } else { - Svg::new("icons/ellipsis_14.svg") - .with_color(style.color) - .into_any() - }; + let mut dropdown = Flex::row().align_children_center(); - img.constrained() - .with_width(style.icon_width) + if let Some(avatar_img) = avatar { + dropdown = dropdown.with_child(Self::render_face( + avatar_img, + *avatar_style, + Color::transparent_black(), + )); + }; + dropdown + .with_child( + Svg::new("icons/caret_down_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .contained() + .into_any(), + ) .aligned() .constrained() - .with_width(style.button_width) .with_height(style.button_width) .contained() .with_style(style.container) From b16b6dcec7ac8852d93a88028d6ddf11d5eb60e4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 22 Jun 2023 17:40:29 +0200 Subject: [PATCH 28/63] Render microphone toggle --- assets/icons/microphone_active_12.svg | 4 ++ assets/icons/microphone_inactive_12.svg | 5 ++ crates/collab_ui/src/collab_titlebar_item.rs | 54 ++++++++++++++++++-- 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 assets/icons/microphone_active_12.svg create mode 100644 assets/icons/microphone_inactive_12.svg diff --git a/assets/icons/microphone_active_12.svg b/assets/icons/microphone_active_12.svg new file mode 100644 index 0000000000..797e64820b --- /dev/null +++ b/assets/icons/microphone_active_12.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/microphone_inactive_12.svg b/assets/icons/microphone_inactive_12.svg new file mode 100644 index 0000000000..90ac393fc2 --- /dev/null +++ b/assets/icons/microphone_inactive_12.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 1d7884fcb7..c0603421bc 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,6 +1,6 @@ use crate::{ - contact_notification::ContactNotification, contacts_popover, face_pile::FacePile, - toggle_screen_sharing, ToggleScreenSharing, + contact_notification::ContactNotification, contacts_popover, face_pile::FacePile, toggle_mute, + toggle_screen_sharing, ToggleMute, ToggleScreenSharing, }; use call::{ActiveCall, ParticipantLocation, Room}; use client::{proto::PeerId, Client, ContactEventKind, SignIn, SignOut, User, UserStore}; @@ -88,6 +88,7 @@ impl View for CollabTitlebarItem { left_container .add_child(self.render_current_user(&workspace, &theme, &user, peer_id, cx)); left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx)); + right_container.add_child(self.render_toggle_microphone(&theme, &room, cx)); right_container.add_child(self.render_toggle_screen_sharing_button(&theme, &room, cx)); } @@ -396,7 +397,6 @@ impl CollabTitlebarItem { .with_children(self.render_contacts_popover_host(titlebar, cx)) .into_any() } - fn render_toggle_screen_sharing_button( &self, theme: &Theme, @@ -441,6 +441,54 @@ impl CollabTitlebarItem { .aligned() .into_any() } + fn render_toggle_microphone( + &self, + theme: &Theme, + room: &ModelHandle, + cx: &mut ViewContext, + ) -> AnyElement { + let icon; + let tooltip; + let background; + if room.read(cx).is_muted().unwrap_or(false) { + icon = "icons/microphone_inactive_12.svg"; + tooltip = "Unmute microphone\nRight click for options"; + background = Color::red(); + } else { + icon = "icons/microphone_active_12.svg"; + tooltip = "Mute microphone\nRight click for options"; + background = Color::transparent_black(); + } + + let titlebar = &theme.workspace.titlebar; + MouseEventHandler::::new(0, cx, |state, _| { + let style = titlebar.call_control.style_for(state); + Svg::new(icon) + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + .with_background_color(background) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, _, cx| { + toggle_mute(&Default::default(), cx) + }) + .with_tooltip::( + 0, + tooltip.into(), + Some(Box::new(ToggleMute)), + theme.tooltip.clone(), + cx, + ) + .aligned() + .into_any() + } fn render_in_call_share_unshare_button( &self, From 05730afdf0d12945ad63534809d419db37a59d95 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 22 Jun 2023 11:58:51 -0400 Subject: [PATCH 29/63] Add radix icons There are a lot of them, but they add less than 350kb to the app --- assets/icons/radix/accessibility.svg | 8 ++ assets/icons/radix/activity-log.svg | 8 ++ assets/icons/radix/align-baseline.svg | 8 ++ assets/icons/radix/align-bottom.svg | 8 ++ .../icons/radix/align-center-horizontally.svg | 8 ++ .../icons/radix/align-center-vertically.svg | 8 ++ assets/icons/radix/align-center.svg | 8 ++ assets/icons/radix/align-end.svg | 8 ++ .../icons/radix/align-horizontal-centers.svg | 8 ++ assets/icons/radix/align-left.svg | 8 ++ assets/icons/radix/align-right.svg | 8 ++ assets/icons/radix/align-start.svg | 8 ++ assets/icons/radix/align-stretch.svg | 8 ++ assets/icons/radix/align-top.svg | 8 ++ assets/icons/radix/align-vertical-centers.svg | 8 ++ assets/icons/radix/all-sides.svg | 8 ++ assets/icons/radix/angle.svg | 8 ++ assets/icons/radix/archive.svg | 8 ++ assets/icons/radix/arrow-bottom-left.svg | 8 ++ assets/icons/radix/arrow-bottom-right.svg | 8 ++ assets/icons/radix/arrow-down.svg | 8 ++ assets/icons/radix/arrow-left.svg | 8 ++ assets/icons/radix/arrow-right.svg | 8 ++ assets/icons/radix/arrow-top-left.svg | 8 ++ assets/icons/radix/arrow-top-right.svg | 8 ++ assets/icons/radix/arrow-up.svg | 8 ++ assets/icons/radix/aspect-ratio.svg | 8 ++ assets/icons/radix/avatar.svg | 8 ++ assets/icons/radix/backpack.svg | 8 ++ assets/icons/radix/badge.svg | 8 ++ assets/icons/radix/bar-chart.svg | 8 ++ assets/icons/radix/bell.svg | 8 ++ assets/icons/radix/blending-mode.svg | 8 ++ assets/icons/radix/bookmark-filled.svg | 8 ++ assets/icons/radix/bookmark.svg | 8 ++ assets/icons/radix/border-all.svg | 17 ++++ assets/icons/radix/border-bottom.svg | 29 +++++++ assets/icons/radix/border-dashed.svg | 8 ++ assets/icons/radix/border-dotted.svg | 8 ++ assets/icons/radix/border-left.svg | 29 +++++++ assets/icons/radix/border-none.svg | 35 +++++++++ assets/icons/radix/border-right.svg | 29 +++++++ assets/icons/radix/border-solid.svg | 8 ++ assets/icons/radix/border-split.svg | 21 +++++ assets/icons/radix/border-style.svg | 8 ++ assets/icons/radix/border-top.svg | 29 +++++++ assets/icons/radix/border-width.svg | 8 ++ assets/icons/radix/box-model.svg | 8 ++ assets/icons/radix/box.svg | 8 ++ assets/icons/radix/button.svg | 8 ++ assets/icons/radix/calendar.svg | 8 ++ assets/icons/radix/camera.svg | 8 ++ assets/icons/radix/card-stack-minus.svg | 8 ++ assets/icons/radix/card-stack-plus.svg | 8 ++ assets/icons/radix/card-stack.svg | 8 ++ assets/icons/radix/caret-down.svg | 8 ++ assets/icons/radix/caret-left.svg | 8 ++ assets/icons/radix/caret-right.svg | 8 ++ assets/icons/radix/caret-sort.svg | 8 ++ assets/icons/radix/caret-up.svg | 8 ++ assets/icons/radix/chat-bubble.svg | 8 ++ assets/icons/radix/check-circled.svg | 8 ++ assets/icons/radix/check.svg | 8 ++ assets/icons/radix/checkbox.svg | 8 ++ assets/icons/radix/chevron-down.svg | 8 ++ assets/icons/radix/chevron-left.svg | 8 ++ assets/icons/radix/chevron-right.svg | 8 ++ assets/icons/radix/chevron-up.svg | 8 ++ assets/icons/radix/circle-backslash.svg | 8 ++ assets/icons/radix/circle.svg | 8 ++ assets/icons/radix/clipboard-copy.svg | 8 ++ assets/icons/radix/clipboard.svg | 8 ++ assets/icons/radix/clock.svg | 8 ++ assets/icons/radix/code.svg | 8 ++ assets/icons/radix/codesandbox-logo.svg | 8 ++ assets/icons/radix/color-wheel.svg | 8 ++ assets/icons/radix/column-spacing.svg | 8 ++ assets/icons/radix/columns.svg | 8 ++ assets/icons/radix/commit.svg | 8 ++ assets/icons/radix/component-1.svg | 8 ++ assets/icons/radix/component-2.svg | 8 ++ assets/icons/radix/component-boolean.svg | 8 ++ assets/icons/radix/component-instance.svg | 8 ++ assets/icons/radix/component-none.svg | 8 ++ assets/icons/radix/component-placeholder.svg | 12 +++ assets/icons/radix/container.svg | 8 ++ assets/icons/radix/cookie.svg | 8 ++ assets/icons/radix/copy.svg | 8 ++ assets/icons/radix/corner-bottom-left.svg | 8 ++ assets/icons/radix/corner-bottom-right.svg | 8 ++ assets/icons/radix/corner-top-left.svg | 8 ++ assets/icons/radix/corner-top-right.svg | 8 ++ assets/icons/radix/corners.svg | 8 ++ assets/icons/radix/countdown-timer.svg | 8 ++ .../icons/radix/counter-clockwise-clock.svg | 8 ++ assets/icons/radix/crop.svg | 8 ++ assets/icons/radix/cross-1.svg | 8 ++ assets/icons/radix/cross-2.svg | 8 ++ assets/icons/radix/cross-circled.svg | 8 ++ assets/icons/radix/crosshair-1.svg | 8 ++ assets/icons/radix/crosshair-2.svg | 8 ++ assets/icons/radix/crumpled-paper.svg | 8 ++ assets/icons/radix/cube.svg | 8 ++ assets/icons/radix/cursor-arrow.svg | 8 ++ assets/icons/radix/cursor-text.svg | 8 ++ assets/icons/radix/dash.svg | 8 ++ assets/icons/radix/dashboard.svg | 8 ++ assets/icons/radix/desktop.svg | 8 ++ assets/icons/radix/dimensions.svg | 8 ++ assets/icons/radix/disc.svg | 8 ++ assets/icons/radix/discord-logo.svg | 13 ++++ assets/icons/radix/divider-horizontal.svg | 8 ++ assets/icons/radix/divider-vertical.svg | 8 ++ assets/icons/radix/dot-filled.svg | 6 ++ assets/icons/radix/dot-solid.svg | 6 ++ assets/icons/radix/dot.svg | 8 ++ assets/icons/radix/dots-horizontal.svg | 8 ++ assets/icons/radix/dots-vertical.svg | 8 ++ assets/icons/radix/double-arrow-down.svg | 8 ++ assets/icons/radix/double-arrow-left.svg | 8 ++ assets/icons/radix/double-arrow-right.svg | 8 ++ assets/icons/radix/double-arrow-up.svg | 8 ++ assets/icons/radix/download.svg | 8 ++ assets/icons/radix/drag-handle-dots-1.svg | 26 +++++++ assets/icons/radix/drag-handle-dots-2.svg | 8 ++ assets/icons/radix/drag-handle-horizontal.svg | 8 ++ assets/icons/radix/drag-handle-vertical.svg | 8 ++ assets/icons/radix/drawing-pin-filled.svg | 14 ++++ assets/icons/radix/drawing-pin-solid.svg | 14 ++++ assets/icons/radix/drawing-pin.svg | 8 ++ assets/icons/radix/dropdown-menu.svg | 8 ++ assets/icons/radix/enter-full-screen.svg | 8 ++ assets/icons/radix/enter.svg | 8 ++ assets/icons/radix/envelope-closed.svg | 8 ++ assets/icons/radix/envelope-open.svg | 8 ++ assets/icons/radix/eraser.svg | 8 ++ assets/icons/radix/exclamation-triangle.svg | 8 ++ assets/icons/radix/exit-full-screen.svg | 8 ++ assets/icons/radix/exit.svg | 8 ++ assets/icons/radix/external-link.svg | 8 ++ assets/icons/radix/eye-closed.svg | 8 ++ assets/icons/radix/eye-none.svg | 8 ++ assets/icons/radix/eye-open.svg | 8 ++ assets/icons/radix/face.svg | 8 ++ assets/icons/radix/figma-logo.svg | 8 ++ assets/icons/radix/file-minus.svg | 8 ++ assets/icons/radix/file-plus.svg | 8 ++ assets/icons/radix/file-text.svg | 8 ++ assets/icons/radix/file.svg | 8 ++ assets/icons/radix/font-bold.svg | 6 ++ assets/icons/radix/font-family.svg | 6 ++ assets/icons/radix/font-italic.svg | 8 ++ assets/icons/radix/font-roman.svg | 8 ++ assets/icons/radix/font-size.svg | 8 ++ assets/icons/radix/font-style.svg | 8 ++ assets/icons/radix/frame.svg | 8 ++ assets/icons/radix/framer-logo.svg | 8 ++ assets/icons/radix/gear.svg | 8 ++ assets/icons/radix/github-logo.svg | 8 ++ assets/icons/radix/globe.svg | 26 +++++++ assets/icons/radix/grid.svg | 8 ++ assets/icons/radix/group.svg | 8 ++ assets/icons/radix/half-1.svg | 8 ++ assets/icons/radix/half-2.svg | 8 ++ assets/icons/radix/hamburger-menu.svg | 8 ++ assets/icons/radix/hand.svg | 8 ++ assets/icons/radix/heading.svg | 8 ++ assets/icons/radix/heart-filled.svg | 8 ++ assets/icons/radix/heart.svg | 8 ++ assets/icons/radix/height.svg | 8 ++ assets/icons/radix/hobby-knife.svg | 8 ++ assets/icons/radix/home.svg | 8 ++ assets/icons/radix/iconjar-logo.svg | 8 ++ assets/icons/radix/id-card.svg | 8 ++ assets/icons/radix/image.svg | 8 ++ assets/icons/radix/info-circled.svg | 8 ++ assets/icons/radix/inner-shadow.svg | 78 +++++++++++++++++++ assets/icons/radix/input.svg | 8 ++ assets/icons/radix/instagram-logo.svg | 8 ++ assets/icons/radix/justify-center.svg | 8 ++ assets/icons/radix/justify-end.svg | 8 ++ assets/icons/radix/justify-start.svg | 8 ++ assets/icons/radix/justify-stretch.svg | 8 ++ assets/icons/radix/keyboard.svg | 7 ++ assets/icons/radix/lap-timer.svg | 8 ++ assets/icons/radix/laptop.svg | 8 ++ assets/icons/radix/layers.svg | 8 ++ assets/icons/radix/layout.svg | 8 ++ assets/icons/radix/letter-case-capitalize.svg | 8 ++ assets/icons/radix/letter-case-lowercase.svg | 8 ++ assets/icons/radix/letter-case-toggle.svg | 8 ++ assets/icons/radix/letter-case-uppercase.svg | 8 ++ assets/icons/radix/letter-spacing.svg | 8 ++ assets/icons/radix/lightning-bolt.svg | 8 ++ assets/icons/radix/line-height.svg | 8 ++ assets/icons/radix/link-1.svg | 8 ++ assets/icons/radix/link-2.svg | 8 ++ assets/icons/radix/link-break-1.svg | 8 ++ assets/icons/radix/link-break-2.svg | 8 ++ assets/icons/radix/link-none-1.svg | 8 ++ assets/icons/radix/link-none-2.svg | 8 ++ assets/icons/radix/linkedin-logo.svg | 8 ++ assets/icons/radix/list-bullet.svg | 8 ++ assets/icons/radix/lock-closed.svg | 8 ++ assets/icons/radix/lock-open-1.svg | 8 ++ assets/icons/radix/lock-open-2.svg | 8 ++ assets/icons/radix/loop.svg | 8 ++ assets/icons/radix/magic-wand.svg | 8 ++ assets/icons/radix/magnifying-glass.svg | 8 ++ assets/icons/radix/margin.svg | 8 ++ assets/icons/radix/mask-off.svg | 8 ++ assets/icons/radix/mask-on.svg | 8 ++ assets/icons/radix/minus-circled.svg | 8 ++ assets/icons/radix/minus.svg | 8 ++ assets/icons/radix/mix.svg | 8 ++ assets/icons/radix/mixer-horizontal.svg | 8 ++ assets/icons/radix/mixer-vertical.svg | 8 ++ assets/icons/radix/mobile.svg | 8 ++ assets/icons/radix/modulz-logo.svg | 8 ++ assets/icons/radix/moon.svg | 8 ++ assets/icons/radix/move.svg | 8 ++ assets/icons/radix/notion-logo.svg | 6 ++ assets/icons/radix/opacity.svg | 8 ++ assets/icons/radix/open-in-new-window.svg | 10 +++ assets/icons/radix/outer-shadow.svg | 43 ++++++++++ assets/icons/radix/overline.svg | 8 ++ assets/icons/radix/padding.svg | 8 ++ assets/icons/radix/paper-plane.svg | 8 ++ assets/icons/radix/pause.svg | 8 ++ assets/icons/radix/pencil-1.svg | 8 ++ assets/icons/radix/pencil-2.svg | 8 ++ assets/icons/radix/person.svg | 8 ++ assets/icons/radix/pie-chart.svg | 8 ++ assets/icons/radix/pilcrow.svg | 8 ++ assets/icons/radix/pin-bottom.svg | 8 ++ assets/icons/radix/pin-left.svg | 8 ++ assets/icons/radix/pin-right.svg | 8 ++ assets/icons/radix/pin-top.svg | 8 ++ assets/icons/radix/play.svg | 8 ++ assets/icons/radix/plus-circled.svg | 8 ++ assets/icons/radix/plus.svg | 8 ++ assets/icons/radix/question-mark-circled.svg | 8 ++ assets/icons/radix/question-mark.svg | 8 ++ assets/icons/radix/quote.svg | 8 ++ assets/icons/radix/radiobutton.svg | 8 ++ assets/icons/radix/reader.svg | 8 ++ assets/icons/radix/reload.svg | 8 ++ assets/icons/radix/reset.svg | 8 ++ assets/icons/radix/resume.svg | 8 ++ assets/icons/radix/rocket.svg | 8 ++ .../icons/radix/rotate-counter-clockwise.svg | 8 ++ assets/icons/radix/row-spacing.svg | 8 ++ assets/icons/radix/rows.svg | 8 ++ assets/icons/radix/ruler-horizontal.svg | 8 ++ assets/icons/radix/ruler-square.svg | 8 ++ assets/icons/radix/scissors.svg | 8 ++ assets/icons/radix/section.svg | 8 ++ assets/icons/radix/sewing-pin-filled.svg | 8 ++ assets/icons/radix/sewing-pin-solid.svg | 8 ++ assets/icons/radix/sewing-pin.svg | 8 ++ assets/icons/radix/shadow-inner.svg | 78 +++++++++++++++++++ assets/icons/radix/shadow-none.svg | 78 +++++++++++++++++++ assets/icons/radix/shadow-outer.svg | 43 ++++++++++ assets/icons/radix/shadow.svg | 78 +++++++++++++++++++ assets/icons/radix/share-1.svg | 8 ++ assets/icons/radix/share-2.svg | 8 ++ assets/icons/radix/shuffle.svg | 8 ++ assets/icons/radix/size.svg | 8 ++ assets/icons/radix/sketch-logo.svg | 8 ++ assets/icons/radix/slash.svg | 8 ++ assets/icons/radix/slider.svg | 8 ++ .../radix/space-between-horizontally.svg | 8 ++ .../icons/radix/space-between-vertically.svg | 8 ++ .../icons/radix/space-evenly-horizontally.svg | 8 ++ .../icons/radix/space-evenly-vertically.svg | 8 ++ assets/icons/radix/speaker-loud.svg | 8 ++ assets/icons/radix/speaker-moderate.svg | 8 ++ assets/icons/radix/speaker-off.svg | 8 ++ assets/icons/radix/speaker-quiet.svg | 8 ++ assets/icons/radix/square.svg | 8 ++ assets/icons/radix/stack.svg | 8 ++ assets/icons/radix/star-filled.svg | 6 ++ assets/icons/radix/star.svg | 8 ++ assets/icons/radix/stitches-logo.svg | 8 ++ assets/icons/radix/stop.svg | 8 ++ assets/icons/radix/stopwatch.svg | 8 ++ assets/icons/radix/stretch-horizontally.svg | 8 ++ assets/icons/radix/stretch-vertically.svg | 8 ++ assets/icons/radix/strikethrough.svg | 8 ++ assets/icons/radix/sun.svg | 8 ++ assets/icons/radix/switch.svg | 8 ++ assets/icons/radix/symbol.svg | 8 ++ assets/icons/radix/table.svg | 8 ++ assets/icons/radix/target.svg | 8 ++ assets/icons/radix/text-align-bottom.svg | 8 ++ assets/icons/radix/text-align-center.svg | 8 ++ assets/icons/radix/text-align-justify.svg | 8 ++ assets/icons/radix/text-align-left.svg | 8 ++ assets/icons/radix/text-align-middle.svg | 8 ++ assets/icons/radix/text-align-right.svg | 8 ++ assets/icons/radix/text-align-top.svg | 8 ++ assets/icons/radix/text-none.svg | 8 ++ assets/icons/radix/text.svg | 8 ++ assets/icons/radix/thick-arrow-down.svg | 8 ++ assets/icons/radix/thick-arrow-left.svg | 8 ++ assets/icons/radix/thick-arrow-right.svg | 8 ++ assets/icons/radix/thick-arrow-up.svg | 8 ++ assets/icons/radix/timer.svg | 8 ++ assets/icons/radix/tokens.svg | 8 ++ assets/icons/radix/track-next.svg | 8 ++ assets/icons/radix/track-previous.svg | 8 ++ assets/icons/radix/transform.svg | 8 ++ assets/icons/radix/transparency-grid.svg | 9 +++ assets/icons/radix/trash.svg | 8 ++ assets/icons/radix/triangle-down.svg | 3 + assets/icons/radix/triangle-left.svg | 3 + assets/icons/radix/triangle-right.svg | 3 + assets/icons/radix/triangle-up.svg | 3 + assets/icons/radix/twitter-logo.svg | 8 ++ assets/icons/radix/underline.svg | 8 ++ assets/icons/radix/update.svg | 8 ++ assets/icons/radix/upload.svg | 8 ++ assets/icons/radix/value-none.svg | 8 ++ assets/icons/radix/value.svg | 8 ++ assets/icons/radix/vercel-logo.svg | 8 ++ assets/icons/radix/video.svg | 8 ++ assets/icons/radix/view-grid.svg | 8 ++ assets/icons/radix/view-horizontal.svg | 8 ++ assets/icons/radix/view-none.svg | 8 ++ assets/icons/radix/view-vertical.svg | 8 ++ assets/icons/radix/width.svg | 8 ++ assets/icons/radix/zoom-in.svg | 8 ++ assets/icons/radix/zoom-out.svg | 8 ++ 333 files changed, 3174 insertions(+) create mode 100644 assets/icons/radix/accessibility.svg create mode 100644 assets/icons/radix/activity-log.svg create mode 100644 assets/icons/radix/align-baseline.svg create mode 100644 assets/icons/radix/align-bottom.svg create mode 100644 assets/icons/radix/align-center-horizontally.svg create mode 100644 assets/icons/radix/align-center-vertically.svg create mode 100644 assets/icons/radix/align-center.svg create mode 100644 assets/icons/radix/align-end.svg create mode 100644 assets/icons/radix/align-horizontal-centers.svg create mode 100644 assets/icons/radix/align-left.svg create mode 100644 assets/icons/radix/align-right.svg create mode 100644 assets/icons/radix/align-start.svg create mode 100644 assets/icons/radix/align-stretch.svg create mode 100644 assets/icons/radix/align-top.svg create mode 100644 assets/icons/radix/align-vertical-centers.svg create mode 100644 assets/icons/radix/all-sides.svg create mode 100644 assets/icons/radix/angle.svg create mode 100644 assets/icons/radix/archive.svg create mode 100644 assets/icons/radix/arrow-bottom-left.svg create mode 100644 assets/icons/radix/arrow-bottom-right.svg create mode 100644 assets/icons/radix/arrow-down.svg create mode 100644 assets/icons/radix/arrow-left.svg create mode 100644 assets/icons/radix/arrow-right.svg create mode 100644 assets/icons/radix/arrow-top-left.svg create mode 100644 assets/icons/radix/arrow-top-right.svg create mode 100644 assets/icons/radix/arrow-up.svg create mode 100644 assets/icons/radix/aspect-ratio.svg create mode 100644 assets/icons/radix/avatar.svg create mode 100644 assets/icons/radix/backpack.svg create mode 100644 assets/icons/radix/badge.svg create mode 100644 assets/icons/radix/bar-chart.svg create mode 100644 assets/icons/radix/bell.svg create mode 100644 assets/icons/radix/blending-mode.svg create mode 100644 assets/icons/radix/bookmark-filled.svg create mode 100644 assets/icons/radix/bookmark.svg create mode 100644 assets/icons/radix/border-all.svg create mode 100644 assets/icons/radix/border-bottom.svg create mode 100644 assets/icons/radix/border-dashed.svg create mode 100644 assets/icons/radix/border-dotted.svg create mode 100644 assets/icons/radix/border-left.svg create mode 100644 assets/icons/radix/border-none.svg create mode 100644 assets/icons/radix/border-right.svg create mode 100644 assets/icons/radix/border-solid.svg create mode 100644 assets/icons/radix/border-split.svg create mode 100644 assets/icons/radix/border-style.svg create mode 100644 assets/icons/radix/border-top.svg create mode 100644 assets/icons/radix/border-width.svg create mode 100644 assets/icons/radix/box-model.svg create mode 100644 assets/icons/radix/box.svg create mode 100644 assets/icons/radix/button.svg create mode 100644 assets/icons/radix/calendar.svg create mode 100644 assets/icons/radix/camera.svg create mode 100644 assets/icons/radix/card-stack-minus.svg create mode 100644 assets/icons/radix/card-stack-plus.svg create mode 100644 assets/icons/radix/card-stack.svg create mode 100644 assets/icons/radix/caret-down.svg create mode 100644 assets/icons/radix/caret-left.svg create mode 100644 assets/icons/radix/caret-right.svg create mode 100644 assets/icons/radix/caret-sort.svg create mode 100644 assets/icons/radix/caret-up.svg create mode 100644 assets/icons/radix/chat-bubble.svg create mode 100644 assets/icons/radix/check-circled.svg create mode 100644 assets/icons/radix/check.svg create mode 100644 assets/icons/radix/checkbox.svg create mode 100644 assets/icons/radix/chevron-down.svg create mode 100644 assets/icons/radix/chevron-left.svg create mode 100644 assets/icons/radix/chevron-right.svg create mode 100644 assets/icons/radix/chevron-up.svg create mode 100644 assets/icons/radix/circle-backslash.svg create mode 100644 assets/icons/radix/circle.svg create mode 100644 assets/icons/radix/clipboard-copy.svg create mode 100644 assets/icons/radix/clipboard.svg create mode 100644 assets/icons/radix/clock.svg create mode 100644 assets/icons/radix/code.svg create mode 100644 assets/icons/radix/codesandbox-logo.svg create mode 100644 assets/icons/radix/color-wheel.svg create mode 100644 assets/icons/radix/column-spacing.svg create mode 100644 assets/icons/radix/columns.svg create mode 100644 assets/icons/radix/commit.svg create mode 100644 assets/icons/radix/component-1.svg create mode 100644 assets/icons/radix/component-2.svg create mode 100644 assets/icons/radix/component-boolean.svg create mode 100644 assets/icons/radix/component-instance.svg create mode 100644 assets/icons/radix/component-none.svg create mode 100644 assets/icons/radix/component-placeholder.svg create mode 100644 assets/icons/radix/container.svg create mode 100644 assets/icons/radix/cookie.svg create mode 100644 assets/icons/radix/copy.svg create mode 100644 assets/icons/radix/corner-bottom-left.svg create mode 100644 assets/icons/radix/corner-bottom-right.svg create mode 100644 assets/icons/radix/corner-top-left.svg create mode 100644 assets/icons/radix/corner-top-right.svg create mode 100644 assets/icons/radix/corners.svg create mode 100644 assets/icons/radix/countdown-timer.svg create mode 100644 assets/icons/radix/counter-clockwise-clock.svg create mode 100644 assets/icons/radix/crop.svg create mode 100644 assets/icons/radix/cross-1.svg create mode 100644 assets/icons/radix/cross-2.svg create mode 100644 assets/icons/radix/cross-circled.svg create mode 100644 assets/icons/radix/crosshair-1.svg create mode 100644 assets/icons/radix/crosshair-2.svg create mode 100644 assets/icons/radix/crumpled-paper.svg create mode 100644 assets/icons/radix/cube.svg create mode 100644 assets/icons/radix/cursor-arrow.svg create mode 100644 assets/icons/radix/cursor-text.svg create mode 100644 assets/icons/radix/dash.svg create mode 100644 assets/icons/radix/dashboard.svg create mode 100644 assets/icons/radix/desktop.svg create mode 100644 assets/icons/radix/dimensions.svg create mode 100644 assets/icons/radix/disc.svg create mode 100644 assets/icons/radix/discord-logo.svg create mode 100644 assets/icons/radix/divider-horizontal.svg create mode 100644 assets/icons/radix/divider-vertical.svg create mode 100644 assets/icons/radix/dot-filled.svg create mode 100644 assets/icons/radix/dot-solid.svg create mode 100644 assets/icons/radix/dot.svg create mode 100644 assets/icons/radix/dots-horizontal.svg create mode 100644 assets/icons/radix/dots-vertical.svg create mode 100644 assets/icons/radix/double-arrow-down.svg create mode 100644 assets/icons/radix/double-arrow-left.svg create mode 100644 assets/icons/radix/double-arrow-right.svg create mode 100644 assets/icons/radix/double-arrow-up.svg create mode 100644 assets/icons/radix/download.svg create mode 100644 assets/icons/radix/drag-handle-dots-1.svg create mode 100644 assets/icons/radix/drag-handle-dots-2.svg create mode 100644 assets/icons/radix/drag-handle-horizontal.svg create mode 100644 assets/icons/radix/drag-handle-vertical.svg create mode 100644 assets/icons/radix/drawing-pin-filled.svg create mode 100644 assets/icons/radix/drawing-pin-solid.svg create mode 100644 assets/icons/radix/drawing-pin.svg create mode 100644 assets/icons/radix/dropdown-menu.svg create mode 100644 assets/icons/radix/enter-full-screen.svg create mode 100644 assets/icons/radix/enter.svg create mode 100644 assets/icons/radix/envelope-closed.svg create mode 100644 assets/icons/radix/envelope-open.svg create mode 100644 assets/icons/radix/eraser.svg create mode 100644 assets/icons/radix/exclamation-triangle.svg create mode 100644 assets/icons/radix/exit-full-screen.svg create mode 100644 assets/icons/radix/exit.svg create mode 100644 assets/icons/radix/external-link.svg create mode 100644 assets/icons/radix/eye-closed.svg create mode 100644 assets/icons/radix/eye-none.svg create mode 100644 assets/icons/radix/eye-open.svg create mode 100644 assets/icons/radix/face.svg create mode 100644 assets/icons/radix/figma-logo.svg create mode 100644 assets/icons/radix/file-minus.svg create mode 100644 assets/icons/radix/file-plus.svg create mode 100644 assets/icons/radix/file-text.svg create mode 100644 assets/icons/radix/file.svg create mode 100644 assets/icons/radix/font-bold.svg create mode 100644 assets/icons/radix/font-family.svg create mode 100644 assets/icons/radix/font-italic.svg create mode 100644 assets/icons/radix/font-roman.svg create mode 100644 assets/icons/radix/font-size.svg create mode 100644 assets/icons/radix/font-style.svg create mode 100644 assets/icons/radix/frame.svg create mode 100644 assets/icons/radix/framer-logo.svg create mode 100644 assets/icons/radix/gear.svg create mode 100644 assets/icons/radix/github-logo.svg create mode 100644 assets/icons/radix/globe.svg create mode 100644 assets/icons/radix/grid.svg create mode 100644 assets/icons/radix/group.svg create mode 100644 assets/icons/radix/half-1.svg create mode 100644 assets/icons/radix/half-2.svg create mode 100644 assets/icons/radix/hamburger-menu.svg create mode 100644 assets/icons/radix/hand.svg create mode 100644 assets/icons/radix/heading.svg create mode 100644 assets/icons/radix/heart-filled.svg create mode 100644 assets/icons/radix/heart.svg create mode 100644 assets/icons/radix/height.svg create mode 100644 assets/icons/radix/hobby-knife.svg create mode 100644 assets/icons/radix/home.svg create mode 100644 assets/icons/radix/iconjar-logo.svg create mode 100644 assets/icons/radix/id-card.svg create mode 100644 assets/icons/radix/image.svg create mode 100644 assets/icons/radix/info-circled.svg create mode 100644 assets/icons/radix/inner-shadow.svg create mode 100644 assets/icons/radix/input.svg create mode 100644 assets/icons/radix/instagram-logo.svg create mode 100644 assets/icons/radix/justify-center.svg create mode 100644 assets/icons/radix/justify-end.svg create mode 100644 assets/icons/radix/justify-start.svg create mode 100644 assets/icons/radix/justify-stretch.svg create mode 100644 assets/icons/radix/keyboard.svg create mode 100644 assets/icons/radix/lap-timer.svg create mode 100644 assets/icons/radix/laptop.svg create mode 100644 assets/icons/radix/layers.svg create mode 100644 assets/icons/radix/layout.svg create mode 100644 assets/icons/radix/letter-case-capitalize.svg create mode 100644 assets/icons/radix/letter-case-lowercase.svg create mode 100644 assets/icons/radix/letter-case-toggle.svg create mode 100644 assets/icons/radix/letter-case-uppercase.svg create mode 100644 assets/icons/radix/letter-spacing.svg create mode 100644 assets/icons/radix/lightning-bolt.svg create mode 100644 assets/icons/radix/line-height.svg create mode 100644 assets/icons/radix/link-1.svg create mode 100644 assets/icons/radix/link-2.svg create mode 100644 assets/icons/radix/link-break-1.svg create mode 100644 assets/icons/radix/link-break-2.svg create mode 100644 assets/icons/radix/link-none-1.svg create mode 100644 assets/icons/radix/link-none-2.svg create mode 100644 assets/icons/radix/linkedin-logo.svg create mode 100644 assets/icons/radix/list-bullet.svg create mode 100644 assets/icons/radix/lock-closed.svg create mode 100644 assets/icons/radix/lock-open-1.svg create mode 100644 assets/icons/radix/lock-open-2.svg create mode 100644 assets/icons/radix/loop.svg create mode 100644 assets/icons/radix/magic-wand.svg create mode 100644 assets/icons/radix/magnifying-glass.svg create mode 100644 assets/icons/radix/margin.svg create mode 100644 assets/icons/radix/mask-off.svg create mode 100644 assets/icons/radix/mask-on.svg create mode 100644 assets/icons/radix/minus-circled.svg create mode 100644 assets/icons/radix/minus.svg create mode 100644 assets/icons/radix/mix.svg create mode 100644 assets/icons/radix/mixer-horizontal.svg create mode 100644 assets/icons/radix/mixer-vertical.svg create mode 100644 assets/icons/radix/mobile.svg create mode 100644 assets/icons/radix/modulz-logo.svg create mode 100644 assets/icons/radix/moon.svg create mode 100644 assets/icons/radix/move.svg create mode 100644 assets/icons/radix/notion-logo.svg create mode 100644 assets/icons/radix/opacity.svg create mode 100644 assets/icons/radix/open-in-new-window.svg create mode 100644 assets/icons/radix/outer-shadow.svg create mode 100644 assets/icons/radix/overline.svg create mode 100644 assets/icons/radix/padding.svg create mode 100644 assets/icons/radix/paper-plane.svg create mode 100644 assets/icons/radix/pause.svg create mode 100644 assets/icons/radix/pencil-1.svg create mode 100644 assets/icons/radix/pencil-2.svg create mode 100644 assets/icons/radix/person.svg create mode 100644 assets/icons/radix/pie-chart.svg create mode 100644 assets/icons/radix/pilcrow.svg create mode 100644 assets/icons/radix/pin-bottom.svg create mode 100644 assets/icons/radix/pin-left.svg create mode 100644 assets/icons/radix/pin-right.svg create mode 100644 assets/icons/radix/pin-top.svg create mode 100644 assets/icons/radix/play.svg create mode 100644 assets/icons/radix/plus-circled.svg create mode 100644 assets/icons/radix/plus.svg create mode 100644 assets/icons/radix/question-mark-circled.svg create mode 100644 assets/icons/radix/question-mark.svg create mode 100644 assets/icons/radix/quote.svg create mode 100644 assets/icons/radix/radiobutton.svg create mode 100644 assets/icons/radix/reader.svg create mode 100644 assets/icons/radix/reload.svg create mode 100644 assets/icons/radix/reset.svg create mode 100644 assets/icons/radix/resume.svg create mode 100644 assets/icons/radix/rocket.svg create mode 100644 assets/icons/radix/rotate-counter-clockwise.svg create mode 100644 assets/icons/radix/row-spacing.svg create mode 100644 assets/icons/radix/rows.svg create mode 100644 assets/icons/radix/ruler-horizontal.svg create mode 100644 assets/icons/radix/ruler-square.svg create mode 100644 assets/icons/radix/scissors.svg create mode 100644 assets/icons/radix/section.svg create mode 100644 assets/icons/radix/sewing-pin-filled.svg create mode 100644 assets/icons/radix/sewing-pin-solid.svg create mode 100644 assets/icons/radix/sewing-pin.svg create mode 100644 assets/icons/radix/shadow-inner.svg create mode 100644 assets/icons/radix/shadow-none.svg create mode 100644 assets/icons/radix/shadow-outer.svg create mode 100644 assets/icons/radix/shadow.svg create mode 100644 assets/icons/radix/share-1.svg create mode 100644 assets/icons/radix/share-2.svg create mode 100644 assets/icons/radix/shuffle.svg create mode 100644 assets/icons/radix/size.svg create mode 100644 assets/icons/radix/sketch-logo.svg create mode 100644 assets/icons/radix/slash.svg create mode 100644 assets/icons/radix/slider.svg create mode 100644 assets/icons/radix/space-between-horizontally.svg create mode 100644 assets/icons/radix/space-between-vertically.svg create mode 100644 assets/icons/radix/space-evenly-horizontally.svg create mode 100644 assets/icons/radix/space-evenly-vertically.svg create mode 100644 assets/icons/radix/speaker-loud.svg create mode 100644 assets/icons/radix/speaker-moderate.svg create mode 100644 assets/icons/radix/speaker-off.svg create mode 100644 assets/icons/radix/speaker-quiet.svg create mode 100644 assets/icons/radix/square.svg create mode 100644 assets/icons/radix/stack.svg create mode 100644 assets/icons/radix/star-filled.svg create mode 100644 assets/icons/radix/star.svg create mode 100644 assets/icons/radix/stitches-logo.svg create mode 100644 assets/icons/radix/stop.svg create mode 100644 assets/icons/radix/stopwatch.svg create mode 100644 assets/icons/radix/stretch-horizontally.svg create mode 100644 assets/icons/radix/stretch-vertically.svg create mode 100644 assets/icons/radix/strikethrough.svg create mode 100644 assets/icons/radix/sun.svg create mode 100644 assets/icons/radix/switch.svg create mode 100644 assets/icons/radix/symbol.svg create mode 100644 assets/icons/radix/table.svg create mode 100644 assets/icons/radix/target.svg create mode 100644 assets/icons/radix/text-align-bottom.svg create mode 100644 assets/icons/radix/text-align-center.svg create mode 100644 assets/icons/radix/text-align-justify.svg create mode 100644 assets/icons/radix/text-align-left.svg create mode 100644 assets/icons/radix/text-align-middle.svg create mode 100644 assets/icons/radix/text-align-right.svg create mode 100644 assets/icons/radix/text-align-top.svg create mode 100644 assets/icons/radix/text-none.svg create mode 100644 assets/icons/radix/text.svg create mode 100644 assets/icons/radix/thick-arrow-down.svg create mode 100644 assets/icons/radix/thick-arrow-left.svg create mode 100644 assets/icons/radix/thick-arrow-right.svg create mode 100644 assets/icons/radix/thick-arrow-up.svg create mode 100644 assets/icons/radix/timer.svg create mode 100644 assets/icons/radix/tokens.svg create mode 100644 assets/icons/radix/track-next.svg create mode 100644 assets/icons/radix/track-previous.svg create mode 100644 assets/icons/radix/transform.svg create mode 100644 assets/icons/radix/transparency-grid.svg create mode 100644 assets/icons/radix/trash.svg create mode 100644 assets/icons/radix/triangle-down.svg create mode 100644 assets/icons/radix/triangle-left.svg create mode 100644 assets/icons/radix/triangle-right.svg create mode 100644 assets/icons/radix/triangle-up.svg create mode 100644 assets/icons/radix/twitter-logo.svg create mode 100644 assets/icons/radix/underline.svg create mode 100644 assets/icons/radix/update.svg create mode 100644 assets/icons/radix/upload.svg create mode 100644 assets/icons/radix/value-none.svg create mode 100644 assets/icons/radix/value.svg create mode 100644 assets/icons/radix/vercel-logo.svg create mode 100644 assets/icons/radix/video.svg create mode 100644 assets/icons/radix/view-grid.svg create mode 100644 assets/icons/radix/view-horizontal.svg create mode 100644 assets/icons/radix/view-none.svg create mode 100644 assets/icons/radix/view-vertical.svg create mode 100644 assets/icons/radix/width.svg create mode 100644 assets/icons/radix/zoom-in.svg create mode 100644 assets/icons/radix/zoom-out.svg diff --git a/assets/icons/radix/accessibility.svg b/assets/icons/radix/accessibility.svg new file mode 100644 index 0000000000..32d78f2d8d --- /dev/null +++ b/assets/icons/radix/accessibility.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/activity-log.svg b/assets/icons/radix/activity-log.svg new file mode 100644 index 0000000000..8feab7d449 --- /dev/null +++ b/assets/icons/radix/activity-log.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-baseline.svg b/assets/icons/radix/align-baseline.svg new file mode 100644 index 0000000000..07213dc1ae --- /dev/null +++ b/assets/icons/radix/align-baseline.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-bottom.svg b/assets/icons/radix/align-bottom.svg new file mode 100644 index 0000000000..7d11c0cd5a --- /dev/null +++ b/assets/icons/radix/align-bottom.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-center-horizontally.svg b/assets/icons/radix/align-center-horizontally.svg new file mode 100644 index 0000000000..69509a7d09 --- /dev/null +++ b/assets/icons/radix/align-center-horizontally.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-center-vertically.svg b/assets/icons/radix/align-center-vertically.svg new file mode 100644 index 0000000000..4f1b50cc43 --- /dev/null +++ b/assets/icons/radix/align-center-vertically.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-center.svg b/assets/icons/radix/align-center.svg new file mode 100644 index 0000000000..caaec36477 --- /dev/null +++ b/assets/icons/radix/align-center.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-end.svg b/assets/icons/radix/align-end.svg new file mode 100644 index 0000000000..18f1b64912 --- /dev/null +++ b/assets/icons/radix/align-end.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-horizontal-centers.svg b/assets/icons/radix/align-horizontal-centers.svg new file mode 100644 index 0000000000..2d1d64ea4b --- /dev/null +++ b/assets/icons/radix/align-horizontal-centers.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-left.svg b/assets/icons/radix/align-left.svg new file mode 100644 index 0000000000..0d5dba095c --- /dev/null +++ b/assets/icons/radix/align-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-right.svg b/assets/icons/radix/align-right.svg new file mode 100644 index 0000000000..1b6b3f0ffa --- /dev/null +++ b/assets/icons/radix/align-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-start.svg b/assets/icons/radix/align-start.svg new file mode 100644 index 0000000000..ada50e1079 --- /dev/null +++ b/assets/icons/radix/align-start.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-stretch.svg b/assets/icons/radix/align-stretch.svg new file mode 100644 index 0000000000..3cb28605cb --- /dev/null +++ b/assets/icons/radix/align-stretch.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-top.svg b/assets/icons/radix/align-top.svg new file mode 100644 index 0000000000..23db80f4dd --- /dev/null +++ b/assets/icons/radix/align-top.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/align-vertical-centers.svg b/assets/icons/radix/align-vertical-centers.svg new file mode 100644 index 0000000000..07eaee7bf7 --- /dev/null +++ b/assets/icons/radix/align-vertical-centers.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/all-sides.svg b/assets/icons/radix/all-sides.svg new file mode 100644 index 0000000000..8ace7df03f --- /dev/null +++ b/assets/icons/radix/all-sides.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/angle.svg b/assets/icons/radix/angle.svg new file mode 100644 index 0000000000..a0d93f3460 --- /dev/null +++ b/assets/icons/radix/angle.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/archive.svg b/assets/icons/radix/archive.svg new file mode 100644 index 0000000000..74063f1d1e --- /dev/null +++ b/assets/icons/radix/archive.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/arrow-bottom-left.svg b/assets/icons/radix/arrow-bottom-left.svg new file mode 100644 index 0000000000..7a4511aa2d --- /dev/null +++ b/assets/icons/radix/arrow-bottom-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/arrow-bottom-right.svg b/assets/icons/radix/arrow-bottom-right.svg new file mode 100644 index 0000000000..2ba9fef101 --- /dev/null +++ b/assets/icons/radix/arrow-bottom-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/arrow-down.svg b/assets/icons/radix/arrow-down.svg new file mode 100644 index 0000000000..5dc21a6689 --- /dev/null +++ b/assets/icons/radix/arrow-down.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/arrow-left.svg b/assets/icons/radix/arrow-left.svg new file mode 100644 index 0000000000..3a64c8394f --- /dev/null +++ b/assets/icons/radix/arrow-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/arrow-right.svg b/assets/icons/radix/arrow-right.svg new file mode 100644 index 0000000000..e3d30988d5 --- /dev/null +++ b/assets/icons/radix/arrow-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/arrow-top-left.svg b/assets/icons/radix/arrow-top-left.svg new file mode 100644 index 0000000000..69fef41dee --- /dev/null +++ b/assets/icons/radix/arrow-top-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/arrow-top-right.svg b/assets/icons/radix/arrow-top-right.svg new file mode 100644 index 0000000000..c1016376e3 --- /dev/null +++ b/assets/icons/radix/arrow-top-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/arrow-up.svg b/assets/icons/radix/arrow-up.svg new file mode 100644 index 0000000000..ba426119e9 --- /dev/null +++ b/assets/icons/radix/arrow-up.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/aspect-ratio.svg b/assets/icons/radix/aspect-ratio.svg new file mode 100644 index 0000000000..0851f2e1e9 --- /dev/null +++ b/assets/icons/radix/aspect-ratio.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/avatar.svg b/assets/icons/radix/avatar.svg new file mode 100644 index 0000000000..cb229c77fe --- /dev/null +++ b/assets/icons/radix/avatar.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/backpack.svg b/assets/icons/radix/backpack.svg new file mode 100644 index 0000000000..a5c9cedbd3 --- /dev/null +++ b/assets/icons/radix/backpack.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/badge.svg b/assets/icons/radix/badge.svg new file mode 100644 index 0000000000..aa764d4726 --- /dev/null +++ b/assets/icons/radix/badge.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/bar-chart.svg b/assets/icons/radix/bar-chart.svg new file mode 100644 index 0000000000..f8054781d9 --- /dev/null +++ b/assets/icons/radix/bar-chart.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/bell.svg b/assets/icons/radix/bell.svg new file mode 100644 index 0000000000..ea1c6dd42e --- /dev/null +++ b/assets/icons/radix/bell.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/blending-mode.svg b/assets/icons/radix/blending-mode.svg new file mode 100644 index 0000000000..bd58cf4ee3 --- /dev/null +++ b/assets/icons/radix/blending-mode.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/bookmark-filled.svg b/assets/icons/radix/bookmark-filled.svg new file mode 100644 index 0000000000..5b725cd88d --- /dev/null +++ b/assets/icons/radix/bookmark-filled.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/bookmark.svg b/assets/icons/radix/bookmark.svg new file mode 100644 index 0000000000..90c4d827f1 --- /dev/null +++ b/assets/icons/radix/bookmark.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/border-all.svg b/assets/icons/radix/border-all.svg new file mode 100644 index 0000000000..3bfde7d59b --- /dev/null +++ b/assets/icons/radix/border-all.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/assets/icons/radix/border-bottom.svg b/assets/icons/radix/border-bottom.svg new file mode 100644 index 0000000000..f2d3c3d554 --- /dev/null +++ b/assets/icons/radix/border-bottom.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/radix/border-dashed.svg b/assets/icons/radix/border-dashed.svg new file mode 100644 index 0000000000..85fdcdfe5d --- /dev/null +++ b/assets/icons/radix/border-dashed.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/border-dotted.svg b/assets/icons/radix/border-dotted.svg new file mode 100644 index 0000000000..5eb514ed2a --- /dev/null +++ b/assets/icons/radix/border-dotted.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/border-left.svg b/assets/icons/radix/border-left.svg new file mode 100644 index 0000000000..5deb197da5 --- /dev/null +++ b/assets/icons/radix/border-left.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/radix/border-none.svg b/assets/icons/radix/border-none.svg new file mode 100644 index 0000000000..1ad3f59d7c --- /dev/null +++ b/assets/icons/radix/border-none.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/radix/border-right.svg b/assets/icons/radix/border-right.svg new file mode 100644 index 0000000000..c939095ad7 --- /dev/null +++ b/assets/icons/radix/border-right.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/radix/border-solid.svg b/assets/icons/radix/border-solid.svg new file mode 100644 index 0000000000..5c0d26a058 --- /dev/null +++ b/assets/icons/radix/border-solid.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/border-split.svg b/assets/icons/radix/border-split.svg new file mode 100644 index 0000000000..7fdf6cc34e --- /dev/null +++ b/assets/icons/radix/border-split.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/radix/border-style.svg b/assets/icons/radix/border-style.svg new file mode 100644 index 0000000000..f729cb993b --- /dev/null +++ b/assets/icons/radix/border-style.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/border-top.svg b/assets/icons/radix/border-top.svg new file mode 100644 index 0000000000..bde739d755 --- /dev/null +++ b/assets/icons/radix/border-top.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/radix/border-width.svg b/assets/icons/radix/border-width.svg new file mode 100644 index 0000000000..37c270756e --- /dev/null +++ b/assets/icons/radix/border-width.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/box-model.svg b/assets/icons/radix/box-model.svg new file mode 100644 index 0000000000..45d1a7ce41 --- /dev/null +++ b/assets/icons/radix/box-model.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/box.svg b/assets/icons/radix/box.svg new file mode 100644 index 0000000000..6e035c21ed --- /dev/null +++ b/assets/icons/radix/box.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/button.svg b/assets/icons/radix/button.svg new file mode 100644 index 0000000000..31622bcf15 --- /dev/null +++ b/assets/icons/radix/button.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/calendar.svg b/assets/icons/radix/calendar.svg new file mode 100644 index 0000000000..2adbe0bc28 --- /dev/null +++ b/assets/icons/radix/calendar.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/camera.svg b/assets/icons/radix/camera.svg new file mode 100644 index 0000000000..d7cccf74c2 --- /dev/null +++ b/assets/icons/radix/camera.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/card-stack-minus.svg b/assets/icons/radix/card-stack-minus.svg new file mode 100644 index 0000000000..04d8e51178 --- /dev/null +++ b/assets/icons/radix/card-stack-minus.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/card-stack-plus.svg b/assets/icons/radix/card-stack-plus.svg new file mode 100644 index 0000000000..a184f4bc1a --- /dev/null +++ b/assets/icons/radix/card-stack-plus.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/card-stack.svg b/assets/icons/radix/card-stack.svg new file mode 100644 index 0000000000..defea0e165 --- /dev/null +++ b/assets/icons/radix/card-stack.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/caret-down.svg b/assets/icons/radix/caret-down.svg new file mode 100644 index 0000000000..ff8b8c3b88 --- /dev/null +++ b/assets/icons/radix/caret-down.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/caret-left.svg b/assets/icons/radix/caret-left.svg new file mode 100644 index 0000000000..969bc3b95c --- /dev/null +++ b/assets/icons/radix/caret-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/caret-right.svg b/assets/icons/radix/caret-right.svg new file mode 100644 index 0000000000..75c55d8676 --- /dev/null +++ b/assets/icons/radix/caret-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/caret-sort.svg b/assets/icons/radix/caret-sort.svg new file mode 100644 index 0000000000..a65e20b660 --- /dev/null +++ b/assets/icons/radix/caret-sort.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/caret-up.svg b/assets/icons/radix/caret-up.svg new file mode 100644 index 0000000000..53026b83d8 --- /dev/null +++ b/assets/icons/radix/caret-up.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/chat-bubble.svg b/assets/icons/radix/chat-bubble.svg new file mode 100644 index 0000000000..5766f46de8 --- /dev/null +++ b/assets/icons/radix/chat-bubble.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/check-circled.svg b/assets/icons/radix/check-circled.svg new file mode 100644 index 0000000000..19ee22eb51 --- /dev/null +++ b/assets/icons/radix/check-circled.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/check.svg b/assets/icons/radix/check.svg new file mode 100644 index 0000000000..476a3baa18 --- /dev/null +++ b/assets/icons/radix/check.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/checkbox.svg b/assets/icons/radix/checkbox.svg new file mode 100644 index 0000000000..d6bb3c7ef2 --- /dev/null +++ b/assets/icons/radix/checkbox.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/chevron-down.svg b/assets/icons/radix/chevron-down.svg new file mode 100644 index 0000000000..175c1312fd --- /dev/null +++ b/assets/icons/radix/chevron-down.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/chevron-left.svg b/assets/icons/radix/chevron-left.svg new file mode 100644 index 0000000000..d7628202f2 --- /dev/null +++ b/assets/icons/radix/chevron-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/chevron-right.svg b/assets/icons/radix/chevron-right.svg new file mode 100644 index 0000000000..e3ebd73d99 --- /dev/null +++ b/assets/icons/radix/chevron-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/chevron-up.svg b/assets/icons/radix/chevron-up.svg new file mode 100644 index 0000000000..0e8e796dab --- /dev/null +++ b/assets/icons/radix/chevron-up.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/circle-backslash.svg b/assets/icons/radix/circle-backslash.svg new file mode 100644 index 0000000000..40c4dd5398 --- /dev/null +++ b/assets/icons/radix/circle-backslash.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/circle.svg b/assets/icons/radix/circle.svg new file mode 100644 index 0000000000..ba4a8f22fe --- /dev/null +++ b/assets/icons/radix/circle.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/clipboard-copy.svg b/assets/icons/radix/clipboard-copy.svg new file mode 100644 index 0000000000..5293fdc493 --- /dev/null +++ b/assets/icons/radix/clipboard-copy.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/clipboard.svg b/assets/icons/radix/clipboard.svg new file mode 100644 index 0000000000..e18b32943b --- /dev/null +++ b/assets/icons/radix/clipboard.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/clock.svg b/assets/icons/radix/clock.svg new file mode 100644 index 0000000000..ac3b526fbb --- /dev/null +++ b/assets/icons/radix/clock.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/code.svg b/assets/icons/radix/code.svg new file mode 100644 index 0000000000..70fe381b68 --- /dev/null +++ b/assets/icons/radix/code.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/codesandbox-logo.svg b/assets/icons/radix/codesandbox-logo.svg new file mode 100644 index 0000000000..4a3f549c2f --- /dev/null +++ b/assets/icons/radix/codesandbox-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/color-wheel.svg b/assets/icons/radix/color-wheel.svg new file mode 100644 index 0000000000..2153b84428 --- /dev/null +++ b/assets/icons/radix/color-wheel.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/column-spacing.svg b/assets/icons/radix/column-spacing.svg new file mode 100644 index 0000000000..aafcf555cb --- /dev/null +++ b/assets/icons/radix/column-spacing.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/columns.svg b/assets/icons/radix/columns.svg new file mode 100644 index 0000000000..e1607611b1 --- /dev/null +++ b/assets/icons/radix/columns.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/commit.svg b/assets/icons/radix/commit.svg new file mode 100644 index 0000000000..ac128a2b08 --- /dev/null +++ b/assets/icons/radix/commit.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/component-1.svg b/assets/icons/radix/component-1.svg new file mode 100644 index 0000000000..e3e9f38af1 --- /dev/null +++ b/assets/icons/radix/component-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/component-2.svg b/assets/icons/radix/component-2.svg new file mode 100644 index 0000000000..df2091d143 --- /dev/null +++ b/assets/icons/radix/component-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/component-boolean.svg b/assets/icons/radix/component-boolean.svg new file mode 100644 index 0000000000..942e8832eb --- /dev/null +++ b/assets/icons/radix/component-boolean.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/component-instance.svg b/assets/icons/radix/component-instance.svg new file mode 100644 index 0000000000..048c401291 --- /dev/null +++ b/assets/icons/radix/component-instance.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/component-none.svg b/assets/icons/radix/component-none.svg new file mode 100644 index 0000000000..a622c3ee96 --- /dev/null +++ b/assets/icons/radix/component-none.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/component-placeholder.svg b/assets/icons/radix/component-placeholder.svg new file mode 100644 index 0000000000..b8892d5d23 --- /dev/null +++ b/assets/icons/radix/component-placeholder.svg @@ -0,0 +1,12 @@ + + + + diff --git a/assets/icons/radix/container.svg b/assets/icons/radix/container.svg new file mode 100644 index 0000000000..1c2a4fd0e1 --- /dev/null +++ b/assets/icons/radix/container.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/cookie.svg b/assets/icons/radix/cookie.svg new file mode 100644 index 0000000000..8c165601a2 --- /dev/null +++ b/assets/icons/radix/cookie.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/copy.svg b/assets/icons/radix/copy.svg new file mode 100644 index 0000000000..bf2b504ecf --- /dev/null +++ b/assets/icons/radix/copy.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/corner-bottom-left.svg b/assets/icons/radix/corner-bottom-left.svg new file mode 100644 index 0000000000..26df9dbad8 --- /dev/null +++ b/assets/icons/radix/corner-bottom-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/corner-bottom-right.svg b/assets/icons/radix/corner-bottom-right.svg new file mode 100644 index 0000000000..15e3957123 --- /dev/null +++ b/assets/icons/radix/corner-bottom-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/corner-top-left.svg b/assets/icons/radix/corner-top-left.svg new file mode 100644 index 0000000000..8fc1b84b82 --- /dev/null +++ b/assets/icons/radix/corner-top-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/corner-top-right.svg b/assets/icons/radix/corner-top-right.svg new file mode 100644 index 0000000000..533ea6c678 --- /dev/null +++ b/assets/icons/radix/corner-top-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/corners.svg b/assets/icons/radix/corners.svg new file mode 100644 index 0000000000..c41c4e0183 --- /dev/null +++ b/assets/icons/radix/corners.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/countdown-timer.svg b/assets/icons/radix/countdown-timer.svg new file mode 100644 index 0000000000..58494bd416 --- /dev/null +++ b/assets/icons/radix/countdown-timer.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/counter-clockwise-clock.svg b/assets/icons/radix/counter-clockwise-clock.svg new file mode 100644 index 0000000000..0b3acbcebf --- /dev/null +++ b/assets/icons/radix/counter-clockwise-clock.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/crop.svg b/assets/icons/radix/crop.svg new file mode 100644 index 0000000000..008457fff6 --- /dev/null +++ b/assets/icons/radix/crop.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/cross-1.svg b/assets/icons/radix/cross-1.svg new file mode 100644 index 0000000000..62135d27ed --- /dev/null +++ b/assets/icons/radix/cross-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/cross-2.svg b/assets/icons/radix/cross-2.svg new file mode 100644 index 0000000000..4c55700928 --- /dev/null +++ b/assets/icons/radix/cross-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/cross-circled.svg b/assets/icons/radix/cross-circled.svg new file mode 100644 index 0000000000..df3cb896c8 --- /dev/null +++ b/assets/icons/radix/cross-circled.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/crosshair-1.svg b/assets/icons/radix/crosshair-1.svg new file mode 100644 index 0000000000..05b22f8461 --- /dev/null +++ b/assets/icons/radix/crosshair-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/crosshair-2.svg b/assets/icons/radix/crosshair-2.svg new file mode 100644 index 0000000000..f5ee0a92af --- /dev/null +++ b/assets/icons/radix/crosshair-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/crumpled-paper.svg b/assets/icons/radix/crumpled-paper.svg new file mode 100644 index 0000000000..33e9b65581 --- /dev/null +++ b/assets/icons/radix/crumpled-paper.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/cube.svg b/assets/icons/radix/cube.svg new file mode 100644 index 0000000000..b327158be4 --- /dev/null +++ b/assets/icons/radix/cube.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/cursor-arrow.svg b/assets/icons/radix/cursor-arrow.svg new file mode 100644 index 0000000000..b0227e4ded --- /dev/null +++ b/assets/icons/radix/cursor-arrow.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/cursor-text.svg b/assets/icons/radix/cursor-text.svg new file mode 100644 index 0000000000..05939503b8 --- /dev/null +++ b/assets/icons/radix/cursor-text.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/dash.svg b/assets/icons/radix/dash.svg new file mode 100644 index 0000000000..d70daf7fed --- /dev/null +++ b/assets/icons/radix/dash.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/dashboard.svg b/assets/icons/radix/dashboard.svg new file mode 100644 index 0000000000..38008c64e4 --- /dev/null +++ b/assets/icons/radix/dashboard.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/desktop.svg b/assets/icons/radix/desktop.svg new file mode 100644 index 0000000000..ad252e64cf --- /dev/null +++ b/assets/icons/radix/desktop.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/dimensions.svg b/assets/icons/radix/dimensions.svg new file mode 100644 index 0000000000..767d1d2896 --- /dev/null +++ b/assets/icons/radix/dimensions.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/disc.svg b/assets/icons/radix/disc.svg new file mode 100644 index 0000000000..6e19caab35 --- /dev/null +++ b/assets/icons/radix/disc.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/discord-logo.svg b/assets/icons/radix/discord-logo.svg new file mode 100644 index 0000000000..50567c212e --- /dev/null +++ b/assets/icons/radix/discord-logo.svg @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/assets/icons/radix/divider-horizontal.svg b/assets/icons/radix/divider-horizontal.svg new file mode 100644 index 0000000000..59e43649c9 --- /dev/null +++ b/assets/icons/radix/divider-horizontal.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/divider-vertical.svg b/assets/icons/radix/divider-vertical.svg new file mode 100644 index 0000000000..95f5cc8f2f --- /dev/null +++ b/assets/icons/radix/divider-vertical.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/dot-filled.svg b/assets/icons/radix/dot-filled.svg new file mode 100644 index 0000000000..0c1a17b3bd --- /dev/null +++ b/assets/icons/radix/dot-filled.svg @@ -0,0 +1,6 @@ + + + diff --git a/assets/icons/radix/dot-solid.svg b/assets/icons/radix/dot-solid.svg new file mode 100644 index 0000000000..0c1a17b3bd --- /dev/null +++ b/assets/icons/radix/dot-solid.svg @@ -0,0 +1,6 @@ + + + diff --git a/assets/icons/radix/dot.svg b/assets/icons/radix/dot.svg new file mode 100644 index 0000000000..c553a1422d --- /dev/null +++ b/assets/icons/radix/dot.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/dots-horizontal.svg b/assets/icons/radix/dots-horizontal.svg new file mode 100644 index 0000000000..347d1ae13d --- /dev/null +++ b/assets/icons/radix/dots-horizontal.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/dots-vertical.svg b/assets/icons/radix/dots-vertical.svg new file mode 100644 index 0000000000..5ca1a181e3 --- /dev/null +++ b/assets/icons/radix/dots-vertical.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/double-arrow-down.svg b/assets/icons/radix/double-arrow-down.svg new file mode 100644 index 0000000000..8b86db2f8a --- /dev/null +++ b/assets/icons/radix/double-arrow-down.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/double-arrow-left.svg b/assets/icons/radix/double-arrow-left.svg new file mode 100644 index 0000000000..0ef30ff955 --- /dev/null +++ b/assets/icons/radix/double-arrow-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/double-arrow-right.svg b/assets/icons/radix/double-arrow-right.svg new file mode 100644 index 0000000000..9997fdc403 --- /dev/null +++ b/assets/icons/radix/double-arrow-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/double-arrow-up.svg b/assets/icons/radix/double-arrow-up.svg new file mode 100644 index 0000000000..8d571fcd66 --- /dev/null +++ b/assets/icons/radix/double-arrow-up.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/download.svg b/assets/icons/radix/download.svg new file mode 100644 index 0000000000..49a05d5f47 --- /dev/null +++ b/assets/icons/radix/download.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/drag-handle-dots-1.svg b/assets/icons/radix/drag-handle-dots-1.svg new file mode 100644 index 0000000000..fc046bb9d9 --- /dev/null +++ b/assets/icons/radix/drag-handle-dots-1.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/radix/drag-handle-dots-2.svg b/assets/icons/radix/drag-handle-dots-2.svg new file mode 100644 index 0000000000..aed0e702d7 --- /dev/null +++ b/assets/icons/radix/drag-handle-dots-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/drag-handle-horizontal.svg b/assets/icons/radix/drag-handle-horizontal.svg new file mode 100644 index 0000000000..c1bb138a24 --- /dev/null +++ b/assets/icons/radix/drag-handle-horizontal.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/drag-handle-vertical.svg b/assets/icons/radix/drag-handle-vertical.svg new file mode 100644 index 0000000000..8d48c7894a --- /dev/null +++ b/assets/icons/radix/drag-handle-vertical.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/drawing-pin-filled.svg b/assets/icons/radix/drawing-pin-filled.svg new file mode 100644 index 0000000000..e1894619c3 --- /dev/null +++ b/assets/icons/radix/drawing-pin-filled.svg @@ -0,0 +1,14 @@ + + + + diff --git a/assets/icons/radix/drawing-pin-solid.svg b/assets/icons/radix/drawing-pin-solid.svg new file mode 100644 index 0000000000..e1894619c3 --- /dev/null +++ b/assets/icons/radix/drawing-pin-solid.svg @@ -0,0 +1,14 @@ + + + + diff --git a/assets/icons/radix/drawing-pin.svg b/assets/icons/radix/drawing-pin.svg new file mode 100644 index 0000000000..5625e7588f --- /dev/null +++ b/assets/icons/radix/drawing-pin.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/dropdown-menu.svg b/assets/icons/radix/dropdown-menu.svg new file mode 100644 index 0000000000..c938052be8 --- /dev/null +++ b/assets/icons/radix/dropdown-menu.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/enter-full-screen.svg b/assets/icons/radix/enter-full-screen.svg new file mode 100644 index 0000000000..d368a6d415 --- /dev/null +++ b/assets/icons/radix/enter-full-screen.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/enter.svg b/assets/icons/radix/enter.svg new file mode 100644 index 0000000000..cc57d74cea --- /dev/null +++ b/assets/icons/radix/enter.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/envelope-closed.svg b/assets/icons/radix/envelope-closed.svg new file mode 100644 index 0000000000..4b5e037840 --- /dev/null +++ b/assets/icons/radix/envelope-closed.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/envelope-open.svg b/assets/icons/radix/envelope-open.svg new file mode 100644 index 0000000000..df1e3fea95 --- /dev/null +++ b/assets/icons/radix/envelope-open.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/eraser.svg b/assets/icons/radix/eraser.svg new file mode 100644 index 0000000000..bb448d4d23 --- /dev/null +++ b/assets/icons/radix/eraser.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/exclamation-triangle.svg b/assets/icons/radix/exclamation-triangle.svg new file mode 100644 index 0000000000..210d4c45c6 --- /dev/null +++ b/assets/icons/radix/exclamation-triangle.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/exit-full-screen.svg b/assets/icons/radix/exit-full-screen.svg new file mode 100644 index 0000000000..9b6439b043 --- /dev/null +++ b/assets/icons/radix/exit-full-screen.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/exit.svg b/assets/icons/radix/exit.svg new file mode 100644 index 0000000000..2cc6ce120d --- /dev/null +++ b/assets/icons/radix/exit.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/external-link.svg b/assets/icons/radix/external-link.svg new file mode 100644 index 0000000000..0ee7420162 --- /dev/null +++ b/assets/icons/radix/external-link.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/eye-closed.svg b/assets/icons/radix/eye-closed.svg new file mode 100644 index 0000000000..f824fe55f9 --- /dev/null +++ b/assets/icons/radix/eye-closed.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/eye-none.svg b/assets/icons/radix/eye-none.svg new file mode 100644 index 0000000000..d4beecd33a --- /dev/null +++ b/assets/icons/radix/eye-none.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/eye-open.svg b/assets/icons/radix/eye-open.svg new file mode 100644 index 0000000000..d39d26b2c1 --- /dev/null +++ b/assets/icons/radix/eye-open.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/face.svg b/assets/icons/radix/face.svg new file mode 100644 index 0000000000..81b14dd8d7 --- /dev/null +++ b/assets/icons/radix/face.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/figma-logo.svg b/assets/icons/radix/figma-logo.svg new file mode 100644 index 0000000000..6c19276554 --- /dev/null +++ b/assets/icons/radix/figma-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/file-minus.svg b/assets/icons/radix/file-minus.svg new file mode 100644 index 0000000000..bd1a841881 --- /dev/null +++ b/assets/icons/radix/file-minus.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/file-plus.svg b/assets/icons/radix/file-plus.svg new file mode 100644 index 0000000000..2396e20015 --- /dev/null +++ b/assets/icons/radix/file-plus.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/file-text.svg b/assets/icons/radix/file-text.svg new file mode 100644 index 0000000000..f341ab8abf --- /dev/null +++ b/assets/icons/radix/file-text.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/file.svg b/assets/icons/radix/file.svg new file mode 100644 index 0000000000..5f256b42e1 --- /dev/null +++ b/assets/icons/radix/file.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/font-bold.svg b/assets/icons/radix/font-bold.svg new file mode 100644 index 0000000000..7dc6caf3b0 --- /dev/null +++ b/assets/icons/radix/font-bold.svg @@ -0,0 +1,6 @@ + + + diff --git a/assets/icons/radix/font-family.svg b/assets/icons/radix/font-family.svg new file mode 100644 index 0000000000..9134b9086d --- /dev/null +++ b/assets/icons/radix/font-family.svg @@ -0,0 +1,6 @@ + + + diff --git a/assets/icons/radix/font-italic.svg b/assets/icons/radix/font-italic.svg new file mode 100644 index 0000000000..6e6288d6bc --- /dev/null +++ b/assets/icons/radix/font-italic.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/font-roman.svg b/assets/icons/radix/font-roman.svg new file mode 100644 index 0000000000..c595b790fc --- /dev/null +++ b/assets/icons/radix/font-roman.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/font-size.svg b/assets/icons/radix/font-size.svg new file mode 100644 index 0000000000..e389a58d73 --- /dev/null +++ b/assets/icons/radix/font-size.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/font-style.svg b/assets/icons/radix/font-style.svg new file mode 100644 index 0000000000..31c3730130 --- /dev/null +++ b/assets/icons/radix/font-style.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/frame.svg b/assets/icons/radix/frame.svg new file mode 100644 index 0000000000..ec61a48efa --- /dev/null +++ b/assets/icons/radix/frame.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/framer-logo.svg b/assets/icons/radix/framer-logo.svg new file mode 100644 index 0000000000..68be3b317b --- /dev/null +++ b/assets/icons/radix/framer-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/gear.svg b/assets/icons/radix/gear.svg new file mode 100644 index 0000000000..52f9e17312 --- /dev/null +++ b/assets/icons/radix/gear.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/github-logo.svg b/assets/icons/radix/github-logo.svg new file mode 100644 index 0000000000..e46612cf56 --- /dev/null +++ b/assets/icons/radix/github-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/globe.svg b/assets/icons/radix/globe.svg new file mode 100644 index 0000000000..4728b827df --- /dev/null +++ b/assets/icons/radix/globe.svg @@ -0,0 +1,26 @@ + + + + + + diff --git a/assets/icons/radix/grid.svg b/assets/icons/radix/grid.svg new file mode 100644 index 0000000000..5d9af33572 --- /dev/null +++ b/assets/icons/radix/grid.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/group.svg b/assets/icons/radix/group.svg new file mode 100644 index 0000000000..c3c91d211f --- /dev/null +++ b/assets/icons/radix/group.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/half-1.svg b/assets/icons/radix/half-1.svg new file mode 100644 index 0000000000..9890e26bb8 --- /dev/null +++ b/assets/icons/radix/half-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/half-2.svg b/assets/icons/radix/half-2.svg new file mode 100644 index 0000000000..4db1d564cb --- /dev/null +++ b/assets/icons/radix/half-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/hamburger-menu.svg b/assets/icons/radix/hamburger-menu.svg new file mode 100644 index 0000000000..039168055b --- /dev/null +++ b/assets/icons/radix/hamburger-menu.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/hand.svg b/assets/icons/radix/hand.svg new file mode 100644 index 0000000000..12afac8f5f --- /dev/null +++ b/assets/icons/radix/hand.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/heading.svg b/assets/icons/radix/heading.svg new file mode 100644 index 0000000000..0a5e2caaf1 --- /dev/null +++ b/assets/icons/radix/heading.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/heart-filled.svg b/assets/icons/radix/heart-filled.svg new file mode 100644 index 0000000000..94928accd7 --- /dev/null +++ b/assets/icons/radix/heart-filled.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/heart.svg b/assets/icons/radix/heart.svg new file mode 100644 index 0000000000..91cbc450fd --- /dev/null +++ b/assets/icons/radix/heart.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/height.svg b/assets/icons/radix/height.svg new file mode 100644 index 0000000000..28424f4d51 --- /dev/null +++ b/assets/icons/radix/height.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/hobby-knife.svg b/assets/icons/radix/hobby-knife.svg new file mode 100644 index 0000000000..c2ed3fb1ed --- /dev/null +++ b/assets/icons/radix/hobby-knife.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/home.svg b/assets/icons/radix/home.svg new file mode 100644 index 0000000000..733bd79113 --- /dev/null +++ b/assets/icons/radix/home.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/iconjar-logo.svg b/assets/icons/radix/iconjar-logo.svg new file mode 100644 index 0000000000..c154b4e864 --- /dev/null +++ b/assets/icons/radix/iconjar-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/id-card.svg b/assets/icons/radix/id-card.svg new file mode 100644 index 0000000000..efde9ffa7e --- /dev/null +++ b/assets/icons/radix/id-card.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/image.svg b/assets/icons/radix/image.svg new file mode 100644 index 0000000000..0ff4475252 --- /dev/null +++ b/assets/icons/radix/image.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/info-circled.svg b/assets/icons/radix/info-circled.svg new file mode 100644 index 0000000000..4ab1b260e3 --- /dev/null +++ b/assets/icons/radix/info-circled.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/inner-shadow.svg b/assets/icons/radix/inner-shadow.svg new file mode 100644 index 0000000000..1056a7bffc --- /dev/null +++ b/assets/icons/radix/inner-shadow.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + diff --git a/assets/icons/radix/input.svg b/assets/icons/radix/input.svg new file mode 100644 index 0000000000..4ed4605b2c --- /dev/null +++ b/assets/icons/radix/input.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/instagram-logo.svg b/assets/icons/radix/instagram-logo.svg new file mode 100644 index 0000000000..5d78937966 --- /dev/null +++ b/assets/icons/radix/instagram-logo.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/assets/icons/radix/justify-center.svg b/assets/icons/radix/justify-center.svg new file mode 100644 index 0000000000..7999a4ea46 --- /dev/null +++ b/assets/icons/radix/justify-center.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/justify-end.svg b/assets/icons/radix/justify-end.svg new file mode 100644 index 0000000000..bb52f493d7 --- /dev/null +++ b/assets/icons/radix/justify-end.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/justify-start.svg b/assets/icons/radix/justify-start.svg new file mode 100644 index 0000000000..648ca0b603 --- /dev/null +++ b/assets/icons/radix/justify-start.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/justify-stretch.svg b/assets/icons/radix/justify-stretch.svg new file mode 100644 index 0000000000..83df0a8959 --- /dev/null +++ b/assets/icons/radix/justify-stretch.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/keyboard.svg b/assets/icons/radix/keyboard.svg new file mode 100644 index 0000000000..fc6f86bfc2 --- /dev/null +++ b/assets/icons/radix/keyboard.svg @@ -0,0 +1,7 @@ + + + + diff --git a/assets/icons/radix/lap-timer.svg b/assets/icons/radix/lap-timer.svg new file mode 100644 index 0000000000..1de0b3be6c --- /dev/null +++ b/assets/icons/radix/lap-timer.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/laptop.svg b/assets/icons/radix/laptop.svg new file mode 100644 index 0000000000..6aff5d6d44 --- /dev/null +++ b/assets/icons/radix/laptop.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/layers.svg b/assets/icons/radix/layers.svg new file mode 100644 index 0000000000..821993fc70 --- /dev/null +++ b/assets/icons/radix/layers.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/layout.svg b/assets/icons/radix/layout.svg new file mode 100644 index 0000000000..8e4a352f50 --- /dev/null +++ b/assets/icons/radix/layout.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/letter-case-capitalize.svg b/assets/icons/radix/letter-case-capitalize.svg new file mode 100644 index 0000000000..16617ecf7e --- /dev/null +++ b/assets/icons/radix/letter-case-capitalize.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/letter-case-lowercase.svg b/assets/icons/radix/letter-case-lowercase.svg new file mode 100644 index 0000000000..61aefb9aad --- /dev/null +++ b/assets/icons/radix/letter-case-lowercase.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/letter-case-toggle.svg b/assets/icons/radix/letter-case-toggle.svg new file mode 100644 index 0000000000..a021a2b922 --- /dev/null +++ b/assets/icons/radix/letter-case-toggle.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/letter-case-uppercase.svg b/assets/icons/radix/letter-case-uppercase.svg new file mode 100644 index 0000000000..ccd2be04e7 --- /dev/null +++ b/assets/icons/radix/letter-case-uppercase.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/letter-spacing.svg b/assets/icons/radix/letter-spacing.svg new file mode 100644 index 0000000000..073023e0f4 --- /dev/null +++ b/assets/icons/radix/letter-spacing.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/lightning-bolt.svg b/assets/icons/radix/lightning-bolt.svg new file mode 100644 index 0000000000..7c35df9cfe --- /dev/null +++ b/assets/icons/radix/lightning-bolt.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/line-height.svg b/assets/icons/radix/line-height.svg new file mode 100644 index 0000000000..1c302d1ffc --- /dev/null +++ b/assets/icons/radix/line-height.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/link-1.svg b/assets/icons/radix/link-1.svg new file mode 100644 index 0000000000..d5682b113e --- /dev/null +++ b/assets/icons/radix/link-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/link-2.svg b/assets/icons/radix/link-2.svg new file mode 100644 index 0000000000..be8370606e --- /dev/null +++ b/assets/icons/radix/link-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/link-break-1.svg b/assets/icons/radix/link-break-1.svg new file mode 100644 index 0000000000..05ae93e47a --- /dev/null +++ b/assets/icons/radix/link-break-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/link-break-2.svg b/assets/icons/radix/link-break-2.svg new file mode 100644 index 0000000000..78f28f98e8 --- /dev/null +++ b/assets/icons/radix/link-break-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/link-none-1.svg b/assets/icons/radix/link-none-1.svg new file mode 100644 index 0000000000..6ea56a386f --- /dev/null +++ b/assets/icons/radix/link-none-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/link-none-2.svg b/assets/icons/radix/link-none-2.svg new file mode 100644 index 0000000000..0b19d940d1 --- /dev/null +++ b/assets/icons/radix/link-none-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/linkedin-logo.svg b/assets/icons/radix/linkedin-logo.svg new file mode 100644 index 0000000000..0f0138bdf6 --- /dev/null +++ b/assets/icons/radix/linkedin-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/list-bullet.svg b/assets/icons/radix/list-bullet.svg new file mode 100644 index 0000000000..2630b95ef0 --- /dev/null +++ b/assets/icons/radix/list-bullet.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/lock-closed.svg b/assets/icons/radix/lock-closed.svg new file mode 100644 index 0000000000..3871b5d5ad --- /dev/null +++ b/assets/icons/radix/lock-closed.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/lock-open-1.svg b/assets/icons/radix/lock-open-1.svg new file mode 100644 index 0000000000..8f6bfd5bbf --- /dev/null +++ b/assets/icons/radix/lock-open-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/lock-open-2.svg b/assets/icons/radix/lock-open-2.svg new file mode 100644 index 0000000000..ce69f67f29 --- /dev/null +++ b/assets/icons/radix/lock-open-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/loop.svg b/assets/icons/radix/loop.svg new file mode 100644 index 0000000000..bfa90ed084 --- /dev/null +++ b/assets/icons/radix/loop.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/magic-wand.svg b/assets/icons/radix/magic-wand.svg new file mode 100644 index 0000000000..bbc9826aa5 --- /dev/null +++ b/assets/icons/radix/magic-wand.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/magnifying-glass.svg b/assets/icons/radix/magnifying-glass.svg new file mode 100644 index 0000000000..a3a89bfa50 --- /dev/null +++ b/assets/icons/radix/magnifying-glass.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/margin.svg b/assets/icons/radix/margin.svg new file mode 100644 index 0000000000..1a513b37d6 --- /dev/null +++ b/assets/icons/radix/margin.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/mask-off.svg b/assets/icons/radix/mask-off.svg new file mode 100644 index 0000000000..5f847668e8 --- /dev/null +++ b/assets/icons/radix/mask-off.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/mask-on.svg b/assets/icons/radix/mask-on.svg new file mode 100644 index 0000000000..684c1b934d --- /dev/null +++ b/assets/icons/radix/mask-on.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/minus-circled.svg b/assets/icons/radix/minus-circled.svg new file mode 100644 index 0000000000..2c6df4cebf --- /dev/null +++ b/assets/icons/radix/minus-circled.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/minus.svg b/assets/icons/radix/minus.svg new file mode 100644 index 0000000000..2b39602979 --- /dev/null +++ b/assets/icons/radix/minus.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/mix.svg b/assets/icons/radix/mix.svg new file mode 100644 index 0000000000..9412a01843 --- /dev/null +++ b/assets/icons/radix/mix.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/mixer-horizontal.svg b/assets/icons/radix/mixer-horizontal.svg new file mode 100644 index 0000000000..f29ba25548 --- /dev/null +++ b/assets/icons/radix/mixer-horizontal.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/mixer-vertical.svg b/assets/icons/radix/mixer-vertical.svg new file mode 100644 index 0000000000..dc85d3a9e7 --- /dev/null +++ b/assets/icons/radix/mixer-vertical.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/mobile.svg b/assets/icons/radix/mobile.svg new file mode 100644 index 0000000000..b62b6506ff --- /dev/null +++ b/assets/icons/radix/mobile.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/modulz-logo.svg b/assets/icons/radix/modulz-logo.svg new file mode 100644 index 0000000000..754b229db6 --- /dev/null +++ b/assets/icons/radix/modulz-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/moon.svg b/assets/icons/radix/moon.svg new file mode 100644 index 0000000000..1dac2ca212 --- /dev/null +++ b/assets/icons/radix/moon.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/move.svg b/assets/icons/radix/move.svg new file mode 100644 index 0000000000..3d0a0e56c9 --- /dev/null +++ b/assets/icons/radix/move.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/notion-logo.svg b/assets/icons/radix/notion-logo.svg new file mode 100644 index 0000000000..c2df152619 --- /dev/null +++ b/assets/icons/radix/notion-logo.svg @@ -0,0 +1,6 @@ + + + diff --git a/assets/icons/radix/opacity.svg b/assets/icons/radix/opacity.svg new file mode 100644 index 0000000000..a2d01bff82 --- /dev/null +++ b/assets/icons/radix/opacity.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/open-in-new-window.svg b/assets/icons/radix/open-in-new-window.svg new file mode 100644 index 0000000000..22baf82cff --- /dev/null +++ b/assets/icons/radix/open-in-new-window.svg @@ -0,0 +1,10 @@ + + + + + diff --git a/assets/icons/radix/outer-shadow.svg b/assets/icons/radix/outer-shadow.svg new file mode 100644 index 0000000000..b44e3d553c --- /dev/null +++ b/assets/icons/radix/outer-shadow.svg @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/assets/icons/radix/overline.svg b/assets/icons/radix/overline.svg new file mode 100644 index 0000000000..57262c76e6 --- /dev/null +++ b/assets/icons/radix/overline.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/padding.svg b/assets/icons/radix/padding.svg new file mode 100644 index 0000000000..483a25a27e --- /dev/null +++ b/assets/icons/radix/padding.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/paper-plane.svg b/assets/icons/radix/paper-plane.svg new file mode 100644 index 0000000000..37ad070300 --- /dev/null +++ b/assets/icons/radix/paper-plane.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/pause.svg b/assets/icons/radix/pause.svg new file mode 100644 index 0000000000..b399fb2f5a --- /dev/null +++ b/assets/icons/radix/pause.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/pencil-1.svg b/assets/icons/radix/pencil-1.svg new file mode 100644 index 0000000000..decf0122ef --- /dev/null +++ b/assets/icons/radix/pencil-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/pencil-2.svg b/assets/icons/radix/pencil-2.svg new file mode 100644 index 0000000000..2559a393a9 --- /dev/null +++ b/assets/icons/radix/pencil-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/person.svg b/assets/icons/radix/person.svg new file mode 100644 index 0000000000..051abcc703 --- /dev/null +++ b/assets/icons/radix/person.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/pie-chart.svg b/assets/icons/radix/pie-chart.svg new file mode 100644 index 0000000000..bb58e47274 --- /dev/null +++ b/assets/icons/radix/pie-chart.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/pilcrow.svg b/assets/icons/radix/pilcrow.svg new file mode 100644 index 0000000000..6996765fd6 --- /dev/null +++ b/assets/icons/radix/pilcrow.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/pin-bottom.svg b/assets/icons/radix/pin-bottom.svg new file mode 100644 index 0000000000..ad0842054f --- /dev/null +++ b/assets/icons/radix/pin-bottom.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/pin-left.svg b/assets/icons/radix/pin-left.svg new file mode 100644 index 0000000000..eb89b2912f --- /dev/null +++ b/assets/icons/radix/pin-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/pin-right.svg b/assets/icons/radix/pin-right.svg new file mode 100644 index 0000000000..89a98bae4e --- /dev/null +++ b/assets/icons/radix/pin-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/pin-top.svg b/assets/icons/radix/pin-top.svg new file mode 100644 index 0000000000..edfeb64d5d --- /dev/null +++ b/assets/icons/radix/pin-top.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/play.svg b/assets/icons/radix/play.svg new file mode 100644 index 0000000000..92af9e1ae7 --- /dev/null +++ b/assets/icons/radix/play.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/plus-circled.svg b/assets/icons/radix/plus-circled.svg new file mode 100644 index 0000000000..808ddc4c2c --- /dev/null +++ b/assets/icons/radix/plus-circled.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/plus.svg b/assets/icons/radix/plus.svg new file mode 100644 index 0000000000..57ce90219b --- /dev/null +++ b/assets/icons/radix/plus.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/question-mark-circled.svg b/assets/icons/radix/question-mark-circled.svg new file mode 100644 index 0000000000..be99968787 --- /dev/null +++ b/assets/icons/radix/question-mark-circled.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/question-mark.svg b/assets/icons/radix/question-mark.svg new file mode 100644 index 0000000000..577aae5349 --- /dev/null +++ b/assets/icons/radix/question-mark.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/quote.svg b/assets/icons/radix/quote.svg new file mode 100644 index 0000000000..50205479c3 --- /dev/null +++ b/assets/icons/radix/quote.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/radiobutton.svg b/assets/icons/radix/radiobutton.svg new file mode 100644 index 0000000000..f0c3a60aee --- /dev/null +++ b/assets/icons/radix/radiobutton.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/reader.svg b/assets/icons/radix/reader.svg new file mode 100644 index 0000000000..e893cfa685 --- /dev/null +++ b/assets/icons/radix/reader.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/reload.svg b/assets/icons/radix/reload.svg new file mode 100644 index 0000000000..cf1dfb7fa2 --- /dev/null +++ b/assets/icons/radix/reload.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/reset.svg b/assets/icons/radix/reset.svg new file mode 100644 index 0000000000..f21a508514 --- /dev/null +++ b/assets/icons/radix/reset.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/resume.svg b/assets/icons/radix/resume.svg new file mode 100644 index 0000000000..79cdec2374 --- /dev/null +++ b/assets/icons/radix/resume.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/rocket.svg b/assets/icons/radix/rocket.svg new file mode 100644 index 0000000000..2226aacb1a --- /dev/null +++ b/assets/icons/radix/rocket.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/rotate-counter-clockwise.svg b/assets/icons/radix/rotate-counter-clockwise.svg new file mode 100644 index 0000000000..c43c90b90b --- /dev/null +++ b/assets/icons/radix/rotate-counter-clockwise.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/row-spacing.svg b/assets/icons/radix/row-spacing.svg new file mode 100644 index 0000000000..e155bd5947 --- /dev/null +++ b/assets/icons/radix/row-spacing.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/rows.svg b/assets/icons/radix/rows.svg new file mode 100644 index 0000000000..fb4ca0f9e3 --- /dev/null +++ b/assets/icons/radix/rows.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/ruler-horizontal.svg b/assets/icons/radix/ruler-horizontal.svg new file mode 100644 index 0000000000..db6f1ef488 --- /dev/null +++ b/assets/icons/radix/ruler-horizontal.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/ruler-square.svg b/assets/icons/radix/ruler-square.svg new file mode 100644 index 0000000000..7de70cc5dc --- /dev/null +++ b/assets/icons/radix/ruler-square.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/scissors.svg b/assets/icons/radix/scissors.svg new file mode 100644 index 0000000000..2893b34712 --- /dev/null +++ b/assets/icons/radix/scissors.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/section.svg b/assets/icons/radix/section.svg new file mode 100644 index 0000000000..1e939e2b2f --- /dev/null +++ b/assets/icons/radix/section.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/sewing-pin-filled.svg b/assets/icons/radix/sewing-pin-filled.svg new file mode 100644 index 0000000000..97f6f1120d --- /dev/null +++ b/assets/icons/radix/sewing-pin-filled.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/sewing-pin-solid.svg b/assets/icons/radix/sewing-pin-solid.svg new file mode 100644 index 0000000000..97f6f1120d --- /dev/null +++ b/assets/icons/radix/sewing-pin-solid.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/sewing-pin.svg b/assets/icons/radix/sewing-pin.svg new file mode 100644 index 0000000000..068dfd7bdf --- /dev/null +++ b/assets/icons/radix/sewing-pin.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/shadow-inner.svg b/assets/icons/radix/shadow-inner.svg new file mode 100644 index 0000000000..4d073bf35f --- /dev/null +++ b/assets/icons/radix/shadow-inner.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + diff --git a/assets/icons/radix/shadow-none.svg b/assets/icons/radix/shadow-none.svg new file mode 100644 index 0000000000..b02d3466ad --- /dev/null +++ b/assets/icons/radix/shadow-none.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + diff --git a/assets/icons/radix/shadow-outer.svg b/assets/icons/radix/shadow-outer.svg new file mode 100644 index 0000000000..dc7ea84087 --- /dev/null +++ b/assets/icons/radix/shadow-outer.svg @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/assets/icons/radix/shadow.svg b/assets/icons/radix/shadow.svg new file mode 100644 index 0000000000..c991af6156 --- /dev/null +++ b/assets/icons/radix/shadow.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + diff --git a/assets/icons/radix/share-1.svg b/assets/icons/radix/share-1.svg new file mode 100644 index 0000000000..58328e4d1e --- /dev/null +++ b/assets/icons/radix/share-1.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/share-2.svg b/assets/icons/radix/share-2.svg new file mode 100644 index 0000000000..1302ea5fbe --- /dev/null +++ b/assets/icons/radix/share-2.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/shuffle.svg b/assets/icons/radix/shuffle.svg new file mode 100644 index 0000000000..8670e1a048 --- /dev/null +++ b/assets/icons/radix/shuffle.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/size.svg b/assets/icons/radix/size.svg new file mode 100644 index 0000000000..dece8c5182 --- /dev/null +++ b/assets/icons/radix/size.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/sketch-logo.svg b/assets/icons/radix/sketch-logo.svg new file mode 100644 index 0000000000..6c54c4c825 --- /dev/null +++ b/assets/icons/radix/sketch-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/slash.svg b/assets/icons/radix/slash.svg new file mode 100644 index 0000000000..aa7dac30c1 --- /dev/null +++ b/assets/icons/radix/slash.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/slider.svg b/assets/icons/radix/slider.svg new file mode 100644 index 0000000000..66e0452bc0 --- /dev/null +++ b/assets/icons/radix/slider.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/space-between-horizontally.svg b/assets/icons/radix/space-between-horizontally.svg new file mode 100644 index 0000000000..a71638d52b --- /dev/null +++ b/assets/icons/radix/space-between-horizontally.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/space-between-vertically.svg b/assets/icons/radix/space-between-vertically.svg new file mode 100644 index 0000000000..bae247222f --- /dev/null +++ b/assets/icons/radix/space-between-vertically.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/space-evenly-horizontally.svg b/assets/icons/radix/space-evenly-horizontally.svg new file mode 100644 index 0000000000..70169492e4 --- /dev/null +++ b/assets/icons/radix/space-evenly-horizontally.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/space-evenly-vertically.svg b/assets/icons/radix/space-evenly-vertically.svg new file mode 100644 index 0000000000..469b4c05d4 --- /dev/null +++ b/assets/icons/radix/space-evenly-vertically.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/speaker-loud.svg b/assets/icons/radix/speaker-loud.svg new file mode 100644 index 0000000000..68982ee5e9 --- /dev/null +++ b/assets/icons/radix/speaker-loud.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/speaker-moderate.svg b/assets/icons/radix/speaker-moderate.svg new file mode 100644 index 0000000000..0f1d1b4210 --- /dev/null +++ b/assets/icons/radix/speaker-moderate.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/speaker-off.svg b/assets/icons/radix/speaker-off.svg new file mode 100644 index 0000000000..f60c35de7f --- /dev/null +++ b/assets/icons/radix/speaker-off.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/speaker-quiet.svg b/assets/icons/radix/speaker-quiet.svg new file mode 100644 index 0000000000..eb68cefcee --- /dev/null +++ b/assets/icons/radix/speaker-quiet.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/square.svg b/assets/icons/radix/square.svg new file mode 100644 index 0000000000..82843f51c3 --- /dev/null +++ b/assets/icons/radix/square.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/stack.svg b/assets/icons/radix/stack.svg new file mode 100644 index 0000000000..92426ffb0d --- /dev/null +++ b/assets/icons/radix/stack.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/star-filled.svg b/assets/icons/radix/star-filled.svg new file mode 100644 index 0000000000..2b17b7f579 --- /dev/null +++ b/assets/icons/radix/star-filled.svg @@ -0,0 +1,6 @@ + + + diff --git a/assets/icons/radix/star.svg b/assets/icons/radix/star.svg new file mode 100644 index 0000000000..23f09ad7b2 --- /dev/null +++ b/assets/icons/radix/star.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/stitches-logo.svg b/assets/icons/radix/stitches-logo.svg new file mode 100644 index 0000000000..319a1481f3 --- /dev/null +++ b/assets/icons/radix/stitches-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/stop.svg b/assets/icons/radix/stop.svg new file mode 100644 index 0000000000..57aac59cab --- /dev/null +++ b/assets/icons/radix/stop.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/stopwatch.svg b/assets/icons/radix/stopwatch.svg new file mode 100644 index 0000000000..ce5661e5cc --- /dev/null +++ b/assets/icons/radix/stopwatch.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/stretch-horizontally.svg b/assets/icons/radix/stretch-horizontally.svg new file mode 100644 index 0000000000..37977363b3 --- /dev/null +++ b/assets/icons/radix/stretch-horizontally.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/stretch-vertically.svg b/assets/icons/radix/stretch-vertically.svg new file mode 100644 index 0000000000..c4b1fe79ce --- /dev/null +++ b/assets/icons/radix/stretch-vertically.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/strikethrough.svg b/assets/icons/radix/strikethrough.svg new file mode 100644 index 0000000000..b814ef420a --- /dev/null +++ b/assets/icons/radix/strikethrough.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/sun.svg b/assets/icons/radix/sun.svg new file mode 100644 index 0000000000..1807a51b4c --- /dev/null +++ b/assets/icons/radix/sun.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/switch.svg b/assets/icons/radix/switch.svg new file mode 100644 index 0000000000..6dea528ce9 --- /dev/null +++ b/assets/icons/radix/switch.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/symbol.svg b/assets/icons/radix/symbol.svg new file mode 100644 index 0000000000..b529b2b08b --- /dev/null +++ b/assets/icons/radix/symbol.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/table.svg b/assets/icons/radix/table.svg new file mode 100644 index 0000000000..8ff059b847 --- /dev/null +++ b/assets/icons/radix/table.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/target.svg b/assets/icons/radix/target.svg new file mode 100644 index 0000000000..d67989e01f --- /dev/null +++ b/assets/icons/radix/target.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/text-align-bottom.svg b/assets/icons/radix/text-align-bottom.svg new file mode 100644 index 0000000000..862a5aeb88 --- /dev/null +++ b/assets/icons/radix/text-align-bottom.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/text-align-center.svg b/assets/icons/radix/text-align-center.svg new file mode 100644 index 0000000000..673cf8cd0a --- /dev/null +++ b/assets/icons/radix/text-align-center.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/text-align-justify.svg b/assets/icons/radix/text-align-justify.svg new file mode 100644 index 0000000000..df877f9513 --- /dev/null +++ b/assets/icons/radix/text-align-justify.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/text-align-left.svg b/assets/icons/radix/text-align-left.svg new file mode 100644 index 0000000000..b7a64fbd43 --- /dev/null +++ b/assets/icons/radix/text-align-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/text-align-middle.svg b/assets/icons/radix/text-align-middle.svg new file mode 100644 index 0000000000..e739d04efa --- /dev/null +++ b/assets/icons/radix/text-align-middle.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/text-align-right.svg b/assets/icons/radix/text-align-right.svg new file mode 100644 index 0000000000..e7609908ff --- /dev/null +++ b/assets/icons/radix/text-align-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/text-align-top.svg b/assets/icons/radix/text-align-top.svg new file mode 100644 index 0000000000..21660fe7d3 --- /dev/null +++ b/assets/icons/radix/text-align-top.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/text-none.svg b/assets/icons/radix/text-none.svg new file mode 100644 index 0000000000..2a87f9372a --- /dev/null +++ b/assets/icons/radix/text-none.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/text.svg b/assets/icons/radix/text.svg new file mode 100644 index 0000000000..bd41d8ac19 --- /dev/null +++ b/assets/icons/radix/text.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/thick-arrow-down.svg b/assets/icons/radix/thick-arrow-down.svg new file mode 100644 index 0000000000..32923bec58 --- /dev/null +++ b/assets/icons/radix/thick-arrow-down.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/thick-arrow-left.svg b/assets/icons/radix/thick-arrow-left.svg new file mode 100644 index 0000000000..0cfd863903 --- /dev/null +++ b/assets/icons/radix/thick-arrow-left.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/thick-arrow-right.svg b/assets/icons/radix/thick-arrow-right.svg new file mode 100644 index 0000000000..a0cb605693 --- /dev/null +++ b/assets/icons/radix/thick-arrow-right.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/thick-arrow-up.svg b/assets/icons/radix/thick-arrow-up.svg new file mode 100644 index 0000000000..68687be28d --- /dev/null +++ b/assets/icons/radix/thick-arrow-up.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/timer.svg b/assets/icons/radix/timer.svg new file mode 100644 index 0000000000..20c52dff95 --- /dev/null +++ b/assets/icons/radix/timer.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/tokens.svg b/assets/icons/radix/tokens.svg new file mode 100644 index 0000000000..2bbbc82030 --- /dev/null +++ b/assets/icons/radix/tokens.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/track-next.svg b/assets/icons/radix/track-next.svg new file mode 100644 index 0000000000..24fd40e36f --- /dev/null +++ b/assets/icons/radix/track-next.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/track-previous.svg b/assets/icons/radix/track-previous.svg new file mode 100644 index 0000000000..d99e7ab53f --- /dev/null +++ b/assets/icons/radix/track-previous.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/transform.svg b/assets/icons/radix/transform.svg new file mode 100644 index 0000000000..e913ccc9a7 --- /dev/null +++ b/assets/icons/radix/transform.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/transparency-grid.svg b/assets/icons/radix/transparency-grid.svg new file mode 100644 index 0000000000..6559ef8c2b --- /dev/null +++ b/assets/icons/radix/transparency-grid.svg @@ -0,0 +1,9 @@ + + + diff --git a/assets/icons/radix/trash.svg b/assets/icons/radix/trash.svg new file mode 100644 index 0000000000..18780e492c --- /dev/null +++ b/assets/icons/radix/trash.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/triangle-down.svg b/assets/icons/radix/triangle-down.svg new file mode 100644 index 0000000000..ebfd8f2a12 --- /dev/null +++ b/assets/icons/radix/triangle-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/radix/triangle-left.svg b/assets/icons/radix/triangle-left.svg new file mode 100644 index 0000000000..0014139716 --- /dev/null +++ b/assets/icons/radix/triangle-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/radix/triangle-right.svg b/assets/icons/radix/triangle-right.svg new file mode 100644 index 0000000000..aed1393b9c --- /dev/null +++ b/assets/icons/radix/triangle-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/radix/triangle-up.svg b/assets/icons/radix/triangle-up.svg new file mode 100644 index 0000000000..5eb1b416d3 --- /dev/null +++ b/assets/icons/radix/triangle-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/radix/twitter-logo.svg b/assets/icons/radix/twitter-logo.svg new file mode 100644 index 0000000000..7dcf2f58eb --- /dev/null +++ b/assets/icons/radix/twitter-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/underline.svg b/assets/icons/radix/underline.svg new file mode 100644 index 0000000000..3344685097 --- /dev/null +++ b/assets/icons/radix/underline.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/update.svg b/assets/icons/radix/update.svg new file mode 100644 index 0000000000..b529b2b08b --- /dev/null +++ b/assets/icons/radix/update.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/upload.svg b/assets/icons/radix/upload.svg new file mode 100644 index 0000000000..a7f6bddb2e --- /dev/null +++ b/assets/icons/radix/upload.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/value-none.svg b/assets/icons/radix/value-none.svg new file mode 100644 index 0000000000..a86c08be1a --- /dev/null +++ b/assets/icons/radix/value-none.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/value.svg b/assets/icons/radix/value.svg new file mode 100644 index 0000000000..59dd7d9373 --- /dev/null +++ b/assets/icons/radix/value.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/vercel-logo.svg b/assets/icons/radix/vercel-logo.svg new file mode 100644 index 0000000000..5466fd9f0e --- /dev/null +++ b/assets/icons/radix/vercel-logo.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/video.svg b/assets/icons/radix/video.svg new file mode 100644 index 0000000000..e405396bef --- /dev/null +++ b/assets/icons/radix/video.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/view-grid.svg b/assets/icons/radix/view-grid.svg new file mode 100644 index 0000000000..04825a870b --- /dev/null +++ b/assets/icons/radix/view-grid.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/view-horizontal.svg b/assets/icons/radix/view-horizontal.svg new file mode 100644 index 0000000000..2ca7336b99 --- /dev/null +++ b/assets/icons/radix/view-horizontal.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/view-none.svg b/assets/icons/radix/view-none.svg new file mode 100644 index 0000000000..71b08a46d2 --- /dev/null +++ b/assets/icons/radix/view-none.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/view-vertical.svg b/assets/icons/radix/view-vertical.svg new file mode 100644 index 0000000000..0c8f8164b4 --- /dev/null +++ b/assets/icons/radix/view-vertical.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/width.svg b/assets/icons/radix/width.svg new file mode 100644 index 0000000000..3ae2b56e3d --- /dev/null +++ b/assets/icons/radix/width.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/zoom-in.svg b/assets/icons/radix/zoom-in.svg new file mode 100644 index 0000000000..caac722ad0 --- /dev/null +++ b/assets/icons/radix/zoom-in.svg @@ -0,0 +1,8 @@ + + + diff --git a/assets/icons/radix/zoom-out.svg b/assets/icons/radix/zoom-out.svg new file mode 100644 index 0000000000..62046a9e0f --- /dev/null +++ b/assets/icons/radix/zoom-out.svg @@ -0,0 +1,8 @@ + + + From 10798384436ac46f2dc30205a086338214a93af0 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 22 Jun 2023 12:00:42 -0400 Subject: [PATCH 30/63] Add mic and mic-mute icons --- assets/icons/radix/mic-mute.svg | 3 +++ assets/icons/radix/mic.svg | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 assets/icons/radix/mic-mute.svg create mode 100644 assets/icons/radix/mic.svg diff --git a/assets/icons/radix/mic-mute.svg b/assets/icons/radix/mic-mute.svg new file mode 100644 index 0000000000..fe5f8201cc --- /dev/null +++ b/assets/icons/radix/mic-mute.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/radix/mic.svg b/assets/icons/radix/mic.svg new file mode 100644 index 0000000000..01f4c9bf66 --- /dev/null +++ b/assets/icons/radix/mic.svg @@ -0,0 +1,3 @@ + + + From 1a0b2bfef8a91db86555c657727fa5bffad10fab Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 22 Jun 2023 12:21:59 -0400 Subject: [PATCH 31/63] Update stream control icons --- assets/icons/radix/desktop-mute.svg | 4 ++++ crates/collab_ui/src/collab_titlebar_item.rs | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 assets/icons/radix/desktop-mute.svg diff --git a/assets/icons/radix/desktop-mute.svg b/assets/icons/radix/desktop-mute.svg new file mode 100644 index 0000000000..83d249176f --- /dev/null +++ b/assets/icons/radix/desktop-mute.svg @@ -0,0 +1,4 @@ + + + + diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index c0603421bc..0bdca28037 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -370,7 +370,7 @@ impl CollabTitlebarItem { .toggle_contacts_button .in_state(self.contacts_popover.is_some()) .style_for(state); - Svg::new("icons/user_plus_16.svg") + Svg::new("icons/radix/person.svg") .with_color(style.color) .constrained() .with_width(style.icon_width) @@ -406,10 +406,10 @@ impl CollabTitlebarItem { let icon; let tooltip; if room.read(cx).is_screen_sharing() { - icon = "icons/enable_screen_sharing_12.svg"; + icon = "icons/radix/desktop-mute.svg"; tooltip = "Stop Sharing Screen" } else { - icon = "icons/disable_screen_sharing_12.svg"; + icon = "icons/radix/desktop.svg"; tooltip = "Share Screen"; } @@ -451,11 +451,11 @@ impl CollabTitlebarItem { let tooltip; let background; if room.read(cx).is_muted().unwrap_or(false) { - icon = "icons/microphone_inactive_12.svg"; + icon = "icons/radix/mic-mute.svg"; tooltip = "Unmute microphone\nRight click for options"; background = Color::red(); } else { - icon = "icons/microphone_active_12.svg"; + icon = "icons/radix/mic.svg"; tooltip = "Mute microphone\nRight click for options"; background = Color::transparent_black(); } @@ -502,9 +502,9 @@ impl CollabTitlebarItem { } let is_shared = project.read(cx).is_shared(); - let label = if is_shared { "Unshare" } else { "Share" }; + let label = if is_shared { "Stop Sharing" } else { "Share" }; let tooltip = if is_shared { - "Unshare project from call participants" + "Stop sharing project with call participants" } else { "Share project with call participants" }; From 18c4d43ee718172cab2179100017fcc28ba172b3 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 22 Jun 2023 18:49:18 +0200 Subject: [PATCH 32/63] Add deafen button --- assets/icons/speakers_active_12.svg | 1 + assets/icons/speakers_inactive_12.svg | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 53 ++++++++++++++++++-- 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 assets/icons/speakers_active_12.svg create mode 100644 assets/icons/speakers_inactive_12.svg diff --git a/assets/icons/speakers_active_12.svg b/assets/icons/speakers_active_12.svg new file mode 100644 index 0000000000..10416a8f79 --- /dev/null +++ b/assets/icons/speakers_active_12.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/speakers_inactive_12.svg b/assets/icons/speakers_inactive_12.svg new file mode 100644 index 0000000000..a74990d983 --- /dev/null +++ b/assets/icons/speakers_inactive_12.svg @@ -0,0 +1 @@ + diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 0bdca28037..ddc3f53801 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,6 +1,7 @@ use crate::{ - contact_notification::ContactNotification, contacts_popover, face_pile::FacePile, toggle_mute, - toggle_screen_sharing, ToggleMute, ToggleScreenSharing, + contact_notification::ContactNotification, contacts_popover, face_pile::FacePile, + toggle_deafen, toggle_mute, toggle_screen_sharing, ToggleDeafen, ToggleMute, + ToggleScreenSharing, }; use call::{ActiveCall, ParticipantLocation, Room}; use client::{proto::PeerId, Client, ContactEventKind, SignIn, SignOut, User, UserStore}; @@ -88,7 +89,8 @@ impl View for CollabTitlebarItem { left_container .add_child(self.render_current_user(&workspace, &theme, &user, peer_id, cx)); left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx)); - right_container.add_child(self.render_toggle_microphone(&theme, &room, cx)); + right_container.add_child(self.render_toggle_mute(&theme, &room, cx)); + right_container.add_child(self.render_toggle_deafen(&theme, &room, cx)); right_container.add_child(self.render_toggle_screen_sharing_button(&theme, &room, cx)); } @@ -441,7 +443,7 @@ impl CollabTitlebarItem { .aligned() .into_any() } - fn render_toggle_microphone( + fn render_toggle_mute( &self, theme: &Theme, room: &ModelHandle, @@ -489,7 +491,50 @@ impl CollabTitlebarItem { .aligned() .into_any() } + fn render_toggle_deafen( + &self, + theme: &Theme, + room: &ModelHandle, + cx: &mut ViewContext, + ) -> AnyElement { + let icon; + let tooltip; + if room.read(cx).is_deafened().unwrap_or(false) { + icon = "icons/speakers_inactive_12.svg"; + tooltip = "Unmute speakers\nRight click for options"; + } else { + icon = "icons/speakers_active_12.svg"; + tooltip = "Mute speakers\nRight click for options"; + } + let titlebar = &theme.workspace.titlebar; + MouseEventHandler::::new(0, cx, |state, _| { + let style = titlebar.call_control.style_for(state); + Svg::new(icon) + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, _, cx| { + toggle_deafen(&Default::default(), cx) + }) + .with_tooltip::( + 0, + tooltip.into(), + Some(Box::new(ToggleDeafen)), + theme.tooltip.clone(), + cx, + ) + .aligned() + .into_any() + } fn render_in_call_share_unshare_button( &self, workspace: &ViewHandle, From f774dbfe2e5db9c6fd79b17e2879b397fbaa65c1 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 22 Jun 2023 18:58:11 +0200 Subject: [PATCH 33/63] Use new icons, delete old ones --- assets/icons/microphone_active_12.svg | 4 ---- assets/icons/microphone_inactive_12.svg | 5 ----- assets/icons/speakers_active_12.svg | 1 - assets/icons/speakers_inactive_12.svg | 1 - crates/collab_ui/src/collab_titlebar_item.rs | 4 ++-- 5 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 assets/icons/microphone_active_12.svg delete mode 100644 assets/icons/microphone_inactive_12.svg delete mode 100644 assets/icons/speakers_active_12.svg delete mode 100644 assets/icons/speakers_inactive_12.svg diff --git a/assets/icons/microphone_active_12.svg b/assets/icons/microphone_active_12.svg deleted file mode 100644 index 797e64820b..0000000000 --- a/assets/icons/microphone_active_12.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/assets/icons/microphone_inactive_12.svg b/assets/icons/microphone_inactive_12.svg deleted file mode 100644 index 90ac393fc2..0000000000 --- a/assets/icons/microphone_inactive_12.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/assets/icons/speakers_active_12.svg b/assets/icons/speakers_active_12.svg deleted file mode 100644 index 10416a8f79..0000000000 --- a/assets/icons/speakers_active_12.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/speakers_inactive_12.svg b/assets/icons/speakers_inactive_12.svg deleted file mode 100644 index a74990d983..0000000000 --- a/assets/icons/speakers_inactive_12.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index ddc3f53801..1cfa18f432 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -500,10 +500,10 @@ impl CollabTitlebarItem { let icon; let tooltip; if room.read(cx).is_deafened().unwrap_or(false) { - icon = "icons/speakers_inactive_12.svg"; + icon = "icons/radix/speaker-off.svg"; tooltip = "Unmute speakers\nRight click for options"; } else { - icon = "icons/speakers_active_12.svg"; + icon = "icons/radix/speaker-loud.svg"; tooltip = "Mute speakers\nRight click for options"; } From 8bd9fe1fb087be224bf082ab9ee989d49645570e Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 22 Jun 2023 20:05:06 +0200 Subject: [PATCH 34/63] Deafen now also mutes microphone --- crates/call/src/room.rs | 46 +++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 0c0855fd6c..714a1b19b5 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1176,24 +1176,31 @@ impl Room { }) }) } - - pub fn toggle_mute(&mut self, cx: &mut ModelContext) -> Result>> { - if let Some(live_kit) = self.live_kit.as_mut() { - match &mut live_kit.microphone_track { - LocalTrack::None => Err(anyhow!("microphone was not shared")), - LocalTrack::Pending { muted, .. } => { - *muted = !*muted; - Ok(Task::Ready(Some(Ok(())))) - } - LocalTrack::Published { - track_publication, - muted, - } => { - *muted = !*muted; - - Ok(cx.background().spawn(track_publication.set_mute(*muted))) - } + fn set_mute( + live_kit: &mut LiveKitRoom, + should_mute: bool, + cx: &mut ModelContext, + ) -> Result>> { + match &mut live_kit.microphone_track { + LocalTrack::None => Err(anyhow!("microphone was not shared")), + LocalTrack::Pending { muted, .. } => { + *muted = should_mute; + Ok(Task::Ready(Some(Ok(())))) } + LocalTrack::Published { + track_publication, + muted, + } => { + *muted = should_mute; + + Ok(cx.background().spawn(track_publication.set_mute(*muted))) + } + } + } + pub fn toggle_mute(&mut self, cx: &mut ModelContext) -> Result>> { + let should_mute = self.is_muted().unwrap_or(false); + if let Some(live_kit) = self.live_kit.as_mut() { + Self::set_mute(live_kit, !should_mute, cx) } else { Err(anyhow!("LiveKit not started")) } @@ -1204,9 +1211,9 @@ impl Room { (*live_kit).deafened = !live_kit.deafened } - if let Some(live_kit) = &self.live_kit { + if let Some(live_kit) = self.live_kit.as_mut() { let mut tasks = Vec::with_capacity(self.remote_participants.len()); - + let _ = Self::set_mute(live_kit, live_kit.deafened, cx)?; // todo (osiewicz): we probably want to schedule it on fg/bg? for participant in self.remote_participants.values() { for track in live_kit .room @@ -1215,7 +1222,6 @@ impl Room { tasks.push(cx.foreground().spawn(track.set_enabled(!live_kit.deafened))); } } - Ok(cx.foreground().spawn(async move { for task in tasks { task.await?; From 437e41f99d9d712cbe8e2a886c6619fc05cf610b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 23 Jun 2023 11:52:06 +0200 Subject: [PATCH 35/63] Add styles for microphone and speakers buttons --- crates/collab_ui/src/collab_titlebar_item.rs | 29 ++++++--- crates/theme/src/theme.rs | 2 + styles/src/styleTree/workspace.ts | 64 ++++++++++++++++++++ 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 1cfa18f432..17f4f06ff9 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -451,21 +451,22 @@ impl CollabTitlebarItem { ) -> AnyElement { let icon; let tooltip; - let background; - if room.read(cx).is_muted().unwrap_or(false) { + let is_muted = room.read(cx).is_muted().unwrap_or(false); + if is_muted { icon = "icons/radix/mic-mute.svg"; tooltip = "Unmute microphone\nRight click for options"; - background = Color::red(); } else { icon = "icons/radix/mic.svg"; tooltip = "Mute microphone\nRight click for options"; - background = Color::transparent_black(); } let titlebar = &theme.workspace.titlebar; MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar.call_control.style_for(state); - Svg::new(icon) + let style = titlebar + .toggle_microphone_button + .in_state(is_muted) + .style_for(state); + let image = Svg::new(icon) .with_color(style.color) .constrained() .with_width(style.icon_width) @@ -474,8 +475,12 @@ impl CollabTitlebarItem { .with_width(style.button_width) .with_height(style.button_width) .contained() - .with_style(style.container) - .with_background_color(background) + .with_style(style.container); + if let Some(color) = style.container.background_color { + image.with_background_color(color) + } else { + image + } }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, _, cx| { @@ -499,7 +504,8 @@ impl CollabTitlebarItem { ) -> AnyElement { let icon; let tooltip; - if room.read(cx).is_deafened().unwrap_or(false) { + let is_deafened = room.read(cx).is_deafened().unwrap_or(false); + if is_deafened { icon = "icons/radix/speaker-off.svg"; tooltip = "Unmute speakers\nRight click for options"; } else { @@ -509,7 +515,10 @@ impl CollabTitlebarItem { let titlebar = &theme.workspace.titlebar; MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar.call_control.style_for(state); + let style = titlebar + .toggle_speakers_button + .in_state(is_deafened) + .style_for(state); Svg::new(icon) .with_color(style.color) .constrained() diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 69c06c85ad..bccb74a7ac 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -133,6 +133,8 @@ pub struct Titlebar { pub share_button: Toggleable>, pub call_control: Interactive, pub toggle_contacts_button: Toggleable>, + pub toggle_microphone_button: Toggleable>, + pub toggle_speakers_button: Toggleable>, pub user_menu_button: Toggleable>, pub toggle_contacts_badge: ContainerStyle, } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index d7f994d98e..0e00f6f5bc 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -343,6 +343,70 @@ export default function workspace(colorScheme: ColorScheme) { }, }, }), + toggleMicrophoneButton: toggleable({ + base: interactive({ + base: { + margin: { left: itemSpacing }, + cornerRadius: 6, + color: foreground(layer, "variant"), + iconWidth: 14, + buttonWidth: 20, + }, + state: { + clicked: { + background: background(layer, "variant", "pressed"), + }, + hovered: { + background: background(layer, "variant", "hovered"), + }, + }, + }), + state: { + active: { + default: { + background: background(layer, "on", "default"), + }, + hovered: { + background: background(layer, "on", "hovered"), + }, + clicked: { + background: background(layer, "on", "pressed"), + }, + }, + }, + }), + toggleSpeakersButton: toggleable({ + base: interactive({ + base: { + margin: { left: itemSpacing }, + cornerRadius: 6, + color: foreground(layer, "variant"), + iconWidth: 14, + buttonWidth: 20, + }, + state: { + clicked: { + background: background(layer, "variant", "pressed"), + }, + hovered: { + background: background(layer, "variant", "hovered"), + }, + }, + }), + state: { + active: { + default: { + background: background(layer, "on", "default"), + }, + hovered: { + background: background(layer, "on", "hovered"), + }, + clicked: { + background: background(layer, "on", "pressed"), + }, + }, + }, + }), userMenuButton: merge(titlebarButton, { inactive: { default: { From 62786cd508c62beb63309d4c2843f4d52fa0a78f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 23 Jun 2023 13:15:25 +0200 Subject: [PATCH 36/63] Add button --- crates/collab_ui/src/collab_titlebar_item.rs | 41 +++++++++++++++++++- crates/collab_ui/src/collab_ui.rs | 1 + 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 17f4f06ff9..81e2736164 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,6 +1,6 @@ use crate::{ contact_notification::ContactNotification, contacts_popover, face_pile::FacePile, - toggle_deafen, toggle_mute, toggle_screen_sharing, ToggleDeafen, ToggleMute, + toggle_deafen, toggle_mute, toggle_screen_sharing, LeaveCall, ToggleDeafen, ToggleMute, ToggleScreenSharing, }; use call::{ActiveCall, ParticipantLocation, Room}; @@ -85,7 +85,7 @@ impl View for CollabTitlebarItem { { right_container .add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx)); - + right_container.add_child(self.render_leave_call(&theme, cx)); left_container .add_child(self.render_current_user(&workspace, &theme, &user, peer_id, cx)); left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx)); @@ -544,6 +544,43 @@ impl CollabTitlebarItem { .aligned() .into_any() } + fn render_leave_call(&self, theme: &Theme, cx: &mut ViewContext) -> AnyElement { + let icon = "icons/radix/exit.svg"; + let tooltip = "Leave call"; + + let titlebar = &theme.workspace.titlebar; + MouseEventHandler::::new(0, cx, |state, _| { + let style = titlebar + .toggle_speakers_button + .in_state(false) + .style_for(state); + Svg::new(icon) + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, _, cx| { + ActiveCall::global(cx) + .update(cx, |call, cx| call.hang_up(cx)) + .detach_and_log_err(cx); + }) + .with_tooltip::( + 0, + tooltip.into(), + Some(Box::new(LeaveCall)), + theme.tooltip.clone(), + cx, + ) + .aligned() + .into_any() + } fn render_in_call_share_unshare_button( &self, workspace: &ViewHandle, diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 38a39762f4..a809b9c7e6 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -22,6 +22,7 @@ actions!( ToggleScreenSharing, ToggleMute, ToggleDeafen, + LeaveCall, ShareMicrophone ] ); From 6c676121f286fc85a7ca652a3b8c5d7d3aa6db85 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 23 Jun 2023 13:22:33 +0200 Subject: [PATCH 37/63] Fixed mic's state not being updated in titlebar if user mutes a mic via command palette --- crates/call/src/room.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 714a1b19b5..00c6b43eb3 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1185,6 +1185,7 @@ impl Room { LocalTrack::None => Err(anyhow!("microphone was not shared")), LocalTrack::Pending { muted, .. } => { *muted = should_mute; + cx.notify(); Ok(Task::Ready(Some(Ok(())))) } LocalTrack::Published { @@ -1192,7 +1193,7 @@ impl Room { muted, } => { *muted = should_mute; - + cx.notify(); Ok(cx.background().spawn(track_publication.set_mute(*muted))) } } @@ -1207,11 +1208,9 @@ impl Room { } pub fn toggle_deafen(&mut self, cx: &mut ModelContext) -> Result>> { - if let Some(live_kit) = &mut self.live_kit { - (*live_kit).deafened = !live_kit.deafened - } - if let Some(live_kit) = self.live_kit.as_mut() { + (*live_kit).deafened = !live_kit.deafened; + cx.notify(); let mut tasks = Vec::with_capacity(self.remote_participants.len()); let _ = Self::set_mute(live_kit, live_kit.deafened, cx)?; // todo (osiewicz): we probably want to schedule it on fg/bg? for participant in self.remote_participants.values() { From 4b52ff65c138042a6b23ee0de9f94f8f71574a8a Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 23 Jun 2023 13:30:40 +0200 Subject: [PATCH 38/63] Add styles for button --- crates/collab_ui/src/collab_titlebar_item.rs | 5 +---- crates/theme/src/theme.rs | 1 + styles/src/styleTree/workspace.ts | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 81e2736164..cb696dab9d 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -550,10 +550,7 @@ impl CollabTitlebarItem { let titlebar = &theme.workspace.titlebar; MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar - .toggle_speakers_button - .in_state(false) - .style_for(state); + let style = titlebar.leave_call_button.style_for(state); Svg::new(icon) .with_color(style.color) .constrained() diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index bccb74a7ac..b4a4ba69fd 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -135,6 +135,7 @@ pub struct Titlebar { pub toggle_contacts_button: Toggleable>, pub toggle_microphone_button: Toggleable>, pub toggle_speakers_button: Toggleable>, + pub leave_call_button: Interactive, pub user_menu_button: Toggleable>, pub toggle_contacts_badge: ContainerStyle, } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 0e00f6f5bc..abc237468a 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -407,6 +407,24 @@ export default function workspace(colorScheme: ColorScheme) { }, }, }), + leaveCallButton: interactive({ + base: { + margin: { left: itemSpacing }, + cornerRadius: 6, + color: foreground(layer, "variant"), + iconWidth: 14, + buttonWidth: 20, + }, + state: { + clicked: { + background: background(layer, "variant", "pressed"), + }, + hovered: { + background: background(layer, "variant", "hovered"), + }, + }, + }), + userMenuButton: merge(titlebarButton, { inactive: { default: { From a9b5c1d867b35531d1c15b888401609b86dee657 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 26 Jun 2023 12:45:50 +0200 Subject: [PATCH 39/63] Remove unnecessary call to context.notify --- crates/call/src/room.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 00c6b43eb3..48a35b29f6 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1210,8 +1210,9 @@ impl Room { pub fn toggle_deafen(&mut self, cx: &mut ModelContext) -> Result>> { if let Some(live_kit) = self.live_kit.as_mut() { (*live_kit).deafened = !live_kit.deafened; - cx.notify(); + let mut tasks = Vec::with_capacity(self.remote_participants.len()); + // Context notification is sent within set_mute itself. let _ = Self::set_mute(live_kit, live_kit.deafened, cx)?; // todo (osiewicz): we probably want to schedule it on fg/bg? for participant in self.remote_participants.values() { for track in live_kit From 55f06dcdb54914c2d8570d5a091cc846be597bce Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 26 Jun 2023 13:31:46 +0200 Subject: [PATCH 40/63] Add headers and footers to file finder --- crates/picker/src/picker.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 69f16e4949..ee1eaf3bc9 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -45,6 +45,12 @@ pub trait PickerDelegate: Sized + 'static { fn center_selection_after_match_updates(&self) -> bool { false } + fn render_header(&self, cx: &AppContext) -> Option>> { + None + } + fn render_footer(&self, cx: &AppContext) -> Option>> { + None + } } impl Entity for Picker { @@ -77,6 +83,7 @@ impl View for Picker { .contained() .with_style(editor_style), ) + .with_children(self.delegate.render_header(cx)) .with_children(if match_count == 0 { if query.is_empty() { None @@ -118,6 +125,7 @@ impl View for Picker { .into_any(), ) }) + .with_children(self.delegate.render_footer(cx)) .contained() .with_style(container_style) .constrained() From f6edc6861397c07c166bd6d074fb928663b248e6 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 26 Jun 2023 17:07:33 +0200 Subject: [PATCH 41/63] picker: fix warnings --- crates/picker/src/picker.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index ee1eaf3bc9..33d6e84241 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -45,10 +45,10 @@ pub trait PickerDelegate: Sized + 'static { fn center_selection_after_match_updates(&self) -> bool { false } - fn render_header(&self, cx: &AppContext) -> Option>> { + fn render_header(&self, _cx: &AppContext) -> Option>> { None } - fn render_footer(&self, cx: &AppContext) -> Option>> { + fn render_footer(&self, _cx: &AppContext) -> Option>> { None } } From 71c0b7d74ded6f93c96830c71e653c5639d5a391 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 26 Jun 2023 18:56:30 +0200 Subject: [PATCH 42/63] Add styles for user menu --- crates/collab_ui/src/collab_titlebar_item.rs | 22 +++--- crates/theme/src/theme.rs | 11 +++ styles/src/styleTree/app.ts | 2 + styles/src/styleTree/titlebar.ts | 81 ++++++++++++++++++++ 4 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 styles/src/styleTree/titlebar.ts diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index cb696dab9d..52f0bc7d61 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -639,12 +639,17 @@ impl CollabTitlebarItem { avatar: Option>, cx: &mut ViewContext, ) -> AnyElement { - let titlebar = &theme.workspace.titlebar; - let avatar_style = &theme.workspace.titlebar.follower_avatar; + let tooltip = theme.tooltip.clone(); + let user_menu_button = &theme.titlebar; + let avatar_style = &user_menu_button.user_menu_button.avatar; Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar.call_control.style_for(state); + let style = user_menu_button + .user_menu_button + .user_menu + .inactive_state() + .style_for(state); let mut dropdown = Flex::row().align_children_center(); @@ -658,15 +663,15 @@ impl CollabTitlebarItem { dropdown .with_child( Svg::new("icons/caret_down_8.svg") - .with_color(style.color) + .with_color(theme.titlebar.user_menu_button.icon.color) .constrained() - .with_width(style.icon_width) + .with_width(theme.titlebar.user_menu_button.icon.width) .contained() .into_any(), ) .aligned() .constrained() - .with_height(style.button_width) + .with_height(style.width) .contained() .with_style(style.container) .into_any() @@ -679,11 +684,10 @@ impl CollabTitlebarItem { 0, "Toggle user menu".to_owned(), Some(Box::new(ToggleUserMenu)), - theme.tooltip.clone(), + tooltip, cx, ) - .contained() - .with_margin_left(theme.workspace.titlebar.item_spacing), + .contained(), ) .with_child( ChildView::new(&self.user_menu, cx) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index b4a4ba69fd..7de10db6c5 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -65,6 +65,7 @@ pub struct Theme { pub feedback: FeedbackStyle, pub welcome: WelcomeStyle, pub color_scheme: ColorScheme, + pub titlebar: UserMenu, } #[derive(Deserialize, Default, Clone)] @@ -140,6 +141,16 @@ pub struct Titlebar { pub toggle_contacts_badge: ContainerStyle, } +#[derive(Clone, Deserialize, Default)] +pub struct UserMenu { + pub user_menu_button: UserMenuButton, +} +#[derive(Clone, Deserialize, Default)] +pub struct UserMenuButton { + pub user_menu: Toggleable>, + pub avatar: AvatarStyle, + pub icon: Icon, +} #[derive(Copy, Clone, Deserialize, Default)] pub struct AvatarStyle { #[serde(flatten)] diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index 754443cc5f..d98e00383f 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -23,6 +23,7 @@ import feedback from "./feedback" import welcome from "./welcome" import copilot from "./copilot" import assistant from "./assistant" +import { titlebar } from "./titlebar" export default function app(colorScheme: ColorScheme): Object { return { @@ -36,6 +37,7 @@ export default function app(colorScheme: ColorScheme): Object { incomingCallNotification: incomingCallNotification(colorScheme), picker: picker(colorScheme), workspace: workspace(colorScheme), + titlebar: titlebar(colorScheme), copilot: copilot(colorScheme), welcome: welcome(colorScheme), contextMenu: contextMenu(colorScheme), diff --git a/styles/src/styleTree/titlebar.ts b/styles/src/styleTree/titlebar.ts new file mode 100644 index 0000000000..4c2c2147f8 --- /dev/null +++ b/styles/src/styleTree/titlebar.ts @@ -0,0 +1,81 @@ +import { ColorScheme } from "../common"; +import { interactive, toggleable } from "../element" +import { background, foreground, text } from "./components"; + +const titlebarButton = (theme: ColorScheme) => toggleable({ + base: interactive({ + base: { + cornerRadius: 6, + height: 24, + width: 24, + padding: { + top: 4, + bottom: 4, + left: 4, + right: 4, + }, + ...text(theme.lowest, "sans", { size: "xs" }), + background: background(theme.lowest), + }, + state: { + hovered: { + ...text(theme.lowest, "sans", "hovered", { + size: "xs", + }), + background: background(theme.lowest, "hovered"), + }, + clicked: { + ...text(theme.lowest, "sans", "pressed", { + size: "xs", + }), + background: background(theme.lowest, "pressed"), + }, + }, + }), + state: { + active: { + default: { + ...text(theme.lowest, "sans", "active", { size: "xs" }), + background: background(theme.middle), + }, + hovered: { + ...text(theme.lowest, "sans", "active", { size: "xs" }), + background: background(theme.middle, "hovered"), + }, + clicked: { + ...text(theme.lowest, "sans", "active", { size: "xs" }), + background: background(theme.middle, "pressed"), + }, + }, + } +}); + +/** +* Opens the User Menu when toggled +* +* When logged in shows the user's avatar and a chevron, +* When logged out only shows a chevron. +*/ +function userMenuButton(theme: ColorScheme) { + return { + userMenu: titlebarButton(theme), + avatar: { + icon_width: 16, + icon_height: 16, + cornerRadius: 4, + outerWidth: 10, + outerCornerRadius: 10 + }, + icon: { + width: 11, + height: 11, + color: foreground(theme.lowest) + } + } +} + +export function titlebar(theme: ColorScheme) { + return { + userMenuButton: userMenuButton(theme) + } +} From 3104275d87af9b4abd9cd8a927d3c389e8583249 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:19:18 +0200 Subject: [PATCH 43/63] Keep users muted once they undeafen themselves --- crates/call/src/room.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 48a35b29f6..3c94feae4c 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1213,7 +1213,11 @@ impl Room { let mut tasks = Vec::with_capacity(self.remote_participants.len()); // Context notification is sent within set_mute itself. - let _ = Self::set_mute(live_kit, live_kit.deafened, cx)?; // todo (osiewicz): we probably want to schedule it on fg/bg? + if live_kit.deafened { + // Unmute microphone only if we're going from unmuted -> muted state. + // We don't want to unmute user automatically. + let _ = Self::set_mute(live_kit, live_kit.deafened, cx)?; // todo (osiewicz): we probably want to schedule it on fg/bg? + } for participant in self.remote_participants.values() { for track in live_kit .room From 331800c14d174a3a54848a79a81890b294180a07 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 27 Jun 2023 11:34:12 -0400 Subject: [PATCH 44/63] Use `icon_button` for leave call --- styles/src/component/icon_button.ts | 54 +++++++++++++++++++++++++++++ styles/src/element/index.ts | 4 +-- styles/src/element/interactive.ts | 2 +- styles/src/styleTree/titlebar.ts | 4 ++- styles/src/styleTree/workspace.ts | 22 +++--------- 5 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 styles/src/component/icon_button.ts diff --git a/styles/src/component/icon_button.ts b/styles/src/component/icon_button.ts new file mode 100644 index 0000000000..d71996042b --- /dev/null +++ b/styles/src/component/icon_button.ts @@ -0,0 +1,54 @@ +import { ColorScheme } from "../common"; +import { interactive } from "../element"; +import { background, foreground } from "../styleTree/components"; +import { Margin } from "../types/zed"; + +interface IconButtonOptions { + color?: keyof ColorScheme['lowest']; + margin?: Partial; +} + +export function icon_button(theme: ColorScheme, { color, margin }: IconButtonOptions) { + if (!color) + color = "base"; + + const m = { + top: margin?.top ?? 0, + bottom: margin?.bottom ?? 0, + left: margin?.left ?? 0, + right: margin?.right ?? 0, + } + + return interactive({ + base: { + corner_radius: 4, + padding: { + top: 2, + bottom: 2, + left: 4, + right: 4, + }, + margin: m, + icon_width: 15, + icon_height: 15, + button_width: 23, + button_height: 19, + }, + state: { + default: { + background: background(theme.lowest, color), + color: foreground(theme.lowest, color), + }, + hovered: { + background: background(theme.lowest, color, "hovered"), + color: foreground(theme.lowest, color, "hovered"), + + }, + clicked: { + background: background(theme.lowest, color, "pressed"), + color: foreground(theme.lowest, color, "pressed"), + + }, + }, + }); +} diff --git a/styles/src/element/index.ts b/styles/src/element/index.ts index b1e3cfe415..81c911c7bd 100644 --- a/styles/src/element/index.ts +++ b/styles/src/element/index.ts @@ -1,4 +1,4 @@ -import { interactive } from "./interactive" +import { interactive, Interactive } from "./interactive" import { toggleable } from "./toggle" -export { interactive, toggleable } +export { interactive, Interactive, toggleable } diff --git a/styles/src/element/interactive.ts b/styles/src/element/interactive.ts index 1c0f393cff..79fee70cb9 100644 --- a/styles/src/element/interactive.ts +++ b/styles/src/element/interactive.ts @@ -8,7 +8,7 @@ type InteractiveState = | "selected" | "disabled" -type Interactive = { +export type Interactive = { default: T hovered?: T clicked?: T diff --git a/styles/src/styleTree/titlebar.ts b/styles/src/styleTree/titlebar.ts index 4c2c2147f8..3aa41359fa 100644 --- a/styles/src/styleTree/titlebar.ts +++ b/styles/src/styleTree/titlebar.ts @@ -58,11 +58,13 @@ const titlebarButton = (theme: ColorScheme) => toggleable({ */ function userMenuButton(theme: ColorScheme) { return { - userMenu: titlebarButton(theme), + user_menu: titlebarButton(theme), avatar: { icon_width: 16, icon_height: 16, cornerRadius: 4, + outer_corner_radius: 0, + outer_width: 0, outerWidth: 10, outerCornerRadius: 10 }, diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index abc237468a..a500988bf7 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -13,6 +13,7 @@ import statusBar from "./statusBar" import tabBar from "./tabBar" import { interactive } from "../element" import merge from "ts-deepmerge" +import { icon_button } from "../component/icon_button" export default function workspace(colorScheme: ColorScheme) { const layer = colorScheme.lowest const isLight = colorScheme.isLight @@ -252,7 +253,7 @@ export default function workspace(colorScheme: ColorScheme) { }, avatarRibbon: { height: 3, - width: 12, + width: 11, // TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded. }, @@ -407,22 +408,9 @@ export default function workspace(colorScheme: ColorScheme) { }, }, }), - leaveCallButton: interactive({ - base: { - margin: { left: itemSpacing }, - cornerRadius: 6, - color: foreground(layer, "variant"), - iconWidth: 14, - buttonWidth: 20, - }, - state: { - clicked: { - background: background(layer, "variant", "pressed"), - }, - hovered: { - background: background(layer, "variant", "hovered"), - }, - }, + + leaveCallButton: icon_button(colorScheme, { + margin: { left: itemSpacing }, }), userMenuButton: merge(titlebarButton, { From 5af33407f03c2b311f6db604707132899ded3cb7 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 27 Jun 2023 11:34:26 -0400 Subject: [PATCH 45/63] Add script for building theme types --- script/build-theme-types | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 script/build-theme-types diff --git a/script/build-theme-types b/script/build-theme-types new file mode 100755 index 0000000000..b78631f3d1 --- /dev/null +++ b/script/build-theme-types @@ -0,0 +1,10 @@ +#!/bin/bash + +echo "running xtask" +(cd crates/theme && cargo xtask build-theme-types) + +echo "updating theme packages" +(cd styles && npm install) + +echo "building theme types" +(cd styles && npm run build-types) From b4f9faee3bca19a35577686cc5c335223f1a0c45 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 27 Jun 2023 12:24:19 -0400 Subject: [PATCH 46/63] Update share, call control buttons --- styles/src/component/icon_button.ts | 33 ++++-- styles/src/component/text_button.ts | 74 +++++++++++++ styles/src/styleTree/workspace.ts | 165 ++++++---------------------- 3 files changed, 134 insertions(+), 138 deletions(-) create mode 100644 styles/src/component/text_button.ts diff --git a/styles/src/component/icon_button.ts b/styles/src/component/icon_button.ts index d71996042b..47953a56d3 100644 --- a/styles/src/component/icon_button.ts +++ b/styles/src/component/icon_button.ts @@ -1,14 +1,17 @@ import { ColorScheme } from "../common"; -import { interactive } from "../element"; +import { interactive, toggleable } from "../element"; import { background, foreground } from "../styleTree/components"; import { Margin } from "../types/zed"; interface IconButtonOptions { + layer?: ColorScheme['lowest'] | ColorScheme['middle'] | ColorScheme['highest']; color?: keyof ColorScheme['lowest']; margin?: Partial; } -export function icon_button(theme: ColorScheme, { color, margin }: IconButtonOptions) { +type ToggleableIconButtonOptions = IconButtonOptions & { active_color?: keyof ColorScheme['lowest'] }; + +export function icon_button(theme: ColorScheme, { color, margin, layer }: IconButtonOptions) { if (!color) color = "base"; @@ -21,7 +24,7 @@ export function icon_button(theme: ColorScheme, { color, margin }: IconButtonOpt return interactive({ base: { - corner_radius: 4, + corner_radius: 6, padding: { top: 2, bottom: 2, @@ -36,19 +39,31 @@ export function icon_button(theme: ColorScheme, { color, margin }: IconButtonOpt }, state: { default: { - background: background(theme.lowest, color), - color: foreground(theme.lowest, color), + background: background(layer ?? theme.lowest, color), + color: foreground(layer ?? theme.lowest, color), }, hovered: { - background: background(theme.lowest, color, "hovered"), - color: foreground(theme.lowest, color, "hovered"), + background: background(layer ?? theme.lowest, color, "hovered"), + color: foreground(layer ?? theme.lowest, color, "hovered"), }, clicked: { - background: background(theme.lowest, color, "pressed"), - color: foreground(theme.lowest, color, "pressed"), + background: background(layer ?? theme.lowest, color, "pressed"), + color: foreground(layer ?? theme.lowest, color, "pressed"), }, }, }); } + +export function toggleable_icon_button(theme: ColorScheme, { color, active_color, margin }: ToggleableIconButtonOptions) { + if (!color) + color = "base"; + + return toggleable({ + state: { + inactive: icon_button(theme, { color, margin }), + active: icon_button(theme, { color: active_color ? active_color : color, margin, layer: theme.middle }), + } + }) +} diff --git a/styles/src/component/text_button.ts b/styles/src/component/text_button.ts new file mode 100644 index 0000000000..921ecc639f --- /dev/null +++ b/styles/src/component/text_button.ts @@ -0,0 +1,74 @@ +import { ColorScheme } from "../common"; +import { interactive, toggleable } from "../element"; +import { TextProperties, background, foreground, text } from "../styleTree/components"; +import { Margin } from "../types/zed"; + +interface TextButtonOptions { + layer?: ColorScheme['lowest'] | ColorScheme['middle'] | ColorScheme['highest']; + color?: keyof ColorScheme['lowest']; + margin?: Partial; + text_properties?: TextProperties; +} + +type ToggleableTextButtonOptions = TextButtonOptions & { active_color?: keyof ColorScheme['lowest'] }; + +export function text_button(theme: ColorScheme, { color, layer, margin, text_properties }: TextButtonOptions) { + if (!color) + color = "base"; + + const text_options: TextProperties = { + size: "xs", + weight: "normal", + ...text_properties + } + + const m = { + top: margin?.top ?? 0, + bottom: margin?.bottom ?? 0, + left: margin?.left ?? 0, + right: margin?.right ?? 0, + } + + return interactive({ + base: { + corner_radius: 6, + padding: { + top: 1, + bottom: 1, + left: 6, + right: 6, + }, + margin: m, + button_height: 22, + ...text(layer ?? theme.lowest, "sans", color, text_options) + }, + state: { + default: { + background: background(layer ?? theme.lowest, color), + color: foreground(layer ?? theme.lowest, color), + }, + hovered: { + background: background(layer ?? theme.lowest, color, "hovered"), + color: foreground(layer ?? theme.lowest, color, "hovered"), + + }, + clicked: { + background: background(layer ?? theme.lowest, color, "pressed"), + color: foreground(layer ?? theme.lowest, color, "pressed"), + + }, + }, + }); +} + +export function toggleable_text_button(theme: ColorScheme, { color, active_color, margin }: ToggleableTextButtonOptions) { + if (!color) + color = "base"; + + return toggleable({ + state: { + inactive: text_button(theme, { color, margin }), + active: text_button(theme, { color: active_color ? active_color : color, margin, layer: theme.middle }), + } + }) +} diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index a500988bf7..da1ad1fe18 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -13,7 +13,8 @@ import statusBar from "./statusBar" import tabBar from "./tabBar" import { interactive } from "../element" import merge from "ts-deepmerge" -import { icon_button } from "../component/icon_button" +import { icon_button, toggleable_icon_button } from "../component/icon_button" +import { text_button, toggleable_text_button } from "../component/text_button" export default function workspace(colorScheme: ColorScheme) { const layer = colorScheme.lowest const isLight = colorScheme.isLight @@ -298,137 +299,45 @@ export default function workspace(colorScheme: ColorScheme) { }, cornerRadius: 6, }, - callControl: interactive({ - base: { - cornerRadius: 6, - color: foreground(layer, "variant"), - iconWidth: 12, - buttonWidth: 20, - }, - state: { - hovered: { - background: background(layer, "variant", "hovered"), - color: foreground(layer, "variant", "hovered"), - }, - }, - }), - toggleContactsButton: toggleable({ - base: interactive({ - base: { - margin: { left: itemSpacing }, - cornerRadius: 6, - color: foreground(layer, "variant"), - iconWidth: 14, - buttonWidth: 20, - }, - state: { - clicked: { - background: background(layer, "variant", "pressed"), - }, - hovered: { - background: background(layer, "variant", "hovered"), - }, - }, - }), - state: { - active: { - default: { - background: background(layer, "on", "default"), - }, - hovered: { - background: background(layer, "on", "hovered"), - }, - clicked: { - background: background(layer, "on", "pressed"), - }, - }, - }, - }), - toggleMicrophoneButton: toggleable({ - base: interactive({ - base: { - margin: { left: itemSpacing }, - cornerRadius: 6, - color: foreground(layer, "variant"), - iconWidth: 14, - buttonWidth: 20, - }, - state: { - clicked: { - background: background(layer, "variant", "pressed"), - }, - hovered: { - background: background(layer, "variant", "hovered"), - }, - }, - }), - state: { - active: { - default: { - background: background(layer, "on", "default"), - }, - hovered: { - background: background(layer, "on", "hovered"), - }, - clicked: { - background: background(layer, "on", "pressed"), - }, - }, - }, - }), - toggleSpeakersButton: toggleable({ - base: interactive({ - base: { - margin: { left: itemSpacing }, - cornerRadius: 6, - color: foreground(layer, "variant"), - iconWidth: 14, - buttonWidth: 20, - }, - state: { - clicked: { - background: background(layer, "variant", "pressed"), - }, - hovered: { - background: background(layer, "variant", "hovered"), - }, - }, - }), - state: { - active: { - default: { - background: background(layer, "on", "default"), - }, - hovered: { - background: background(layer, "on", "hovered"), - }, - clicked: { - background: background(layer, "on", "pressed"), - }, - }, - }, + + call_control: icon_button(colorScheme, { + margin: { left: itemSpacing / 2 }, }), - leaveCallButton: icon_button(colorScheme, { + toggle_contacts_button: toggleable_icon_button(colorScheme, { + margin: { left: itemSpacing } + }), + + toggle_microphone_button: toggleable_icon_button(colorScheme, { + margin: { left: itemSpacing }, + active_color: 'negative' + }), + + toggle_speakers_button: toggleable_icon_button(colorScheme, { + margin: { left: itemSpacing / 2 }, + }), + + leave_call_button: icon_button(colorScheme, { margin: { left: itemSpacing }, }), - userMenuButton: merge(titlebarButton, { - inactive: { - default: { - buttonWidth: 20, - iconWidth: 12, + user_menu_button: + merge(titlebarButton, { + inactive: { + default: { + buttonWidth: 20, + iconWidth: 12, + }, }, - }, - active: { - default: { - iconWidth: 12, - button_width: 20, - background: background(layer, "variant", "active"), - color: foreground(layer, "variant", "active"), - } - }, - }), + active: { + default: { + iconWidth: 12, + button_width: 20, + background: background(layer, "variant", "active"), + color: foreground(layer, "variant", "active"), + } + }, + }), toggleContactsBadge: { cornerRadius: 3, @@ -437,9 +346,7 @@ export default function workspace(colorScheme: ColorScheme) { border: border(layer), background: foreground(layer, "accent"), }, - shareButton: { - ...titlebarButton, - }, + shareButton: toggleable_text_button(colorScheme, {}), }, toolbar: { From 37cb202c9337fce50a482e63f8963c66eaba5bb6 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 27 Jun 2023 09:56:41 -0700 Subject: [PATCH 47/63] Rename and toggle screenshare --- crates/collab_ui/src/collab_titlebar_item.rs | 2 +- crates/theme/src/theme.rs | 2 +- styles/package.json | 2 +- styles/src/styleTree/workspace.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 52f0bc7d61..0a91a488ed 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -417,7 +417,7 @@ impl CollabTitlebarItem { let titlebar = &theme.workspace.titlebar; MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar.call_control.style_for(state); + let style = titlebar.screen_share_button.style_for(state); Svg::new(icon) .with_color(style.color) .constrained() diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 3fda62ced9..f2e9c4aa3e 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -133,7 +133,7 @@ pub struct Titlebar { pub sign_in_prompt: Toggleable>, pub outdated_warning: ContainedText, pub share_button: Toggleable>, - pub call_control: Interactive, + pub screen_share_button: Toggleable>, pub toggle_contacts_button: Toggleable>, pub toggle_microphone_button: Toggleable>, pub toggle_speakers_button: Toggleable>, diff --git a/styles/package.json b/styles/package.json index 8820259e86..b14fb5f527 100644 --- a/styles/package.json +++ b/styles/package.json @@ -7,7 +7,7 @@ "build": "ts-node ./src/buildThemes.ts", "build-licenses": "ts-node ./src/buildLicenses.ts", "build-tokens": "ts-node ./src/buildTokens.ts", - "build-types": "cd ../crates/theme && cargo test && cd ../../styles && ts-node ./src/buildTypes.ts", + "build-types": "ts-node ./src/buildTypes.ts", "test": "vitest" }, "author": "", diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index da1ad1fe18..ec3776dcb0 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -300,7 +300,7 @@ export default function workspace(colorScheme: ColorScheme) { cornerRadius: 6, }, - call_control: icon_button(colorScheme, { + screen_share_button: icon_button(colorScheme, { margin: { left: itemSpacing / 2 }, }), From 60ce58442768efdfbc63dcfd1249825fb97b5697 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 27 Jun 2023 13:12:52 -0700 Subject: [PATCH 48/63] WIP: Add mute icons --- crates/call/src/participant.rs | 1 + crates/call/src/room.rs | 25 +++++++++++++++++++++++-- crates/live_kit_client/src/prod.rs | 1 + crates/live_kit_client/src/test.rs | 1 + 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index 90f256489d..9773e837c3 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -43,6 +43,7 @@ pub struct RemoteParticipant { pub peer_id: proto::PeerId, pub projects: Vec, pub location: ParticipantLocation, + pub muted: bool, pub video_tracks: HashMap>, pub audio_tracks: HashMap>, } diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 3c94feae4c..7b7ec89a5e 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -647,6 +647,7 @@ impl Room { peer_id, projects: participant.projects, location, + muted: false, video_tracks: Default::default(), audio_tracks: Default::default(), }, @@ -781,6 +782,21 @@ impl Room { cx: &mut ModelContext, ) -> Result<()> { match change { + RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => { + for participant in &mut self.remote_participants.values_mut() { + let mut found = false; + for track in participant.audio_tracks.values() { + if track.sid() == track_id { + found = true; + break; + } + } + if found { + participant.muted = muted; + break; + } + } + } RemoteAudioTrackUpdate::Subscribed(track) => { let user_id = track.publisher_id().parse()?; let track_id = track.sid().to_string(); @@ -1213,11 +1229,12 @@ impl Room { let mut tasks = Vec::with_capacity(self.remote_participants.len()); // Context notification is sent within set_mute itself. + let mut mute_task = None; if live_kit.deafened { // Unmute microphone only if we're going from unmuted -> muted state. // We don't want to unmute user automatically. - let _ = Self::set_mute(live_kit, live_kit.deafened, cx)?; // todo (osiewicz): we probably want to schedule it on fg/bg? - } + mute_task = Some(Self::set_mute(live_kit, live_kit.deafened, cx)?); + }; for participant in self.remote_participants.values() { for track in live_kit .room @@ -1226,7 +1243,11 @@ impl Room { tasks.push(cx.foreground().spawn(track.set_enabled(!live_kit.deafened))); } } + Ok(cx.foreground().spawn(async move { + if let Some(mute_task) = mute_task { + mute_task.await?; + } for task in tasks { task.await?; } diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index dbd9e1917e..96bf40ca4e 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -761,6 +761,7 @@ pub enum RemoteVideoTrackUpdate { } pub enum RemoteAudioTrackUpdate { + MuteChanged { track_id: Sid, muted: bool}, Subscribed(Arc), Unsubscribed { publisher_id: Sid, track_id: Sid }, } diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index 9db57a3294..286c7215e9 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -580,6 +580,7 @@ pub enum RemoteVideoTrackUpdate { #[derive(Clone)] pub enum RemoteAudioTrackUpdate { + MuteChanged { track_id: Sid, muted: bool}, Subscribed(Arc), Unsubscribed { publisher_id: Sid, track_id: Sid }, } From 825a7cb799bf297ba099ba69004eecff2771c832 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 27 Jun 2023 16:20:45 -0400 Subject: [PATCH 49/63] Update screenshare toggle, titlebar spacing --- crates/collab_ui/src/collab_titlebar_item.rs | 11 ++++-- crates/theme/src/theme.rs | 2 +- styles/src/component/icon_button.ts | 12 ++++-- styles/src/component/text_button.ts | 2 +- styles/src/styleTree/workspace.ts | 39 ++++++++++++++------ 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 0a91a488ed..08d63dc1b4 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -408,16 +408,21 @@ impl CollabTitlebarItem { let icon; let tooltip; if room.read(cx).is_screen_sharing() { - icon = "icons/radix/desktop-mute.svg"; + icon = "icons/radix/desktop.svg"; tooltip = "Stop Sharing Screen" } else { icon = "icons/radix/desktop.svg"; tooltip = "Share Screen"; } + let active = room.read(cx).is_screen_sharing(); let titlebar = &theme.workspace.titlebar; MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar.screen_share_button.style_for(state); + let style = titlebar + .screen_share_button + .in_state(active) + .style_for(state); + Svg::new(icon) .with_color(style.color) .constrained() @@ -701,7 +706,7 @@ impl CollabTitlebarItem { fn render_sign_in_button(&self, theme: &Theme, cx: &mut ViewContext) -> AnyElement { let titlebar = &theme.workspace.titlebar; MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar.sign_in_prompt.inactive_state().style_for(state); + let style = titlebar.sign_in_button.inactive_state().style_for(state); Label::new("Sign In", style.text.clone()) .contained() .with_style(style.container) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index f2e9c4aa3e..49c93f38f3 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -130,7 +130,7 @@ pub struct Titlebar { pub leader_avatar: AvatarStyle, pub follower_avatar: AvatarStyle, pub inactive_avatar_grayscale: bool, - pub sign_in_prompt: Toggleable>, + pub sign_in_button: Toggleable>, pub outdated_warning: ContainedText, pub share_button: Toggleable>, pub screen_share_button: Toggleable>, diff --git a/styles/src/component/icon_button.ts b/styles/src/component/icon_button.ts index 47953a56d3..850acc62f9 100644 --- a/styles/src/component/icon_button.ts +++ b/styles/src/component/icon_button.ts @@ -1,7 +1,13 @@ import { ColorScheme } from "../common"; import { interactive, toggleable } from "../element"; import { background, foreground } from "../styleTree/components"; -import { Margin } from "../types/zed"; + +export type Margin = { + top: number; + bottom: number; + left: number; + right: number; +} interface IconButtonOptions { layer?: ColorScheme['lowest'] | ColorScheme['middle'] | ColorScheme['highest']; @@ -34,8 +40,8 @@ export function icon_button(theme: ColorScheme, { color, margin, layer }: IconBu margin: m, icon_width: 15, icon_height: 15, - button_width: 23, - button_height: 19, + button_width: 21, + button_height: 17, }, state: { default: { diff --git a/styles/src/component/text_button.ts b/styles/src/component/text_button.ts index 921ecc639f..ae7fede900 100644 --- a/styles/src/component/text_button.ts +++ b/styles/src/component/text_button.ts @@ -1,7 +1,7 @@ import { ColorScheme } from "../common"; import { interactive, toggleable } from "../element"; import { TextProperties, background, foreground, text } from "../styleTree/components"; -import { Margin } from "../types/zed"; +import { Margin } from "./icon_button"; interface TextButtonOptions { layer?: ColorScheme['lowest'] | ColorScheme['middle'] | ColorScheme['highest']; diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index ec3776dcb0..4b3c5f3b51 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -260,7 +260,7 @@ export default function workspace(colorScheme: ColorScheme) { // Sign in buttom // FlatButton, Variant - signInPrompt: merge(titlebarButton, { + sign_in_button: merge(titlebarButton, { inactive: { default: { margin: { @@ -300,25 +300,41 @@ export default function workspace(colorScheme: ColorScheme) { cornerRadius: 6, }, - screen_share_button: icon_button(colorScheme, { - margin: { left: itemSpacing / 2 }, - }), - - toggle_contacts_button: toggleable_icon_button(colorScheme, { - margin: { left: itemSpacing } + leave_call_button: icon_button(colorScheme, { + margin: { + left: itemSpacing / 2, + right: itemSpacing + }, }), toggle_microphone_button: toggleable_icon_button(colorScheme, { - margin: { left: itemSpacing }, + margin: { + left: itemSpacing, + right: itemSpacing / 2 + }, active_color: 'negative' }), toggle_speakers_button: toggleable_icon_button(colorScheme, { - margin: { left: itemSpacing / 2 }, + margin: { + left: itemSpacing / 2, + right: itemSpacing / 2 + }, }), - leave_call_button: icon_button(colorScheme, { - margin: { left: itemSpacing }, + screen_share_button: toggleable_icon_button(colorScheme, { + margin: { + left: itemSpacing / 2, + right: itemSpacing + }, + active_color: 'accent' + }), + + toggle_contacts_button: toggleable_icon_button(colorScheme, { + margin: { + left: itemSpacing, + right: itemSpacing / 2 + }, }), user_menu_button: @@ -339,6 +355,7 @@ export default function workspace(colorScheme: ColorScheme) { }, }), + // Jewel that notifies you that there are new contact requests toggleContactsBadge: { cornerRadius: 3, padding: 2, From 19ca6a88752a1ca991f2c0b7f0d241615529b713 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 27 Jun 2023 13:32:42 -0700 Subject: [PATCH 50/63] Seperate online and offline --- crates/collab_ui/src/collab_titlebar_item.rs | 25 ++++++++++---------- crates/theme/src/theme.rs | 3 ++- styles/src/styleTree/titlebar.ts | 8 ++++--- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 08d63dc1b4..6c900f5a07 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -98,13 +98,7 @@ 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)); - let avatar = ActiveCall::global(cx) - .read(cx) - .room() - .is_none() - .then(|| user.as_ref()) - .flatten() - .and_then(|user| user.avatar.clone()); + let avatar = user.as_ref().and_then(|user| user.avatar.clone()); right_container.add_child(self.render_user_menu_button(&theme, avatar, cx)); } else { right_container.add_children(self.render_connection_status(status, cx)); @@ -645,13 +639,17 @@ impl CollabTitlebarItem { cx: &mut ViewContext, ) -> AnyElement { let tooltip = theme.tooltip.clone(); - let user_menu_button = &theme.titlebar; - let avatar_style = &user_menu_button.user_menu_button.avatar; + let user_menu_button_style = if avatar.is_some() { + &theme.titlebar.user_menu_button_online + } else { + &theme.titlebar.user_menu_button_offline + }; + + let avatar_style = &user_menu_button_style.avatar; Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, _| { - let style = user_menu_button - .user_menu_button + let style = user_menu_button_style .user_menu .inactive_state() .style_for(state); @@ -665,12 +663,13 @@ impl CollabTitlebarItem { Color::transparent_black(), )); }; + dropdown .with_child( Svg::new("icons/caret_down_8.svg") - .with_color(theme.titlebar.user_menu_button.icon.color) + .with_color(user_menu_button_style.icon.color) .constrained() - .with_width(theme.titlebar.user_menu_button.icon.width) + .with_width(user_menu_button_style.icon.width) .contained() .into_any(), ) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 49c93f38f3..4d7f334086 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -144,7 +144,8 @@ pub struct Titlebar { #[derive(Clone, Deserialize, Default, JsonSchema)] pub struct UserMenu { - pub user_menu_button: UserMenuButton, + pub user_menu_button_online: UserMenuButton, + pub user_menu_button_offline: UserMenuButton, } #[derive(Clone, Deserialize, Default, JsonSchema)] pub struct UserMenuButton { diff --git a/styles/src/styleTree/titlebar.ts b/styles/src/styleTree/titlebar.ts index 3aa41359fa..881600d76c 100644 --- a/styles/src/styleTree/titlebar.ts +++ b/styles/src/styleTree/titlebar.ts @@ -56,7 +56,7 @@ const titlebarButton = (theme: ColorScheme) => toggleable({ * When logged in shows the user's avatar and a chevron, * When logged out only shows a chevron. */ -function userMenuButton(theme: ColorScheme) { +function userMenuButton(theme: ColorScheme, online: boolean) { return { user_menu: titlebarButton(theme), avatar: { @@ -71,13 +71,15 @@ function userMenuButton(theme: ColorScheme) { icon: { width: 11, height: 11, - color: foreground(theme.lowest) + color: online ? foreground(theme.lowest) : background(theme.lowest) } } } export function titlebar(theme: ColorScheme) { return { - userMenuButton: userMenuButton(theme) + userMenuButtonOnline: userMenuButton(theme, true), + userMenuButtonOffline: userMenuButton(theme, false) + } } From 8b2732c3aa33c04d126e629b13e84b25cc3f7716 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 27 Jun 2023 17:27:58 -0400 Subject: [PATCH 51/63] `workspace.titlebar` -> `titlebar` in Theme --- crates/collab_ui/src/collab_titlebar_item.rs | 54 ++-- crates/theme/src/theme.rs | 5 +- crates/workspace/src/workspace.rs | 6 +- styles/src/styleTree/titlebar.ts | 240 +++++++++++++++++- styles/src/styleTree/workspace.ts | 246 +------------------ 5 files changed, 266 insertions(+), 285 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 6c900f5a07..708fb89e86 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -113,7 +113,6 @@ impl View for CollabTitlebarItem { .with_child( right_container.contained().with_background_color( theme - .workspace .titlebar .container .background_color @@ -200,11 +199,11 @@ impl CollabTitlebarItem { .as_ref() .and_then(RepositoryEntry::branch) .map(|branch| format!("/{branch}")); - let text_style = theme.workspace.titlebar.title.clone(); - let item_spacing = theme.workspace.titlebar.item_spacing; + let text_style = theme.titlebar.title.clone(); + let item_spacing = theme.titlebar.item_spacing; let mut highlight = text_style.clone(); - highlight.color = theme.workspace.titlebar.highlight_color; + highlight.color = theme.titlebar.highlight_color; let style = LabelStyle { text: text_style, @@ -325,7 +324,7 @@ impl CollabTitlebarItem { theme: &Theme, cx: &mut ViewContext, ) -> AnyElement { - let titlebar = &theme.workspace.titlebar; + let titlebar = &theme.titlebar; let badge = if self .user_store @@ -410,7 +409,7 @@ impl CollabTitlebarItem { } let active = room.read(cx).is_screen_sharing(); - let titlebar = &theme.workspace.titlebar; + let titlebar = &theme.titlebar; MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar .screen_share_button @@ -459,7 +458,7 @@ impl CollabTitlebarItem { tooltip = "Mute microphone\nRight click for options"; } - let titlebar = &theme.workspace.titlebar; + let titlebar = &theme.titlebar; MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar .toggle_microphone_button @@ -512,7 +511,7 @@ impl CollabTitlebarItem { tooltip = "Mute speakers\nRight click for options"; } - let titlebar = &theme.workspace.titlebar; + let titlebar = &theme.titlebar; MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar .toggle_speakers_button @@ -547,7 +546,7 @@ impl CollabTitlebarItem { let icon = "icons/radix/exit.svg"; let tooltip = "Leave call"; - let titlebar = &theme.workspace.titlebar; + let titlebar = &theme.titlebar; MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar.leave_call_button.style_for(state); Svg::new(icon) @@ -596,7 +595,7 @@ impl CollabTitlebarItem { "Share project with call participants" }; - let titlebar = &theme.workspace.titlebar; + let titlebar = &theme.titlebar; enum ShareUnshare {} Some( @@ -627,7 +626,7 @@ impl CollabTitlebarItem { ) .aligned() .contained() - .with_margin_left(theme.workspace.titlebar.item_spacing) + .with_margin_left(theme.titlebar.item_spacing) .into_any(), ) } @@ -640,9 +639,9 @@ impl CollabTitlebarItem { ) -> AnyElement { let tooltip = theme.tooltip.clone(); let user_menu_button_style = if avatar.is_some() { - &theme.titlebar.user_menu_button_online + &theme.titlebar.user_menu.user_menu_button_online } else { - &theme.titlebar.user_menu_button_offline + &theme.titlebar.user_menu.user_menu_button_offline }; let avatar_style = &user_menu_button_style.avatar; @@ -703,7 +702,7 @@ impl CollabTitlebarItem { } fn render_sign_in_button(&self, theme: &Theme, cx: &mut ViewContext) -> AnyElement { - let titlebar = &theme.workspace.titlebar; + let titlebar = &theme.titlebar; MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar.sign_in_button.inactive_state().style_for(state); Label::new("Sign In", style.text.clone()) @@ -771,7 +770,7 @@ impl CollabTitlebarItem { theme, cx, )) - .with_margin_right(theme.workspace.titlebar.face_pile_spacing), + .with_margin_right(theme.titlebar.face_pile_spacing), ) }) .collect() @@ -795,7 +794,7 @@ impl CollabTitlebarItem { theme, cx, )) - .with_margin_right(theme.workspace.titlebar.item_spacing) + .with_margin_right(theme.titlebar.item_spacing) .into_any() } @@ -827,11 +826,10 @@ impl CollabTitlebarItem { }) .unwrap_or(false); - let leader_style = theme.workspace.titlebar.leader_avatar; - let follower_style = theme.workspace.titlebar.follower_avatar; + let leader_style = theme.titlebar.leader_avatar; + let follower_style = theme.titlebar.follower_avatar; let mut background_color = theme - .workspace .titlebar .container .background_color @@ -846,7 +844,7 @@ impl CollabTitlebarItem { let mut content = Stack::new() .with_children(user.avatar.as_ref().map(|avatar| { - let face_pile = FacePile::new(theme.workspace.titlebar.follower_avatar_overlap) + let face_pile = FacePile::new(theme.titlebar.follower_avatar_overlap) .with_child(Self::render_face( avatar.clone(), Self::location_style(workspace, location, leader_style, cx), @@ -891,7 +889,7 @@ impl CollabTitlebarItem { let mut container = face_pile .contained() - .with_style(theme.workspace.titlebar.leader_selection); + .with_style(theme.titlebar.leader_selection); if let Some(replica_id) = replica_id { if followed_by_self { @@ -908,8 +906,8 @@ impl CollabTitlebarItem { Some( AvatarRibbon::new(color) .constrained() - .with_width(theme.workspace.titlebar.avatar_ribbon.width) - .with_height(theme.workspace.titlebar.avatar_ribbon.height) + .with_width(theme.titlebar.avatar_ribbon.width) + .with_height(theme.titlebar.avatar_ribbon.height) .aligned() .bottom(), ) @@ -1029,22 +1027,22 @@ impl CollabTitlebarItem { | client::Status::Reconnecting { .. } | client::Status::ReconnectionError { .. } => Some( Svg::new("icons/cloud_slash_12.svg") - .with_color(theme.workspace.titlebar.offline_icon.color) + .with_color(theme.titlebar.offline_icon.color) .constrained() - .with_width(theme.workspace.titlebar.offline_icon.width) + .with_width(theme.titlebar.offline_icon.width) .aligned() .contained() - .with_style(theme.workspace.titlebar.offline_icon.container) + .with_style(theme.titlebar.offline_icon.container) .into_any(), ), client::Status::UpgradeRequired => Some( MouseEventHandler::::new(0, cx, |_, _| { Label::new( "Please update Zed to collaborate", - theme.workspace.titlebar.outdated_warning.text.clone(), + theme.titlebar.outdated_warning.text.clone(), ) .contained() - .with_style(theme.workspace.titlebar.outdated_warning.container) + .with_style(theme.titlebar.outdated_warning.container) .aligned() }) .with_cursor_style(CursorStyle::PointingHand) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 4d7f334086..06c6026e3a 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -66,7 +66,7 @@ pub struct Theme { pub feedback: FeedbackStyle, pub welcome: WelcomeStyle, pub color_scheme: ColorScheme, - pub titlebar: UserMenu, + pub titlebar: Titlebar, } #[derive(Deserialize, Default, Clone, JsonSchema)] @@ -81,7 +81,6 @@ pub struct ThemeMeta { pub struct Workspace { pub background: Color, pub blank_pane: BlankPaneStyle, - pub titlebar: Titlebar, pub tab_bar: TabBar, pub pane_divider: Border, pub leader_border_opacity: f32, @@ -138,8 +137,8 @@ pub struct Titlebar { pub toggle_microphone_button: Toggleable>, pub toggle_speakers_button: Toggleable>, pub leave_call_button: Interactive, - pub user_menu_button: Toggleable>, pub toggle_contacts_badge: ContainerStyle, + pub user_menu: UserMenu, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index dfc2f4f795..066ea5f8a6 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2296,11 +2296,11 @@ impl Workspace { // (https://github.com/zed-industries/zed/issues/1290) let is_fullscreen = cx.window_is_fullscreen(); let container_theme = if is_fullscreen { - let mut container_theme = theme.workspace.titlebar.container; + let mut container_theme = theme.titlebar.container; container_theme.padding.left = container_theme.padding.right; container_theme } else { - theme.workspace.titlebar.container + theme.titlebar.container }; enum TitleBar {} @@ -2320,7 +2320,7 @@ impl Workspace { } }) .constrained() - .with_height(theme.workspace.titlebar.height) + .with_height(theme.titlebar.height) .into_any_named("titlebar") } diff --git a/styles/src/styleTree/titlebar.ts b/styles/src/styleTree/titlebar.ts index 881600d76c..5f0fc3f528 100644 --- a/styles/src/styleTree/titlebar.ts +++ b/styles/src/styleTree/titlebar.ts @@ -1,6 +1,49 @@ import { ColorScheme } from "../common"; +import { icon_button, toggleable_icon_button } from "../component/icon_button" +import { toggleable_text_button } from "../component/text_button" import { interactive, toggleable } from "../element" -import { background, foreground, text } from "./components"; +import { withOpacity } from "../theme/color"; +import { background, border, foreground, text } from "./components"; + +const ITEM_SPACING = 8 + +interface SpacingProps { + container_height: number; + spacing: number; +} + +function build_spacing( + container_height: number, + element_height: number, + spacing: number +) { + return { + group: spacing * 2, + item: spacing / 2, + marginY: (container_height - element_height) / 2, + marginX: (container_height - element_height) / 2, + } +} + +function mac_os_controls(theme: ColorScheme, { container_height, spacing }: SpacingProps) { + return {} +} + +function project_info(theme: ColorScheme, { container_height, spacing }: SpacingProps) { + return {} +} + +function collaboration_stacks(theme: ColorScheme, { container_height, spacing }: SpacingProps) { + return {} +} + +function sharing_controls(theme: ColorScheme, { container_height, spacing }: SpacingProps) { + return {} +} + +function call_controls(theme: ColorScheme, { container_height, spacing }: SpacingProps) { + return {} +} const titlebarButton = (theme: ColorScheme) => toggleable({ base: interactive({ @@ -57,29 +100,210 @@ const titlebarButton = (theme: ColorScheme) => toggleable({ * When logged out only shows a chevron. */ function userMenuButton(theme: ColorScheme, online: boolean) { + const button = toggleable({ + base: interactive({ + base: { + cornerRadius: 6, + height: 19, + width: online ? 36 : 23, + padding: { + top: 2, + bottom: 2, + left: 6, + right: 6, + }, + ...text(theme.lowest, "sans", { size: "xs" }), + background: background(theme.lowest), + }, + state: { + hovered: { + ...text(theme.lowest, "sans", "hovered", { + size: "xs", + }), + background: background(theme.lowest, "hovered"), + }, + clicked: { + ...text(theme.lowest, "sans", "pressed", { + size: "xs", + }), + background: background(theme.lowest, "pressed"), + }, + }, + }), + state: { + active: { + default: { + ...text(theme.lowest, "sans", "active", { size: "xs" }), + background: background(theme.middle), + }, + hovered: { + ...text(theme.lowest, "sans", "active", { size: "xs" }), + background: background(theme.middle, "hovered"), + }, + clicked: { + ...text(theme.lowest, "sans", "active", { size: "xs" }), + background: background(theme.middle, "pressed"), + }, + }, + } + }); + return { - user_menu: titlebarButton(theme), + user_menu: button, avatar: { icon_width: 16, icon_height: 16, cornerRadius: 4, outer_corner_radius: 0, outer_width: 0, - outerWidth: 10, - outerCornerRadius: 10 + outerWidth: 16, + outerCornerRadius: 16 }, icon: { + margin: { + left: online ? 2 : 0, + }, width: 11, height: 11, - color: online ? foreground(theme.lowest) : background(theme.lowest) + color: foreground(theme.lowest) } } } export function titlebar(theme: ColorScheme) { - return { - userMenuButtonOnline: userMenuButton(theme, true), - userMenuButtonOffline: userMenuButton(theme, false) + const avatarWidth = 18 + const avatarOuterWidth = avatarWidth + 4 + const followerAvatarWidth = 14 + const followerAvatarOuterWidth = followerAvatarWidth + 4 + return { + ITEM_SPACING, + facePileSpacing: 2, + height: 33, // 32px + 1px border. It's important the content area of the titlebar is evenly sized to vertically center avatar images. + background: background(theme.lowest), + border: border(theme.lowest, { bottom: true }), + padding: { + left: 80, + right: ITEM_SPACING, + }, + + // Project + title: text(theme.lowest, "sans", "variant"), + highlight_color: text(theme.lowest, "sans", "active").color, + + // Collaborators + leaderAvatar: { + width: avatarWidth, + outerWidth: avatarOuterWidth, + cornerRadius: avatarWidth / 2, + outerCornerRadius: avatarOuterWidth / 2, + }, + followerAvatar: { + width: followerAvatarWidth, + outerWidth: followerAvatarOuterWidth, + cornerRadius: followerAvatarWidth / 2, + outerCornerRadius: followerAvatarOuterWidth / 2, + }, + inactiveAvatarGrayscale: true, + followerAvatarOverlap: 8, + leaderSelection: { + margin: { + top: 4, + bottom: 4, + }, + padding: { + left: 2, + right: 2, + top: 2, + bottom: 2, + }, + cornerRadius: 6, + }, + avatarRibbon: { + height: 3, + width: 11, + // TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded. + }, + + // Sign in buttom + sign_in_button: toggleable_text_button(theme, {}), + + // Offline Indicator + offlineIcon: { + color: foreground(theme.lowest, "variant"), + width: 16, + margin: { + left: ITEM_SPACING, + }, + padding: { + right: 4, + }, + }, + + // Notice that the collaboration server is out of date + outdatedWarning: { + ...text(theme.lowest, "sans", "warning", { size: "xs" }), + background: withOpacity(background(theme.lowest, "warning"), 0.3), + border: border(theme.lowest, "warning"), + margin: { + left: ITEM_SPACING, + }, + padding: { + left: 8, + right: 8, + }, + cornerRadius: 6, + }, + + leave_call_button: icon_button(theme, { + margin: { + left: ITEM_SPACING / 2, + right: ITEM_SPACING + }, + }), + + toggle_microphone_button: toggleable_icon_button(theme, { + margin: { + left: ITEM_SPACING, + right: ITEM_SPACING / 2 + }, + active_color: 'negative' + }), + + toggle_speakers_button: toggleable_icon_button(theme, { + margin: { + left: ITEM_SPACING / 2, + right: ITEM_SPACING / 2 + }, + }), + + screen_share_button: toggleable_icon_button(theme, { + margin: { + left: ITEM_SPACING / 2, + right: ITEM_SPACING + }, + active_color: 'accent' + }), + + toggle_contacts_button: toggleable_icon_button(theme, { + margin: { + left: ITEM_SPACING, + right: ITEM_SPACING / 2 + }, + }), + + // Jewel that notifies you that there are new contact requests + toggleContactsBadge: { + cornerRadius: 3, + padding: 2, + margin: { top: 3, left: 3 }, + border: border(theme.lowest), + background: foreground(theme.lowest, "accent"), + }, + shareButton: toggleable_text_button(theme, {}), + user_menu: { + userMenuButtonOnline: userMenuButton(theme, true), + userMenuButtonOffline: userMenuButton(theme, false), + } } } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 4b3c5f3b51..afc2ea4d98 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -1,6 +1,5 @@ import { ColorScheme } from "../theme/colorScheme" import { withOpacity } from "../theme/color" -import { toggleable } from "../element" import { background, border, @@ -12,94 +11,11 @@ import { import statusBar from "./statusBar" import tabBar from "./tabBar" import { interactive } from "../element" -import merge from "ts-deepmerge" -import { icon_button, toggleable_icon_button } from "../component/icon_button" -import { text_button, toggleable_text_button } from "../component/text_button" + +import { titlebar } from "./titlebar" export default function workspace(colorScheme: ColorScheme) { const layer = colorScheme.lowest const isLight = colorScheme.isLight - const itemSpacing = 8 - const titlebarButton = toggleable({ - base: interactive({ - base: { - cornerRadius: 6, - padding: { - top: 1, - bottom: 1, - left: 8, - right: 8, - }, - ...text(layer, "sans", "variant", { size: "xs" }), - background: background(layer, "variant"), - border: border(layer), - }, - state: { - hovered: { - ...text(layer, "sans", "variant", "hovered", { - size: "xs", - }), - background: background(layer, "variant", "hovered"), - border: border(layer, "variant", "hovered"), - }, - clicked: { - ...text(layer, "sans", "variant", "pressed", { - size: "xs", - }), - background: background(layer, "variant", "pressed"), - border: border(layer, "variant", "pressed"), - }, - }, - }), - state: { - active: { - default: { - ...text(layer, "sans", "variant", "active", { size: "xs" }), - background: background(layer, "variant", "active"), - border: border(layer, "variant", "active"), - }, - }, - } - }); - const signInButton = toggleable({ - base: interactive({ - base: { - cornerRadius: 6, - padding: { - top: 1, - bottom: 1, - left: 8, - right: 8, - }, - ...text(layer, "sans", "variant", { size: "xs" }), - background: background(layer, "variant"), - }, - state: { - hovered: { - ...text(layer, "sans", "variant", "hovered", { size: "xs" }), - background: background(layer, "variant", "hovered"), - //border: border(layer, "variant", "hovered"), - }, - clicked: { - ...text(layer, "sans", "variant", "pressed", { size: "xs" }), - background: background(layer, "variant", "pressed"), - //border: border(layer, "variant", "pressed"), - } - } - }), - state: { - active: { - default: { - ...text(layer, "sans", "variant", "active", { size: "xs" }), - background: background(layer, "variant", "active"), - //border: border(layer, "variant", "active"), - } - }, - } - }); - const avatarWidth = 18 - const avatarOuterWidth = avatarWidth + 4 - const followerAvatarWidth = 14 - const followerAvatarOuterWidth = followerAvatarWidth + 4 return { background: background(colorScheme.lowest), @@ -209,163 +125,7 @@ export default function workspace(colorScheme: ColorScheme) { width: 1, }, statusBar: statusBar(colorScheme), - titlebar: { - itemSpacing, - facePileSpacing: 2, - 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 }), - padding: { - left: 80, - right: itemSpacing, - }, - - // Project - title: text(layer, "sans", "variant"), - highlight_color: text(layer, "sans", "active").color, - - // Collaborators - leaderAvatar: { - width: avatarWidth, - outerWidth: avatarOuterWidth, - cornerRadius: avatarWidth / 2, - outerCornerRadius: avatarOuterWidth / 2, - }, - followerAvatar: { - width: followerAvatarWidth, - outerWidth: followerAvatarOuterWidth, - cornerRadius: followerAvatarWidth / 2, - outerCornerRadius: followerAvatarOuterWidth / 2, - }, - inactiveAvatarGrayscale: true, - followerAvatarOverlap: 8, - leaderSelection: { - margin: { - top: 4, - bottom: 4, - }, - padding: { - left: 2, - right: 2, - top: 2, - bottom: 2, - }, - cornerRadius: 6, - }, - avatarRibbon: { - height: 3, - width: 11, - // TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded. - }, - - // Sign in buttom - // FlatButton, Variant - sign_in_button: merge(titlebarButton, { - inactive: { - default: { - margin: { - left: itemSpacing, - }, - }, - }, - - signInButton, - - }), - - // Offline Indicator - offlineIcon: { - color: foreground(layer, "variant"), - width: 16, - margin: { - left: itemSpacing, - }, - padding: { - right: 4, - }, - }, - - // Notice that the collaboration server is out of date - outdatedWarning: { - ...text(layer, "sans", "warning", { size: "xs" }), - background: withOpacity(background(layer, "warning"), 0.3), - border: border(layer, "warning"), - margin: { - left: itemSpacing, - }, - padding: { - left: 8, - right: 8, - }, - cornerRadius: 6, - }, - - leave_call_button: icon_button(colorScheme, { - margin: { - left: itemSpacing / 2, - right: itemSpacing - }, - }), - - toggle_microphone_button: toggleable_icon_button(colorScheme, { - margin: { - left: itemSpacing, - right: itemSpacing / 2 - }, - active_color: 'negative' - }), - - toggle_speakers_button: toggleable_icon_button(colorScheme, { - margin: { - left: itemSpacing / 2, - right: itemSpacing / 2 - }, - }), - - screen_share_button: toggleable_icon_button(colorScheme, { - margin: { - left: itemSpacing / 2, - right: itemSpacing - }, - active_color: 'accent' - }), - - toggle_contacts_button: toggleable_icon_button(colorScheme, { - margin: { - left: itemSpacing, - right: itemSpacing / 2 - }, - }), - - user_menu_button: - merge(titlebarButton, { - inactive: { - default: { - buttonWidth: 20, - iconWidth: 12, - }, - }, - active: { - default: { - iconWidth: 12, - button_width: 20, - background: background(layer, "variant", "active"), - color: foreground(layer, "variant", "active"), - } - }, - }), - - // Jewel that notifies you that there are new contact requests - toggleContactsBadge: { - cornerRadius: 3, - padding: 2, - margin: { top: 3, left: 3 }, - border: border(layer), - background: foreground(layer, "accent"), - }, - shareButton: toggleable_text_button(colorScheme, {}), - }, - + titlebar: titlebar(colorScheme), toolbar: { height: 34, background: background(colorScheme.highest), From 9a07696240513bee363a14a9d4d5f9e6c3f7ebf1 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 27 Jun 2023 17:56:44 -0400 Subject: [PATCH 52/63] Update titlebar item spacing --- styles/src/styleTree/titlebar.ts | 273 +++++++++++++------------------ 1 file changed, 112 insertions(+), 161 deletions(-) diff --git a/styles/src/styleTree/titlebar.ts b/styles/src/styleTree/titlebar.ts index 5f0fc3f528..993d421859 100644 --- a/styles/src/styleTree/titlebar.ts +++ b/styles/src/styleTree/titlebar.ts @@ -6,11 +6,7 @@ import { withOpacity } from "../theme/color"; import { background, border, foreground, text } from "./components"; const ITEM_SPACING = 8 - -interface SpacingProps { - container_height: number; - spacing: number; -} +const TITLEBAR_HEIGHT = 32 function build_spacing( container_height: number, @@ -18,80 +14,44 @@ function build_spacing( spacing: number ) { return { - group: spacing * 2, + group: spacing, item: spacing / 2, + half_item: spacing / 4, marginY: (container_height - element_height) / 2, marginX: (container_height - element_height) / 2, } } -function mac_os_controls(theme: ColorScheme, { container_height, spacing }: SpacingProps) { - return {} -} +function call_controls(theme: ColorScheme) { + const button_height = 19 -function project_info(theme: ColorScheme, { container_height, spacing }: SpacingProps) { - return {} -} + const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING); -function collaboration_stacks(theme: ColorScheme, { container_height, spacing }: SpacingProps) { - return {} -} + return { + toggle_microphone_button: toggleable_icon_button(theme, { + margin: { + left: space.group, + right: space.half_item, + }, + active_color: 'negative' + }), -function sharing_controls(theme: ColorScheme, { container_height, spacing }: SpacingProps) { - return {} -} + toggle_speakers_button: toggleable_icon_button(theme, { + margin: { + left: space.half_item, + right: space.half_item + }, + }), -function call_controls(theme: ColorScheme, { container_height, spacing }: SpacingProps) { - return {} -} - -const titlebarButton = (theme: ColorScheme) => toggleable({ - base: interactive({ - base: { - cornerRadius: 6, - height: 24, - width: 24, - padding: { - top: 4, - bottom: 4, - left: 4, - right: 4, + screen_share_button: toggleable_icon_button(theme, { + margin: { + left: space.half_item, + right: space.group }, - ...text(theme.lowest, "sans", { size: "xs" }), - background: background(theme.lowest), - }, - state: { - hovered: { - ...text(theme.lowest, "sans", "hovered", { - size: "xs", - }), - background: background(theme.lowest, "hovered"), - }, - clicked: { - ...text(theme.lowest, "sans", "pressed", { - size: "xs", - }), - background: background(theme.lowest, "pressed"), - }, - }, - }), - state: { - active: { - default: { - ...text(theme.lowest, "sans", "active", { size: "xs" }), - background: background(theme.middle), - }, - hovered: { - ...text(theme.lowest, "sans", "active", { size: "xs" }), - background: background(theme.middle, "hovered"), - }, - clicked: { - ...text(theme.lowest, "sans", "active", { size: "xs" }), - background: background(theme.middle, "pressed"), - }, - }, + active_color: 'accent' + }), } -}); +} /** * Opens the User Menu when toggled @@ -99,75 +59,90 @@ const titlebarButton = (theme: ColorScheme) => toggleable({ * When logged in shows the user's avatar and a chevron, * When logged out only shows a chevron. */ -function userMenuButton(theme: ColorScheme, online: boolean) { - const button = toggleable({ - base: interactive({ - base: { - cornerRadius: 6, - height: 19, - width: online ? 36 : 23, - padding: { - top: 2, - bottom: 2, - left: 6, - right: 6, - }, - ...text(theme.lowest, "sans", { size: "xs" }), - background: background(theme.lowest), - }, - state: { - hovered: { - ...text(theme.lowest, "sans", "hovered", { - size: "xs", - }), - background: background(theme.lowest, "hovered"), - }, - clicked: { - ...text(theme.lowest, "sans", "pressed", { - size: "xs", - }), - background: background(theme.lowest, "pressed"), - }, - }, - }), - state: { - active: { - default: { - ...text(theme.lowest, "sans", "active", { size: "xs" }), - background: background(theme.middle), - }, - hovered: { - ...text(theme.lowest, "sans", "active", { size: "xs" }), - background: background(theme.middle, "hovered"), - }, - clicked: { - ...text(theme.lowest, "sans", "active", { size: "xs" }), - background: background(theme.middle, "pressed"), - }, - }, - } - }); +function user_menu(theme: ColorScheme) { + const button_height = 19 - return { - user_menu: button, - avatar: { - icon_width: 16, - icon_height: 16, - cornerRadius: 4, - outer_corner_radius: 0, - outer_width: 0, - outerWidth: 16, - outerCornerRadius: 16 - }, - icon: { - margin: { - left: online ? 2 : 0, + const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING); + + const build_button = ({ online }: { online: boolean }) => { + const button = toggleable({ + base: interactive({ + base: { + cornerRadius: 6, + height: button_height, + width: online ? 36 : 23, + padding: { + top: 2, + bottom: 2, + left: 6, + right: 6, + }, + margin: { + left: space.item, + right: space.item, + }, + ...text(theme.lowest, "sans", { size: "xs" }), + background: background(theme.lowest), + }, + state: { + hovered: { + ...text(theme.lowest, "sans", "hovered", { + size: "xs", + }), + background: background(theme.lowest, "hovered"), + }, + clicked: { + ...text(theme.lowest, "sans", "pressed", { + size: "xs", + }), + background: background(theme.lowest, "pressed"), + }, + }, + }), + state: { + active: { + default: { + ...text(theme.lowest, "sans", "active", { size: "xs" }), + background: background(theme.middle), + }, + hovered: { + ...text(theme.lowest, "sans", "active", { size: "xs" }), + background: background(theme.middle, "hovered"), + }, + clicked: { + ...text(theme.lowest, "sans", "active", { size: "xs" }), + background: background(theme.middle, "pressed"), + }, + }, + } + }); + + return { + user_menu: button, + avatar: { + icon_width: 15, + icon_height: 15, + corner_radius: 4, + outer_width: 15, + outer_corner_radius: 15 }, - width: 11, - height: 11, - color: foreground(theme.lowest) + icon: { + margin: { + top: 2, + left: online ? space.item : 0, + right: space.group, + bottom: 2, + }, + width: 11, + height: 11, + color: foreground(theme.lowest) + } } } + return { + userMenuButtonOnline: build_button({ online: true }), + userMenuButtonOffline: build_button({ online: false }), + } } export function titlebar(theme: ColorScheme) { @@ -177,14 +152,14 @@ export function titlebar(theme: ColorScheme) { const followerAvatarOuterWidth = followerAvatarWidth + 4 return { - ITEM_SPACING, + item_spacing: ITEM_SPACING, facePileSpacing: 2, - height: 33, // 32px + 1px border. It's important the content area of the titlebar is evenly sized to vertically center avatar images. + height: TITLEBAR_HEIGHT, background: background(theme.lowest), border: border(theme.lowest, { bottom: true }), padding: { left: 80, - right: ITEM_SPACING, + right: 0, }, // Project @@ -262,28 +237,7 @@ export function titlebar(theme: ColorScheme) { }, }), - toggle_microphone_button: toggleable_icon_button(theme, { - margin: { - left: ITEM_SPACING, - right: ITEM_SPACING / 2 - }, - active_color: 'negative' - }), - - toggle_speakers_button: toggleable_icon_button(theme, { - margin: { - left: ITEM_SPACING / 2, - right: ITEM_SPACING / 2 - }, - }), - - screen_share_button: toggleable_icon_button(theme, { - margin: { - left: ITEM_SPACING / 2, - right: ITEM_SPACING - }, - active_color: 'accent' - }), + ...call_controls(theme), toggle_contacts_button: toggleable_icon_button(theme, { margin: { @@ -301,9 +255,6 @@ export function titlebar(theme: ColorScheme) { background: foreground(theme.lowest, "accent"), }, shareButton: toggleable_text_button(theme, {}), - user_menu: { - userMenuButtonOnline: userMenuButton(theme, true), - userMenuButtonOffline: userMenuButton(theme, false), - } + user_menu: user_menu(theme) } } From 5d02b490582f47ebc8641825755ac250d7aad992 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 27 Jun 2023 19:19:08 -0700 Subject: [PATCH 53/63] Added muted and currently speaking tracking --- crates/call/src/participant.rs | 1 + crates/call/src/room.rs | 39 ++++++++++- crates/collab_ui/src/collab_titlebar_item.rs | 31 +++++++-- .../Sources/LiveKitBridge/LiveKitBridge.swift | 21 ++++++ crates/live_kit_client/examples/test_app.rs | 39 ++++++++++- crates/live_kit_client/src/prod.rs | 64 ++++++++++++++++++- crates/live_kit_client/src/test.rs | 1 + script/start-local-collaboration | 2 +- 8 files changed, 185 insertions(+), 13 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index 9773e837c3..e7858869ce 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -44,6 +44,7 @@ pub struct RemoteParticipant { pub projects: Vec, pub location: ParticipantLocation, pub muted: bool, + pub speaking: bool, pub video_tracks: HashMap>, pub audio_tracks: HashMap>, } diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 7b7ec89a5e..e5f000c34b 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -164,6 +164,7 @@ impl Room { microphone_track: LocalTrack::None, next_publish_id: 0, deafened: false, + speaking: false, _maintain_room, _maintain_tracks: [_maintain_video_tracks, _maintain_audio_tracks], }) @@ -648,6 +649,7 @@ impl Room { projects: participant.projects, location, muted: false, + speaking: false, video_tracks: Default::default(), audio_tracks: Default::default(), }, @@ -782,6 +784,30 @@ impl Room { cx: &mut ModelContext, ) -> Result<()> { match change { + RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers } => { + let mut speaker_ids = speakers + .into_iter() + .filter_map(|speaker_sid| speaker_sid.parse().ok()) + .collect::>(); + speaker_ids.sort_unstable(); + for (sid, participant) in &mut self.remote_participants { + if let Ok(_) = speaker_ids.binary_search(sid) { + participant.speaking = true; + } else { + participant.speaking = false; + } + } + if let Some(id) = self.client.user_id() { + if let Some(room) = &mut self.live_kit { + if let Ok(_) = speaker_ids.binary_search(&id) { + room.speaking = true; + } else { + room.speaking = false; + } + } + } + cx.notify(); + } RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => { for participant in &mut self.remote_participants.values_mut() { let mut found = false; @@ -796,6 +822,7 @@ impl Room { break; } } + cx.notify(); } RemoteAudioTrackUpdate::Subscribed(track) => { let user_id = track.publisher_id().parse()?; @@ -1011,7 +1038,7 @@ impl Room { }) } - pub fn is_muted(&self) -> Option { + pub fn is_muted(&self) -> bool { self.live_kit .as_ref() .and_then(|live_kit| match &live_kit.microphone_track { @@ -1019,6 +1046,13 @@ impl Room { LocalTrack::Pending { muted, .. } => Some(*muted), LocalTrack::Published { muted, .. } => Some(*muted), }) + .unwrap_or(false) + } + + pub fn is_speaking(&self) -> bool { + self.live_kit + .as_ref() + .map_or(false, |live_kit| live_kit.speaking) } pub fn is_deafened(&self) -> Option { @@ -1215,7 +1249,7 @@ impl Room { } } pub fn toggle_mute(&mut self, cx: &mut ModelContext) -> Result>> { - let should_mute = self.is_muted().unwrap_or(false); + let should_mute = self.is_muted(); if let Some(live_kit) = self.live_kit.as_mut() { Self::set_mute(live_kit, !should_mute, cx) } else { @@ -1298,6 +1332,7 @@ struct LiveKitRoom { screen_track: LocalTrack, microphone_track: LocalTrack, deafened: bool, + speaking: bool, next_publish_id: usize, _maintain_room: Task<()>, _maintain_tracks: [Task<()>; 2], diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 708fb89e86..e23320f827 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -86,8 +86,10 @@ impl View for CollabTitlebarItem { right_container .add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx)); right_container.add_child(self.render_leave_call(&theme, cx)); + let muted = room.read(cx).is_muted(); + let speaking = room.read(cx).is_speaking(); left_container - .add_child(self.render_current_user(&workspace, &theme, &user, peer_id, cx)); + .add_child(self.render_current_user(&workspace, &theme, &user, peer_id, muted, speaking, cx)); left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx)); right_container.add_child(self.render_toggle_mute(&theme, &room, cx)); right_container.add_child(self.render_toggle_deafen(&theme, &room, cx)); @@ -449,7 +451,7 @@ impl CollabTitlebarItem { ) -> AnyElement { let icon; let tooltip; - let is_muted = room.read(cx).is_muted().unwrap_or(false); + let is_muted = room.read(cx).is_muted(); if is_muted { icon = "icons/radix/mic-mute.svg"; tooltip = "Unmute microphone\nRight click for options"; @@ -766,6 +768,8 @@ impl CollabTitlebarItem { replica_id, participant.peer_id, Some(participant.location), + participant.muted, + participant.speaking, workspace, theme, cx, @@ -782,14 +786,19 @@ impl CollabTitlebarItem { theme: &Theme, user: &Arc, peer_id: PeerId, + muted: bool, + speaking: bool, cx: &mut ViewContext, ) -> AnyElement { let replica_id = workspace.read(cx).project().read(cx).replica_id(); + Container::new(self.render_face_pile( user, Some(replica_id), peer_id, None, + muted, + speaking, workspace, theme, cx, @@ -804,6 +813,8 @@ impl CollabTitlebarItem { replica_id: Option, peer_id: PeerId, location: Option, + muted: bool, + speaking: bool, workspace: &ViewHandle, theme: &Theme, cx: &mut ViewContext, @@ -829,11 +840,17 @@ impl CollabTitlebarItem { let leader_style = theme.titlebar.leader_avatar; let follower_style = theme.titlebar.follower_avatar; - let mut background_color = theme - .titlebar - .container - .background_color - .unwrap_or_default(); + let mut background_color = if muted { + gpui::color::Color::red() + } else if speaking { + gpui::color::Color::green() + } else { + theme + .titlebar + .container + .background_color + .unwrap_or_default() + }; if let Some(replica_id) = replica_id { if followed_by_self { let selection = theme.editor.replica_selection_style(replica_id).selection; diff --git a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift index 74d43d7865..40d3641db2 100644 --- a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift +++ b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift @@ -8,6 +8,8 @@ class LKRoomDelegate: RoomDelegate { var onDidDisconnect: @convention(c) (UnsafeRawPointer) -> Void var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void var onDidUnsubscribeFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void + var onMuteChangedFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void + var onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void var onDidSubscribeToRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void var onDidUnsubscribeFromRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void @@ -16,6 +18,8 @@ class LKRoomDelegate: RoomDelegate { onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void, onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, + onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void, + onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void, onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void) { @@ -25,6 +29,8 @@ class LKRoomDelegate: RoomDelegate { self.onDidUnsubscribeFromRemoteAudioTrack = onDidUnsubscribeFromRemoteAudioTrack self.onDidSubscribeToRemoteVideoTrack = onDidSubscribeToRemoteVideoTrack self.onDidUnsubscribeFromRemoteVideoTrack = onDidUnsubscribeFromRemoteVideoTrack + self.onMuteChangedFromRemoteAudioTrack = onMuteChangedFromRemoteAudioTrack + self.onActiveSpeakersChanged = onActiveSpeakersChanged } func room(_ room: Room, didUpdate connectionState: ConnectionState, oldValue: ConnectionState) { @@ -41,6 +47,17 @@ class LKRoomDelegate: RoomDelegate { } } + func room(_ room: Room, participant: Participant, didUpdate publication: TrackPublication, muted: Bool) { + if publication.kind == .audio { + self.onMuteChangedFromRemoteAudioTrack(self.data, publication.sid as CFString, muted) + } + } + + func room(_ room: Room, didUpdate speakers: [Participant]) { + guard let speaker_ids = speakers.compactMap({ $0.identity as CFString }) as CFArray? else { return } + self.onActiveSpeakersChanged(self.data, speaker_ids) + } + func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) { if track.kind == .video { self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString) @@ -89,6 +106,8 @@ public func LKRoomDelegateCreate( onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void, onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, + onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void, + onActiveSpeakerChanged: @escaping @convention(c) (UnsafeRawPointer, CFArray) -> Void, onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void ) -> UnsafeMutableRawPointer { @@ -97,6 +116,8 @@ public func LKRoomDelegateCreate( onDidDisconnect: onDidDisconnect, onDidSubscribeToRemoteAudioTrack: onDidSubscribeToRemoteAudioTrack, onDidUnsubscribeFromRemoteAudioTrack: onDidUnsubscribeFromRemoteAudioTrack, + onMuteChangedFromRemoteAudioTrack: onMuteChangedFromRemoteAudioTrack, + onActiveSpeakersChanged: onActiveSpeakerChanged, onDidSubscribeToRemoteVideoTrack: onDidSubscribeToRemoteVideoTrack, onDidUnsubscribeFromRemoteVideoTrack: onDidUnsubscribeFromRemoteVideoTrack ) diff --git a/crates/live_kit_client/examples/test_app.rs b/crates/live_kit_client/examples/test_app.rs index faf1b54798..4fc02d9a9d 100644 --- a/crates/live_kit_client/examples/test_app.rs +++ b/crates/live_kit_client/examples/test_app.rs @@ -74,19 +74,54 @@ fn main() { panic!("unexpected message"); } + audio_track_publication.set_mute(true).await.unwrap(); + + println!("waiting for mute changed!"); + if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } = + audio_track_updates.next().await.unwrap() + { + let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); + assert_eq!(remote_tracks[0].sid(), track_id); + assert_eq!(muted, true); + } else { + panic!("unexpected message"); + } + + audio_track_publication.set_mute(false).await.unwrap(); + + if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } = + audio_track_updates.next().await.unwrap() + { + let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); + assert_eq!(remote_tracks[0].sid(), track_id); + assert_eq!(muted, false); + } else { + panic!("unexpected message"); + } + println!("Pausing for 5 seconds to test audio, make some noise!"); let timer = cx.background().timer(Duration::from_secs(5)); timer.await; - let remote_audio_track = room_b .remote_audio_tracks("test-participant-1") .pop() .unwrap(); room_a.unpublish_track(audio_track_publication); + + // Clear out any active speakers changed messages + let mut next = audio_track_updates.next().await.unwrap(); + while let RemoteAudioTrackUpdate::ActiveSpeakersChanged { + speakers + } = next + { + println!("Speakers changed: {:?}", speakers); + next = audio_track_updates.next().await.unwrap(); + } + if let RemoteAudioTrackUpdate::Unsubscribed { publisher_id, track_id, - } = audio_track_updates.next().await.unwrap() + } = next { assert_eq!(publisher_id, "test-participant-1"); assert_eq!(remote_audio_track.sid(), track_id); diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index 96bf40ca4e..bdbba87b51 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -32,6 +32,15 @@ extern "C" { publisher_id: CFStringRef, track_id: CFStringRef, ), + on_mute_changed_from_remote_audio_track: extern "C" fn( + callback_data: *mut c_void, + track_id: CFStringRef, + muted: bool, + ), + on_active_speakers_changed: extern "C" fn( + callback_data: *mut c_void, + participants: CFArrayRef, + ), on_did_subscribe_to_remote_video_track: extern "C" fn( callback_data: *mut c_void, publisher_id: CFStringRef, @@ -381,6 +390,24 @@ impl Room { }); } + fn mute_changed_from_remote_audio_track(&self, track_id: String, muted: bool) { + self.remote_audio_track_subscribers.lock().retain(|tx| { + tx.unbounded_send(RemoteAudioTrackUpdate::MuteChanged { + track_id: track_id.clone(), + muted, + }) + .is_ok() + }); + } + + // A vec of publisher IDs + fn active_speakers_changed(&self, speakers: Vec) { + self.remote_audio_track_subscribers.lock().retain(move |tx| { + tx.unbounded_send(RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers: speakers.clone() }) + .is_ok() + }); + } + fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) { let track = Arc::new(track); self.remote_video_track_subscribers.lock().retain(|tx| { @@ -445,6 +472,8 @@ impl RoomDelegate { Self::on_did_disconnect, Self::on_did_subscribe_to_remote_audio_track, Self::on_did_unsubscribe_from_remote_audio_track, + Self::on_mute_change_from_remote_audio_track, + Self::on_active_speakers_changed, Self::on_did_subscribe_to_remote_video_track, Self::on_did_unsubscribe_from_remote_video_track, ) @@ -493,6 +522,38 @@ impl RoomDelegate { let _ = Weak::into_raw(room); } + extern "C" fn on_mute_change_from_remote_audio_track( + room: *mut c_void, + track_id: CFStringRef, + muted: bool, + ) { + let room = unsafe { Weak::from_raw(room as *mut Room) }; + let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; + if let Some(room) = room.upgrade() { + room.mute_changed_from_remote_audio_track(track_id, muted); + } + let _ = Weak::into_raw(room); + } + + extern "C" fn on_active_speakers_changed(room: *mut c_void, participants: CFArrayRef) { + if participants.is_null() { + return; + } + + let room = unsafe { Weak::from_raw(room as *mut Room) }; + let speakers = unsafe { + CFArray::wrap_under_get_rule(participants) + .into_iter() + .map(|speaker: core_foundation::base::ItemRef<'_, *const c_void>| CFString::wrap_under_get_rule(*speaker as CFStringRef).to_string()) + .collect() + }; + + if let Some(room) = room.upgrade() { + room.active_speakers_changed(speakers); + } + let _ = Weak::into_raw(room); + } + extern "C" fn on_did_subscribe_to_remote_video_track( room: *mut c_void, publisher_id: CFStringRef, @@ -761,7 +822,8 @@ pub enum RemoteVideoTrackUpdate { } pub enum RemoteAudioTrackUpdate { - MuteChanged { track_id: Sid, muted: bool}, + ActiveSpeakersChanged { speakers: Vec }, + MuteChanged { track_id: Sid, muted: bool }, Subscribed(Arc), Unsubscribed { publisher_id: Sid, track_id: Sid }, } diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index 286c7215e9..809a1f5d29 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -580,6 +580,7 @@ pub enum RemoteVideoTrackUpdate { #[derive(Clone)] pub enum RemoteAudioTrackUpdate { + ActiveSpeakersChanged { speakers: Vec }, MuteChanged { track_id: Sid, muted: bool}, Subscribed(Arc), Unsubscribed { publisher_id: Sid, track_id: Sid }, diff --git a/script/start-local-collaboration b/script/start-local-collaboration index b8632c4c22..b702fb4e02 100755 --- a/script/start-local-collaboration +++ b/script/start-local-collaboration @@ -54,5 +54,5 @@ sleep 0.5 # Start the two Zed child processes. Open the given paths with the first instance. trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT ZED_IMPERSONATE=${username_1} ZED_WINDOW_POSITION=${position_1} target/debug/Zed $@ & -ZED_IMPERSONATE=${username_2} ZED_WINDOW_POSITION=${position_2} target/debug/Zed & +SECOND=true ZED_IMPERSONATE=${username_2} ZED_WINDOW_POSITION=${position_2} target/debug/Zed & wait From 749f60ba8199f64b12fe877082f7f4263d234cf5 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 27 Jun 2023 19:25:46 -0700 Subject: [PATCH 54/63] fmt --- crates/collab_ui/src/collab_titlebar_item.rs | 5 +++-- crates/live_kit_client/examples/test_app.rs | 5 +---- crates/live_kit_client/src/prod.rs | 16 ++++++++++++---- crates/live_kit_client/src/test.rs | 2 +- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index e23320f827..ae80e59f22 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -88,8 +88,9 @@ impl View for CollabTitlebarItem { right_container.add_child(self.render_leave_call(&theme, cx)); let muted = room.read(cx).is_muted(); let speaking = room.read(cx).is_speaking(); - left_container - .add_child(self.render_current_user(&workspace, &theme, &user, peer_id, muted, speaking, cx)); + left_container.add_child( + self.render_current_user(&workspace, &theme, &user, peer_id, muted, speaking, cx), + ); left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx)); right_container.add_child(self.render_toggle_mute(&theme, &room, cx)); right_container.add_child(self.render_toggle_deafen(&theme, &room, cx)); diff --git a/crates/live_kit_client/examples/test_app.rs b/crates/live_kit_client/examples/test_app.rs index 4fc02d9a9d..f5f6d0e46f 100644 --- a/crates/live_kit_client/examples/test_app.rs +++ b/crates/live_kit_client/examples/test_app.rs @@ -110,10 +110,7 @@ fn main() { // Clear out any active speakers changed messages let mut next = audio_track_updates.next().await.unwrap(); - while let RemoteAudioTrackUpdate::ActiveSpeakersChanged { - speakers - } = next - { + while let RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers } = next { println!("Speakers changed: {:?}", speakers); next = audio_track_updates.next().await.unwrap(); } diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index bdbba87b51..6daa0601ca 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -402,10 +402,14 @@ impl Room { // A vec of publisher IDs fn active_speakers_changed(&self, speakers: Vec) { - self.remote_audio_track_subscribers.lock().retain(move |tx| { - tx.unbounded_send(RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers: speakers.clone() }) + self.remote_audio_track_subscribers + .lock() + .retain(move |tx| { + tx.unbounded_send(RemoteAudioTrackUpdate::ActiveSpeakersChanged { + speakers: speakers.clone(), + }) .is_ok() - }); + }); } fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) { @@ -544,7 +548,11 @@ impl RoomDelegate { let speakers = unsafe { CFArray::wrap_under_get_rule(participants) .into_iter() - .map(|speaker: core_foundation::base::ItemRef<'_, *const c_void>| CFString::wrap_under_get_rule(*speaker as CFStringRef).to_string()) + .map( + |speaker: core_foundation::base::ItemRef<'_, *const c_void>| { + CFString::wrap_under_get_rule(*speaker as CFStringRef).to_string() + }, + ) .collect() }; diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index 809a1f5d29..3fc046c5a2 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -581,7 +581,7 @@ pub enum RemoteVideoTrackUpdate { #[derive(Clone)] pub enum RemoteAudioTrackUpdate { ActiveSpeakersChanged { speakers: Vec }, - MuteChanged { track_id: Sid, muted: bool}, + MuteChanged { track_id: Sid, muted: bool }, Subscribed(Arc), Unsubscribed { publisher_id: Sid, track_id: Sid }, } From 2ddf0e12bfd214d46efca8d36e8ea4fede4e7a35 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:10:56 +0200 Subject: [PATCH 55/63] Undeafen: unmute if user was not muted prior to deafening --- crates/call/src/room.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index e5f000c34b..da298f9ca2 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -163,6 +163,7 @@ impl Room { screen_track: LocalTrack::None, microphone_track: LocalTrack::None, next_publish_id: 0, + muted_by_user: false, deafened: false, speaking: false, _maintain_room, @@ -1231,6 +1232,10 @@ impl Room { should_mute: bool, cx: &mut ModelContext, ) -> Result>> { + if !should_mute { + // clear user muting state. + live_kit.muted_by_user = false; + } match &mut live_kit.microphone_track { LocalTrack::None => Err(anyhow!("microphone was not shared")), LocalTrack::Pending { muted, .. } => { @@ -1249,9 +1254,11 @@ impl Room { } } pub fn toggle_mute(&mut self, cx: &mut ModelContext) -> Result>> { - let should_mute = self.is_muted(); + let should_mute = !self.is_muted(); if let Some(live_kit) = self.live_kit.as_mut() { - Self::set_mute(live_kit, !should_mute, cx) + let ret = Self::set_mute(live_kit, should_mute, cx); + live_kit.muted_by_user = should_mute; + ret } else { Err(anyhow!("LiveKit not started")) } @@ -1264,9 +1271,9 @@ impl Room { let mut tasks = Vec::with_capacity(self.remote_participants.len()); // Context notification is sent within set_mute itself. let mut mute_task = None; - if live_kit.deafened { - // Unmute microphone only if we're going from unmuted -> muted state. - // We don't want to unmute user automatically. + // When deafening, mute user's mic as well. + // When undeafening, unmute user's mic unless it was manually muted prior to deafening. + if live_kit.deafened || !live_kit.muted_by_user { mute_task = Some(Self::set_mute(live_kit, live_kit.deafened, cx)?); }; for participant in self.remote_participants.values() { @@ -1331,6 +1338,8 @@ struct LiveKitRoom { room: Arc, screen_track: LocalTrack, microphone_track: LocalTrack, + /// Tracks whether we're currently in a muted state due to auto-mute from deafening or manual mute performed by user. + muted_by_user: bool, deafened: bool, speaking: bool, next_publish_id: usize, From 456be1f86e5415e43090dc39b4f4e0dde0427841 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:19:32 +0200 Subject: [PATCH 56/63] Hide user menu on second click --- crates/collab_ui/src/collab_titlebar_item.rs | 77 ++++++++++---------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index ae80e59f22..6621aed875 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -49,7 +49,7 @@ pub struct CollabTitlebarItem { client: Arc, workspace: WeakViewHandle, contacts_popover: Option>, - user_menu: ViewHandle, + user_menu: Option>, _subscriptions: Vec, } @@ -170,18 +170,13 @@ impl CollabTitlebarItem { }), ); - let view_id = cx.view_id(); Self { workspace: workspace.weak_handle(), project, user_store, client, contacts_popover: None, - user_menu: cx.add_view(|cx| { - let mut menu = ContextMenu::new(view_id, cx); - menu.set_position_mode(OverlayPositionMode::Local); - menu - }), + user_menu: None, _subscriptions: subscriptions, } } @@ -294,32 +289,41 @@ impl CollabTitlebarItem { } pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext) { - self.user_menu.update(cx, |user_menu, cx| { - let items = if let Some(_) = self.user_store.read(cx).current_user() { - vec![ - ContextMenuItem::action("Settings", zed_actions::OpenSettings), - ContextMenuItem::action("Theme", theme_selector::Toggle), - ContextMenuItem::separator(), - ContextMenuItem::action( - "Share Feedback", - feedback::feedback_editor::GiveFeedback, - ), - ContextMenuItem::action("Sign out", SignOut), - ] - } else { - vec![ - ContextMenuItem::action("Settings", zed_actions::OpenSettings), - ContextMenuItem::action("Theme", theme_selector::Toggle), - ContextMenuItem::separator(), - ContextMenuItem::action( - "Share Feedback", - feedback::feedback_editor::GiveFeedback, - ), - ] - }; + if self.user_menu.take().is_none() { + let mut user_menu = cx.add_view(|cx| { + let view_id = cx.view_id(); + let mut menu = ContextMenu::new(view_id, cx); + menu.set_position_mode(OverlayPositionMode::Local); + menu + }); + user_menu.update(cx, |user_menu, cx| { + let items = if let Some(_) = self.user_store.read(cx).current_user() { + vec![ + ContextMenuItem::action("Settings", zed_actions::OpenSettings), + ContextMenuItem::action("Theme", theme_selector::Toggle), + ContextMenuItem::separator(), + ContextMenuItem::action( + "Share Feedback", + feedback::feedback_editor::GiveFeedback, + ), + ContextMenuItem::action("Sign out", SignOut), + ] + } else { + vec![ + ContextMenuItem::action("Settings", zed_actions::OpenSettings), + ContextMenuItem::action("Theme", theme_selector::Toggle), + ContextMenuItem::separator(), + ContextMenuItem::action( + "Share Feedback", + feedback::feedback_editor::GiveFeedback, + ), + ] + }; - user_menu.show(Default::default(), AnchorCorner::TopRight, items, cx); - }); + user_menu.show(Default::default(), AnchorCorner::TopRight, items, cx); + }); + self.user_menu = Some(user_menu); + } } fn render_toggle_contacts_button( @@ -695,11 +699,10 @@ impl CollabTitlebarItem { ) .contained(), ) - .with_child( - ChildView::new(&self.user_menu, cx) - .aligned() - .bottom() - .right(), + .with_children( + self.user_menu + .as_ref() + .map(|menu| ChildView::new(&menu, cx).aligned().bottom().right()), ) .into_any() } From c2f5855fdc21158f28ecebba7cce8146682128cd Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:22:17 +0200 Subject: [PATCH 57/63] Fix warning --- crates/collab_ui/src/collab_titlebar_item.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 6621aed875..e5837330a4 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -290,7 +290,7 @@ impl CollabTitlebarItem { pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext) { if self.user_menu.take().is_none() { - let mut user_menu = cx.add_view(|cx| { + let user_menu = cx.add_view(|cx| { let view_id = cx.view_id(); let mut menu = ContextMenu::new(view_id, cx); menu.set_position_mode(OverlayPositionMode::Local); From 49ce1977e3409740f29b4d5565345c557c0938e0 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:27:38 +0200 Subject: [PATCH 58/63] Optimize user menu trigger --- crates/collab_ui/src/collab_titlebar_item.rs | 34 +++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index e5837330a4..76d1741c14 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -49,7 +49,8 @@ pub struct CollabTitlebarItem { client: Arc, workspace: WeakViewHandle, contacts_popover: Option>, - user_menu: Option>, + user_menu: ViewHandle, + user_menu_is_visible: bool, _subscriptions: Vec, } @@ -176,7 +177,13 @@ impl CollabTitlebarItem { user_store, client, contacts_popover: None, - user_menu: None, + user_menu: cx.add_view(|cx| { + let view_id = cx.view_id(); + let mut menu = ContextMenu::new(view_id, cx); + menu.set_position_mode(OverlayPositionMode::Local); + menu + }), + user_menu_is_visible: false, _subscriptions: subscriptions, } } @@ -289,14 +296,8 @@ impl CollabTitlebarItem { } pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext) { - if self.user_menu.take().is_none() { - let user_menu = cx.add_view(|cx| { - let view_id = cx.view_id(); - let mut menu = ContextMenu::new(view_id, cx); - menu.set_position_mode(OverlayPositionMode::Local); - menu - }); - user_menu.update(cx, |user_menu, cx| { + if !self.user_menu_is_visible { + self.user_menu.update(cx, |user_menu, cx| { let items = if let Some(_) = self.user_store.read(cx).current_user() { vec![ ContextMenuItem::action("Settings", zed_actions::OpenSettings), @@ -322,8 +323,8 @@ impl CollabTitlebarItem { user_menu.show(Default::default(), AnchorCorner::TopRight, items, cx); }); - self.user_menu = Some(user_menu); } + self.user_menu_is_visible = !self.user_menu_is_visible; } fn render_toggle_contacts_button( @@ -699,11 +700,12 @@ impl CollabTitlebarItem { ) .contained(), ) - .with_children( - self.user_menu - .as_ref() - .map(|menu| ChildView::new(&menu, cx).aligned().bottom().right()), - ) + .with_children(self.user_menu_is_visible.then(|| { + ChildView::new(&self.user_menu, cx) + .aligned() + .bottom() + .right() + })) .into_any() } From a889f3db54866ae690516cde8bbf967a99a56383 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 28 Jun 2023 10:06:12 -0400 Subject: [PATCH 59/63] Reduce the right spacing of the contacts menu --- styles/src/styleTree/titlebar.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/styles/src/styleTree/titlebar.ts b/styles/src/styleTree/titlebar.ts index 993d421859..b1971a2e8d 100644 --- a/styles/src/styleTree/titlebar.ts +++ b/styles/src/styleTree/titlebar.ts @@ -200,10 +200,7 @@ export function titlebar(theme: ColorScheme) { // TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded. }, - // Sign in buttom sign_in_button: toggleable_text_button(theme, {}), - - // Offline Indicator offlineIcon: { color: foreground(theme.lowest, "variant"), width: 16, @@ -215,7 +212,7 @@ export function titlebar(theme: ColorScheme) { }, }, - // Notice that the collaboration server is out of date + // When the collaboration server is out of date, show a warning outdatedWarning: { ...text(theme.lowest, "sans", "warning", { size: "xs" }), background: withOpacity(background(theme.lowest, "warning"), 0.3), @@ -242,7 +239,6 @@ export function titlebar(theme: ColorScheme) { toggle_contacts_button: toggleable_icon_button(theme, { margin: { left: ITEM_SPACING, - right: ITEM_SPACING / 2 }, }), From c381e8821ce68e887a2ae92ae798960fb15a84aa Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 28 Jun 2023 10:19:30 -0400 Subject: [PATCH 60/63] Update icon sizes --- styles/src/component/icon_button.ts | 8 ++++---- styles/src/styleTree/titlebar.ts | 23 +++++++++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/styles/src/component/icon_button.ts b/styles/src/component/icon_button.ts index 850acc62f9..43f8d0f9c4 100644 --- a/styles/src/component/icon_button.ts +++ b/styles/src/component/icon_button.ts @@ -38,10 +38,10 @@ export function icon_button(theme: ColorScheme, { color, margin, layer }: IconBu right: 4, }, margin: m, - icon_width: 15, - icon_height: 15, - button_width: 21, - button_height: 17, + icon_width: 14, + icon_height: 14, + button_width: 20, + button_height: 16, }, state: { default: { diff --git a/styles/src/styleTree/titlebar.ts b/styles/src/styleTree/titlebar.ts index b1971a2e8d..f27c301433 100644 --- a/styles/src/styleTree/titlebar.ts +++ b/styles/src/styleTree/titlebar.ts @@ -23,13 +23,18 @@ function build_spacing( } function call_controls(theme: ColorScheme) { - const button_height = 19 + const button_height = 18 const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING); + const marginY = { + top: space.marginY, + bottom: space.marginY, + } return { toggle_microphone_button: toggleable_icon_button(theme, { margin: { + ...marginY, left: space.group, right: space.half_item, }, @@ -38,6 +43,7 @@ function call_controls(theme: ColorScheme) { toggle_speakers_button: toggleable_icon_button(theme, { margin: { + ...marginY, left: space.half_item, right: space.half_item }, @@ -45,6 +51,7 @@ function call_controls(theme: ColorScheme) { screen_share_button: toggleable_icon_button(theme, { margin: { + ...marginY, left: space.half_item, right: space.group }, @@ -60,7 +67,7 @@ function call_controls(theme: ColorScheme) { * When logged out only shows a chevron. */ function user_menu(theme: ColorScheme) { - const button_height = 19 + const button_height = 18 const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING); @@ -70,7 +77,7 @@ function user_menu(theme: ColorScheme) { base: { cornerRadius: 6, height: button_height, - width: online ? 36 : 23, + width: online ? 37 : 24, padding: { top: 2, bottom: 2, @@ -120,11 +127,11 @@ function user_menu(theme: ColorScheme) { return { user_menu: button, avatar: { - icon_width: 15, - icon_height: 15, + icon_width: 16, + icon_height: 16, corner_radius: 4, - outer_width: 15, - outer_corner_radius: 15 + outer_width: 16, + outer_corner_radius: 16 }, icon: { margin: { @@ -196,7 +203,7 @@ export function titlebar(theme: ColorScheme) { }, avatarRibbon: { height: 3, - width: 11, + width: 14, // TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded. }, From 9ab0efa1f80714fa50aab6001d64c543f6fa25f5 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 28 Jun 2023 08:41:31 -0700 Subject: [PATCH 61/63] Fix microphone and speaking styles --- crates/collab_ui/src/collab_titlebar_item.rs | 25 +++++++++++++------- crates/theme/src/theme.rs | 3 +++ styles/src/styleTree/titlebar.ts | 5 +++- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 76d1741c14..72aa247f35 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -668,6 +668,7 @@ impl CollabTitlebarItem { avatar_img, *avatar_style, Color::transparent_black(), + None )); }; @@ -846,17 +847,20 @@ impl CollabTitlebarItem { let leader_style = theme.titlebar.leader_avatar; let follower_style = theme.titlebar.follower_avatar; - let mut background_color = if muted { - gpui::color::Color::red() + let microphone_state = if muted { + Some(theme.titlebar.muted) } else if speaking { - gpui::color::Color::green() + Some(theme.titlebar.speaking) } else { - theme - .titlebar - .container - .background_color - .unwrap_or_default() + None }; + + let mut background_color = theme + .titlebar + .container + .background_color + .unwrap_or_default(); + if let Some(replica_id) = replica_id { if followed_by_self { let selection = theme.editor.replica_selection_style(replica_id).selection; @@ -872,6 +876,7 @@ impl CollabTitlebarItem { avatar.clone(), Self::location_style(workspace, location, leader_style, cx), background_color, + microphone_state )) .with_children( (|| { @@ -903,6 +908,7 @@ impl CollabTitlebarItem { avatar.clone(), follower_style, background_color, + None )) })) })() @@ -1021,12 +1027,13 @@ impl CollabTitlebarItem { avatar: Arc, avatar_style: AvatarStyle, background_color: Color, + microphone_state: Option ) -> AnyElement { Image::from_data(avatar) .with_style(avatar_style.image) .aligned() .contained() - .with_background_color(background_color) + .with_background_color(microphone_state.unwrap_or(background_color)) .with_corner_radius(avatar_style.outer_corner_radius) .constrained() .with_width(avatar_style.outer_width) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 06c6026e3a..0a62459a3a 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -132,6 +132,8 @@ pub struct Titlebar { pub sign_in_button: Toggleable>, pub outdated_warning: ContainedText, pub share_button: Toggleable>, + pub muted: Color, + pub speaking: Color, pub screen_share_button: Toggleable>, pub toggle_contacts_button: Toggleable>, pub toggle_microphone_button: Toggleable>, @@ -146,6 +148,7 @@ pub struct UserMenu { pub user_menu_button_online: UserMenuButton, pub user_menu_button_offline: UserMenuButton, } + #[derive(Clone, Deserialize, Default, JsonSchema)] pub struct UserMenuButton { pub user_menu: Toggleable>, diff --git a/styles/src/styleTree/titlebar.ts b/styles/src/styleTree/titlebar.ts index f27c301433..fe0885c8e7 100644 --- a/styles/src/styleTree/titlebar.ts +++ b/styles/src/styleTree/titlebar.ts @@ -57,6 +57,9 @@ function call_controls(theme: ColorScheme) { }, active_color: 'accent' }), + + muted: foreground(theme.lowest, "negative"), + speaking: foreground(theme.lowest, "accent"), } } @@ -153,7 +156,7 @@ function user_menu(theme: ColorScheme) { } export function titlebar(theme: ColorScheme) { - const avatarWidth = 18 + const avatarWidth = 15 const avatarOuterWidth = avatarWidth + 4 const followerAvatarWidth = 14 const followerAvatarOuterWidth = followerAvatarWidth + 4 From 88b30dea100137538c86e35ca941fff3b9ff2102 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 28 Jun 2023 08:44:17 -0700 Subject: [PATCH 62/63] fmt --- crates/collab_ui/src/collab_titlebar_item.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 72aa247f35..e76778b2cd 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -668,7 +668,7 @@ impl CollabTitlebarItem { avatar_img, *avatar_style, Color::transparent_black(), - None + None, )); }; @@ -876,7 +876,7 @@ impl CollabTitlebarItem { avatar.clone(), Self::location_style(workspace, location, leader_style, cx), background_color, - microphone_state + microphone_state, )) .with_children( (|| { @@ -908,7 +908,7 @@ impl CollabTitlebarItem { avatar.clone(), follower_style, background_color, - None + None, )) })) })() @@ -1027,7 +1027,7 @@ impl CollabTitlebarItem { avatar: Arc, avatar_style: AvatarStyle, background_color: Color, - microphone_state: Option + microphone_state: Option, ) -> AnyElement { Image::from_data(avatar) .with_style(avatar_style.image) From cb782c5a7b0c306056c76734f72997592210248a Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 28 Jun 2023 09:06:06 -0700 Subject: [PATCH 63/63] Remove menu tracking state --- crates/collab_ui/src/collab_titlebar_item.rs | 62 +++++++++----------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index e76778b2cd..2ab8928166 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -50,7 +50,6 @@ pub struct CollabTitlebarItem { workspace: WeakViewHandle, contacts_popover: Option>, user_menu: ViewHandle, - user_menu_is_visible: bool, _subscriptions: Vec, } @@ -183,7 +182,6 @@ impl CollabTitlebarItem { menu.set_position_mode(OverlayPositionMode::Local); menu }), - user_menu_is_visible: false, _subscriptions: subscriptions, } } @@ -296,35 +294,31 @@ impl CollabTitlebarItem { } pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext) { - if !self.user_menu_is_visible { - self.user_menu.update(cx, |user_menu, cx| { - let items = if let Some(_) = self.user_store.read(cx).current_user() { - vec![ - ContextMenuItem::action("Settings", zed_actions::OpenSettings), - ContextMenuItem::action("Theme", theme_selector::Toggle), - ContextMenuItem::separator(), - ContextMenuItem::action( - "Share Feedback", - feedback::feedback_editor::GiveFeedback, - ), - ContextMenuItem::action("Sign out", SignOut), - ] - } else { - vec![ - ContextMenuItem::action("Settings", zed_actions::OpenSettings), - ContextMenuItem::action("Theme", theme_selector::Toggle), - ContextMenuItem::separator(), - ContextMenuItem::action( - "Share Feedback", - feedback::feedback_editor::GiveFeedback, - ), - ] - }; - - user_menu.show(Default::default(), AnchorCorner::TopRight, items, cx); - }); - } - self.user_menu_is_visible = !self.user_menu_is_visible; + self.user_menu.update(cx, |user_menu, cx| { + let items = if let Some(_) = self.user_store.read(cx).current_user() { + vec![ + ContextMenuItem::action("Settings", zed_actions::OpenSettings), + ContextMenuItem::action("Theme", theme_selector::Toggle), + ContextMenuItem::separator(), + ContextMenuItem::action( + "Share Feedback", + feedback::feedback_editor::GiveFeedback, + ), + ContextMenuItem::action("Sign out", SignOut), + ] + } else { + vec![ + ContextMenuItem::action("Settings", zed_actions::OpenSettings), + ContextMenuItem::action("Theme", theme_selector::Toggle), + ContextMenuItem::separator(), + ContextMenuItem::action( + "Share Feedback", + feedback::feedback_editor::GiveFeedback, + ), + ] + }; + user_menu.show(Default::default(), AnchorCorner::TopRight, items, cx); + }); } fn render_toggle_contacts_button( @@ -701,12 +695,12 @@ impl CollabTitlebarItem { ) .contained(), ) - .with_children(self.user_menu_is_visible.then(|| { + .with_child( ChildView::new(&self.user_menu, cx) .aligned() .bottom() - .right() - })) + .right(), + ) .into_any() }