From 55ef88b7262ef5c2fa2eede4dbbbc9bd8e8c226c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Tue, 9 Dec 2025 22:25:01 +0800 Subject: [PATCH] feat!: rounded corners for indicator bar (#3419) --- CHANGELOG.md | 3 + Cargo.lock | 26 ++--- Cargo.toml | 2 +- yazi-actor/src/lives/core.rs | 2 +- yazi-actor/src/lives/file.rs | 5 +- yazi-actor/src/lives/task.rs | 3 +- yazi-actor/src/lives/tasks.rs | 3 +- yazi-binding/src/elements/rect.rs | 2 +- yazi-binding/src/macros.rs | 109 +++++++++++++++----- yazi-binding/src/style.rs | 7 +- yazi-binding/src/utils.rs | 5 +- yazi-config/preset/theme-dark.toml | 15 ++- yazi-config/preset/theme-light.toml | 15 ++- yazi-config/src/color.rs | 27 ----- yazi-config/src/lib.rs | 2 +- yazi-config/src/style.rs | 113 +++++++++++---------- yazi-config/src/theme/icon.rs | 3 +- yazi-config/src/theme/theme.rs | 49 +++++---- yazi-fm/src/app/commands/reflow.rs | 2 +- yazi-plugin/preset/components/entity.lua | 33 +++++- yazi-plugin/preset/components/linemode.lua | 19 +++- yazi-plugin/preset/components/parent.lua | 15 +-- yazi-plugin/src/elements/elements.rs | 4 +- yazi-plugin/src/fs/fs.rs | 8 +- yazi-plugin/src/runtime/runtime.rs | 7 +- yazi-plugin/src/theme/theme.rs | 42 +++++++- yazi-plugin/src/utils/app.rs | 2 +- yazi-plugin/src/utils/json.rs | 3 +- 28 files changed, 340 insertions(+), 186 deletions(-) delete mode 100644 yazi-config/src/color.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1da97b9d..b8c51214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Improve the UX of the pick and input components ([#2906], [#2935]) - Show progress of each task in task manager ([#3121], [#3131], [#3134]) - New `overall` option to set the overall background color ([#3317]) +- Rounded corners for indicator bar ([#3419]) - New `bulk_rename` command always renames files with the editor ([#2984]) - `key-*` DDS events to allow changing or canceling user key events ([#3005], [#3037]) - New `--bg` specifying image background color in the preset SVG and ImageMagick previewers ([#3189]) @@ -45,6 +46,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Rename `name` to `url` for open, fetchers, spotters, preloaders, previewers, filetype, and `globs` icon rules to support virtual file system ([#3034]) - Rename `mime` fetcher to `mime.local`, and introduce `mime.dir` fetcher to support folder MIME types ([#3222]) +- Reclassify `hovered` and `preview_hovered` under `[mgr]` of `theme.toml` into `[indicator]` as `current` and `preview`, respectively ([#3419]) - Remove `$0` parameter in opener rules to make the `open` command work under empty directories ([#3226]) - Return `Path` instead of `Url` from `Url:strip_prefix()` and `File.link_to` to enforce type safety ([#3361], [#3385]) - Use `body` instead of the term `content` in confirmations ([#2921]) @@ -1551,3 +1553,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3391]: https://github.com/sxyazi/yazi/pull/3391 [#3393]: https://github.com/sxyazi/yazi/pull/3393 [#3396]: https://github.com/sxyazi/yazi/pull/3396 +[#3419]: https://github.com/sxyazi/yazi/pull/3419 diff --git a/Cargo.lock b/Cargo.lock index 58543723..77c36bf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,9 +300,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bcrypt-pbkdf" @@ -505,9 +505,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.48" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -662,6 +662,7 @@ dependencies = [ "itoa", "rustversion", "ryu", + "serde", "static_assertions", ] @@ -1305,9 +1306,9 @@ checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flate2" -version = "1.1.6" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30f4148e3c9b7dbe0cc7e842ad5a61b28f9025f201d78149383e778a08bc9215" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -2825,9 +2826,9 @@ dependencies = [ [[package]] name = "pxfm" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3502d6155304a4173a5f2c34b52b7ed0dd085890326cb50fd625fdf39e86b3b" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" dependencies = [ "num-traits", ] @@ -2969,6 +2970,7 @@ dependencies = [ "itertools 0.13.0", "lru 0.12.5", "paste", + "serde", "strum", "unicode-segmentation", "unicode-truncate", @@ -3526,9 +3528,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simd_helpers" @@ -5270,9 +5272,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6fb7703e32e9a07fb3f757360338b3a567a5054f21b5f52a666752e333d58e" +checksum = "f520eebad972262a1dde0ec455bce4f8b298b1e5154513de58c114c4c54303e8" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index e0d1044b..1988d1c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ parking_lot = "0.12.5" paste = "1.0.15" percent-encoding = "2.3.2" rand = { version = "0.9.2", default-features = false, features = [ "os_rng", "small_rng", "std" ] } -ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info", "unstable-widget-ref" ] } +ratatui = { version = "0.29.0", features = [ "serde", "unstable-rendered-line-info", "unstable-widget-ref" ] } regex = "1.12.2" russh = { version = "0.55.0", default-features = false, features = [ "ring", "rsa" ] } scopeguard = "1.2.0" diff --git a/yazi-actor/src/lives/core.rs b/yazi-actor/src/lives/core.rs index d7860c01..a0341054 100644 --- a/yazi-actor/src/lives/core.rs +++ b/yazi-actor/src/lives/core.rs @@ -50,7 +50,7 @@ impl UserData for Core { } }; } - Ok(match key.as_bytes().as_ref() { + Ok(match &*key.as_bytes() { b"active" => reuse!(active, super::Tab::make(me.active())), b"tabs" => reuse!(tabs, super::Tabs::make(&me.mgr.tabs)), b"tasks" => reuse!(tasks, super::Tasks::make(&me.tasks)), diff --git a/yazi-actor/src/lives/file.rs b/yazi-actor/src/lives/file.rs index d3c22917..c981e750 100644 --- a/yazi-actor/src/lives/file.rs +++ b/yazi-actor/src/lives/file.rs @@ -68,8 +68,11 @@ impl UserData for File { fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1)); fields.add_field_method_get("is_hovered", |_, me| Ok(me.idx == me.folder.cursor)); + fields.add_field_method_get("in_current", |_, me| { + Ok(&*me.folder as *const _ == &me.tab.current as *const _) + }); fields.add_field_method_get("in_preview", |_, me| { - Ok(me.tab.hovered().is_some_and(|f| f.url == me.folder.url)) + Ok(me.idx == me.folder.cursor && me.tab.hovered().is_some_and(|f| f.url == me.folder.url)) }); } diff --git a/yazi-actor/src/lives/task.rs b/yazi-actor/src/lives/task.rs index 9003c252..b646c3b3 100644 --- a/yazi-actor/src/lives/task.rs +++ b/yazi-actor/src/lives/task.rs @@ -1,8 +1,7 @@ use std::ops::Deref; use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value}; -use yazi_binding::cached_field; -use yazi_plugin::runtime::SER_OPT; +use yazi_binding::{SER_OPT, cached_field}; use super::{Lives, PtrCell}; diff --git a/yazi-actor/src/lives/tasks.rs b/yazi-actor/src/lives/tasks.rs index 9d264484..e39129cc 100644 --- a/yazi-actor/src/lives/tasks.rs +++ b/yazi-actor/src/lives/tasks.rs @@ -1,8 +1,7 @@ use std::ops::Deref; use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value}; -use yazi_binding::{cached_field, deprecate}; -use yazi_plugin::runtime::SER_OPT; +use yazi_binding::{SER_OPT, cached_field, deprecate}; use super::{Lives, PtrCell}; use crate::lives::TaskSnap; diff --git a/yazi-binding/src/elements/rect.rs b/yazi-binding/src/elements/rect.rs index c1f8262d..f54d9069 100644 --- a/yazi-binding/src/elements/rect.rs +++ b/yazi-binding/src/elements/rect.rs @@ -36,7 +36,7 @@ impl Rect { })?; let index = lua.create_function(move |lua, (_, key): (Table, mlua::String)| { - Ok(match key.as_bytes().as_ref() { + Ok(match &*key.as_bytes() { b"default" => { deprecate!(lua, "`ui.Rect.default` is deprecated, use `ui.Rect{{}}` instead, in your {}\nSee #2927 for more details: https://github.com/sxyazi/yazi/pull/2927"); Some(Self(Default::default())) diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs index e9ff87f2..8ad662a7 100644 --- a/yazi-binding/src/macros.rs +++ b/yazi-binding/src/macros.rs @@ -82,65 +82,122 @@ macro_rules! impl_style_method { macro_rules! impl_style_shorthands { ($methods:ident, $($field:tt).+) => { $methods.add_function_mut("fg", |lua, (ud, value): (mlua::AnyUserData, mlua::Value)| { + use ratatui::style::Modifier; + + let me = &mut ud.borrow_mut::()?.$($field).+; match value { - mlua::Value::Nil => { - ud.borrow::()?.$($field).+.fg.map($crate::Color).into_lua(lua) - }, + mlua::Value::Boolean(true) if me.add_modifier.contains(Modifier::REVERSED) && !me.sub_modifier.contains(Modifier::REVERSED) => { + me.bg.map($crate::Color).into_lua(lua) + } + mlua::Value::Nil | mlua::Value::Boolean(_) => { + me.fg.map($crate::Color).into_lua(lua) + } _ => { - ud.borrow_mut::()?.$($field).+.fg = Some($crate::Color::try_from(value)?.0); + me.fg = Some($crate::Color::try_from(value)?.0); ud.into_lua(lua) } } }); $methods.add_function_mut("bg", |lua, (ud, value): (mlua::AnyUserData, mlua::Value)| { + use ratatui::style::Modifier; + + let me = &mut ud.borrow_mut::()?.$($field).+; match value { - mlua::Value::Nil => { - ud.borrow::()?.$($field).+.bg.map($crate::Color).into_lua(lua) + mlua::Value::Boolean(true) if me.add_modifier.contains(Modifier::REVERSED) && !me.sub_modifier.contains(Modifier::REVERSED) => { + me.fg.map($crate::Color).into_lua(lua) + } + mlua::Value::Nil | mlua::Value::Boolean(_) => { + me.bg.map($crate::Color).into_lua(lua) } _ => { - ud.borrow_mut::()?.$($field).+.bg = Some($crate::Color::try_from(value)?.0); + me.bg = Some($crate::Color::try_from(value)?.0); ud.into_lua(lua) } } }); - $methods.add_function_mut("bold", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::BOLD; + $methods.add_function_mut("bold", |_, (ud, remove): (mlua::AnyUserData, bool)| { + let me = &mut ud.borrow_mut::()?.$($field).+; + if remove { + *me = me.remove_modifier(ratatui::style::Modifier::BOLD); + } else { + *me = me.add_modifier(ratatui::style::Modifier::BOLD); + } Ok(ud) }); - $methods.add_function_mut("dim", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::DIM; + $methods.add_function_mut("dim", |_, (ud, remove): (mlua::AnyUserData, bool)| { + let me = &mut ud.borrow_mut::()?.$($field).+; + if remove { + *me = me.remove_modifier(ratatui::style::Modifier::DIM); + } else { + *me = me.add_modifier(ratatui::style::Modifier::DIM); + } Ok(ud) }); - $methods.add_function_mut("italic", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::ITALIC; + $methods.add_function_mut("italic", |_, (ud, remove): (mlua::AnyUserData, bool)| { + let me = &mut ud.borrow_mut::()?.$($field).+; + if remove { + *me = me.remove_modifier(ratatui::style::Modifier::ITALIC); + } else { + *me = me.add_modifier(ratatui::style::Modifier::ITALIC); + } Ok(ud) }); - $methods.add_function_mut("underline", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::UNDERLINED; + $methods.add_function_mut("underline", |_, (ud, remove): (mlua::AnyUserData, bool)| { + let me = &mut ud.borrow_mut::()?.$($field).+; + if remove { + *me = me.remove_modifier(ratatui::style::Modifier::UNDERLINED); + } else { + *me = me.add_modifier(ratatui::style::Modifier::UNDERLINED); + } Ok(ud) }); - $methods.add_function_mut("blink", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::SLOW_BLINK; + $methods.add_function_mut("blink", |_, (ud, remove): (mlua::AnyUserData, bool)| { + let me = &mut ud.borrow_mut::()?.$($field).+; + if remove { + *me = me.remove_modifier(ratatui::style::Modifier::SLOW_BLINK); + } else { + *me = me.add_modifier(ratatui::style::Modifier::SLOW_BLINK); + } Ok(ud) }); - $methods.add_function_mut("blink_rapid", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::RAPID_BLINK; + $methods.add_function_mut("blink_rapid", |_, (ud, remove): (mlua::AnyUserData, bool)| { + let me = &mut ud.borrow_mut::()?.$($field).+; + if remove { + *me = me.remove_modifier(ratatui::style::Modifier::RAPID_BLINK); + } else { + *me = me.add_modifier(ratatui::style::Modifier::RAPID_BLINK); + } Ok(ud) }); - $methods.add_function_mut("reverse", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::REVERSED; + $methods.add_function_mut("reverse", |_, (ud, remove): (mlua::AnyUserData, bool)| { + let me = &mut ud.borrow_mut::()?.$($field).+; + if remove { + *me = me.remove_modifier(ratatui::style::Modifier::REVERSED); + } else { + *me = me.add_modifier(ratatui::style::Modifier::REVERSED); + } Ok(ud) }); - $methods.add_function_mut("hidden", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::HIDDEN; + $methods.add_function_mut("hidden", |_, (ud, remove): (mlua::AnyUserData, bool)| { + let me = &mut ud.borrow_mut::()?.$($field).+; + if remove { + *me = me.remove_modifier(ratatui::style::Modifier::HIDDEN); + } else { + *me = me.add_modifier(ratatui::style::Modifier::HIDDEN); + } Ok(ud) }); - $methods.add_function_mut("crossed", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::CROSSED_OUT; + $methods.add_function_mut("crossed", |_, (ud, remove): (mlua::AnyUserData, bool)| { + let me = &mut ud.borrow_mut::()?.$($field).+; + if remove { + *me = me.remove_modifier(ratatui::style::Modifier::CROSSED_OUT); + } else { + *me = me.add_modifier(ratatui::style::Modifier::CROSSED_OUT); + } Ok(ud) }); $methods.add_function_mut("reset", |_, ud: mlua::AnyUserData| { - ud.borrow_mut::()?.$($field).+.add_modifier = ratatui::style::Modifier::empty(); + ud.borrow_mut::()?.$($field).+ = ratatui::style::Style::reset(); Ok(ud) }); }; diff --git a/yazi-binding/src/style.rs b/yazi-binding/src/style.rs index 3403619e..ddcaf181 100644 --- a/yazi-binding/src/style.rs +++ b/yazi-binding/src/style.rs @@ -1,4 +1,6 @@ -use mlua::{AnyUserData, ExternalError, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use mlua::{AnyUserData, ExternalError, IntoLua, Lua, LuaSerdeExt, MetaMethod, Table, UserData, UserDataMethods, Value}; + +use crate::SER_OPT; #[derive(Clone, Copy, Default)] pub struct Style(pub ratatui::style::Style); @@ -34,6 +36,9 @@ impl UserData for Style { fn add_methods>(methods: &mut M) { crate::impl_style_shorthands!(methods, 0); + methods + .add_method("raw", |lua, me, ()| lua.to_value_with(&yazi_config::Style::from(me.0), SER_OPT)); + methods.add_function_mut("patch", |_, (ud, value): (AnyUserData, Value)| { { let mut me = ud.borrow_mut::()?; diff --git a/yazi-binding/src/utils.rs b/yazi-binding/src/utils.rs index 52cada49..c48f3173 100644 --- a/yazi-binding/src/utils.rs +++ b/yazi-binding/src/utils.rs @@ -1,4 +1,7 @@ -use mlua::{IntoLua, Lua, Table, Value}; +use mlua::{IntoLua, Lua, SerializeOptions, Table, Value}; + +pub const SER_OPT: SerializeOptions = + SerializeOptions::new().serialize_none_to_null(false).serialize_unit_to_null(false); pub fn get_metatable(lua: &Lua, value: impl IntoLua) -> mlua::Result { let (_, mt): (Value, Table) = unsafe { diff --git a/yazi-config/preset/theme-dark.toml b/yazi-config/preset/theme-dark.toml index d4bb9230..56f46344 100644 --- a/yazi-config/preset/theme-dark.toml +++ b/yazi-config/preset/theme-dark.toml @@ -29,10 +29,6 @@ overall = {} [mgr] cwd = { fg = "cyan" } -# Hovered -hovered = { reversed = true } -preview_hovered = { underline = true } - # Find find_keyword = { fg = "yellow", bold = true, italic = true, underline = true } find_position = { fg = "magenta", bg = "reset", bold = true, italic = true } @@ -91,6 +87,17 @@ unset_alt = { fg = "red", bg = "gray" } # : }}} +# : Indicator of hovered file {{{ + +[indicator] +parent = { reversed = true } +current = { reversed = true } +preview = { underline = true } +padding = { open = "", close = "" } + +# : }}} + + # : Status bar {{{ [status] diff --git a/yazi-config/preset/theme-light.toml b/yazi-config/preset/theme-light.toml index 8bd2c90e..d6aef72b 100644 --- a/yazi-config/preset/theme-light.toml +++ b/yazi-config/preset/theme-light.toml @@ -29,10 +29,6 @@ overall = {} [mgr] cwd = { fg = "cyan" } -# Hovered -hovered = { reversed = true } -preview_hovered = { underline = true } - # Find find_keyword = { fg = "yellow", bold = true, italic = true, underline = true } find_position = { fg = "magenta", bg = "reset", bold = true, italic = true } @@ -91,6 +87,17 @@ unset_alt = { fg = "red", bg = "gray" } # : }}} +# : Indicator of hovered file {{{ + +[indicator] +parent = { reversed = true } +current = { reversed = true } +preview = { underline = true } +padding = { open = "", close = "" } + +# : }}} + + # : Status bar {{{ [status] diff --git a/yazi-config/src/color.rs b/yazi-config/src/color.rs deleted file mode 100644 index bf0b63ba..00000000 --- a/yazi-config/src/color.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::{ops::Deref, str::FromStr}; - -use serde::Deserialize; - -#[derive(Clone, Copy, Debug, Default)] -pub struct Color(ratatui::style::Color); - -impl Deref for Color { - type Target = ratatui::style::Color; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl From for ratatui::style::Color { - fn from(value: Color) -> Self { value.0 } -} - -impl<'de> Deserialize<'de> for Color { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - ratatui::style::Color::from_str(&String::deserialize(deserializer)?) - .map_err(serde::de::Error::custom) - .map(Self) - } -} diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index 11c4e37f..f08dd301 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -1,6 +1,6 @@ yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which vfs); -yazi_macro::mod_flat!(color icon layout pattern platform preset priority style yazi); +yazi_macro::mod_flat!(icon layout pattern platform preset priority style yazi); use std::io::{Read, Write}; diff --git a/yazi-config/src/style.rs b/yazi-config/src/style.rs index e2531bb0..d9d326fd 100644 --- a/yazi-config/src/style.rs +++ b/yazi-config/src/style.rs @@ -1,75 +1,86 @@ -use ratatui::style::Modifier; -use serde::Deserialize; +use ratatui::style::Color; +use serde::{Deserialize, Serialize}; -use crate::Color; - -#[derive(Clone, Copy, Debug, Default, Deserialize)] +#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)] pub struct Style { pub fg: Option, pub bg: Option, - #[serde(default)] - pub bold: bool, - #[serde(default)] - pub dim: bool, - #[serde(default)] - pub italic: bool, - #[serde(default)] - pub underline: bool, - #[serde(default)] - pub blink: bool, - #[serde(default)] - pub blink_rapid: bool, - #[serde(default)] - pub reversed: bool, - #[serde(default)] - pub hidden: bool, - #[serde(default)] - pub crossed: bool, + pub bold: Option, + pub dim: Option, + pub italic: Option, + pub underline: Option, + pub blink: Option, + pub blink_rapid: Option, + pub reversed: Option, + pub hidden: Option, + pub crossed: Option, } -impl From