1#![forbid(unsafe_code)]
2
3pub mod color;
25pub mod interactive;
27pub mod style;
29pub mod stylesheet;
31pub mod table_theme;
33pub mod theme;
35
36pub use color::{
37 Ansi16,
39 Color,
40 ColorCache,
41 ColorProfile,
42 MonoColor,
43 Rgb,
44 WCAG_AA_LARGE_TEXT,
46 WCAG_AA_NORMAL_TEXT,
47 WCAG_AAA_LARGE_TEXT,
48 WCAG_AAA_NORMAL_TEXT,
49 best_text_color,
51 best_text_color_packed,
52 contrast_ratio,
53 contrast_ratio_packed,
54 meets_wcag_aa,
55 meets_wcag_aa_large_text,
56 meets_wcag_aa_packed,
57 meets_wcag_aaa,
58 relative_luminance,
59 relative_luminance_packed,
60};
61pub use interactive::{InteractionState, InteractiveStyle};
62pub use style::{
63 LineClamp, Overflow, Style, StyleFlags, TextAlign, TextOverflow, TextTransform, WhiteSpaceMode,
64};
65pub use stylesheet::{StyleId, StyleSheet};
66pub use table_theme::{
67 BlendMode, Gradient, StyleMask, TableEffect, TableEffectResolver, TableEffectRule,
68 TableEffectScope, TableEffectTarget, TablePresetId, TableSection, TableTheme,
69 TableThemeDiagnostics, TableThemeSpec,
70};
71pub use theme::{AdaptiveColor, ResolvedTheme, Theme, ThemeBuilder};
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use ftui_render::cell::{CellAttrs, PackedRgba, StyleFlags as CellFlags};
77
78 #[test]
79 fn theme_builder_from_theme_preserves_base_fields() {
80 let base = Theme::builder()
81 .primary(Color::rgb(10, 20, 30))
82 .text(Color::rgb(40, 50, 60))
83 .build();
84
85 let updated = ThemeBuilder::from_theme(base.clone())
86 .text(Color::rgb(70, 80, 90))
87 .build();
88
89 assert_eq!(updated.primary, base.primary);
90 assert_eq!(updated.background, base.background);
91 assert_eq!(updated.text, AdaptiveColor::from(Color::rgb(70, 80, 90)));
92 }
93
94 #[test]
95 fn adaptive_color_resolves_by_mode() {
96 let adaptive = AdaptiveColor::adaptive(Color::rgb(1, 2, 3), Color::rgb(4, 5, 6));
97 assert_eq!(adaptive.resolve(false), Color::rgb(1, 2, 3));
98 assert_eq!(adaptive.resolve(true), Color::rgb(4, 5, 6));
99 }
100
101 #[test]
102 fn packed_rgba_round_trip_channels() {
103 let packed = PackedRgba::rgba(12, 34, 56, 78);
104 assert_eq!(packed.r(), 12);
105 assert_eq!(packed.g(), 34);
106 assert_eq!(packed.b(), 56);
107 assert_eq!(packed.a(), 78);
108
109 let rgb: Rgb = packed.into();
110 assert_eq!(rgb, Rgb::new(12, 34, 56));
111
112 let color: Color = packed.into();
113 assert_eq!(color.to_rgb(), Rgb::new(12, 34, 56));
114 }
115
116 #[test]
117 fn packed_rgba_rgb_defaults_to_opaque() {
118 let packed = PackedRgba::rgb(1, 2, 3);
119 assert_eq!(packed.a(), 255);
120 }
121
122 #[test]
123 fn color_profile_defaults_to_ansi16() {
124 let profile = ColorProfile::detect_from_env(None, None, None);
125 assert_eq!(profile, ColorProfile::Ansi16);
126 }
127
128 #[test]
129 fn style_flags_round_trip_to_cell_flags() {
130 let style_flags = StyleFlags::BOLD
131 .union(StyleFlags::ITALIC)
132 .union(StyleFlags::UNDERLINE)
133 .union(StyleFlags::BLINK);
134
135 let cell_flags: CellFlags = style_flags.into();
136 assert!(cell_flags.contains(CellFlags::BOLD));
137 assert!(cell_flags.contains(CellFlags::ITALIC));
138 assert!(cell_flags.contains(CellFlags::UNDERLINE));
139 assert!(cell_flags.contains(CellFlags::BLINK));
140
141 let round_trip = StyleFlags::from(cell_flags);
142 assert!(round_trip.contains(StyleFlags::BOLD));
143 assert!(round_trip.contains(StyleFlags::ITALIC));
144 assert!(round_trip.contains(StyleFlags::UNDERLINE));
145 assert!(round_trip.contains(StyleFlags::BLINK));
146 }
147
148 #[test]
149 fn extended_underlines_map_to_cell_underline() {
150 let style_flags = StyleFlags::DOUBLE_UNDERLINE.union(StyleFlags::CURLY_UNDERLINE);
151 let cell_flags: CellFlags = style_flags.into();
152 assert!(cell_flags.contains(CellFlags::UNDERLINE));
153 }
154
155 #[test]
156 fn cell_attrs_preserve_link_id_with_flags() {
157 let flags = CellFlags::BOLD | CellFlags::ITALIC | CellFlags::UNDERLINE | CellFlags::BLINK;
158 let attrs = CellAttrs::new(flags, 4242);
159 assert_eq!(attrs.link_id(), 4242);
160 assert!(attrs.has_flag(CellFlags::BOLD));
161 assert!(attrs.has_flag(CellFlags::ITALIC));
162 assert!(attrs.has_flag(CellFlags::UNDERLINE));
163 assert!(attrs.has_flag(CellFlags::BLINK));
164 }
165}