Skip to main content

fluidattacks_designs/
lib.rs

1//! The Fluid Attacks design system for GPUI desktop apps, over the tokens in
2//! [`tokens`]. Widgets are macOS-gated with their toolkit.
3//!
4//! They style against the semantic [`Theme`], read via [`ActiveTheme`] at
5//! render time, in either light or dark. An app picks its mode at
6//! [`Theme::init`] and may switch it whenever it likes: the colors resolve when
7//! painting, so nothing below has to be told.
8//!
9//! Preview the catalog with `cargo run --example gallery`, whose header carries
10//! the switch.
11
12pub use fluidattacks_designs_domain as tokens;
13
14/// The toolkit, re-exported so consuming apps build against the exact gpui
15/// version the widgets were compiled with.
16#[cfg(target_os = "macos")]
17pub use gpui;
18
19#[cfg(target_os = "macos")]
20mod alert;
21#[cfg(target_os = "macos")]
22mod assets;
23#[cfg(target_os = "macos")]
24mod avatar;
25#[cfg(target_os = "macos")]
26mod button;
27#[cfg(target_os = "macos")]
28mod checkbox;
29#[cfg(target_os = "macos")]
30mod cloud_image;
31#[cfg(target_os = "macos")]
32mod container;
33#[cfg(target_os = "macos")]
34mod empty_state;
35#[cfg(target_os = "macos")]
36pub mod file_picker;
37#[cfg(target_os = "macos")]
38mod icon;
39#[cfg(target_os = "macos")]
40mod image_view;
41#[cfg(target_os = "macos")]
42mod link;
43#[cfg(target_os = "macos")]
44mod list;
45#[cfg(target_os = "macos")]
46mod loading;
47#[cfg(target_os = "macos")]
48mod modal;
49#[cfg(target_os = "macos")]
50mod notification_sign;
51#[cfg(target_os = "macos")]
52mod scrollbar;
53#[cfg(target_os = "macos")]
54mod select;
55#[cfg(target_os = "macos")]
56mod table;
57#[cfg(target_os = "macos")]
58mod tabs;
59#[cfg(target_os = "macos")]
60mod tag;
61#[cfg(target_os = "macos")]
62mod text;
63#[cfg(target_os = "macos")]
64mod text_area;
65#[cfg(target_os = "macos")]
66mod text_input;
67#[cfg(target_os = "macos")]
68mod theme;
69#[cfg(target_os = "macos")]
70mod tooltip;
71#[cfg(target_os = "macos")]
72mod widget;
73
74#[cfg(target_os = "macos")]
75pub use alert::{Alert, AlertDialog};
76#[cfg(target_os = "macos")]
77pub use assets::{Assets, EmbeddedAsset};
78#[cfg(target_os = "macos")]
79pub use avatar::Avatar;
80#[cfg(target_os = "macos")]
81pub use button::Button;
82#[cfg(target_os = "macos")]
83pub use checkbox::Checkbox;
84#[cfg(target_os = "macos")]
85pub use cloud_image::CloudImage;
86#[cfg(target_os = "macos")]
87pub use container::Container;
88#[cfg(target_os = "macos")]
89pub use empty_state::EmptyState;
90#[cfg(target_os = "macos")]
91pub use icon::Icon;
92#[cfg(target_os = "macos")]
93pub use image_view::{ImageView, ImageViewer};
94#[cfg(target_os = "macos")]
95pub use link::{IconPosition, Link};
96#[cfg(target_os = "macos")]
97pub use list::{List, ListItem, ListState};
98#[cfg(target_os = "macos")]
99pub use loading::Loading;
100#[cfg(target_os = "macos")]
101pub use modal::{ConfirmDialog, Modal};
102#[cfg(target_os = "macos")]
103pub use notification_sign::NotificationSign;
104#[cfg(target_os = "macos")]
105pub use scrollbar::{stop_dominant_axis, ScrollAxis, Scrollbar};
106#[cfg(target_os = "macos")]
107pub use select::{Select, SelectOption, SelectState};
108#[cfg(target_os = "macos")]
109pub use table::{Column, Table, TableState};
110#[cfg(target_os = "macos")]
111pub use tabs::{TabItem, Tabs};
112#[cfg(target_os = "macos")]
113pub use tag::Tag;
114#[cfg(target_os = "macos")]
115pub use text::{Heading, Span, Text};
116#[cfg(target_os = "macos")]
117pub use text_area::{TextArea, TextAreaState};
118#[cfg(target_os = "macos")]
119pub use text_input::{TextInput, TextInputState};
120#[cfg(target_os = "macos")]
121pub use theme::{ActiveTheme, Theme};
122#[cfg(target_os = "macos")]
123pub use tooltip::Tooltip;
124
125#[cfg(test)]
126mod tests {
127    const GATE: &str = "#[cfg(target_os = \"macos\")]";
128
129    /// A module declaration, `pub` or not, with its trailing semicolon gone.
130    fn declared(line: &str) -> Option<&str> {
131        line.strip_prefix("pub mod ")
132            .or_else(|| line.strip_prefix("mod "))?
133            .strip_suffix(';')
134    }
135
136    /// Whether this line declares or re-exports one of our widget modules, and
137    /// so has to sit under the gate.
138    fn needs_gate(line: &str, modules: &[&str]) -> bool {
139        let declares = declared(line).is_some();
140        // a re-export of one of our own modules, not of a dependency
141        let re_exports = line
142            .strip_prefix("pub use ")
143            .and_then(|rest| rest.split("::").next())
144            .is_some_and(|head| modules.contains(&head));
145        declares || re_exports
146    }
147
148    #[test]
149    fn every_widget_module_is_gated_with_its_toolkit() {
150        let source = include_str!("lib.rs");
151        let modules: Vec<&str> = source.lines().filter_map(declared).collect();
152        assert!(
153            !modules.is_empty(),
154            "the scan found no modules, so it is not reading this file"
155        );
156        let mut ungated: Vec<&str> = Vec::new();
157        let mut previous = "";
158        for line in source.lines() {
159            if needs_gate(line, &modules) && previous != GATE {
160                ungated.push(line);
161            }
162            if !line.trim().is_empty() {
163                previous = line;
164            }
165        }
166        assert!(ungated.is_empty(), "these need the macOS gate: {ungated:?}");
167    }
168}