diff --git a/assets/icons/radix/maximize.svg b/assets/icons/radix/maximize.svg
new file mode 100644
index 0000000000..f37f6a2087
--- /dev/null
+++ b/assets/icons/radix/maximize.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/radix/minimize.svg b/assets/icons/radix/minimize.svg
new file mode 100644
index 0000000000..ec78f152e1
--- /dev/null
+++ b/assets/icons/radix/minimize.svg
@@ -0,0 +1,4 @@
+
diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs
index 4d300230e1..35c88486f7 100644
--- a/crates/ai/src/assistant.rs
+++ b/crates/ai/src/assistant.rs
@@ -2061,6 +2061,8 @@ impl ConversationEditor {
let remaining_tokens = self.conversation.read(cx).remaining_tokens()?;
let remaining_tokens_style = if remaining_tokens <= 0 {
&style.no_remaining_tokens
+ } else if remaining_tokens <= 500 {
+ &style.low_remaining_tokens
} else {
&style.remaining_tokens
};
diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs
index 1949a5d9bb..fae7c470e3 100644
--- a/crates/theme/src/theme.rs
+++ b/crates/theme/src/theme.rs
@@ -1030,6 +1030,7 @@ pub struct AssistantStyle {
pub system_sender: Interactive,
pub model: Interactive,
pub remaining_tokens: ContainedText,
+ pub low_remaining_tokens: ContainedText,
pub no_remaining_tokens: ContainedText,
pub error_icon: Icon,
pub api_key_editor: FieldEditor,
diff --git a/styles/src/component/tab_bar_button.ts b/styles/src/component/tab_bar_button.ts
new file mode 100644
index 0000000000..0c43e7010e
--- /dev/null
+++ b/styles/src/component/tab_bar_button.ts
@@ -0,0 +1,55 @@
+import { Theme, StyleSets } from "../common"
+import { interactive } from "../element"
+import { InteractiveState } from "../element/interactive"
+import { background, foreground } from "../style_tree/components"
+
+interface TabBarButtonOptions {
+ icon: string
+ color?: StyleSets
+}
+
+type TabBarButtonProps = TabBarButtonOptions & {
+ state?: Partial>>
+}
+
+export function tab_bar_button(theme: Theme, { icon, color = "base" }: TabBarButtonProps) {
+ const button_spacing = 8
+
+ return (
+ interactive({
+ base: {
+ icon: {
+ color: foreground(theme.middle, color),
+ asset: icon,
+ dimensions: {
+ width: 15,
+ height: 15,
+ },
+ },
+ container: {
+ corner_radius: 4,
+ padding: {
+ top: 4, bottom: 4, left: 4, right: 4
+ },
+ margin: {
+ left: button_spacing / 2,
+ right: button_spacing / 2,
+ },
+ },
+ },
+ state: {
+ hovered: {
+ container: {
+ background: background(theme.middle, color, "hovered"),
+
+ }
+ },
+ clicked: {
+ container: {
+ background: background(theme.middle, color, "pressed"),
+ }
+ },
+ },
+ })
+ )
+}
diff --git a/styles/src/style_tree/assistant.ts b/styles/src/style_tree/assistant.ts
index 6df02a0e33..cfc1f8d813 100644
--- a/styles/src/style_tree/assistant.ts
+++ b/styles/src/style_tree/assistant.ts
@@ -1,233 +1,133 @@
-import { text, border, background, foreground } from "./components"
-import { interactive } from "../element"
-import { useTheme } from "../theme"
+import { text, border, background, foreground, TextStyle } from "./components"
+import { Interactive, interactive } from "../element"
+import { tab_bar_button } from "../component/tab_bar_button"
+import { StyleSets, useTheme } from "../theme"
+
+type RoleCycleButton = TextStyle & {
+ background?: string
+}
+// TODO: Replace these with zed types
+type RemainingTokens = TextStyle & {
+ background: string,
+ margin: { top: number, right: number },
+ padding: {
+ right: number,
+ left: number,
+ top: number,
+ bottom: number,
+ },
+ corner_radius: number,
+}
export default function assistant(): any {
const theme = useTheme()
+ const interactive_role = (color: StyleSets): Interactive => {
+ return (
+ interactive({
+ base: {
+ ...text(theme.highest, "sans", color, { size: "sm" }),
+ },
+ state: {
+ hovered: {
+ ...text(theme.highest, "sans", color, { size: "sm" }),
+ background: background(theme.highest, color, "hovered"),
+ },
+ clicked: {
+ ...text(theme.highest, "sans", color, { size: "sm" }),
+ background: background(theme.highest, color, "pressed"),
+ }
+ },
+ })
+ )
+ }
+
+ const tokens_remaining = (color: StyleSets): RemainingTokens => {
+ return (
+ {
+ ...text(theme.highest, "mono", color, { size: "xs" }),
+ background: background(theme.highest, "on", "default"),
+ margin: { top: 12, right: 20 },
+ padding: { right: 4, left: 4, top: 1, bottom: 1 },
+ corner_radius: 6,
+ }
+ )
+ }
+
return {
container: {
background: background(theme.highest),
padding: { left: 12 },
},
message_header: {
- margin: { bottom: 6, top: 6 },
+ margin: { bottom: 4, top: 4 },
background: background(theme.highest),
},
- hamburger_button: interactive({
- base: {
- icon: {
- color: foreground(theme.highest, "variant"),
- asset: "icons/hamburger_15.svg",
- dimensions: {
- width: 15,
- height: 15,
- },
- },
- container: {
- padding: { left: 12, right: 8.5 },
- },
- },
- state: {
- hovered: {
- icon: {
- color: foreground(theme.highest, "hovered"),
- },
- },
- },
+ hamburger_button: tab_bar_button(theme, {
+ icon: "icons/hamburger_15.svg",
}),
- split_button: interactive({
- base: {
- icon: {
- color: foreground(theme.highest, "variant"),
- asset: "icons/split_message_15.svg",
- dimensions: {
- width: 15,
- height: 15,
- },
- },
- container: {
- padding: { left: 8.5, right: 8.5 },
- },
- },
- state: {
- hovered: {
- icon: {
- color: foreground(theme.highest, "hovered"),
- },
- },
- },
+
+ split_button: tab_bar_button(theme, {
+ icon: "icons/split_message_15.svg",
}),
- quote_button: interactive({
- base: {
- icon: {
- color: foreground(theme.highest, "variant"),
- asset: "icons/quote_15.svg",
- dimensions: {
- width: 15,
- height: 15,
- },
- },
- container: {
- padding: { left: 8.5, right: 8.5 },
- },
- },
- state: {
- hovered: {
- icon: {
- color: foreground(theme.highest, "hovered"),
- },
- },
- },
+ quote_button: tab_bar_button(theme, {
+ icon: "icons/radix/quote.svg",
}),
- assist_button: interactive({
- base: {
- icon: {
- color: foreground(theme.highest, "variant"),
- asset: "icons/assist_15.svg",
- dimensions: {
- width: 15,
- height: 15,
- },
- },
- container: {
- padding: { left: 8.5, right: 8.5 },
- },
- },
- state: {
- hovered: {
- icon: {
- color: foreground(theme.highest, "hovered"),
- },
- },
- },
+ assist_button: tab_bar_button(theme, {
+ icon: "icons/radix/magic-wand.svg",
}),
- zoom_in_button: interactive({
- base: {
- icon: {
- color: foreground(theme.highest, "variant"),
- asset: "icons/maximize_8.svg",
- dimensions: {
- width: 12,
- height: 12,
- },
- },
- container: {
- padding: { left: 10, right: 10 },
- },
- },
- state: {
- hovered: {
- icon: {
- color: foreground(theme.highest, "hovered"),
- },
- },
- },
+ zoom_in_button: tab_bar_button(theme, {
+ icon: "icons/radix/maximize.svg",
}),
- zoom_out_button: interactive({
- base: {
- icon: {
- color: foreground(theme.highest, "variant"),
- asset: "icons/minimize_8.svg",
- dimensions: {
- width: 12,
- height: 12,
- },
- },
- container: {
- padding: { left: 10, right: 10 },
- },
- },
- state: {
- hovered: {
- icon: {
- color: foreground(theme.highest, "hovered"),
- },
- },
- },
+ zoom_out_button: tab_bar_button(theme, {
+ icon: "icons/radix/minimize.svg",
}),
- plus_button: interactive({
- base: {
- icon: {
- color: foreground(theme.highest, "variant"),
- asset: "icons/plus_12.svg",
- dimensions: {
- width: 12,
- height: 12,
- },
- },
- container: {
- padding: { left: 10, right: 10 },
- },
- },
- state: {
- hovered: {
- icon: {
- color: foreground(theme.highest, "hovered"),
- },
- },
- },
+ plus_button: tab_bar_button(theme, {
+ icon: "icons/radix/plus.svg",
}),
title: {
- ...text(theme.highest, "sans", "default", { size: "sm" }),
+ ...text(theme.highest, "sans", "default", { size: "xs" }),
},
saved_conversation: {
container: interactive({
base: {
- background: background(theme.highest, "on"),
+ background: background(theme.middle),
padding: { top: 4, bottom: 4 },
+ border: border(theme.middle, "default", { top: true, overlay: true }),
},
state: {
hovered: {
- background: background(theme.highest, "on", "hovered"),
+ background: background(theme.middle, "hovered"),
},
+ clicked: {
+ background: background(theme.middle, "pressed"),
+ }
},
}),
saved_at: {
margin: { left: 8 },
- ...text(theme.highest, "sans", "default", { size: "xs" }),
+ ...text(theme.highest, "sans", "variant", { size: "xs" }),
},
title: {
- margin: { left: 16 },
+ margin: { left: 12 },
...text(theme.highest, "sans", "default", {
size: "sm",
weight: "bold",
}),
},
},
- user_sender: {
- default: {
- ...text(theme.highest, "sans", "default", {
- size: "sm",
- weight: "bold",
- }),
- },
- },
- assistant_sender: {
- default: {
- ...text(theme.highest, "sans", "accent", {
- size: "sm",
- weight: "bold",
- }),
- },
- },
- system_sender: {
- default: {
- ...text(theme.highest, "sans", "variant", {
- size: "sm",
- weight: "bold",
- }),
- },
- },
+ user_sender: interactive_role("base"),
+ assistant_sender: interactive_role("accent"),
+ system_sender: interactive_role("warning"),
sent_at: {
margin: { top: 2, left: 8 },
- ...text(theme.highest, "sans", "default", { size: "2xs" }),
+ ...text(theme.highest, "sans", "variant", { size: "2xs" }),
},
model: interactive({
base: {
- background: background(theme.highest, "on"),
- margin: { left: 12, right: 12, top: 12 },
- padding: 4,
+ background: background(theme.highest),
+ margin: { left: 12, right: 4, top: 12 },
+ padding: { right: 4, left: 4, top: 1, bottom: 1 },
corner_radius: 4,
...text(theme.highest, "sans", "default", { size: "xs" }),
},
@@ -238,20 +138,9 @@ export default function assistant(): any {
},
},
}),
- remaining_tokens: {
- background: background(theme.highest, "on"),
- margin: { top: 12, right: 24 },
- padding: 4,
- corner_radius: 4,
- ...text(theme.highest, "sans", "positive", { size: "xs" }),
- },
- no_remaining_tokens: {
- background: background(theme.highest, "on"),
- margin: { top: 12, right: 24 },
- padding: 4,
- corner_radius: 4,
- ...text(theme.highest, "sans", "negative", { size: "xs" }),
- },
+ remaining_tokens: tokens_remaining("positive"),
+ low_remaining_tokens: tokens_remaining("warning"),
+ no_remaining_tokens: tokens_remaining("negative"),
error_icon: {
margin: { left: 8 },
color: foreground(theme.highest, "negative"),
@@ -259,7 +148,7 @@ export default function assistant(): any {
},
api_key_editor: {
background: background(theme.highest, "on"),
- corner_radius: 6,
+ corner_radius: 4,
text: text(theme.highest, "mono", "on"),
placeholder_text: text(theme.highest, "mono", "on", "disabled", {
size: "xs",
diff --git a/styles/src/theme/create_theme.ts b/styles/src/theme/create_theme.ts
index dff4c3dbc4..d2701f8341 100644
--- a/styles/src/theme/create_theme.ts
+++ b/styles/src/theme/create_theme.ts
@@ -12,8 +12,17 @@ export interface Theme {
name: string
is_light: boolean
+ /**
+ * App background, other elements that should sit directly on top of the background.
+ */
lowest: Layer
+ /**
+ * Panels, tabs, other UI surfaces that sit on top of the background.
+ */
middle: Layer
+ /**
+ * Editors like code buffers, conversation editors, etc.
+ */
highest: Layer
ramps: RampSet