Start on contact finder modal

Nathan Sobo and Antonio Scandurra created

Co-Authored-By: Antonio Scandurra <me@as-cii.com>

Change summary

assets/themes/cave-dark.json                | 145 ++++++++++++++++++
assets/themes/cave-light.json               | 145 ++++++++++++++++++
assets/themes/dark.json                     | 145 ++++++++++++++++++
assets/themes/light.json                    | 145 ++++++++++++++++++
assets/themes/solarized-dark.json           | 145 ++++++++++++++++++
assets/themes/solarized-light.json          | 145 ++++++++++++++++++
assets/themes/sulphurpool-dark.json         | 145 ++++++++++++++++++
assets/themes/sulphurpool-light.json        | 145 ++++++++++++++++++
crates/contacts_panel/src/contact_finder.rs | 175 ++++++++++++++++++++++
crates/contacts_panel/src/contacts_panel.rs | 177 +++++-----------------
crates/theme/src/theme.rs                   |  16 +
crates/workspace/src/workspace.rs           |   6 
styles/src/styleTree/app.ts                 |   2 
styles/src/styleTree/contactFinder.ts       |  42 +++++
styles/src/styleTree/contactsPanel.ts       |   4 
15 files changed, 1,438 insertions(+), 144 deletions(-)

Detailed changes

assets/themes/cave-dark.json 🔗

@@ -1261,7 +1261,7 @@
         "left": 8
       }
     },
-    "edit_contact": {
+    "contact_button": {
       "family": "Zed Mono",
       "color": "#e2dfe7",
       "size": 14,
@@ -1378,6 +1378,149 @@
       "corner_radius": 6
     }
   },
+  "contact_finder": {
+    "background": "#26232a",
+    "corner_radius": 8,
+    "padding": 8,
+    "item": {
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 4
+      },
+      "corner_radius": 8,
+      "text": {
+        "family": "Zed Sans",
+        "color": "#8b8792",
+        "size": 14
+      },
+      "highlight_text": {
+        "family": "Zed Sans",
+        "color": "#576ddb",
+        "weight": "bold",
+        "size": 14
+      },
+      "active": {
+        "background": "#5852605c",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#e2dfe7",
+          "size": 14
+        }
+      },
+      "hover": {
+        "background": "#5852603d"
+      }
+    },
+    "border": {
+      "color": "#19171c",
+      "width": 1
+    },
+    "empty": {
+      "text": {
+        "family": "Zed Sans",
+        "color": "#7e7887",
+        "size": 14
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 8
+      }
+    },
+    "input_editor": {
+      "background": "#19171c",
+      "corner_radius": 8,
+      "placeholder_text": {
+        "family": "Zed Sans",
+        "color": "#7e7887",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#576ddb",
+        "selection": "#576ddb3d"
+      },
+      "text": {
+        "family": "Zed Mono",
+        "color": "#e2dfe7",
+        "size": 14
+      },
+      "border": {
+        "color": "#26232a",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 7,
+        "left": 16,
+        "right": 16,
+        "top": 7
+      }
+    },
+    "shadow": {
+      "blur": 16,
+      "color": "#0000003d",
+      "offset": [
+        0,
+        2
+      ]
+    },
+    "max_width": 540,
+    "max_height": 420,
+    "query_editor": {
+      "background": "#19171c",
+      "corner_radius": 6,
+      "text": {
+        "family": "Zed Mono",
+        "color": "#e2dfe7",
+        "size": 14
+      },
+      "placeholder_text": {
+        "family": "Zed Mono",
+        "color": "#7e7887",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#576ddb",
+        "selection": "#576ddb3d"
+      },
+      "border": {
+        "color": "#26232a",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 8,
+        "right": 8,
+        "top": 4
+      }
+    },
+    "row_height": 28,
+    "contact_avatar": {
+      "corner_radius": 10,
+      "width": 18
+    },
+    "contact_username": {
+      "family": "Zed Mono",
+      "color": "#e2dfe7",
+      "size": 14,
+      "padding": {
+        "left": 8
+      }
+    },
+    "contact_button": {
+      "family": "Zed Mono",
+      "color": "#e2dfe7",
+      "size": 14,
+      "background": "#26232a",
+      "corner_radius": 12,
+      "padding": {
+        "left": 7,
+        "right": 7
+      }
+    }
+  },
   "search": {
     "match_background": "#955ae77a",
     "tab_icon_spacing": 8,

assets/themes/cave-light.json 🔗

@@ -1261,7 +1261,7 @@
         "left": 8
       }
     },
-    "edit_contact": {
+    "contact_button": {
       "family": "Zed Mono",
       "color": "#26232a",
       "size": 14,
@@ -1378,6 +1378,149 @@
       "corner_radius": 6
     }
   },
+  "contact_finder": {
+    "background": "#e2dfe7",
+    "corner_radius": 8,
+    "padding": 8,
+    "item": {
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 4
+      },
+      "corner_radius": 8,
+      "text": {
+        "family": "Zed Sans",
+        "color": "#585260",
+        "size": 14
+      },
+      "highlight_text": {
+        "family": "Zed Sans",
+        "color": "#576ddb",
+        "weight": "bold",
+        "size": 14
+      },
+      "active": {
+        "background": "#8b87922e",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#26232a",
+          "size": 14
+        }
+      },
+      "hover": {
+        "background": "#8b87921f"
+      }
+    },
+    "border": {
+      "color": "#efecf4",
+      "width": 1
+    },
+    "empty": {
+      "text": {
+        "family": "Zed Sans",
+        "color": "#655f6d",
+        "size": 14
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 8
+      }
+    },
+    "input_editor": {
+      "background": "#efecf4",
+      "corner_radius": 8,
+      "placeholder_text": {
+        "family": "Zed Sans",
+        "color": "#655f6d",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#576ddb",
+        "selection": "#576ddb3d"
+      },
+      "text": {
+        "family": "Zed Mono",
+        "color": "#26232a",
+        "size": 14
+      },
+      "border": {
+        "color": "#e2dfe7",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 7,
+        "left": 16,
+        "right": 16,
+        "top": 7
+      }
+    },
+    "shadow": {
+      "blur": 16,
+      "color": "#0000001f",
+      "offset": [
+        0,
+        2
+      ]
+    },
+    "max_width": 540,
+    "max_height": 420,
+    "query_editor": {
+      "background": "#efecf4",
+      "corner_radius": 6,
+      "text": {
+        "family": "Zed Mono",
+        "color": "#26232a",
+        "size": 14
+      },
+      "placeholder_text": {
+        "family": "Zed Mono",
+        "color": "#655f6d",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#576ddb",
+        "selection": "#576ddb3d"
+      },
+      "border": {
+        "color": "#e2dfe7",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 8,
+        "right": 8,
+        "top": 4
+      }
+    },
+    "row_height": 28,
+    "contact_avatar": {
+      "corner_radius": 10,
+      "width": 18
+    },
+    "contact_username": {
+      "family": "Zed Mono",
+      "color": "#26232a",
+      "size": 14,
+      "padding": {
+        "left": 8
+      }
+    },
+    "contact_button": {
+      "family": "Zed Mono",
+      "color": "#26232a",
+      "size": 14,
+      "background": "#e2dfe7",
+      "corner_radius": 12,
+      "padding": {
+        "left": 7,
+        "right": 7
+      }
+    }
+  },
   "search": {
     "match_background": "#955ae73d",
     "tab_icon_spacing": 8,

assets/themes/dark.json 🔗

@@ -1261,7 +1261,7 @@
         "left": 8
       }
     },
-    "edit_contact": {
+    "contact_button": {
       "family": "Zed Mono",
       "color": "#f1f1f1",
       "size": 14,
@@ -1378,6 +1378,149 @@
       "corner_radius": 6
     }
   },
+  "contact_finder": {
+    "background": "#1c1c1c",
+    "corner_radius": 8,
+    "padding": 8,
+    "item": {
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 4
+      },
+      "corner_radius": 8,
+      "text": {
+        "family": "Zed Sans",
+        "color": "#9c9c9c",
+        "size": 14
+      },
+      "highlight_text": {
+        "family": "Zed Sans",
+        "color": "#4f8ff7",
+        "weight": "bold",
+        "size": 14
+      },
+      "active": {
+        "background": "#2b2b2b",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#f1f1f1",
+          "size": 14
+        }
+      },
+      "hover": {
+        "background": "#232323"
+      }
+    },
+    "border": {
+      "color": "#070707",
+      "width": 1
+    },
+    "empty": {
+      "text": {
+        "family": "Zed Sans",
+        "color": "#474747",
+        "size": 14
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 8
+      }
+    },
+    "input_editor": {
+      "background": "#000000",
+      "corner_radius": 8,
+      "placeholder_text": {
+        "family": "Zed Sans",
+        "color": "#474747",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#2472f2",
+        "selection": "#2472f23d"
+      },
+      "text": {
+        "family": "Zed Mono",
+        "color": "#f1f1f1",
+        "size": 14
+      },
+      "border": {
+        "color": "#232323",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 7,
+        "left": 16,
+        "right": 16,
+        "top": 7
+      }
+    },
+    "shadow": {
+      "blur": 16,
+      "color": "#00000052",
+      "offset": [
+        0,
+        2
+      ]
+    },
+    "max_width": 540,
+    "max_height": 420,
+    "query_editor": {
+      "background": "#000000",
+      "corner_radius": 6,
+      "text": {
+        "family": "Zed Mono",
+        "color": "#f1f1f1",
+        "size": 14
+      },
+      "placeholder_text": {
+        "family": "Zed Mono",
+        "color": "#474747",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#2472f2",
+        "selection": "#2472f23d"
+      },
+      "border": {
+        "color": "#232323",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 8,
+        "right": 8,
+        "top": 4
+      }
+    },
+    "row_height": 28,
+    "contact_avatar": {
+      "corner_radius": 10,
+      "width": 18
+    },
+    "contact_username": {
+      "family": "Zed Mono",
+      "color": "#f1f1f1",
+      "size": 14,
+      "padding": {
+        "left": 8
+      }
+    },
+    "contact_button": {
+      "family": "Zed Mono",
+      "color": "#f1f1f1",
+      "size": 14,
+      "background": "#2b2b2b",
+      "corner_radius": 12,
+      "padding": {
+        "left": 7,
+        "right": 7
+      }
+    }
+  },
   "search": {
     "match_background": "#3f15a380",
     "tab_icon_spacing": 8,

assets/themes/light.json 🔗

@@ -1261,7 +1261,7 @@
         "left": 8
       }
     },
-    "edit_contact": {
+    "contact_button": {
       "family": "Zed Mono",
       "color": "#2b2b2b",
       "size": 14,
@@ -1378,6 +1378,149 @@
       "corner_radius": 6
     }
   },
+  "contact_finder": {
+    "background": "#f8f8f8",
+    "corner_radius": 8,
+    "padding": 8,
+    "item": {
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 4
+      },
+      "corner_radius": 8,
+      "text": {
+        "family": "Zed Sans",
+        "color": "#474747",
+        "size": 14
+      },
+      "highlight_text": {
+        "family": "Zed Sans",
+        "color": "#484bed",
+        "weight": "bold",
+        "size": 14
+      },
+      "active": {
+        "background": "#e3e3e3",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#2b2b2b",
+          "size": 14
+        }
+      },
+      "hover": {
+        "background": "#eaeaea"
+      }
+    },
+    "border": {
+      "color": "#d5d5d5",
+      "width": 1
+    },
+    "empty": {
+      "text": {
+        "family": "Zed Sans",
+        "color": "#808080",
+        "size": 14
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 8
+      }
+    },
+    "input_editor": {
+      "background": "#ffffff",
+      "corner_radius": 8,
+      "placeholder_text": {
+        "family": "Zed Sans",
+        "color": "#808080",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#2472f2",
+        "selection": "#2472f23d"
+      },
+      "text": {
+        "family": "Zed Mono",
+        "color": "#2b2b2b",
+        "size": 14
+      },
+      "border": {
+        "color": "#d5d5d5",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 7,
+        "left": 16,
+        "right": 16,
+        "top": 7
+      }
+    },
+    "shadow": {
+      "blur": 16,
+      "color": "#0000001f",
+      "offset": [
+        0,
+        2
+      ]
+    },
+    "max_width": 540,
+    "max_height": 420,
+    "query_editor": {
+      "background": "#ffffff",
+      "corner_radius": 6,
+      "text": {
+        "family": "Zed Mono",
+        "color": "#2b2b2b",
+        "size": 14
+      },
+      "placeholder_text": {
+        "family": "Zed Mono",
+        "color": "#808080",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#2472f2",
+        "selection": "#2472f23d"
+      },
+      "border": {
+        "color": "#d5d5d5",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 8,
+        "right": 8,
+        "top": 4
+      }
+    },
+    "row_height": 28,
+    "contact_avatar": {
+      "corner_radius": 10,
+      "width": 18
+    },
+    "contact_username": {
+      "family": "Zed Mono",
+      "color": "#2b2b2b",
+      "size": 14,
+      "padding": {
+        "left": 8
+      }
+    },
+    "contact_button": {
+      "family": "Zed Mono",
+      "color": "#2b2b2b",
+      "size": 14,
+      "background": "#eaeaea",
+      "corner_radius": 12,
+      "padding": {
+        "left": 7,
+        "right": 7
+      }
+    }
+  },
   "search": {
     "match_background": "#fce9b7",
     "tab_icon_spacing": 8,

assets/themes/solarized-dark.json 🔗

@@ -1261,7 +1261,7 @@
         "left": 8
       }
     },
-    "edit_contact": {
+    "contact_button": {
       "family": "Zed Mono",
       "color": "#eee8d5",
       "size": 14,
@@ -1378,6 +1378,149 @@
       "corner_radius": 6
     }
   },
+  "contact_finder": {
+    "background": "#073642",
+    "corner_radius": 8,
+    "padding": 8,
+    "item": {
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 4
+      },
+      "corner_radius": 8,
+      "text": {
+        "family": "Zed Sans",
+        "color": "#93a1a1",
+        "size": 14
+      },
+      "highlight_text": {
+        "family": "Zed Sans",
+        "color": "#268bd2",
+        "weight": "bold",
+        "size": 14
+      },
+      "active": {
+        "background": "#586e755c",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#eee8d5",
+          "size": 14
+        }
+      },
+      "hover": {
+        "background": "#586e753d"
+      }
+    },
+    "border": {
+      "color": "#002b36",
+      "width": 1
+    },
+    "empty": {
+      "text": {
+        "family": "Zed Sans",
+        "color": "#839496",
+        "size": 14
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 8
+      }
+    },
+    "input_editor": {
+      "background": "#002b36",
+      "corner_radius": 8,
+      "placeholder_text": {
+        "family": "Zed Sans",
+        "color": "#839496",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#268bd2",
+        "selection": "#268bd23d"
+      },
+      "text": {
+        "family": "Zed Mono",
+        "color": "#eee8d5",
+        "size": 14
+      },
+      "border": {
+        "color": "#073642",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 7,
+        "left": 16,
+        "right": 16,
+        "top": 7
+      }
+    },
+    "shadow": {
+      "blur": 16,
+      "color": "#0000003d",
+      "offset": [
+        0,
+        2
+      ]
+    },
+    "max_width": 540,
+    "max_height": 420,
+    "query_editor": {
+      "background": "#002b36",
+      "corner_radius": 6,
+      "text": {
+        "family": "Zed Mono",
+        "color": "#eee8d5",
+        "size": 14
+      },
+      "placeholder_text": {
+        "family": "Zed Mono",
+        "color": "#839496",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#268bd2",
+        "selection": "#268bd23d"
+      },
+      "border": {
+        "color": "#073642",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 8,
+        "right": 8,
+        "top": 4
+      }
+    },
+    "row_height": 28,
+    "contact_avatar": {
+      "corner_radius": 10,
+      "width": 18
+    },
+    "contact_username": {
+      "family": "Zed Mono",
+      "color": "#eee8d5",
+      "size": 14,
+      "padding": {
+        "left": 8
+      }
+    },
+    "contact_button": {
+      "family": "Zed Mono",
+      "color": "#eee8d5",
+      "size": 14,
+      "background": "#073642",
+      "corner_radius": 12,
+      "padding": {
+        "left": 7,
+        "right": 7
+      }
+    }
+  },
   "search": {
     "match_background": "#6c71c47a",
     "tab_icon_spacing": 8,

assets/themes/solarized-light.json 🔗

@@ -1261,7 +1261,7 @@
         "left": 8
       }
     },
-    "edit_contact": {
+    "contact_button": {
       "family": "Zed Mono",
       "color": "#073642",
       "size": 14,
@@ -1378,6 +1378,149 @@
       "corner_radius": 6
     }
   },
+  "contact_finder": {
+    "background": "#eee8d5",
+    "corner_radius": 8,
+    "padding": 8,
+    "item": {
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 4
+      },
+      "corner_radius": 8,
+      "text": {
+        "family": "Zed Sans",
+        "color": "#586e75",
+        "size": 14
+      },
+      "highlight_text": {
+        "family": "Zed Sans",
+        "color": "#268bd2",
+        "weight": "bold",
+        "size": 14
+      },
+      "active": {
+        "background": "#93a1a12e",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#073642",
+          "size": 14
+        }
+      },
+      "hover": {
+        "background": "#93a1a11f"
+      }
+    },
+    "border": {
+      "color": "#fdf6e3",
+      "width": 1
+    },
+    "empty": {
+      "text": {
+        "family": "Zed Sans",
+        "color": "#657b83",
+        "size": 14
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 8
+      }
+    },
+    "input_editor": {
+      "background": "#fdf6e3",
+      "corner_radius": 8,
+      "placeholder_text": {
+        "family": "Zed Sans",
+        "color": "#657b83",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#268bd2",
+        "selection": "#268bd23d"
+      },
+      "text": {
+        "family": "Zed Mono",
+        "color": "#073642",
+        "size": 14
+      },
+      "border": {
+        "color": "#eee8d5",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 7,
+        "left": 16,
+        "right": 16,
+        "top": 7
+      }
+    },
+    "shadow": {
+      "blur": 16,
+      "color": "#0000001f",
+      "offset": [
+        0,
+        2
+      ]
+    },
+    "max_width": 540,
+    "max_height": 420,
+    "query_editor": {
+      "background": "#fdf6e3",
+      "corner_radius": 6,
+      "text": {
+        "family": "Zed Mono",
+        "color": "#073642",
+        "size": 14
+      },
+      "placeholder_text": {
+        "family": "Zed Mono",
+        "color": "#657b83",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#268bd2",
+        "selection": "#268bd23d"
+      },
+      "border": {
+        "color": "#eee8d5",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 8,
+        "right": 8,
+        "top": 4
+      }
+    },
+    "row_height": 28,
+    "contact_avatar": {
+      "corner_radius": 10,
+      "width": 18
+    },
+    "contact_username": {
+      "family": "Zed Mono",
+      "color": "#073642",
+      "size": 14,
+      "padding": {
+        "left": 8
+      }
+    },
+    "contact_button": {
+      "family": "Zed Mono",
+      "color": "#073642",
+      "size": 14,
+      "background": "#eee8d5",
+      "corner_radius": 12,
+      "padding": {
+        "left": 7,
+        "right": 7
+      }
+    }
+  },
   "search": {
     "match_background": "#6c71c43d",
     "tab_icon_spacing": 8,

assets/themes/sulphurpool-dark.json 🔗

@@ -1261,7 +1261,7 @@
         "left": 8
       }
     },
-    "edit_contact": {
+    "contact_button": {
       "family": "Zed Mono",
       "color": "#dfe2f1",
       "size": 14,
@@ -1378,6 +1378,149 @@
       "corner_radius": 6
     }
   },
+  "contact_finder": {
+    "background": "#293256",
+    "corner_radius": 8,
+    "padding": 8,
+    "item": {
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 4
+      },
+      "corner_radius": 8,
+      "text": {
+        "family": "Zed Sans",
+        "color": "#979db4",
+        "size": 14
+      },
+      "highlight_text": {
+        "family": "Zed Sans",
+        "color": "#3d8fd1",
+        "weight": "bold",
+        "size": 14
+      },
+      "active": {
+        "background": "#5e66875c",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#dfe2f1",
+          "size": 14
+        }
+      },
+      "hover": {
+        "background": "#5e66873d"
+      }
+    },
+    "border": {
+      "color": "#202746",
+      "width": 1
+    },
+    "empty": {
+      "text": {
+        "family": "Zed Sans",
+        "color": "#898ea4",
+        "size": 14
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 8
+      }
+    },
+    "input_editor": {
+      "background": "#202746",
+      "corner_radius": 8,
+      "placeholder_text": {
+        "family": "Zed Sans",
+        "color": "#898ea4",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#3d8fd1",
+        "selection": "#3d8fd13d"
+      },
+      "text": {
+        "family": "Zed Mono",
+        "color": "#dfe2f1",
+        "size": 14
+      },
+      "border": {
+        "color": "#293256",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 7,
+        "left": 16,
+        "right": 16,
+        "top": 7
+      }
+    },
+    "shadow": {
+      "blur": 16,
+      "color": "#0000003d",
+      "offset": [
+        0,
+        2
+      ]
+    },
+    "max_width": 540,
+    "max_height": 420,
+    "query_editor": {
+      "background": "#202746",
+      "corner_radius": 6,
+      "text": {
+        "family": "Zed Mono",
+        "color": "#dfe2f1",
+        "size": 14
+      },
+      "placeholder_text": {
+        "family": "Zed Mono",
+        "color": "#898ea4",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#3d8fd1",
+        "selection": "#3d8fd13d"
+      },
+      "border": {
+        "color": "#293256",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 8,
+        "right": 8,
+        "top": 4
+      }
+    },
+    "row_height": 28,
+    "contact_avatar": {
+      "corner_radius": 10,
+      "width": 18
+    },
+    "contact_username": {
+      "family": "Zed Mono",
+      "color": "#dfe2f1",
+      "size": 14,
+      "padding": {
+        "left": 8
+      }
+    },
+    "contact_button": {
+      "family": "Zed Mono",
+      "color": "#dfe2f1",
+      "size": 14,
+      "background": "#293256",
+      "corner_radius": 12,
+      "padding": {
+        "left": 7,
+        "right": 7
+      }
+    }
+  },
   "search": {
     "match_background": "#6679cc7a",
     "tab_icon_spacing": 8,

assets/themes/sulphurpool-light.json 🔗

@@ -1261,7 +1261,7 @@
         "left": 8
       }
     },
-    "edit_contact": {
+    "contact_button": {
       "family": "Zed Mono",
       "color": "#293256",
       "size": 14,
@@ -1378,6 +1378,149 @@
       "corner_radius": 6
     }
   },
+  "contact_finder": {
+    "background": "#dfe2f1",
+    "corner_radius": 8,
+    "padding": 8,
+    "item": {
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 4
+      },
+      "corner_radius": 8,
+      "text": {
+        "family": "Zed Sans",
+        "color": "#5e6687",
+        "size": 14
+      },
+      "highlight_text": {
+        "family": "Zed Sans",
+        "color": "#3d8fd1",
+        "weight": "bold",
+        "size": 14
+      },
+      "active": {
+        "background": "#979db42e",
+        "text": {
+          "family": "Zed Sans",
+          "color": "#293256",
+          "size": 14
+        }
+      },
+      "hover": {
+        "background": "#979db41f"
+      }
+    },
+    "border": {
+      "color": "#f5f7ff",
+      "width": 1
+    },
+    "empty": {
+      "text": {
+        "family": "Zed Sans",
+        "color": "#6b7394",
+        "size": 14
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 12,
+        "right": 12,
+        "top": 8
+      }
+    },
+    "input_editor": {
+      "background": "#f5f7ff",
+      "corner_radius": 8,
+      "placeholder_text": {
+        "family": "Zed Sans",
+        "color": "#6b7394",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#3d8fd1",
+        "selection": "#3d8fd13d"
+      },
+      "text": {
+        "family": "Zed Mono",
+        "color": "#293256",
+        "size": 14
+      },
+      "border": {
+        "color": "#dfe2f1",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 7,
+        "left": 16,
+        "right": 16,
+        "top": 7
+      }
+    },
+    "shadow": {
+      "blur": 16,
+      "color": "#0000001f",
+      "offset": [
+        0,
+        2
+      ]
+    },
+    "max_width": 540,
+    "max_height": 420,
+    "query_editor": {
+      "background": "#f5f7ff",
+      "corner_radius": 6,
+      "text": {
+        "family": "Zed Mono",
+        "color": "#293256",
+        "size": 14
+      },
+      "placeholder_text": {
+        "family": "Zed Mono",
+        "color": "#6b7394",
+        "size": 14
+      },
+      "selection": {
+        "cursor": "#3d8fd1",
+        "selection": "#3d8fd13d"
+      },
+      "border": {
+        "color": "#dfe2f1",
+        "width": 1
+      },
+      "padding": {
+        "bottom": 4,
+        "left": 8,
+        "right": 8,
+        "top": 4
+      }
+    },
+    "row_height": 28,
+    "contact_avatar": {
+      "corner_radius": 10,
+      "width": 18
+    },
+    "contact_username": {
+      "family": "Zed Mono",
+      "color": "#293256",
+      "size": 14,
+      "padding": {
+        "left": 8
+      }
+    },
+    "contact_button": {
+      "family": "Zed Mono",
+      "color": "#293256",
+      "size": 14,
+      "background": "#dfe2f1",
+      "corner_radius": 12,
+      "padding": {
+        "left": 7,
+        "right": 7
+      }
+    }
+  },
   "search": {
     "match_background": "#6679cc3d",
     "tab_icon_spacing": 8,

crates/contacts_panel/src/contact_finder.rs 🔗

@@ -0,0 +1,175 @@
+use client::{ContactRequestStatus, User, UserStore};
+use editor::Editor;
+use gpui::{
+    color::Color, elements::*, platform::CursorStyle, Entity, LayoutContext, ModelHandle,
+    RenderContext, Task, View, ViewContext, ViewHandle,
+};
+use settings::Settings;
+use std::sync::Arc;
+use util::TryFutureExt;
+
+use crate::{RemoveContact, RequestContact};
+
+pub struct ContactFinder {
+    query_editor: ViewHandle<Editor>,
+    list_state: UniformListState,
+    potential_contacts: Arc<[Arc<User>]>,
+    user_store: ModelHandle<UserStore>,
+    contacts_search_task: Option<Task<Option<()>>>,
+}
+
+impl Entity for ContactFinder {
+    type Event = ();
+}
+
+impl View for ContactFinder {
+    fn ui_name() -> &'static str {
+        "ContactFinder"
+    }
+
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        let theme = cx.global::<Settings>().theme.clone();
+        let user_store = self.user_store.clone();
+        let potential_contacts = self.potential_contacts.clone();
+        Flex::column()
+            .with_child(
+                ChildView::new(self.query_editor.clone())
+                    .contained()
+                    .with_style(theme.contact_finder.query_editor.container)
+                    .boxed(),
+            )
+            .with_child(
+                UniformList::new(self.list_state.clone(), self.potential_contacts.len(), {
+                    let theme = theme.clone();
+                    move |range, items, cx| {
+                        items.extend(range.map(|ix| {
+                            Self::render_potential_contact(
+                                &potential_contacts[ix],
+                                &user_store,
+                                &theme.contact_finder,
+                                cx,
+                            )
+                        }))
+                    }
+                })
+                .flex(1., false)
+                .boxed(),
+            )
+            .contained()
+            .with_style(theme.contact_finder.container)
+            .constrained()
+            .with_max_width(theme.contact_finder.max_width)
+            .with_max_height(theme.contact_finder.max_height)
+            .boxed()
+    }
+}
+
+impl ContactFinder {
+    pub fn new(user_store: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) -> Self {
+        let query_editor = cx.add_view(|cx| {
+            Editor::single_line(Some(|theme| theme.contact_finder.query_editor.clone()), cx)
+        });
+
+        cx.subscribe(&query_editor, |this, _, event, cx| {
+            if let editor::Event::BufferEdited = event {
+                this.query_changed(cx)
+            }
+        })
+        .detach();
+        Self {
+            query_editor,
+            list_state: Default::default(),
+            potential_contacts: Arc::from([]),
+            user_store,
+            contacts_search_task: None,
+        }
+    }
+
+    fn render_potential_contact(
+        contact: &User,
+        user_store: &ModelHandle<UserStore>,
+        theme: &theme::ContactFinder,
+        cx: &mut LayoutContext,
+    ) -> ElementBox {
+        enum RequestContactButton {}
+
+        let contact_id = contact.id;
+        let request_status = user_store.read(cx).contact_request_status(&contact);
+
+        Flex::row()
+            .with_children(contact.avatar.clone().map(|avatar| {
+                Image::new(avatar)
+                    .with_style(theme.contact_avatar)
+                    .aligned()
+                    .left()
+                    .boxed()
+            }))
+            .with_child(
+                Label::new(
+                    contact.github_login.clone(),
+                    theme.contact_username.text.clone(),
+                )
+                .contained()
+                .with_style(theme.contact_username.container)
+                .aligned()
+                .left()
+                .boxed(),
+            )
+            .with_child(
+                MouseEventHandler::new::<RequestContactButton, _, _>(
+                    contact.id as usize,
+                    cx,
+                    |_, _| {
+                        let label = match request_status {
+                            ContactRequestStatus::None | ContactRequestStatus::RequestReceived => {
+                                "+"
+                            }
+                            ContactRequestStatus::RequestSent => "-",
+                            ContactRequestStatus::Pending
+                            | ContactRequestStatus::RequestAccepted => "…",
+                        };
+
+                        Label::new(label.to_string(), theme.contact_button.text.clone())
+                            .contained()
+                            .with_style(theme.contact_button.container)
+                            .aligned()
+                            .flex_float()
+                            .boxed()
+                    },
+                )
+                .on_click(move |_, cx| match request_status {
+                    ContactRequestStatus::None => {
+                        cx.dispatch_action(RequestContact(contact_id));
+                    }
+                    ContactRequestStatus::RequestSent => {
+                        cx.dispatch_action(RemoveContact(contact_id));
+                    }
+                    _ => {}
+                })
+                .with_cursor_style(CursorStyle::PointingHand)
+                .boxed(),
+            )
+            .constrained()
+            .with_height(theme.row_height)
+            .boxed()
+    }
+
+    fn query_changed(&mut self, cx: &mut ViewContext<Self>) {
+        let query = self.query_editor.read(cx).text(cx);
+        let search_users = self
+            .user_store
+            .update(cx, |store, cx| store.fuzzy_search_users(query, cx));
+
+        self.contacts_search_task = Some(cx.spawn(|this, mut cx| {
+            async move {
+                let potential_contacts = search_users.await?;
+                this.update(&mut cx, |this, cx| {
+                    this.potential_contacts = potential_contacts.into();
+                    cx.notify();
+                });
+                Ok(())
+            }
+            .log_err()
+        }));
+    }
+}

crates/contacts_panel/src/contacts_panel.rs 🔗

@@ -1,20 +1,24 @@
+mod contact_finder;
+
 use client::{Contact, ContactRequestStatus, User, UserStore};
+use contact_finder::ContactFinder;
 use editor::Editor;
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
+    actions,
     elements::*,
     geometry::{rect::RectF, vector::vec2f},
     impl_actions,
     platform::CursorStyle,
     Element, ElementBox, Entity, LayoutContext, ModelHandle, MutableAppContext, RenderContext,
-    Subscription, Task, View, ViewContext, ViewHandle,
+    Subscription, View, ViewContext, ViewHandle,
 };
 use serde::Deserialize;
 use settings::Settings;
 use std::sync::Arc;
-use util::TryFutureExt;
-use workspace::{AppState, JoinProject};
+use workspace::{AppState, JoinProject, Workspace};
 
+actions!(contacts_panel, [FindNewContacts]);
 impl_actions!(
     contacts_panel,
     [RequestContact, RemoveContact, RespondToContactRequest]
@@ -26,16 +30,13 @@ enum ContactEntry {
     IncomingRequest(Arc<User>),
     OutgoingRequest(Arc<User>),
     Contact(Arc<Contact>),
-    PotentialContact(Arc<User>),
 }
 
 pub struct ContactsPanel {
     entries: Vec<ContactEntry>,
     match_candidates: Vec<StringMatchCandidate>,
-    potential_contacts: Vec<Arc<User>>,
     list_state: ListState,
     user_store: ModelHandle<UserStore>,
-    contacts_search_task: Option<Task<Option<()>>>,
     user_query_editor: ViewHandle<Editor>,
     _maintain_contacts: Subscription,
 }
@@ -56,6 +57,7 @@ pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(ContactsPanel::request_contact);
     cx.add_action(ContactsPanel::remove_contact);
     cx.add_action(ContactsPanel::respond_to_contact_request);
+    cx.add_action(ContactsPanel::find_new_contacts);
 }
 
 impl ContactsPanel {
@@ -69,7 +71,7 @@ impl ContactsPanel {
 
         cx.subscribe(&user_query_editor, |this, _, event, cx| {
             if let editor::Event::BufferEdited = event {
-                this.query_changed(cx)
+                this.update_entries(cx)
             }
         })
         .detach();
@@ -120,22 +122,14 @@ impl ContactsPanel {
                             theme,
                             cx,
                         ),
-                        ContactEntry::PotentialContact(user) => Self::render_potential_contact(
-                            user.clone(),
-                            this.user_store.clone(),
-                            theme,
-                            cx,
-                        ),
                     }
                 }
             }),
             entries: Default::default(),
-            potential_contacts: Default::default(),
             match_candidates: Default::default(),
             user_query_editor,
             _maintain_contacts: cx
                 .observe(&app_state.user_store, |this, _, cx| this.update_entries(cx)),
-            contacts_search_task: None,
             user_store: app_state.user_store.clone(),
         };
         this.update_entries(cx);
@@ -347,9 +341,9 @@ impl ContactsPanel {
 
         if request_status == ContactRequestStatus::Pending {
             row.add_child(
-                Label::new("…".to_string(), theme.edit_contact.text.clone())
+                Label::new("…".to_string(), theme.contact_button.text.clone())
                     .contained()
-                    .with_style(theme.edit_contact.container)
+                    .with_style(theme.contact_button.container)
                     .aligned()
                     .flex_float()
                     .boxed(),
@@ -357,9 +351,9 @@ impl ContactsPanel {
         } else {
             row.add_children([
                 MouseEventHandler::new::<Reject, _, _>(user.id as usize, cx, |_, _| {
-                    Label::new("Reject".to_string(), theme.edit_contact.text.clone())
+                    Label::new("Reject".to_string(), theme.contact_button.text.clone())
                         .contained()
-                        .with_style(theme.edit_contact.container)
+                        .with_style(theme.contact_button.container)
                         .aligned()
                         .flex_float()
                         .boxed()
@@ -374,9 +368,9 @@ impl ContactsPanel {
                 .flex_float()
                 .boxed(),
                 MouseEventHandler::new::<Accept, _, _>(user.id as usize, cx, |_, _| {
-                    Label::new("Accept".to_string(), theme.edit_contact.text.clone())
+                    Label::new("Accept".to_string(), theme.contact_button.text.clone())
                         .contained()
-                        .with_style(theme.edit_contact.container)
+                        .with_style(theme.contact_button.container)
                         .aligned()
                         .boxed()
                 })
@@ -427,9 +421,9 @@ impl ContactsPanel {
 
         if request_status == ContactRequestStatus::Pending {
             row.add_child(
-                Label::new("…".to_string(), theme.edit_contact.text.clone())
+                Label::new("…".to_string(), theme.contact_button.text.clone())
                     .contained()
-                    .with_style(theme.edit_contact.container)
+                    .with_style(theme.contact_button.container)
                     .aligned()
                     .flex_float()
                     .boxed(),
@@ -437,9 +431,9 @@ impl ContactsPanel {
         } else {
             row.add_child(
                 MouseEventHandler::new::<Cancel, _, _>(user.id as usize, cx, |_, _| {
-                    Label::new("Cancel".to_string(), theme.edit_contact.text.clone())
+                    Label::new("Cancel".to_string(), theme.contact_button.text.clone())
                         .contained()
-                        .with_style(theme.edit_contact.container)
+                        .with_style(theme.contact_button.container)
                         .aligned()
                         .flex_float()
                         .boxed()
@@ -454,95 +448,6 @@ impl ContactsPanel {
         row.constrained().with_height(theme.row_height).boxed()
     }
 
-    fn render_potential_contact(
-        contact: Arc<User>,
-        user_store: ModelHandle<UserStore>,
-        theme: &theme::ContactsPanel,
-        cx: &mut LayoutContext,
-    ) -> ElementBox {
-        enum RequestContactButton {}
-
-        let request_status = user_store.read(cx).contact_request_status(&contact);
-
-        Flex::row()
-            .with_children(contact.avatar.clone().map(|avatar| {
-                Image::new(avatar)
-                    .with_style(theme.contact_avatar)
-                    .aligned()
-                    .left()
-                    .boxed()
-            }))
-            .with_child(
-                Label::new(
-                    contact.github_login.clone(),
-                    theme.contact_username.text.clone(),
-                )
-                .contained()
-                .with_style(theme.contact_username.container)
-                .aligned()
-                .left()
-                .boxed(),
-            )
-            .with_child(
-                MouseEventHandler::new::<RequestContactButton, _, _>(
-                    contact.id as usize,
-                    cx,
-                    |_, _| {
-                        let label = match request_status {
-                            ContactRequestStatus::None | ContactRequestStatus::RequestReceived => {
-                                "+"
-                            }
-                            ContactRequestStatus::RequestSent => "-",
-                            ContactRequestStatus::Pending
-                            | ContactRequestStatus::RequestAccepted => "…",
-                        };
-
-                        Label::new(label.to_string(), theme.edit_contact.text.clone())
-                            .contained()
-                            .with_style(theme.edit_contact.container)
-                            .aligned()
-                            .flex_float()
-                            .boxed()
-                    },
-                )
-                .on_click(move |_, cx| match request_status {
-                    ContactRequestStatus::None => {
-                        cx.dispatch_action(RequestContact(contact.id));
-                    }
-                    ContactRequestStatus::RequestSent => {
-                        cx.dispatch_action(RemoveContact(contact.id));
-                    }
-                    _ => {}
-                })
-                .with_cursor_style(CursorStyle::PointingHand)
-                .boxed(),
-            )
-            .constrained()
-            .with_height(theme.row_height)
-            .boxed()
-    }
-
-    fn query_changed(&mut self, cx: &mut ViewContext<Self>) {
-        self.update_entries(cx);
-
-        let query = self.user_query_editor.read(cx).text(cx);
-        let search_users = self
-            .user_store
-            .update(cx, |store, cx| store.fuzzy_search_users(query, cx));
-
-        self.contacts_search_task = Some(cx.spawn(|this, mut cx| {
-            async move {
-                let potential_contacts = search_users.await?;
-                this.update(&mut cx, |this, cx| {
-                    this.potential_contacts = potential_contacts;
-                    this.update_entries(cx);
-                });
-                Ok(())
-            }
-            .log_err()
-        }));
-    }
-
     fn update_entries(&mut self, cx: &mut ViewContext<Self>) {
         let user_store = self.user_store.read(cx);
         let query = self.user_query_editor.read(cx).text(cx);
@@ -656,15 +561,6 @@ impl ContactsPanel {
             }
         }
 
-        if !self.potential_contacts.is_empty() {
-            self.entries.push(ContactEntry::Header("Add Contacts"));
-            self.entries.extend(
-                self.potential_contacts
-                    .iter()
-                    .map(|user| ContactEntry::PotentialContact(user.clone())),
-            );
-        }
-
         self.list_state.reset(self.entries.len());
         cx.notify();
     }
@@ -692,6 +588,16 @@ impl ContactsPanel {
             })
             .detach();
     }
+
+    fn find_new_contacts(
+        workspace: &mut Workspace,
+        _: &FindNewContacts,
+        cx: &mut ViewContext<Workspace>,
+    ) {
+        workspace.toggle_modal(cx, |cx, workspace| {
+            cx.add_view(|cx| ContactFinder::new(workspace.user_store().clone(), cx))
+        });
+    }
 }
 
 pub enum Event {}
@@ -706,7 +612,10 @@ impl View for ContactsPanel {
     }
 
     fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
-        let theme = &cx.global::<Settings>().theme.contacts_panel;
+        enum AddContact {}
+
+        let theme = cx.global::<Settings>().theme.clone();
+        let theme = &theme.contacts_panel;
         Container::new(
             Flex::column()
                 .with_child(
@@ -719,14 +628,18 @@ impl View for ContactsPanel {
                                 .boxed(),
                         )
                         .with_child(
-                            Svg::new("icons/add-contact.svg")
-                                .with_color(theme.add_contact_icon.color)
-                                .constrained()
-                                .with_height(12.)
-                                .contained()
-                                .with_style(theme.add_contact_icon.container)
-                                .aligned()
-                                .boxed(),
+                            MouseEventHandler::new::<AddContact, _, _>(0, cx, |_, _| {
+                                Svg::new("icons/add-contact.svg")
+                                    .with_color(theme.add_contact_icon.color)
+                                    .constrained()
+                                    .with_height(12.)
+                                    .contained()
+                                    .with_style(theme.add_contact_icon.container)
+                                    .aligned()
+                                    .boxed()
+                            })
+                            .on_click(|_, cx| cx.dispatch_action(FindNewContacts))
+                            .boxed(),
                         )
                         .constrained()
                         .with_height(32.)

crates/theme/src/theme.rs 🔗

@@ -21,6 +21,7 @@ pub struct Theme {
     pub workspace: Workspace,
     pub chat_panel: ChatPanel,
     pub contacts_panel: ContactsPanel,
+    pub contact_finder: ContactFinder,
     pub project_panel: ProjectPanel,
     pub command_palette: CommandPalette,
     pub picker: Picker,
@@ -240,7 +241,7 @@ pub struct ContactsPanel {
     pub row_height: f32,
     pub contact_avatar: ImageStyle,
     pub contact_username: ContainedText,
-    pub edit_contact: ContainedText,
+    pub contact_button: ContainedText,
     pub tree_branch_width: f32,
     pub tree_branch_color: Color,
     pub shared_project: ProjectRow,
@@ -249,6 +250,19 @@ pub struct ContactsPanel {
     pub hovered_unshared_project: ProjectRow,
 }
 
+#[derive(Deserialize, Default)]
+pub struct ContactFinder {
+    #[serde(flatten)]
+    pub container: ContainerStyle,
+    pub max_width: f32,
+    pub max_height: f32,
+    pub query_editor: FieldEditor,
+    pub row_height: f32,
+    pub contact_avatar: ImageStyle,
+    pub contact_username: ContainedText,
+    pub contact_button: ContainedText,
+}
+
 #[derive(Deserialize, Default)]
 pub struct AddContactIcon {
     #[serde(flatten)]

crates/workspace/src/workspace.rs 🔗

@@ -824,6 +824,10 @@ impl Workspace {
         &self.status_bar
     }
 
+    pub fn user_store(&self) -> &ModelHandle<UserStore> {
+        &self.user_store
+    }
+
     pub fn project(&self) -> &ModelHandle<Project> {
         &self.project
     }
@@ -931,7 +935,7 @@ impl Workspace {
         })
     }
 
-    // Returns the model that was toggled closed if it was open
+    /// Returns the modal that was toggled closed if it was open.
     pub fn toggle_modal<V, F>(
         &mut self,
         cx: &mut ViewContext<Self>,

styles/src/styleTree/app.ts 🔗

@@ -1,6 +1,7 @@
 import Theme from "../themes/theme";
 import chatPanel from "./chatPanel";
 import { text } from "./components";
+import contactFinder from "./contactFinder";
 import contactsPanel from "./contactsPanel";
 import commandPalette from "./commandPalette";
 import editor from "./editor";
@@ -24,6 +25,7 @@ export default function app(theme: Theme): Object {
     projectPanel: projectPanel(theme),
     chatPanel: chatPanel(theme),
     contactsPanel: contactsPanel(theme),
+    contactFinder: contactFinder(theme),
     search: search(theme),
     breadcrumbs: {
       ...text(theme, "sans", "secondary"),

styles/src/styleTree/contactFinder.ts 🔗

@@ -0,0 +1,42 @@
+import Theme from "../themes/theme";
+import picker from "./picker";
+import { backgroundColor, border, player, text } from "./components";
+
+export default function contactFinder(theme: Theme) {
+  return {
+    ...picker(theme),
+    maxWidth: 540.,
+    maxHeight: 420.,
+    queryEditor: {
+      background: backgroundColor(theme, 500),
+      cornerRadius: 6,
+      text: text(theme, "mono", "primary"),
+      placeholderText: text(theme, "mono", "placeholder", { size: "sm" }),
+      selection: player(theme, 1).selection,
+      border: border(theme, "secondary"),
+      padding: {
+        bottom: 4,
+        left: 8,
+        right: 8,
+        top: 4,
+      },
+    },
+    rowHeight: 28,
+    contactAvatar: {
+      cornerRadius: 10,
+      width: 18,
+    },
+    contactUsername: {
+      ...text(theme, "mono", "primary", { size: "sm" }),
+      padding: {
+        left: 8,
+      },
+    },
+    contactButton: {
+      ...text(theme, "mono", "primary", { size: "sm" }),
+      background: backgroundColor(theme, 100),
+      cornerRadius: 12,
+      padding: { left: 7, right: 7 }
+    },
+  }
+}

styles/src/styleTree/contactsPanel.ts 🔗

@@ -2,7 +2,7 @@ import Theme from "../themes/theme";
 import { panel } from "./app";
 import { backgroundColor, border, borderColor, iconColor, player, text } from "./components";
 
-export default function(theme: Theme) {
+export default function contactsPanel(theme: Theme) {
   const project = {
     guestAvatarSpacing: 4,
     height: 24,
@@ -64,7 +64,7 @@ export default function(theme: Theme) {
         left: 8,
       },
     },
-    editContact: {
+    contactButton: {
       ...text(theme, "mono", "primary", { size: "sm" }),
       background: backgroundColor(theme, 100),
       cornerRadius: 12,