WIP UI Tweaks (#2911)

Nate Butler created

- Tighten up toolbar
- Reduce intensity of active tools
- Remove divider between project + branch
- Add a styletree for toolbar + move breadcrumb into it
- Some ts theme tidying

[[PR Description]]

Release Notes:

- Improved density and contrast of a number of UI elements.

Change summary

assets/icons/check_circle.svg                |   4 
assets/icons/error.svg                       |   4 
assets/icons/warning.svg                     |   7 
crates/breadcrumbs/src/breadcrumbs.rs        |   8 
crates/collab_ui/src/collab_titlebar_item.rs |  75 +++++-------
crates/theme/src/theme.rs                    |   5 
styles/src/build_themes.ts                   |   9 -
styles/src/build_tokens.ts                   |   4 
styles/src/component/button.ts               |  55 +++++---
styles/src/component/icon_button.ts          |  52 +++++---
styles/src/component/index.ts                |   6 +
styles/src/component/indicator.ts            |   8 +
styles/src/component/input.ts                |   2 
styles/src/component/tab.ts                  |  20 +-
styles/src/component/tab_bar_button.ts       |  67 +++++-----
styles/src/component/text_button.ts          |  52 +++++--
styles/src/element/index.ts                  |   2 
styles/src/element/margin.ts                 |  23 ++-
styles/src/element/padding.ts                |  23 ++-
styles/src/style_tree/assistant.ts           |  69 +++++-----
styles/src/style_tree/collab_modals.ts       |  23 ++-
styles/src/style_tree/collab_panel.ts        |  14 +-
styles/src/style_tree/component_test.ts      |   9 
styles/src/style_tree/contacts_popover.ts    |   1 
styles/src/style_tree/editor.ts              |   2 
styles/src/style_tree/feedback.ts            |   2 
styles/src/style_tree/picker.ts              |   2 
styles/src/style_tree/project_panel.ts       |  16 +-
styles/src/style_tree/search.ts              | 131 +++++++++++++++------
styles/src/style_tree/status_bar.ts          |  42 +++---
styles/src/style_tree/tab_bar.ts             |   6 
styles/src/style_tree/titlebar.ts            |  24 +--
styles/src/style_tree/toolbar.ts             |  38 ++++++
styles/src/style_tree/workspace.ts           |  32 -----
styles/src/theme/create_theme.ts             |  17 +-
styles/src/theme/index.ts                    |   1 
styles/src/theme/tokens/theme.ts             |   6 
styles/tsconfig.json                         |   4 
38 files changed, 493 insertions(+), 372 deletions(-)

Detailed changes

assets/icons/check_circle.svg 🔗

@@ -1,4 +1,4 @@
 <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M5 8L6.5 9L9 5.5" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
-<circle cx="7" cy="7" r="4.875" stroke="black" stroke-width="1.25"/>
+<path d="M5 8L6.5 9L9 5.5" stroke="#11181C" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
+<circle cx="7" cy="7" r="4.875" stroke="#11181C" stroke-width="1.25"/>
 </svg>

assets/icons/error.svg 🔗

@@ -1,4 +1,4 @@
 <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M8.86396 2C8.99657 2 9.12375 2.05268 9.21751 2.14645L11.8536 4.78249C11.9473 4.87625 12 5.00343 12 5.13604L12 8.86396C12 8.99657 11.9473 9.12375 11.8536 9.21751L9.21751 11.8536C9.12375 11.9473 8.99657 12 8.86396 12L5.13604 12C5.00343 12 4.87625 11.9473 4.78249 11.8536L2.14645 9.21751C2.05268 9.12375 2 8.99657 2 8.86396L2 5.13604C2 5.00343 2.05268 4.87625 2.14645 4.78249L4.78249 2.14645C4.87625 2.05268 5.00343 2 5.13604 2L8.86396 2Z" stroke="black" stroke-width="1.25" stroke-linejoin="round"/>
-<path d="M8.89063 5.10938L5.10937 8.89063M8.89063 8.89063L5.10937 5.10938" stroke="black" stroke-width="1.25" stroke-linecap="round"/>
+<path d="M8.86396 2C8.99657 2 9.12375 2.05268 9.21751 2.14645L11.8536 4.78249C11.9473 4.87625 12 5.00343 12 5.13604L12 8.86396C12 8.99657 11.9473 9.12375 11.8536 9.21751L9.21751 11.8536C9.12375 11.9473 8.99657 12 8.86396 12L5.13604 12C5.00343 12 4.87625 11.9473 4.78249 11.8536L2.14645 9.21751C2.05268 9.12375 2 8.99657 2 8.86396L2 5.13604C2 5.00343 2.05268 4.87625 2.14645 4.78249L4.78249 2.14645C4.87625 2.05268 5.00343 2 5.13604 2L8.86396 2Z" fill="#001A33" fill-opacity="0.157" stroke="#11181C" stroke-width="1.25" stroke-linejoin="round"/>
+<path d="M8.89063 5.10938L5.10937 8.89063M8.89063 8.89063L5.10937 5.10938" stroke="#11181C" stroke-width="1.25" stroke-linecap="round"/>
 </svg>

assets/icons/warning.svg 🔗

@@ -1,5 +1,6 @@
 <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M9.5 6.5L11.994 11.625C12.1556 11.9571 11.9137 12.3438 11.5444 12.3438H2.45563C2.08628 12.3438 1.84442 11.9571 2.00603 11.625L4.5 6.5" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M7 7L7 2" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
-<circle cx="7" cy="9.24219" r="0.75" fill="black"/>
+<path d="M2.45563 12.3438H11.5444C11.9137 12.3438 12.1556 11.9571 11.994 11.625L10.2346 8.00952C9.77174 7.05841 8.89104 6.37821 7.85383 6.17077C7.29019 6.05804 6.70981 6.05804 6.14617 6.17077C5.10896 6.37821 4.22826 7.05841 3.76542 8.00952L2.00603 11.625C1.84442 11.9571 2.08628 12.3438 2.45563 12.3438Z" fill="#001A33" fill-opacity="0.157"/>
+<path d="M9.5 6.5L11.994 11.625C12.1556 11.9571 11.9137 12.3438 11.5444 12.3438H2.45563C2.08628 12.3438 1.84442 11.9571 2.00603 11.625L4.5 6.5" stroke="#11181C" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M7 7L7 2" stroke="#11181C" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
+<circle cx="7" cy="9.24219" r="0.75" fill="#11181C"/>
 </svg>

crates/breadcrumbs/src/breadcrumbs.rs 🔗

@@ -50,7 +50,7 @@ impl View for Breadcrumbs {
         let not_editor = active_item.downcast::<editor::Editor>().is_none();
 
         let theme = theme::current(cx).clone();
-        let style = &theme.workspace.breadcrumbs;
+        let style = &theme.workspace.toolbar.breadcrumbs;
 
         let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
             Some(breadcrumbs) => breadcrumbs,
@@ -60,7 +60,7 @@ impl View for Breadcrumbs {
         .map(|breadcrumb| {
             Text::new(
                 breadcrumb.text,
-                theme.workspace.breadcrumbs.default.text.clone(),
+                theme.workspace.toolbar.breadcrumbs.default.text.clone(),
             )
             .with_highlights(breadcrumb.highlights.unwrap_or_default())
             .into_any()
@@ -68,10 +68,10 @@ impl View for Breadcrumbs {
 
         let crumbs = Flex::row()
             .with_children(Itertools::intersperse_with(breadcrumbs, || {
-                Label::new(" 〉 ", style.default.text.clone()).into_any()
+                Label::new(" › ", style.default.text.clone()).into_any()
             }))
             .constrained()
-            .with_height(theme.workspace.breadcrumb_height)
+            .with_height(theme.workspace.toolbar.breadcrumb_height)
             .contained();
 
         if not_editor || !self.pane_focused {

crates/collab_ui/src/collab_titlebar_item.rs 🔗

@@ -213,7 +213,6 @@ impl CollabTitlebarItem {
             .map(|branch| util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH));
         let project_style = theme.titlebar.project_menu_button.clone();
         let git_style = theme.titlebar.git_menu_button.clone();
-        let divider_style = theme.titlebar.project_name_divider.clone();
         let item_spacing = theme.titlebar.item_spacing;
 
         let mut ret = Flex::row().with_child(
@@ -248,49 +247,37 @@ impl CollabTitlebarItem {
         );
         if let Some(git_branch) = branch_prepended {
             ret = ret.with_child(
-                Flex::row()
-                    .with_child(
-                        Label::new("/", divider_style.text)
-                            .contained()
-                            .with_style(divider_style.container)
-                            .aligned()
-                            .left(),
-                    )
-                    .with_child(
-                        Stack::new()
-                            .with_child(
-                                MouseEventHandler::new::<ToggleVcsMenu, _>(
-                                    0,
-                                    cx,
-                                    |mouse_state, cx| {
-                                        enum BranchPopoverTooltip {}
-                                        let style = git_style
-                                            .in_state(self.branch_popover.is_some())
-                                            .style_for(mouse_state);
-                                        Label::new(git_branch, style.text.clone())
-                                            .contained()
-                                            .with_style(style.container.clone())
-                                            .with_margin_right(item_spacing)
-                                            .aligned()
-                                            .left()
-                                            .with_tooltip::<BranchPopoverTooltip>(
-                                                0,
-                                                "Recent branches",
-                                                Some(Box::new(ToggleVcsMenu)),
-                                                theme.tooltip.clone(),
-                                                cx,
-                                            )
-                                            .into_any_named("title-project-branch")
-                                    },
-                                )
-                                .with_cursor_style(CursorStyle::PointingHand)
-                                .on_down(MouseButton::Left, move |_, this, cx| {
-                                    this.toggle_vcs_menu(&Default::default(), cx)
-                                })
-                                .on_click(MouseButton::Left, move |_, _, _| {}),
-                            )
-                            .with_children(self.render_branches_popover_host(&theme.titlebar, cx)),
-                    ),
+                Flex::row().with_child(
+                    Stack::new()
+                        .with_child(
+                            MouseEventHandler::new::<ToggleVcsMenu, _>(0, cx, |mouse_state, cx| {
+                                enum BranchPopoverTooltip {}
+                                let style = git_style
+                                    .in_state(self.branch_popover.is_some())
+                                    .style_for(mouse_state);
+                                Label::new(git_branch, style.text.clone())
+                                    .contained()
+                                    .with_style(style.container.clone())
+                                    .with_margin_right(item_spacing)
+                                    .aligned()
+                                    .left()
+                                    .with_tooltip::<BranchPopoverTooltip>(
+                                        0,
+                                        "Recent branches",
+                                        Some(Box::new(ToggleVcsMenu)),
+                                        theme.tooltip.clone(),
+                                        cx,
+                                    )
+                                    .into_any_named("title-project-branch")
+                            })
+                            .with_cursor_style(CursorStyle::PointingHand)
+                            .on_down(MouseButton::Left, move |_, this, cx| {
+                                this.toggle_vcs_menu(&Default::default(), cx)
+                            })
+                            .on_click(MouseButton::Left, move |_, _, _| {}),
+                        )
+                        .with_children(self.render_branches_popover_host(&theme.titlebar, cx)),
+                ),
             )
         }
         ret.into_any()

crates/theme/src/theme.rs 🔗

@@ -88,8 +88,6 @@ pub struct Workspace {
     pub dock: Dock,
     pub status_bar: StatusBar,
     pub toolbar: Toolbar,
-    pub breadcrumb_height: f32,
-    pub breadcrumbs: Interactive<ContainedText>,
     pub disconnected_overlay: ContainedText,
     pub modal: ContainerStyle,
     pub zoomed_panel_foreground: ContainerStyle,
@@ -120,7 +118,6 @@ pub struct Titlebar {
     pub height: f32,
     pub menu: TitlebarMenu,
     pub project_menu_button: Toggleable<Interactive<ContainedText>>,
-    pub project_name_divider: ContainedText,
     pub git_menu_button: Toggleable<Interactive<ContainedText>>,
     pub item_spacing: f32,
     pub face_pile_spacing: f32,
@@ -411,6 +408,8 @@ pub struct Toolbar {
     pub height: f32,
     pub item_spacing: f32,
     pub toggleable_tool: Toggleable<Interactive<IconButton>>,
+    pub breadcrumb_height: f32,
+    pub breadcrumbs: Interactive<ContainedText>,
 }
 
 #[derive(Clone, Deserialize, Default, JsonSchema)]

styles/src/build_themes.ts 🔗

@@ -21,9 +21,7 @@ function clear_themes(theme_directory: string) {
     }
 }
 
-const all_themes: Theme[] = themes.map((theme) =>
-    create_theme(theme)
-)
+const all_themes: Theme[] = themes.map((theme) => create_theme(theme))
 
 function write_themes(themes: Theme[], output_directory: string) {
     clear_themes(output_directory)
@@ -34,10 +32,7 @@ function write_themes(themes: Theme[], output_directory: string) {
         const style_tree = app()
         const style_tree_json = JSON.stringify(style_tree, null, 2)
         const temp_path = path.join(temp_directory, `${theme.name}.json`)
-        const out_path = path.join(
-            output_directory,
-            `${theme.name}.json`
-        )
+        const out_path = path.join(output_directory, `${theme.name}.json`)
         fs.writeFileSync(temp_path, style_tree_json)
         fs.renameSync(temp_path, out_path)
         console.log(`- ${out_path} created`)

styles/src/build_tokens.ts 🔗

@@ -83,8 +83,6 @@ function write_tokens(themes: Theme[], tokens_directory: string) {
     console.log(`- ${METADATA_FILE} created`)
 }
 
-const all_themes: Theme[] = themes.map((theme) =>
-    create_theme(theme)
-)
+const all_themes: Theme[] = themes.map((theme) => create_theme(theme))
 
 write_tokens(all_themes, TOKENS_DIRECTORY)

styles/src/component/button.ts 🔗

@@ -5,7 +5,7 @@ import { TextStyle, background } from "../style_tree/components"
 // eslint-disable-next-line @typescript-eslint/no-namespace
 export namespace Button {
     export type Options = {
-        layer: Layer,
+        layer: Layer
         background: keyof Theme["lowest"]
         color: keyof Theme["lowest"]
         variant: Button.Variant
@@ -16,13 +16,13 @@ export namespace Button {
             bottom?: number
             left?: number
             right?: number
-        },
+        }
         states: {
-            enabled?: boolean,
-            hovered?: boolean,
-            pressed?: boolean,
-            focused?: boolean,
-            disabled?: boolean,
+            enabled?: boolean
+            hovered?: boolean
+            pressed?: boolean
+            focused?: boolean
+            disabled?: boolean
         }
     }
 
@@ -38,26 +38,26 @@ export namespace Button {
     export const CORNER_RADIUS = 6
 
     export const variant = {
-        Default: 'filled',
-        Outline: 'outline',
-        Ghost: 'ghost'
+        Default: "filled",
+        Outline: "outline",
+        Ghost: "ghost",
     } as const
 
-    export type Variant = typeof variant[keyof typeof variant]
+    export type Variant = (typeof variant)[keyof typeof variant]
 
     export const shape = {
-        Rectangle: 'rectangle',
-        Square: 'square'
+        Rectangle: "rectangle",
+        Square: "square",
     } as const
 
-    export type Shape = typeof shape[keyof typeof shape]
+    export type Shape = (typeof shape)[keyof typeof shape]
 
     export const size = {
         Small: "sm",
-        Medium: "md"
+        Medium: "md",
     } as const
 
-    export type Size = typeof size[keyof typeof size]
+    export type Size = (typeof size)[keyof typeof size]
 
     export type BaseStyle = {
         corder_radius: number
@@ -67,8 +67,8 @@ export namespace Button {
             bottom: number
             left: number
             right: number
-        },
-        margin: Button.Options['margin']
+        }
+        margin: Button.Options["margin"]
         button_height: number
     }
 
@@ -81,15 +81,18 @@ export namespace Button {
             shape: Button.shape.Rectangle,
             states: {
                 hovered: true,
-                pressed: true
-            }
+                pressed: true,
+            },
         }
     ): BaseStyle => {
         const theme = useTheme()
 
         const layer = options.layer ?? theme.middle
         const color = options.color ?? "base"
-        const background_color = options.variant === Button.variant.Ghost ? null : background(layer, options.background ?? color)
+        const background_color =
+            options.variant === Button.variant.Ghost
+                ? null
+                : background(layer, options.background ?? color)
 
         const m = {
             top: options.margin?.top ?? 0,
@@ -106,8 +109,14 @@ export namespace Button {
             padding: {
                 top: padding,
                 bottom: padding,
-                left: options.shape === Button.shape.Rectangle ? padding + Button.RECTANGLE_PADDING : padding,
-                right: options.shape === Button.shape.Rectangle ? padding + Button.RECTANGLE_PADDING : padding
+                left:
+                    options.shape === Button.shape.Rectangle
+                        ? padding + Button.RECTANGLE_PADDING
+                        : padding,
+                right:
+                    options.shape === Button.shape.Rectangle
+                        ? padding + Button.RECTANGLE_PADDING
+                        : padding,
             },
             margin: m,
             button_height: 16,

styles/src/component/icon_button.ts 🔗

@@ -11,11 +11,9 @@ export type Margin = {
 }
 
 interface IconButtonOptions {
-    layer?:
-    | Theme["lowest"]
-    | Theme["middle"]
-    | Theme["highest"]
+    layer?: Theme["lowest"] | Theme["middle"] | Theme["highest"]
     color?: keyof Theme["lowest"]
+    background_color?: keyof Theme["lowest"]
     margin?: Partial<Margin>
     variant?: Button.Variant
     size?: Button.Size
@@ -23,18 +21,25 @@ interface IconButtonOptions {
 
 type ToggleableIconButtonOptions = IconButtonOptions & {
     active_color?: keyof Theme["lowest"]
+    active_background_color?: keyof Theme["lowest"]
     active_layer?: Layer
+    active_variant?: Button.Variant
 }
 
-export function icon_button({ color, margin, layer, variant, size }: IconButtonOptions = {
-    variant: Button.variant.Default,
-    size: Button.size.Medium,
-}) {
+export function icon_button(
+    { color, background_color, margin, layer, variant, size }: IconButtonOptions = {
+        variant: Button.variant.Default,
+        size: Button.size.Medium,
+    }
+) {
     const theme = useTheme()
 
     if (!color) color = "base"
 
-    const background_color = variant === Button.variant.Ghost ? null : background(layer ?? theme.lowest, color)
+    const default_background =
+        variant === Button.variant.Ghost
+            ? null
+            : background(layer ?? theme.lowest, background_color ?? color)
 
     const m = {
         top: margin?.top ?? 0,
@@ -55,42 +60,51 @@ export function icon_button({ color, margin, layer, variant, size }: IconButtonO
             corner_radius: 6,
             padding: padding,
             margin: m,
-            icon_width: 12,
+            icon_width: 14,
             icon_height: 14,
             button_width: size === Button.size.Small ? 16 : 20,
             button_height: 14,
         },
         state: {
             default: {
-                background: background_color,
+                background: default_background,
                 color: foreground(layer ?? theme.lowest, color),
             },
             hovered: {
-                background: background(layer ?? theme.lowest, color, "hovered"),
+                background: background(layer ?? theme.lowest, background_color ?? color, "hovered"),
                 color: foreground(layer ?? theme.lowest, color, "hovered"),
             },
             clicked: {
-                background: background(layer ?? theme.lowest, color, "pressed"),
+                background: background(layer ?? theme.lowest, background_color ?? color, "pressed"),
                 color: foreground(layer ?? theme.lowest, color, "pressed"),
             },
         },
     })
 }
 
-export function toggleable_icon_button(
-    theme: Theme,
-    { color, active_color, margin, variant, size, active_layer }: ToggleableIconButtonOptions
-) {
+export function toggleable_icon_button({
+    color,
+    background_color,
+    active_color,
+    active_background_color,
+    active_variant,
+    margin,
+    variant,
+    size,
+    active_layer,
+}: ToggleableIconButtonOptions) {
     if (!color) color = "base"
 
     return toggleable({
         state: {
-            inactive: icon_button({ color, margin, variant, size }),
+            inactive: icon_button({ color, background_color, margin, variant, size }),
             active: icon_button({
                 color: active_color ? active_color : color,
+                background_color: active_background_color ? active_background_color : background_color,
                 margin,
                 layer: active_layer,
-                size
+                variant: active_variant || variant,
+                size,
             }),
         },
     })

styles/src/component/index.ts 🔗

@@ -0,0 +1,6 @@
+export * from "./icon_button"
+export * from "./indicator"
+export * from "./input"
+export * from "./tab"
+export * from "./tab_bar_button"
+export * from "./text_button"

styles/src/component/indicator.ts 🔗

@@ -1,7 +1,13 @@
 import { foreground } from "../style_tree/components"
 import { Layer, StyleSets } from "../theme"
 
-export const indicator = ({ layer, color }: { layer: Layer, color: StyleSets }) => ({
+export const indicator = ({
+    layer,
+    color,
+}: {
+    layer: Layer
+    color: StyleSets
+}) => ({
     corner_radius: 4,
     padding: 4,
     margin: { top: 12, left: 12 },

styles/src/component/tab.ts 🔗

@@ -9,7 +9,7 @@ type TabProps = {
 export const tab = ({ layer }: TabProps) => {
     const active_color = text(layer, "sans", "base").color
     const inactive_border: Border = {
-        color: '#FFFFFF00',
+        color: "#FFFFFF00",
         width: 1,
         bottom: true,
         left: false,
@@ -27,7 +27,7 @@ export const tab = ({ layer }: TabProps) => {
             top: 8,
             left: 8,
             right: 8,
-            bottom: 6
+            bottom: 6,
         },
         border: inactive_border,
     }
@@ -35,17 +35,17 @@ export const tab = ({ layer }: TabProps) => {
     const i = interactive({
         state: {
             default: {
-                ...base
+                ...base,
             },
             hovered: {
                 ...base,
-                ...text(layer, "sans", "base", "hovered")
+                ...text(layer, "sans", "base", "hovered"),
             },
             clicked: {
                 ...base,
-                ...text(layer, "sans", "base", "pressed")
+                ...text(layer, "sans", "base", "pressed"),
             },
-        }
+        },
     })
 
     return toggleable({
@@ -60,14 +60,14 @@ export const tab = ({ layer }: TabProps) => {
                 hovered: {
                     ...i,
                     ...text(layer, "sans", "base", "hovered"),
-                    border: active_border
+                    border: active_border,
                 },
                 clicked: {
                     ...i,
                     ...text(layer, "sans", "base", "pressed"),
-                    border: active_border
+                    border: active_border,
                 },
-            }
-        }
+            },
+        },
     })
 }

styles/src/component/tab_bar_button.ts 🔗

@@ -12,44 +12,47 @@ type TabBarButtonProps = TabBarButtonOptions & {
     state?: Partial<Record<InteractiveState, Partial<TabBarButtonOptions>>>
 }
 
-export function tab_bar_button(theme: Theme, { icon, color = "base" }: TabBarButtonProps) {
+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,
-                    },
+    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,
-                    },
+            },
+            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"),
-
-                    }
+        },
+        state: {
+            hovered: {
+                container: {
+                    background: background(theme.middle, color, "hovered"),
                 },
-                clicked: {
-                    container: {
-                        background: background(theme.middle, color, "pressed"),
-                    }
+            },
+            clicked: {
+                container: {
+                    background: background(theme.middle, color, "pressed"),
                 },
             },
-        })
-    )
+        },
+    })
 }

styles/src/component/text_button.ts 🔗

@@ -10,10 +10,7 @@ import { Button } from "./button"
 import { Margin } from "./icon_button"
 
 interface TextButtonOptions {
-    layer?:
-    | Theme["lowest"]
-    | Theme["middle"]
-    | Theme["highest"]
+    layer?: Theme["lowest"] | Theme["middle"] | Theme["highest"]
     variant?: Button.Variant
     color?: keyof Theme["lowest"]
     margin?: Partial<Margin>
@@ -36,7 +33,10 @@ export function text_button({
     const theme = useTheme()
     if (!color) color = "base"
 
-    const background_color = variant === Button.variant.Ghost ? null : background(layer ?? theme.lowest, color)
+    const background_color =
+        variant === Button.variant.Ghost
+            ? null
+            : background(layer ?? theme.lowest, color)
 
     const text_options: TextProperties = {
         size: "xs",
@@ -67,20 +67,38 @@ export function text_button({
         state: {
             default: {
                 background: background_color,
-                color:
-                    disabled
-                        ? foreground(layer ?? theme.lowest, "disabled")
-                        : foreground(layer ?? theme.lowest, color),
+                color: disabled
+                    ? foreground(layer ?? theme.lowest, "disabled")
+                    : foreground(layer ?? theme.lowest, color),
             },
-            hovered:
-                disabled ? {} : {
-                    background: background(layer ?? theme.lowest, color, "hovered"),
-                    color: foreground(layer ?? theme.lowest, color, "hovered"),
+            hovered: disabled
+                ? {}
+                : {
+                    background: background(
+                        layer ?? theme.lowest,
+                        color,
+                        "hovered"
+                    ),
+                    color: foreground(
+                        layer ?? theme.lowest,
+                        color,
+                        "hovered"
+                    ),
+                },
+            clicked: disabled
+                ? {}
+                : {
+                    background: background(
+                        layer ?? theme.lowest,
+                        color,
+                        "pressed"
+                    ),
+                    color: foreground(
+                        layer ?? theme.lowest,
+                        color,
+                        "pressed"
+                    ),
                 },
-            clicked: disabled ? {} : {
-                background: background(layer ?? theme.lowest, color, "pressed"),
-                color: foreground(layer ?? theme.lowest, color, "pressed"),
-            },
         },
     })
 }

styles/src/element/index.ts 🔗

@@ -1,4 +1,6 @@
 import { interactive, Interactive } from "./interactive"
 import { toggleable, Toggleable } from "./toggle"
 
+export * from "./padding"
+export * from "./margin"
 export { interactive, Interactive, toggleable, Toggleable }

styles/src/component/margin.ts → styles/src/element/margin.ts 🔗

@@ -16,19 +16,26 @@ export type MarginStyle = {
 export const margin_style = (options: MarginOptions): MarginStyle => {
     const { all, top, bottom, left, right } = options
 
-    if (all !== undefined) return {
-        top: all,
-        bottom: all,
-        left: all,
-        right: all
-    }
+    if (all !== undefined)
+        return {
+            top: all,
+            bottom: all,
+            left: all,
+            right: all,
+        }
 
-    if (top === undefined && bottom === undefined && left === undefined && right === undefined) throw new Error("Margin must have at least one value")
+    if (
+        top === undefined &&
+        bottom === undefined &&
+        left === undefined &&
+        right === undefined
+    )
+        throw new Error("Margin must have at least one value")
 
     return {
         top: top || 0,
         bottom: bottom || 0,
         left: left || 0,
-        right: right || 0
+        right: right || 0,
     }
 }

styles/src/component/padding.ts → styles/src/element/padding.ts 🔗

@@ -16,19 +16,26 @@ export type PaddingStyle = {
 export const padding_style = (options: PaddingOptions): PaddingStyle => {
     const { all, top, bottom, left, right } = options
 
-    if (all !== undefined) return {
-        top: all,
-        bottom: all,
-        left: all,
-        right: all
-    }
+    if (all !== undefined)
+        return {
+            top: all,
+            bottom: all,
+            left: all,
+            right: all,
+        }
 
-    if (top === undefined && bottom === undefined && left === undefined && right === undefined) throw new Error("Padding must have at least one value")
+    if (
+        top === undefined &&
+        bottom === undefined &&
+        left === undefined &&
+        right === undefined
+    )
+        throw new Error("Padding must have at least one value")
 
     return {
         top: top || 0,
         bottom: bottom || 0,
         left: left || 0,
-        right: right || 0
+        right: right || 0,
     }
 }

styles/src/style_tree/assistant.ts 🔗

@@ -8,50 +8,48 @@ type RoleCycleButton = TextStyle & {
 }
 // TODO: Replace these with zed types
 type RemainingTokens = TextStyle & {
-    background: string,
-    margin: { top: number, right: number },
+    background: string
+    margin: { top: number; right: number }
     padding: {
-        right: number,
-        left: number,
-        top: number,
-        bottom: number,
-    },
-    corner_radius: number,
+        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<RoleCycleButton> => {
-        return (
-            interactive({
-                base: {
+    const interactive_role = (
+        color: StyleSets
+    ): Interactive<RoleCycleButton> => {
+        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"),
                 },
-                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"),
-                    }
+                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 {
+            ...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 {
@@ -172,7 +170,10 @@ export default function assistant(): any {
                 base: {
                     background: background(theme.middle),
                     padding: { top: 4, bottom: 4 },
-                    border: border(theme.middle, "default", { top: true, overlay: true }),
+                    border: border(theme.middle, "default", {
+                        top: true,
+                        overlay: true,
+                    }),
                 },
                 state: {
                     hovered: {
@@ -180,7 +181,7 @@ export default function assistant(): any {
                     },
                     clicked: {
                         background: background(theme.middle, "pressed"),
-                    }
+                    },
                 },
             }),
             saved_at: {

styles/src/style_tree/collab_modals.ts 🔗

@@ -39,7 +39,12 @@ export default function channel_modal(): any {
             row_height: ITEM_HEIGHT,
             header: {
                 background: background(theme.lowest),
-                border: border(theme.middle, { "bottom": true, "top": false, left: false, right: false }),
+                border: border(theme.middle, {
+                    bottom: true,
+                    top: false,
+                    left: false,
+                    right: false,
+                }),
                 padding: {
                     top: SPACING,
                     left: SPACING - BUTTON_OFFSET,
@@ -48,7 +53,7 @@ export default function channel_modal(): any {
                 corner_radii: {
                     top_right: 12,
                     top_left: 12,
-                }
+                },
             },
             body: {
                 background: background(theme.middle),
@@ -57,12 +62,11 @@ export default function channel_modal(): any {
                     left: SPACING,
                     right: SPACING,
                     bottom: SPACING,
-
                 },
                 corner_radii: {
                     bottom_right: 12,
                     bottom_left: 12,
-                }
+                },
             },
             modal: {
                 background: background(theme.middle),
@@ -74,7 +78,6 @@ export default function channel_modal(): any {
                     right: 0,
                     top: 0,
                 },
-
             },
             // FIXME: due to a bug in the picker's size calculation, this must be 600
             max_height: 600,
@@ -83,7 +86,7 @@ export default function channel_modal(): any {
                 ...text(theme.middle, "sans", "on", { size: "lg" }),
                 padding: {
                     left: BUTTON_OFFSET,
-                }
+                },
             },
             picker: {
                 empty_container: {},
@@ -108,8 +111,8 @@ export default function channel_modal(): any {
                 background: background(theme.middle),
                 padding: {
                     left: 7,
-                    right: 7
-                }
+                    right: 7,
+                },
             },
             cancel_invite_button: {
                 ...text(theme.middle, "sans", { size: "xs" }),
@@ -125,7 +128,7 @@ export default function channel_modal(): any {
                 padding: {
                     left: 4,
                     right: 4,
-                }
+                },
             },
             contact_avatar: {
                 corner_radius: 10,
@@ -147,6 +150,6 @@ export default function channel_modal(): any {
                 background: background(theme.middle, "disabled"),
                 color: foreground(theme.middle, "disabled"),
             },
-        }
+        },
     }
 }

styles/src/style_tree/collab_panel.ts 🔗

@@ -27,7 +27,7 @@ export default function contacts_panel(): any {
         color: foreground(layer, "on"),
         icon_width: 14,
         button_width: 16,
-        corner_radius: 8
+        corner_radius: 8,
     }
 
     const project_row = {
@@ -61,7 +61,7 @@ export default function contacts_panel(): any {
         width: 14,
     }
 
-    const header_icon_button = toggleable_icon_button(theme, {
+    const header_icon_button = toggleable_icon_button({
         variant: "ghost",
         size: "sm",
         active_layer: theme.lowest,
@@ -275,7 +275,7 @@ export default function contacts_panel(): any {
         list_empty_label_container: {
             margin: {
                 left: NAME_MARGIN,
-            }
+            },
         },
         list_empty_icon: {
             color: foreground(layer, "variant"),
@@ -289,7 +289,7 @@ export default function contacts_panel(): any {
                         top: SPACING / 2,
                         bottom: SPACING / 2,
                         left: SPACING,
-                        right: SPACING
+                        right: SPACING,
                     },
                 },
                 state: {
@@ -330,7 +330,7 @@ export default function contacts_panel(): any {
                 right: 4,
             },
             background: background(layer, "hovered"),
-            ...text(layer, "sans", "hovered", { size: "xs" })
+            ...text(layer, "sans", "hovered", { size: "xs" }),
         },
         contact_status_free: indicator({ layer, color: "positive" }),
         contact_status_busy: indicator({ layer, color: "negative" }),
@@ -404,7 +404,7 @@ export default function contacts_panel(): any {
         channel_editor: {
             padding: {
                 left: NAME_MARGIN,
-            }
-        }
+            },
+        },
     }
 }

styles/src/style_tree/component_test.ts 🔗

@@ -1,4 +1,3 @@
-
 import { useTheme } from "../common"
 import { text_button } from "../component/text_button"
 import { icon_button } from "../component/icon_button"
@@ -14,14 +13,14 @@ export default function contacts_panel(): any {
             base: text_button({}),
             state: {
                 active: {
-                    ...text_button({ color: "accent" })
-                }
-            }
+                    ...text_button({ color: "accent" }),
+                },
+            },
         }),
         disclosure: {
             ...text(theme.lowest, "sans", "base"),
             button: icon_button({ variant: "ghost" }),
             spacing: 4,
-        }
+        },
     }
 }

styles/src/style_tree/editor.ts 🔗

@@ -307,7 +307,7 @@ export default function editor(): any {
                     ? with_opacity(theme.ramps.green(0.5).hex(), 0.8)
                     : with_opacity(theme.ramps.green(0.4).hex(), 0.8),
             },
-            selections: foreground(layer, "accent")
+            selections: foreground(layer, "accent"),
         },
         composition_mark: {
             underline: {

styles/src/style_tree/feedback.ts 🔗

@@ -37,7 +37,7 @@ export default function feedback(): any {
                     ...text(theme.highest, "mono", "on", "disabled"),
                     background: background(theme.highest, "on", "disabled"),
                     border: border(theme.highest, "on", "disabled"),
-                }
+                },
             },
         }),
         button_margin: 8,

styles/src/style_tree/project_panel.ts 🔗

@@ -64,17 +64,17 @@ export default function project_panel(): any {
         const unselected_default_style = merge(
             base_properties,
             unselected?.default ?? {},
-            {},
+            {}
         )
         const unselected_hovered_style = merge(
             base_properties,
             { background: background(theme.middle, "hovered") },
-            unselected?.hovered ?? {},
+            unselected?.hovered ?? {}
         )
         const unselected_clicked_style = merge(
             base_properties,
             { background: background(theme.middle, "pressed") },
-            unselected?.clicked ?? {},
+            unselected?.clicked ?? {}
         )
         const selected_default_style = merge(
             base_properties,
@@ -82,7 +82,7 @@ export default function project_panel(): any {
                 background: background(theme.lowest),
                 text: text(theme.lowest, "sans", { size: "sm" }),
             },
-            selected_style?.default ?? {},
+            selected_style?.default ?? {}
         )
         const selected_hovered_style = merge(
             base_properties,
@@ -90,7 +90,7 @@ export default function project_panel(): any {
                 background: background(theme.lowest, "hovered"),
                 text: text(theme.lowest, "sans", { size: "sm" }),
             },
-            selected_style?.hovered ?? {},
+            selected_style?.hovered ?? {}
         )
         const selected_clicked_style = merge(
             base_properties,
@@ -98,7 +98,7 @@ export default function project_panel(): any {
                 background: background(theme.lowest, "pressed"),
                 text: text(theme.lowest, "sans", { size: "sm" }),
             },
-            selected_style?.clicked ?? {},
+            selected_style?.clicked ?? {}
         )
 
         return toggleable({
@@ -175,7 +175,7 @@ export default function project_panel(): any {
                 default: {
                     icon_color: foreground(theme.middle, "variant"),
                 },
-            },
+            }
         ),
         cut_entry: entry(
             {
@@ -190,7 +190,7 @@ export default function project_panel(): any {
                         size: "sm",
                     }),
                 },
-            },
+            }
         ),
         filename_editor: {
             background: background(theme.middle, "on"),

styles/src/style_tree/search.ts 🔗

@@ -31,7 +31,7 @@ export default function search(): any {
         text: text(theme.highest, "mono", "default"),
         border: border(theme.highest),
         margin: {
-            right: 9,
+            right: SEARCH_ROW_SPACING,
         },
         padding: {
             top: 4,
@@ -48,7 +48,7 @@ export default function search(): any {
     }
 
     return {
-        padding: { top: 4, bottom: 4 },
+        padding: { top: 0, bottom: 0 },
 
         option_button: toggleable({
             base: interactive({
@@ -60,7 +60,8 @@ export default function search(): any {
                     corner_radius: 2,
                     margin: { right: 2 },
                     border: {
-                        width: 1., color: background(theme.highest, "on")
+                        width: 1,
+                        color: background(theme.highest, "on"),
                     },
                     padding: {
                         left: 4,
@@ -74,14 +75,16 @@ export default function search(): any {
                         ...text(theme.highest, "mono", "variant", "hovered"),
                         background: background(theme.highest, "on", "hovered"),
                         border: {
-                            width: 1., color: background(theme.highest, "on", "hovered")
+                            width: 1,
+                            color: background(theme.highest, "on", "hovered"),
                         },
                     },
                     clicked: {
                         ...text(theme.highest, "mono", "variant", "pressed"),
                         background: background(theme.highest, "on", "pressed"),
                         border: {
-                            width: 1., color: background(theme.highest, "on", "pressed")
+                            width: 1,
+                            color: background(theme.highest, "on", "pressed"),
                         },
                     },
                 },
@@ -96,11 +99,19 @@ export default function search(): any {
                         border: border(theme.highest, "accent"),
                     },
                     hovered: {
-                        background: background(theme.highest, "accent", "hovered"),
+                        background: background(
+                            theme.highest,
+                            "accent",
+                            "hovered"
+                        ),
                         border: border(theme.highest, "accent", "hovered"),
                     },
                     clicked: {
-                        background: background(theme.highest, "accent", "pressed"),
+                        background: background(
+                            theme.highest,
+                            "accent",
+                            "pressed"
+                        ),
                         border: border(theme.highest, "accent", "pressed"),
                     },
                 },
@@ -117,7 +128,8 @@ export default function search(): any {
                     corner_radius: 2,
                     margin: { right: 2 },
                     border: {
-                        width: 1., color: background(theme.highest, "on")
+                        width: 1,
+                        color: background(theme.highest, "on"),
                     },
                     padding: {
                         left: 4,
@@ -131,14 +143,16 @@ export default function search(): any {
                         ...text(theme.highest, "mono", "variant", "hovered"),
                         background: background(theme.highest, "on", "hovered"),
                         border: {
-                            width: 1., color: background(theme.highest, "on", "hovered")
+                            width: 1,
+                            color: background(theme.highest, "on", "hovered"),
                         },
                     },
                     clicked: {
                         ...text(theme.highest, "mono", "variant", "pressed"),
                         background: background(theme.highest, "on", "pressed"),
                         border: {
-                            width: 1., color: background(theme.highest, "on", "pressed")
+                            width: 1,
+                            color: background(theme.highest, "on", "pressed"),
                         },
                     },
                 },
@@ -153,11 +167,19 @@ export default function search(): any {
                         border: border(theme.highest, "accent"),
                     },
                     hovered: {
-                        background: background(theme.highest, "accent", "hovered"),
+                        background: background(
+                            theme.highest,
+                            "accent",
+                            "hovered"
+                        ),
                         border: border(theme.highest, "accent", "hovered"),
                     },
                     clicked: {
-                        background: background(theme.highest, "accent", "pressed"),
+                        background: background(
+                            theme.highest,
+                            "accent",
+                            "pressed"
+                        ),
                         border: border(theme.highest, "accent", "pressed"),
                     },
                 },
@@ -168,9 +190,20 @@ export default function search(): any {
         // Disabled elements should use a disabled state of an interactive element, not a toggleable element with the inactive state being disabled
         action_button: toggleable({
             state: {
-                inactive: text_button({ variant: "ghost", layer: theme.highest, disabled: true, margin: { right: SEARCH_ROW_SPACING }, text_properties: { size: "sm" } }),
-                active: text_button({ variant: "ghost", layer: theme.highest, margin: { right: SEARCH_ROW_SPACING }, text_properties: { size: "sm" } })
-            }
+                inactive: text_button({
+                    variant: "ghost",
+                    layer: theme.highest,
+                    disabled: true,
+                    margin: { right: SEARCH_ROW_SPACING },
+                    text_properties: { size: "sm" },
+                }),
+                active: text_button({
+                    variant: "ghost",
+                    layer: theme.highest,
+                    margin: { right: SEARCH_ROW_SPACING },
+                    text_properties: { size: "sm" },
+                }),
+            },
         }),
         editor,
         invalid_editor: {
@@ -216,12 +249,12 @@ export default function search(): any {
                 dimensions: {
                     width: 14,
                     height: 14,
-                }
+                },
             },
             container: {
                 margin: { right: 4 },
                 padding: { left: 1, right: 1 },
-            }
+            },
         },
         // Toggle group buttons - Text | Regex | Semantic
         mode_button: toggleable({
@@ -233,27 +266,39 @@ export default function search(): any {
                     border: {
                         ...border(theme.highest, "on"),
                         left: false,
-                        right: false
+                        right: false,
                     },
                     margin: {
                         top: 1,
                         bottom: 1,
                     },
                     padding: {
-                        left: 12,
-                        right: 12,
+                        left: 10,
+                        right: 10,
                     },
                     corner_radius: 6,
                 },
                 state: {
                     hovered: {
-                        ...text(theme.highest, "mono", "variant", "hovered", { size: "sm" }),
-                        background: background(theme.highest, "variant", "hovered"),
+                        ...text(theme.highest, "mono", "variant", "hovered", {
+                            size: "sm",
+                        }),
+                        background: background(
+                            theme.highest,
+                            "variant",
+                            "hovered"
+                        ),
                         border: border(theme.highest, "on", "hovered"),
                     },
                     clicked: {
-                        ...text(theme.highest, "mono", "variant", "pressed", { size: "sm" }),
-                        background: background(theme.highest, "variant", "pressed"),
+                        ...text(theme.highest, "mono", "variant", "pressed", {
+                            size: "sm",
+                        }),
+                        background: background(
+                            theme.highest,
+                            "variant",
+                            "pressed"
+                        ),
                         border: border(theme.highest, "on", "pressed"),
                     },
                 },
@@ -262,15 +307,19 @@ export default function search(): any {
                 active: {
                     default: {
                         ...text(theme.highest, "mono", "on", { size: "sm" }),
-                        background: background(theme.highest, "on")
+                        background: background(theme.highest, "on"),
                     },
                     hovered: {
-                        ...text(theme.highest, "mono", "on", "hovered", { size: "sm" }),
-                        background: background(theme.highest, "on", "hovered")
+                        ...text(theme.highest, "mono", "on", "hovered", {
+                            size: "sm",
+                        }),
+                        background: background(theme.highest, "on", "hovered"),
                     },
                     clicked: {
-                        ...text(theme.highest, "mono", "on", "pressed", { size: "sm" }),
-                        background: background(theme.highest, "on", "pressed")
+                        ...text(theme.highest, "mono", "on", "pressed", {
+                            size: "sm",
+                        }),
+                        background: background(theme.highest, "on", "pressed"),
                     },
                 },
             },
@@ -300,8 +349,8 @@ export default function search(): any {
                         },
                     },
                     state: {
-                        hovered: {}
-                    }
+                        hovered: {},
+                    },
                 }),
                 active: interactive({
                     base: {
@@ -325,22 +374,30 @@ export default function search(): any {
                     state: {
                         hovered: {
                             ...text(theme.highest, "mono", "on", "hovered"),
-                            background: background(theme.highest, "on", "hovered"),
+                            background: background(
+                                theme.highest,
+                                "on",
+                                "hovered"
+                            ),
                             border: border(theme.highest, "on", "hovered"),
                         },
                         clicked: {
                             ...text(theme.highest, "mono", "on", "pressed"),
-                            background: background(theme.highest, "on", "pressed"),
+                            background: background(
+                                theme.highest,
+                                "on",
+                                "pressed"
+                            ),
                             border: border(theme.highest, "on", "pressed"),
                         },
                     },
-                })
-            }
+                }),
+            },
         }),
-        search_bar_row_height: 34,
+        search_bar_row_height: 32,
         search_row_spacing: 8,
         option_button_height: 22,
         modes_container: {},
-        ...search_results()
+        ...search_results(),
     }
 }

styles/src/style_tree/status_bar.ts 🔗

@@ -34,9 +34,11 @@ export default function status_bar(): any {
             ...text(layer, "mono", "base", { size: "xs" }),
         },
         active_language: text_button({
-            color: "base"
+            color: "base",
+        }),
+        auto_update_progress_message: text(layer, "sans", "base", {
+            size: "xs",
         }),
-        auto_update_progress_message: text(layer, "sans", "base", { size: "xs" }),
         auto_update_done_message: text(layer, "sans", "base", { size: "xs" }),
         lsp_status: interactive({
             base: {
@@ -73,34 +75,36 @@ export default function status_bar(): any {
                 icon_color_error: foreground(layer, "negative"),
                 container_ok: {
                     corner_radius: 6,
-                    padding: { top: 3, bottom: 3, left: 7, right: 7 },
-                },
-                container_warning: {
-                    ...diagnostic_status_container,
-                    background: background(layer, "warning"),
-                    border: border(layer, "warning"),
-                },
-                container_error: {
-                    ...diagnostic_status_container,
-                    background: background(layer, "negative"),
-                    border: border(layer, "negative"),
+                    padding: { top: 2, bottom: 2, left: 6, right: 6 },
                 },
+                container_warning: diagnostic_status_container,
+                container_error: diagnostic_status_container
             },
             state: {
                 hovered: {
                     icon_color_ok: foreground(layer, "on"),
                     container_ok: {
-                        background: background(layer, "on", "hovered"),
+                        background: background(layer, "hovered")
                     },
                     container_warning: {
-                        background: background(layer, "warning", "hovered"),
-                        border: border(layer, "warning", "hovered"),
+                        background: background(layer, "hovered")
                     },
                     container_error: {
-                        background: background(layer, "negative", "hovered"),
-                        border: border(layer, "negative", "hovered"),
+                        background: background(layer, "hovered")
                     },
                 },
+                clicked: {
+                    icon_color_ok: foreground(layer, "on"),
+                    container_ok: {
+                        background: background(layer, "pressed")
+                    },
+                    container_warning: {
+                        background: background(layer, "pressed")
+                    },
+                    container_error: {
+                        background: background(layer, "pressed")
+                    }
+                }
             },
         }),
         panel_buttons: {
@@ -125,7 +129,7 @@ export default function status_bar(): any {
                         },
                         clicked: {
                             background: background(layer, "pressed"),
-                        }
+                        },
                     },
                 }),
                 state: {

styles/src/style_tree/tab_bar.ts 🔗

@@ -93,7 +93,7 @@ export default function tab_bar(): any {
             border: border(theme.lowest, "on", {
                 bottom: true,
                 overlay: true,
-            })
+            }),
         },
         state: {
             hovered: {
@@ -101,7 +101,7 @@ export default function tab_bar(): any {
                 background: background(theme.highest, "on", "hovered"),
             },
             disabled: {
-                color: foreground(theme.highest, "on", "disabled")
+                color: foreground(theme.highest, "on", "disabled"),
             },
         },
     })
@@ -162,6 +162,6 @@ export default function tab_bar(): any {
                 right: false,
             },
         },
-        nav_button: nav_button
+        nav_button: nav_button,
     }
 }

styles/src/style_tree/titlebar.ts 🔗

@@ -1,8 +1,6 @@
-import { icon_button, toggleable_icon_button } from "../component/icon_button"
-import { toggleable_text_button } from "../component/text_button"
+import { icon_button, toggleable_icon_button, toggleable_text_button } from "../component"
 import { interactive, toggleable } from "../element"
-import { useTheme } from "../theme"
-import { with_opacity } from "../theme/color"
+import { useTheme, with_opacity } from "../theme"
 import { background, border, foreground, text } from "./components"
 
 const ITEM_SPACING = 8
@@ -34,16 +32,17 @@ function call_controls() {
     }
 
     return {
-        toggle_microphone_button: toggleable_icon_button(theme, {
+        toggle_microphone_button: toggleable_icon_button({
             margin: {
                 ...margin_y,
                 left: space.group,
                 right: space.half_item,
             },
             active_color: "negative",
+            active_background_color: "negative",
         }),
 
-        toggle_speakers_button: toggleable_icon_button(theme, {
+        toggle_speakers_button: toggleable_icon_button({
             margin: {
                 ...margin_y,
                 left: space.half_item,
@@ -51,13 +50,14 @@ function call_controls() {
             },
         }),
 
-        screen_share_button: toggleable_icon_button(theme, {
+        screen_share_button: toggleable_icon_button({
             margin: {
                 ...margin_y,
                 left: space.half_item,
                 right: space.group,
             },
             active_color: "accent",
+            active_background_color: "accent",
         }),
 
         muted: foreground(theme.lowest, "negative"),
@@ -183,14 +183,12 @@ export function titlebar(): any {
             height: 400,
         },
 
-        // Project
-        project_name_divider: text(theme.lowest, "sans", "variant"),
-
         project_menu_button: toggleable_text_button(theme, {
-            color: 'base',
+            color: "base"
         }),
+
         git_menu_button: toggleable_text_button(theme, {
-            color: 'variant',
+            color: "variant",
         }),
 
         // Collaborators
@@ -263,7 +261,7 @@ export function titlebar(): any {
 
         ...call_controls(),
 
-        toggle_contacts_button: toggleable_icon_button(theme, {
+        toggle_contacts_button: toggleable_icon_button({
             margin: {
                 left: ITEM_SPACING,
             },

styles/src/style_tree/toolbar.ts 🔗

@@ -0,0 +1,38 @@
+import { useTheme } from "../common"
+import { toggleable_icon_button } from "../component/icon_button"
+import { interactive } from "../element"
+import { background, border, foreground, text } from "./components"
+
+export const toolbar = () => {
+    const theme = useTheme()
+
+    return {
+        height: 32,
+        padding: { left: 4, right: 4, top: 4, bottom: 4 },
+        background: background(theme.highest),
+        border: border(theme.highest, { bottom: true }),
+        item_spacing: 4,
+        toggleable_tool: toggleable_icon_button({
+            margin: { left: 4 },
+            variant: "ghost",
+            active_color: "accent",
+        }),
+        breadcrumb_height: 24,
+        breadcrumbs: interactive({
+            base: {
+                ...text(theme.highest, "sans", "variant"),
+                corner_radius: 6,
+                padding: {
+                    left: 6,
+                    right: 6,
+                },
+            },
+            state: {
+                hovered: {
+                    color: foreground(theme.highest, "on", "hovered"),
+                    background: background(theme.highest, "on", "hovered"),
+                },
+            },
+        }),
+    }
+}

styles/src/style_tree/workspace.ts 🔗

@@ -12,7 +12,7 @@ import tabBar from "./tab_bar"
 import { interactive } from "../element"
 import { titlebar } from "./titlebar"
 import { useTheme } from "../theme"
-import { toggleable_icon_button } from "../component/icon_button"
+import { toolbar } from "./toolbar"
 
 export default function workspace(): any {
     const theme = useTheme()
@@ -128,35 +128,7 @@ export default function workspace(): any {
         },
         status_bar: statusBar(),
         titlebar: titlebar(),
-        toolbar: {
-            height: 42,
-            background: background(theme.highest),
-            border: border(theme.highest, { bottom: true }),
-            item_spacing: 8,
-            toggleable_tool: toggleable_icon_button(theme, {
-                margin: { left: 8 },
-                variant: "ghost",
-                active_color: "accent",
-            }),
-            padding: { left: 8, right: 8 },
-        },
-        breadcrumb_height: 24,
-        breadcrumbs: interactive({
-            base: {
-                ...text(theme.highest, "sans", "variant"),
-                corner_radius: 6,
-                padding: {
-                    left: 6,
-                    right: 6,
-                },
-            },
-            state: {
-                hovered: {
-                    color: foreground(theme.highest, "on", "hovered"),
-                    background: background(theme.highest, "on", "hovered"),
-                },
-            },
-        }),
+        toolbar: toolbar(),
         disconnected_overlay: {
             ...text(theme.lowest, "sans"),
             background: with_opacity(background(theme.lowest), 0.8),

styles/src/theme/create_theme.ts 🔗

@@ -13,16 +13,16 @@ export interface Theme {
     is_light: boolean
 
     /**
-    * App background, other elements that should sit directly on top of the background.
-    */
+     * 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.
-    */
+     * Panels, tabs, other UI surfaces that sit on top of the background.
+     */
     middle: Layer
     /**
-    * Editors like code buffers, conversation editors, etc.
-    */
+     * Editors like code buffers, conversation editors, etc.
+     */
     highest: Layer
 
     ramps: RampSet
@@ -206,7 +206,10 @@ function build_color_family(ramps: RampSet): ColorFamily {
     for (const ramp in ramps) {
         const ramp_value = ramps[ramp as keyof RampSet]
 
-        const lightnessValues = [ramp_value(0).get('hsl.l') * 100, ramp_value(1).get('hsl.l') * 100]
+        const lightnessValues = [
+            ramp_value(0).get("hsl.l") * 100,
+            ramp_value(1).get("hsl.l") * 100,
+        ]
         const low = Math.min(...lightnessValues)
         const high = Math.max(...lightnessValues)
         const range = high - low

styles/src/theme/index.ts 🔗

@@ -23,3 +23,4 @@ export * from "./create_theme"
 export * from "./ramps"
 export * from "./syntax"
 export * from "./theme_config"
+export * from "./color"

styles/src/theme/tokens/theme.ts 🔗

@@ -4,11 +4,7 @@ import {
     SingleOtherToken,
     TokenTypes,
 } from "@tokens-studio/types"
-import {
-    Shadow,
-    SyntaxHighlightStyle,
-    ThemeSyntax,
-} from "../create_theme"
+import { Shadow, SyntaxHighlightStyle, ThemeSyntax } from "../create_theme"
 import { LayerToken, layer_token } from "./layer"
 import { PlayersToken, players_token } from "./players"
 import { color_token } from "./token"

styles/tsconfig.json 🔗

@@ -23,7 +23,5 @@
         "skipLibCheck": true,
         "useUnknownInCatchVariables": false
     },
-    "exclude": [
-        "node_modules"
-    ]
+    "exclude": ["node_modules"]
 }