Add assistant icons to the toolbar

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2023-06-26 16:49:33 +02:00
parent c5b3785be5
commit edc7f30660
6 changed files with 132 additions and 102 deletions

View file

@ -0,0 +1 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.9 0.499976C13.9 0.279062 13.7209 0.0999756 13.5 0.0999756C13.2791 0.0999756 13.1 0.279062 13.1 0.499976V1.09998H12.5C12.2791 1.09998 12.1 1.27906 12.1 1.49998C12.1 1.72089 12.2791 1.89998 12.5 1.89998H13.1V2.49998C13.1 2.72089 13.2791 2.89998 13.5 2.89998C13.7209 2.89998 13.9 2.72089 13.9 2.49998V1.89998H14.5C14.7209 1.89998 14.9 1.72089 14.9 1.49998C14.9 1.27906 14.7209 1.09998 14.5 1.09998H13.9V0.499976ZM11.8536 3.14642C12.0488 3.34168 12.0488 3.65826 11.8536 3.85353L10.8536 4.85353C10.6583 5.04879 10.3417 5.04879 10.1465 4.85353C9.9512 4.65827 9.9512 4.34169 10.1465 4.14642L11.1464 3.14643C11.3417 2.95116 11.6583 2.95116 11.8536 3.14642ZM9.85357 5.14642C10.0488 5.34168 10.0488 5.65827 9.85357 5.85353L2.85355 12.8535C2.65829 13.0488 2.34171 13.0488 2.14645 12.8535C1.95118 12.6583 1.95118 12.3417 2.14645 12.1464L9.14646 5.14642C9.34172 4.95116 9.65831 4.95116 9.85357 5.14642ZM13.5 5.09998C13.7209 5.09998 13.9 5.27906 13.9 5.49998V6.09998H14.5C14.7209 6.09998 14.9 6.27906 14.9 6.49998C14.9 6.72089 14.7209 6.89998 14.5 6.89998H13.9V7.49998C13.9 7.72089 13.7209 7.89998 13.5 7.89998C13.2791 7.89998 13.1 7.72089 13.1 7.49998V6.89998H12.5C12.2791 6.89998 12.1 6.72089 12.1 6.49998C12.1 6.27906 12.2791 6.09998 12.5 6.09998H13.1V5.49998C13.1 5.27906 13.2791 5.09998 13.5 5.09998ZM8.90002 0.499976C8.90002 0.279062 8.72093 0.0999756 8.50002 0.0999756C8.2791 0.0999756 8.10002 0.279062 8.10002 0.499976V1.09998H7.50002C7.2791 1.09998 7.10002 1.27906 7.10002 1.49998C7.10002 1.72089 7.2791 1.89998 7.50002 1.89998H8.10002V2.49998C8.10002 2.72089 8.2791 2.89998 8.50002 2.89998C8.72093 2.89998 8.90002 2.72089 8.90002 2.49998V1.89998H9.50002C9.72093 1.89998 9.90002 1.72089 9.90002 1.49998C9.90002 1.27906 9.72093 1.09998 9.50002 1.09998H8.90002V0.499976Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.81832 0.68179C7.64258 0.506054 7.35766 0.506054 7.18192 0.68179L5.18192 2.68179C5.00619 2.85753 5.00619 3.14245 5.18192 3.31819C5.35766 3.49392 5.64258 3.49392 5.81832 3.31819L7.05012 2.08638L7.05012 5.50023C7.05012 5.74876 7.25159 5.95023 7.50012 5.95023C7.74865 5.95023 7.95012 5.74876 7.95012 5.50023L7.95012 2.08638L9.18192 3.31819C9.35766 3.49392 9.64258 3.49392 9.81832 3.31819C9.99406 3.14245 9.99406 2.85753 9.81832 2.68179L7.81832 0.68179ZM7.95012 12.9136V9.50023C7.95012 9.2517 7.74865 9.05023 7.50012 9.05023C7.25159 9.05023 7.05012 9.2517 7.05012 9.50023V12.9136L5.81832 11.6818C5.64258 11.5061 5.35766 11.5061 5.18192 11.6818C5.00619 11.8575 5.00619 12.1424 5.18192 12.3182L7.18192 14.3182C7.26632 14.4026 7.38077 14.45 7.50012 14.45C7.61947 14.45 7.73393 14.4026 7.81832 14.3182L9.81832 12.3182C9.99406 12.1424 9.99406 11.8575 9.81832 11.6818C9.64258 11.5061 9.35766 11.5061 9.18192 11.6818L7.95012 12.9136ZM1.49994 7.00017C1.2238 7.00017 0.999939 7.22403 0.999939 7.50017C0.999939 7.77631 1.2238 8.00017 1.49994 8.00017L13.4999 8.00017C13.7761 8.00017 13.9999 7.77631 13.9999 7.50017C13.9999 7.22403 13.7761 7.00017 13.4999 7.00017L1.49994 7.00017Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -28,7 +28,6 @@ use search::BufferSearchBar;
use serde::Deserialize;
use settings::SettingsStore;
use std::{
borrow::Cow,
cell::RefCell,
cmp, env,
fmt::Write,
@ -40,13 +39,9 @@ use std::{
time::Duration,
};
use theme::{ui::IconStyle, AssistantStyle};
use util::{
channel::ReleaseChannel, paths::CONVERSATIONS_DIR, post_inc, truncate_and_trailoff, ResultExt,
TryFutureExt,
};
use util::{channel::ReleaseChannel, paths::CONVERSATIONS_DIR, post_inc, ResultExt, TryFutureExt};
use workspace::{
dock::{DockPosition, Panel},
item::Item,
searchable::Direction,
Save, ToggleZoom, Toolbar, Workspace,
};
@ -361,64 +356,43 @@ impl AssistantPanel {
})
}
fn render_current_model(
&self,
style: &AssistantStyle,
cx: &mut ViewContext<Self>,
) -> Option<impl Element<Self>> {
enum Model {}
let model = self
.active_editor()?
.read(cx)
.conversation
.read(cx)
.model
.clone();
Some(
MouseEventHandler::<Model, _>::new(0, cx, |state, _| {
let style = style.model.style_for(state);
Label::new(model, style.text.clone())
.contained()
.with_style(style.container)
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, this, cx| {
if let Some(editor) = this.active_editor() {
editor.update(cx, |editor, cx| {
editor.cycle_model(cx);
});
}
}),
)
fn render_editor_tools(&self, style: &AssistantStyle) -> Vec<AnyElement<Self>> {
if self.active_editor().is_some() {
vec![
Self::render_split_button(&style.split_button).into_any(),
Self::render_assist_button(&style.assist_button).into_any(),
]
} else {
Default::default()
}
}
fn render_remaining_tokens(
&self,
style: &AssistantStyle,
cx: &mut ViewContext<Self>,
) -> Option<impl Element<Self>> {
self.active_editor().and_then(|editor| {
editor
.read(cx)
.conversation
.read(cx)
.remaining_tokens()
.map(|remaining_tokens| {
let remaining_tokens_style = if remaining_tokens <= 0 {
&style.no_remaining_tokens
} else {
&style.remaining_tokens
};
Label::new(
remaining_tokens.to_string(),
remaining_tokens_style.text.clone(),
)
.contained()
.with_style(remaining_tokens_style.container)
})
})
fn render_split_button(style: &IconStyle) -> impl Element<Self> {
enum SplitMessage {}
Svg::for_style(style.icon.clone())
.contained()
.with_style(style.container)
.mouse::<SplitMessage>(0)
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, this: &mut Self, cx| {
if let Some(active_editor) = this.active_editor() {
active_editor.update(cx, |editor, cx| editor.split(&Default::default(), cx));
}
})
}
fn render_assist_button(style: &IconStyle) -> impl Element<Self> {
enum Assist {}
Svg::for_style(style.icon.clone())
.contained()
.with_style(style.container)
.mouse::<Assist>(0)
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, this: &mut Self, cx| {
if let Some(active_editor) = this.active_editor() {
active_editor.update(cx, |editor, cx| editor.assist(&Default::default(), cx));
}
})
}
fn render_plus_button(style: &IconStyle) -> impl Element<Self> {
@ -589,19 +563,16 @@ impl View for AssistantPanel {
)
.with_children(title)
.with_children(
self.render_current_model(&style, cx)
.map(|current_model| current_model.aligned().flex_float()),
)
.with_children(
self.render_remaining_tokens(&style, cx)
.map(|remaining_tokens| remaining_tokens.aligned().flex_float()),
self.render_editor_tools(&style)
.into_iter()
.map(|tool| tool.aligned().flex_float()),
)
.with_child(
Self::render_plus_button(&style.plus_button)
.aligned()
.flex_float(),
)
.with_child(self.render_zoom_button(&style, cx).aligned().flex_float())
.with_child(self.render_zoom_button(&style, cx).aligned())
.contained()
.with_style(theme.workspace.tab_bar.container)
.expanded()
@ -1995,6 +1966,44 @@ impl ConversationEditor {
.map(|summary| summary.text.clone())
.unwrap_or_else(|| "New Conversation".into())
}
fn render_current_model(
&self,
style: &AssistantStyle,
cx: &mut ViewContext<Self>,
) -> impl Element<Self> {
enum Model {}
MouseEventHandler::<Model, _>::new(0, cx, |state, cx| {
let style = style.model.style_for(state);
Label::new(self.conversation.read(cx).model.clone(), style.text.clone())
.contained()
.with_style(style.container)
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, |_, this, cx| this.cycle_model(cx))
}
fn render_remaining_tokens(
&self,
style: &AssistantStyle,
cx: &mut ViewContext<Self>,
) -> Option<impl Element<Self>> {
let remaining_tokens = self.conversation.read(cx).remaining_tokens()?;
let remaining_tokens_style = if remaining_tokens <= 0 {
&style.no_remaining_tokens
} else {
&style.remaining_tokens
};
Some(
Label::new(
remaining_tokens.to_string(),
remaining_tokens_style.text.clone(),
)
.contained()
.with_style(remaining_tokens_style.container),
)
}
}
impl Entity for ConversationEditor {
@ -2008,9 +2017,20 @@ impl View for ConversationEditor {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = &theme::current(cx).assistant;
ChildView::new(&self.editor, cx)
.contained()
.with_style(theme.container)
Stack::new()
.with_child(
ChildView::new(&self.editor, cx)
.contained()
.with_style(theme.container),
)
.with_child(
Flex::row()
.with_child(self.render_current_model(theme, cx))
.with_children(self.render_remaining_tokens(theme, cx))
.aligned()
.top()
.right(),
)
.into_any()
}
@ -2021,29 +2041,6 @@ impl View for ConversationEditor {
}
}
impl Item for ConversationEditor {
fn tab_content<V: View>(
&self,
_: Option<usize>,
style: &theme::Tab,
cx: &gpui::AppContext,
) -> AnyElement<V> {
let title = truncate_and_trailoff(&self.title(cx), editor::MAX_TAB_TITLE_LEN);
Label::new(title, style.label.clone()).into_any()
}
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<Cow<str>> {
Some(self.title(cx).into())
}
fn as_searchable(
&self,
_: &ViewHandle<Self>,
) -> Option<Box<dyn workspace::searchable::SearchableItemHandle>> {
Some(Box::new(self.editor.clone()))
}
}
#[derive(Clone, Debug)]
struct MessageAnchor {
id: MessageId,

View file

@ -165,6 +165,7 @@ impl<V: View> Element<V> for Label {
_: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
line.paint(
scene,
bounds.origin(),

View file

@ -994,6 +994,8 @@ pub struct TerminalStyle {
pub struct AssistantStyle {
pub container: ContainerStyle,
pub hamburger_button: IconStyle,
pub split_button: IconStyle,
pub assist_button: IconStyle,
pub zoom_in_button: IconStyle,
pub zoom_out_button: IconStyle,
pub plus_button: IconStyle,
@ -1003,7 +1005,6 @@ pub struct AssistantStyle {
pub user_sender: Interactive<ContainedText>,
pub assistant_sender: Interactive<ContainedText>,
pub system_sender: Interactive<ContainedText>,
pub model_info_container: ContainerStyle,
pub model: Interactive<ContainedText>,
pub remaining_tokens: ContainedText,
pub no_remaining_tokens: ContainedText,

View file

@ -28,6 +28,32 @@ export default function assistant(colorScheme: ColorScheme) {
margin: { left: 12 },
}
},
splitButton: {
icon: {
color: text(layer, "sans", "default", { size: "sm" }).color,
asset: "icons/split_message_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
margin: { left: 12 },
}
},
assistButton: {
icon: {
color: text(layer, "sans", "default", { size: "sm" }).color,
asset: "icons/assist_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
margin: { left: 12, right: 12 },
}
},
zoomInButton: {
icon: {
color: text(layer, "sans", "default", { size: "sm" }).color,
@ -120,13 +146,10 @@ export default function assistant(colorScheme: ColorScheme) {
margin: { top: 2, left: 8 },
...text(layer, "sans", "default", { size: "2xs" }),
},
modelInfoContainer: {
margin: { right: 16, top: 4 },
},
model: interactive({
base: {
background: background(layer, "on"),
margin: { right: 8 },
margin: { left: 12, right: 12, top: 12 },
padding: 4,
cornerRadius: 4,
...text(layer, "sans", "default", { size: "xs" }),
@ -139,11 +162,17 @@ export default function assistant(colorScheme: ColorScheme) {
},
}),
remainingTokens: {
margin: { right: 12 },
background: background(layer, "on"),
margin: { top: 12, right: 12 },
padding: 4,
cornerRadius: 4,
...text(layer, "sans", "positive", { size: "xs" }),
},
noRemainingTokens: {
margin: { right: 12 },
background: background(layer, "on"),
margin: { top: 12, right: 12 },
padding: 4,
cornerRadius: 4,
...text(layer, "sans", "negative", { size: "xs" }),
},
errorIcon: {