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

+1 -1
View File
@@ -358,7 +358,7 @@ fn keep_in_view(
}
/// Boxes covering display offsets `start..end`, relative to the text origin.
fn selection_rects(
pub(super) fn selection_rects(
lines: &[(usize, WrappedLine)],
start: usize,
end: usize,
+31 -1
View File
@@ -1,6 +1,8 @@
use std::ops::Range;
use gpui::{Bounds, Pixels, Point};
use super::TextInput;
use super::{TextInput, element::selection_rects};
impl TextInput {
/// The caret's box in window coordinates, once laid out.
@@ -22,6 +24,34 @@ impl TextInput {
))
}
/// A range's boxes in the window, a line each, from the last layout; later offsets clamp to its end.
pub fn bounds_for_range(&self, range: Range<usize>) -> Vec<Bounds<Pixels>> {
let Some(layout) = self.layout.as_ref() else {
return Vec::new();
};
if layout.placeholder {
return Vec::new();
}
let laid = layout
.lines
.last()
.map_or(0, |(start, line)| start + line.len());
let (start, end) = (
self.display_offset(range.start).min(laid),
self.display_offset(range.end).min(laid),
);
selection_rects(
&layout.lines,
start,
end,
layout.line_height,
layout.bounds.size.width,
)
.into_iter()
.map(|rect| Bounds::new(rect.origin + layout.bounds.origin - self.scroll, rect.size))
.collect()
}
/// Top-left of a display offset, relative to the text's origin.
pub(crate) fn position_for(&self, display: usize) -> Option<Point<Pixels>> {
let layout = self.layout.as_ref()?;