| @@ -79,10 +79,9 @@ use crate::{ · 78 unmodified lines |
| 79 | 79 | ToggleBurnMode, ToggleProfileSelector, |
| 80 | 80 | }; |
| 81 | 81 | |
| 82 | | −/// Maximum number of lines to show for a collapsed terminal command preview. |
| 83 | 82 | const MAX_COLLAPSED_LINES: usize = 3; |
| 84 | | −const STOPWATCH_THRESHOLD: Duration = Duration::from_secs(1); |
| 85 | | −const TOKEN_THRESHOLD: u64 = 1; |
| 83 | +const STOPWATCH_THRESHOLD: Duration = Duration::from_secs(30); |
| 84 | +const TOKEN_THRESHOLD: u64 = 250; |
| 86 | 85 | |
| 87 | 86 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| 88 | 87 | enum ThreadFeedback { |
| @@ -6058,46 +6057,118 @@ impl AcpThreadView { · 5969 unmodified lines |
| 6058 | 6057 | .is_some_and(|model| model.provider_id() == language_model::ZED_CLOUD_PROVIDER_ID) |
| 6059 | 6058 | } |
| 6060 | 6059 | |
| 6060 | + fn supports_split_token_display(&self, cx: &App) -> bool { |
| 6061 | + self.as_native_thread(cx) |
| 6062 | + .and_then(|thread| thread.read(cx).model()) |
| 6063 | + .is_some_and(|model| model.supports_split_token_display()) |
| 6064 | + } |
| 6065 | + |
| 6061 | 6066 | fn render_token_usage(&self, cx: &mut Context<Self>) -> Option<Div> { |
| 6062 | 6067 | let thread = self.thread()?.read(cx); |
| 6063 | 6068 | let usage = thread.token_usage()?; |
| 6064 | 6069 | let is_generating = thread.status() != ThreadStatus::Idle; |
| 6070 | + let show_split = self.supports_split_token_display(cx); |
| 6065 | 6071 | |
| 6066 | | − let used = crate::text_thread_editor::humanize_token_count(usage.used_tokens); |
| 6067 | | − let max = crate::text_thread_editor::humanize_token_count(usage.max_tokens); |
| 6072 | + let separator_color = Color::Custom(cx.theme().colors().text_muted.opacity(0.5)); |
| 6073 | + let token_label = |text: String, animation_id: &'static str| { |
| 6074 | + Label::new(text) |
| 6075 | + .size(LabelSize::Small) |
| 6076 | + .color(Color::Muted) |
| 6077 | + .map(|label| { |
| 6078 | + if is_generating { |
| 6079 | + label |
| 6080 | + .with_animation( |
| 6081 | + animation_id, |
| 6082 | + Animation::new(Duration::from_secs(2)) |
| 6083 | + .repeat() |
| 6084 | + .with_easing(pulsating_between(0.3, 0.8)), |
| 6085 | + |label, delta| label.alpha(delta), |
| 6086 | + ) |
| 6087 | + .into_any() |
| 6088 | + } else { |
| 6089 | + label.into_any_element() |
| 6090 | + } |
| 6091 | + }) |
| 6092 | + }; |
| 6068 | 6093 | |
| 6069 | | − Some( |
| 6070 | | − h_flex() |
| 6071 | | − .flex_shrink_0() |
| 6072 | | − .gap_0p5() |
| 6073 | | − .mr_1p5() |
| 6074 | | − .child( |
| 6075 | | − Label::new(used) |
| 6076 | | − .size(LabelSize::Small) |
| 6077 | | − .color(Color::Muted) |
| 6078 | | − .map(|label| { |
| 6079 | | − if is_generating { |
| 6080 | | − label |
| 6081 | | − .with_animation( |
| 6082 | | − "used-tokens-label", |
| 6083 | | − Animation::new(Duration::from_secs(2)) |
| 6084 | | − .repeat() |
| 6085 | | − .with_easing(pulsating_between(0.3, 0.8)), |
| 6086 | | − |label, delta| label.alpha(delta), |
| 6087 | | − ) |
| 6088 | | − .into_any() |
| 6089 | | − } else { |
| 6090 | | − label.into_any_element() |
| 6091 | | − } |
| 6092 | | − }), |
| 6093 | | − ) |
| 6094 | | − .child( |
| 6095 | | − Label::new("/") |
| 6096 | | − .size(LabelSize::Small) |
| 6097 | | − .color(Color::Custom(cx.theme().colors().text_muted.opacity(0.5))), |
| 6098 | | − ) |
| 6099 | | − .child(Label::new(max).size(LabelSize::Small).color(Color::Muted)), |
| 6100 | | − ) |
| 6094 | + if show_split { |
| 6095 | + let max_output_tokens = self |
| 6096 | + .as_native_thread(cx) |
| 6097 | + .and_then(|thread| thread.read(cx).model()) |
| 6098 | + .and_then(|model| model.max_output_tokens()) |
| 6099 | + .unwrap_or(0); |
| 6100 | + |
| 6101 | + let input = crate::text_thread_editor::humanize_token_count(usage.input_tokens); |
| 6102 | + let input_max = crate::text_thread_editor::humanize_token_count( |
| 6103 | + usage.max_tokens.saturating_sub(max_output_tokens), |
| 6104 | + ); |
| 6105 | + let output = crate::text_thread_editor::humanize_token_count(usage.output_tokens); |
| 6106 | + let output_max = crate::text_thread_editor::humanize_token_count(max_output_tokens); |
| 6107 | + |
| 6108 | + Some( |
| 6109 | + h_flex() |
| 6110 | + .flex_shrink_0() |
| 6111 | + .gap_1() |
| 6112 | + .mr_1p5() |
| 6113 | + .child( |
| 6114 | + h_flex() |
| 6115 | + .gap_0p5() |
| 6116 | + .child( |
| 6117 | + Icon::new(IconName::ArrowUp) |
| 6118 | + .size(IconSize::XSmall) |
| 6119 | + .color(Color::Muted), |
| 6120 | + ) |
| 6121 | + .child(token_label(input, "input-tokens-label")) |
| 6122 | + .child( |
| 6123 | + Label::new("/") |
| 6124 | + .size(LabelSize::Small) |
| 6125 | + .color(separator_color), |
| 6126 | + ) |
| 6127 | + .child( |
| 6128 | + Label::new(input_max) |
| 6129 | + .size(LabelSize::Small) |
| 6130 | + .color(Color::Muted), |
| 6131 | + ), |
| 6132 | + ) |
| 6133 | + .child( |
| 6134 | + h_flex() |
| 6135 | + .gap_0p5() |
| 6136 | + .child( |
| 6137 | + Icon::new(IconName::ArrowDown) |
| 6138 | + .size(IconSize::XSmall) |
| 6139 | + .color(Color::Muted), |
| 6140 | + ) |
| 6141 | + .child(token_label(output, "output-tokens-label")) |
| 6142 | + .child( |
| 6143 | + Label::new("/") |
| 6144 | + .size(LabelSize::Small) |
| 6145 | + .color(separator_color), |
| 6146 | + ) |
| 6147 | + .child( |
| 6148 | + Label::new(output_max) |
| 6149 | + .size(LabelSize::Small) |
| 6150 | + .color(Color::Muted), |
| 6151 | + ), |
| 6152 | + ), |
| 6153 | + ) |
| 6154 | + } else { |
| 6155 | + let used = crate::text_thread_editor::humanize_token_count(usage.used_tokens); |
| 6156 | + let max = crate::text_thread_editor::humanize_token_count(usage.max_tokens); |
| 6157 | + |
| 6158 | + Some( |
| 6159 | + h_flex() |
| 6160 | + .flex_shrink_0() |
| 6161 | + .gap_0p5() |
| 6162 | + .mr_1p5() |
| 6163 | + .child(token_label(used, "used-tokens-label")) |
| 6164 | + .child( |
| 6165 | + Label::new("/") |
| 6166 | + .size(LabelSize::Small) |
| 6167 | + .color(separator_color), |
| 6168 | + ) |
| 6169 | + .child(Label::new(max).size(LabelSize::Small).color(Color::Muted)), |
| 6170 | + ) |
| 6171 | + } |
| 6101 | 6172 | } |
| 6102 | 6173 | |
| 6103 | 6174 | fn toggle_burn_mode( |