agent_ui: Adjust thread item component and fix thread switcher (#54126)

Danilo Leal created

This PR adds some adjustments to the thread item component so metadata
labels (project, worktree, and branch names) properly show up and
truncate in different places, as well as fixes the thread switcher by
making hover auto-select the item, making it more consistent with the
regular tab switcher. We still have to figure out how to make the modal
dismiss on click, though; that's pending.

Ended up also cleaning up the thread item's tooltip a bit, and tweaking
the preview examples we have for it.

Release Notes:

- Agent: Fixed worktree and branch labels not showing up in the thread
switcher.
- Agent: Fixed the thread switcher not selecting on hover.

Change summary

crates/sidebar/src/thread_switcher.rs      |  63 +-
crates/ui/src/components/ai/thread_item.rs | 545 +++++++++++------------
2 files changed, 290 insertions(+), 318 deletions(-)

Detailed changes

crates/sidebar/src/thread_switcher.rs 🔗

@@ -146,6 +146,15 @@ impl ThreadSwitcher {
         }
     }
 
+    fn select_index(&mut self, index: usize, cx: &mut Context<Self>) {
+        if index >= self.entries.len() || index == self.selected_index {
+            return;
+        }
+        self.selected_index = index;
+        self.emit_preview(cx);
+        cx.notify();
+    }
+
     fn cancel(&mut self, _: &menu::Cancel, _window: &mut gpui::Window, cx: &mut Context<Self>) {
         cx.emit(ThreadSwitcherEvent::Dismissed);
         cx.emit(DismissEvent);
@@ -213,37 +222,39 @@ impl Render for ThreadSwitcher {
             .children(self.entries.iter().enumerate().map(|(ix, entry)| {
                 let id = SharedString::from(format!("thread-switcher-{}", entry.session_id));
 
-                div()
-                    .id(id.clone())
+                ThreadItem::new(id, entry.title.clone())
+                    .rounded(true)
+                    .icon(entry.icon)
+                    .status(entry.status)
+                    .when_some(entry.icon_from_external_svg.clone(), |this, svg| {
+                        this.custom_icon_from_external_svg(svg)
+                    })
+                    .when_some(entry.project_name.clone(), |this, name| {
+                        this.project_name(name)
+                    })
+                    .worktrees(entry.worktrees.clone())
+                    .timestamp(entry.timestamp.clone())
+                    .title_generating(entry.is_title_generating)
+                    .notified(entry.notified)
+                    .when(entry.diff_stats.lines_added > 0, |this| {
+                        this.added(entry.diff_stats.lines_added as usize)
+                    })
+                    .when(entry.diff_stats.lines_removed > 0, |this| {
+                        this.removed(entry.diff_stats.lines_removed as usize)
+                    })
+                    .selected(ix == selected_index)
+                    .base_bg(cx.theme().colors().elevated_surface_background)
+                    .on_hover(cx.listener(move |this, hovered: &bool, _window, cx| {
+                        if *hovered {
+                            this.select_index(ix, cx);
+                        }
+                    }))
+                    // TODO: This is not properly propagating to the tread item.
                     .on_click(
                         cx.listener(move |this, _event: &gpui::ClickEvent, _window, cx| {
                             this.select_and_confirm(ix, cx);
                         }),
                     )
-                    .child(
-                        ThreadItem::new(id, entry.title.clone())
-                            .rounded(true)
-                            .icon(entry.icon)
-                            .status(entry.status)
-                            .when_some(entry.icon_from_external_svg.clone(), |this, svg| {
-                                this.custom_icon_from_external_svg(svg)
-                            })
-                            .when_some(entry.project_name.clone(), |this, name| {
-                                this.project_name(name)
-                            })
-                            .worktrees(entry.worktrees.clone())
-                            .timestamp(entry.timestamp.clone())
-                            .title_generating(entry.is_title_generating)
-                            .notified(entry.notified)
-                            .when(entry.diff_stats.lines_added > 0, |this| {
-                                this.added(entry.diff_stats.lines_added as usize)
-                            })
-                            .when(entry.diff_stats.lines_removed > 0, |this| {
-                                this.removed(entry.diff_stats.lines_removed as usize)
-                            })
-                            .selected(ix == selected_index)
-                            .base_bg(cx.theme().colors().elevated_surface_background),
-                    )
                     .into_any_element()
             }))
     }

crates/ui/src/components/ai/thread_item.rs 🔗

@@ -244,11 +244,11 @@ impl RenderOnce for ThreadItem {
             .gradient_stop(0.75)
             .group_name("thread-item");
 
+        let separator_color = Color::Custom(color.text_muted.opacity(0.4));
         let dot_separator = || {
             Label::new("•")
                 .size(LabelSize::Small)
-                .color(Color::Muted)
-                .alpha(0.5)
+                .color(separator_color)
         };
 
         let icon_id = format!("icon-{}", self.id);
@@ -307,12 +307,6 @@ impl RenderOnce for ThreadItem {
             icon_container().child(agent_icon).into_any_element()
         };
 
-        let tooltip_title = self.title.clone();
-        let tooltip_status = self.status;
-        let tooltip_worktrees = self.worktrees.clone();
-        let tooltip_added = self.added;
-        let tooltip_removed = self.removed;
-
         let title = self.title;
         let highlight_positions = self.highlight_positions;
 
@@ -361,185 +355,24 @@ impl RenderOnce for ThreadItem {
         let has_timestamp = !self.timestamp.is_empty();
         let timestamp = self.timestamp;
 
-        let mut worktree_labels: Vec<AnyElement> = Vec::new();
-
-        let slash_color = Color::Custom(cx.theme().colors().text_muted.opacity(0.4));
-
-        for wt in self.worktrees {
-            match wt.kind {
-                WorktreeKind::Main => continue,
-                WorktreeKind::Linked => {
-                    let chip_index = worktree_labels.len();
-
-                    let label = if wt.highlight_positions.is_empty() {
-                        Label::new(wt.name)
-                            .size(LabelSize::Small)
-                            .color(Color::Muted)
-                            .truncate()
-                            .into_any_element()
-                    } else {
-                        HighlightedLabel::new(wt.name, wt.highlight_positions)
-                            .size(LabelSize::Small)
-                            .color(Color::Muted)
-                            .truncate()
-                            .into_any_element()
-                    };
-
-                    worktree_labels.push(
-                        h_flex()
-                            .id(format!("{}-worktree-{chip_index}", self.id.clone()))
-                            .min_w_0()
-                            .gap_0p5()
-                            .child(
-                                Icon::new(IconName::GitWorktree)
-                                    .size(IconSize::XSmall)
-                                    .color(Color::Muted),
-                            )
-                            .child(label)
-                            .when_some(wt.branch_name, |this, branch| {
-                                this.child(
-                                    Label::new("/")
-                                        .size(LabelSize::Small)
-                                        .color(slash_color)
-                                        .flex_shrink_0(),
-                                )
-                                .child(
-                                    Label::new(branch)
-                                        .size(LabelSize::Small)
-                                        .color(Color::Muted)
-                                        .truncate(),
-                                )
-                            })
-                            .into_any_element(),
-                    );
-                }
-            }
-        }
+        let show_tooltip = matches!(
+            self.status,
+            AgentThreadStatus::Error | AgentThreadStatus::WaitingForConfirmation
+        );
 
-        let has_worktree = !worktree_labels.is_empty();
+        let linked_worktrees: Vec<ThreadItemWorktreeInfo> = self
+            .worktrees
+            .into_iter()
+            .filter(|wt| wt.kind == WorktreeKind::Linked)
+            .collect();
 
-        let unified_tooltip = {
-            let title = tooltip_title;
-            let status = tooltip_status;
-            let worktrees = tooltip_worktrees;
-            let added = tooltip_added;
-            let removed = tooltip_removed;
+        let has_worktree = !linked_worktrees.is_empty();
 
-            Tooltip::element(move |_window, cx| {
-                v_flex()
-                    .min_w_0()
-                    .gap_1()
-                    .child(Label::new(title.clone()))
-                    .children(worktrees.iter().map(|wt| {
-                        let is_linked = wt.kind == WorktreeKind::Linked;
-
-                        v_flex()
-                            .gap_1()
-                            .when(is_linked, |this| {
-                                this.child(
-                                    v_flex()
-                                        .child(
-                                            h_flex()
-                                                .gap_1()
-                                                .child(
-                                                    Icon::new(IconName::GitWorktree)
-                                                        .size(IconSize::Small)
-                                                        .color(Color::Muted),
-                                                )
-                                                .child(
-                                                    Label::new(wt.name.clone())
-                                                        .size(LabelSize::Small)
-                                                        .color(Color::Muted),
-                                                ),
-                                        )
-                                        .child(
-                                            div()
-                                                .pl(IconSize::Small.rems() + rems(0.25))
-                                                .w(px(280.))
-                                                .whitespace_normal()
-                                                .text_ui_sm(cx)
-                                                .text_color(
-                                                    cx.theme().colors().text_muted.opacity(0.8),
-                                                )
-                                                .child(wt.full_path.clone()),
-                                        ),
-                                )
-                            })
-                            .when_some(wt.branch_name.clone(), |this, branch| {
-                                this.child(
-                                    h_flex()
-                                        .gap_1()
-                                        .child(
-                                            Icon::new(IconName::GitBranch)
-                                                .size(IconSize::Small)
-                                                .color(Color::Muted),
-                                        )
-                                        .child(
-                                            Label::new(branch)
-                                                .size(LabelSize::Small)
-                                                .color(Color::Muted),
-                                        ),
-                                )
-                            })
-                    }))
-                    .when(status == AgentThreadStatus::Error, |this| {
-                        this.child(
-                            h_flex()
-                                .gap_1()
-                                .pt_1()
-                                .border_t_1()
-                                .border_color(cx.theme().colors().border_variant)
-                                .child(
-                                    Icon::new(IconName::Close)
-                                        .size(IconSize::Small)
-                                        .color(Color::Error),
-                                )
-                                .child(Label::new("Error").size(LabelSize::Small)),
-                        )
-                    })
-                    .when(
-                        status == AgentThreadStatus::WaitingForConfirmation,
-                        |this| {
-                            this.child(
-                                h_flex()
-                                    .pt_1()
-                                    .border_t_1()
-                                    .border_color(cx.theme().colors().border_variant)
-                                    .gap_1()
-                                    .child(
-                                        Icon::new(IconName::Warning)
-                                            .size(IconSize::Small)
-                                            .color(Color::Warning),
-                                    )
-                                    .child(
-                                        Label::new("Waiting for Confirmation")
-                                            .size(LabelSize::Small),
-                                    ),
-                            )
-                        },
-                    )
-                    .when(added.is_some() || removed.is_some(), |this| {
-                        this.child(
-                            h_flex()
-                                .pt_1()
-                                .border_t_1()
-                                .border_color(cx.theme().colors().border_variant)
-                                .gap_1()
-                                .child(DiffStat::new(
-                                    "diff",
-                                    added.unwrap_or(0),
-                                    removed.unwrap_or(0),
-                                ))
-                                .child(
-                                    Label::new("Unreviewed Changes")
-                                        .size(LabelSize::Small)
-                                        .color(Color::Muted),
-                                ),
-                        )
-                    })
-                    .into_any_element()
-            })
-        };
+        let has_metadata = has_project_name
+            || has_project_paths
+            || has_worktree
+            || has_diff_stats
+            || has_timestamp;
 
         v_flex()
             .id(self.id.clone())
@@ -557,7 +390,6 @@ impl RenderOnce for ThreadItem {
             .when(self.rounded, |s| s.rounded_sm())
             .hover(|s| s.bg(hover_color))
             .on_hover(self.on_hover)
-            .tooltip(unified_tooltip)
             .child(
                 h_flex()
                     .min_w_0()
@@ -594,75 +426,120 @@ impl RenderOnce for ThreadItem {
                         })
                     }),
             )
-            .when(
-                has_project_name
-                    || has_project_paths
-                    || has_worktree
-                    || has_diff_stats
-                    || has_timestamp,
-                |this| {
-                    this.child(
-                        h_flex()
-                            .min_w_0()
-                            .gap_1p5()
-                            .child(icon_container()) // Icon Spacing
-                            .when(
-                                has_project_name || has_project_paths || has_worktree,
-                                |this| {
+            .when(has_metadata, |this| {
+                this.child(
+                    h_flex()
+                        .gap_1p5()
+                        .child(icon_container()) // Icon Spacing
+                        .when(
+                            has_project_name || has_project_paths || has_worktree,
+                            |this| {
+                                this.when_some(self.project_name, |this, name| {
                                     this.child(
+                                        Label::new(name).size(LabelSize::Small).color(Color::Muted),
+                                    )
+                                })
+                                .when(
+                                    has_project_name && (has_project_paths || has_worktree),
+                                    |this| this.child(dot_separator()),
+                                )
+                                .when_some(project_paths, |this, paths| {
+                                    this.child(
+                                        Label::new(paths)
+                                            .size(LabelSize::Small)
+                                            .color(Color::Muted),
+                                    )
+                                })
+                                .when(has_project_paths && has_worktree, |this| {
+                                    this.child(dot_separator())
+                                })
+                                .children(
+                                    linked_worktrees.into_iter().map(|wt| {
+                                        let worktree_label = if wt.highlight_positions.is_empty() {
+                                            Label::new(wt.name)
+                                                .size(LabelSize::Small)
+                                                .color(Color::Muted)
+                                                .truncate()
+                                                .into_any_element()
+                                        } else {
+                                            HighlightedLabel::new(wt.name, wt.highlight_positions)
+                                                .size(LabelSize::Small)
+                                                .color(Color::Muted)
+                                                .truncate()
+                                                .into_any_element()
+                                        };
+
                                         h_flex()
                                             .min_w_0()
-                                            .flex_shrink()
-                                            .overflow_hidden()
-                                            .gap_1p5()
-                                            .when_some(self.project_name, |this, name| {
+                                            .gap_0p5()
+                                            .child(
+                                                Icon::new(IconName::GitWorktree)
+                                                    .size(IconSize::XSmall)
+                                                    .color(Color::Muted),
+                                            )
+                                            .child(worktree_label)
+                                            .when_some(wt.branch_name, |this, branch| {
                                                 this.child(
-                                                    Label::new(name)
+                                                    Label::new("/")
                                                         .size(LabelSize::Small)
-                                                        .color(Color::Muted),
+                                                        .color(separator_color)
+                                                        .flex_shrink_0(),
                                                 )
-                                            })
-                                            .when(
-                                                has_project_name
-                                                    && (has_project_paths || has_worktree),
-                                                |this| this.child(dot_separator()),
-                                            )
-                                            .when_some(project_paths, |this, paths| {
-                                                this.child(
-                                                    Label::new(paths)
+                                                .child(
+                                                    Label::new(branch)
                                                         .size(LabelSize::Small)
                                                         .color(Color::Muted)
-                                                        .into_any_element(),
+                                                        .truncate(),
                                                 )
                                             })
-                                            .when(has_project_paths && has_worktree, |this| {
-                                                this.child(dot_separator())
-                                            })
-                                            .children(worktree_labels),
-                                    )
-                                },
-                            )
-                            .when(
-                                (has_project_name || has_project_paths || has_worktree)
-                                    && (has_diff_stats || has_timestamp),
-                                |this| this.child(dot_separator()),
-                            )
-                            .when(has_diff_stats, |this| {
-                                this.child(DiffStat::new(diff_stat_id, added_count, removed_count))
-                            })
-                            .when(has_diff_stats && has_timestamp, |this| {
-                                this.child(dot_separator())
-                            })
-                            .when(has_timestamp, |this| {
-                                this.child(
-                                    Label::new(timestamp.clone())
-                                        .size(LabelSize::Small)
-                                        .color(Color::Muted),
+                                    }),
                                 )
-                            }),
-                    )
-                },
-            )
+                            },
+                        )
+                        .when(
+                            (has_project_name || has_project_paths || has_worktree)
+                                && (has_diff_stats || has_timestamp),
+                            |this| this.child(dot_separator()),
+                        )
+                        .when(has_diff_stats, |this| {
+                            this.child(DiffStat::new(diff_stat_id, added_count, removed_count))
+                        })
+                        .when(has_diff_stats && has_timestamp, |this| {
+                            this.child(dot_separator())
+                        })
+                        .when(has_timestamp, |this| {
+                            this.child(
+                                Label::new(timestamp.clone())
+                                    .size(LabelSize::Small)
+                                    .color(Color::Muted),
+                            )
+                        }),
+                )
+            })
+            .when(show_tooltip, |this| {
+                let status = self.status;
+                this.tooltip(Tooltip::element(move |_, _| match status {
+                    AgentThreadStatus::Error => h_flex()
+                        .gap_1()
+                        .child(
+                            Icon::new(IconName::Close)
+                                .size(IconSize::Small)
+                                .color(Color::Error),
+                        )
+                        .child(Label::new("Thread has an Error"))
+                        .into_any_element(),
+                    AgentThreadStatus::WaitingForConfirmation => h_flex()
+                        .gap_1()
+                        .child(
+                            Icon::new(IconName::Warning)
+                                .size(IconSize::Small)
+                                .color(Color::Warning),
+                        )
+                        .child(Label::new("Waiting for Confirmation"))
+                        .into_any_element(),
+                    _ => gpui::Empty.into_any_element(),
+                }))
+            })
             .when_some(self.on_click, |this, on_click| this.on_click(on_click))
     }
 }
@@ -688,7 +565,7 @@ impl Component for ThreadItem {
 
         let thread_item_examples = vec![
             single_example(
-                "Default (minutes)",
+                "Default",
                 container()
                     .child(
                         ThreadItem::new("ti-1", "Linking to the Agent Panel Depending on Settings")
@@ -697,16 +574,6 @@ impl Component for ThreadItem {
                     )
                     .into_any_element(),
             ),
-            single_example(
-                "Notified (weeks)",
-                container()
-                    .child(
-                        ThreadItem::new("ti-2", "Refine thread view scrolling behavior")
-                            .timestamp("1w")
-                            .notified(true),
-                    )
-                    .into_any_element(),
-            ),
             single_example(
                 "Waiting for Confirmation",
                 container()
@@ -756,7 +623,7 @@ impl Component for ThreadItem {
                     .into_any_element(),
             ),
             single_example(
-                "With Changes (months)",
+                "With Changes",
                 container()
                     .child(
                         ThreadItem::new("ti-5", "Managing user and project settings interactions")
@@ -844,80 +711,174 @@ impl Component for ThreadItem {
                     .into_any_element(),
             ),
             single_example(
-                "Selected Item",
+                "Long Worktree Name (truncation)",
                 container()
                     .child(
-                        ThreadItem::new("ti-6", "Refine textarea interaction behavior")
-                            .icon(IconName::AiGemini)
-                            .timestamp("45m")
-                            .selected(true),
+                        ThreadItem::new("ti-5f", "Thread with a very long worktree name")
+                            .icon(IconName::AiClaude)
+                            .worktrees(vec![ThreadItemWorktreeInfo {
+                                name: "very-long-worktree-name-that-should-truncate".into(),
+                                full_path: "/worktrees/very-long-worktree-name/zed".into(),
+                                highlight_positions: Vec::new(),
+                                kind: WorktreeKind::Linked,
+                                branch_name: None,
+                            }])
+                            .timestamp("1h"),
                     )
                     .into_any_element(),
             ),
             single_example(
-                "Focused Item (Keyboard Selection)",
+                "Worktree with Search Highlights",
                 container()
                     .child(
-                        ThreadItem::new("ti-7", "Implement keyboard navigation")
+                        ThreadItem::new("ti-5g", "Filtered thread with highlighted worktree")
                             .icon(IconName::AiClaude)
-                            .timestamp("12h")
-                            .focused(true),
+                            .worktrees(vec![ThreadItemWorktreeInfo {
+                                name: "jade-glen".into(),
+                                full_path: "/worktrees/jade-glen/zed".into(),
+                                highlight_positions: vec![0, 1, 2, 3],
+                                kind: WorktreeKind::Linked,
+                                branch_name: Some("fix-scrolling".into()),
+                            }])
+                            .timestamp("3d"),
                     )
                     .into_any_element(),
             ),
             single_example(
-                "Selected + Focused",
+                "Multiple Worktrees (no branches)",
                 container()
                     .child(
-                        ThreadItem::new("ti-8", "Active and keyboard-focused thread")
-                            .icon(IconName::AiGemini)
-                            .timestamp("2mo")
-                            .selected(true)
-                            .focused(true),
+                        ThreadItem::new("ti-5h", "Thread spanning multiple worktrees")
+                            .icon(IconName::AiClaude)
+                            .worktrees(vec![
+                                ThreadItemWorktreeInfo {
+                                    name: "jade-glen".into(),
+                                    full_path: "/worktrees/jade-glen/zed".into(),
+                                    highlight_positions: Vec::new(),
+                                    kind: WorktreeKind::Linked,
+                                    branch_name: None,
+                                },
+                                ThreadItemWorktreeInfo {
+                                    name: "fawn-otter".into(),
+                                    full_path: "/worktrees/fawn-otter/zed-slides".into(),
+                                    highlight_positions: Vec::new(),
+                                    kind: WorktreeKind::Linked,
+                                    branch_name: None,
+                                },
+                            ])
+                            .timestamp("2h"),
                     )
                     .into_any_element(),
             ),
             single_example(
-                "Hovered with Action Slot",
+                "Multiple Worktrees with Branches",
                 container()
                     .child(
-                        ThreadItem::new("ti-9", "Hover to see action button")
-                            .icon(IconName::AiClaude)
-                            .timestamp("6h")
-                            .hovered(true)
-                            .action_slot(
-                                IconButton::new("delete", IconName::Trash)
-                                    .icon_size(IconSize::Small)
-                                    .icon_color(Color::Muted),
-                            ),
+                        ThreadItem::new("ti-5i", "Multi-root with per-worktree branches")
+                            .icon(IconName::ZedAgent)
+                            .worktrees(vec![
+                                ThreadItemWorktreeInfo {
+                                    name: "jade-glen".into(),
+                                    full_path: "/worktrees/jade-glen/zed".into(),
+                                    highlight_positions: Vec::new(),
+                                    kind: WorktreeKind::Linked,
+                                    branch_name: Some("fix".into()),
+                                },
+                                ThreadItemWorktreeInfo {
+                                    name: "fawn-otter".into(),
+                                    full_path: "/worktrees/fawn-otter/zed-slides".into(),
+                                    highlight_positions: Vec::new(),
+                                    kind: WorktreeKind::Linked,
+                                    branch_name: Some("main".into()),
+                                },
+                            ])
+                            .timestamp("15m"),
                     )
                     .into_any_element(),
             ),
             single_example(
-                "Search Highlight",
+                "Project Name + Worktree + Branch",
                 container()
                     .child(
-                        ThreadItem::new("ti-10", "Implement keyboard navigation")
+                        ThreadItem::new("ti-5j", "Thread with project context")
                             .icon(IconName::AiClaude)
-                            .timestamp("4w")
-                            .highlight_positions(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
+                            .project_name("my-remote-server")
+                            .worktrees(vec![ThreadItemWorktreeInfo {
+                                name: "jade-glen".into(),
+                                full_path: "/worktrees/jade-glen/zed".into(),
+                                highlight_positions: Vec::new(),
+                                kind: WorktreeKind::Linked,
+                                branch_name: Some("feature-branch".into()),
+                            }])
+                            .timestamp("1d"),
                     )
                     .into_any_element(),
             ),
             single_example(
-                "Worktree Search Highlight",
+                "Project Paths + Worktree (archive view)",
                 container()
                     .child(
-                        ThreadItem::new("ti-11", "Search in worktree name")
+                        ThreadItem::new("ti-5k", "Archived thread with folder paths")
                             .icon(IconName::AiClaude)
-                            .timestamp("3mo")
+                            .project_paths(Arc::from(vec![
+                                PathBuf::from("/projects/zed"),
+                                PathBuf::from("/projects/zed-slides"),
+                            ]))
                             .worktrees(vec![ThreadItemWorktreeInfo {
-                                name: "my-project-name".into(),
-                                full_path: "my-project-name".into(),
-                                highlight_positions: vec![3, 4, 5, 6, 7, 8, 9, 10, 11],
+                                name: "jade-glen".into(),
+                                full_path: "/worktrees/jade-glen/zed".into(),
+                                highlight_positions: Vec::new(),
                                 kind: WorktreeKind::Linked,
-                                branch_name: None,
-                            }]),
+                                branch_name: Some("feature".into()),
+                            }])
+                            .timestamp("2mo"),
+                    )
+                    .into_any_element(),
+            ),
+            single_example(
+                "All Metadata",
+                container()
+                    .child(
+                        ThreadItem::new("ti-5l", "Thread with every metadata field populated")
+                            .icon(IconName::ZedAgent)
+                            .project_name("remote-dev")
+                            .worktrees(vec![ThreadItemWorktreeInfo {
+                                name: "my-worktree".into(),
+                                full_path: "/worktrees/my-worktree/zed".into(),
+                                highlight_positions: Vec::new(),
+                                kind: WorktreeKind::Linked,
+                                branch_name: Some("main".into()),
+                            }])
+                            .added(15)
+                            .removed(4)
+                            .timestamp("8h"),
+                    )
+                    .into_any_element(),
+            ),
+            single_example(
+                "Focused Item (Keyboard Selection)",
+                container()
+                    .child(
+                        ThreadItem::new("ti-7", "Implement keyboard navigation")
+                            .icon(IconName::AiClaude)
+                            .timestamp("12h")
+                            .focused(true),
+                    )
+                    .into_any_element(),
+            ),
+            single_example(
+                "Action Slot",
+                container()
+                    .child(
+                        ThreadItem::new("ti-9", "Hover to see action button")
+                            .icon(IconName::AiClaude)
+                            .timestamp("6h")
+                            .hovered(true)
+                            .action_slot(
+                                IconButton::new("delete", IconName::Trash)
+                                    .icon_size(IconSize::Small)
+                                    .icon_color(Color::Muted),
+                            ),
                     )
                     .into_any_element(),
             ),