gpuikit/lib.rs
1#![allow(missing_docs)]
2//! gpuikit
3//!
4//! A comprehensive UI component library for GPUI applications.
5//!
6//! # Quick Start
7//!
8//! ```no_run
9//! use gpui::Application;
10//! use gpuikit::init;
11//!
12//! fn main() {
13//! Application::with_platform(gpui_platform::current_platform(false))
14//! .with_assets(gpuikit::assets())
15//! .run(|cx| {
16//! init(cx);
17//! // ... your app code
18//! });
19//! }
20//! ```
21//!
22//! The platform comes from `gpui_platform`, which your app depends on
23//! alongside `gpui` — gpuikit does not re-export either. `false` asks for a
24//! real windowing platform rather than a headless one.
25//!
26//! # Feature Flags
27//!
28//! All features are off by default.
29//!
30//! - `editor` — the editor component, and the syntect-backed syntax
31//! highlighting markdown code fences use once an app calls
32//! `markdown::init_code_highlighting` (itself gated on this feature)
33//! - `stitch` — closes the syntax a partially streamed markdown document leaves
34//! open (`**bold`, `[label](htt`) before parsing, so streaming text does not
35//! flicker between literal markers and styled text. Pulls in
36//! [mdstitch](https://docs.rs/mdstitch), which **requires Rust 1.95**;
37//! [`markdown::preprocessing_available`] reports which build you got
38//! - `runtime_shaders` — compiles Metal shaders at runtime rather than at build
39//! time, so a macOS build needs no Xcode Metal toolchain
40//! - `schema` — adds the `schemars` dependency. Nothing here derives
41//! `JsonSchema` yet, so today this only affects your dependency graph
42//!
43//! # Minimum Rust version
44//!
45//! This crate declares `rust-version = "1.85"`, which is a statement about its
46//! own source — async closures, and edition 2024 — rather than a guarantee
47//! about a whole build. Edition 2024 selects cargo's v3 resolver, which unlike
48//! v2 does take that floor into account: when it picks a *new* version of a
49//! dependency it prefers one whose own `rust-version` fits. That is a
50//! preference, not a wall, and it says nothing about the versions `Cargo.lock`
51//! already names — several of those declare more (cosmic-text and smol_str
52//! 1.89, oo7 1.92 on Linux), so on a toolchain near 1.85 you will most likely
53//! meet one of theirs first. A recent stable is the practical answer.
54//!
55//! The `stitch` feature raises gpuikit's own floor to **1.95**. It is the only
56//! one that does.
57
58use gpui::App;
59use rust_embed::RustEmbed;
60
61// Core modules
62pub mod a11y;
63pub mod date;
64pub mod element_id;
65pub mod elements;
66pub mod error;
67pub mod fs;
68pub mod icons;
69pub mod input;
70pub mod keymap;
71pub mod layout;
72pub mod markdown;
73pub mod resource;
74pub mod selection;
75pub mod theme;
76pub mod traits;
77pub mod utils;
78
79// Feature-gated editor module
80#[cfg(feature = "editor")]
81pub mod editor;
82
83pub use icons::Icons as DefaultIcons;
84
85/// Tests for the release workflows' version guard — that the version either of
86/// them would publish is the one `CHANGELOG.md` names. `release.yml` computes
87/// the version; `release-deploy.yml` is the one that runs `cargo publish`, and
88/// it is reachable without `release.yml` having run at all, so both carry it.
89///
90/// No runtime code, and nothing outside a test build: the module exists
91/// because `cargo test --lib` is the only thing in this repository that can
92/// check a workflow before it runs for real. See its own docs.
93#[cfg(test)]
94mod release_version_guard;
95
96/// Tests for the rule that keeps a workflow's outside values out of its shell
97/// — no `${{ }}` inside a `run:` body, and free-form values judged in a step of
98/// their own before anything uses them.
99///
100/// No runtime code, and nothing outside a test build. Covers every workflow in
101/// `.github/workflows/`, which a test enforces by reading the directory. See
102/// its own docs.
103#[cfg(test)]
104mod release_input_validation;
105
106/// Tests for the build configuration that keeps `ld` from being OOM-killed
107/// while linking this crate's eight examples — the dev profile's debug level,
108/// Linux's `split-debuginfo`, and the `examples` feature every `[[example]]`
109/// requires.
110///
111/// No runtime code, and nothing outside a test build. One test reads
112/// `examples/` from disk, because a new undeclared file there is autodiscovered
113/// as a target and cannot carry `required-features`. See its own docs.
114#[cfg(test)]
115mod build_profile_guard;
116
117/// Tests for the rule that this crate creates no thread it cannot join — no
118/// `smol` / `async-io` dependency, no `smol::` or `async_io::` in the source,
119/// and the two delays (cursor blink, toast auto-dismiss) scheduled on gpui's
120/// `BackgroundExecutor::timer`.
121///
122/// No runtime code, and nothing outside a test build. The `async-io` thread's
123/// `main_loop` has no exit path, so it raced process teardown and aborted a
124/// fully green `cargo test --lib` (#190). See its own docs.
125#[cfg(test)]
126mod undying_thread_guard;
127
128/// Tests for the rule that a rustdoc example which is not checked does not
129/// exist — no `` ```ignore `` anywhere in `src/`, and `no_run` only with a
130/// reason on record. rustdoc never compiles an `ignore`d block, so the crate's
131/// own Quick Start went on naming `Application::new()` long after that
132/// function stopped existing.
133///
134/// No runtime code, and nothing outside a test build. Its docs carry the
135/// hidden prelude a new example should copy.
136#[cfg(test)]
137mod doctest_fence_guard;
138
139/// Tests for the rule that runtime code stays runnable on
140/// `wasm32-unknown-unknown` — no `std::time::Instant`/`SystemTime`,
141/// `std::fs`, or `std::thread::spawn` outside an explicit allowlist of
142/// native-only APIs and test-only code. These compile for wasm and then
143/// panic or error in the browser, which no local `cargo test` would catch.
144///
145/// No runtime code, and nothing outside a test build. See its own docs, and
146/// <https://github.com/iamnbutler/gpuikit-demo> for gpuikit running on wasm.
147#[cfg(test)]
148mod wasm_compat_guard;
149
150/// Embedded assets for gpuikit (icons, fonts, etc.)
151#[derive(RustEmbed)]
152#[folder = "assets"]
153pub struct Assets;
154
155/// Returns the gpuikit asset source, for `Application::with_assets`.
156///
157/// # Example
158/// ```no_run
159/// # use gpui::Application;
160/// Application::with_platform(gpui_platform::current_platform(false))
161/// .with_assets(gpuikit::assets())
162/// .run(|cx| {
163/// gpuikit::init(cx);
164/// // ...
165/// });
166/// ```
167pub fn assets() -> resource::ResourceSource<Assets> {
168 resource::ResourceSource::new()
169}
170
171/// Initialize gpuikit - sets up themes and global state.
172///
173/// This must be called as soon as possible after your `gpui::Application` is created.
174/// Make sure to also call `.with_assets(gpuikit::Assets)` on your Application.
175///
176/// # Panics
177/// Calling a gpuikit component before initialization will panic.
178pub fn init(cx: &mut App) {
179 theme::init(cx);
180 utils::element_manager::init(cx);
181 // Before `bind_input_keys`, and the order is load-bearing: both bind Tab,
182 // gpui prefers the later-registered binding at equal context depth, and
183 // that is what keeps Tab inside a focused text input rather than moving
184 // focus out of it. See `a11y`'s module docs, section 4.
185 a11y::bind_focus_keys(cx);
186 input::bind_input_keys(cx, None);
187 elements::dialog::bind_dialog_keys(cx);
188 // After `bind_focus_keys`, and after `bind_dialog_keys`. Binding
189 // precedence is by key-context depth with ties broken by registration
190 // order, and a binding with no context counts as the deepest — so these
191 // `Listbox`-scoped bindings can never outrank `a11y`'s context-less Tab
192 // (the popup answers Tab with an action listener instead) and always
193 // outrank `Dialog`'s Escape, which is what lets a select inside a dialog
194 // close its own popup. Registering last is belt and braces for the second
195 // half of that. See `elements::select`'s `# The keyboard`.
196 elements::select::bind_select_keys(cx);
197 // Same reasoning as the listbox above, one component along: the grid's
198 // arrows, Home/End, PageUp/PageDown and Enter are bound in the deeper
199 // `Calendar` context, so a calendar inside a dialog keeps them and the
200 // dialog still gets Escape. See `elements::calendar`'s `# The keyboard`.
201 elements::calendar::bind_calendar_keys(cx);
202 // **After `input::bind_input_keys`, and the order is load-bearing.** Both
203 // of these bind `up`, `down`, `enter` and `escape` in a
204 // `"<Component> > Input"` context predicate, which matches at the focused
205 // field's own node and so *ties* on depth with `bind_input_keys`' plain
206 // `Input` binding. gpui's `KeyBindingContextPredicate::Descendant`
207 // (`gpui/src/keymap/context.rs:181`, parsed from `>` at `:361`) is what
208 // makes that predicate legal, and `Keymap::bindings_for_input`
209 // (`gpui/src/keymap.rs:173`) sorts candidates
210 // `depth_b.cmp(depth_a).then(ix_b.cmp(ix_a))` — descending depth, then
211 // descending registration index. So the later registration wins the tie,
212 // and registered before `bind_input_keys` these two would compile, run,
213 // and do nothing: every arrow key would move the text cursor. See
214 // `elements::combobox`'s and `elements::command`'s `# The keyboard`.
215 elements::combobox::bind_combobox_keys(cx);
216 elements::command::bind_command_keys(cx);
217 elements::toast::init(cx);
218}