thread_view: Adjust empty state and error displays (#36774)

Danilo Leal and Conrad Irwin created

Also changes the message editor placeholder depending on the agent.

Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/agent2/src/native_agent_server.rs |   4 
crates/agent_ui/src/acp/thread_view.rs   | 456 ++++++++++++-------------
crates/agent_ui/src/agent_panel.rs       |   2 
crates/ui/src/components/callout.rs      |   1 
4 files changed, 225 insertions(+), 238 deletions(-)

Detailed changes

crates/agent2/src/native_agent_server.rs 🔗

@@ -23,11 +23,11 @@ impl NativeAgentServer {
 
 impl AgentServer for NativeAgentServer {
     fn name(&self) -> &'static str {
-        "Native Agent"
+        "Zed Agent"
     }
 
     fn empty_state_headline(&self) -> &'static str {
-        "Welcome to the Agent Panel"
+        self.name()
     }
 
     fn empty_state_message(&self) -> &'static str {

crates/agent_ui/src/acp/thread_view.rs 🔗

@@ -258,6 +258,7 @@ pub struct AcpThreadView {
     hovered_recent_history_item: Option<usize>,
     entry_view_state: Entity<EntryViewState>,
     message_editor: Entity<MessageEditor>,
+    focus_handle: FocusHandle,
     model_selector: Option<Entity<AcpModelSelectorPopover>>,
     profile_selector: Option<Entity<ProfileSelector>>,
     notifications: Vec<WindowHandle<AgentNotification>>,
@@ -312,6 +313,13 @@ impl AcpThreadView {
     ) -> Self {
         let prompt_capabilities = Rc::new(Cell::new(acp::PromptCapabilities::default()));
         let prevent_slash_commands = agent.clone().downcast::<ClaudeCode>().is_some();
+
+        let placeholder = if agent.name() == "Zed Agent" {
+            format!("Message the {} — @ to include context", agent.name())
+        } else {
+            format!("Message {} — @ to include context", agent.name())
+        };
+
         let message_editor = cx.new(|cx| {
             let mut editor = MessageEditor::new(
                 workspace.clone(),
@@ -319,7 +327,7 @@ impl AcpThreadView {
                 history_store.clone(),
                 prompt_store.clone(),
                 prompt_capabilities.clone(),
-                "Message the agent — @ to include context",
+                placeholder,
                 prevent_slash_commands,
                 editor::EditorMode::AutoHeight {
                     min_lines: MIN_EDITOR_LINES,
@@ -381,6 +389,7 @@ impl AcpThreadView {
             prompt_capabilities,
             _subscriptions: subscriptions,
             _cancel_task: None,
+            focus_handle: cx.focus_handle(),
         }
     }
 
@@ -404,8 +413,12 @@ impl AcpThreadView {
             let connection = match connect_task.await {
                 Ok(connection) => connection,
                 Err(err) => {
-                    this.update(cx, |this, cx| {
-                        this.handle_load_error(err, cx);
+                    this.update_in(cx, |this, window, cx| {
+                        if err.downcast_ref::<LoadError>().is_some() {
+                            this.handle_load_error(err, window, cx);
+                        } else {
+                            this.handle_thread_error(err, cx);
+                        }
                         cx.notify();
                     })
                     .log_err();
@@ -522,6 +535,7 @@ impl AcpThreadView {
                             title_editor,
                             _subscriptions: subscriptions,
                         };
+                        this.message_editor.focus_handle(cx).focus(window);
 
                         this.profile_selector = this.as_native_thread(cx).map(|thread| {
                             cx.new(|cx| {
@@ -537,7 +551,7 @@ impl AcpThreadView {
                         cx.notify();
                     }
                     Err(err) => {
-                        this.handle_load_error(err, cx);
+                        this.handle_load_error(err, window, cx);
                     }
                 };
             })
@@ -606,17 +620,28 @@ impl AcpThreadView {
                     .map(|desc| cx.new(|cx| Markdown::new(desc.into(), None, None, cx))),
                 _subscription: subscription,
             };
+            if this.message_editor.focus_handle(cx).is_focused(window) {
+                this.focus_handle.focus(window)
+            }
             cx.notify();
         })
         .ok();
     }
 
-    fn handle_load_error(&mut self, err: anyhow::Error, cx: &mut Context<Self>) {
+    fn handle_load_error(
+        &mut self,
+        err: anyhow::Error,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if let Some(load_err) = err.downcast_ref::<LoadError>() {
             self.thread_state = ThreadState::LoadError(load_err.clone());
         } else {
             self.thread_state = ThreadState::LoadError(LoadError::Other(err.to_string().into()))
         }
+        if self.message_editor.focus_handle(cx).is_focused(window) {
+            self.focus_handle.focus(window)
+        }
         cx.notify();
     }
 
@@ -633,12 +658,11 @@ impl AcpThreadView {
         }
     }
 
-    pub fn title(&self, cx: &App) -> SharedString {
+    pub fn title(&self) -> SharedString {
         match &self.thread_state {
-            ThreadState::Ready { thread, .. } => thread.read(cx).title(),
+            ThreadState::Ready { .. } | ThreadState::Unauthenticated { .. } => "New Thread".into(),
             ThreadState::Loading { .. } => "Loading…".into(),
             ThreadState::LoadError(_) => "Failed to load".into(),
-            ThreadState::Unauthenticated { .. } => "Authentication Required".into(),
         }
     }
 
@@ -1069,6 +1093,9 @@ impl AcpThreadView {
             AcpThreadEvent::LoadError(error) => {
                 self.thread_retry_status.take();
                 self.thread_state = ThreadState::LoadError(error.clone());
+                if self.message_editor.focus_handle(cx).is_focused(window) {
+                    self.focus_handle.focus(window)
+                }
             }
             AcpThreadEvent::TitleUpdated => {
                 let title = thread.read(cx).title();
@@ -2338,33 +2365,6 @@ impl AcpThreadView {
             .into_any()
     }
 
-    fn render_agent_logo(&self) -> AnyElement {
-        Icon::new(self.agent.logo())
-            .color(Color::Muted)
-            .size(IconSize::XLarge)
-            .into_any_element()
-    }
-
-    fn render_error_agent_logo(&self) -> AnyElement {
-        let logo = Icon::new(self.agent.logo())
-            .color(Color::Muted)
-            .size(IconSize::XLarge)
-            .into_any_element();
-
-        h_flex()
-            .relative()
-            .justify_center()
-            .child(div().opacity(0.3).child(logo))
-            .child(
-                h_flex()
-                    .absolute()
-                    .right_1()
-                    .bottom_0()
-                    .child(Icon::new(IconName::XCircleFilled).color(Color::Error)),
-            )
-            .into_any_element()
-    }
-
     fn render_rules_item(&self, cx: &Context<Self>) -> Option<AnyElement> {
         let project_context = self
             .as_native_thread(cx)?
@@ -2493,8 +2493,7 @@ impl AcpThreadView {
         )
     }
 
-    fn render_empty_state(&self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
-        let loading = matches!(&self.thread_state, ThreadState::Loading { .. });
+    fn render_recent_history(&self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
         let render_history = self
             .agent
             .clone()
@@ -2506,38 +2505,6 @@ impl AcpThreadView {
 
         v_flex()
             .size_full()
-            .when(!render_history, |this| {
-                this.child(
-                    v_flex()
-                        .size_full()
-                        .items_center()
-                        .justify_center()
-                        .child(if loading {
-                            h_flex()
-                                .justify_center()
-                                .child(self.render_agent_logo())
-                                .with_animation(
-                                    "pulsating_icon",
-                                    Animation::new(Duration::from_secs(2))
-                                        .repeat()
-                                        .with_easing(pulsating_between(0.4, 1.0)),
-                                    |icon, delta| icon.opacity(delta),
-                                )
-                                .into_any()
-                        } else {
-                            self.render_agent_logo().into_any_element()
-                        })
-                        .child(h_flex().mt_4().mb_2().justify_center().child(if loading {
-                            div()
-                                .child(LoadingLabel::new("").size(LabelSize::Large))
-                                .into_any_element()
-                        } else {
-                            Headline::new(self.agent.empty_state_headline())
-                                .size(HeadlineSize::Medium)
-                                .into_any_element()
-                        })),
-                )
-            })
             .when(render_history, |this| {
                 let recent_history: Vec<_> = self.history_store.update(cx, |history_store, _| {
                     history_store.entries().take(3).collect()
@@ -2612,142 +2579,118 @@ impl AcpThreadView {
         window: &mut Window,
         cx: &Context<Self>,
     ) -> Div {
-        v_flex()
-            .p_2()
-            .gap_2()
-            .flex_1()
-            .items_center()
-            .justify_center()
-            .child(
-                v_flex()
-                    .items_center()
-                    .justify_center()
-                    .child(self.render_error_agent_logo())
-                    .child(
-                        h_flex().mt_4().mb_1().justify_center().child(
-                            Headline::new("Authentication Required").size(HeadlineSize::Medium),
+        v_flex().flex_1().size_full().justify_end().child(
+            v_flex()
+                .p_2()
+                .pr_3()
+                .w_full()
+                .border_t_1()
+                .border_color(cx.theme().colors().border)
+                .bg(cx.theme().status().warning.opacity(0.04))
+                .child(
+                    h_flex()
+                        .gap_1p5()
+                        .child(
+                            Icon::new(IconName::Warning)
+                                .color(Color::Warning)
+                                .size(IconSize::Small),
+                        )
+                        .child(Label::new("Authentication Required")),
+                )
+                .children(description.map(|desc| {
+                    div().text_ui(cx).child(self.render_markdown(
+                        desc.clone(),
+                        default_markdown_style(false, false, window, cx),
+                    ))
+                }))
+                .children(
+                    configuration_view
+                        .cloned()
+                        .map(|view| div().w_full().child(view)),
+                )
+                .when(
+                    configuration_view.is_none()
+                        && description.is_none()
+                        && pending_auth_method.is_none(),
+                    |el| {
+                        el.child(
+                            Label::new(format!(
+                                "You are not currently authenticated with {}. Please choose one of the following options:",
+                                self.agent.name()
+                            ))
+                            .color(Color::Muted)
+                            .mb_1()
+                            .ml_5(),
+                        )
+                    },
+                )
+                .when(!connection.auth_methods().is_empty(), |this| {
+                    this.child(
+                        h_flex().justify_end().flex_wrap().gap_1().children(
+                            connection.auth_methods().iter().enumerate().rev().map(
+                                |(ix, method)| {
+                                    Button::new(
+                                        SharedString::from(method.id.0.clone()),
+                                        method.name.clone(),
+                                    )
+                                    .when(ix == 0, |el| {
+                                        el.style(ButtonStyle::Tinted(ui::TintColor::Warning))
+                                    })
+                                    .on_click({
+                                        let method_id = method.id.clone();
+                                        cx.listener(move |this, _, window, cx| {
+                                            this.authenticate(method_id.clone(), window, cx)
+                                        })
+                                    })
+                                },
+                            ),
                         ),
                     )
-                    .into_any(),
-            )
-            .children(description.map(|desc| {
-                div().text_ui(cx).text_center().child(self.render_markdown(
-                    desc.clone(),
-                    default_markdown_style(false, false, window, cx),
-                ))
-            }))
-            .children(
-                configuration_view
-                    .cloned()
-                    .map(|view| div().px_4().w_full().max_w_128().child(view)),
-            )
-            .when(
-                configuration_view.is_none()
-                    && description.is_none()
-                    && pending_auth_method.is_none(),
-                |el| {
+                })
+                .when_some(pending_auth_method, |el, _| {
                     el.child(
-                        div()
-                            .text_ui(cx)
-                            .text_center()
-                            .px_4()
+                        h_flex()
+                            .py_4()
                             .w_full()
-                            .max_w_128()
-                            .child(Label::new("Authentication required")),
-                    )
-                },
-            )
-            .when_some(pending_auth_method, |el, _| {
-                let spinner_icon = div()
-                    .px_0p5()
-                    .id("generating")
-                    .tooltip(Tooltip::text("Generating Changes…"))
-                    .child(
-                        Icon::new(IconName::ArrowCircle)
-                            .size(IconSize::Small)
-                            .with_animation(
-                                "arrow-circle",
-                                Animation::new(Duration::from_secs(2)).repeat(),
-                                |icon, delta| {
-                                    icon.transform(Transformation::rotate(percentage(delta)))
-                                },
+                            .justify_center()
+                            .gap_1()
+                            .child(
+                                Icon::new(IconName::ArrowCircle)
+                                    .size(IconSize::Small)
+                                    .color(Color::Muted)
+                                    .with_animation(
+                                        "arrow-circle",
+                                        Animation::new(Duration::from_secs(2)).repeat(),
+                                        |icon, delta| {
+                                            icon.transform(Transformation::rotate(percentage(
+                                                delta,
+                                            )))
+                                        },
+                                    )
+                                    .into_any_element(),
                             )
-                            .into_any_element(),
+                            .child(Label::new("Authenticating…")),
                     )
-                    .into_any();
-                el.child(
-                    h_flex()
-                        .text_ui(cx)
-                        .text_center()
-                        .justify_center()
-                        .gap_2()
-                        .px_4()
-                        .w_full()
-                        .max_w_128()
-                        .child(Label::new("Authenticating..."))
-                        .child(spinner_icon),
-                )
-            })
-            .child(
-                h_flex()
-                    .mt_1p5()
-                    .gap_1()
-                    .flex_wrap()
-                    .justify_center()
-                    .children(connection.auth_methods().iter().enumerate().rev().map(
-                        |(ix, method)| {
-                            Button::new(
-                                SharedString::from(method.id.0.clone()),
-                                method.name.clone(),
-                            )
-                            .style(ButtonStyle::Outlined)
-                            .when(ix == 0, |el| {
-                                el.style(ButtonStyle::Tinted(ui::TintColor::Accent))
-                            })
-                            .size(ButtonSize::Medium)
-                            .label_size(LabelSize::Small)
-                            .on_click({
-                                let method_id = method.id.clone();
-                                cx.listener(move |this, _, window, cx| {
-                                    this.authenticate(method_id.clone(), window, cx)
-                                })
-                            })
-                        },
-                    )),
-            )
+                }),
+        )
     }
 
     fn render_load_error(&self, e: &LoadError, cx: &Context<Self>) -> AnyElement {
-        let mut container = v_flex()
-            .items_center()
-            .justify_center()
-            .child(self.render_error_agent_logo())
-            .child(
-                v_flex()
-                    .mt_4()
-                    .mb_2()
-                    .gap_0p5()
-                    .text_center()
-                    .items_center()
-                    .child(Headline::new("Failed to launch").size(HeadlineSize::Medium))
-                    .child(
-                        Label::new(e.to_string())
-                            .size(LabelSize::Small)
-                            .color(Color::Muted),
-                    ),
-            );
-
-        if let LoadError::Unsupported {
-            upgrade_message,
-            upgrade_command,
-            ..
-        } = &e
-        {
-            let upgrade_message = upgrade_message.clone();
-            let upgrade_command = upgrade_command.clone();
-            container = container.child(
-                Button::new("upgrade", upgrade_message)
-                    .tooltip(Tooltip::text(upgrade_command.clone()))
+        let (message, action_slot) = match e {
+            LoadError::NotInstalled {
+                error_message,
+                install_message,
+                install_command,
+            } => {
+                let install_command = install_command.clone();
+                let button = Button::new("install", install_message)
+                    .tooltip(Tooltip::text(install_command.clone()))
+                    .style(ButtonStyle::Outlined)
+                    .label_size(LabelSize::Small)
+                    .icon(IconName::Download)
+                    .icon_size(IconSize::Small)
+                    .icon_color(Color::Muted)
+                    .icon_position(IconPosition::Start)
                     .on_click(cx.listener(move |this, _, window, cx| {
                         let task = this
                             .workspace
@@ -2756,12 +2699,12 @@ impl AcpThreadView {
                                 let cwd = project.first_project_directory(cx);
                                 let shell = project.terminal_settings(&cwd, cx).shell.clone();
                                 let spawn_in_terminal = task::SpawnInTerminal {
-                                    id: task::TaskId("upgrade".to_string()),
-                                    full_label: upgrade_command.clone(),
-                                    label: upgrade_command.clone(),
-                                    command: Some(upgrade_command.clone()),
+                                    id: task::TaskId("install".to_string()),
+                                    full_label: install_command.clone(),
+                                    label: install_command.clone(),
+                                    command: Some(install_command.clone()),
                                     args: Vec::new(),
-                                    command_label: upgrade_command.clone(),
+                                    command_label: install_command.clone(),
                                     cwd,
                                     env: Default::default(),
                                     use_new_terminal: true,
@@ -2787,21 +2730,24 @@ impl AcpThreadView {
                             }
                         })
                         .detach()
-                    })),
-            );
-        } else if let LoadError::NotInstalled {
-            install_message,
-            install_command,
-            ..
-        } = e
-        {
-            let install_message = install_message.clone();
-            let install_command = install_command.clone();
-            container = container.child(
-                Button::new("install", install_message)
-                    .style(ButtonStyle::Tinted(ui::TintColor::Accent))
-                    .size(ButtonSize::Medium)
-                    .tooltip(Tooltip::text(install_command.clone()))
+                    }));
+
+                (error_message.clone(), Some(button.into_any_element()))
+            }
+            LoadError::Unsupported {
+                error_message,
+                upgrade_message,
+                upgrade_command,
+            } => {
+                let upgrade_command = upgrade_command.clone();
+                let button = Button::new("upgrade", upgrade_message)
+                    .tooltip(Tooltip::text(upgrade_command.clone()))
+                    .style(ButtonStyle::Outlined)
+                    .label_size(LabelSize::Small)
+                    .icon(IconName::Download)
+                    .icon_size(IconSize::Small)
+                    .icon_color(Color::Muted)
+                    .icon_position(IconPosition::Start)
                     .on_click(cx.listener(move |this, _, window, cx| {
                         let task = this
                             .workspace
@@ -2810,12 +2756,12 @@ impl AcpThreadView {
                                 let cwd = project.first_project_directory(cx);
                                 let shell = project.terminal_settings(&cwd, cx).shell.clone();
                                 let spawn_in_terminal = task::SpawnInTerminal {
-                                    id: task::TaskId("install".to_string()),
-                                    full_label: install_command.clone(),
-                                    label: install_command.clone(),
-                                    command: Some(install_command.clone()),
+                                    id: task::TaskId("upgrade".to_string()),
+                                    full_label: upgrade_command.clone(),
+                                    label: upgrade_command.clone(),
+                                    command: Some(upgrade_command.clone()),
                                     args: Vec::new(),
-                                    command_label: install_command.clone(),
+                                    command_label: upgrade_command.clone(),
                                     cwd,
                                     env: Default::default(),
                                     use_new_terminal: true,
@@ -2841,11 +2787,24 @@ impl AcpThreadView {
                             }
                         })
                         .detach()
-                    })),
-            );
-        }
+                    }));
+
+                (error_message.clone(), Some(button.into_any_element()))
+            }
+            LoadError::Exited { .. } => ("Server exited with status {status}".into(), None),
+            LoadError::Other(msg) => (
+                msg.into(),
+                Some(self.create_copy_button(msg.to_string()).into_any_element()),
+            ),
+        };
 
-        container.into_any()
+        Callout::new()
+            .severity(Severity::Error)
+            .icon(IconName::XCircleFilled)
+            .title("Failed to Launch")
+            .description(message)
+            .actions_slot(div().children(action_slot))
+            .into_any_element()
     }
 
     fn render_activity_bar(
@@ -3336,6 +3295,19 @@ impl AcpThreadView {
             (IconName::Maximize, "Expand Message Editor")
         };
 
+        let backdrop = div()
+            .size_full()
+            .absolute()
+            .inset_0()
+            .bg(cx.theme().colors().panel_background)
+            .opacity(0.8)
+            .block_mouse_except_scroll();
+
+        let enable_editor = match self.thread_state {
+            ThreadState::Loading { .. } | ThreadState::Ready { .. } => true,
+            ThreadState::Unauthenticated { .. } | ThreadState::LoadError(..) => false,
+        };
+
         v_flex()
             .on_action(cx.listener(Self::expand_message_editor))
             .on_action(cx.listener(|this, _: &ToggleProfileSelector, window, cx| {
@@ -3411,6 +3383,7 @@ impl AcpThreadView {
                             .child(self.render_send_button(cx)),
                     ),
             )
+            .when(!enable_editor, |this| this.child(backdrop))
             .into_any()
     }
 
@@ -3913,18 +3886,19 @@ impl AcpThreadView {
             return;
         }
 
-        let title = self.title(cx);
+        // TODO: Change this once we have title summarization for external agents.
+        let title = self.agent.name();
 
         match AgentSettings::get_global(cx).notify_when_agent_waiting {
             NotifyWhenAgentWaiting::PrimaryScreen => {
                 if let Some(primary) = cx.primary_display() {
-                    self.pop_up(icon, caption.into(), title, window, primary, cx);
+                    self.pop_up(icon, caption.into(), title.into(), window, primary, cx);
                 }
             }
             NotifyWhenAgentWaiting::AllScreens => {
                 let caption = caption.into();
                 for screen in cx.displays() {
-                    self.pop_up(icon, caption.clone(), title.clone(), window, screen, cx);
+                    self.pop_up(icon, caption.clone(), title.into(), window, screen, cx);
                 }
             }
             NotifyWhenAgentWaiting::Never => {
@@ -4423,6 +4397,7 @@ impl AcpThreadView {
         Callout::new()
             .severity(Severity::Error)
             .title("Error")
+            .icon(IconName::XCircle)
             .description(error.clone())
             .actions_slot(self.create_copy_button(error.to_string()))
             .dismiss_action(self.dismiss_error_button(cx))
@@ -4434,6 +4409,7 @@ impl AcpThreadView {
 
         Callout::new()
             .severity(Severity::Error)
+            .icon(IconName::XCircle)
             .title("Free Usage Exceeded")
             .description(ERROR_MESSAGE)
             .actions_slot(
@@ -4453,6 +4429,7 @@ impl AcpThreadView {
         Callout::new()
             .severity(Severity::Error)
             .title("Authentication Required")
+            .icon(IconName::XCircle)
             .description(error.clone())
             .actions_slot(
                 h_flex()
@@ -4478,6 +4455,7 @@ impl AcpThreadView {
         Callout::new()
             .severity(Severity::Error)
             .title("Model Prompt Limit Reached")
+            .icon(IconName::XCircle)
             .description(error_message)
             .actions_slot(
                 h_flex()
@@ -4648,7 +4626,14 @@ impl AcpThreadView {
 
 impl Focusable for AcpThreadView {
     fn focus_handle(&self, cx: &App) -> FocusHandle {
-        self.message_editor.focus_handle(cx)
+        match self.thread_state {
+            ThreadState::Loading { .. } | ThreadState::Ready { .. } => {
+                self.message_editor.focus_handle(cx)
+            }
+            ThreadState::LoadError(_) | ThreadState::Unauthenticated { .. } => {
+                self.focus_handle.clone()
+            }
+        }
     }
 }
 
@@ -4664,6 +4649,7 @@ impl Render for AcpThreadView {
             .on_action(cx.listener(Self::toggle_burn_mode))
             .on_action(cx.listener(Self::keep_all))
             .on_action(cx.listener(Self::reject_all))
+            .track_focus(&self.focus_handle)
             .bg(cx.theme().colors().panel_background)
             .child(match &self.thread_state {
                 ThreadState::Unauthenticated {
@@ -4680,14 +4666,14 @@ impl Render for AcpThreadView {
                     window,
                     cx,
                 ),
-                ThreadState::Loading { .. } => {
-                    v_flex().flex_1().child(self.render_empty_state(window, cx))
-                }
+                ThreadState::Loading { .. } => v_flex()
+                    .flex_1()
+                    .child(self.render_recent_history(window, cx)),
                 ThreadState::LoadError(e) => v_flex()
-                    .p_2()
                     .flex_1()
+                    .size_full()
                     .items_center()
-                    .justify_center()
+                    .justify_end()
                     .child(self.render_load_error(e, cx)),
                 ThreadState::Ready { thread, .. } => {
                     let thread_clone = thread.clone();
@@ -4724,7 +4710,7 @@ impl Render for AcpThreadView {
                                 },
                             )
                         } else {
-                            this.child(self.render_empty_state(window, cx))
+                            this.child(self.render_recent_history(window, cx))
                         }
                     })
                 }

crates/agent_ui/src/agent_panel.rs 🔗

@@ -2097,7 +2097,7 @@ impl AgentPanel {
                         .child(title_editor)
                         .into_any_element()
                 } else {
-                    Label::new(thread_view.read(cx).title(cx))
+                    Label::new(thread_view.read(cx).title())
                         .color(Color::Muted)
                         .truncate()
                         .into_any_element()