
The UI was mistakenly using the cumulative token usage for the token counter. It will now display the last request token count, plus an estimation of the tokens in the message editor and context entries that haven't been sent yet. https://github.com/user-attachments/assets/0438c501-b850-4397-9135-57214ca3c07a Additionally, when the user edits a message, we'll display the actual token count up to it and estimate the tokens in the new message. Note: We don't currently estimate the delta when switching profiles. In the future, we want to use the count tokens API to measure every part of the request and display a breakdown. Release Notes: - agent: Made the token count more accurate and added back estimation of used tokens as you type and add context. --------- Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de> Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
229 lines
7.2 KiB
Rust
229 lines
7.2 KiB
Rust
use std::cmp::Ordering;
|
|
|
|
use gpui::{AnyElement, IntoElement, Stateful};
|
|
use smallvec::SmallVec;
|
|
|
|
use crate::prelude::*;
|
|
|
|
/// The position of a [`Tab`] within a list of tabs.
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
pub enum TabPosition {
|
|
/// The tab is first in the list.
|
|
First,
|
|
|
|
/// The tab is in the middle of the list (i.e., it is not the first or last tab).
|
|
///
|
|
/// The [`Ordering`] is where this tab is positioned with respect to the selected tab.
|
|
Middle(Ordering),
|
|
|
|
/// The tab is last in the list.
|
|
Last,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
pub enum TabCloseSide {
|
|
Start,
|
|
End,
|
|
}
|
|
|
|
#[derive(IntoElement, RegisterComponent)]
|
|
pub struct Tab {
|
|
div: Stateful<Div>,
|
|
selected: bool,
|
|
position: TabPosition,
|
|
close_side: TabCloseSide,
|
|
start_slot: Option<AnyElement>,
|
|
end_slot: Option<AnyElement>,
|
|
children: SmallVec<[AnyElement; 2]>,
|
|
}
|
|
|
|
impl Tab {
|
|
pub fn new(id: impl Into<ElementId>) -> Self {
|
|
let id = id.into();
|
|
Self {
|
|
div: div()
|
|
.id(id.clone())
|
|
.debug_selector(|| format!("TAB-{}", id)),
|
|
selected: false,
|
|
position: TabPosition::First,
|
|
close_side: TabCloseSide::End,
|
|
start_slot: None,
|
|
end_slot: None,
|
|
children: SmallVec::new(),
|
|
}
|
|
}
|
|
|
|
pub fn position(mut self, position: TabPosition) -> Self {
|
|
self.position = position;
|
|
self
|
|
}
|
|
|
|
pub fn close_side(mut self, close_side: TabCloseSide) -> Self {
|
|
self.close_side = close_side;
|
|
self
|
|
}
|
|
|
|
pub fn start_slot<E: IntoElement>(mut self, element: impl Into<Option<E>>) -> Self {
|
|
self.start_slot = element.into().map(IntoElement::into_any_element);
|
|
self
|
|
}
|
|
|
|
pub fn end_slot<E: IntoElement>(mut self, element: impl Into<Option<E>>) -> Self {
|
|
self.end_slot = element.into().map(IntoElement::into_any_element);
|
|
self
|
|
}
|
|
|
|
pub fn content_height(cx: &App) -> Pixels {
|
|
DynamicSpacing::Base32.px(cx) - px(1.)
|
|
}
|
|
|
|
pub fn container_height(cx: &App) -> Pixels {
|
|
DynamicSpacing::Base32.px(cx)
|
|
}
|
|
}
|
|
|
|
impl InteractiveElement for Tab {
|
|
fn interactivity(&mut self) -> &mut gpui::Interactivity {
|
|
self.div.interactivity()
|
|
}
|
|
}
|
|
|
|
impl StatefulInteractiveElement for Tab {}
|
|
|
|
impl Toggleable for Tab {
|
|
fn toggle_state(mut self, selected: bool) -> Self {
|
|
self.selected = selected;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl ParentElement for Tab {
|
|
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
|
self.children.extend(elements)
|
|
}
|
|
}
|
|
|
|
impl RenderOnce for Tab {
|
|
#[allow(refining_impl_trait)]
|
|
fn render(self, _: &mut Window, cx: &mut App) -> Stateful<Div> {
|
|
let (text_color, tab_bg, _tab_hover_bg, _tab_active_bg) = match self.selected {
|
|
false => (
|
|
cx.theme().colors().text_muted,
|
|
cx.theme().colors().tab_inactive_background,
|
|
cx.theme().colors().ghost_element_hover,
|
|
cx.theme().colors().ghost_element_active,
|
|
),
|
|
true => (
|
|
cx.theme().colors().text,
|
|
cx.theme().colors().tab_active_background,
|
|
cx.theme().colors().element_hover,
|
|
cx.theme().colors().element_active,
|
|
),
|
|
};
|
|
|
|
let (start_slot, end_slot) = {
|
|
let start_slot = h_flex().size_3().justify_center().children(self.start_slot);
|
|
|
|
let end_slot = h_flex().size_3().justify_center().children(self.end_slot);
|
|
|
|
match self.close_side {
|
|
TabCloseSide::End => (start_slot, end_slot),
|
|
TabCloseSide::Start => (end_slot, start_slot),
|
|
}
|
|
};
|
|
|
|
self.div
|
|
.h(Tab::container_height(cx))
|
|
.bg(tab_bg)
|
|
.border_color(cx.theme().colors().border)
|
|
.map(|this| match self.position {
|
|
TabPosition::First => {
|
|
if self.selected {
|
|
this.pl_px().border_r_1().pb_px()
|
|
} else {
|
|
this.pl_px().pr_px().border_b_1()
|
|
}
|
|
}
|
|
TabPosition::Last => {
|
|
if self.selected {
|
|
this.border_l_1().border_r_1().pb_px()
|
|
} else {
|
|
this.pr_px().pl_px().border_b_1().border_r_1()
|
|
}
|
|
}
|
|
TabPosition::Middle(Ordering::Equal) => this.border_l_1().border_r_1().pb_px(),
|
|
TabPosition::Middle(Ordering::Less) => this.border_l_1().pr_px().border_b_1(),
|
|
TabPosition::Middle(Ordering::Greater) => this.border_r_1().pl_px().border_b_1(),
|
|
})
|
|
.cursor_pointer()
|
|
.child(
|
|
h_flex()
|
|
.group("")
|
|
.relative()
|
|
.h(Tab::content_height(cx))
|
|
.px(DynamicSpacing::Base04.px(cx))
|
|
.gap(DynamicSpacing::Base04.rems(cx))
|
|
.text_color(text_color)
|
|
.child(start_slot)
|
|
.children(self.children)
|
|
.child(end_slot),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Component for Tab {
|
|
fn scope() -> ComponentScope {
|
|
ComponentScope::None
|
|
}
|
|
|
|
fn description() -> Option<&'static str> {
|
|
Some(
|
|
"A tab component that can be used in a tabbed interface, supporting different positions and states.",
|
|
)
|
|
}
|
|
|
|
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
|
Some(
|
|
v_flex()
|
|
.gap_6()
|
|
.children(vec![example_group_with_title(
|
|
"Variations",
|
|
vec![
|
|
single_example(
|
|
"Default",
|
|
Tab::new("default").child("Default Tab").into_any_element(),
|
|
),
|
|
single_example(
|
|
"Selected",
|
|
Tab::new("selected")
|
|
.toggle_state(true)
|
|
.child("Selected Tab")
|
|
.into_any_element(),
|
|
),
|
|
single_example(
|
|
"First",
|
|
Tab::new("first")
|
|
.position(TabPosition::First)
|
|
.child("First Tab")
|
|
.into_any_element(),
|
|
),
|
|
single_example(
|
|
"Middle",
|
|
Tab::new("middle")
|
|
.position(TabPosition::Middle(Ordering::Equal))
|
|
.child("Middle Tab")
|
|
.into_any_element(),
|
|
),
|
|
single_example(
|
|
"Last",
|
|
Tab::new("last")
|
|
.position(TabPosition::Last)
|
|
.child("Last Tab")
|
|
.into_any_element(),
|
|
),
|
|
],
|
|
)])
|
|
.into_any_element(),
|
|
)
|
|
}
|
|
}
|