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//! | [`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
237pub mod a11y;
238pub mod app;
239pub mod constants;
240pub mod devtools;
241pub mod dom;
242pub mod event;
243pub mod layout;
244pub mod patterns;
245pub mod plugin;
246pub mod query;
247pub mod reactive;
248pub mod render;
249pub mod style;
250pub mod tasks;
251pub mod testing;
252pub mod text;
253pub mod utils;
254pub mod widget;
255pub mod worker;
256
257// Re-export derive macros
258pub use revue_macros::Store;
259
260/// Error type for Revue operations.
261///
262/// This enum covers all error cases that can occur when using Revue,
263/// including CSS parsing errors, I/O errors, and general runtime errors.
264///
265/// # Example
266///
267/// ```rust,ignore
268/// use revue::{Error, Result};
269///
270/// fn load_styles() -> Result<()> {
271///     // Operations that might fail...
272///     Ok(())
273/// }
274/// ```
275#[derive(Debug, thiserror::Error)]
276pub enum Error {
277    /// CSS parsing error.
278    ///
279    /// Occurs when parsing invalid CSS syntax or unsupported properties.
280    #[error("CSS error: {0}")]
281    Css(#[from] style::ParseError),
282
283    /// I/O error.
284    ///
285    /// Occurs during file operations (loading CSS, images, etc.).
286    #[error("I/O error: {0}")]
287    Io(#[from] std::io::Error),
288
289    /// Generic error with custom message.
290    ///
291    /// Used for errors that don't fit other categories.
292    #[error("{0}")]
293    Other(String),
294}
295
296/// Result type alias for Revue operations.
297///
298/// Shorthand for `std::result::Result<T, revue::Error>`.
299///
300/// # Example
301///
302/// ```rust,ignore
303/// use revue::Result;
304///
305/// fn load_config() -> Result<String> {
306///     let content = std::fs::read_to_string("config.css")?;
307///     Ok(content)
308/// }
309/// ```
310pub type Result<T> = std::result::Result<T, Error>;
311
312// ─────────────────────────────────────────────────────────────────────────
313// Error Handling Guidelines
314// ─────────────────────────────────────────────────────────────────────────
315
316/// # Error Handling Guidelines
317///
318/// Revue follows these error handling patterns to provide clear, actionable error messages.
319///
320/// ## Public APIs
321///
322/// **Always return `Result<T>`** for public APIs that can fail:
323///
324/// ```rust,ignore
325/// use revue::Result;
326///
327/// pub fn parse_css(input: &str) -> Result<Stylesheet> {
328///     // Parse and return Ok(stylesheet) or Err(Error)
329/// }
330/// ```
331///
332/// ## Internal Code
333///
334/// Choose the appropriate error handling strategy:
335///
336/// | Situation | Use | Example |
337/// |-----------|-----|---------|
338/// | Recoverable error | `Result<T, E>` | File not found, invalid input |
339/// | Optional value | `Option<T>` | Missing config, lookup by ID |
340/// | Truly invariant | `expect(msg)` | Logic errors, "never happens" |
341/// | Never fails | `unwrap()` // With comment explaining why | Static constants, known-good values |
342///
343/// ## Error Type Hierarchy
344///
345/// Revue uses `thiserror` for error definitions:
346///
347/// ```rust,ignore
348/// #[derive(Debug, thiserror::Error)]
349/// pub enum Error {
350///     #[error("CSS error: {0}")]
351///     Css(#[from] style::ParseError),
352///
353///     #[error("I/O error: {0}")]
354///     Io(#[from] std::io::Error),
355///
356///     #[error("{0}")]
357///     Other(String),
358/// }
359/// ```
360///
361/// ## Error Context
362///
363/// **Good: Add context for clarity**
364///
365/// ```rust,ignore
366/// let file = std::fs::read_to_string(path)
367///     .map_err(|e| Error::Other(format!("Failed to read config: {}", e)))?;
368/// ```
369///
370/// **Bad: Lose context**
371///
372/// ```rust,ignore
373/// let file = std::fs::read_to_string(path)?;  // Error doesn't mention config
374/// ```
375///
376/// ## Converting Errors
377///
378/// Use `?` operator for automatic conversion:
379///
380/// ```rust,ignore
381/// use revue::Result;
382///
383/// fn load_stylesheet(path: &str) -> Result<Stylesheet> {
384///     let content = std::fs::read_to_string(path)?;  // io::Error → Error::Io
385///     Ok(parse_css(&content)?)
386/// }
387/// ```
388///
389/// Prelude module for convenient imports.
390///
391/// Import everything you need with a single line:
392///
393/// ```rust,ignore
394/// use revue::prelude::*;
395/// ```
396///
397/// # Included Items
398///
399/// ## Core Types
400/// - [`app::App`] - Application builder and runner
401/// - [`widget::View`], [`widget::RenderContext`] - Widget rendering trait
402/// - [`Result`] - Error handling
403///
404/// ## Events
405/// - [`event::Key`], [`event::KeyEvent`], [`event::Event`] - Input handling
406///
407/// ## Reactive
408/// - [`reactive::signal`], [`reactive::computed`], [`reactive::effect`] - State primitives
409/// - [`reactive::Signal`], [`reactive::Computed`] - Reactive types
410///
411/// ## Layout
412/// - [`widget::vstack`], [`widget::hstack`] - Stack layouts
413/// - [`layout::Rect`] - Rectangle geometry
414///
415/// ## Widgets
416/// All 85+ widgets and their constructors are included.
417/// See [`widget`] module documentation for the full list.
418///
419/// ## Testing
420/// - [`testing::Pilot`], [`testing::TestApp`], [`testing::TestConfig`] - Testing utilities
421///
422/// ## Workers
423/// - [`worker::WorkerPool`], [`worker::WorkerHandle`] - Background tasks
424pub mod prelude {
425    // Macros
426    pub use crate::Store;
427
428    // App
429    pub use crate::app::App;
430
431    // Events
432    pub use crate::event::{Event, Key, KeyEvent, MouseButton, MouseEvent, MouseEventKind};
433
434    // Layout
435    pub use crate::layout::Rect;
436
437    // Reactive primitives
438    pub use crate::reactive::{computed, effect, signal, Computed, Signal};
439
440    // Store support
441    pub use crate::reactive::{create_store, use_store, Store, StoreExt, StoreRegistry};
442
443    // Async support
444    pub use crate::reactive::{
445        use_async, use_async_immediate, use_async_poll, AsyncResult, AsyncState,
446    };
447
448    // Style
449    pub use crate::style::Color;
450
451    // Theme system
452    pub use crate::style::{
453        cycle_theme, register_theme, set_theme, set_theme_by_id, theme_ids, toggle_theme,
454        use_theme, Theme, ThemeVariant, Themes,
455    };
456
457    // Animation system
458    pub use crate::style::{
459        easing,
460        effective_duration,
461        // Reduced motion support
462        should_skip_animation,
463        // Widget animation presets
464        widget_animations,
465        Animation,
466        AnimationDirection,
467        AnimationFillMode,
468        AnimationGroup,
469        // Core animation types
470        AnimationState,
471        Animations,
472        Choreographer,
473        CssKeyframe,
474        GroupMode,
475        // CSS @keyframes style
476        KeyframeAnimation,
477        // Choreography
478        Stagger,
479        Tween,
480    };
481
482    // Widgets - Types
483    pub use crate::widget::{
484        Alignment,
485        Anchor,
486        Avatar,
487        AvatarShape,
488        AvatarSize,
489        Badge,
490        BadgeShape,
491        BadgeVariant,
492        BarChart,
493        BarOrientation,
494        Border,
495        BorderType,
496        // Braille canvas
497        BrailleCanvas,
498        BrailleContext,
499        BrailleGrid,
500        // New widgets
501        Button,
502        ButtonVariant,
503        Canvas,
504        Checkbox,
505        CheckboxStyle,
506        Circle,
507        Column,
508        Command,
509        // Command palette
510        CommandPalette,
511        Direction,
512        // Convenience widgets
513        Divider,
514        DividerStyle,
515        DrawContext,
516        EventResult,
517        FilledCircle,
518        FilledRectangle,
519        FocusStyle,
520        Input,
521        Interactive,
522        // Layer system
523        Layers,
524        Line,
525        List,
526        Modal,
527        ModalButton,
528        ModalButtonStyle,
529        Orientation,
530        Pagination,
531        PaginationStyle,
532        Points,
533        Positioned,
534        Progress,
535        ProgressStyle,
536        RadioGroup,
537        RadioLayout,
538        RadioStyle,
539        Rectangle,
540        RenderContext,
541        ScrollView,
542        Select,
543        Shape,
544        Skeleton,
545        SkeletonShape,
546        Sparkline,
547        SparklineStyle,
548        Spinner,
549        SpinnerStyle,
550        Stack,
551        Tab,
552        Table,
553        Tabs,
554        Tag,
555        TagStyle,
556        Text,
557        TextArea,
558        ThemePicker,
559        Timeout,
560        // UX widgets
561        Toast,
562        ToastLevel,
563        ToastPosition,
564        Tree,
565        TreeNode,
566        View,
567        WidgetState,
568    };
569
570    // Feature-gated widget types
571    #[cfg(feature = "image")]
572    pub use crate::widget::{Image, ScaleMode};
573    #[cfg(feature = "markdown")]
574    pub use crate::widget::{Markdown, MarkdownPresentation, ViewMode};
575
576    // Widgets - Constructors
577    pub use crate::widget::{
578        avatar,
579        avatar_icon,
580        badge,
581        barchart,
582        border,
583        braille_canvas,
584        // New constructors
585        button,
586        canvas,
587        checkbox,
588        chip,
589        column,
590        // Command palette
591        command_palette,
592        // Convenience widget constructors
593        divider,
594        dot_badge,
595        hstack,
596        input,
597        // Layer system constructors
598        layers,
599        list,
600        modal,
601        pagination,
602        positioned,
603        progress,
604        radio_group,
605        scroll_view,
606        select,
607        skeleton,
608        skeleton_avatar,
609        skeleton_paragraph,
610        skeleton_text,
611        sparkline,
612        spinner,
613        table,
614        tabs,
615        tag,
616        text,
617        textarea,
618        theme_picker,
619        // UX constructors
620        toast,
621        tree,
622        tree_node,
623        vdivider,
624        vstack,
625    };
626
627    // Feature-gated widget constructors
628    #[cfg(feature = "image")]
629    pub use crate::widget::image_from_file;
630    #[cfg(feature = "markdown")]
631    pub use crate::widget::{markdown, markdown_presentation};
632
633    // DOM system
634    pub use crate::dom::{DomId, DomNode, DomRenderer, DomTree, NodeState, Query, WidgetMeta};
635
636    // Worker system
637    pub use crate::worker::{
638        run_blocking, spawn as spawn_worker, WorkerChannel, WorkerHandle, WorkerMessage,
639        WorkerPool, WorkerState,
640    };
641
642    // Tasks - Timer, TaskRunner, EventBus
643    pub use crate::tasks::{
644        EventBus, EventId, Subscription, TaskId, TaskResult, TaskRunner, Timer, TimerEntry, TimerId,
645    };
646
647    // Patterns - Common TUI patterns
648    pub use crate::patterns::{
649        build_color,
650        priority_color,
651        spinner_char,
652        status_color,
653        // Async operations
654        AsyncTask,
655        BreadcrumbItem,
656        ConfirmAction,
657        ConfirmState,
658        FieldType,
659        FormField,
660        // Form validation
661        FormState,
662        // State management
663        MessageState,
664        NavigationEvent,
665        // Navigation
666        NavigationState,
667        Route,
668        SearchMode,
669        // Search/filter
670        SearchState,
671        ValidationError,
672        Validators,
673        BG,
674        BG_INSET,
675        BG_SUBTLE,
676        BLUE,
677        BORDER,
678        BORDER_MUTED,
679        // Colors
680        CYAN,
681        ERROR,
682        FG,
683        FG_DIM,
684        FG_SUBTLE,
685        GREEN,
686        INFO,
687        ORANGE,
688        PURPLE,
689        RED,
690        SPINNER_FRAMES,
691        SUCCESS,
692        WARNING,
693        YELLOW,
694    };
695
696    // Config loading (requires config feature)
697    #[cfg(feature = "config")]
698    pub use crate::patterns::{AppConfig, ConfigError};
699
700    // Accessibility
701    pub use crate::utils::{
702        // Announcement functions
703        announce,
704        // Widget-specific announcement helpers
705        announce_button_clicked,
706        announce_checkbox_changed,
707        announce_dialog_closed,
708        announce_dialog_opened,
709        announce_error,
710        announce_list_selection,
711        announce_now,
712        announce_success,
713        announce_tab_changed,
714        has_announcements,
715        is_high_contrast,
716        // Preference getters/setters
717        prefers_reduced_motion,
718        set_high_contrast,
719        set_reduced_motion,
720        take_announcements,
721    };
722
723    // Testing (Pilot)
724    pub use crate::testing::{Pilot, TestApp, TestConfig};
725    // Visual regression testing
726    pub use crate::testing::{
727        CapturedCell, CiEnvironment, CiProvider, TestReport, VisualCapture, VisualDiff, VisualTest,
728        VisualTestConfig, VisualTestResult,
729    };
730
731    // DevTools
732    #[allow(deprecated)] // Re-exporting deprecated functions for backwards compatibility
733    pub use crate::devtools::{
734        disable_devtools, enable_devtools, is_devtools_enabled, toggle_devtools, ComputedProperty,
735        DevTools, DevToolsConfig, DevToolsPosition, DevToolsTab, EventFilter, EventLogger,
736        EventType, Inspector, InspectorConfig, LoggedEvent, PropertySource, StateDebugger,
737        StateEntry, StateValue, StyleCategory, StyleInspector, WidgetNode,
738    };
739
740    // Profiler
741    pub use crate::utils::profiler::{
742        profile, profiler_report, start_profile, FlameNode, ProfileGuard, Profiler, Stats, Timing,
743    };
744
745    // Result type
746    pub use crate::Result;
747
748    // Constants
749    pub use crate::constants::{
750        // Animation durations
751        ANIMATION_DEFAULT_DURATION,
752        ANIMATION_FAST_DURATION,
753        ANIMATION_SLOW_DURATION,
754        ANIMATION_VERY_SLOW_DURATION,
755        // Debounce
756        DEBOUNCE_DEFAULT,
757        DEBOUNCE_FILE_SYSTEM,
758        DEBOUNCE_SEARCH,
759        FRAME_DURATION_30FPS,
760        // Frame rates
761        FRAME_DURATION_60FPS,
762        // Messages
763        MESSAGE_DEFAULT_DURATION,
764        MESSAGE_LONG_DURATION,
765        MESSAGE_QUICK_DURATION,
766        POLL_IMMEDIATE,
767        // Screen transitions
768        SCREEN_TRANSITION_DURATION,
769        // Stagger
770        STAGGER_DELAY_DEFAULT,
771        // Tick rates
772        TICK_RATE_DEFAULT,
773    };
774}