#[cfg(feature = "gpui_text_run_styles")]
use gpui::TextTransform;
use gpui::{
hsla, point, prelude::*, px, relative, rems, rgb, rgba, AbsoluteLength, AlignItems, BoxShadow,
DefiniteLength, Div, Length,
};
use crate::context::TemplateContext;
pub fn apply_class(d: Div, class: &str) -> Div {
apply_class_with_ctx(d, class, None)
}
pub fn apply_class_with_ctx(d: Div, class: &str, ctx: Option<&TemplateContext>) -> Div {
if class.starts_with("hover:") || class.starts_with("focus:") || class.starts_with("active:") {
return d;
}
if let Some(ctx) = ctx {
if class.contains('{') {
return apply_context_class(d, class, ctx);
}
}
apply_base_class(d, class)
}
pub fn apply_base_class(d: Div, class: &str) -> Div {
match apply_static(d, class) {
Ok(d) => d,
Err(d) => apply_dynamic(d, class),
}
}
fn apply_context_class(d: Div, class: &str, ctx: &TemplateContext) -> Div {
for (prefix, apply_fn) in &[
("bg-", apply_bg as fn(Div, u32) -> Div),
("text-", apply_text_color as fn(Div, u32) -> Div),
("border-", apply_border_color as fn(Div, u32) -> Div),
] {
if let Some(rest) = class.strip_prefix(prefix) {
if rest.starts_with('{') && rest.ends_with('}') {
let expr = &rest[1..rest.len() - 1];
let val = crate::eval::eval_expr(expr, ctx);
let color_str = crate::context::value_to_str(&val);
if let Some(hex) = parse_color_str(&color_str) {
return apply_fn(d, hex);
}
}
}
}
if let Some(rest) = class.strip_prefix("bg-") {
if let Some((expr_part, alpha_str)) = rest.rsplit_once('/') {
if expr_part.starts_with('{') && expr_part.ends_with('}') {
let expr = &expr_part[1..expr_part.len() - 1];
let val = crate::eval::eval_expr(expr, ctx);
let color_str = crate::context::value_to_str(&val);
if let Some(hex) = parse_color_str(&color_str) {
if let Ok(alpha) = alpha_str.parse::<u32>() {
let alpha_byte = alpha * 255 / 100;
let rgba_val = (hex << 8) | alpha_byte;
return d.bg(rgba(rgba_val));
}
}
}
}
}
if let Some(rest) = class.strip_prefix("opacity-") {
if rest.starts_with('{') && rest.ends_with('}') {
let expr = &rest[1..rest.len() - 1];
let val = crate::eval::eval_expr(expr, ctx);
let s = crate::context::value_to_str(&val);
if let Ok(n) = s.parse::<f32>() {
return d.opacity(n / 100.0);
}
}
}
apply_base_class(d, class)
}
fn parse_color_str(s: &str) -> Option<u32> {
let hex_str = s
.strip_prefix('#')
.or_else(|| s.strip_prefix("0x"))
.unwrap_or(s);
u32::from_str_radix(hex_str, 16).ok()
}
#[allow(clippy::result_large_err)]
fn apply_static(d: Div, class: &str) -> Result<Div, Div> {
Ok(match class {
"block" => d.block(),
"flex" => d.flex(),
"grid" => d.grid(),
"hidden" => d.hidden(),
"visible" => d.visible(),
"invisible" => d.invisible(),
"absolute" => d.absolute(),
"relative" => d.relative(),
"overflow-hidden" => d.overflow_hidden(),
"overflow-x-hidden" => d.overflow_x_hidden(),
"overflow-y-hidden" => d.overflow_y_hidden(),
"overflow-visible" | "overflow-x-visible" | "overflow-y-visible" | "overflow-scroll"
| "overflow-auto" | "overflow-y-scroll" | "overflow-y-auto" | "overflow-x-scroll"
| "overflow-x-auto" => d,
"flex-row" => d.flex_row(),
"flex-row-reverse" => d.flex_row_reverse(),
"flex-col" => d.flex_col(),
"flex-col-reverse" => d.flex_col_reverse(),
"flex-wrap" => d.flex_wrap(),
"flex-wrap-reverse" => d.flex_wrap_reverse(),
"flex-nowrap" => d.flex_nowrap(),
"flex-1" => d.flex_1(),
"flex-auto" => d.flex_auto(),
"flex-initial" => d.flex_initial(),
"flex-none" => d.flex_none(),
"grow" => d.flex_grow(),
"grow-0" => d.flex_none(),
"shrink" => d.flex_shrink(),
"shrink-0" => d.flex_shrink_0(),
"justify-start" => d.justify_start(),
"justify-end" => d.justify_end(),
"justify-center" => d.justify_center(),
"justify-between" => d.justify_between(),
"justify-around" => d.justify_around(),
"items-start" => d.items_start(),
"items-end" => d.items_end(),
"items-center" => d.items_center(),
"items-baseline" => d.items_baseline(),
"content-normal" => d.content_normal(),
"content-center" => d.content_center(),
"content-start" => d.content_start(),
"content-end" => d.content_end(),
"content-between" => d.content_between(),
"content-around" => d.content_around(),
"content-evenly" => d.content_evenly(),
"content-stretch" => d.content_stretch(),
"w-0" => d.w(px(0.)),
"w-px" => d.w_px(),
"w-full" => d.w_full(),
"w-auto" => d.w_auto(),
"h-0" => d.h(px(0.)),
"h-px" => d.h_px(),
"h-full" => d.h_full(),
"h-auto" => d.h_auto(),
"size-full" => d.size_full(),
"size-auto" => d.size_auto(),
"min-w-0" => d.min_w(px(0.)),
"min-w-full" => d.min_w_full(),
"min-h-0" => d.min_h(px(0.)),
"min-h-full" => d.min_h_full(),
"max-w-full" => d.max_w_full(),
"max-h-full" => d.max_h_full(),
"bg-black" => d.bg(gpui::black()),
"bg-white" => d.bg(gpui::white()),
"bg-transparent" => d.bg(gpui::transparent_black()),
"bg-red" => d.bg(gpui::red()),
"bg-green" => d.bg(gpui::green()),
"bg-blue" => d.bg(gpui::blue()),
"bg-yellow" => d.bg(gpui::yellow()),
"text-white" => d.text_color(gpui::white()),
"text-black" => d.text_color(gpui::black()),
"text-transparent" => d.text_color(gpui::transparent_black()),
"text-red" => d.text_color(gpui::red()),
"text-green" => d.text_color(gpui::green()),
"text-blue" => d.text_color(gpui::blue()),
"text-yellow" => d.text_color(gpui::yellow()),
"border-white" => d.border_color(gpui::white()),
"border-black" => d.border_color(gpui::black()),
"border-transparent" => d.border_color(gpui::transparent_black()),
"border-dashed" => d.border_dashed(),
"border" => d.border_1(),
"border-0" => d.border_0(),
"border-2" => d.border_2(),
"border-4" => d.border_4(),
"border-8" => d.border_8(),
"border-t" => d.border_t_1(),
"border-t-0" => d.border_t_0(),
"border-t-2" => d.border_t_2(),
"border-t-4" => d.border_t_4(),
"border-t-8" => d.border_t_8(),
"border-b" => d.border_b_1(),
"border-b-0" => d.border_b_0(),
"border-b-2" => d.border_b_2(),
"border-b-4" => d.border_b_4(),
"border-b-8" => d.border_b_8(),
"border-l" => d.border_l_1(),
"border-l-0" => d.border_l_0(),
"border-l-2" => d.border_l_2(),
"border-l-4" => d.border_l_4(),
"border-l-8" => d.border_l_8(),
"border-r" => d.border_r_1(),
"border-r-0" => d.border_r_0(),
"border-r-2" => d.border_r_2(),
"border-r-4" => d.border_r_4(),
"border-r-8" => d.border_r_8(),
"rounded-none" => d.rounded_none(),
"rounded-sm" => d.rounded_sm(),
"rounded" | "rounded-md" => d.rounded_md(),
"rounded-lg" => d.rounded_lg(),
"rounded-xl" => d.rounded_xl(),
"rounded-2xl" => d.rounded_2xl(),
"rounded-3xl" => d.rounded_3xl(),
"rounded-full" => d.rounded_full(),
"rounded-t-none" => d.rounded_t_none(),
"rounded-t-sm" => d.rounded_t_sm(),
"rounded-t" | "rounded-t-md" => d.rounded_t_md(),
"rounded-t-lg" => d.rounded_t_lg(),
"rounded-t-xl" => d.rounded_t_xl(),
"rounded-t-2xl" => d.rounded_t_2xl(),
"rounded-t-3xl" => d.rounded_t_3xl(),
"rounded-t-full" => d.rounded_t_full(),
"rounded-b-none" => d.rounded_b_none(),
"rounded-b-sm" => d.rounded_b_sm(),
"rounded-b" | "rounded-b-md" => d.rounded_b_md(),
"rounded-b-lg" => d.rounded_b_lg(),
"rounded-b-xl" => d.rounded_b_xl(),
"rounded-b-2xl" => d.rounded_b_2xl(),
"rounded-b-3xl" => d.rounded_b_3xl(),
"rounded-b-full" => d.rounded_b_full(),
"rounded-l-none" => d.rounded_l_none(),
"rounded-l-sm" => d.rounded_l_sm(),
"rounded-l" | "rounded-l-md" => d.rounded_l_md(),
"rounded-l-lg" => d.rounded_l_lg(),
"rounded-l-xl" => d.rounded_l_xl(),
"rounded-l-2xl" => d.rounded_l_2xl(),
"rounded-l-3xl" => d.rounded_l_3xl(),
"rounded-l-full" => d.rounded_l_full(),
"rounded-r-none" => d.rounded_r_none(),
"rounded-r-sm" => d.rounded_r_sm(),
"rounded-r" | "rounded-r-md" => d.rounded_r_md(),
"rounded-r-lg" => d.rounded_r_lg(),
"rounded-r-xl" => d.rounded_r_xl(),
"rounded-r-2xl" => d.rounded_r_2xl(),
"rounded-r-3xl" => d.rounded_r_3xl(),
"rounded-r-full" => d.rounded_r_full(),
"rounded-tl-none" => d.rounded_tl_none(),
"rounded-tl-sm" => d.rounded_tl_sm(),
"rounded-tl" | "rounded-tl-md" => d.rounded_tl_md(),
"rounded-tl-lg" => d.rounded_tl_lg(),
"rounded-tl-xl" => d.rounded_tl_xl(),
"rounded-tl-2xl" => d.rounded_tl_2xl(),
"rounded-tl-3xl" => d.rounded_tl_3xl(),
"rounded-tl-full" => d.rounded_tl_full(),
"rounded-tr-none" => d.rounded_tr_none(),
"rounded-tr-sm" => d.rounded_tr_sm(),
"rounded-tr" | "rounded-tr-md" => d.rounded_tr_md(),
"rounded-tr-lg" => d.rounded_tr_lg(),
"rounded-tr-xl" => d.rounded_tr_xl(),
"rounded-tr-2xl" => d.rounded_tr_2xl(),
"rounded-tr-3xl" => d.rounded_tr_3xl(),
"rounded-tr-full" => d.rounded_tr_full(),
"rounded-bl-none" => d.rounded_bl_none(),
"rounded-bl-sm" => d.rounded_bl_sm(),
"rounded-bl" | "rounded-bl-md" => d.rounded_bl_md(),
"rounded-bl-lg" => d.rounded_bl_lg(),
"rounded-bl-xl" => d.rounded_bl_xl(),
"rounded-bl-2xl" => d.rounded_bl_2xl(),
"rounded-bl-3xl" => d.rounded_bl_3xl(),
"rounded-bl-full" => d.rounded_bl_full(),
"rounded-br-none" => d.rounded_br_none(),
"rounded-br-sm" => d.rounded_br_sm(),
"rounded-br" | "rounded-br-md" => d.rounded_br_md(),
"rounded-br-lg" => d.rounded_br_lg(),
"rounded-br-xl" => d.rounded_br_xl(),
"rounded-br-2xl" => d.rounded_br_2xl(),
"rounded-br-3xl" => d.rounded_br_3xl(),
"rounded-br-full" => d.rounded_br_full(),
"font-thin" => d.font_weight(gpui::FontWeight::THIN),
"font-light" => d.font_weight(gpui::FontWeight::LIGHT),
"font-normal" => d.font_weight(gpui::FontWeight::NORMAL),
"font-medium" => d.font_weight(gpui::FontWeight::MEDIUM),
"font-semibold" => d.font_weight(gpui::FontWeight::SEMIBOLD),
"font-bold" => d.font_weight(gpui::FontWeight::BOLD),
"font-extrabold" => d.font_weight(gpui::FontWeight::EXTRA_BOLD),
"font-black" => d.font_weight(gpui::FontWeight::BLACK),
"italic" | "font-italic" => d.italic(),
"not-italic" => d.not_italic(),
"text-xs" => d.text_xs(),
"text-sm" => d.text_sm(),
"text-base" => d.text_base(),
"text-lg" => d.text_lg(),
"text-xl" => d.text_xl(),
"text-2xl" => d.text_2xl(),
"text-3xl" => d.text_3xl(),
"text-4xl" => d.text_size(rems(2.25)),
"text-5xl" => d.text_size(rems(3.)),
"text-6xl" => d.text_size(rems(3.75)),
"text-7xl" => d.text_size(rems(4.5)),
"text-8xl" => d.text_size(rems(6.)),
"text-9xl" => d.text_size(rems(8.)),
"text-left" => d.text_left(),
"text-center" => d.text_center(),
"text-right" => d.text_right(),
"underline" => d.underline(),
"line-through" => d.line_through(),
"no-underline" => d.text_decoration_none(),
"decoration-solid" => d.text_decoration_solid(),
"decoration-wavy" => d.text_decoration_wavy(),
"decoration-0" => d.text_decoration_0(),
"decoration-1" => d.text_decoration_1(),
"decoration-2" => d.text_decoration_2(),
"decoration-4" => d.text_decoration_4(),
"decoration-8" => d.text_decoration_8(),
"leading-none" => d.line_height(relative(1.)),
"leading-tight" => d.line_height(relative(1.25)),
"leading-snug" => d.line_height(relative(1.375)),
"leading-normal" => d.line_height(relative(1.5)),
"leading-relaxed" => d.line_height(relative(1.625)),
"leading-loose" => d.line_height(relative(2.)),
"truncate" => d.truncate(),
"text-ellipsis" => d.text_ellipsis(),
"whitespace-nowrap" => d.whitespace_nowrap(),
"whitespace-normal" => d.whitespace_normal(),
"cursor-default" => d.cursor_default(),
"cursor-pointer" => d.cursor_pointer(),
"cursor-text" => d.cursor_text(),
"cursor-move" => d.cursor_move(),
"cursor-not-allowed" => d.cursor_not_allowed(),
"cursor-context-menu" => d.cursor_context_menu(),
"cursor-crosshair" => d.cursor_crosshair(),
"cursor-vertical-text" => d.cursor_vertical_text(),
"cursor-alias" => d.cursor_alias(),
"cursor-copy" => d.cursor_copy(),
"cursor-no-drop" => d.cursor_no_drop(),
"cursor-grab" => d.cursor_grab(),
"cursor-grabbing" => d.cursor_grabbing(),
"cursor-ew-resize" => d.cursor_ew_resize(),
"cursor-ns-resize" => d.cursor_ns_resize(),
"cursor-nesw-resize" => d.cursor_nesw_resize(),
"cursor-nwse-resize" => d.cursor_nwse_resize(),
"cursor-col-resize" => d.cursor_col_resize(),
"cursor-row-resize" => d.cursor_row_resize(),
"cursor-n-resize" => d.cursor_n_resize(),
"cursor-e-resize" => d.cursor_e_resize(),
"cursor-s-resize" => d.cursor_s_resize(),
"cursor-w-resize" => d.cursor_w_resize(),
"shadow-2xs" => d.shadow_2xs(),
"shadow-xs" => d.shadow_xs(),
"shadow-sm" => d.shadow_sm(),
"shadow" | "shadow-md" => d.shadow_md(),
"shadow-lg" => d.shadow_lg(),
"shadow-xl" => d.shadow_xl(),
"shadow-2xl" => d.shadow_2xl(),
"shadow-none" => d.shadow_none(),
"grid-cols-1" => d.grid_cols(1),
"grid-cols-2" => d.grid_cols(2),
"grid-cols-3" => d.grid_cols(3),
"grid-cols-4" => d.grid_cols(4),
"grid-cols-5" => d.grid_cols(5),
"grid-cols-6" => d.grid_cols(6),
"grid-cols-7" => d.grid_cols(7),
"grid-cols-8" => d.grid_cols(8),
"grid-cols-9" => d.grid_cols(9),
"grid-cols-10" => d.grid_cols(10),
"grid-cols-11" => d.grid_cols(11),
"grid-cols-12" => d.grid_cols(12),
"grid-rows-1" => d.grid_rows(1),
"grid-rows-2" => d.grid_rows(2),
"grid-rows-3" => d.grid_rows(3),
"grid-rows-4" => d.grid_rows(4),
"grid-rows-5" => d.grid_rows(5),
"grid-rows-6" => d.grid_rows(6),
"col-span-full" => d.col_span_full(),
"col-start-auto" => d.col_start_auto(),
"col-end-auto" => d.col_end_auto(),
"row-span-full" => d.row_span_full(),
"row-start-auto" => d.row_start_auto(),
"row-end-auto" => d.row_end_auto(),
"transition"
| "transition-all"
| "transition-colors"
| "transition-opacity"
| "transition-shadow"
| "transition-transform"
| "duration-75"
| "duration-100"
| "duration-150"
| "duration-200"
| "duration-300"
| "duration-500"
| "duration-700"
| "duration-1000"
| "ease-linear"
| "ease-in"
| "ease-out"
| "ease-in-out"
| "delay-75"
| "delay-100"
| "delay-150"
| "delay-200"
| "delay-300"
| "delay-500"
| "delay-700"
| "delay-1000"
| "animate-none"
| "animate-spin"
| "animate-ping"
| "animate-pulse"
| "animate-bounce" => d,
"self-start" => {
let mut d = d;
d.style().align_self = Some(AlignItems::Start);
d
}
"self-end" => {
let mut d = d;
d.style().align_self = Some(AlignItems::End);
d
}
"self-center" => {
let mut d = d;
d.style().align_self = Some(AlignItems::Center);
d
}
"self-stretch" => {
let mut d = d;
d.style().align_self = Some(AlignItems::Stretch);
d
}
"self-baseline" => {
let mut d = d;
d.style().align_self = Some(AlignItems::Baseline);
d
}
"self-auto" => {
let mut d = d;
d.style().align_self = None;
d
}
"aspect-square" => {
let mut d = d;
d.style().aspect_ratio = Some(1.0);
d
}
"aspect-video" => {
let mut d = d;
d.style().aspect_ratio = Some(16.0 / 9.0);
d
}
"aspect-auto" => {
let mut d = d;
d.style().aspect_ratio = None;
d
}
"ring" => d.shadow(vec![BoxShadow {
color: hsla(0.603, 0.912, 0.602, 0.5),
offset: point(px(0.), px(0.)),
blur_radius: px(0.),
spread_radius: px(3.),
}]),
"ring-0" => d.shadow_none(),
"ring-1" => d.shadow(vec![BoxShadow {
color: hsla(0.603, 0.912, 0.602, 0.5),
offset: point(px(0.), px(0.)),
blur_radius: px(0.),
spread_radius: px(1.),
}]),
"ring-2" => d.shadow(vec![BoxShadow {
color: hsla(0.603, 0.912, 0.602, 0.5),
offset: point(px(0.), px(0.)),
blur_radius: px(0.),
spread_radius: px(2.),
}]),
"ring-4" => d.shadow(vec![BoxShadow {
color: hsla(0.603, 0.912, 0.602, 0.5),
offset: point(px(0.), px(0.)),
blur_radius: px(0.),
spread_radius: px(4.),
}]),
"ring-8" => d.shadow(vec![BoxShadow {
color: hsla(0.603, 0.912, 0.602, 0.5),
offset: point(px(0.), px(0.)),
blur_radius: px(0.),
spread_radius: px(8.),
}]),
"ring-inset" => d,
#[cfg(feature = "gpui_text_run_styles")]
"uppercase" => d.text_transform(TextTransform::Uppercase),
#[cfg(feature = "gpui_text_run_styles")]
"lowercase" => d.text_transform(TextTransform::Lowercase),
#[cfg(feature = "gpui_text_run_styles")]
"capitalize" => d.text_transform(TextTransform::Capitalize),
#[cfg(feature = "gpui_text_run_styles")]
"normal-case" => d.text_transform(TextTransform::None),
#[cfg(not(feature = "gpui_text_run_styles"))]
"uppercase" | "lowercase" | "capitalize" | "normal-case" => d,
"inline-flex"
| "inline"
| "inline-block"
| "select-none"
| "pointer-events-none"
| "whitespace-pre"
| "sticky"
| "fixed"
| "items-stretch" => d,
#[cfg(debug_assertions)]
"debug" => d.debug(),
#[cfg(debug_assertions)]
"debug-below" => d.debug_below(),
#[cfg(not(debug_assertions))]
"debug" | "debug-below" => d,
_ => return Err(d),
})
}
type LengthPropEntry = (&'static str, fn(Div, Length) -> Div);
type DefiniteLengthPropEntry = (&'static str, fn(Div, DefiniteLength) -> Div);
fn apply_dynamic(d: Div, class: &str) -> Div {
const LENGTH_PROPS: &[LengthPropEntry] = &[
("w-", |d, v| d.w(v)),
("h-", |d, v| d.h(v)),
("min-w-", |d, v| d.min_w(v)),
("min-h-", |d, v| d.min_h(v)),
("max-w-", |d, v| d.max_w(v)),
("max-h-", |d, v| d.max_h(v)),
("m-", |d, v| d.m(v)),
("mx-", |d, v| d.mx(v)),
("my-", |d, v| d.my(v)),
("mt-", |d, v| d.mt(v)),
("mb-", |d, v| d.mb(v)),
("ml-", |d, v| d.ml(v)),
("mr-", |d, v| d.mr(v)),
("top-", |d, v| d.top(v)),
("bottom-", |d, v| d.bottom(v)),
("left-", |d, v| d.left(v)),
("right-", |d, v| d.right(v)),
("inset-", |d, v| d.inset(v)),
("size-", |d, v| d.size(v)),
("basis-", |d, v| d.flex_basis(v)),
];
for (prefix, apply) in LENGTH_PROPS {
if let Some(rest) = class.strip_prefix(prefix) {
if let Some(len) = parse_length(rest) {
return apply(d, len);
}
}
}
const DEFINITE_PROPS: &[DefiniteLengthPropEntry] = &[
("p-", |d, v| d.p(v)),
("px-", |d, v| d.px(v)),
("py-", |d, v| d.py(v)),
("pt-", |d, v| d.pt(v)),
("pb-", |d, v| d.pb(v)),
("pl-", |d, v| d.pl(v)),
("pr-", |d, v| d.pr(v)),
("gap-", |d, v| d.gap(v)),
("gap-x-", |d, v| d.gap_x(v)),
("gap-y-", |d, v| d.gap_y(v)),
];
for (prefix, apply) in DEFINITE_PROPS {
if let Some(rest) = class.strip_prefix(prefix) {
if let Some(len) = parse_definite_length(rest) {
return apply(d, len);
}
}
}
for (prefix, apply_fn) in &[
(
"rounded-tl-[",
Div::rounded_tl as fn(Div, AbsoluteLength) -> Div,
),
(
"rounded-tr-[",
Div::rounded_tr as fn(Div, AbsoluteLength) -> Div,
),
(
"rounded-bl-[",
Div::rounded_bl as fn(Div, AbsoluteLength) -> Div,
),
(
"rounded-br-[",
Div::rounded_br as fn(Div, AbsoluteLength) -> Div,
),
(
"rounded-t-[",
Div::rounded_t as fn(Div, AbsoluteLength) -> Div,
),
(
"rounded-b-[",
Div::rounded_b as fn(Div, AbsoluteLength) -> Div,
),
(
"rounded-l-[",
Div::rounded_l as fn(Div, AbsoluteLength) -> Div,
),
(
"rounded-r-[",
Div::rounded_r as fn(Div, AbsoluteLength) -> Div,
),
("rounded-[", Div::rounded as fn(Div, AbsoluteLength) -> Div),
] {
if let Some(rest) = class.strip_prefix(prefix) {
if let Some(inner) = rest.strip_suffix(']') {
if let Some(abs) = parse_absolute_length(inner) {
return apply_fn(d, abs);
}
}
}
}
for (prefix, apply_fn) in &[
(
"border-t-[",
Div::border_t as fn(Div, AbsoluteLength) -> Div,
),
(
"border-b-[",
Div::border_b as fn(Div, AbsoluteLength) -> Div,
),
(
"border-l-[",
Div::border_l as fn(Div, AbsoluteLength) -> Div,
),
(
"border-r-[",
Div::border_r as fn(Div, AbsoluteLength) -> Div,
),
("border-[", Div::border as fn(Div, AbsoluteLength) -> Div),
] {
if let Some(rest) = class.strip_prefix(prefix) {
if let Some(inner) = rest.strip_suffix(']') {
if let Some(abs) = parse_absolute_length(inner) {
return apply_fn(d, abs);
}
}
}
}
if let Some(rest) = class.strip_prefix("col-span-") {
if let Ok(n) = rest.parse::<u16>() {
return d.col_span(n);
}
}
if let Some(rest) = class.strip_prefix("col-start-") {
if let Ok(n) = rest.parse::<i16>() {
return d.col_start(n);
}
}
if let Some(rest) = class.strip_prefix("col-end-") {
if let Ok(n) = rest.parse::<i16>() {
return d.col_end(n);
}
}
if let Some(rest) = class.strip_prefix("row-span-") {
if let Ok(n) = rest.parse::<u16>() {
return d.row_span(n);
}
}
if let Some(rest) = class.strip_prefix("row-start-") {
if let Ok(n) = rest.parse::<i16>() {
return d.row_start(n);
}
}
if let Some(rest) = class.strip_prefix("row-end-") {
if let Ok(n) = rest.parse::<i16>() {
return d.row_end(n);
}
}
if let Some(rest) = class.strip_prefix("grid-cols-") {
if let Ok(n) = rest.parse::<u16>() {
return d.grid_cols(n);
}
}
if let Some(rest) = class.strip_prefix("grid-rows-") {
if let Ok(n) = rest.parse::<u16>() {
return d.grid_rows(n);
}
}
#[cfg(feature = "gpui_text_run_styles")]
{
match class {
"tracking-tighter" => return d.letter_spacing(px(-2.0)),
"tracking-tight" => return d.letter_spacing(px(-1.0)),
"tracking-normal" => return d.letter_spacing(px(0.)),
"tracking-wide" => return d.letter_spacing(px(1.5)),
"tracking-wider" => return d.letter_spacing(px(3.0)),
"tracking-widest" => return d.letter_spacing(px(5.0)),
_ => {}
}
if let Some(rest) = class.strip_prefix("tracking-[") {
if let Some(inner) = rest.strip_suffix(']') {
if let Some(abs) = parse_absolute_length(inner) {
let px_val = match abs {
gpui::AbsoluteLength::Pixels(p) => p,
gpui::AbsoluteLength::Rems(r) => px(r.0 * 16.0),
};
return d.letter_spacing(px_val);
}
}
}
}
if let Some(rest) = class.strip_prefix("aspect-[") {
if let Some(inner) = rest.strip_suffix(']') {
let ratio = if let Some(slash) = inner.find('/') {
let w = inner[..slash].parse::<f32>().ok();
let h = inner[slash + 1..].parse::<f32>().ok();
w.zip(h)
.and_then(|(w, h)| if h != 0.0 { Some(w / h) } else { None })
} else {
inner.parse::<f32>().ok()
};
if let Some(r) = ratio {
let mut d = d;
d.style().aspect_ratio = Some(r);
return d;
}
}
}
if let Some(rest) = class.strip_prefix("ring-[") {
if let Some(inner) = rest.strip_suffix(']') {
if let Some(abs) = parse_absolute_length(inner) {
let spread = match abs {
gpui::AbsoluteLength::Pixels(p) => p,
gpui::AbsoluteLength::Rems(r) => px(r.0 * 16.0),
};
return d.shadow(vec![BoxShadow {
color: hsla(0.603, 0.912, 0.602, 0.5),
offset: point(px(0.), px(0.)),
blur_radius: px(0.),
spread_radius: spread,
}]);
}
}
}
if let Some(rest) = class.strip_prefix("line-clamp-") {
if let Ok(n) = rest.parse::<usize>() {
return d.line_clamp(n);
}
}
if let Some(rest) = class.strip_prefix("font-[") {
if let Some(inner) = rest.strip_suffix(']') {
let family = inner.trim_matches('\'').trim_matches('"').replace('_', " ");
return d.font_family(family);
}
}
if let Some(rest) = class.strip_prefix("text-[") {
if let Some(inner) = rest.strip_suffix(']') {
if let Some(hex_str) = inner.strip_prefix('#') {
if let Ok(hex) = u32::from_str_radix(hex_str, 16) {
return d.text_color(rgb(hex));
}
}
if let Some(len) = parse_absolute_length(inner) {
return d.text_size(len);
}
}
}
if let Some(rest) = class.strip_prefix("leading-[") {
if let Some(inner) = rest.strip_suffix(']') {
if let Some(abs) = parse_absolute_length(inner) {
return d.line_height(abs);
}
}
}
if let Some(rest) = class.strip_prefix("opacity-") {
if let Ok(n) = rest.parse::<f32>() {
return d.opacity(n / 100.0);
}
}
if let Some(rest) = class.strip_prefix("decoration-[") {
if let Some(inner) = rest.strip_suffix(']') {
if let Some(hex_str) = inner.strip_prefix('#') {
if let Ok(hex) = u32::from_str_radix(hex_str, 16) {
return d.text_decoration_color(rgb(hex));
}
}
}
}
if let Some(rest) = class.strip_prefix("decoration-") {
if let Some(dash_pos) = rest.rfind('-') {
let family = &rest[..dash_pos];
let shade = &rest[dash_pos + 1..];
if let Some(hex) = tailwind_color(family, shade) {
return d.text_decoration_color(rgb(hex));
}
}
}
if let Some(rest) = class.strip_prefix("text-bg-[") {
if let Some(inner) = rest.strip_suffix(']') {
if let Some(hex_str) = inner.strip_prefix('#') {
if let Ok(hex) = u32::from_str_radix(hex_str, 16) {
return d.text_bg(rgb(hex));
}
}
}
}
if let Some(rest) = class.strip_prefix("text-bg-") {
if let Some(dash_pos) = rest.rfind('-') {
let family = &rest[..dash_pos];
let shade = &rest[dash_pos + 1..];
if let Some(hex) = tailwind_color(family, shade) {
return d.text_bg(rgb(hex));
}
}
}
for (prefix, apply_fn) in &[
("bg-", apply_bg as fn(Div, u32) -> Div),
("text-", apply_text_color as fn(Div, u32) -> Div),
("border-", apply_border_color as fn(Div, u32) -> Div),
] {
if let Some(rest) = class.strip_prefix(prefix) {
if rest.starts_with('[') && rest.ends_with(']') {
let inner = &rest[1..rest.len() - 1];
if let Some(hex_str) = inner.strip_prefix('#') {
if hex_str.len() == 8 {
if let Ok(hex) = u32::from_str_radix(hex_str, 16) {
return d.bg(rgba(hex));
}
}
if let Ok(hex) = u32::from_str_radix(hex_str, 16) {
return apply_fn(d, hex);
}
}
if let Some(hsla_str) = inner.strip_prefix("hsla(") {
if let Some(vals) = hsla_str.strip_suffix(')') {
let parts: Vec<&str> = vals.split(',').map(|s| s.trim()).collect();
if parts.len() == 4 {
if let (Ok(h), Ok(s), Ok(l), Ok(a)) = (
parts[0].parse::<f32>(),
parts[1].trim_end_matches('%').parse::<f32>(),
parts[2].trim_end_matches('%').parse::<f32>(),
parts[3].parse::<f32>(),
) {
let color = hsla(h / 360.0, s / 100.0, l / 100.0, a);
if *prefix == "bg-" {
return d.bg(color);
}
if *prefix == "text-" {
return d.text_color(color);
}
if *prefix == "border-" {
return d.border_color(color);
}
}
}
}
}
}
if let Some(slash_pos) = rest.rfind('/') {
let color_part = &rest[..slash_pos];
let alpha_str = &rest[slash_pos + 1..];
if let Ok(alpha_pct) = alpha_str.parse::<u32>() {
if let Some(dash_pos) = color_part.rfind('-') {
let family = &color_part[..dash_pos];
let shade = &color_part[dash_pos + 1..];
if let Some(hex) = tailwind_color(family, shade) {
let alpha_byte = alpha_pct * 255 / 100;
let rgba_val = (hex << 8) | alpha_byte;
return d.bg(rgba(rgba_val));
}
}
}
}
if let Some(dash_pos) = rest.rfind('-') {
let family = &rest[..dash_pos];
let shade = &rest[dash_pos + 1..];
if let Some(hex) = tailwind_color(family, shade) {
return apply_fn(d, hex);
}
}
}
}
d
}
fn apply_bg(d: Div, hex: u32) -> Div {
d.bg(rgb(hex))
}
fn apply_text_color(d: Div, hex: u32) -> Div {
d.text_color(rgb(hex))
}
fn apply_border_color(d: Div, hex: u32) -> Div {
d.border_color(rgb(hex))
}
fn parse_length(value: &str) -> Option<Length> {
if value.starts_with('[') && value.ends_with(']') {
let inner = &value[1..value.len() - 1];
if let Some(abs) = parse_absolute_length(inner) {
return Some(abs.into());
}
if let Some(rest) = inner.strip_suffix('%') {
if let Ok(n) = rest.parse::<f32>() {
return Some(relative(n / 100.0).into());
}
}
return None;
}
match value {
"full" | "screen" => return Some(relative(1.).into()),
"auto" => return Some(Length::Auto),
"px" => return Some(px(1.).into()),
"1/2" => return Some(relative(0.5).into()),
"1/3" => return Some(relative(1.0 / 3.0).into()),
"2/3" => return Some(relative(2.0 / 3.0).into()),
"1/4" => return Some(relative(0.25).into()),
"2/4" => return Some(relative(0.5).into()),
"3/4" => return Some(relative(0.75).into()),
"1/5" => return Some(relative(0.2).into()),
"2/5" => return Some(relative(0.4).into()),
"3/5" => return Some(relative(0.6).into()),
"4/5" => return Some(relative(0.8).into()),
"1/6" => return Some(relative(1.0 / 6.0).into()),
"2/6" => return Some(relative(2.0 / 6.0).into()),
"3/6" => return Some(relative(0.5).into()),
"4/6" => return Some(relative(4.0 / 6.0).into()),
"5/6" => return Some(relative(5.0 / 6.0).into()),
"1/12" => return Some(relative(1.0 / 12.0).into()),
"2/12" => return Some(relative(2.0 / 12.0).into()),
"3/12" => return Some(relative(0.25).into()),
"4/12" => return Some(relative(4.0 / 12.0).into()),
"5/12" => return Some(relative(5.0 / 12.0).into()),
"6/12" => return Some(relative(0.5).into()),
"7/12" => return Some(relative(7.0 / 12.0).into()),
"8/12" => return Some(relative(8.0 / 12.0).into()),
"9/12" => return Some(relative(9.0 / 12.0).into()),
"10/12" => return Some(relative(10.0 / 12.0).into()),
"11/12" => return Some(relative(11.0 / 12.0).into()),
_ => {}
}
if let Ok(n) = value.parse::<f32>() {
return Some(rems(n * 0.25).into());
}
None
}
fn parse_definite_length(value: &str) -> Option<DefiniteLength> {
if value.starts_with('[') && value.ends_with(']') {
return parse_absolute_length(&value[1..value.len() - 1]).map(Into::into);
}
if value == "px" {
return Some(px(1.).into());
}
if let Ok(n) = value.parse::<f32>() {
return Some(rems(n * 0.25).into());
}
None
}
pub fn parse_absolute_length(inner: &str) -> Option<AbsoluteLength> {
if let Some(rest) = inner.strip_suffix("px") {
if let Ok(n) = rest.parse::<f32>() {
return Some(px(n).into());
}
}
if let Some(rest) = inner.strip_suffix("rem") {
if let Ok(n) = rest.parse::<f32>() {
return Some(rems(n).into());
}
}
if let Ok(n) = inner.parse::<f32>() {
return Some(px(n).into());
}
None
}
pub fn parse_duration_ms(s: &str) -> Option<u64> {
if let Some(rest) = s.strip_suffix("ms") {
return rest.parse::<u64>().ok();
}
if let Some(rest) = s.strip_suffix('s') {
return rest.parse::<f64>().ok().map(|v| (v * 1000.0) as u64);
}
s.parse::<u64>().ok()
}
pub fn tailwind_color(family: &str, shade: &str) -> Option<u32> {
match (family, shade) {
("slate", "50") => Some(0xf8fafc),
("slate", "100") => Some(0xf1f5f9),
("slate", "200") => Some(0xe2e8f0),
("slate", "300") => Some(0xcbd5e1),
("slate", "400") => Some(0x94a3b8),
("slate", "500") => Some(0x64748b),
("slate", "600") => Some(0x475569),
("slate", "700") => Some(0x334155),
("slate", "800") => Some(0x1e293b),
("slate", "900") => Some(0x0f172a),
("slate", "950") => Some(0x020617),
("gray", "50") => Some(0xf9fafb),
("gray", "100") => Some(0xf3f4f6),
("gray", "200") => Some(0xe5e7eb),
("gray", "300") => Some(0xd1d5db),
("gray", "400") => Some(0x9ca3af),
("gray", "500") => Some(0x6b7280),
("gray", "600") => Some(0x4b5563),
("gray", "700") => Some(0x374151),
("gray", "800") => Some(0x1f2937),
("gray", "900") => Some(0x111827),
("gray", "950") => Some(0x030712),
("zinc", "50") => Some(0xfafafa),
("zinc", "100") => Some(0xf4f4f5),
("zinc", "200") => Some(0xe4e4e7),
("zinc", "300") => Some(0xd4d4d8),
("zinc", "400") => Some(0xa1a1aa),
("zinc", "500") => Some(0x71717a),
("zinc", "600") => Some(0x52525b),
("zinc", "700") => Some(0x3f3f46),
("zinc", "800") => Some(0x27272a),
("zinc", "900") => Some(0x18181b),
("zinc", "950") => Some(0x09090b),
("neutral", "50") => Some(0xfafafa),
("neutral", "100") => Some(0xf5f5f5),
("neutral", "200") => Some(0xe5e5e5),
("neutral", "300") => Some(0xd4d4d4),
("neutral", "400") => Some(0xa3a3a3),
("neutral", "500") => Some(0x737373),
("neutral", "600") => Some(0x525252),
("neutral", "700") => Some(0x404040),
("neutral", "800") => Some(0x262626),
("neutral", "900") => Some(0x171717),
("neutral", "950") => Some(0x0a0a0a),
("stone", "50") => Some(0xfafaf9),
("stone", "100") => Some(0xf5f5f4),
("stone", "200") => Some(0xe7e5e4),
("stone", "300") => Some(0xd6d3d1),
("stone", "400") => Some(0xa8a29e),
("stone", "500") => Some(0x78716c),
("stone", "600") => Some(0x57534e),
("stone", "700") => Some(0x44403c),
("stone", "800") => Some(0x292524),
("stone", "900") => Some(0x1c1917),
("stone", "950") => Some(0x0c0a09),
("red", "50") => Some(0xfef2f2),
("red", "100") => Some(0xfee2e2),
("red", "200") => Some(0xfecaca),
("red", "300") => Some(0xfca5a5),
("red", "400") => Some(0xf87171),
("red", "500") => Some(0xef4444),
("red", "600") => Some(0xdc2626),
("red", "700") => Some(0xb91c1c),
("red", "800") => Some(0x991b1b),
("red", "900") => Some(0x7f1d1d),
("red", "950") => Some(0x450a0a),
("orange", "50") => Some(0xfff7ed),
("orange", "100") => Some(0xffedd5),
("orange", "200") => Some(0xfed7aa),
("orange", "300") => Some(0xfdba74),
("orange", "400") => Some(0xfb923c),
("orange", "500") => Some(0xf97316),
("orange", "600") => Some(0xea580c),
("orange", "700") => Some(0xc2410c),
("orange", "800") => Some(0x9a3412),
("orange", "900") => Some(0x7c2d12),
("orange", "950") => Some(0x431407),
("amber", "50") => Some(0xfffbeb),
("amber", "100") => Some(0xfef3c7),
("amber", "200") => Some(0xfde68a),
("amber", "300") => Some(0xfcd34d),
("amber", "400") => Some(0xfbbf24),
("amber", "500") => Some(0xf59e0b),
("amber", "600") => Some(0xd97706),
("amber", "700") => Some(0xb45309),
("amber", "800") => Some(0x92400e),
("amber", "900") => Some(0x78350f),
("amber", "950") => Some(0x451a03),
("yellow", "50") => Some(0xfefce8),
("yellow", "100") => Some(0xfef9c3),
("yellow", "200") => Some(0xfef08a),
("yellow", "300") => Some(0xfde047),
("yellow", "400") => Some(0xfacc15),
("yellow", "500") => Some(0xeab308),
("yellow", "600") => Some(0xca8a04),
("yellow", "700") => Some(0xa16207),
("yellow", "800") => Some(0x854d0e),
("yellow", "900") => Some(0x713f12),
("yellow", "950") => Some(0x422006),
("lime", "50") => Some(0xf7fee7),
("lime", "100") => Some(0xecfccb),
("lime", "200") => Some(0xd9f99d),
("lime", "300") => Some(0xbef264),
("lime", "400") => Some(0xa3e635),
("lime", "500") => Some(0x84cc16),
("lime", "600") => Some(0x65a30d),
("lime", "700") => Some(0x4d7c0f),
("lime", "800") => Some(0x3f6212),
("lime", "900") => Some(0x365314),
("lime", "950") => Some(0x1a2e05),
("green", "50") => Some(0xf0fdf4),
("green", "100") => Some(0xdcfce7),
("green", "200") => Some(0xbbf7d0),
("green", "300") => Some(0x86efac),
("green", "400") => Some(0x4ade80),
("green", "500") => Some(0x22c55e),
("green", "600") => Some(0x16a34a),
("green", "700") => Some(0x15803d),
("green", "800") => Some(0x166534),
("green", "900") => Some(0x14532d),
("green", "950") => Some(0x052e16),
("emerald", "50") => Some(0xecfdf5),
("emerald", "100") => Some(0xd1fae5),
("emerald", "200") => Some(0xa7f3d0),
("emerald", "300") => Some(0x6ee7b7),
("emerald", "400") => Some(0x34d399),
("emerald", "500") => Some(0x10b981),
("emerald", "600") => Some(0x059669),
("emerald", "700") => Some(0x047857),
("emerald", "800") => Some(0x065f46),
("emerald", "900") => Some(0x064e3b),
("emerald", "950") => Some(0x022c22),
("teal", "50") => Some(0xf0fdfa),
("teal", "100") => Some(0xccfbf1),
("teal", "200") => Some(0x99f6e4),
("teal", "300") => Some(0x5eead4),
("teal", "400") => Some(0x2dd4bf),
("teal", "500") => Some(0x14b8a6),
("teal", "600") => Some(0x0d9488),
("teal", "700") => Some(0x0f766e),
("teal", "800") => Some(0x115e59),
("teal", "900") => Some(0x134e4a),
("teal", "950") => Some(0x042f2e),
("cyan", "50") => Some(0xecfeff),
("cyan", "100") => Some(0xcffafe),
("cyan", "200") => Some(0xa5f3fc),
("cyan", "300") => Some(0x67e8f9),
("cyan", "400") => Some(0x22d3ee),
("cyan", "500") => Some(0x06b6d4),
("cyan", "600") => Some(0x0891b2),
("cyan", "700") => Some(0x0e7490),
("cyan", "800") => Some(0x155e75),
("cyan", "900") => Some(0x164e63),
("cyan", "950") => Some(0x083344),
("sky", "50") => Some(0xf0f9ff),
("sky", "100") => Some(0xe0f2fe),
("sky", "200") => Some(0xbae6fd),
("sky", "300") => Some(0x7dd3fc),
("sky", "400") => Some(0x38bdf8),
("sky", "500") => Some(0x0ea5e9),
("sky", "600") => Some(0x0284c7),
("sky", "700") => Some(0x0369a1),
("sky", "800") => Some(0x075985),
("sky", "900") => Some(0x0c4a6e),
("sky", "950") => Some(0x082f49),
("blue", "50") => Some(0xeff6ff),
("blue", "100") => Some(0xdbeafe),
("blue", "200") => Some(0xbfdbfe),
("blue", "300") => Some(0x93c5fd),
("blue", "400") => Some(0x60a5fa),
("blue", "500") => Some(0x3b82f6),
("blue", "600") => Some(0x2563eb),
("blue", "700") => Some(0x1d4ed8),
("blue", "800") => Some(0x1e40af),
("blue", "900") => Some(0x1e3a8a),
("blue", "950") => Some(0x172554),
("indigo", "50") => Some(0xeef2ff),
("indigo", "100") => Some(0xe0e7ff),
("indigo", "200") => Some(0xc7d2fe),
("indigo", "300") => Some(0xa5b4fc),
("indigo", "400") => Some(0x818cf8),
("indigo", "500") => Some(0x6366f1),
("indigo", "600") => Some(0x4f46e5),
("indigo", "700") => Some(0x4338ca),
("indigo", "800") => Some(0x3730a3),
("indigo", "900") => Some(0x312e81),
("indigo", "950") => Some(0x1e1b4b),
("violet", "50") => Some(0xf5f3ff),
("violet", "100") => Some(0xede9fe),
("violet", "200") => Some(0xddd6fe),
("violet", "300") => Some(0xc4b5fd),
("violet", "400") => Some(0xa78bfa),
("violet", "500") => Some(0x8b5cf6),
("violet", "600") => Some(0x7c3aed),
("violet", "700") => Some(0x6d28d9),
("violet", "800") => Some(0x5b21b6),
("violet", "900") => Some(0x4c1d95),
("violet", "950") => Some(0x2e1065),
("purple", "50") => Some(0xfaf5ff),
("purple", "100") => Some(0xf3e8ff),
("purple", "200") => Some(0xe9d5ff),
("purple", "300") => Some(0xd8b4fe),
("purple", "400") => Some(0xc084fc),
("purple", "500") => Some(0xa855f7),
("purple", "600") => Some(0x9333ea),
("purple", "700") => Some(0x7e22ce),
("purple", "800") => Some(0x6b21a8),
("purple", "900") => Some(0x581c87),
("purple", "950") => Some(0x3b0764),
("fuchsia", "50") => Some(0xfdf4ff),
("fuchsia", "100") => Some(0xfae8ff),
("fuchsia", "200") => Some(0xf5d0fe),
("fuchsia", "300") => Some(0xf0abfc),
("fuchsia", "400") => Some(0xe879f9),
("fuchsia", "500") => Some(0xd946ef),
("fuchsia", "600") => Some(0xc026d3),
("fuchsia", "700") => Some(0xa21caf),
("fuchsia", "800") => Some(0x86198f),
("fuchsia", "900") => Some(0x701a75),
("fuchsia", "950") => Some(0x4a044e),
("pink", "50") => Some(0xfdf2f8),
("pink", "100") => Some(0xfce7f3),
("pink", "200") => Some(0xfbcfe8),
("pink", "300") => Some(0xf9a8d4),
("pink", "400") => Some(0xf472b6),
("pink", "500") => Some(0xec4899),
("pink", "600") => Some(0xdb2777),
("pink", "700") => Some(0xbe185d),
("pink", "800") => Some(0x9d174d),
("pink", "900") => Some(0x831843),
("pink", "950") => Some(0x500724),
("rose", "50") => Some(0xfff1f2),
("rose", "100") => Some(0xffe4e6),
("rose", "200") => Some(0xfecdd3),
("rose", "300") => Some(0xfda4af),
("rose", "400") => Some(0xfb7185),
("rose", "500") => Some(0xf43f5e),
("rose", "600") => Some(0xe11d48),
("rose", "700") => Some(0xbe123c),
("rose", "800") => Some(0x9f1239),
("rose", "900") => Some(0x881337),
("rose", "950") => Some(0x4c0519),
_ => None,
}
}