update
This commit is contained in:
@@ -7,7 +7,7 @@ use gpui::{
|
|||||||
Styled as _, Window, deferred, div, ease_out_quint, px,
|
Styled as _, Window, deferred, div, ease_out_quint, px,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::{IconRole, icon};
|
||||||
use gpui_native_theme::ActiveTheme as _;
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
|
|
||||||
use crate::common::DismissHandler;
|
use crate::common::DismissHandler;
|
||||||
use crate::common::v_flex;
|
use crate::common::v_flex;
|
||||||
@@ -128,8 +128,10 @@ impl RenderOnce for Alert {
|
|||||||
let surface = theme.colors.surface;
|
let surface = theme.colors.surface;
|
||||||
let border = theme.colors.border;
|
let border = theme.colors.border;
|
||||||
let shadows = theme.elevations.popover.shadows();
|
let shadows = theme.elevations.popover.shadows();
|
||||||
let text_size = theme.sizes.small.text_size;
|
let metrics = theme.sizes.resolve(Size::Medium);
|
||||||
let icon_size = theme.sizes.small.icon_size;
|
let icon_size = metrics.icon_size;
|
||||||
|
let title_size = metrics.text_size;
|
||||||
|
let caption_size = metrics.caption_size;
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.w_full()
|
.w_full()
|
||||||
@@ -160,7 +162,7 @@ impl RenderOnce for Alert {
|
|||||||
.when_some(self.title.clone(), |alert, title| {
|
.when_some(self.title.clone(), |alert, title| {
|
||||||
alert.child(
|
alert.child(
|
||||||
div()
|
div()
|
||||||
.text_size(text_size)
|
.text_size(title_size)
|
||||||
.text_color(foreground)
|
.text_color(foreground)
|
||||||
.when(self.message.is_some(), |title| {
|
.when(self.message.is_some(), |title| {
|
||||||
title.font_weight(gpui::FontWeight::SEMIBOLD)
|
title.font_weight(gpui::FontWeight::SEMIBOLD)
|
||||||
@@ -169,13 +171,17 @@ impl RenderOnce for Alert {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.when_some(self.message.clone(), |alert, message| {
|
.when_some(self.message.clone(), |alert, message| {
|
||||||
// With no title, the message is the primary line.
|
|
||||||
let color = if self.title.is_some() {
|
let color = if self.title.is_some() {
|
||||||
muted_foreground
|
muted_foreground
|
||||||
} else {
|
} else {
|
||||||
foreground
|
foreground
|
||||||
};
|
};
|
||||||
alert.child(div().text_size(text_size).text_color(color).child(message))
|
alert.child(
|
||||||
|
div()
|
||||||
|
.text_size(caption_size)
|
||||||
|
.text_color(color)
|
||||||
|
.child(message),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.when_some(self.on_dismiss, |alert, on_dismiss| {
|
.when_some(self.on_dismiss, |alert, on_dismiss| {
|
||||||
@@ -189,7 +195,7 @@ impl RenderOnce for Alert {
|
|||||||
.hover(|style| style.bg(muted))
|
.hover(|style| style.bg(muted))
|
||||||
.child(
|
.child(
|
||||||
icon(IconRole::Close)
|
icon(IconRole::Close)
|
||||||
.size(theme.sizes.small.icon_size * 0.75)
|
.size(theme.sizes.small.dense_icon_size)
|
||||||
.color(foreground.alpha(0.55)),
|
.color(foreground.alpha(0.55)),
|
||||||
)
|
)
|
||||||
.on_click(move |_, _window, cx| on_dismiss(cx)),
|
.on_click(move |_, _window, cx| on_dismiss(cx)),
|
||||||
|
|||||||
@@ -83,11 +83,9 @@ impl RenderOnce for Badge {
|
|||||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
let metrics = theme.sizes.resolve(self.size);
|
let metrics = theme.sizes.resolve(self.size);
|
||||||
|
let label_size = metrics.caption_size;
|
||||||
let (background, foreground) = self.variant.tokens(theme);
|
let (background, foreground) = self.variant.tokens(theme);
|
||||||
|
|
||||||
// Counts read smaller than control labels, like every native badge.
|
|
||||||
let label_size = metrics.text_size * 0.8;
|
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
@@ -95,8 +93,7 @@ impl RenderOnce for Badge {
|
|||||||
.bg(background)
|
.bg(background)
|
||||||
.text_color(foreground)
|
.text_color(foreground)
|
||||||
.text_size(label_size)
|
.text_size(label_size)
|
||||||
.line_height(label_size * 1.25)
|
.px(metrics.dense_padding_x)
|
||||||
.px(metrics.padding_x * 0.5)
|
|
||||||
.py_0p5()
|
.py_0p5()
|
||||||
.child(self.label)
|
.child(self.label)
|
||||||
.refine_style(&self.style)
|
.refine_style(&self.style)
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ impl RenderOnce for Checkbox {
|
|||||||
h_flex()
|
h_flex()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.size(metrics.icon_size)
|
.size(metrics.indicator_size)
|
||||||
.rounded(theme.radius.sm)
|
.rounded(theme.radius.sm)
|
||||||
.border_1()
|
.border_1()
|
||||||
.when(self.checked, |indicator| {
|
.when(self.checked, |indicator| {
|
||||||
@@ -129,7 +129,11 @@ impl RenderOnce for Checkbox {
|
|||||||
.bg(ramps.primary.base)
|
.bg(ramps.primary.base)
|
||||||
.border_color(ramps.primary.base)
|
.border_color(ramps.primary.base)
|
||||||
.hover(|style| style.bg(ramps.primary.hover))
|
.hover(|style| style.bg(ramps.primary.hover))
|
||||||
.child(icon(IconRole::Check).color(colors.primary_foreground))
|
.child(
|
||||||
|
icon(IconRole::Check)
|
||||||
|
.size(metrics.dense_icon_size)
|
||||||
|
.color(colors.primary_foreground),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.when(!self.checked, |indicator| {
|
.when(!self.checked, |indicator| {
|
||||||
indicator
|
indicator
|
||||||
|
|||||||
@@ -1551,7 +1551,7 @@ impl RenderOnce for Input {
|
|||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.w_full()
|
.w_full()
|
||||||
.h(metrics.input_height)
|
.h(metrics.input_height)
|
||||||
.px(metrics.padding_x / 2.0)
|
.px(metrics.dense_padding_x)
|
||||||
.rounded(radius.control)
|
.rounded(radius.control)
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(colors.border)
|
.border_color(colors.border)
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ struct MenuChrome {
|
|||||||
radius_overlay: Pixels,
|
radius_overlay: Pixels,
|
||||||
radius_sm: Pixels,
|
radius_sm: Pixels,
|
||||||
icon_size: Pixels,
|
icon_size: Pixels,
|
||||||
|
dense_icon_size: Pixels,
|
||||||
shadow: Vec<BoxShadow>,
|
shadow: Vec<BoxShadow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,6 +235,7 @@ impl RenderOnce for Menu {
|
|||||||
radius_overlay: theme.radius.overlay,
|
radius_overlay: theme.radius.overlay,
|
||||||
radius_sm: theme.radius.sm,
|
radius_sm: theme.radius.sm,
|
||||||
icon_size: theme.sizes.medium.icon_size,
|
icon_size: theme.sizes.medium.icon_size,
|
||||||
|
dense_icon_size: theme.sizes.medium.dense_icon_size,
|
||||||
shadow: theme.elevations.popover.shadows(),
|
shadow: theme.elevations.popover.shadows(),
|
||||||
},
|
},
|
||||||
theme.motion.overlay,
|
theme.motion.overlay,
|
||||||
@@ -348,7 +350,7 @@ impl RenderOnce for Menu {
|
|||||||
.role(Role::Menu)
|
.role(Role::Menu)
|
||||||
.aria_label(label)
|
.aria_label(label)
|
||||||
.mt_1()
|
.mt_1()
|
||||||
.min_w_40()
|
.min_w_56()
|
||||||
.p_1()
|
.p_1()
|
||||||
.bg(chrome.surface)
|
.bg(chrome.surface)
|
||||||
.text_color(chrome.foreground)
|
.text_color(chrome.foreground)
|
||||||
@@ -522,7 +524,7 @@ fn render_item(
|
|||||||
.when(checked == Some(true), |row| {
|
.when(checked == Some(true), |row| {
|
||||||
row.child(
|
row.child(
|
||||||
icon_glyph(IconRole::Check)
|
icon_glyph(IconRole::Check)
|
||||||
.size(chrome.icon_size * 0.75)
|
.size(chrome.dense_icon_size)
|
||||||
.color(label_color),
|
.color(label_color),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ where
|
|||||||
h_flex()
|
h_flex()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.size(metrics.icon_size)
|
.size(metrics.indicator_size)
|
||||||
.rounded(radius.full)
|
.rounded(radius.full)
|
||||||
.border_1()
|
.border_1()
|
||||||
.when(is_selected, |indicator| {
|
.when(is_selected, |indicator| {
|
||||||
@@ -199,7 +199,7 @@ where
|
|||||||
.hover(|style| style.bg(ramps.primary.hover))
|
.hover(|style| style.bg(ramps.primary.hover))
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.size(metrics.icon_size / 2.0)
|
.size(metrics.indicator_size / 2.0)
|
||||||
.rounded(radius.full)
|
.rounded(radius.full)
|
||||||
.bg(colors.primary_foreground),
|
.bg(colors.primary_foreground),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use gpui::{
|
|||||||
AnimationExt as _, App, Bounds, ElementId, Entity, InteractiveElement as _, IntoElement,
|
AnimationExt as _, App, Bounds, ElementId, Entity, InteractiveElement as _, IntoElement,
|
||||||
KeyDownEvent, ParentElement as _, Point, RenderOnce, Role, SharedString, SpringAnimation,
|
KeyDownEvent, ParentElement as _, Point, RenderOnce, Role, SharedString, SpringAnimation,
|
||||||
SpringConfig, StatefulInteractiveElement as _, StyleRefinement, Styled as _, Toggled, Window,
|
SpringConfig, StatefulInteractiveElement as _, StyleRefinement, Styled as _, Toggled, Window,
|
||||||
canvas, div, rems,
|
canvas, div,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::{IconRole, icon};
|
||||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
@@ -152,29 +152,12 @@ where
|
|||||||
let ring = theme.colors.ring;
|
let ring = theme.colors.ring;
|
||||||
let radius = theme.radius.full;
|
let radius = theme.radius.full;
|
||||||
let plate_shadow = theme.elevations.control.shadows();
|
let plate_shadow = theme.elevations.control.shadows();
|
||||||
let icon_size = metrics.icon_size * 0.85;
|
|
||||||
|
|
||||||
let track_height = match self.size {
|
let track_height = metrics.strip_height;
|
||||||
Size::Small => metrics.control_height,
|
let track_inset = metrics.plate_inset;
|
||||||
Size::Medium => metrics.control_height * 1.15,
|
let horizontal_padding = metrics.dense_padding_x;
|
||||||
Size::Large => metrics.control_height * 1.35,
|
let text_size = metrics.caption_size;
|
||||||
};
|
let icon_size = metrics.icon_size;
|
||||||
|
|
||||||
let track_inset = match self.size {
|
|
||||||
Size::Small => rems(0.125),
|
|
||||||
Size::Medium => rems(0.25),
|
|
||||||
Size::Large => rems(0.375),
|
|
||||||
};
|
|
||||||
|
|
||||||
let horizontal_padding = match self.size {
|
|
||||||
Size::Small => metrics.padding_x * 0.5,
|
|
||||||
Size::Medium | Size::Large => metrics.padding_x * 0.75,
|
|
||||||
};
|
|
||||||
|
|
||||||
let text_size = match self.size {
|
|
||||||
Size::Small => metrics.text_size * 0.85,
|
|
||||||
Size::Medium | Size::Large => metrics.text_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
let selected_index = self
|
let selected_index = self
|
||||||
.options
|
.options
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ where
|
|||||||
let foreground = theme.colors.foreground;
|
let foreground = theme.colors.foreground;
|
||||||
let muted_foreground = theme.colors.muted_foreground;
|
let muted_foreground = theme.colors.muted_foreground;
|
||||||
let radius = theme.radius.control;
|
let radius = theme.radius.control;
|
||||||
let icon_size = metrics.icon_size * 0.75;
|
let icon_size = metrics.dense_icon_size;
|
||||||
|
|
||||||
let label = self
|
let label = self
|
||||||
.a11y_label
|
.a11y_label
|
||||||
@@ -214,7 +214,7 @@ where
|
|||||||
.gap_2()
|
.gap_2()
|
||||||
.w_full()
|
.w_full()
|
||||||
.h(metrics.input_height)
|
.h(metrics.input_height)
|
||||||
.px(metrics.padding_x / 2.0)
|
.px(metrics.dense_padding_x)
|
||||||
.rounded(radius)
|
.rounded(radius)
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(if is_open { ring } else { border })
|
.border_color(if is_open { ring } else { border })
|
||||||
|
|||||||
@@ -96,11 +96,11 @@ impl RenderOnce for Switch {
|
|||||||
let ramps = theme.ramps;
|
let ramps = theme.ramps;
|
||||||
let metrics = theme.sizes.resolve(self.size);
|
let metrics = theme.sizes.resolve(self.size);
|
||||||
|
|
||||||
let track_height = metrics.icon_size * 1.25;
|
let track_height = metrics.control_height;
|
||||||
let track_width = metrics.icon_size * 2.25;
|
let track_width = metrics.control_height * 1.65;
|
||||||
|
|
||||||
let knob = metrics.icon_size;
|
let knob = metrics.indicator_size;
|
||||||
let inset = metrics.icon_size * 0.125;
|
let inset = metrics.plate_inset;
|
||||||
let travel = track_width - knob - inset * 2.;
|
let travel = track_width - knob - inset * 2.;
|
||||||
|
|
||||||
let next = !self.checked;
|
let next = !self.checked;
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ fn pill(entry: &ToastEntry, toasts: &Entity<Toasts>, cx: &App) -> gpui::Stateful
|
|||||||
let radius = theme.radius.full;
|
let radius = theme.radius.full;
|
||||||
let shadows = theme.elevations.popover.shadows();
|
let shadows = theme.elevations.popover.shadows();
|
||||||
let icon_size = theme.sizes.small.icon_size;
|
let icon_size = theme.sizes.small.icon_size;
|
||||||
|
let dense_icon_size = theme.sizes.small.dense_icon_size;
|
||||||
let text_size = theme.sizes.small.text_size;
|
let text_size = theme.sizes.small.text_size;
|
||||||
let toast = &entry.toast;
|
let toast = &entry.toast;
|
||||||
let toasts = toasts.clone();
|
let toasts = toasts.clone();
|
||||||
@@ -336,7 +337,7 @@ fn pill(entry: &ToastEntry, toasts: &Entity<Toasts>, cx: &App) -> gpui::Stateful
|
|||||||
.hover(|style| style.bg(muted))
|
.hover(|style| style.bg(muted))
|
||||||
.child(
|
.child(
|
||||||
icon(IconRole::Close)
|
icon(IconRole::Close)
|
||||||
.size(icon_size * 0.75)
|
.size(dense_icon_size)
|
||||||
.color(foreground.alpha(0.55)),
|
.color(foreground.alpha(0.55)),
|
||||||
)
|
)
|
||||||
.on_click(move |_, _window, cx| {
|
.on_click(move |_, _window, cx| {
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ impl RenderOnce for Toggle {
|
|||||||
.id(self.id)
|
.id(self.id)
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.h(metrics.control_height)
|
.h(metrics.control_height)
|
||||||
.px(metrics.padding_x * 0.75)
|
.px(metrics.dense_padding_x)
|
||||||
.rounded(theme.radius.control)
|
.rounded(theme.radius.control)
|
||||||
.text_size(metrics.text_size)
|
.text_size(metrics.text_size)
|
||||||
.text_color(colors.foreground)
|
.text_color(colors.foreground)
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ fn render_tab(
|
|||||||
.when_some(tab.icon, |strip_tab, role| {
|
.when_some(tab.icon, |strip_tab, role| {
|
||||||
strip_tab.child(
|
strip_tab.child(
|
||||||
icon(role)
|
icon(role)
|
||||||
.size(theme.sizes.medium.icon_size * 0.75)
|
.size(theme.sizes.medium.dense_icon_size)
|
||||||
.color(label_color),
|
.color(label_color),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ fn render_disclosure(
|
|||||||
} else {
|
} else {
|
||||||
IconRole::ChevronRight
|
IconRole::ChevronRight
|
||||||
})
|
})
|
||||||
.size(theme.sizes.medium.icon_size * 0.75)
|
.size(theme.sizes.medium.dense_icon_size)
|
||||||
.color(glyph_color),
|
.color(glyph_color),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+76
-12
@@ -267,8 +267,20 @@ pub struct ControlMetrics {
|
|||||||
pub text_size: Pixels,
|
pub text_size: Pixels,
|
||||||
/// Icons and on/off indicators.
|
/// Icons and on/off indicators.
|
||||||
pub icon_size: Pixels,
|
pub icon_size: Pixels,
|
||||||
|
/// Icons inline with dense content — menu rows, badges, chevrons.
|
||||||
|
pub dense_icon_size: Pixels,
|
||||||
|
/// Selection indicators — checkboxes, radios, and switch knobs.
|
||||||
|
pub indicator_size: Pixels,
|
||||||
/// Avatars.
|
/// Avatars.
|
||||||
pub avatar_size: Pixels,
|
pub avatar_size: Pixels,
|
||||||
|
/// Dense label text.
|
||||||
|
pub caption_size: Pixels,
|
||||||
|
/// Tight horizontal padding inside pills, segments, and fields.
|
||||||
|
pub dense_padding_x: Pixels,
|
||||||
|
/// The inset between a control's edge and a raised inner plate.
|
||||||
|
pub plate_inset: Pixels,
|
||||||
|
/// The band height of multi-segment strips.
|
||||||
|
pub strip_height: Pixels,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The control metrics for every size class.
|
/// The control metrics for every size class.
|
||||||
@@ -280,20 +292,72 @@ pub struct SizeTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SizeTable {
|
impl SizeTable {
|
||||||
|
/// Every metric derives from the root font size; components fetch, and
|
||||||
|
/// never scale these themselves.
|
||||||
pub fn with_root_font_size(rem_size: Pixels) -> Self {
|
pub fn with_root_font_size(rem_size: Pixels) -> Self {
|
||||||
let class = |height: f32, padding: f32, text: f32, icon: f32, avatar: f32| ControlMetrics {
|
// One row per size class: control height, padding, label text, icon,
|
||||||
control_height: rem_size * height,
|
// avatar, strip band, and plate inset — all as root-size multiples.
|
||||||
input_height: rem_size * (height + 0.125),
|
struct ClassFactors {
|
||||||
padding_x: rem_size * padding,
|
height: f32,
|
||||||
text_size: rem_size * text,
|
padding: f32,
|
||||||
icon_size: rem_size * icon,
|
text: f32,
|
||||||
avatar_size: rem_size * avatar,
|
icon: f32,
|
||||||
};
|
avatar: f32,
|
||||||
Self {
|
strip: f32,
|
||||||
small: class(1.5, 0.75, 0.85, 0.875, 1.5),
|
inset: f32,
|
||||||
medium: class(1.75, 1.0, 1.0, 1.0, 2.0),
|
|
||||||
large: class(2.25, 1.25, 1.15, 1.25, 3.0),
|
|
||||||
}
|
}
|
||||||
|
let small = ClassFactors {
|
||||||
|
height: 1.5,
|
||||||
|
padding: 0.75,
|
||||||
|
text: 0.85,
|
||||||
|
icon: 0.875,
|
||||||
|
avatar: 1.5,
|
||||||
|
strip: 1.5,
|
||||||
|
inset: 0.125,
|
||||||
|
};
|
||||||
|
let medium = ClassFactors {
|
||||||
|
height: 1.75,
|
||||||
|
padding: 1.0,
|
||||||
|
text: 1.0,
|
||||||
|
icon: 1.0,
|
||||||
|
avatar: 2.0,
|
||||||
|
strip: 2.0,
|
||||||
|
inset: 0.25,
|
||||||
|
};
|
||||||
|
let large = ClassFactors {
|
||||||
|
height: 2.25,
|
||||||
|
padding: 1.25,
|
||||||
|
text: 1.15,
|
||||||
|
icon: 1.25,
|
||||||
|
avatar: 3.0,
|
||||||
|
strip: 3.0,
|
||||||
|
inset: 0.375,
|
||||||
|
};
|
||||||
|
let metrics = |f: &ClassFactors| ControlMetrics {
|
||||||
|
control_height: rem_size * f.height,
|
||||||
|
input_height: rem_size * (f.height + 0.125),
|
||||||
|
padding_x: rem_size * f.padding,
|
||||||
|
text_size: rem_size * f.text,
|
||||||
|
icon_size: rem_size * f.icon,
|
||||||
|
dense_icon_size: rem_size * f.icon * 0.75,
|
||||||
|
indicator_size: rem_size * f.height * 0.7,
|
||||||
|
avatar_size: rem_size * f.avatar,
|
||||||
|
caption_size: rem_size * f.text,
|
||||||
|
dense_padding_x: rem_size * f.padding * 0.75,
|
||||||
|
plate_inset: rem_size * f.inset,
|
||||||
|
strip_height: rem_size * f.strip,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut table = Self {
|
||||||
|
small: metrics(&small),
|
||||||
|
medium: metrics(&medium),
|
||||||
|
large: metrics(&large),
|
||||||
|
};
|
||||||
|
// Dense text stops growing with the large class, and small's is
|
||||||
|
// one step below its label.
|
||||||
|
table.small.caption_size = table.small.text_size * 0.85;
|
||||||
|
table.large.caption_size = table.medium.text_size;
|
||||||
|
table
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The metrics for `size`.
|
/// The metrics for `size`.
|
||||||
|
|||||||
@@ -292,6 +292,12 @@ mod tests {
|
|||||||
assert_eq!(resolved.sizes.medium.input_height, px(30.));
|
assert_eq!(resolved.sizes.medium.input_height, px(30.));
|
||||||
assert_eq!(resolved.sizes.medium.text_size, px(16.));
|
assert_eq!(resolved.sizes.medium.text_size, px(16.));
|
||||||
assert_eq!(resolved.sizes.large.avatar_size, px(48.));
|
assert_eq!(resolved.sizes.large.avatar_size, px(48.));
|
||||||
|
assert_eq!(resolved.sizes.small.caption_size, px(13.6 * 0.85));
|
||||||
|
assert_eq!(resolved.sizes.medium.caption_size, px(16.));
|
||||||
|
assert_eq!(resolved.sizes.large.caption_size, px(16.));
|
||||||
|
assert_eq!(resolved.sizes.medium.dense_padding_x, px(16. * 0.75));
|
||||||
|
assert_eq!(resolved.sizes.medium.plate_inset, px(4.));
|
||||||
|
assert_eq!(resolved.sizes.medium.strip_height, px(32.));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -342,6 +342,7 @@ Pure tests for resolve/overlay chain, payload parsing (fixtures), icon resolutio
|
|||||||
| D22 | The color set does not expand along gpuikit's `Themeable` shape (evaluated 2026-10-31): their per-component token layers (`button_*`, `input_*`) are passthroughs of their own primitives — a drift-prone parallel layer our role-plus-ramp model replaces with OKLCH-derived hover/pressed shades; depth variants (`surface_secondary`), the badge palette, and `fg_disabled` wait for real consumers (Badge, nested elevation, a theme that can't express disabled by opacity). The one adoption: `info` as the fourth severity — the Alert's Info severity had a live consumer painting from the primary ramp, which collides with Danger under warm accents. Domain palettes (editor diff colors) may revisit gpuikit's `ThemeExtension` idea when `crates/editor` lands |
|
| D22 | The color set does not expand along gpuikit's `Themeable` shape (evaluated 2026-10-31): their per-component token layers (`button_*`, `input_*`) are passthroughs of their own primitives — a drift-prone parallel layer our role-plus-ramp model replaces with OKLCH-derived hover/pressed shades; depth variants (`surface_secondary`), the badge palette, and `fg_disabled` wait for real consumers (Badge, nested elevation, a theme that can't express disabled by opacity). The one adoption: `info` as the fourth severity — the Alert's Info severity had a live consumer painting from the primary ramp, which collides with Danger under warm accents. Domain palettes (editor diff colors) may revisit gpuikit's `ThemeExtension` idea when `crates/editor` lands |
|
||||||
| D23 | The old Toast split into `Alert` + `Toast` (2026-10-31): one component can't be both a persistent page callout and a fleeting bottom-center nudge — different lifetime (persists until acted on vs auto-dismisses), different placement (inline in flow vs floating bottom center), different content shape (title + message + severity tint vs one line + icon + action). Alert is fully controlled — the app renders it while the condition holds and its close button reports intent; Toast keeps the store + timers. Toast drops severity and titles: Adwaita toasts are single-line nudges, and severity belongs to the banners that stay on the page |
|
| D23 | The old Toast split into `Alert` + `Toast` (2026-10-31): one component can't be both a persistent page callout and a fleeting bottom-center nudge — different lifetime (persists until acted on vs auto-dismisses), different placement (inline in flow vs floating bottom center), different content shape (title + message + severity tint vs one line + icon + action). Alert is fully controlled — the app renders it while the condition holds and its close button reports intent; Toast keeps the store + timers. Toast drops severity and titles: Adwaita toasts are single-line nudges, and severity belongs to the banners that stay on the page |
|
||||||
| D24 | Shadows follow the platform's design language (2026-11-01): in-app shadows are painted by gpui, so "native" means each platform's shadow *values* — `Elevation` is a two-layer key + ambient stack and `resolve` picks it per `SourceKind` (macOS soft and layered at 12% alpha, Windows tight with more offset, Linux compact and darker; one shared dark stack where borders do the separation work). Static design constants, never OS-read (no platform API states shadow values); replaces the Alert's light-mode alpha hack, which was a single-profile workaround |
|
| D24 | Shadows follow the platform's design language (2026-11-01): in-app shadows are painted by gpui, so "native" means each platform's shadow *values* — `Elevation` is a two-layer key + ambient stack and `resolve` picks it per `SourceKind` (macOS soft and layered at 12% alpha, Windows tight with more offset, Linux compact and darker; one shared dark stack where borders do the separation work). Static design constants, never OS-read (no platform API states shadow values); replaces the Alert's light-mode alpha hack, which was a single-profile workaround |
|
||||||
|
| D25 | Components fetch, never scale (2026-11-01): every derived metric a component kept re-deriving from root metrics — dense label text (`×0.8/0.85`), dense inline icons (`×0.75`), dense horizontal padding (`×0.5/0.75/2`), plate insets (`0.125/0.25/0.375 rem`), strip band heights (`×1/1.15/1.35`) — moved into the per-size-class `ControlMetrics` table (`caption_size`, `dense_icon_size`, `dense_padding_x`, `plate_inset`, `strip_height`, plus `indicator_size` for checkboxes, radios, and switch knobs), computed once in `with_root_font_size`. The table stays role-based — one value per concept per size class, shared by every component — not gpuikit's per-component token layers (D22). Component-unique geometry that has no second consumer keeps its ratios over root metrics (switch track length, slider knob, avatar initials, tree indent). Two icon roles are fetch-only: content icons (alert info, segmented options) take `icon_size` — never smaller than the label — and indicator glyphs (checks, chevrons, close buttons) take `dense_icon_size` |
|
||||||
|
|
||||||
Dropped from rev 3: D2, D5, D9 (port-era decisions); the review gate returns in a lighter form as D20. D10 (no icon bundling) is superseded by D19.
|
Dropped from rev 3: D2, D5, D9 (port-era decisions); the review gate returns in a lighter form as D20. D10 (no icon bundling) is superseded by D19.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user