Skip to main content

revue/
lib.rs

1//! # Revue
2//!
3//! A Vue-style TUI framework for Rust with CSS styling, reactive state management,
4//! and a rich set of widgets for building beautiful terminal user interfaces.
5//!
6//! ## Features
7//!
8//! | Feature | Description |
9//! |---------|-------------|
10//! | **CSS Styling** | External CSS files with variables, selectors, transitions, and hot reload |
11//! | **Flexbox Layout** | Custom TUI-optimized layout engine for flexible layouts |
12//! | **Reactive State** | Vue-inspired Signal/Computed/Effect pattern |
13//! | **80+ Widgets** | Text, Button, Input, Table, Tree, Modal, Toast, Charts, and more |
14//! | **Markdown & Images** | Built-in markdown rendering with Kitty image protocol support |
15//! | **Developer Tools** | Hot reload, widget inspector, snapshot testing (Pilot) |
16//! | **Theming** | Built-in themes: Dracula, Nord, Monokai, Gruvbox, Catppuccin |
17//!
18//! ## Quick Start
19//!
20//! ```rust,ignore
21//! use revue::prelude::*;
22//!
23//! fn main() -> Result<()> {
24//!     let mut app = App::builder().build();
25//!     let counter = Counter::new();
26//!
27//!     app.run_with_handler(counter, |key, state| {
28//!         state.handle_key(&key.key)
29//!     })
30//! }
31//!
32//! struct Counter { value: i32 }
33//!
34//! impl Counter {
35//!     fn new() -> Self { Self { value: 0 } }
36//!
37//!     fn handle_key(&mut self, key: &Key) -> bool {
38//!         match key {
39//!             Key::Up => { self.value += 1; true }
40//!             Key::Down => { self.value -= 1; true }
41//!             _ => false,
42//!         }
43//!     }
44//! }
45//!
46//! impl View for Counter {
47//!     fn render(&self, ctx: &mut RenderContext) {
48//!         vstack()
49//!             .child(Text::new(format!("Count: {}", self.value)))
50//!             .child(Text::muted("[↑/↓] to change, [q] to quit"))
51//!             .render(ctx);
52//!     }
53//! }
54//! ```
55//!
56//! ## CSS Styling
57//!
58//! Revue supports CSS for styling widgets:
59//!
60//! ```css
61//! /* styles.css */
62//! :root {
63//!     --primary: #bd93f9;
64//!     --bg: #282a36;
65//! }
66//!
67//! .button {
68//!     background: var(--primary);
69//!     color: var(--bg);
70//!     transition: background 0.3s ease;
71//! }
72//!
73//! .button:hover {
74//!     background: #ff79c6;
75//! }
76//! ```
77//!
78//! ## Reactive State
79//!
80//! ```rust,ignore
81//! use revue::prelude::*;
82//!
83//! let count = signal(0);
84//! let doubled = computed(move || count.get() * 2);
85//!
86//! effect(move || {
87//!     println!("Count changed to: {}", count.get());
88//! });
89//!
90//! count.set(5); // Triggers effect, doubled is now 10
91//! ```
92//!
93//! ## Widget Gallery
94//!
95//! ### Layout
96//! - [`widget::vstack()`] / [`widget::hstack()`] - Vertical/horizontal stack layout
97//! - [`widget::Border`] - Bordered container with title support
98//! - [`widget::Tabs`] - Tab navigation
99//! - [`widget::ScrollView`] - Scrollable content area
100//! - [`widget::Layers`] - Overlapping widgets (for modals, toasts)
101//!
102//! ### Input
103//! - [`widget::Input`] - Single-line text input
104//! - [`widget::TextArea`] - Multi-line text editor
105//! - [`widget::Button`] - Clickable button with variants
106//! - [`widget::Checkbox`] - Toggle checkbox
107//! - [`widget::RadioGroup`] - Radio button group
108//! - [`widget::Select`] - Dropdown selection
109//!
110//! ### Display
111//! - [`widget::Text`] - Styled text display
112//! - [`widget::Progress`] - Progress bar
113//! - [`widget::Spinner`] - Loading spinner
114//! - [`widget::Badge`] / [`widget::Tag`] - Labels and tags
115//! - [`widget::Avatar`] - User avatar display
116//! - [`widget::Skeleton`] - Loading placeholder
117//!
118//! ### Data
119//! - [`widget::Table`] - Data table with columns
120//! - [`widget::List`] - Selectable list
121//! - [`widget::Tree`] - Hierarchical tree view
122//! - [`widget::Sparkline`] - Inline mini chart
123//! - [`widget::BarChart`] - Bar chart visualization
124//! - [`widget::Canvas`] / [`widget::BrailleCanvas`] - Custom drawing
125//!
126//! ### Feedback
127//! - [`widget::Modal`] - Dialog overlay
128//! - [`widget::Toast`] - Notification popup
129//! - [`widget::CommandPalette`] - Fuzzy command search (Ctrl+P)
130//!
131//! ## Module Overview
132//!
133//! | Module | Description |
134//! |--------|-------------|
135//! | [`core::app`] | Application lifecycle and event loop |
136//! | [`dom`] | Virtual DOM and rendering tree |
137//! | [`event`] | Keyboard/mouse events and keymaps |
138//! | [`layout`] | Flexbox layout engine |
139//! | [`reactive`] | Signal/Computed/Effect primitives |
140//! | [`render`] | Terminal rendering and buffer |
141//! | [`style`] | CSS parsing and theming |
142//! | [`testing`] | Pilot testing framework |
143//! | [`widget`] | All widget implementations |
144//! | [`worker`] | Background task execution |
145//!
146//! ## Testing with Pilot
147//!
148//! ```rust,ignore
149//! use revue::testing::{Pilot, TestApp};
150//!
151//! #[test]
152//! fn test_counter() {
153//!     let mut app = TestApp::new(Counter::new());
154//!     let mut pilot = Pilot::new(&mut app);
155//!
156//!     pilot
157//!         .press(Key::Up)
158//!         .press(Key::Up)
159//!         .assert_contains("2");
160//! }
161//! ```
162//!
163//! ## Themes
164//!
165//! Built-in themes available via [`style::themes`]:
166//!
167//! - **Dracula** - Dark purple theme
168//! - **Nord** - Arctic blue theme
169//! - **Monokai** - Classic dark theme
170//! - **Gruvbox** - Retro groove theme
171//! - **Catppuccin** - Pastel dark theme
172//!
173//! ## Comparison with Other Frameworks
174//!
175//! | Feature | Revue | Ratatui | Textual | Cursive |
176//! |---------|-------|---------|---------|---------|
177//! | Language | Rust | Rust | Python | Rust |
178//! | CSS Styling | ✅ | ❌ | ✅ | ❌ |
179//! | Reactive State | ✅ | ❌ | ✅ | ❌ |
180//! | Hot Reload | ✅ | ❌ | ✅ | ❌ |
181//! | Widget Count | 80+ | 13 | 35+ | 40+ |
182//! | Snapshot Testing | ✅ | ❌ | ❌ | ❌ |
183
184#![warn(missing_docs)]
185
186/// The version of the library, including the git commit hash in development builds.
187///
188/// In production releases (from crates.io), this will be a semver like "2.33.4".
189///
190/// In development builds, this will be in the format "2.33.4-SHA" where SHA is the
191/// short git commit hash, allowing precise identification of the exact code version.
192pub const VERSION: &str = env!("REVUE_VERSION");
193
194/// The full git commit hash of this build.
195///
196/// Empty in release builds, contains the 40-character commit SHA in development builds.
197pub const GIT_SHA: &str = env!("GIT_SHA");
198
199/// Whether this is a development build (with commit hash in version) or a release build.
200///
201/// Returns `true` for development builds (version includes SHA), `false` for releases.
202pub fn is_dev_build() -> bool {
203    env!("REVUE_IS_DEV") == "true"
204}
205
206// Internal logging macros - no-op when tracing feature is disabled
207#[cfg(feature = "tracing")]
208macro_rules! log_debug {
209    ($($arg:tt)*) => { tracing::debug!($($arg)*) }
210}
211#[cfg(not(feature = "tracing"))]
212macro_rules! log_debug {
213    ($($arg:tt)*) => { { let _ = ($($arg)*,); } }
214}
215pub(crate) use log_debug;
216
217#[cfg(feature = "tracing")]
218macro_rules! log_warn {
219    ($($arg:tt)*) => { tracing::warn!($($arg)*) }
220}
221#[cfg(not(feature = "tracing"))]
222macro_rules! log_warn {
223    ($($arg:tt)*) => { { let _ = ($($arg)*,); } }
224}
225pub(crate) use log_warn;
226
227#[cfg(feature = "tracing")]
228macro_rules! log_error {
229    ($($arg:tt)*) => { tracing::error!($($arg)*) }
230}
231#[cfg(not(feature = "tracing"))]
232macro_rules! log_error {
233    ($($arg:tt)*) => { { let _ = ($($arg)*,); } }
234}
235pub(crate) use log_error;
236
237// Core modules
238pub mod core;
239pub use core::constants; // Re-export from core
240
241// Runtime systems
242pub mod runtime;
243pub use runtime::{dom, event, layout, render, style};
244
245// State management
246pub mod state;
247pub use state::{patterns, plugin, reactive, tasks, worker};
248
249// Other modules (keep at root for now)
250pub mod a11y;
251pub mod devtools;
252pub mod query;
253pub mod testing;
254pub mod text;
255pub mod utils;
256pub mod widget;
257
258// Re-export derive macros
259pub use revue_macros::Store;
260
261/// Error type for Revue operations.
262///
263/// This enum covers all error cases that can occur when using Revue,
264/// including CSS parsing errors, I/O errors, and general runtime errors.
265///
266/// # Example
267///
268/// ```rust,ignore
269/// use revue::{Error, Result};
270///
271/// fn load_styles() -> Result<()> {
272///     // Operations that might fail...
273///     Ok(())
274/// }
275/// ```
276#[derive(Debug, thiserror::Error)]
277pub enum Error {
278    /// CSS parsing error.
279    ///
280    /// Occurs when parsing invalid CSS syntax or unsupported properties.
281    #[error("CSS error: {0}")]
282    Css(#[from] style::ParseError),
283
284    /// I/O error.
285    ///
286    /// Occurs during file operations (loading CSS, images, etc.).
287    #[error("I/O error: {0}")]
288    Io(#[from] std::io::Error),
289
290    /// Layout error.
291    ///
292    /// Occurs during layout computation.
293    #[error("Layout error: {0}")]
294    Layout(#[from] layout::LayoutError),
295
296    /// Rendering error.
297    ///
298    /// Occurs during buffer operations or rendering.
299    #[error("Render error: {0}")]
300    Render(String),
301
302    /// Generic error with custom message.
303    ///
304    /// Used for errors that don't fit other categories.
305    /// This variant preserves the underlying error source for better debugging.
306    #[error("Unexpected error: {0}")]
307    Other(#[from] anyhow::Error),
308}
309
310/// Result type alias for Revue operations.
311///
312/// Shorthand for `std::result::Result<T, revue::Error>`.
313///
314/// # Example
315///
316/// ```rust,ignore
317/// use revue::Result;
318///
319/// fn load_config() -> Result<String> {
320///     let content = std::fs::read_to_string("config.css")?;
321///     Ok(content)
322/// }
323/// ```
324pub type Result<T> = std::result::Result<T, Error>;
325
326// ─────────────────────────────────────────────────────────────────────────
327// Error Handling Guidelines
328// ─────────────────────────────────────────────────────────────────────────
329
330/// # Error Handling Guidelines
331///
332/// Revue follows these error handling patterns to provide clear, actionable error messages.
333///
334/// ## Public APIs
335///
336/// **Always return `Result<T>`** for public APIs that can fail:
337///
338/// ```rust,ignore
339/// use revue::Result;
340///
341/// pub fn parse_css(input: &str) -> Result<Stylesheet> {
342///     // Parse and return Ok(stylesheet) or Err(Error)
343/// }
344/// ```
345///
346/// ## Internal Code
347///
348/// Choose the appropriate error handling strategy:
349///
350/// | Situation | Use | Example |
351/// |-----------|-----|---------|
352/// | Recoverable error | `Result<T, E>` | File not found, invalid input |
353/// | Optional value | `Option<T>` | Missing config, lookup by ID |
354/// | Truly invariant | `expect(msg)` | Logic errors, "never happens" |
355/// | Never fails | `unwrap()` // With comment explaining why | Static constants, known-good values |
356///
357/// ## Error Type Hierarchy
358///
359/// Revue uses `thiserror` for error definitions:
360///
361/// ```rust,ignore
362/// #[derive(Debug, thiserror::Error)]
363/// pub enum Error {
364///     #[error("CSS error: {0}")]
365///     Css(#[from] style::ParseError),
366///
367///     #[error("I/O error: {0}")]
368///     Io(#[from] std::io::Error),
369///
370///     #[error("Layout error: {0}")]
371///     Layout(#[from] layout::LayoutError),
372///
373///     #[error("Render error: {0}")]
374///     Render(String),
375///
376///     #[error("Unexpected error: {0}")]
377///     Other(#[from] anyhow::Error),
378/// }
379/// ```
380///
381/// ## Error Context
382///
383/// **Good: Use anyhow for context**
384///
385/// ```rust,ignore
386/// use anyhow::Context;
387///
388/// let file = std::fs::read_to_string(path)
389///     .context("Failed to read config")?;
390/// ```
391///
392/// **Bad: Lose context**
393///
394/// ```rust,ignore
395/// let file = std::fs::read_to_string(path)?;  // Error doesn't mention config
396/// ```
397///
398/// ## Converting Errors
399///
400/// Use `?` operator for automatic conversion:
401///
402/// ```rust,ignore
403/// use revue::Result;
404///
405/// fn load_stylesheet(path: &str) -> Result<Stylesheet> {
406///     let content = std::fs::read_to_string(path)?;  // io::Error → Error::Io
407///     Ok(parse_css(&content)?)
408/// }
409/// ```
410///
411/// Prelude module for convenient imports.
412///
413/// Import everything you need with a single line:
414///
415/// ```rust,ignore
416/// use revue::prelude::*;
417/// ```
418///
419/// # Included Items
420///
421/// ## Core Types
422/// - [`core::app::App`] - Application builder and runner
423/// - [`widget::View`], [`widget::RenderContext`] - Widget rendering trait
424/// - [`Result`] - Error handling
425///
426/// ## Events
427/// - [`event::Key`], [`event::KeyEvent`], [`event::Event`] - Input handling
428///
429/// ## Reactive
430/// - [`reactive::signal`], [`reactive::computed`], [`reactive::effect`] - State primitives
431/// - [`reactive::Signal`], [`reactive::Computed`] - Reactive types
432///
433/// ## Layout
434/// - [`widget::vstack`], [`widget::hstack`] - Stack layouts
435/// - [`layout::Rect`] - Rectangle geometry
436///
437/// ## Widgets
438/// All 85+ widgets and their constructors are included.
439/// See [`widget`] module documentation for the full list.
440///
441/// ## Testing
442/// - [`testing::Pilot`], [`testing::TestApp`], [`testing::TestConfig`] - Testing utilities
443///
444/// ## Workers
445/// - [`worker::WorkerPool`], [`worker::WorkerHandle`] - Background tasks
446pub mod prelude {
447    // Macros
448    pub use crate::Store;
449
450    // App
451    pub use crate::core::app::App;
452
453    // Events
454    pub use crate::event::{Event, Key, KeyEvent, MouseButton, MouseEvent, MouseEventKind};
455
456    // Layout
457    pub use crate::layout::Rect;
458
459    // Reactive primitives
460    pub use crate::reactive::{computed, effect, signal, Computed, Signal};
461
462    // Store support
463    pub use crate::reactive::{create_store, use_store, Store, StoreExt, StoreRegistry};
464
465    // Async support
466    pub use crate::reactive::{
467        use_async, use_async_immediate, use_async_poll, AsyncResult, AsyncState,
468    };
469
470    // Style
471    pub use crate::style::Color;
472
473    // Theme system
474    pub use crate::style::{
475        cycle_theme, register_theme, set_theme, set_theme_by_id, theme_ids, toggle_theme,
476        use_theme, Theme, ThemeVariant, Themes,
477    };
478
479    // Animation system
480    pub use crate::style::{
481        easing,
482        effective_duration,
483        // Reduced motion support
484        should_skip_animation,
485        // Widget animation presets
486        widget_animations,
487        Animation,
488        AnimationDirection,
489        AnimationFillMode,
490        AnimationGroup,
491        // Core animation types
492        AnimationState,
493        Animations,
494        Choreographer,
495        CssKeyframe,
496        GroupMode,
497        // CSS @keyframes style
498        KeyframeAnimation,
499        // CSS @keyframes parser types
500        KeyframeBlock,
501        KeyframesDefinition,
502        // Choreography
503        Stagger,
504        Tween,
505    };
506
507    // Widgets - Types
508    pub use crate::widget::{
509        Alignment,
510        Anchor,
511        Avatar,
512        AvatarShape,
513        AvatarSize,
514        Badge,
515        BadgeShape,
516        BadgeVariant,
517        BarChart,
518        BarOrientation,
519        BigText,
520        Border,
521        BorderType,
522        // Braille canvas
523        BrailleCanvas,
524        BrailleContext,
525        BrailleGrid,
526        // New widgets
527        Button,
528        ButtonVariant,
529        Canvas,
530        Checkbox,
531        CheckboxStyle,
532        Circle,
533        Column,
534        Command,
535        // Command palette
536        CommandPalette,
537        DigitStyle,
538        Digits,
539        Direction,
540        // Convenience widgets
541        Divider,
542        DividerStyle,
543        DrawContext,
544        EmptyState,
545        EmptyStateType,
546        EmptyStateVariant,
547        EventResult,
548        FilledCircle,
549        FilledRectangle,
550        FocusStyle,
551        Gauge,
552        GaugeStyle,
553        Input,
554        Interactive,
555        LabelPosition,
556        // Layer system
557        Layers,
558        Line,
559        List,
560        LogEntry,
561        LogFormat,
562        LogLevel,
563        Modal,
564        ModalButton,
565        ModalButtonStyle,
566        Orientation,
567        Pagination,
568        PaginationStyle,
569        Points,
570        Positioned,
571        Progress,
572        ProgressStyle,
573        RadioGroup,
574        RadioLayout,
575        RadioStyle,
576        Rectangle,
577        RenderContext,
578        RichLog,
579        RichText,
580        ScrollView,
581        Select,
582        Shape,
583        Skeleton,
584        SkeletonShape,
585        Span,
586        Sparkline,
587        SparklineStyle,
588        Spinner,
589        SpinnerStyle,
590        Stack,
591        Status,
592        StatusIndicator,
593        StatusSize,
594        StatusStyle,
595        Style,
596        Tab,
597        Table,
598        Tabs,
599        Tag,
600        TagStyle,
601        Text,
602        TextArea,
603        ThemePicker,
604        Timeout,
605        // UX widgets
606        Toast,
607        ToastLevel,
608        ToastPosition,
609        Tree,
610        TreeNode,
611        View,
612        WidgetState,
613    };
614
615    // Feature-gated widget types
616    #[cfg(feature = "image")]
617    pub use crate::widget::{Image, ScaleMode};
618    #[cfg(feature = "markdown")]
619    pub use crate::widget::{Markdown, MarkdownPresentation, ViewMode};
620
621    // Widgets - Constructors
622    pub use crate::widget::{
623        avatar,
624        avatar_icon,
625        away_indicator,
626        badge,
627        barchart,
628        battery,
629        bigtext,
630        border,
631        braille_canvas,
632        busy_indicator,
633        // New constructors
634        button,
635        canvas,
636        checkbox,
637        chip,
638        clock,
639        column,
640        // Command palette
641        command_palette,
642        digits,
643        // Convenience widget constructors
644        divider,
645        dot_badge,
646        empty_error,
647        empty_state,
648        first_use,
649        gauge,
650        h1,
651        h2,
652        h3,
653        hstack,
654        input,
655        // Layer system constructors
656        layers,
657        list,
658        log_entry,
659        markup,
660        modal,
661        no_results,
662        offline,
663        online,
664        pagination,
665        percentage,
666        positioned,
667        progress,
668        radio_group,
669        rich_text,
670        richlog,
671        scroll_view,
672        select,
673        skeleton,
674        skeleton_avatar,
675        skeleton_paragraph,
676        skeleton_text,
677        span,
678        sparkline,
679        spinner,
680        status_indicator,
681        style,
682        table,
683        tabs,
684        tag,
685        text,
686        textarea,
687        theme_picker,
688        timer_widget as timer,
689        // UX constructors
690        toast,
691        tree,
692        tree_node,
693        vdivider,
694        vstack,
695    };
696
697    // Feature-gated widget constructors
698    #[cfg(feature = "image")]
699    pub use crate::widget::image_from_file;
700    #[cfg(feature = "markdown")]
701    pub use crate::widget::{markdown, markdown_presentation};
702
703    // DOM system
704    pub use crate::dom::{DomId, DomNode, DomRenderer, DomTree, NodeState, Query, WidgetMeta};
705
706    // Render system
707    pub use crate::runtime::render::{Buffer, Modifier};
708
709    // Worker system
710    pub use crate::worker::{
711        run_blocking, spawn as spawn_worker, WorkerChannel, WorkerHandle, WorkerMessage,
712        WorkerPool, WorkerState,
713    };
714
715    // Tasks - Timer, TaskRunner, EventBus
716    pub use crate::tasks::{
717        EventBus, EventId, Subscription, TaskId, TaskResult, TaskRunner, Timer, TimerEntry, TimerId,
718    };
719
720    // Patterns - Common TUI patterns
721    pub use crate::patterns::{
722        build_color,
723        priority_color,
724        spinner_char,
725        status_color,
726        // Async operations
727        AsyncTask,
728        BreadcrumbItem,
729        ConfirmAction,
730        ConfirmState,
731        FieldType,
732        FormField,
733        // Form validation
734        FormState,
735        // State management
736        MessageState,
737        NavigationEvent,
738        // Navigation
739        NavigationState,
740        Route,
741        SearchMode,
742        // Search/filter
743        SearchState,
744        ValidationError,
745        Validators,
746        BG,
747        BG_INSET,
748        BG_SUBTLE,
749        BLUE,
750        BORDER,
751        BORDER_MUTED,
752        // Colors
753        CYAN,
754        ERROR,
755        FG,
756        FG_DIM,
757        FG_SUBTLE,
758        GREEN,
759        INFO,
760        ORANGE,
761        PURPLE,
762        RED,
763        SPINNER_FRAMES,
764        SUCCESS,
765        WARNING,
766        YELLOW,
767    };
768
769    // Config loading (requires config feature)
770    #[cfg(feature = "config")]
771    pub use crate::patterns::{AppConfig, ConfigError};
772
773    // Accessibility
774    pub use crate::utils::{
775        // Announcement functions
776        announce,
777        // Widget-specific announcement helpers
778        announce_button_clicked,
779        announce_checkbox_changed,
780        announce_dialog_closed,
781        announce_dialog_opened,
782        announce_error,
783        announce_list_selection,
784        announce_now,
785        announce_success,
786        announce_tab_changed,
787        has_announcements,
788        is_high_contrast,
789        // Preference getters/setters
790        prefers_reduced_motion,
791        set_high_contrast,
792        set_reduced_motion,
793        take_announcements,
794    };
795
796    // Figlet fonts (for BigText widget)
797    pub use crate::utils::figlet::FigletFont;
798
799    // Testing (Pilot)
800    pub use crate::testing::{Pilot, TestApp, TestConfig};
801    // Visual regression testing
802    pub use crate::testing::{
803        CapturedCell, CiEnvironment, CiProvider, TestReport, VisualCapture, VisualDiff, VisualTest,
804        VisualTestConfig, VisualTestResult,
805    };
806
807    // DevTools
808    #[allow(deprecated)] // Re-exporting deprecated functions for backwards compatibility
809    pub use crate::devtools::{
810        disable_devtools, enable_devtools, is_devtools_enabled, toggle_devtools, ComputedProperty,
811        DevTools, DevToolsConfig, DevToolsPosition, DevToolsTab, EventFilter, EventLogger,
812        EventType, Inspector, InspectorConfig, LoggedEvent, PropertySource, StateDebugger,
813        StateEntry, StateValue, StyleCategory, StyleInspector, WidgetNode,
814    };
815
816    // Profiler
817    pub use crate::utils::profiler::{
818        profile, profiler_report, start_profile, FlameNode, ProfileGuard, Profiler, Stats, Timing,
819    };
820
821    // Result type
822    pub use crate::Result;
823
824    // Constants
825    pub use crate::constants::{
826        // Animation durations
827        ANIMATION_DEFAULT_DURATION,
828        ANIMATION_FAST_DURATION,
829        ANIMATION_SLOW_DURATION,
830        ANIMATION_VERY_SLOW_DURATION,
831        // Debounce
832        DEBOUNCE_DEFAULT,
833        DEBOUNCE_FILE_SYSTEM,
834        DEBOUNCE_SEARCH,
835        FRAME_DURATION_30FPS,
836        // Frame rates
837        FRAME_DURATION_60FPS,
838        // Messages
839        MESSAGE_DEFAULT_DURATION,
840        MESSAGE_LONG_DURATION,
841        MESSAGE_QUICK_DURATION,
842        POLL_IMMEDIATE,
843        // Screen transitions
844        SCREEN_TRANSITION_DURATION,
845        // Stagger
846        STAGGER_DELAY_DEFAULT,
847        // Tick rates
848        TICK_RATE_DEFAULT,
849    };
850}
851
852// Tests extracted to tests/lib_tests.rs
853// All tests use only public constants, functions, and types from revue