Merge pull request #921 from zed-industries/new-status-bar-design

Max Brunsfeld created

Style the status bar according to the latest design

Change summary

assets/icons/contacts-solid-14.svg         |   3 
assets/icons/download-solid-14.svg         |   3 
assets/icons/error-solid-14.svg            |   3 
assets/icons/folder-tree-16.svg            |   1 
assets/icons/folder-tree-solid-14.svg      |   3 
assets/icons/no-error-solid-14.svg         |   3 
assets/icons/user-16.svg                   |   3 
assets/icons/warning-solid-14.svg          |   3 
assets/themes/cave-dark.json               | 280 ++++++++++++++---------
assets/themes/cave-light.json              | 280 ++++++++++++++---------
assets/themes/dark.json                    | 282 ++++++++++++++---------
assets/themes/light.json                   | 282 ++++++++++++++---------
assets/themes/solarized-dark.json          | 280 ++++++++++++++---------
assets/themes/solarized-light.json         | 280 ++++++++++++++---------
assets/themes/sulphurpool-dark.json        | 280 ++++++++++++++---------
assets/themes/sulphurpool-light.json       | 280 ++++++++++++++---------
crates/diagnostics/src/diagnostics.rs      |   1 
crates/diagnostics/src/items.rs            | 213 +++++++++++++++--
crates/editor/src/items.rs                 |  74 ------
crates/theme/src/theme.rs                  | 147 ++++++++++--
crates/workspace/src/lsp_status.rs         | 101 +++++---
crates/workspace/src/sidebar.rs            | 242 ++++++++++++--------
crates/workspace/src/status_bar.rs         |   4 
crates/workspace/src/workspace.rs          | 135 ++++++----
crates/zed/src/zed.rs                      |  36 +-
styles/src/styleTree/app.ts                |  14 -
styles/src/styleTree/projectDiagnostics.ts |  15 +
styles/src/styleTree/workspace.ts          | 190 ++++++++++------
styles/src/themes/base16.ts                |  40 +-
styles/src/themes/dark.ts                  |  44 +-
styles/src/themes/light.ts                 |  44 +-
31 files changed, 2,154 insertions(+), 1,412 deletions(-)

Detailed changes

assets/icons/contacts-solid-14.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5.2 7C6.52563 7 7.6 5.92544 7.6 4.6C7.6 3.27456 6.52563 2.2 5.2 2.2C3.87438 2.2 2.8 3.27456 2.8 4.6C2.8 5.92544 3.87438 7 5.2 7ZM6.15063 7.9H4.24938C2.45444 7.9 1 9.355 1 11.1494C1 11.5094 1.291 11.8 1.64988 11.8H8.74938C9.10938 11.8 9.4 11.5094 9.4 11.1494C9.4 9.355 7.945 7.9 6.15063 7.9ZM9.98313 8.2H8.59844C9.46 8.90688 10 9.96438 10 11.1494C10 11.3894 9.92875 11.6106 9.8125 11.8H12.4C12.7319 11.8 13 11.53 13 11.1831C13 9.5425 11.6575 8.2 9.98313 8.2ZM9.1 7C10.2606 7 11.2 6.06063 11.2 4.9C11.2 3.73938 10.2606 2.8 9.1 2.8C8.62919 2.8 8.19925 2.96041 7.849 3.22206C8.065 3.63681 8.2 4.10125 8.2 4.6C8.2 5.266 7.97631 5.87763 7.60769 6.37581C7.98813 6.76 8.515 7 9.1 7Z" fill="white"/>
+</svg>

assets/icons/download-solid-14.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4 10C3.58579 10 3.25 10.3358 3.25 10.75C3.25 11.1642 3.58579 11.5 4 11.5V10ZM10 11.5C10.4142 11.5 10.75 11.1642 10.75 10.75C10.75 10.3358 10.4142 10 10 10V11.5ZM7.75 3.5C7.75 3.08579 7.41421 2.75 7 2.75C6.58579 2.75 6.25 3.08579 6.25 3.5H7.75ZM7 8L6.51191 8.56944C6.79277 8.81019 7.20723 8.81019 7.48809 8.56944L7 8ZM9.23809 7.06944C9.55259 6.79988 9.58901 6.3264 9.31944 6.01191C9.04988 5.69741 8.5764 5.66099 8.26191 5.93056L9.23809 7.06944ZM5.73809 5.93056C5.4236 5.66099 4.95012 5.69741 4.68056 6.01191C4.41099 6.3264 4.44741 6.79988 4.76191 7.06944L5.73809 5.93056ZM4 11.5H10V10H4V11.5ZM6.25 3.5V8H7.75V3.5H6.25ZM7.48809 8.56944L9.23809 7.06944L8.26191 5.93056L6.51191 7.43056L7.48809 8.56944ZM7.48809 7.43056L5.73809 5.93056L4.76191 7.06944L6.51191 8.56944L7.48809 7.43056Z" fill="white"/>
+</svg>

assets/icons/error-solid-14.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7 11.5C9.48528 11.5 11.5 9.48528 11.5 7C11.5 4.51472 9.48528 2.5 7 2.5C4.51472 2.5 2.5 4.51472 2.5 7C2.5 9.48528 4.51472 11.5 7 11.5ZM4.91475 5.71025L6.2045 7L4.91475 8.28975L5.71025 9.08525L7 7.79549L8.28975 9.08525L9.08525 8.28975L7.79549 7L9.08525 5.71025L8.28975 4.91475L7 6.2045L5.71025 4.91475L4.91475 5.71025Z" fill="white"/>
+</svg>

assets/icons/folder-tree-solid-14.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.3333 2.33333H10L9.33333 1.66666H7.66667C7.29958 1.66666 7 1.96625 7 2.33333V5.66666C7 6.03375 7.29958 6.33333 7.66667 6.33333H12.3333C12.7004 6.33333 13 6.03375 13 5.66666V3C13 2.63291 12.7 2.33333 12.3333 2.33333ZM12.3333 8.33333H10L9.33333 7.66666H7.66667C7.29958 7.66666 7 7.96625 7 8.33333V11.6667C7 12.0337 7.29958 12.3333 7.66667 12.3333H12.3333C12.7004 12.3333 13 12.0337 13 11.6667V9C13 8.63333 12.7 8.33333 12.3333 8.33333ZM2.33333 2C2.33333 1.8151 2.185 1.66666 2 1.66666H1.33333C1.14844 1.66666 1 1.815 1 2V10.3333C1 10.7004 1.29958 11 1.66667 11H6.33333V9.66666H2.33333V5H6.33333V3.66666H2.33333V2Z" fill="white"/>
+</svg>

assets/icons/no-error-solid-14.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7 11.5C9.48528 11.5 11.5 9.48528 11.5 7C11.5 4.51472 9.48528 2.5 7 2.5C4.51472 2.5 2.5 4.51472 2.5 7C2.5 9.48528 4.51472 11.5 7 11.5ZM9.64775 5.71025L8.85225 4.91475L6.15625 7.61076L5.14775 6.60225L4.35225 7.39775L6.15625 9.20174L9.64775 5.71025Z" fill="white"/>
+</svg>

assets/icons/user-16.svg 🔗

@@ -1,3 +0,0 @@
-<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M9.3125 9.3125H6.6875C4.02969 9.3125 1.875 11.4672 1.875 14.125C1.875 14.6082 2.26684 15 2.75 15H13.25C13.7332 15 14.125 14.6082 14.125 14.125C14.125 11.4672 11.9703 9.3125 9.3125 9.3125ZM3.21457 13.6875C3.43059 11.9621 4.90469 10.625 6.6875 10.625H9.3125C11.0942 10.625 12.5691 11.9635 12.7852 13.6875H3.21457ZM8 8C9.93293 8 11.5 6.43293 11.5 4.5C11.5 2.56707 9.93293 1 8 1C6.06707 1 4.5 2.56707 4.5 4.5C4.5 6.4332 6.0668 8 8 8ZM8 2.3125C9.20613 2.3125 10.1875 3.29387 10.1875 4.5C10.1875 5.70613 9.20613 6.6875 8 6.6875C6.79387 6.6875 5.8125 5.70586 5.8125 4.5C5.8125 3.29387 6.79414 2.3125 8 2.3125Z" fill="#9BA8BE"/>
-</svg>

assets/icons/warning-solid-14.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5 11.5H2.5V9.7L6.4375 2.5H7.5625L11.5 9.7V11.5ZM6.4375 4.9H7.5625V7.9H6.4375V4.9ZM6.4375 9.1H7.5625V10.3H6.4375V9.1Z" fill="white"/>
+</svg>

assets/themes/cave-dark.json 🔗

@@ -161,54 +161,10 @@
       },
       "cursor": "Arrow"
     },
-    "left_sidebar": {
-      "width": 30,
-      "background": "#26232a",
-      "border": {
-        "color": "#19171c",
-        "width": 1,
-        "right": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#8b8792",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#efecf4",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#19171c",
-        "padding": {
-          "left": 1
-        }
-      }
-    },
-    "right_sidebar": {
-      "width": 30,
-      "background": "#26232a",
-      "border": {
-        "color": "#19171c",
-        "width": 1,
-        "left": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#8b8792",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#efecf4",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#19171c",
-        "padding": {
-          "left": 1
-        }
+    "sidebar_resize_handle": {
+      "background": "#19171c",
+      "padding": {
+        "left": 1
       }
     },
     "pane_divider": {
@@ -216,9 +172,11 @@
       "width": 1
     },
     "status_bar": {
-      "height": 24,
+      "height": 30,
       "item_spacing": 8,
       "padding": {
+        "top": 1,
+        "bottom": 1,
         "left": 6,
         "right": 6
       },
@@ -233,25 +191,154 @@
         "color": "#8b8792",
         "size": 14
       },
-      "diagnostic_message": {
+      "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#8b8792",
         "size": 14
       },
-      "lsp_message": {
+      "auto_update_done_message": {
         "family": "Zed Sans",
         "color": "#8b8792",
         "size": 14
       },
-      "auto_update_progress_message": {
-        "family": "Zed Sans",
-        "color": "#8b8792",
-        "size": 14
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "message": {
+          "family": "Zed Sans",
+          "color": "#8b8792",
+          "size": 14
+        },
+        "icon_color": "#8b8792",
+        "hover": {
+          "message": {
+            "family": "Zed Sans",
+            "color": "#e2dfe7",
+            "size": 14
+          },
+          "icon_color": "#e2dfe7",
+          "background": "#58526052"
+        }
       },
-      "auto_update_done_message": {
+      "diagnostic_message": {
         "family": "Zed Sans",
         "color": "#8b8792",
-        "size": 14
+        "size": 14,
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#8b8792",
+          "size": 14
+        }
+      },
+      "diagnostic_summary": {
+        "height": 16,
+        "icon_width": 14,
+        "icon_spacing": 2,
+        "summary_spacing": 6,
+        "text": {
+          "family": "Zed Sans",
+          "color": "#e2dfe7",
+          "size": 14
+        },
+        "icon_color_ok": "#8b8792",
+        "icon_color_warning": "#a06e3b",
+        "icon_color_error": "#be4678",
+        "container_ok": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#58526052"
+        },
+        "container_warning": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#a06e3b26",
+          "border": {
+            "color": "#a06e3b26",
+            "width": 1
+          }
+        },
+        "container_error": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#be467826",
+          "border": {
+            "color": "#be467826",
+            "width": 1
+          }
+        },
+        "hover": {
+          "icon_color_ok": "#e2dfe7",
+          "container_ok": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#58526052"
+          },
+          "container_warning": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#a06e3b33",
+            "border": {
+              "color": "#a06e3b26",
+              "width": 1
+            }
+          },
+          "container_error": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#be467833",
+            "border": {
+              "color": "#be467826",
+              "width": 1
+            }
+          }
+        }
+      },
+      "sidebar_buttons": {
+        "group_left": {},
+        "group_right": {},
+        "item": {
+          "icon_size": 14,
+          "padding": {
+            "top": 3,
+            "bottom": 3,
+            "left": 6,
+            "right": 6
+          },
+          "corner_radius": 6,
+          "icon_color": "#8b8792",
+          "hover": {
+            "icon_color": "#e2dfe7",
+            "background": "#58526052"
+          },
+          "active": {
+            "icon_color": "#efecf4",
+            "background": "#5852607a"
+          }
+        }
       }
     },
     "titlebar": {
@@ -283,9 +370,6 @@
         "bottom": true
       },
       "sign_in_prompt": {
-        "family": "Zed Sans",
-        "color": "#8b8792",
-        "size": 12,
         "border": {
           "color": "#19171c",
           "width": 1
@@ -298,24 +382,14 @@
         "padding": {
           "left": 6,
           "right": 6
-        }
-      },
-      "hovered_sign_in_prompt": {
+        },
         "family": "Zed Sans",
-        "color": "#efecf4",
+        "color": "#8b8792",
         "size": 12,
-        "border": {
-          "color": "#19171c",
-          "width": 1
-        },
-        "corner_radius": 6,
-        "margin": {
-          "top": 1,
-          "right": 6
-        },
-        "padding": {
-          "left": 6,
-          "right": 6
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#efecf4",
+          "size": 12
         }
       },
       "offline_icon": {
@@ -326,39 +400,24 @@
         }
       },
       "share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
-        },
         "corner_radius": 6,
-        "color": "#8b8792"
-      },
-      "hovered_share_icon": {
         "margin": {
           "top": 3,
           "bottom": 2
         },
-        "corner_radius": 6,
-        "background": "#58526052",
-        "color": "#8b8792"
-      },
-      "hovered_active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "color": "#8b8792",
+        "hover": {
+          "background": "#58526052",
+          "color": "#8b8792"
         },
-        "corner_radius": 6,
-        "background": "#58526052",
-        "color": "#efecf4"
-      },
-      "active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "active": {
+          "background": "#5852607a",
+          "color": "#efecf4"
         },
-        "corner_radius": 6,
-        "background": "#5852607a",
-        "color": "#efecf4"
+        "active_hover": {
+          "background": "#58526052",
+          "color": "#efecf4"
+        }
       },
       "outdated_warning": {
         "family": "Zed Sans",
@@ -405,8 +464,8 @@
     "background": "#19171c",
     "active_line_background": "#efecf412",
     "code_actions_indicator": "#8b8792",
-    "diff_background_deleted": "#be4678",
-    "diff_background_inserted": "#2a9292",
+    "diff_background_deleted": "#be467826",
+    "diff_background_inserted": "#2a929226",
     "document_highlight_read_background": "#19171c1f",
     "document_highlight_write_background": "#19171c29",
     "error_color": "#be4678",
@@ -816,21 +875,14 @@
     }
   },
   "project_diagnostics": {
+    "background": "#19171c",
     "tab_icon_spacing": 4,
     "tab_icon_width": 13,
     "tab_summary_spacing": 10,
     "empty_message": {
-      "family": "Zed Sans",
-      "color": "#e2dfe7",
-      "size": 18
-    },
-    "status_bar_item": {
       "family": "Zed Sans",
       "color": "#8b8792",
-      "size": 14,
-      "margin": {
-        "right": 10
-      }
+      "size": 16
     }
   },
   "command_palette": {
@@ -1372,7 +1424,7 @@
         "size": 14
       },
       "border": {
-        "color": "#be4678",
+        "color": "#be467826",
         "width": 1
       },
       "margin": {

assets/themes/cave-light.json 🔗

@@ -161,54 +161,10 @@
       },
       "cursor": "Arrow"
     },
-    "left_sidebar": {
-      "width": 30,
-      "background": "#e2dfe7",
-      "border": {
-        "color": "#efecf4",
-        "width": 1,
-        "right": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#585260",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#19171c",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#efecf4",
-        "padding": {
-          "left": 1
-        }
-      }
-    },
-    "right_sidebar": {
-      "width": 30,
-      "background": "#e2dfe7",
-      "border": {
-        "color": "#efecf4",
-        "width": 1,
-        "left": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#585260",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#19171c",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#efecf4",
-        "padding": {
-          "left": 1
-        }
+    "sidebar_resize_handle": {
+      "background": "#efecf4",
+      "padding": {
+        "left": 1
       }
     },
     "pane_divider": {
@@ -216,9 +172,11 @@
       "width": 1
     },
     "status_bar": {
-      "height": 24,
+      "height": 30,
       "item_spacing": 8,
       "padding": {
+        "top": 1,
+        "bottom": 1,
         "left": 6,
         "right": 6
       },
@@ -233,25 +191,154 @@
         "color": "#585260",
         "size": 14
       },
-      "diagnostic_message": {
+      "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#585260",
         "size": 14
       },
-      "lsp_message": {
+      "auto_update_done_message": {
         "family": "Zed Sans",
         "color": "#585260",
         "size": 14
       },
-      "auto_update_progress_message": {
-        "family": "Zed Sans",
-        "color": "#585260",
-        "size": 14
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "message": {
+          "family": "Zed Sans",
+          "color": "#585260",
+          "size": 14
+        },
+        "icon_color": "#585260",
+        "hover": {
+          "message": {
+            "family": "Zed Sans",
+            "color": "#26232a",
+            "size": 14
+          },
+          "icon_color": "#26232a",
+          "background": "#8b87921f"
+        }
       },
-      "auto_update_done_message": {
+      "diagnostic_message": {
         "family": "Zed Sans",
         "color": "#585260",
-        "size": 14
+        "size": 14,
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#585260",
+          "size": 14
+        }
+      },
+      "diagnostic_summary": {
+        "height": 16,
+        "icon_width": 14,
+        "icon_spacing": 2,
+        "summary_spacing": 6,
+        "text": {
+          "family": "Zed Sans",
+          "color": "#26232a",
+          "size": 14
+        },
+        "icon_color_ok": "#585260",
+        "icon_color_warning": "#a06e3b",
+        "icon_color_error": "#be4678",
+        "container_ok": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#8b87921f"
+        },
+        "container_warning": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#a06e3b26",
+          "border": {
+            "color": "#a06e3b26",
+            "width": 1
+          }
+        },
+        "container_error": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#be467826",
+          "border": {
+            "color": "#be467826",
+            "width": 1
+          }
+        },
+        "hover": {
+          "icon_color_ok": "#26232a",
+          "container_ok": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#8b87921f"
+          },
+          "container_warning": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#a06e3b33",
+            "border": {
+              "color": "#a06e3b26",
+              "width": 1
+            }
+          },
+          "container_error": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#be467833",
+            "border": {
+              "color": "#be467826",
+              "width": 1
+            }
+          }
+        }
+      },
+      "sidebar_buttons": {
+        "group_left": {},
+        "group_right": {},
+        "item": {
+          "icon_size": 14,
+          "padding": {
+            "top": 3,
+            "bottom": 3,
+            "left": 6,
+            "right": 6
+          },
+          "corner_radius": 6,
+          "icon_color": "#585260",
+          "hover": {
+            "icon_color": "#26232a",
+            "background": "#8b87921f"
+          },
+          "active": {
+            "icon_color": "#19171c",
+            "background": "#8b87922e"
+          }
+        }
       }
     },
     "titlebar": {
@@ -283,9 +370,6 @@
         "bottom": true
       },
       "sign_in_prompt": {
-        "family": "Zed Sans",
-        "color": "#585260",
-        "size": 12,
         "border": {
           "color": "#efecf4",
           "width": 1
@@ -298,24 +382,14 @@
         "padding": {
           "left": 6,
           "right": 6
-        }
-      },
-      "hovered_sign_in_prompt": {
+        },
         "family": "Zed Sans",
-        "color": "#19171c",
+        "color": "#585260",
         "size": 12,
-        "border": {
-          "color": "#efecf4",
-          "width": 1
-        },
-        "corner_radius": 6,
-        "margin": {
-          "top": 1,
-          "right": 6
-        },
-        "padding": {
-          "left": 6,
-          "right": 6
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#19171c",
+          "size": 12
         }
       },
       "offline_icon": {
@@ -326,39 +400,24 @@
         }
       },
       "share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
-        },
         "corner_radius": 6,
-        "color": "#585260"
-      },
-      "hovered_share_icon": {
         "margin": {
           "top": 3,
           "bottom": 2
         },
-        "corner_radius": 6,
-        "background": "#8b87921f",
-        "color": "#585260"
-      },
-      "hovered_active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "color": "#585260",
+        "hover": {
+          "background": "#8b87921f",
+          "color": "#585260"
         },
-        "corner_radius": 6,
-        "background": "#8b87921f",
-        "color": "#19171c"
-      },
-      "active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "active": {
+          "background": "#8b87922e",
+          "color": "#19171c"
         },
-        "corner_radius": 6,
-        "background": "#8b87922e",
-        "color": "#19171c"
+        "active_hover": {
+          "background": "#8b87921f",
+          "color": "#19171c"
+        }
       },
       "outdated_warning": {
         "family": "Zed Sans",
@@ -405,8 +464,8 @@
     "background": "#efecf4",
     "active_line_background": "#19171c12",
     "code_actions_indicator": "#585260",
-    "diff_background_deleted": "#be4678",
-    "diff_background_inserted": "#2a9292",
+    "diff_background_deleted": "#be467826",
+    "diff_background_inserted": "#2a929226",
     "document_highlight_read_background": "#efecf41f",
     "document_highlight_write_background": "#efecf429",
     "error_color": "#be4678",
@@ -816,21 +875,14 @@
     }
   },
   "project_diagnostics": {
+    "background": "#efecf4",
     "tab_icon_spacing": 4,
     "tab_icon_width": 13,
     "tab_summary_spacing": 10,
     "empty_message": {
-      "family": "Zed Sans",
-      "color": "#26232a",
-      "size": 18
-    },
-    "status_bar_item": {
       "family": "Zed Sans",
       "color": "#585260",
-      "size": 14,
-      "margin": {
-        "right": 10
-      }
+      "size": 16
     }
   },
   "command_palette": {
@@ -1372,7 +1424,7 @@
         "size": 14
       },
       "border": {
-        "color": "#be4678",
+        "color": "#be467826",
         "width": 1
       },
       "margin": {

assets/themes/dark.json 🔗

@@ -161,54 +161,10 @@
       },
       "cursor": "Arrow"
     },
-    "left_sidebar": {
-      "width": 30,
-      "background": "#1c1c1c",
-      "border": {
-        "color": "#070707",
-        "width": 1,
-        "right": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#9c9c9c",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#ffffff",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#070707",
-        "padding": {
-          "left": 1
-        }
-      }
-    },
-    "right_sidebar": {
-      "width": 30,
-      "background": "#1c1c1c",
-      "border": {
-        "color": "#070707",
-        "width": 1,
-        "left": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#9c9c9c",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#ffffff",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#070707",
-        "padding": {
-          "left": 1
-        }
+    "sidebar_resize_handle": {
+      "background": "#070707",
+      "padding": {
+        "left": 1
       }
     },
     "pane_divider": {
@@ -216,9 +172,11 @@
       "width": 1
     },
     "status_bar": {
-      "height": 24,
+      "height": 30,
       "item_spacing": 8,
       "padding": {
+        "top": 1,
+        "bottom": 1,
         "left": 6,
         "right": 6
       },
@@ -233,25 +191,154 @@
         "color": "#808080",
         "size": 14
       },
-      "diagnostic_message": {
+      "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#808080",
         "size": 14
       },
-      "lsp_message": {
+      "auto_update_done_message": {
         "family": "Zed Sans",
         "color": "#808080",
         "size": 14
       },
-      "auto_update_progress_message": {
-        "family": "Zed Sans",
-        "color": "#808080",
-        "size": 14
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "message": {
+          "family": "Zed Sans",
+          "color": "#808080",
+          "size": 14
+        },
+        "icon_color": "#555555",
+        "hover": {
+          "message": {
+            "family": "Zed Sans",
+            "color": "#f1f1f1",
+            "size": 14
+          },
+          "icon_color": "#c6c6c6",
+          "background": "#232323"
+        }
       },
-      "auto_update_done_message": {
+      "diagnostic_message": {
         "family": "Zed Sans",
         "color": "#808080",
-        "size": 14
+        "size": 14,
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#9c9c9c",
+          "size": 14
+        }
+      },
+      "diagnostic_summary": {
+        "height": 16,
+        "icon_width": 14,
+        "icon_spacing": 2,
+        "summary_spacing": 6,
+        "text": {
+          "family": "Zed Sans",
+          "color": "#f1f1f1",
+          "size": 14
+        },
+        "icon_color_ok": "#9c9c9c",
+        "icon_color_warning": "#f6a724",
+        "icon_color_error": "#eb2d2d",
+        "container_ok": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#232323"
+        },
+        "container_warning": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#f6a72426",
+          "border": {
+            "color": "#f6a72426",
+            "width": 1
+          }
+        },
+        "container_error": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#c9181826",
+          "border": {
+            "color": "#eb2d2d26",
+            "width": 1
+          }
+        },
+        "hover": {
+          "icon_color_ok": "#c6c6c6",
+          "container_ok": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#232323"
+          },
+          "container_warning": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#f6a72433",
+            "border": {
+              "color": "#f6a72426",
+              "width": 1
+            }
+          },
+          "container_error": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#c9181833",
+            "border": {
+              "color": "#eb2d2d26",
+              "width": 1
+            }
+          }
+        }
+      },
+      "sidebar_buttons": {
+        "group_left": {},
+        "group_right": {},
+        "item": {
+          "icon_size": 14,
+          "padding": {
+            "top": 3,
+            "bottom": 3,
+            "left": 6,
+            "right": 6
+          },
+          "corner_radius": 6,
+          "icon_color": "#9c9c9c",
+          "hover": {
+            "icon_color": "#c6c6c6",
+            "background": "#232323"
+          },
+          "active": {
+            "icon_color": "#ffffff",
+            "background": "#2b2b2b"
+          }
+        }
       }
     },
     "titlebar": {
@@ -283,9 +370,6 @@
         "bottom": true
       },
       "sign_in_prompt": {
-        "family": "Zed Sans",
-        "color": "#9c9c9c",
-        "size": 12,
         "border": {
           "color": "#070707",
           "width": 1
@@ -298,24 +382,14 @@
         "padding": {
           "left": 6,
           "right": 6
-        }
-      },
-      "hovered_sign_in_prompt": {
+        },
         "family": "Zed Sans",
-        "color": "#ffffff",
+        "color": "#9c9c9c",
         "size": 12,
-        "border": {
-          "color": "#070707",
-          "width": 1
-        },
-        "corner_radius": 6,
-        "margin": {
-          "top": 1,
-          "right": 6
-        },
-        "padding": {
-          "left": 6,
-          "right": 6
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#ffffff",
+          "size": 12
         }
       },
       "offline_icon": {
@@ -326,39 +400,24 @@
         }
       },
       "share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
-        },
         "corner_radius": 6,
-        "color": "#9c9c9c"
-      },
-      "hovered_share_icon": {
         "margin": {
           "top": 3,
           "bottom": 2
         },
-        "corner_radius": 6,
-        "background": "#323232",
-        "color": "#9c9c9c"
-      },
-      "hovered_active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "color": "#9c9c9c",
+        "hover": {
+          "background": "#323232",
+          "color": "#9c9c9c"
         },
-        "corner_radius": 6,
-        "background": "#323232",
-        "color": "#ffffff"
-      },
-      "active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "active": {
+          "background": "#1c1c1c",
+          "color": "#ffffff"
         },
-        "corner_radius": 6,
-        "background": "#1c1c1c",
-        "color": "#ffffff"
+        "active_hover": {
+          "background": "#323232",
+          "color": "#ffffff"
+        }
       },
       "outdated_warning": {
         "family": "Zed Sans",
@@ -405,8 +464,8 @@
     "background": "#000000",
     "active_line_background": "#ffffff12",
     "code_actions_indicator": "#555555",
-    "diff_background_deleted": "#f15656",
-    "diff_background_inserted": "#1b9447",
+    "diff_background_deleted": "#c9181826",
+    "diff_background_inserted": "#1b944726",
     "document_highlight_read_background": "#ffffff1f",
     "document_highlight_write_background": "#ffffff29",
     "error_color": "#f15656",
@@ -816,21 +875,14 @@
     }
   },
   "project_diagnostics": {
+    "background": "#000000",
     "tab_icon_spacing": 4,
     "tab_icon_width": 13,
     "tab_summary_spacing": 10,
     "empty_message": {
       "family": "Zed Sans",
-      "color": "#f1f1f1",
-      "size": 18
-    },
-    "status_bar_item": {
-      "family": "Zed Sans",
-      "color": "#808080",
-      "size": 14,
-      "margin": {
-        "right": 10
-      }
+      "color": "#9c9c9c",
+      "size": 16
     }
   },
   "command_palette": {
@@ -1372,7 +1424,7 @@
         "size": 14
       },
       "border": {
-        "color": "#eb2d2d",
+        "color": "#eb2d2d26",
         "width": 1
       },
       "margin": {

assets/themes/light.json 🔗

@@ -161,54 +161,10 @@
       },
       "cursor": "Arrow"
     },
-    "left_sidebar": {
-      "width": 30,
-      "background": "#f8f8f8",
-      "border": {
-        "color": "#d5d5d5",
-        "width": 1,
-        "right": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#717171",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#000000",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#d5d5d5",
-        "padding": {
-          "left": 1
-        }
-      }
-    },
-    "right_sidebar": {
-      "width": 30,
-      "background": "#f8f8f8",
-      "border": {
-        "color": "#d5d5d5",
-        "width": 1,
-        "left": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#717171",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#000000",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#d5d5d5",
-        "padding": {
-          "left": 1
-        }
+    "sidebar_resize_handle": {
+      "background": "#d5d5d5",
+      "padding": {
+        "left": 1
       }
     },
     "pane_divider": {
@@ -216,9 +172,11 @@
       "width": 1
     },
     "status_bar": {
-      "height": 24,
+      "height": 30,
       "item_spacing": 8,
       "padding": {
+        "top": 1,
+        "bottom": 1,
         "left": 6,
         "right": 6
       },
@@ -233,25 +191,154 @@
         "color": "#636363",
         "size": 14
       },
-      "diagnostic_message": {
+      "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#636363",
         "size": 14
       },
-      "lsp_message": {
+      "auto_update_done_message": {
         "family": "Zed Sans",
         "color": "#636363",
         "size": 14
       },
-      "auto_update_progress_message": {
-        "family": "Zed Sans",
-        "color": "#636363",
-        "size": 14
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "message": {
+          "family": "Zed Sans",
+          "color": "#636363",
+          "size": 14
+        },
+        "icon_color": "#9c9c9c",
+        "hover": {
+          "message": {
+            "family": "Zed Sans",
+            "color": "#2b2b2b",
+            "size": 14
+          },
+          "icon_color": "#393939",
+          "background": "#eaeaea"
+        }
       },
-      "auto_update_done_message": {
+      "diagnostic_message": {
         "family": "Zed Sans",
         "color": "#636363",
-        "size": 14
+        "size": 14,
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#474747",
+          "size": 14
+        }
+      },
+      "diagnostic_summary": {
+        "height": 16,
+        "icon_width": 14,
+        "icon_spacing": 2,
+        "summary_spacing": 6,
+        "text": {
+          "family": "Zed Sans",
+          "color": "#2b2b2b",
+          "size": 14
+        },
+        "icon_color_ok": "#717171",
+        "icon_color_warning": "#f7bf17",
+        "icon_color_error": "#c91818",
+        "container_ok": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#eaeaea"
+        },
+        "container_warning": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#f6a72426",
+          "border": {
+            "color": "#f6a72426",
+            "width": 1
+          }
+        },
+        "container_error": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#c9181826",
+          "border": {
+            "color": "#eb2d2d26",
+            "width": 1
+          }
+        },
+        "hover": {
+          "icon_color_ok": "#393939",
+          "container_ok": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#eaeaea"
+          },
+          "container_warning": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#f6a72433",
+            "border": {
+              "color": "#f6a72426",
+              "width": 1
+            }
+          },
+          "container_error": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#c9181833",
+            "border": {
+              "color": "#eb2d2d26",
+              "width": 1
+            }
+          }
+        }
+      },
+      "sidebar_buttons": {
+        "group_left": {},
+        "group_right": {},
+        "item": {
+          "icon_size": 14,
+          "padding": {
+            "top": 3,
+            "bottom": 3,
+            "left": 6,
+            "right": 6
+          },
+          "corner_radius": 6,
+          "icon_color": "#717171",
+          "hover": {
+            "icon_color": "#393939",
+            "background": "#eaeaea"
+          },
+          "active": {
+            "icon_color": "#000000",
+            "background": "#e3e3e3"
+          }
+        }
       }
     },
     "titlebar": {
@@ -283,9 +370,6 @@
         "bottom": true
       },
       "sign_in_prompt": {
-        "family": "Zed Sans",
-        "color": "#474747",
-        "size": 12,
         "border": {
           "color": "#d5d5d5",
           "width": 1
@@ -298,24 +382,14 @@
         "padding": {
           "left": 6,
           "right": 6
-        }
-      },
-      "hovered_sign_in_prompt": {
+        },
         "family": "Zed Sans",
-        "color": "#000000",
+        "color": "#474747",
         "size": 12,
-        "border": {
-          "color": "#d5d5d5",
-          "width": 1
-        },
-        "corner_radius": 6,
-        "margin": {
-          "top": 1,
-          "right": 6
-        },
-        "padding": {
-          "left": 6,
-          "right": 6
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#000000",
+          "size": 12
         }
       },
       "offline_icon": {
@@ -326,39 +400,24 @@
         }
       },
       "share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
-        },
         "corner_radius": 6,
-        "color": "#717171"
-      },
-      "hovered_share_icon": {
         "margin": {
           "top": 3,
           "bottom": 2
         },
-        "corner_radius": 6,
-        "background": "#e3e3e3",
-        "color": "#717171"
-      },
-      "hovered_active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "color": "#717171",
+        "hover": {
+          "background": "#e3e3e3",
+          "color": "#717171"
         },
-        "corner_radius": 6,
-        "background": "#e3e3e3",
-        "color": "#000000"
-      },
-      "active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "active": {
+          "background": "#d5d5d5",
+          "color": "#000000"
         },
-        "corner_radius": 6,
-        "background": "#d5d5d5",
-        "color": "#000000"
+        "active_hover": {
+          "background": "#e3e3e3",
+          "color": "#000000"
+        }
       },
       "outdated_warning": {
         "family": "Zed Sans",
@@ -405,8 +464,8 @@
     "background": "#ffffff",
     "active_line_background": "#0000000f",
     "code_actions_indicator": "#9c9c9c",
-    "diff_background_deleted": "#fcc6c6",
-    "diff_background_inserted": "#b7f9ce",
+    "diff_background_deleted": "#c9181826",
+    "diff_background_inserted": "#1b944726",
     "document_highlight_read_background": "#0000000f",
     "document_highlight_write_background": "#00000029",
     "error_color": "#eb2d2d",
@@ -816,21 +875,14 @@
     }
   },
   "project_diagnostics": {
+    "background": "#ffffff",
     "tab_icon_spacing": 4,
     "tab_icon_width": 13,
     "tab_summary_spacing": 10,
     "empty_message": {
       "family": "Zed Sans",
-      "color": "#2b2b2b",
-      "size": 18
-    },
-    "status_bar_item": {
-      "family": "Zed Sans",
-      "color": "#636363",
-      "size": 14,
-      "margin": {
-        "right": 10
-      }
+      "color": "#474747",
+      "size": 16
     }
   },
   "command_palette": {
@@ -1372,7 +1424,7 @@
         "size": 14
       },
       "border": {
-        "color": "#f9a0a0",
+        "color": "#eb2d2d26",
         "width": 1
       },
       "margin": {

assets/themes/solarized-dark.json 🔗

@@ -161,54 +161,10 @@
       },
       "cursor": "Arrow"
     },
-    "left_sidebar": {
-      "width": 30,
-      "background": "#073642",
-      "border": {
-        "color": "#002b36",
-        "width": 1,
-        "right": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#93a1a1",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#fdf6e3",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#002b36",
-        "padding": {
-          "left": 1
-        }
-      }
-    },
-    "right_sidebar": {
-      "width": 30,
-      "background": "#073642",
-      "border": {
-        "color": "#002b36",
-        "width": 1,
-        "left": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#93a1a1",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#fdf6e3",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#002b36",
-        "padding": {
-          "left": 1
-        }
+    "sidebar_resize_handle": {
+      "background": "#002b36",
+      "padding": {
+        "left": 1
       }
     },
     "pane_divider": {
@@ -216,9 +172,11 @@
       "width": 1
     },
     "status_bar": {
-      "height": 24,
+      "height": 30,
       "item_spacing": 8,
       "padding": {
+        "top": 1,
+        "bottom": 1,
         "left": 6,
         "right": 6
       },
@@ -233,25 +191,154 @@
         "color": "#93a1a1",
         "size": 14
       },
-      "diagnostic_message": {
+      "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#93a1a1",
         "size": 14
       },
-      "lsp_message": {
+      "auto_update_done_message": {
         "family": "Zed Sans",
         "color": "#93a1a1",
         "size": 14
       },
-      "auto_update_progress_message": {
-        "family": "Zed Sans",
-        "color": "#93a1a1",
-        "size": 14
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "message": {
+          "family": "Zed Sans",
+          "color": "#93a1a1",
+          "size": 14
+        },
+        "icon_color": "#93a1a1",
+        "hover": {
+          "message": {
+            "family": "Zed Sans",
+            "color": "#eee8d5",
+            "size": 14
+          },
+          "icon_color": "#eee8d5",
+          "background": "#586e7552"
+        }
       },
-      "auto_update_done_message": {
+      "diagnostic_message": {
         "family": "Zed Sans",
         "color": "#93a1a1",
-        "size": 14
+        "size": 14,
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#93a1a1",
+          "size": 14
+        }
+      },
+      "diagnostic_summary": {
+        "height": 16,
+        "icon_width": 14,
+        "icon_spacing": 2,
+        "summary_spacing": 6,
+        "text": {
+          "family": "Zed Sans",
+          "color": "#eee8d5",
+          "size": 14
+        },
+        "icon_color_ok": "#93a1a1",
+        "icon_color_warning": "#b58900",
+        "icon_color_error": "#dc322f",
+        "container_ok": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#586e7552"
+        },
+        "container_warning": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#b5890026",
+          "border": {
+            "color": "#b5890026",
+            "width": 1
+          }
+        },
+        "container_error": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#dc322f26",
+          "border": {
+            "color": "#dc322f26",
+            "width": 1
+          }
+        },
+        "hover": {
+          "icon_color_ok": "#eee8d5",
+          "container_ok": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#586e7552"
+          },
+          "container_warning": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#b5890033",
+            "border": {
+              "color": "#b5890026",
+              "width": 1
+            }
+          },
+          "container_error": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#dc322f33",
+            "border": {
+              "color": "#dc322f26",
+              "width": 1
+            }
+          }
+        }
+      },
+      "sidebar_buttons": {
+        "group_left": {},
+        "group_right": {},
+        "item": {
+          "icon_size": 14,
+          "padding": {
+            "top": 3,
+            "bottom": 3,
+            "left": 6,
+            "right": 6
+          },
+          "corner_radius": 6,
+          "icon_color": "#93a1a1",
+          "hover": {
+            "icon_color": "#eee8d5",
+            "background": "#586e7552"
+          },
+          "active": {
+            "icon_color": "#fdf6e3",
+            "background": "#586e757a"
+          }
+        }
       }
     },
     "titlebar": {
@@ -283,9 +370,6 @@
         "bottom": true
       },
       "sign_in_prompt": {
-        "family": "Zed Sans",
-        "color": "#93a1a1",
-        "size": 12,
         "border": {
           "color": "#002b36",
           "width": 1
@@ -298,24 +382,14 @@
         "padding": {
           "left": 6,
           "right": 6
-        }
-      },
-      "hovered_sign_in_prompt": {
+        },
         "family": "Zed Sans",
-        "color": "#fdf6e3",
+        "color": "#93a1a1",
         "size": 12,
-        "border": {
-          "color": "#002b36",
-          "width": 1
-        },
-        "corner_radius": 6,
-        "margin": {
-          "top": 1,
-          "right": 6
-        },
-        "padding": {
-          "left": 6,
-          "right": 6
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#fdf6e3",
+          "size": 12
         }
       },
       "offline_icon": {
@@ -326,39 +400,24 @@
         }
       },
       "share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
-        },
         "corner_radius": 6,
-        "color": "#93a1a1"
-      },
-      "hovered_share_icon": {
         "margin": {
           "top": 3,
           "bottom": 2
         },
-        "corner_radius": 6,
-        "background": "#586e7552",
-        "color": "#93a1a1"
-      },
-      "hovered_active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "color": "#93a1a1",
+        "hover": {
+          "background": "#586e7552",
+          "color": "#93a1a1"
         },
-        "corner_radius": 6,
-        "background": "#586e7552",
-        "color": "#fdf6e3"
-      },
-      "active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "active": {
+          "background": "#586e757a",
+          "color": "#fdf6e3"
         },
-        "corner_radius": 6,
-        "background": "#586e757a",
-        "color": "#fdf6e3"
+        "active_hover": {
+          "background": "#586e7552",
+          "color": "#fdf6e3"
+        }
       },
       "outdated_warning": {
         "family": "Zed Sans",
@@ -405,8 +464,8 @@
     "background": "#002b36",
     "active_line_background": "#fdf6e312",
     "code_actions_indicator": "#93a1a1",
-    "diff_background_deleted": "#dc322f",
-    "diff_background_inserted": "#859900",
+    "diff_background_deleted": "#dc322f26",
+    "diff_background_inserted": "#85990026",
     "document_highlight_read_background": "#002b361f",
     "document_highlight_write_background": "#002b3629",
     "error_color": "#dc322f",
@@ -816,21 +875,14 @@
     }
   },
   "project_diagnostics": {
+    "background": "#002b36",
     "tab_icon_spacing": 4,
     "tab_icon_width": 13,
     "tab_summary_spacing": 10,
     "empty_message": {
-      "family": "Zed Sans",
-      "color": "#eee8d5",
-      "size": 18
-    },
-    "status_bar_item": {
       "family": "Zed Sans",
       "color": "#93a1a1",
-      "size": 14,
-      "margin": {
-        "right": 10
-      }
+      "size": 16
     }
   },
   "command_palette": {
@@ -1372,7 +1424,7 @@
         "size": 14
       },
       "border": {
-        "color": "#dc322f",
+        "color": "#dc322f26",
         "width": 1
       },
       "margin": {

assets/themes/solarized-light.json 🔗

@@ -161,54 +161,10 @@
       },
       "cursor": "Arrow"
     },
-    "left_sidebar": {
-      "width": 30,
-      "background": "#eee8d5",
-      "border": {
-        "color": "#fdf6e3",
-        "width": 1,
-        "right": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#586e75",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#002b36",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#fdf6e3",
-        "padding": {
-          "left": 1
-        }
-      }
-    },
-    "right_sidebar": {
-      "width": 30,
-      "background": "#eee8d5",
-      "border": {
-        "color": "#fdf6e3",
-        "width": 1,
-        "left": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#586e75",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#002b36",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#fdf6e3",
-        "padding": {
-          "left": 1
-        }
+    "sidebar_resize_handle": {
+      "background": "#fdf6e3",
+      "padding": {
+        "left": 1
       }
     },
     "pane_divider": {
@@ -216,9 +172,11 @@
       "width": 1
     },
     "status_bar": {
-      "height": 24,
+      "height": 30,
       "item_spacing": 8,
       "padding": {
+        "top": 1,
+        "bottom": 1,
         "left": 6,
         "right": 6
       },
@@ -233,25 +191,154 @@
         "color": "#586e75",
         "size": 14
       },
-      "diagnostic_message": {
+      "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#586e75",
         "size": 14
       },
-      "lsp_message": {
+      "auto_update_done_message": {
         "family": "Zed Sans",
         "color": "#586e75",
         "size": 14
       },
-      "auto_update_progress_message": {
-        "family": "Zed Sans",
-        "color": "#586e75",
-        "size": 14
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "message": {
+          "family": "Zed Sans",
+          "color": "#586e75",
+          "size": 14
+        },
+        "icon_color": "#586e75",
+        "hover": {
+          "message": {
+            "family": "Zed Sans",
+            "color": "#073642",
+            "size": 14
+          },
+          "icon_color": "#073642",
+          "background": "#93a1a11f"
+        }
       },
-      "auto_update_done_message": {
+      "diagnostic_message": {
         "family": "Zed Sans",
         "color": "#586e75",
-        "size": 14
+        "size": 14,
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#586e75",
+          "size": 14
+        }
+      },
+      "diagnostic_summary": {
+        "height": 16,
+        "icon_width": 14,
+        "icon_spacing": 2,
+        "summary_spacing": 6,
+        "text": {
+          "family": "Zed Sans",
+          "color": "#073642",
+          "size": 14
+        },
+        "icon_color_ok": "#586e75",
+        "icon_color_warning": "#b58900",
+        "icon_color_error": "#dc322f",
+        "container_ok": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#93a1a11f"
+        },
+        "container_warning": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#b5890026",
+          "border": {
+            "color": "#b5890026",
+            "width": 1
+          }
+        },
+        "container_error": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#dc322f26",
+          "border": {
+            "color": "#dc322f26",
+            "width": 1
+          }
+        },
+        "hover": {
+          "icon_color_ok": "#073642",
+          "container_ok": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#93a1a11f"
+          },
+          "container_warning": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#b5890033",
+            "border": {
+              "color": "#b5890026",
+              "width": 1
+            }
+          },
+          "container_error": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#dc322f33",
+            "border": {
+              "color": "#dc322f26",
+              "width": 1
+            }
+          }
+        }
+      },
+      "sidebar_buttons": {
+        "group_left": {},
+        "group_right": {},
+        "item": {
+          "icon_size": 14,
+          "padding": {
+            "top": 3,
+            "bottom": 3,
+            "left": 6,
+            "right": 6
+          },
+          "corner_radius": 6,
+          "icon_color": "#586e75",
+          "hover": {
+            "icon_color": "#073642",
+            "background": "#93a1a11f"
+          },
+          "active": {
+            "icon_color": "#002b36",
+            "background": "#93a1a12e"
+          }
+        }
       }
     },
     "titlebar": {
@@ -283,9 +370,6 @@
         "bottom": true
       },
       "sign_in_prompt": {
-        "family": "Zed Sans",
-        "color": "#586e75",
-        "size": 12,
         "border": {
           "color": "#fdf6e3",
           "width": 1
@@ -298,24 +382,14 @@
         "padding": {
           "left": 6,
           "right": 6
-        }
-      },
-      "hovered_sign_in_prompt": {
+        },
         "family": "Zed Sans",
-        "color": "#002b36",
+        "color": "#586e75",
         "size": 12,
-        "border": {
-          "color": "#fdf6e3",
-          "width": 1
-        },
-        "corner_radius": 6,
-        "margin": {
-          "top": 1,
-          "right": 6
-        },
-        "padding": {
-          "left": 6,
-          "right": 6
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#002b36",
+          "size": 12
         }
       },
       "offline_icon": {
@@ -326,39 +400,24 @@
         }
       },
       "share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
-        },
         "corner_radius": 6,
-        "color": "#586e75"
-      },
-      "hovered_share_icon": {
         "margin": {
           "top": 3,
           "bottom": 2
         },
-        "corner_radius": 6,
-        "background": "#93a1a11f",
-        "color": "#586e75"
-      },
-      "hovered_active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "color": "#586e75",
+        "hover": {
+          "background": "#93a1a11f",
+          "color": "#586e75"
         },
-        "corner_radius": 6,
-        "background": "#93a1a11f",
-        "color": "#002b36"
-      },
-      "active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "active": {
+          "background": "#93a1a12e",
+          "color": "#002b36"
         },
-        "corner_radius": 6,
-        "background": "#93a1a12e",
-        "color": "#002b36"
+        "active_hover": {
+          "background": "#93a1a11f",
+          "color": "#002b36"
+        }
       },
       "outdated_warning": {
         "family": "Zed Sans",
@@ -405,8 +464,8 @@
     "background": "#fdf6e3",
     "active_line_background": "#002b3612",
     "code_actions_indicator": "#586e75",
-    "diff_background_deleted": "#dc322f",
-    "diff_background_inserted": "#859900",
+    "diff_background_deleted": "#dc322f26",
+    "diff_background_inserted": "#85990026",
     "document_highlight_read_background": "#fdf6e31f",
     "document_highlight_write_background": "#fdf6e329",
     "error_color": "#dc322f",
@@ -816,21 +875,14 @@
     }
   },
   "project_diagnostics": {
+    "background": "#fdf6e3",
     "tab_icon_spacing": 4,
     "tab_icon_width": 13,
     "tab_summary_spacing": 10,
     "empty_message": {
-      "family": "Zed Sans",
-      "color": "#073642",
-      "size": 18
-    },
-    "status_bar_item": {
       "family": "Zed Sans",
       "color": "#586e75",
-      "size": 14,
-      "margin": {
-        "right": 10
-      }
+      "size": 16
     }
   },
   "command_palette": {
@@ -1372,7 +1424,7 @@
         "size": 14
       },
       "border": {
-        "color": "#dc322f",
+        "color": "#dc322f26",
         "width": 1
       },
       "margin": {

assets/themes/sulphurpool-dark.json 🔗

@@ -161,54 +161,10 @@
       },
       "cursor": "Arrow"
     },
-    "left_sidebar": {
-      "width": 30,
-      "background": "#293256",
-      "border": {
-        "color": "#202746",
-        "width": 1,
-        "right": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#979db4",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#f5f7ff",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#202746",
-        "padding": {
-          "left": 1
-        }
-      }
-    },
-    "right_sidebar": {
-      "width": 30,
-      "background": "#293256",
-      "border": {
-        "color": "#202746",
-        "width": 1,
-        "left": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#979db4",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#f5f7ff",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#202746",
-        "padding": {
-          "left": 1
-        }
+    "sidebar_resize_handle": {
+      "background": "#202746",
+      "padding": {
+        "left": 1
       }
     },
     "pane_divider": {
@@ -216,9 +172,11 @@
       "width": 1
     },
     "status_bar": {
-      "height": 24,
+      "height": 30,
       "item_spacing": 8,
       "padding": {
+        "top": 1,
+        "bottom": 1,
         "left": 6,
         "right": 6
       },
@@ -233,25 +191,154 @@
         "color": "#979db4",
         "size": 14
       },
-      "diagnostic_message": {
+      "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#979db4",
         "size": 14
       },
-      "lsp_message": {
+      "auto_update_done_message": {
         "family": "Zed Sans",
         "color": "#979db4",
         "size": 14
       },
-      "auto_update_progress_message": {
-        "family": "Zed Sans",
-        "color": "#979db4",
-        "size": 14
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "message": {
+          "family": "Zed Sans",
+          "color": "#979db4",
+          "size": 14
+        },
+        "icon_color": "#979db4",
+        "hover": {
+          "message": {
+            "family": "Zed Sans",
+            "color": "#dfe2f1",
+            "size": 14
+          },
+          "icon_color": "#dfe2f1",
+          "background": "#5e668752"
+        }
       },
-      "auto_update_done_message": {
+      "diagnostic_message": {
         "family": "Zed Sans",
         "color": "#979db4",
-        "size": 14
+        "size": 14,
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#979db4",
+          "size": 14
+        }
+      },
+      "diagnostic_summary": {
+        "height": 16,
+        "icon_width": 14,
+        "icon_spacing": 2,
+        "summary_spacing": 6,
+        "text": {
+          "family": "Zed Sans",
+          "color": "#dfe2f1",
+          "size": 14
+        },
+        "icon_color_ok": "#979db4",
+        "icon_color_warning": "#c08b30",
+        "icon_color_error": "#c94922",
+        "container_ok": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#5e668752"
+        },
+        "container_warning": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#c08b3026",
+          "border": {
+            "color": "#c08b3026",
+            "width": 1
+          }
+        },
+        "container_error": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#c9492226",
+          "border": {
+            "color": "#c9492226",
+            "width": 1
+          }
+        },
+        "hover": {
+          "icon_color_ok": "#dfe2f1",
+          "container_ok": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#5e668752"
+          },
+          "container_warning": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#c08b3033",
+            "border": {
+              "color": "#c08b3026",
+              "width": 1
+            }
+          },
+          "container_error": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#c9492233",
+            "border": {
+              "color": "#c9492226",
+              "width": 1
+            }
+          }
+        }
+      },
+      "sidebar_buttons": {
+        "group_left": {},
+        "group_right": {},
+        "item": {
+          "icon_size": 14,
+          "padding": {
+            "top": 3,
+            "bottom": 3,
+            "left": 6,
+            "right": 6
+          },
+          "corner_radius": 6,
+          "icon_color": "#979db4",
+          "hover": {
+            "icon_color": "#dfe2f1",
+            "background": "#5e668752"
+          },
+          "active": {
+            "icon_color": "#f5f7ff",
+            "background": "#5e66877a"
+          }
+        }
       }
     },
     "titlebar": {
@@ -283,9 +370,6 @@
         "bottom": true
       },
       "sign_in_prompt": {
-        "family": "Zed Sans",
-        "color": "#979db4",
-        "size": 12,
         "border": {
           "color": "#202746",
           "width": 1
@@ -298,24 +382,14 @@
         "padding": {
           "left": 6,
           "right": 6
-        }
-      },
-      "hovered_sign_in_prompt": {
+        },
         "family": "Zed Sans",
-        "color": "#f5f7ff",
+        "color": "#979db4",
         "size": 12,
-        "border": {
-          "color": "#202746",
-          "width": 1
-        },
-        "corner_radius": 6,
-        "margin": {
-          "top": 1,
-          "right": 6
-        },
-        "padding": {
-          "left": 6,
-          "right": 6
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#f5f7ff",
+          "size": 12
         }
       },
       "offline_icon": {
@@ -326,39 +400,24 @@
         }
       },
       "share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
-        },
         "corner_radius": 6,
-        "color": "#979db4"
-      },
-      "hovered_share_icon": {
         "margin": {
           "top": 3,
           "bottom": 2
         },
-        "corner_radius": 6,
-        "background": "#5e668752",
-        "color": "#979db4"
-      },
-      "hovered_active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "color": "#979db4",
+        "hover": {
+          "background": "#5e668752",
+          "color": "#979db4"
         },
-        "corner_radius": 6,
-        "background": "#5e668752",
-        "color": "#f5f7ff"
-      },
-      "active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "active": {
+          "background": "#5e66877a",
+          "color": "#f5f7ff"
         },
-        "corner_radius": 6,
-        "background": "#5e66877a",
-        "color": "#f5f7ff"
+        "active_hover": {
+          "background": "#5e668752",
+          "color": "#f5f7ff"
+        }
       },
       "outdated_warning": {
         "family": "Zed Sans",
@@ -405,8 +464,8 @@
     "background": "#202746",
     "active_line_background": "#f5f7ff12",
     "code_actions_indicator": "#979db4",
-    "diff_background_deleted": "#c94922",
-    "diff_background_inserted": "#ac9739",
+    "diff_background_deleted": "#c9492226",
+    "diff_background_inserted": "#ac973926",
     "document_highlight_read_background": "#2027461f",
     "document_highlight_write_background": "#20274629",
     "error_color": "#c94922",
@@ -816,21 +875,14 @@
     }
   },
   "project_diagnostics": {
+    "background": "#202746",
     "tab_icon_spacing": 4,
     "tab_icon_width": 13,
     "tab_summary_spacing": 10,
     "empty_message": {
-      "family": "Zed Sans",
-      "color": "#dfe2f1",
-      "size": 18
-    },
-    "status_bar_item": {
       "family": "Zed Sans",
       "color": "#979db4",
-      "size": 14,
-      "margin": {
-        "right": 10
-      }
+      "size": 16
     }
   },
   "command_palette": {
@@ -1372,7 +1424,7 @@
         "size": 14
       },
       "border": {
-        "color": "#c94922",
+        "color": "#c9492226",
         "width": 1
       },
       "margin": {

assets/themes/sulphurpool-light.json 🔗

@@ -161,54 +161,10 @@
       },
       "cursor": "Arrow"
     },
-    "left_sidebar": {
-      "width": 30,
-      "background": "#dfe2f1",
-      "border": {
-        "color": "#f5f7ff",
-        "width": 1,
-        "right": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#5e6687",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#202746",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#f5f7ff",
-        "padding": {
-          "left": 1
-        }
-      }
-    },
-    "right_sidebar": {
-      "width": 30,
-      "background": "#dfe2f1",
-      "border": {
-        "color": "#f5f7ff",
-        "width": 1,
-        "left": true
-      },
-      "item": {
-        "height": 32,
-        "icon_color": "#5e6687",
-        "icon_size": 18
-      },
-      "active_item": {
-        "height": 32,
-        "icon_color": "#202746",
-        "icon_size": 18
-      },
-      "resize_handle": {
-        "background": "#f5f7ff",
-        "padding": {
-          "left": 1
-        }
+    "sidebar_resize_handle": {
+      "background": "#f5f7ff",
+      "padding": {
+        "left": 1
       }
     },
     "pane_divider": {
@@ -216,9 +172,11 @@
       "width": 1
     },
     "status_bar": {
-      "height": 24,
+      "height": 30,
       "item_spacing": 8,
       "padding": {
+        "top": 1,
+        "bottom": 1,
         "left": 6,
         "right": 6
       },
@@ -233,25 +191,154 @@
         "color": "#5e6687",
         "size": 14
       },
-      "diagnostic_message": {
+      "auto_update_progress_message": {
         "family": "Zed Sans",
         "color": "#5e6687",
         "size": 14
       },
-      "lsp_message": {
+      "auto_update_done_message": {
         "family": "Zed Sans",
         "color": "#5e6687",
         "size": 14
       },
-      "auto_update_progress_message": {
-        "family": "Zed Sans",
-        "color": "#5e6687",
-        "size": 14
+      "lsp_status": {
+        "icon_spacing": 4,
+        "icon_width": 14,
+        "height": 18,
+        "corner_radius": 6,
+        "padding": {
+          "left": 6,
+          "right": 6
+        },
+        "message": {
+          "family": "Zed Sans",
+          "color": "#5e6687",
+          "size": 14
+        },
+        "icon_color": "#5e6687",
+        "hover": {
+          "message": {
+            "family": "Zed Sans",
+            "color": "#293256",
+            "size": 14
+          },
+          "icon_color": "#293256",
+          "background": "#979db41f"
+        }
       },
-      "auto_update_done_message": {
+      "diagnostic_message": {
         "family": "Zed Sans",
         "color": "#5e6687",
-        "size": 14
+        "size": 14,
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#5e6687",
+          "size": 14
+        }
+      },
+      "diagnostic_summary": {
+        "height": 16,
+        "icon_width": 14,
+        "icon_spacing": 2,
+        "summary_spacing": 6,
+        "text": {
+          "family": "Zed Sans",
+          "color": "#293256",
+          "size": 14
+        },
+        "icon_color_ok": "#5e6687",
+        "icon_color_warning": "#c08b30",
+        "icon_color_error": "#c94922",
+        "container_ok": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#979db41f"
+        },
+        "container_warning": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#c08b3026",
+          "border": {
+            "color": "#c08b3026",
+            "width": 1
+          }
+        },
+        "container_error": {
+          "corner_radius": 6,
+          "padding": {
+            "left": 6,
+            "right": 6
+          },
+          "background": "#c9492226",
+          "border": {
+            "color": "#c9492226",
+            "width": 1
+          }
+        },
+        "hover": {
+          "icon_color_ok": "#293256",
+          "container_ok": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#979db41f"
+          },
+          "container_warning": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#c08b3033",
+            "border": {
+              "color": "#c08b3026",
+              "width": 1
+            }
+          },
+          "container_error": {
+            "corner_radius": 6,
+            "padding": {
+              "left": 6,
+              "right": 6
+            },
+            "background": "#c9492233",
+            "border": {
+              "color": "#c9492226",
+              "width": 1
+            }
+          }
+        }
+      },
+      "sidebar_buttons": {
+        "group_left": {},
+        "group_right": {},
+        "item": {
+          "icon_size": 14,
+          "padding": {
+            "top": 3,
+            "bottom": 3,
+            "left": 6,
+            "right": 6
+          },
+          "corner_radius": 6,
+          "icon_color": "#5e6687",
+          "hover": {
+            "icon_color": "#293256",
+            "background": "#979db41f"
+          },
+          "active": {
+            "icon_color": "#202746",
+            "background": "#979db42e"
+          }
+        }
       }
     },
     "titlebar": {
@@ -283,9 +370,6 @@
         "bottom": true
       },
       "sign_in_prompt": {
-        "family": "Zed Sans",
-        "color": "#5e6687",
-        "size": 12,
         "border": {
           "color": "#f5f7ff",
           "width": 1
@@ -298,24 +382,14 @@
         "padding": {
           "left": 6,
           "right": 6
-        }
-      },
-      "hovered_sign_in_prompt": {
+        },
         "family": "Zed Sans",
-        "color": "#202746",
+        "color": "#5e6687",
         "size": 12,
-        "border": {
-          "color": "#f5f7ff",
-          "width": 1
-        },
-        "corner_radius": 6,
-        "margin": {
-          "top": 1,
-          "right": 6
-        },
-        "padding": {
-          "left": 6,
-          "right": 6
+        "hover": {
+          "family": "Zed Sans",
+          "color": "#202746",
+          "size": 12
         }
       },
       "offline_icon": {
@@ -326,39 +400,24 @@
         }
       },
       "share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
-        },
         "corner_radius": 6,
-        "color": "#5e6687"
-      },
-      "hovered_share_icon": {
         "margin": {
           "top": 3,
           "bottom": 2
         },
-        "corner_radius": 6,
-        "background": "#979db41f",
-        "color": "#5e6687"
-      },
-      "hovered_active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "color": "#5e6687",
+        "hover": {
+          "background": "#979db41f",
+          "color": "#5e6687"
         },
-        "corner_radius": 6,
-        "background": "#979db41f",
-        "color": "#202746"
-      },
-      "active_share_icon": {
-        "margin": {
-          "top": 3,
-          "bottom": 2
+        "active": {
+          "background": "#979db42e",
+          "color": "#202746"
         },
-        "corner_radius": 6,
-        "background": "#979db42e",
-        "color": "#202746"
+        "active_hover": {
+          "background": "#979db41f",
+          "color": "#202746"
+        }
       },
       "outdated_warning": {
         "family": "Zed Sans",
@@ -405,8 +464,8 @@
     "background": "#f5f7ff",
     "active_line_background": "#20274612",
     "code_actions_indicator": "#5e6687",
-    "diff_background_deleted": "#c94922",
-    "diff_background_inserted": "#ac9739",
+    "diff_background_deleted": "#c9492226",
+    "diff_background_inserted": "#ac973926",
     "document_highlight_read_background": "#f5f7ff1f",
     "document_highlight_write_background": "#f5f7ff29",
     "error_color": "#c94922",
@@ -816,21 +875,14 @@
     }
   },
   "project_diagnostics": {
+    "background": "#f5f7ff",
     "tab_icon_spacing": 4,
     "tab_icon_width": 13,
     "tab_summary_spacing": 10,
     "empty_message": {
-      "family": "Zed Sans",
-      "color": "#293256",
-      "size": 18
-    },
-    "status_bar_item": {
       "family": "Zed Sans",
       "color": "#5e6687",
-      "size": 14,
-      "margin": {
-        "right": 10
-      }
+      "size": 16
     }
   },
   "command_palette": {
@@ -1372,7 +1424,7 @@
         "size": 14
       },
       "border": {
-        "color": "#c94922",
+        "color": "#c9492226",
         "width": 1
       },
       "margin": {

crates/diagnostics/src/diagnostics.rs 🔗

@@ -35,6 +35,7 @@ const CONTEXT_LINE_COUNT: u32 = 1;
 
 pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(ProjectDiagnosticsEditor::deploy);
+    items::init(cx);
 }
 
 type Event = editor::Event;

crates/diagnostics/src/items.rs 🔗

@@ -1,30 +1,38 @@
-use crate::render_summary;
+use editor::{Editor, GoToNextDiagnostic};
 use gpui::{
-    elements::*, platform::CursorStyle, serde_json, Entity, ModelHandle, RenderContext, View,
-    ViewContext,
+    elements::*, platform::CursorStyle, serde_json, Entity, ModelHandle, MutableAppContext,
+    RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
 };
+use language::Diagnostic;
 use project::Project;
 use settings::Settings;
 use workspace::StatusItemView;
 
-pub struct DiagnosticSummary {
+pub struct DiagnosticIndicator {
     summary: project::DiagnosticSummary,
-    in_progress: bool,
+    active_editor: Option<WeakViewHandle<Editor>>,
+    current_diagnostic: Option<Diagnostic>,
+    check_in_progress: bool,
+    _observe_active_editor: Option<Subscription>,
 }
 
-impl DiagnosticSummary {
+pub fn init(cx: &mut MutableAppContext) {
+    cx.add_action(DiagnosticIndicator::go_to_next_diagnostic);
+}
+
+impl DiagnosticIndicator {
     pub fn new(project: &ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
         cx.subscribe(project, |this, project, event, cx| match event {
             project::Event::DiskBasedDiagnosticsUpdated => {
                 cx.notify();
             }
             project::Event::DiskBasedDiagnosticsStarted => {
-                this.in_progress = true;
+                this.check_in_progress = true;
                 cx.notify();
             }
             project::Event::DiskBasedDiagnosticsFinished => {
                 this.summary = project.read(cx).diagnostic_summary(cx);
-                this.in_progress = false;
+                this.check_in_progress = false;
                 cx.notify();
             }
             _ => {}
@@ -32,41 +40,174 @@ impl DiagnosticSummary {
         .detach();
         Self {
             summary: project.read(cx).diagnostic_summary(cx),
-            in_progress: project.read(cx).is_running_disk_based_diagnostics(),
+            check_in_progress: project.read(cx).is_running_disk_based_diagnostics(),
+            active_editor: None,
+            current_diagnostic: None,
+            _observe_active_editor: None,
+        }
+    }
+
+    fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
+        if let Some(editor) = self.active_editor.as_ref().and_then(|e| e.upgrade(cx)) {
+            editor.update(cx, |editor, cx| {
+                editor.go_to_diagnostic(editor::Direction::Next, cx);
+            })
+        }
+    }
+
+    fn update(&mut self, editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) {
+        let editor = editor.read(cx);
+        let buffer = editor.buffer().read(cx);
+        let cursor_position = editor
+            .newest_selection_with_snapshot::<usize>(&buffer.read(cx))
+            .head();
+        let new_diagnostic = buffer
+            .read(cx)
+            .diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false)
+            .filter(|entry| !entry.range.is_empty())
+            .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))
+            .map(|entry| entry.diagnostic);
+        if new_diagnostic != self.current_diagnostic {
+            self.current_diagnostic = new_diagnostic;
+            cx.notify();
         }
     }
 }
 
-impl Entity for DiagnosticSummary {
+impl Entity for DiagnosticIndicator {
     type Event = ();
 }
 
-impl View for DiagnosticSummary {
+impl View for DiagnosticIndicator {
     fn ui_name() -> &'static str {
-        "DiagnosticSummary"
+        "DiagnosticIndicator"
     }
 
     fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
-        enum Tag {}
+        enum Summary {}
+        enum Message {}
+
+        let in_progress = self.check_in_progress;
+        let mut element = Flex::row().with_child(
+            MouseEventHandler::new::<Summary, _, _>(0, cx, |state, cx| {
+                let style = &cx
+                    .global::<Settings>()
+                    .theme
+                    .workspace
+                    .status_bar
+                    .diagnostic_summary;
+                let style = if state.hovered {
+                    style.hover()
+                } else {
+                    &style.default
+                };
+
+                let mut summary_row = Flex::row();
+                if self.summary.error_count > 0 {
+                    summary_row.add_children([
+                        Svg::new("icons/error-solid-14.svg")
+                            .with_color(style.icon_color_error)
+                            .constrained()
+                            .with_width(style.icon_width)
+                            .aligned()
+                            .contained()
+                            .with_margin_right(style.icon_spacing)
+                            .named("error-icon"),
+                        Label::new(self.summary.error_count.to_string(), style.text.clone())
+                            .aligned()
+                            .boxed(),
+                    ]);
+                }
 
-        let in_progress = self.in_progress;
-        MouseEventHandler::new::<Tag, _, _>(0, cx, |_, cx| {
-            let theme = &cx.global::<Settings>().theme.project_diagnostics;
-            if in_progress {
+                if self.summary.warning_count > 0 {
+                    summary_row.add_children([
+                        Svg::new("icons/warning-solid-14.svg")
+                            .with_color(style.icon_color_warning)
+                            .constrained()
+                            .with_width(style.icon_width)
+                            .aligned()
+                            .contained()
+                            .with_margin_right(style.icon_spacing)
+                            .with_margin_left(if self.summary.error_count > 0 {
+                                style.summary_spacing
+                            } else {
+                                0.
+                            })
+                            .named("warning-icon"),
+                        Label::new(self.summary.warning_count.to_string(), style.text.clone())
+                            .aligned()
+                            .boxed(),
+                    ]);
+                }
+
+                if self.summary.error_count == 0 && self.summary.warning_count == 0 {
+                    summary_row.add_child(
+                        Svg::new("icons/no-error-solid-14.svg")
+                            .with_color(style.icon_color_ok)
+                            .constrained()
+                            .with_width(style.icon_width)
+                            .aligned()
+                            .named("ok-icon"),
+                    );
+                }
+
+                summary_row
+                    .constrained()
+                    .with_height(style.height)
+                    .contained()
+                    .with_style(if self.summary.error_count > 0 {
+                        style.container_error
+                    } else if self.summary.warning_count > 0 {
+                        style.container_warning
+                    } else {
+                        style.container_ok
+                    })
+                    .boxed()
+            })
+            .with_cursor_style(CursorStyle::PointingHand)
+            .on_click(|cx| cx.dispatch_action(crate::Deploy))
+            .aligned()
+            .boxed(),
+        );
+
+        let style = &cx.global::<Settings>().theme.workspace.status_bar;
+        let item_spacing = style.item_spacing;
+
+        if in_progress {
+            element.add_child(
                 Label::new(
-                    "Checking... ".to_string(),
-                    theme.status_bar_item.text.clone(),
+                    "checking…".into(),
+                    style.diagnostic_message.default.text.clone(),
                 )
+                .aligned()
                 .contained()
-                .with_style(theme.status_bar_item.container)
-                .boxed()
-            } else {
-                render_summary(&self.summary, &theme.status_bar_item.text, &theme)
-            }
-        })
-        .with_cursor_style(CursorStyle::PointingHand)
-        .on_click(|cx| cx.dispatch_action(crate::Deploy))
-        .boxed()
+                .with_margin_left(item_spacing)
+                .boxed(),
+            );
+        } else if let Some(diagnostic) = &self.current_diagnostic {
+            let message_style = style.diagnostic_message.clone();
+            element.add_child(
+                MouseEventHandler::new::<Message, _, _>(1, cx, |state, _| {
+                    Label::new(
+                        diagnostic.message.split('\n').next().unwrap().to_string(),
+                        if state.hovered {
+                            message_style.hover().text.clone()
+                        } else {
+                            message_style.default.text.clone()
+                        },
+                    )
+                    .aligned()
+                    .contained()
+                    .with_margin_left(item_spacing)
+                    .boxed()
+                })
+                .with_cursor_style(CursorStyle::PointingHand)
+                .on_click(|cx| cx.dispatch_action(GoToNextDiagnostic))
+                .boxed(),
+            );
+        }
+
+        element.named("diagnostic indicator")
     }
 
     fn debug_json(&self, _: &gpui::AppContext) -> serde_json::Value {
@@ -74,11 +215,21 @@ impl View for DiagnosticSummary {
     }
 }
 
-impl StatusItemView for DiagnosticSummary {
+impl StatusItemView for DiagnosticIndicator {
     fn set_active_pane_item(
         &mut self,
-        _: Option<&dyn workspace::ItemHandle>,
-        _: &mut ViewContext<Self>,
+        active_pane_item: Option<&dyn workspace::ItemHandle>,
+        cx: &mut ViewContext<Self>,
     ) {
+        if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
+            self.active_editor = Some(editor.downgrade());
+            self._observe_active_editor = Some(cx.observe(&editor, Self::update));
+            self.update(editor, cx);
+        } else {
+            self.active_editor = None;
+            self.current_diagnostic = None;
+            self._observe_active_editor = None;
+        }
+        cx.notify();
     }
 }

crates/editor/src/items.rs 🔗

@@ -5,7 +5,7 @@ use gpui::{
     elements::*, geometry::vector::vec2f, AppContext, Entity, ModelHandle, MutableAppContext,
     RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
 };
-use language::{Bias, Buffer, Diagnostic, File as _, SelectionGoal};
+use language::{Bias, Buffer, File as _, SelectionGoal};
 use project::{File, Project, ProjectEntryId, ProjectPath};
 use rpc::proto::{self, update_view};
 use settings::Settings;
@@ -507,75 +507,3 @@ impl StatusItemView for CursorPosition {
         cx.notify();
     }
 }
-
-pub struct DiagnosticMessage {
-    diagnostic: Option<Diagnostic>,
-    _observe_active_editor: Option<Subscription>,
-}
-
-impl DiagnosticMessage {
-    pub fn new() -> Self {
-        Self {
-            diagnostic: None,
-            _observe_active_editor: None,
-        }
-    }
-
-    fn update(&mut self, editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) {
-        let editor = editor.read(cx);
-        let buffer = editor.buffer().read(cx);
-        let cursor_position = editor
-            .newest_selection_with_snapshot::<usize>(&buffer.read(cx))
-            .head();
-        let new_diagnostic = buffer
-            .read(cx)
-            .diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false)
-            .filter(|entry| !entry.range.is_empty())
-            .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))
-            .map(|entry| entry.diagnostic);
-        if new_diagnostic != self.diagnostic {
-            self.diagnostic = new_diagnostic;
-            cx.notify();
-        }
-    }
-}
-
-impl Entity for DiagnosticMessage {
-    type Event = ();
-}
-
-impl View for DiagnosticMessage {
-    fn ui_name() -> &'static str {
-        "DiagnosticMessage"
-    }
-
-    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
-        if let Some(diagnostic) = &self.diagnostic {
-            let theme = &cx.global::<Settings>().theme.workspace.status_bar;
-            Label::new(
-                diagnostic.message.split('\n').next().unwrap().to_string(),
-                theme.diagnostic_message.clone(),
-            )
-            .boxed()
-        } else {
-            Empty::new().boxed()
-        }
-    }
-}
-
-impl StatusItemView for DiagnosticMessage {
-    fn set_active_pane_item(
-        &mut self,
-        active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
-            self._observe_active_editor = Some(cx.observe(&editor, Self::update));
-            self.update(editor, cx);
-        } else {
-            self.diagnostic = Default::default();
-            self._observe_active_editor = None;
-        }
-        cx.notify();
-    }
-}

crates/theme/src/theme.rs 🔗

@@ -6,7 +6,8 @@ use gpui::{
     fonts::{HighlightStyle, TextStyle},
     Border,
 };
-use serde::Deserialize;
+use serde::{de::DeserializeOwned, Deserialize};
+use serde_json::Value;
 use std::{collections::HashMap, sync::Arc};
 
 pub use theme_registry::*;
@@ -38,8 +39,7 @@ pub struct Workspace {
     pub pane_divider: Border,
     pub leader_border_opacity: f32,
     pub leader_border_width: f32,
-    pub left_sidebar: Sidebar,
-    pub right_sidebar: Sidebar,
+    pub sidebar_resize_handle: ContainerStyle,
     pub status_bar: StatusBar,
     pub toolbar: Toolbar,
     pub disconnected_overlay: ContainedText,
@@ -55,13 +55,9 @@ pub struct Titlebar {
     pub avatar_width: f32,
     pub avatar_ribbon: AvatarRibbon,
     pub offline_icon: OfflineIcon,
-    pub share_icon: ShareIcon,
-    pub hovered_share_icon: ShareIcon,
-    pub active_share_icon: ShareIcon,
-    pub hovered_active_share_icon: ShareIcon,
+    pub share_icon: Interactive<ShareIcon>,
     pub avatar: ImageStyle,
-    pub sign_in_prompt: ContainedText,
-    pub hovered_sign_in_prompt: ContainedText,
+    pub sign_in_prompt: Interactive<ContainedText>,
     pub outdated_warning: ContainedText,
 }
 
@@ -138,33 +134,64 @@ pub struct FindEditor {
 }
 
 #[derive(Deserialize, Default)]
-pub struct Sidebar {
+pub struct StatusBar {
     #[serde(flatten)]
     pub container: ContainerStyle,
-    pub width: f32,
-    pub item: SidebarItem,
-    pub active_item: SidebarItem,
-    pub resize_handle: ContainerStyle,
+    pub height: f32,
+    pub item_spacing: f32,
+    pub cursor_position: TextStyle,
+    pub auto_update_progress_message: TextStyle,
+    pub auto_update_done_message: TextStyle,
+    pub lsp_status: Interactive<StatusBarLspStatus>,
+    pub sidebar_buttons: StatusBarSidebarButtons,
+    pub diagnostic_summary: Interactive<StatusBarDiagnosticSummary>,
+    pub diagnostic_message: Interactive<ContainedText>,
 }
 
 #[derive(Deserialize, Default)]
-pub struct SidebarItem {
-    pub icon_color: Color,
-    pub icon_size: f32,
+pub struct StatusBarSidebarButtons {
+    pub group_left: ContainerStyle,
+    pub group_right: ContainerStyle,
+    pub item: Interactive<SidebarItem>,
+}
+
+#[derive(Deserialize, Default)]
+pub struct StatusBarDiagnosticSummary {
+    pub container_ok: ContainerStyle,
+    pub container_warning: ContainerStyle,
+    pub container_error: ContainerStyle,
+    pub text: TextStyle,
+    pub icon_color_ok: Color,
+    pub icon_color_warning: Color,
+    pub icon_color_error: Color,
     pub height: f32,
+    pub icon_width: f32,
+    pub icon_spacing: f32,
+    pub summary_spacing: f32,
 }
 
 #[derive(Deserialize, Default)]
-pub struct StatusBar {
+pub struct StatusBarLspStatus {
     #[serde(flatten)]
     pub container: ContainerStyle,
     pub height: f32,
-    pub item_spacing: f32,
-    pub cursor_position: TextStyle,
-    pub diagnostic_message: TextStyle,
-    pub lsp_message: TextStyle,
-    pub auto_update_progress_message: TextStyle,
-    pub auto_update_done_message: TextStyle,
+    pub icon_spacing: f32,
+    pub icon_color: Color,
+    pub icon_width: f32,
+    pub message: TextStyle,
+}
+
+#[derive(Deserialize, Default)]
+pub struct Sidebar {
+    pub resize_handle: ContainerStyle,
+}
+
+#[derive(Clone, Copy, Deserialize, Default)]
+pub struct SidebarItem {
+    #[serde(flatten)]
+    pub container: ContainerStyle,
+    pub icon_color: Color,
+    pub icon_size: f32,
 }
 
 #[derive(Deserialize, Default)]
@@ -291,7 +318,6 @@ pub struct ProjectDiagnostics {
     #[serde(flatten)]
     pub container: ContainerStyle,
     pub empty_message: TextStyle,
-    pub status_bar_item: ContainedText,
     pub tab_icon_width: f32,
     pub tab_icon_spacing: f32,
     pub tab_summary_spacing: f32,
@@ -384,6 +410,77 @@ pub struct FieldEditor {
     pub selection: SelectionStyle,
 }
 
+#[derive(Default, Clone, Copy)]
+pub struct Interactive<T> {
+    pub default: T,
+    pub hover: Option<T>,
+    pub active: Option<T>,
+    pub active_hover: Option<T>,
+}
+
+impl<T> Interactive<T> {
+    pub fn active(&self) -> &T {
+        self.active.as_ref().unwrap_or(&self.default)
+    }
+
+    pub fn hover(&self) -> &T {
+        self.hover.as_ref().unwrap_or(&self.default)
+    }
+
+    pub fn active_hover(&self) -> &T {
+        self.active_hover.as_ref().unwrap_or(self.active())
+    }
+}
+
+impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        #[derive(Deserialize)]
+        struct Helper {
+            #[serde(flatten)]
+            default: Value,
+            hover: Option<Value>,
+            active: Option<Value>,
+            active_hover: Option<Value>,
+        }
+
+        let json = Helper::deserialize(deserializer)?;
+
+        let deserialize_state = |state_json: Option<Value>| -> Result<Option<T>, D::Error> {
+            if let Some(mut state_json) = state_json {
+                if let Value::Object(state_json) = &mut state_json {
+                    if let Value::Object(default) = &json.default {
+                        for (key, value) in default {
+                            if !state_json.contains_key(key) {
+                                state_json.insert(key.clone(), value.clone());
+                            }
+                        }
+                    }
+                }
+                Ok(Some(
+                    serde_json::from_value::<T>(state_json).map_err(serde::de::Error::custom)?,
+                ))
+            } else {
+                Ok(None)
+            }
+        };
+
+        let hover = deserialize_state(json.hover)?;
+        let active = deserialize_state(json.active)?;
+        let active_hover = deserialize_state(json.active_hover)?;
+        let default = serde_json::from_value(json.default).map_err(serde::de::Error::custom)?;
+
+        Ok(Interactive {
+            default,
+            hover,
+            active,
+            active_hover,
+        })
+    }
+}
+
 impl Editor {
     pub fn replica_selection_style(&self, replica_id: u16) -> &SelectionStyle {
         let style_ix = replica_id as usize % (self.guest_selections.len() + 1);

crates/workspace/src/lsp_status.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{ItemHandle, StatusItemView};
 use futures::StreamExt;
-use gpui::{actions, AppContext};
+use gpui::{actions, AppContext, EventContext};
 use gpui::{
     elements::*, platform::CursorStyle, Entity, ModelHandle, MutableAppContext, RenderContext,
     View, ViewContext,
@@ -117,11 +117,13 @@ impl View for LspStatus {
     }
 
     fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
-        let theme = &cx.global::<Settings>().theme;
+        let mut message;
+        let mut icon = None;
+        let mut handler = None;
 
         let mut pending_work = self.pending_language_server_work(cx);
         if let Some((lang_server_name, progress_token, progress)) = pending_work.next() {
-            let mut message = lang_server_name.to_string();
+            message = lang_server_name.to_string();
 
             message.push_str(": ");
             if let Some(progress_message) = progress.message.as_ref() {
@@ -138,21 +140,19 @@ impl View for LspStatus {
             if additional_work_count > 0 {
                 write!(&mut message, " + {} more", additional_work_count).unwrap();
             }
+        } else {
+            drop(pending_work);
 
-            Label::new(message, theme.workspace.status_bar.lsp_message.clone()).boxed()
-        } else if !self.downloading.is_empty() {
-            Label::new(
-                format!(
+            if !self.downloading.is_empty() {
+                icon = Some("icons/download-solid-14.svg");
+                message = format!(
                     "Downloading {} language server{}...",
                     self.downloading.join(", "),
                     if self.downloading.len() > 1 { "s" } else { "" }
-                ),
-                theme.workspace.status_bar.lsp_message.clone(),
-            )
-            .boxed()
-        } else if !self.checking_for_update.is_empty() {
-            Label::new(
-                format!(
+                );
+            } else if !self.checking_for_update.is_empty() {
+                icon = Some("icons/download-solid-14.svg");
+                message = format!(
                     "Checking for updates to {} language server{}...",
                     self.checking_for_update.join(", "),
                     if self.checking_for_update.len() > 1 {
@@ -160,30 +160,59 @@ impl View for LspStatus {
                     } else {
                         ""
                     }
-                ),
-                theme.workspace.status_bar.lsp_message.clone(),
-            )
-            .boxed()
-        } else if !self.failed.is_empty() {
-            drop(pending_work);
-            MouseEventHandler::new::<Self, _, _>(0, cx, |_, cx| {
-                let theme = &cx.global::<Settings>().theme;
-                Label::new(
-                    format!(
-                        "Failed to download {} language server{}. Click to dismiss.",
-                        self.failed.join(", "),
-                        if self.failed.len() > 1 { "s" } else { "" }
-                    ),
-                    theme.workspace.status_bar.lsp_message.clone(),
-                )
+                );
+            } else if !self.failed.is_empty() {
+                icon = Some("icons/warning-solid-14.svg");
+                message = format!(
+                    "Failed to download {} language server{}. Click to dismiss.",
+                    self.failed.join(", "),
+                    if self.failed.len() > 1 { "s" } else { "" }
+                );
+                handler = Some(|cx: &mut EventContext| cx.dispatch_action(DismissErrorMessage));
+            } else {
+                return Empty::new().boxed();
+            }
+        }
+
+        let mut element = MouseEventHandler::new::<Self, _, _>(0, cx, |state, cx| {
+            let theme = &cx
+                .global::<Settings>()
+                .theme
+                .workspace
+                .status_bar
+                .lsp_status;
+            let style = if state.hovered && handler.is_some() {
+                theme.hover.as_ref().unwrap_or(&theme.default)
+            } else {
+                &theme.default
+            };
+            Flex::row()
+                .with_children(icon.map(|path| {
+                    Svg::new(path)
+                        .with_color(style.icon_color)
+                        .constrained()
+                        .with_width(style.icon_width)
+                        .contained()
+                        .with_margin_right(style.icon_spacing)
+                        .aligned()
+                        .named("warning-icon")
+                }))
+                .with_child(Label::new(message, style.message.clone()).aligned().boxed())
+                .constrained()
+                .with_height(style.height)
+                .contained()
+                .with_style(style.container)
+                .aligned()
                 .boxed()
-            })
-            .with_cursor_style(CursorStyle::PointingHand)
-            .on_click(|cx| cx.dispatch_action(DismissErrorMessage))
-            .boxed()
-        } else {
-            Empty::new().boxed()
+        });
+
+        if let Some(handler) = handler {
+            element = element
+                .with_cursor_style(CursorStyle::PointingHand)
+                .on_click(handler);
         }
+
+        element.boxed()
     }
 }
 

crates/workspace/src/sidebar.rs 🔗

@@ -1,9 +1,14 @@
-use super::Workspace;
-use gpui::{elements::*, impl_actions, platform::CursorStyle, AnyViewHandle, RenderContext};
+use gpui::{
+    elements::*, impl_actions, platform::CursorStyle, AnyViewHandle, Entity, RenderContext, View,
+    ViewContext, ViewHandle,
+};
 use serde::Deserialize;
+use settings::Settings;
 use std::{cell::RefCell, rc::Rc};
 use theme::Theme;
 
+use crate::StatusItemView;
+
 pub struct Sidebar {
     side: Side,
     items: Vec<Item>,
@@ -12,31 +17,36 @@ pub struct Sidebar {
     custom_width: Rc<RefCell<f32>>,
 }
 
-#[derive(Clone, Copy, Deserialize)]
+#[derive(Clone, Copy, Debug, Deserialize)]
 pub enum Side {
     Left,
     Right,
 }
 
+#[derive(Clone)]
 struct Item {
     icon_path: &'static str,
     view: AnyViewHandle,
 }
 
-#[derive(Clone, Deserialize)]
-pub struct ToggleSidebarItem(pub SidebarItemId);
-
-#[derive(Clone, Deserialize)]
-pub struct ToggleSidebarItemFocus(pub SidebarItemId);
+pub struct SidebarButtons {
+    sidebar: ViewHandle<Sidebar>,
+}
 
-impl_actions!(workspace, [ToggleSidebarItem, ToggleSidebarItemFocus]);
+#[derive(Clone, Debug, Deserialize)]
+pub struct ToggleSidebarItem {
+    pub side: Side,
+    pub item_index: usize,
+}
 
-#[derive(Clone, Deserialize)]
-pub struct SidebarItemId {
+#[derive(Clone, Debug, Deserialize)]
+pub struct ToggleSidebarItemFocus {
     pub side: Side,
     pub item_index: usize,
 }
 
+impl_actions!(workspace, [ToggleSidebarItem, ToggleSidebarItemFocus]);
+
 impl Sidebar {
     pub fn new(side: Side) -> Self {
         Self {
@@ -48,20 +58,28 @@ impl Sidebar {
         }
     }
 
-    pub fn add_item(&mut self, icon_path: &'static str, view: AnyViewHandle) {
+    pub fn add_item(
+        &mut self,
+        icon_path: &'static str,
+        view: AnyViewHandle,
+        cx: &mut ViewContext<Self>,
+    ) {
         self.items.push(Item { icon_path, view });
+        cx.notify()
     }
 
-    pub fn activate_item(&mut self, item_ix: usize) {
+    pub fn activate_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) {
         self.active_item_ix = Some(item_ix);
+        cx.notify();
     }
 
-    pub fn toggle_item(&mut self, item_ix: usize) {
+    pub fn toggle_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) {
         if self.active_item_ix == Some(item_ix) {
             self.active_item_ix = None;
         } else {
             self.active_item_ix = Some(item_ix);
         }
+        cx.notify();
     }
 
     pub fn active_item(&self) -> Option<&AnyViewHandle> {
@@ -70,75 +88,56 @@ impl Sidebar {
             .map(|item| &item.view)
     }
 
-    fn theme<'a>(&self, theme: &'a Theme) -> &'a theme::Sidebar {
-        match self.side {
-            Side::Left => &theme.workspace.left_sidebar,
-            Side::Right => &theme.workspace.right_sidebar,
-        }
-    }
-
-    pub fn render(&self, theme: &Theme, cx: &mut RenderContext<Workspace>) -> ElementBox {
+    fn render_resize_handle(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> ElementBox {
+        let actual_width = self.actual_width.clone();
+        let custom_width = self.custom_width.clone();
         let side = self.side;
-        let theme = self.theme(theme);
-
-        ConstrainedBox::new(
-            Container::new(
-                Flex::column()
-                    .with_children(self.items.iter().enumerate().map(|(item_index, item)| {
-                        let theme = if Some(item_index) == self.active_item_ix {
-                            &theme.active_item
-                        } else {
-                            &theme.item
-                        };
-                        enum SidebarButton {}
-                        MouseEventHandler::new::<SidebarButton, _, _>(item.view.id(), cx, |_, _| {
-                            ConstrainedBox::new(
-                                Align::new(
-                                    ConstrainedBox::new(
-                                        Svg::new(item.icon_path)
-                                            .with_color(theme.icon_color)
-                                            .boxed(),
-                                    )
-                                    .with_height(theme.icon_size)
-                                    .boxed(),
-                                )
-                                .boxed(),
-                            )
-                            .with_height(theme.height)
-                            .boxed()
-                        })
-                        .with_cursor_style(CursorStyle::PointingHand)
-                        .on_mouse_down(move |cx| {
-                            cx.dispatch_action(ToggleSidebarItem(SidebarItemId {
-                                side,
-                                item_index,
-                            }))
-                        })
-                        .boxed()
-                    }))
-                    .boxed(),
-            )
-            .with_style(theme.container)
-            .boxed(),
-        )
-        .with_width(theme.width)
+        MouseEventHandler::new::<Self, _, _>(side as usize, cx, |_, _| {
+            Empty::new()
+                .contained()
+                .with_style(theme.workspace.sidebar_resize_handle)
+                .boxed()
+        })
+        .with_padding(Padding {
+            left: 4.,
+            right: 4.,
+            ..Default::default()
+        })
+        .with_cursor_style(CursorStyle::ResizeLeftRight)
+        .on_drag(move |delta, cx| {
+            let prev_width = *actual_width.borrow();
+            match side {
+                Side::Left => *custom_width.borrow_mut() = 0f32.max(prev_width + delta.x()),
+                Side::Right => *custom_width.borrow_mut() = 0f32.max(prev_width - delta.x()),
+            }
+
+            cx.notify();
+        })
         .boxed()
     }
+}
+
+impl Entity for Sidebar {
+    type Event = ();
+}
 
-    pub fn render_active_item(
-        &self,
-        theme: &Theme,
-        cx: &mut RenderContext<Workspace>,
-    ) -> Option<ElementBox> {
+impl View for Sidebar {
+    fn ui_name() -> &'static str {
+        "Sidebar"
+    }
+
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        let theme = cx.global::<Settings>().theme.clone();
         if let Some(active_item) = self.active_item() {
             let mut container = Flex::row();
             if matches!(self.side, Side::Right) {
-                container.add_child(self.render_resize_handle(theme, cx));
+                container.add_child(self.render_resize_handle(&theme, cx));
             }
 
             container.add_child(
                 Hook::new(
-                    ConstrainedBox::new(ChildView::new(active_item).boxed())
+                    ChildView::new(active_item)
+                        .constrained()
                         .with_max_width(*self.custom_width.borrow())
                         .boxed(),
                 )
@@ -150,38 +149,85 @@ impl Sidebar {
                 .boxed(),
             );
             if matches!(self.side, Side::Left) {
-                container.add_child(self.render_resize_handle(theme, cx));
+                container.add_child(self.render_resize_handle(&theme, cx));
             }
-            Some(container.boxed())
+            container.boxed()
         } else {
-            None
+            Empty::new().boxed()
         }
     }
+}
 
-    fn render_resize_handle(&self, theme: &Theme, cx: &mut RenderContext<Workspace>) -> ElementBox {
-        let actual_width = self.actual_width.clone();
-        let custom_width = self.custom_width.clone();
-        let side = self.side;
-        MouseEventHandler::new::<Self, _, _>(side as usize, cx, |_, _| {
-            Container::new(Empty::new().boxed())
-                .with_style(self.theme(theme).resize_handle)
+impl SidebarButtons {
+    pub fn new(sidebar: ViewHandle<Sidebar>, cx: &mut ViewContext<Self>) -> Self {
+        cx.observe(&sidebar, |_, _, cx| cx.notify()).detach();
+        Self { sidebar }
+    }
+}
+
+impl Entity for SidebarButtons {
+    type Event = ();
+}
+
+impl View for SidebarButtons {
+    fn ui_name() -> &'static str {
+        "SidebarToggleButton"
+    }
+
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        let theme = &cx
+            .global::<Settings>()
+            .theme
+            .workspace
+            .status_bar
+            .sidebar_buttons;
+        let sidebar = self.sidebar.read(cx);
+        let item_style = theme.item;
+        let active_ix = sidebar.active_item_ix;
+        let side = sidebar.side;
+        let group_style = match side {
+            Side::Left => theme.group_left,
+            Side::Right => theme.group_right,
+        };
+        let items = sidebar.items.clone();
+        Flex::row()
+            .with_children(items.iter().enumerate().map(|(ix, item)| {
+                MouseEventHandler::new::<Self, _, _>(ix, cx, move |state, _| {
+                    let style = if Some(ix) == active_ix {
+                        item_style.active()
+                    } else if state.hovered {
+                        item_style.hover()
+                    } else {
+                        &item_style.default
+                    };
+                    Svg::new(item.icon_path)
+                        .with_color(style.icon_color)
+                        .constrained()
+                        .with_height(style.icon_size)
+                        .contained()
+                        .with_style(style.container)
+                        .boxed()
+                })
+                .with_cursor_style(CursorStyle::PointingHand)
+                .on_click(move |cx| {
+                    cx.dispatch_action(ToggleSidebarItem {
+                        side,
+                        item_index: ix,
+                    })
+                })
                 .boxed()
-        })
-        .with_padding(Padding {
-            left: 4.,
-            right: 4.,
-            ..Default::default()
-        })
-        .with_cursor_style(CursorStyle::ResizeLeftRight)
-        .on_drag(move |delta, cx| {
-            let prev_width = *actual_width.borrow();
-            match side {
-                Side::Left => *custom_width.borrow_mut() = 0f32.max(prev_width + delta.x()),
-                Side::Right => *custom_width.borrow_mut() = 0f32.max(prev_width - delta.x()),
-            }
+            }))
+            .contained()
+            .with_style(group_style)
+            .boxed()
+    }
+}
 
-            cx.notify();
-        })
-        .boxed()
+impl StatusItemView for SidebarButtons {
+    fn set_active_pane_item(
+        &mut self,
+        _: Option<&dyn crate::ItemHandle>,
+        _: &mut ViewContext<Self>,
+    ) {
     }
 }

crates/workspace/src/status_bar.rs 🔗

@@ -1,9 +1,9 @@
 use crate::{ItemHandle, Pane};
-use settings::Settings;
 use gpui::{
     elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, Subscription,
     View, ViewContext, ViewHandle,
 };
+use settings::Settings;
 
 pub trait StatusItemView: View {
     fn set_active_pane_item(
@@ -48,7 +48,7 @@ impl View for StatusBar {
                     .with_margin_right(theme.item_spacing)
                     .boxed()
             }))
-            .with_children(self.right_items.iter().map(|i| {
+            .with_children(self.right_items.iter().rev().map(|i| {
                 ChildView::new(i.as_ref())
                     .aligned()
                     .contained()

crates/workspace/src/workspace.rs 🔗

@@ -31,7 +31,7 @@ pub use pane_group::*;
 use postage::prelude::Stream;
 use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, Worktree};
 use settings::Settings;
-use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus};
+use sidebar::{Side, Sidebar, SidebarButtons, ToggleSidebarItem, ToggleSidebarItemFocus};
 use status_bar::StatusBar;
 pub use status_bar::StatusItemView;
 use std::{
@@ -678,8 +678,8 @@ pub struct Workspace {
     themes: Arc<ThemeRegistry>,
     modal: Option<AnyViewHandle>,
     center: PaneGroup,
-    left_sidebar: Sidebar,
-    right_sidebar: Sidebar,
+    left_sidebar: ViewHandle<Sidebar>,
+    right_sidebar: ViewHandle<Sidebar>,
     panes: Vec<ViewHandle<Pane>>,
     active_pane: ViewHandle<Pane>,
     status_bar: ViewHandle<StatusBar>,
@@ -751,7 +751,6 @@ impl Workspace {
         cx.focus(&pane);
         cx.emit(Event::PaneAdded(pane.clone()));
 
-        let status_bar = cx.add_view(|cx| StatusBar::new(&pane, cx));
         let mut current_user = params.user_store.read(cx).watch_current_user().clone();
         let mut connection_status = params.client.status().clone();
         let _observe_current_user = cx.spawn_weak(|this, mut cx| async move {
@@ -773,6 +772,18 @@ impl Workspace {
 
         cx.emit_global(WorkspaceCreated(weak_self.clone()));
 
+        let left_sidebar = cx.add_view(|_| Sidebar::new(Side::Left));
+        let right_sidebar = cx.add_view(|_| Sidebar::new(Side::Right));
+        let left_sidebar_buttons = cx.add_view(|cx| SidebarButtons::new(left_sidebar.clone(), cx));
+        let right_sidebar_buttons =
+            cx.add_view(|cx| SidebarButtons::new(right_sidebar.clone(), cx));
+        let status_bar = cx.add_view(|cx| {
+            let mut status_bar = StatusBar::new(&pane.clone(), cx);
+            status_bar.add_left_item(left_sidebar_buttons, cx);
+            status_bar.add_right_item(right_sidebar_buttons, cx);
+            status_bar
+        });
+
         let mut this = Workspace {
             modal: None,
             weak_self,
@@ -785,8 +796,8 @@ impl Workspace {
             user_store: params.user_store.clone(),
             fs: params.fs.clone(),
             themes: params.themes.clone(),
-            left_sidebar: Sidebar::new(Side::Left),
-            right_sidebar: Sidebar::new(Side::Right),
+            left_sidebar,
+            right_sidebar,
             project: params.project.clone(),
             leader_state: Default::default(),
             follower_states_by_leader: Default::default(),
@@ -801,12 +812,12 @@ impl Workspace {
         self.weak_self.clone()
     }
 
-    pub fn left_sidebar_mut(&mut self) -> &mut Sidebar {
-        &mut self.left_sidebar
+    pub fn left_sidebar(&self) -> &ViewHandle<Sidebar> {
+        &self.left_sidebar
     }
 
-    pub fn right_sidebar_mut(&mut self) -> &mut Sidebar {
-        &mut self.right_sidebar
+    pub fn right_sidebar(&self) -> &ViewHandle<Sidebar> {
+        &self.right_sidebar
     }
 
     pub fn status_bar(&self) -> &ViewHandle<StatusBar> {
@@ -1028,12 +1039,15 @@ impl Workspace {
     }
 
     pub fn toggle_sidebar_item(&mut self, action: &ToggleSidebarItem, cx: &mut ViewContext<Self>) {
-        let sidebar = match action.0.side {
+        let sidebar = match action.side {
             Side::Left => &mut self.left_sidebar,
             Side::Right => &mut self.right_sidebar,
         };
-        sidebar.toggle_item(action.0.item_index);
-        if let Some(active_item) = sidebar.active_item() {
+        let active_item = sidebar.update(cx, |sidebar, cx| {
+            sidebar.toggle_item(action.item_index, cx);
+            sidebar.active_item().cloned()
+        });
+        if let Some(active_item) = active_item {
             cx.focus(active_item);
         } else {
             cx.focus_self();
@@ -1046,12 +1060,15 @@ impl Workspace {
         action: &ToggleSidebarItemFocus,
         cx: &mut ViewContext<Self>,
     ) {
-        let sidebar = match action.0.side {
+        let sidebar = match action.side {
             Side::Left => &mut self.left_sidebar,
             Side::Right => &mut self.right_sidebar,
         };
-        sidebar.activate_item(action.0.item_index);
-        if let Some(active_item) = sidebar.active_item() {
+        let active_item = sidebar.update(cx, |sidebar, cx| {
+            sidebar.toggle_item(action.item_index, cx);
+            sidebar.active_item().cloned()
+        });
+        if let Some(active_item) = active_item {
             if active_item.is_focused(cx) {
                 cx.focus_self();
             } else {
@@ -1558,9 +1575,9 @@ impl Workspace {
             Some(
                 MouseEventHandler::new::<Authenticate, _, _>(0, cx, |state, _| {
                     let style = if state.hovered {
-                        &theme.workspace.titlebar.hovered_sign_in_prompt
+                        &theme.workspace.titlebar.sign_in_prompt.hover()
                     } else {
-                        &theme.workspace.titlebar.sign_in_prompt
+                        &theme.workspace.titlebar.sign_in_prompt.default
                     };
                     Label::new("Sign in".to_string(), style.text.clone())
                         .contained()
@@ -1610,7 +1627,7 @@ impl Workspace {
                     .boxed(),
             )
             .constrained()
-            .with_width(theme.workspace.right_sidebar.width)
+            .with_width(theme.workspace.titlebar.avatar_width)
             .contained()
             .with_margin_left(2.)
             .boxed();
@@ -1632,18 +1649,17 @@ impl Workspace {
         {
             Some(
                 MouseEventHandler::new::<ToggleShare, _, _>(0, cx, |state, cx| {
+                    let style = &theme.workspace.titlebar.share_icon;
                     let style = if self.project().read(cx).is_shared() {
                         if state.hovered {
-                            &theme.workspace.titlebar.hovered_active_share_icon
+                            style.active_hover()
                         } else {
-                            &theme.workspace.titlebar.active_share_icon
+                            &style.active()
                         }
+                    } else if state.hovered {
+                        &style.active()
                     } else {
-                        if state.hovered {
-                            &theme.workspace.titlebar.hovered_share_icon
-                        } else {
-                            &theme.workspace.titlebar.share_icon
-                        }
+                        &style.default
                     };
                     Svg::new("icons/share.svg")
                         .with_color(style.color)
@@ -1656,7 +1672,7 @@ impl Workspace {
                         .with_width(24.)
                         .aligned()
                         .constrained()
-                        .with_width(theme.workspace.right_sidebar.width)
+                        .with_width(24.)
                         .aligned()
                         .boxed()
                 })
@@ -1983,37 +1999,39 @@ impl View for Workspace {
                     .with_child(
                         Stack::new()
                             .with_child({
-                                let mut content = Flex::row();
-                                content.add_child(self.left_sidebar.render(&theme, cx));
-                                if let Some(element) =
-                                    self.left_sidebar.render_active_item(&theme, cx)
-                                {
-                                    content
-                                        .add_child(FlexItem::new(element).flex(0.8, false).boxed());
-                                }
-                                content.add_child(
-                                    Flex::column()
-                                        .with_child(
-                                            FlexItem::new(self.center.render(
-                                                &theme,
-                                                &self.follower_states_by_leader,
-                                                self.project.read(cx).collaborators(),
-                                            ))
-                                            .flex(1., true)
-                                            .boxed(),
-                                        )
-                                        .with_child(ChildView::new(&self.status_bar).boxed())
+                                Flex::row()
+                                    .with_children(
+                                        if self.left_sidebar.read(cx).active_item().is_some() {
+                                            Some(
+                                                ChildView::new(&self.left_sidebar)
+                                                    .flex(0.8, false)
+                                                    .boxed(),
+                                            )
+                                        } else {
+                                            None
+                                        },
+                                    )
+                                    .with_child(
+                                        FlexItem::new(self.center.render(
+                                            &theme,
+                                            &self.follower_states_by_leader,
+                                            self.project.read(cx).collaborators(),
+                                        ))
                                         .flex(1., true)
                                         .boxed(),
-                                );
-                                if let Some(element) =
-                                    self.right_sidebar.render_active_item(&theme, cx)
-                                {
-                                    content
-                                        .add_child(FlexItem::new(element).flex(0.8, false).boxed());
-                                }
-                                content.add_child(self.right_sidebar.render(&theme, cx));
-                                content.boxed()
+                                    )
+                                    .with_children(
+                                        if self.right_sidebar.read(cx).active_item().is_some() {
+                                            Some(
+                                                ChildView::new(&self.right_sidebar)
+                                                    .flex(0.8, false)
+                                                    .boxed(),
+                                            )
+                                        } else {
+                                            None
+                                        },
+                                    )
+                                    .boxed()
                             })
                             .with_children(self.modal.as_ref().map(|m| {
                                 ChildView::new(m)
@@ -2026,6 +2044,7 @@ impl View for Workspace {
                             .flex(1.0, true)
                             .boxed(),
                     )
+                    .with_child(ChildView::new(&self.status_bar).boxed())
                     .contained()
                     .with_background_color(theme.workspace.background)
                     .boxed(),
@@ -2202,10 +2221,10 @@ pub fn open_paths(
                 let mut workspace = (app_state.build_workspace)(project, &app_state, cx);
                 if contains_directory {
                     workspace.toggle_sidebar_item(
-                        &ToggleSidebarItem(SidebarItemId {
+                        &ToggleSidebarItem {
                             side: Side::Left,
                             item_index: 0,
-                        }),
+                        },
                         cx,
                     );
                 }

crates/zed/src/zed.rs 🔗

@@ -6,7 +6,6 @@ pub mod test;
 
 use anyhow::{anyhow, Context, Result};
 use breadcrumbs::Breadcrumbs;
-use chat_panel::ChatPanel;
 pub use client;
 pub use contacts_panel;
 use contacts_panel::ContactsPanel;
@@ -147,7 +146,7 @@ pub fn build_workspace(
         user_store: app_state.user_store.clone(),
         channel_list: app_state.channel_list.clone(),
     };
-    let mut workspace = Workspace::new(&workspace_params, cx);
+    let workspace = Workspace::new(&workspace_params, cx);
     let project = workspace.project().clone();
 
     let theme_names = app_state.themes.list().collect();
@@ -171,26 +170,18 @@ pub fn build_workspace(
         }));
     });
 
-    workspace.left_sidebar_mut().add_item(
-        "icons/folder-tree-16.svg",
-        ProjectPanel::new(project, cx).into(),
-    );
-    workspace.right_sidebar_mut().add_item(
-        "icons/user-16.svg",
-        cx.add_view(|cx| ContactsPanel::new(app_state.clone(), cx))
-            .into(),
-    );
-    workspace.right_sidebar_mut().add_item(
-        "icons/comment-16.svg",
-        cx.add_view(|cx| {
-            ChatPanel::new(app_state.client.clone(), app_state.channel_list.clone(), cx)
-        })
-        .into(),
-    );
+    let project_panel = ProjectPanel::new(project, cx);
+    let contact_panel = cx.add_view(|cx| ContactsPanel::new(app_state.clone(), cx));
+
+    workspace.left_sidebar().update(cx, |sidebar, cx| {
+        sidebar.add_item("icons/folder-tree-solid-14.svg", project_panel.into(), cx)
+    });
+    workspace.right_sidebar().update(cx, |sidebar, cx| {
+        sidebar.add_item("icons/contacts-solid-14.svg", contact_panel.into(), cx)
+    });
 
-    let diagnostic_message = cx.add_view(|_| editor::items::DiagnosticMessage::new());
     let diagnostic_summary =
-        cx.add_view(|cx| diagnostics::items::DiagnosticSummary::new(workspace.project(), cx));
+        cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace.project(), cx));
     let lsp_status = cx.add_view(|cx| {
         workspace::lsp_status::LspStatus::new(workspace.project(), app_state.languages.clone(), cx)
     });
@@ -198,10 +189,9 @@ pub fn build_workspace(
     let auto_update = cx.add_view(|cx| auto_update::AutoUpdateIndicator::new(cx));
     workspace.status_bar().update(cx, |status_bar, cx| {
         status_bar.add_left_item(diagnostic_summary, cx);
-        status_bar.add_left_item(diagnostic_message, cx);
         status_bar.add_left_item(lsp_status, cx);
-        status_bar.add_right_item(auto_update, cx);
         status_bar.add_right_item(cursor_position, cx);
+        status_bar.add_right_item(auto_update, cx);
     });
 
     workspace
@@ -362,7 +352,7 @@ mod tests {
         let workspace_1 = cx.root_view::<Workspace>(cx.window_ids()[0]).unwrap();
         workspace_1.update(cx, |workspace, cx| {
             assert_eq!(workspace.worktrees(cx).count(), 2);
-            assert!(workspace.left_sidebar_mut().active_item().is_some());
+            assert!(workspace.left_sidebar().read(cx).active_item().is_some());
             assert!(workspace.active_pane().is_focused(cx));
         });
 

styles/src/styleTree/app.ts 🔗

@@ -8,6 +8,7 @@ import projectPanel from "./projectPanel";
 import search from "./search";
 import selectorModal from "./selectorModal";
 import workspace from "./workspace";
+import projectDiagnostics from "./projectDiagnostics";
 
 export const panel = {
   padding: { top: 12, left: 12, bottom: 12, right: 12 },
@@ -18,18 +19,7 @@ export default function app(theme: Theme): Object {
     selector: selectorModal(theme),
     workspace: workspace(theme),
     editor: editor(theme),
-    projectDiagnostics: {
-      tabIconSpacing: 4,
-      tabIconWidth: 13,
-      tabSummarySpacing: 10,
-      emptyMessage: text(theme, "sans", "primary", { size: "lg" }),
-      statusBarItem: {
-        ...text(theme, "sans", "muted"),
-        margin: {
-          right: 10,
-        },
-      },
-    },
+    projectDiagnostics: projectDiagnostics(theme),
     commandPalette: commandPalette(theme),
     projectPanel: projectPanel(theme),
     chatPanel: chatPanel(theme),

styles/src/styleTree/projectDiagnostics.ts 🔗

@@ -0,0 +1,15 @@
+import Theme from "../themes/theme";
+import {
+  backgroundColor,
+  text,
+} from "./components";
+
+export default function projectDiagnostics(theme: Theme) {
+  return {
+    background: backgroundColor(theme, 500),
+    tabIconSpacing: 4,
+    tabIconWidth: 13,
+    tabSummarySpacing: 10,
+    emptyMessage: text(theme, "sans", "secondary", { size: "md" }),
+  }
+}

styles/src/styleTree/workspace.ts 🔗

@@ -2,20 +2,6 @@ import Theme from "../themes/theme";
 import { backgroundColor, border, iconColor, text } from "./components";
 
 export default function workspace(theme: Theme) {
-  const signInPrompt = {
-    ...text(theme, "sans", "secondary", { size: "xs" }),
-    border: border(theme, "primary"),
-    cornerRadius: 6,
-    margin: {
-      top: 1,
-      right: 6,
-    },
-    padding: {
-      left: 6,
-      right: 6,
-    },
-  };
-
   const tab = {
     height: 32,
     background: backgroundColor(theme, 300),
@@ -47,32 +33,6 @@ export default function workspace(theme: Theme) {
     },
   };
 
-  const sidebarItem = {
-    height: 32,
-    iconColor: iconColor(theme, "secondary"),
-    iconSize: 18,
-  };
-  const sidebar = {
-    width: 30,
-    background: backgroundColor(theme, 300),
-    border: border(theme, "primary", { right: true }),
-    item: sidebarItem,
-    activeItem: {
-      ...sidebarItem,
-      iconColor: iconColor(theme, "active"),
-    },
-    resizeHandle: {
-      background: border(theme, "primary").color,
-      padding: {
-        left: 1,
-      },
-    },
-  };
-  const shareIcon = {
-    margin: { top: 3, bottom: 2 },
-    cornerRadius: 6,
-  };
-
   return {
     background: backgroundColor(theme, 300),
     leaderBorderOpacity: 0.7,
@@ -86,31 +46,112 @@ export default function workspace(theme: Theme) {
       },
       cursor: "Arrow"
     },
-    leftSidebar: {
-      ...sidebar,
-      border: border(theme, "primary", { right: true }),
-    },
-    rightSidebar: {
-      ...sidebar,
-      border: border(theme, "primary", { left: true }),
+    sidebarResizeHandle: {
+      background: border(theme, "primary").color,
+      padding: {
+        left: 1,
+      },
     },
     paneDivider: {
       color: border(theme, "secondary").color,
       width: 1,
     },
-    status_bar: {
-      height: 24,
+    statusBar: {
+      height: 30,
       itemSpacing: 8,
       padding: {
+        top: 1,
+        bottom: 1,
         left: 6,
         right: 6,
       },
       border: border(theme, "primary", { top: true, overlay: true }),
       cursorPosition: text(theme, "sans", "muted"),
-      diagnosticMessage: text(theme, "sans", "muted"),
-      lspMessage: text(theme, "sans", "muted"),
       autoUpdateProgressMessage: text(theme, "sans", "muted"),
       autoUpdateDoneMessage: text(theme, "sans", "muted"),
+      lspStatus: {
+        iconSpacing: 4,
+        iconWidth: 14,
+        height: 18,
+        cornerRadius: 6,
+        padding: { left: 6, right: 6 },
+        message: text(theme, "sans", "muted"),
+        iconColor: iconColor(theme, "muted"),
+        hover: {
+          message: text(theme, "sans", "primary"),
+          iconColor: iconColor(theme, "primary"),
+          background: backgroundColor(theme, 300, "hovered"),
+        }
+      },
+      diagnosticMessage: {
+        ...text(theme, "sans", "muted"),
+        hover: text(theme, "sans", "secondary"),
+      },
+      diagnosticSummary: {
+        height: 16,
+        iconWidth: 14,
+        iconSpacing: 2,
+        summarySpacing: 6,
+        text: text(theme, "sans", "primary", { size: "sm" }),
+        iconColorOk: iconColor(theme, "secondary"),
+        iconColorWarning: iconColor(theme, "warning"),
+        iconColorError: iconColor(theme, "error"),
+        containerOk: {
+          cornerRadius: 6,
+          padding: { left: 6, right: 6 },
+          background: backgroundColor(theme, 300, "hovered"),
+        },
+        containerWarning: {
+          cornerRadius: 6,
+          padding: { left: 6, right: 6 },
+          background: backgroundColor(theme, "warning"),
+          border: border(theme, "warning"),
+        },
+        containerError: {
+          cornerRadius: 6,
+          padding: { left: 6, right: 6 },
+          background: backgroundColor(theme, "error"),
+          border: border(theme, "error"),
+        },
+        hover: {
+          iconColorOk: iconColor(theme, "primary"),
+          containerOk: {
+            cornerRadius: 6,
+            padding: { left: 6, right: 6 },
+            background: backgroundColor(theme, 300, "hovered"),
+          },
+          containerWarning: {
+            cornerRadius: 6,
+            padding: { left: 6, right: 6 },
+            background: backgroundColor(theme, "warning", "hovered"),
+            border: border(theme, "warning"),
+          },
+          containerError: {
+            cornerRadius: 6,
+            padding: { left: 6, right: 6 },
+            background: backgroundColor(theme, "error", "hovered"),
+            border: border(theme, "error"),
+          }
+        },
+      },
+      sidebarButtons: {
+        groupLeft: {},
+        groupRight: {},
+        item: {
+          iconSize: 14,
+          padding: { top: 3, bottom: 3, left: 6, right: 6 },
+          cornerRadius: 6,
+          iconColor: iconColor(theme, "secondary"),
+          hover: {
+            iconColor: iconColor(theme, "primary"),
+            background: backgroundColor(theme, 300, "hovered"),
+          },
+          active: {
+            iconColor: iconColor(theme, "active"),
+            background: backgroundColor(theme, 300, "active"),
+          }
+        },
+      },
     },
     titlebar: {
       avatarWidth: 18,
@@ -134,10 +175,19 @@ export default function workspace(theme: Theme) {
         // set with a token, not hardcoded in rust
       },
       border: border(theme, "primary", { bottom: true }),
-      signInPrompt,
-      hoveredSignInPrompt: {
-        ...signInPrompt,
-        ...text(theme, "sans", "active", { size: "xs" }),
+      signInPrompt: {
+        border: border(theme, "primary"),
+        cornerRadius: 6,
+        margin: {
+          top: 1,
+          right: 6,
+        },
+        padding: {
+          left: 6,
+          right: 6,
+        },
+        ...text(theme, "sans", "secondary", { size: "xs" }),
+        hover: text(theme, "sans", "active", { size: "xs" }),
       },
       offlineIcon: {
         color: iconColor(theme, "secondary"),
@@ -147,23 +197,21 @@ export default function workspace(theme: Theme) {
         },
       },
       shareIcon: {
-        ...shareIcon,
-        color: iconColor(theme, "secondary")
-      },
-      hoveredShareIcon: {
-        ...shareIcon,
-        background: backgroundColor(theme, 100, "hovered"),
+        cornerRadius: 6,
+        margin: { top: 3, bottom: 2 },
         color: iconColor(theme, "secondary"),
-      },
-      hoveredActiveShareIcon: {
-        ...shareIcon,
-        background: backgroundColor(theme, 100, "hovered"),
-        color: iconColor(theme, "active"),
-      },
-      activeShareIcon: {
-        ...shareIcon,
-        background: backgroundColor(theme, 100, "active"),
-        color: iconColor(theme, "active"),
+        hover: {
+          background: backgroundColor(theme, 100, "hovered"),
+          color: iconColor(theme, "secondary"),
+        },
+        active: {
+          background: backgroundColor(theme, 100, "active"),
+          color: iconColor(theme, "active"),
+        },
+        activeHover: {
+          background: backgroundColor(theme, 100, "hovered"),
+          color: iconColor(theme, "active"),
+        }
       },
       outdatedWarning: {
         ...text(theme, "sans", "warning"),

styles/src/themes/base16.ts 🔗

@@ -51,28 +51,28 @@ export function createTheme(name: string, isLight: boolean, neutral: ColorToken[
       focused: neutral[3],
     },
     ok: {
-      base: accent.green,
-      hovered: accent.green,
-      active: accent.green,
-      focused: accent.green,
+      base: withOpacity(accent.green, 0.15),
+      hovered: withOpacity(accent.green, 0.20),
+      active: withOpacity(accent.green, 0.25),
+      focused: withOpacity(accent.green, 0.20),
     },
     error: {
-      base: accent.red,
-      hovered: accent.red,
-      active: accent.red,
-      focused: accent.red,
+      base: withOpacity(accent.red, 0.15),
+      hovered: withOpacity(accent.red, 0.20),
+      active: withOpacity(accent.red, 0.25),
+      focused: withOpacity(accent.red, 0.20),
     },
     warning: {
-      base: accent.yellow,
-      hovered: accent.yellow,
-      active: accent.yellow,
-      focused: accent.yellow,
+      base: withOpacity(accent.yellow, 0.15),
+      hovered: withOpacity(accent.yellow, 0.20),
+      active: withOpacity(accent.yellow, 0.25),
+      focused: withOpacity(accent.yellow, 0.20),
     },
     info: {
-      base: accent.blue,
-      hovered: accent.blue,
-      active: accent.blue,
-      focused: accent.blue,
+      base: withOpacity(accent.blue, 0.15),
+      hovered: withOpacity(accent.blue, 0.20),
+      active: withOpacity(accent.blue, 0.25),
+      focused: withOpacity(accent.blue, 0.20),
     },
   };
 
@@ -82,10 +82,10 @@ export function createTheme(name: string, isLight: boolean, neutral: ColorToken[
     muted: neutral[3],
     focused: neutral[3],
     active: neutral[3],
-    ok: accent.green,
-    error: accent.red,
-    warning: accent.yellow,
-    info: accent.blue,
+    ok: withOpacity(accent.green, 0.15),
+    error: withOpacity(accent.red, 0.15),
+    warning: withOpacity(accent.yellow, 0.15),
+    info: withOpacity(accent.blue, 0.15),
   };
 
   const textColor = {

styles/src/themes/dark.ts 🔗

@@ -34,28 +34,28 @@ const backgroundColor = {
     focused: colors.neutral[800],
   },
   ok: {
-    base: colors.green[600],
-    hovered: colors.green[600],
-    active: colors.green[600],
-    focused: colors.green[600],
+    base: withOpacity(colors.green[600], 0.15),
+    hovered: withOpacity(colors.green[600], 0.20),
+    active: withOpacity(colors.green[600], 0.25),
+    focused: withOpacity(colors.green[600], 0.20),
   },
   error: {
-    base: colors.red[400],
-    hovered: colors.red[400],
-    active: colors.red[400],
-    focused: colors.red[400],
+    base: withOpacity(colors.red[600], 0.15),
+    hovered: withOpacity(colors.red[600], 0.20),
+    active: withOpacity(colors.red[600], 0.25),
+    focused: withOpacity(colors.red[600], 0.20),
   },
   warning: {
-    base: colors.amber[300],
-    hovered: colors.amber[300],
-    active: colors.amber[300],
-    focused: colors.amber[300],
+    base: withOpacity(colors.amber[400], 0.15),
+    hovered: withOpacity(colors.amber[400], 0.20),
+    active: withOpacity(colors.amber[400], 0.25),
+    focused: withOpacity(colors.amber[400], 0.20),
   },
   info: {
-    base: colors.blue[500],
-    hovered: colors.blue[500],
-    active: colors.blue[500],
-    focused: colors.blue[500],
+    base: withOpacity(colors.blue[500], 0.15),
+    hovered: withOpacity(colors.blue[500], 0.20),
+    active: withOpacity(colors.blue[500], 0.25),
+    focused: withOpacity(colors.blue[500], 0.20),
   },
 };
 
@@ -65,10 +65,10 @@ const borderColor = {
   muted: colors.neutral[675],
   focused: colors.indigo[500],
   active: colors.neutral[900],
-  ok: colors.green[500],
-  error: colors.red[500],
-  warning: colors.amber[500],
-  info: colors.blue[500],
+  ok: withOpacity(colors.green[600], 0.15),
+  error: withOpacity(colors.red[500], 0.15),
+  warning: withOpacity(colors.amber[400], 0.15),
+  info: withOpacity(colors.blue[500], 0.15),
 };
 
 const textColor = {
@@ -77,7 +77,6 @@ const textColor = {
   muted: colors.neutral[450],
   placeholder: colors.neutral[650],
   active: colors.neutral[0],
-  //TODO: (design) define feature and it's correct value
   feature: colors.blue[400],
   ok: colors.green[600],
   error: colors.red[400],
@@ -91,7 +90,6 @@ const iconColor = {
   muted: colors.neutral[600],
   placeholder: colors.neutral[700],
   active: colors.neutral[0],
-  //TODO: (design) define feature and it's correct value
   feature: colors.blue[500],
   ok: colors.green[600],
   error: colors.red[500],
@@ -124,7 +122,7 @@ const editor = {
   highlight: {
     selection: player[1].selectionColor,
     occurrence: withOpacity(colors.neutral[0], 0.12),
-    activeOccurrence: withOpacity(colors.neutral[0], 0.16), // TODO: This is not correctly hooked up to occurences on the rust side
+    activeOccurrence: withOpacity(colors.neutral[0], 0.16),
     matchingBracket: backgroundColor[500].active,
     match: withOpacity(colors.violet[700], 0.5),
     activeMatch: withOpacity(colors.violet[600], 0.7),

styles/src/themes/light.ts 🔗

@@ -34,28 +34,28 @@ const backgroundColor = {
     focused: colors.neutral[25],
   },
   ok: {
-    base: colors.green[100],
-    hovered: colors.green[100],
-    active: colors.green[100],
-    focused: colors.green[100],
+    base: withOpacity(colors.green[600], 0.15),
+    hovered: withOpacity(colors.green[600], 0.20),
+    active: withOpacity(colors.green[600], 0.25),
+    focused: withOpacity(colors.green[600], 0.20),
   },
   error: {
-    base: colors.red[100],
-    hovered: colors.red[100],
-    active: colors.red[100],
-    focused: colors.red[100],
+    base: withOpacity(colors.red[600], 0.15),
+    hovered: withOpacity(colors.red[600], 0.20),
+    active: withOpacity(colors.red[600], 0.25),
+    focused: withOpacity(colors.red[600], 0.20),
   },
   warning: {
-    base: colors.yellow[100],
-    hovered: colors.yellow[100],
-    active: colors.yellow[100],
-    focused: colors.yellow[100],
+    base: withOpacity(colors.amber[400], 0.15),
+    hovered: withOpacity(colors.amber[400], 0.20),
+    active: withOpacity(colors.amber[400], 0.25),
+    focused: withOpacity(colors.amber[400], 0.20),
   },
   info: {
-    base: colors.blue[100],
-    hovered: colors.blue[100],
-    active: colors.blue[100],
-    focused: colors.blue[100],
+    base: withOpacity(colors.blue[500], 0.15),
+    hovered: withOpacity(colors.blue[500], 0.20),
+    active: withOpacity(colors.blue[500], 0.25),
+    focused: withOpacity(colors.blue[500], 0.20),
   },
 };
 
@@ -65,10 +65,10 @@ const borderColor = {
   muted: colors.neutral[100],
   focused: colors.indigo[500],
   active: colors.neutral[250],
-  ok: colors.green[200],
-  error: colors.red[200],
-  warning: colors.yellow[200],
-  info: colors.blue[200],
+  ok: withOpacity(colors.green[600], 0.15),
+  error: withOpacity(colors.red[500], 0.15),
+  warning: withOpacity(colors.amber[400], 0.15),
+  info: withOpacity(colors.blue[500], 0.15),
 };
 
 const textColor = {
@@ -122,10 +122,10 @@ const editor = {
   highlight: {
     selection: player[1].selectionColor,
     occurrence: withOpacity(colors.neutral[900], 0.06),
-    activeOccurrence: withOpacity(colors.neutral[900], 0.16), // TODO: This is not hooked up to occurences on the rust side
+    activeOccurrence: withOpacity(colors.neutral[900], 0.16),
     matchingBracket: colors.neutral[0],
     match: colors.yellow[100],
-    activeMatch: colors.yellow[200], // TODO: This is not hooked up to occurences on the rust side
+    activeMatch: colors.yellow[200],
     related: colors.neutral[0],
   },
   gutter: {