T22: Collaboration

- presence: avatars ringed in each peer's color with where they are, press to
  follow; a breathing live dot; follow mode frames the view and stops on Escape
- remote cursors and selections painted over a field from its own layout
- comment threads with replies, reactions and resolve; a marker that opens a
  thread in a bubble; a sidebar of open and resolved threads
- annotations washed in their authors' colors, notes on hover
- suggestion mode: edits become suggestions word by word; track changes shows
  them struck and underlined, to accept or reject
- sharing: invite by email with checked chips, roles, the access list and who
  the link lets in, in one dialog
- primitives::hold_focus gives a mode its own focus, shared with ZenMode
- AvatarGroup's tuck and count shared; Avatar::ring_color; TagInput::check;
  TextInput::bounds_for_range; Popover::size
- icons: smile-plus, user-plus, message-square-diff
- gallery: the Collaboration page with scripted states
This commit is contained in:
ZacharyZhang-NY committed 2026-09-26 05:10:37 -04:00
1 parent 0623855f38
commit 153cf95b99
34 files changed
+3297 -86

No files matched your search

+40
View File
@@ -170,6 +170,46 @@ pub(crate) fn give_back(state: &Entity<Takeover>, window: &mut Window, cx: &mut
}
}
/// A mode's own focus, where focus was before the mode took it, and whether the mode was on last frame.
pub(crate) struct Held {
pub focus: FocusHandle,
previous: Option<FocusHandle>,
was_on: bool,
}
/// The focus a mode keyed `key` holds: turned on while focus is elsewhere, it takes focus so Escape reaches it.
pub(crate) fn hold_focus(
key: impl Into<ElementId>,
on: bool,
window: &mut Window,
cx: &mut App,
) -> Entity<Held> {
let held = window.use_keyed_state(key, cx, |_, cx| Held {
focus: cx.focus_handle(),
previous: None,
was_on: false,
});
let (focus, was_on) = (held.read(cx).focus.clone(), held.read(cx).was_on);
if on && !was_on && !focus.contains_focused(window, cx) {
log::info!("focus: a mode takes it, so Escape reaches it");
let previous = window.focused(cx);
window.focus(&focus);
held.update(cx, |held, _| held.previous = previous);
}
if on != was_on {
held.update(cx, |held, _| held.was_on = on);
}
held
}
/// Hands focus back to where it was before the mode took it.
pub(crate) fn hand_back(held: &Entity<Held>, window: &mut Window, cx: &mut App) {
if let Some(previous) = held.update(cx, |held, _| held.previous.take()) {
log::info!("focus: handed back after a mode");
window.focus(&previous);
}
}
/// A focus handle kept for `id`, a Tab stop while enabled.
pub(crate) fn tab_stop(
id: ElementId,
+3
View File
@@ -187,6 +187,7 @@ icons! {
Menu => "menu",
Merge => "merge",
MessageSquare => "message-square",
MessageSquareDiff => "message-square-diff",
MicOff => "mic-off",
Mic => "mic",
Minimize2 => "minimize-2",
@@ -242,6 +243,7 @@ icons! {
SlidersHorizontal => "sliders-horizontal",
Smartphone => "smartphone",
Smile => "smile",
SmilePlus => "smile-plus",
Sparkles => "sparkles",
Spline => "spline",
SquareCheck => "square-check",
@@ -267,6 +269,7 @@ icons! {
Undo2 => "undo-2",
Upload => "upload",
User => "user",
UserPlus => "user-plus",
Users => "users",
Variable => "variable",
VideoOff => "video-off",
+1 -1
View File
@@ -14,7 +14,7 @@ pub use backdrop::{Backdrop, Place};
pub use disclosure::Disclosure;
pub use divider::Divider;
pub use focus::{FocusNext, FocusPrev, FocusRing, FocusScope};
pub(crate) use focus::{Takeover, give_back, tab_stop, take_focus};
pub(crate) use focus::{Takeover, give_back, hand_back, hold_focus, tab_stop, take_focus};
pub use ghost::DragGhost;
pub use icon::{Icon, IconName};
pub use image::Image;