Detailed changes
@@ -16878,18 +16878,22 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
+ "component",
"db",
+ "documented",
"editor",
"fuzzy",
"gpui",
"install_cli",
"language",
+ "linkme",
"picker",
"project",
"schemars",
"serde",
"settings",
"telemetry",
+ "theme",
"ui",
"util",
"vim_mode_setting",
@@ -435,6 +435,7 @@ dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "be69a0
dashmap = "6.0"
derive_more = "0.99.17"
dirs = "4.0"
+documented = "0.9.1"
dotenv = "0.15.0"
ec4rs = "1.1"
emojis = "0.6.1"
@@ -797,5 +798,6 @@ ignored = [
"serde",
"component",
"linkme",
+ "documented",
"workspace-hack",
]
@@ -15,6 +15,7 @@ path = "src/ui.rs"
[dependencies]
chrono.workspace = true
component.workspace = true
+documented.workspace = true
gpui.workspace = true
icons.workspace = true
itertools.workspace = true
@@ -28,7 +29,6 @@ strum.workspace = true
theme.workspace = true
ui_macros.workspace = true
util.workspace = true
-documented = "0.9.1"
workspace-hack.workspace = true
[target.'cfg(windows)'.dependencies]
@@ -4,11 +4,13 @@ use gpui::App;
use theme::ActiveTheme;
mod color_contrast;
+mod corner_solver;
mod format_distance;
mod search_input;
mod with_rem_size;
pub use color_contrast::*;
+pub use corner_solver::{CornerSolver, inner_corner_radius};
pub use format_distance::*;
pub use search_input::*;
pub use with_rem_size::*;
@@ -0,0 +1,61 @@
+use gpui::Pixels;
+
+/// Calculates the childโs content-corner radius for a single nested level.
+///
+/// child_content_radius = max(0, parent_radius - parent_border - parent_padding + self_border)
+///
+/// - parent_radius: outer corner radius of the parent element
+/// - parent_border: border width of the parent element
+/// - parent_padding: padding of the parent element
+/// - self_border: border width of this child element (for content inset)
+pub fn inner_corner_radius(
+ parent_radius: Pixels,
+ parent_border: Pixels,
+ parent_padding: Pixels,
+ self_border: Pixels,
+) -> Pixels {
+ (parent_radius - parent_border - parent_padding + self_border).max(Pixels::ZERO)
+}
+
+/// Solver for arbitrarily deep nested corner radii.
+///
+/// Each nested levelโs outer border-box radius is:
+/// Rโ = max(0, root_radius - root_border - root_padding)
+/// Rแตข = max(0, Rแตขโโ - childแตขโโ_border - childแตขโโ_padding) for i > 0
+pub struct CornerSolver {
+ root_radius: Pixels,
+ root_border: Pixels,
+ root_padding: Pixels,
+ children: Vec<(Pixels, Pixels)>, // (border, padding)
+}
+
+impl CornerSolver {
+ pub fn new(root_radius: Pixels, root_border: Pixels, root_padding: Pixels) -> Self {
+ Self {
+ root_radius,
+ root_border,
+ root_padding,
+ children: Vec::new(),
+ }
+ }
+
+ pub fn add_child(mut self, border: Pixels, padding: Pixels) -> Self {
+ self.children.push((border, padding));
+ self
+ }
+
+ pub fn corner_radius(&self, level: usize) -> Pixels {
+ if level == 0 {
+ return (self.root_radius - self.root_border - self.root_padding).max(Pixels::ZERO);
+ }
+ if level >= self.children.len() {
+ return Pixels::ZERO;
+ }
+ let mut r = (self.root_radius - self.root_border - self.root_padding).max(Pixels::ZERO);
+ for i in 0..level {
+ let (b, p) = self.children[i];
+ r = (r - b - p).max(Pixels::ZERO);
+ }
+ r
+ }
+}
@@ -17,23 +17,27 @@ test-support = []
[dependencies]
anyhow.workspace = true
client.workspace = true
+component.workspace = true
db.workspace = true
+documented.workspace = true
fuzzy.workspace = true
gpui.workspace = true
install_cli.workspace = true
language.workspace = true
+linkme.workspace = true
picker.workspace = true
project.workspace = true
schemars.workspace = true
serde.workspace = true
settings.workspace = true
telemetry.workspace = true
+theme.workspace = true
ui.workspace = true
util.workspace = true
vim_mode_setting.workspace = true
+workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
-workspace-hack.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }
@@ -1,7 +1,3 @@
-mod base_keymap_picker;
-mod base_keymap_setting;
-mod multibuffer_hint;
-
use client::{TelemetrySettings, telemetry::Telemetry};
use db::kvp::KEY_VALUE_STORE;
use gpui::{
@@ -24,6 +20,11 @@ use workspace::{
pub use base_keymap_setting::BaseKeymap;
pub use multibuffer_hint::*;
+mod base_keymap_picker;
+mod base_keymap_setting;
+mod multibuffer_hint;
+mod welcome_ui;
+
actions!(welcome, [ResetHints]);
pub const FIRST_OPEN: &str = "first_open";
@@ -0,0 +1 @@
+mod theme_preview;
@@ -0,0 +1,280 @@
+#![allow(unused, dead_code)]
+use gpui::{Hsla, Length};
+use std::sync::Arc;
+use theme::{Theme, ThemeRegistry};
+use ui::{
+ IntoElement, RenderOnce, component_prelude::Documented, prelude::*, utils::inner_corner_radius,
+};
+
+/// Shows a preview of a theme as an abstract illustration
+/// of a thumbnail-sized editor.
+#[derive(IntoElement, RegisterComponent, Documented)]
+pub struct ThemePreviewTile {
+ theme: Arc<Theme>,
+ selected: bool,
+ seed: f32,
+}
+
+impl ThemePreviewTile {
+ pub fn new(theme: Arc<Theme>, selected: bool, seed: f32) -> Self {
+ Self {
+ theme,
+ selected,
+ seed,
+ }
+ }
+
+ pub fn selected(mut self, selected: bool) -> Self {
+ self.selected = selected;
+ self
+ }
+}
+
+impl RenderOnce for ThemePreviewTile {
+ fn render(self, _window: &mut ui::Window, _cx: &mut ui::App) -> impl IntoElement {
+ let color = self.theme.colors();
+
+ let root_radius = px(8.0);
+ let root_border = px(2.0);
+ let root_padding = px(2.0);
+ let child_border = px(1.0);
+ let inner_radius =
+ inner_corner_radius(root_radius, root_border, root_padding, child_border);
+
+ let item_skeleton = |w: Length, h: Pixels, bg: Hsla| div().w(w).h(h).rounded_full().bg(bg);
+
+ let skeleton_height = px(4.);
+
+ let sidebar_seeded_width = |seed: f32, index: usize| {
+ let value = (seed * 1000.0 + index as f32 * 10.0).sin() * 0.5 + 0.5;
+ 0.5 + value * 0.45
+ };
+
+ let sidebar_skeleton_items = 8;
+
+ let sidebar_skeleton = (0..sidebar_skeleton_items)
+ .map(|i| {
+ let width = sidebar_seeded_width(self.seed, i);
+ item_skeleton(
+ relative(width).into(),
+ skeleton_height,
+ color.text.alpha(0.45),
+ )
+ })
+ .collect::<Vec<_>>();
+
+ let sidebar = div()
+ .h_full()
+ .w(relative(0.25))
+ .border_r(px(1.))
+ .border_color(color.border_transparent)
+ .bg(color.panel_background)
+ .child(
+ div()
+ .p_2()
+ .flex()
+ .flex_col()
+ .size_full()
+ .gap(px(4.))
+ .children(sidebar_skeleton),
+ );
+
+ let pseudo_code_skeleton = |theme: Arc<Theme>, seed: f32| -> AnyElement {
+ let colors = theme.colors();
+ let syntax = theme.syntax();
+
+ let keyword_color = syntax.get("keyword").color;
+ let function_color = syntax.get("function").color;
+ let string_color = syntax.get("string").color;
+ let comment_color = syntax.get("comment").color;
+ let variable_color = syntax.get("variable").color;
+ let type_color = syntax.get("type").color;
+ let punctuation_color = syntax.get("punctuation").color;
+
+ let syntax_colors = [
+ keyword_color,
+ function_color,
+ string_color,
+ variable_color,
+ type_color,
+ punctuation_color,
+ comment_color,
+ ];
+
+ let line_width = |line_idx: usize, block_idx: usize| -> f32 {
+ let val = (seed * 100.0 + line_idx as f32 * 20.0 + block_idx as f32 * 5.0).sin()
+ * 0.5
+ + 0.5;
+ 0.05 + val * 0.2
+ };
+
+ let indentation = |line_idx: usize| -> f32 {
+ let step = line_idx % 6;
+ if step < 3 {
+ step as f32 * 0.1
+ } else {
+ (5 - step) as f32 * 0.1
+ }
+ };
+
+ let pick_color = |line_idx: usize, block_idx: usize| -> Hsla {
+ let idx = ((seed * 10.0 + line_idx as f32 * 7.0 + block_idx as f32 * 3.0).sin()
+ * 3.5)
+ .abs() as usize
+ % syntax_colors.len();
+ syntax_colors[idx].unwrap_or(colors.text)
+ };
+
+ let line_count = 13;
+
+ let lines = (0..line_count)
+ .map(|line_idx| {
+ let block_count = (((seed * 30.0 + line_idx as f32 * 12.0).sin() * 0.5 + 0.5)
+ * 3.0)
+ .round() as usize
+ + 2;
+
+ let indent = indentation(line_idx);
+
+ let blocks = (0..block_count)
+ .map(|block_idx| {
+ let width = line_width(line_idx, block_idx);
+ let color = pick_color(line_idx, block_idx);
+ item_skeleton(relative(width).into(), skeleton_height, color)
+ })
+ .collect::<Vec<_>>();
+
+ h_flex().gap(px(2.)).ml(relative(indent)).children(blocks)
+ })
+ .collect::<Vec<_>>();
+
+ v_flex()
+ .size_full()
+ .p_1()
+ .gap(px(6.))
+ .children(lines)
+ .into_any_element()
+ };
+
+ let pane = div()
+ .h_full()
+ .flex_grow()
+ .flex()
+ .flex_col()
+ // .child(
+ // div()
+ // .w_full()
+ // .border_color(color.border)
+ // .border_b(px(1.))
+ // .h(relative(0.1))
+ // .bg(color.tab_bar_background),
+ // )
+ .child(
+ div()
+ .size_full()
+ .overflow_hidden()
+ .bg(color.editor_background)
+ .p_2()
+ .child(pseudo_code_skeleton(self.theme.clone(), self.seed)),
+ );
+
+ let content = div().size_full().flex().child(sidebar).child(pane);
+
+ div()
+ .size_full()
+ .rounded(root_radius)
+ .p(root_padding)
+ .border(root_border)
+ .border_color(color.border_transparent)
+ .when(self.selected, |this| {
+ this.border_color(color.border_selected)
+ })
+ .child(
+ div()
+ .size_full()
+ .rounded(inner_radius)
+ .border(child_border)
+ .border_color(color.border)
+ .bg(color.background)
+ .child(content),
+ )
+ }
+}
+
+impl Component for ThemePreviewTile {
+ fn description() -> Option<&'static str> {
+ Some(Self::DOCS)
+ }
+
+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
+ let theme_registry = ThemeRegistry::global(cx);
+
+ let one_dark = theme_registry.get("One Dark");
+ let one_light = theme_registry.get("One Light");
+ let gruvbox_dark = theme_registry.get("Gruvbox Dark");
+ let gruvbox_light = theme_registry.get("Gruvbox Light");
+
+ let themes_to_preview = vec![
+ one_dark.clone().ok(),
+ one_light.clone().ok(),
+ gruvbox_dark.clone().ok(),
+ gruvbox_light.clone().ok(),
+ ]
+ .into_iter()
+ .flatten()
+ .collect::<Vec<_>>();
+
+ Some(
+ v_flex()
+ .gap_6()
+ .p_4()
+ .children({
+ if let Some(one_dark) = one_dark.ok() {
+ vec![example_group(vec![
+ single_example(
+ "Default",
+ div()
+ .w(px(240.))
+ .h(px(180.))
+ .child(ThemePreviewTile::new(one_dark.clone(), false, 0.42))
+ .into_any_element(),
+ ),
+ single_example(
+ "Selected",
+ div()
+ .w(px(240.))
+ .h(px(180.))
+ .child(ThemePreviewTile::new(one_dark, true, 0.42))
+ .into_any_element(),
+ ),
+ ])]
+ } else {
+ vec![]
+ }
+ })
+ .child(
+ example_group(vec![single_example(
+ "Default Themes",
+ h_flex()
+ .gap_4()
+ .children(
+ themes_to_preview
+ .iter()
+ .enumerate()
+ .map(|(i, theme)| {
+ div().w(px(200.)).h(px(140.)).child(ThemePreviewTile::new(
+ theme.clone(),
+ false,
+ 0.42,
+ ))
+ })
+ .collect::<Vec<_>>(),
+ )
+ .into_any_element(),
+ )])
+ .grow(),
+ )
+ .into_any_element(),
+ )
+ }
+}