| Crates.io | adabraka-ui |
| lib.rs | adabraka-ui |
| version | 0.2.3 |
| created_at | 2025-10-21 20:14:32.666391+00 |
| updated_at | 2025-11-13 13:11:20.761447+00 |
| description | A comprehensive, professional UI component library for GPUI inspired by shadcn/ui. 73+ accessible components for building beautiful desktop applications. |
| homepage | https://github.com/augani/adabraka-ui |
| repository | https://github.com/augani/adabraka-ui |
| max_upload_size | |
| id | 1894384 |
| size | 10,395,021 |
A comprehensive, professional UI component library for GPUI, the GPU-accelerated UI framework powering the Zed editor. Inspired by shadcn/ui, adabraka-ui provides 73+ polished, accessible components for building beautiful desktop applications in Rust.
๐ Documentation ยท ๐ Getting Started ยท ๐ฆ Components ยท ๐ก Examples
See adabraka-ui in action in real applications:

A beautiful desktop music player with offline playing capabilities. Features smooth animations, responsive UI, and a polished user experience built entirely with adabraka-ui components.

A powerful task management application used to track the development of this UI library. Features drag-and-drop task organization with smooth animations, showcasing the library's advanced capabilities.
Note: Currently requires Rust nightly due to GPUI dependencies. Install with:
rustup toolchain install nightly
Add adabraka-ui to your Cargo.toml:
[dependencies]
adabraka-ui = "0.2.3"
gpui = "0.2.0"
Build your project with nightly:
cargo +nightly build
Latest Release (November 13, 2025) - Enhanced slider component with vertical orientation support!
The Slider component now supports both horizontal and vertical orientations. Perfect for volume controls, brightness adjustments, and other vertical UI patterns.
// Horizontal slider (default)
Slider::new(slider_state.clone())
.show_value(true)
// Vertical slider
Slider::new(slider_state.clone())
.vertical()
.size(SliderSize::Lg)
.show_value(true)
.on_change(|value, _, _| {
println!("Value: {}", value);
})
The slider thumb is now perfectly centered on the track line, providing a more polished and professional appearance across all size variants (Sm, Md, Lg).
render_horizontal() and render_vertical() methods for cleaner codeUpdated slider_styled_demo.rs with 10 comprehensive examples showcasing horizontal and vertical sliders with various styling options.
use adabraka_ui::prelude::*;
use gpui::*;
fn main() {
Application::new().run(|cx| {
// Initialize the UI library
adabraka_ui::init(cx);
// Install a theme
install_theme(cx, Theme::dark());
cx.open_window(
WindowOptions {
titlebar: Some(TitlebarOptions {
title: Some("My App".into()),
..Default::default()
}),
..Default::default()
},
|_, cx| cx.new(|_| MyApp::new()),
).unwrap();
});
}
struct MyApp;
impl MyApp {
fn new() -> Self {
Self
}
}
impl Render for MyApp {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
VStack::new()
.p(px(32.0))
.gap(px(16.0))
.child(
div()
.text_size(px(24.0))
.font_weight(FontWeight::BOLD)
.child("Welcome to adabraka-ui!")
)
.child(
Button::new("get-started", "Get Started")
.variant(ButtonVariant::Default)
.on_click(|_event, _window, _cx| {
println!("Button clicked!");
})
)
}
}
All 54 components implement the Styled trait, giving you complete control over styling!
Every component can be customized using GPUI's powerful styling API. Apply any styling method to any component:
Button::new("custom-btn", "Click Me")
.variant(ButtonVariant::Primary) // Use built-in variant
.bg(rgb(0x8b5cf6)) // Custom background
.p_8() // Custom padding
.rounded_xl() // Custom border radius
.border_2() // Custom border
.border_color(rgb(0xa78bfa)) // Custom border color
.shadow_lg() // Shadow effect
.w_full() // Full width
Backgrounds & Colors:
.bg(color) - Background color.text_color(color) - Text color.border_color(color) - Border colorSpacing:
.p_4(), .p_8() - Padding (all sides).px_6(), .py_4() - Padding (horizontal/vertical).m_4(), .mx_auto() - MarginsBorders & Radius:
.border_2(), .border_4() - Border width.rounded_sm(), .rounded_lg(), .rounded_xl() - Border radius.rounded(px(16.0)) - Custom radiusSizing:
.w_full(), .h_full() - Full width/height.w(px(300.0)), .h(px(200.0)) - Custom dimensions.min_w(), .max_w() - Min/max constraintsEffects:
.shadow_sm(), .shadow_lg() - Shadow effects.opacity() - Opacity controlAnd hundreds more! Any GPUI styling method works.
Following the shadcn/ui philosophy:
Components ship with sensible defaults that you can completely override.
adabraka-ui provides great defaults AND 100% control over every component's styling!
Every component now has a *_styled_demo.rs example showing full customization capabilities:
cargo +nightly run --example button_styled_demo
cargo +nightly run --example input_styled_demo
cargo +nightly run --example data_table_styled_demo
# ... and 51 more!
adabraka-ui provides a complete theming system with semantic color tokens inspired by shadcn/ui. Themes include both light and dark variants with carefully chosen colors for accessibility and visual hierarchy.
use adabraka_ui::theme::{install_theme, Theme, use_theme};
// In your app initialization
install_theme(cx, Theme::dark()); // or Theme::light()
// In your render method
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = use_theme();
div()
.bg(theme.tokens.background)
.text_color(theme.tokens.foreground)
.child("Themed content")
}
Theme::light() - Clean light themeTheme::dark() - Dark theme with proper contrastThe theme system provides semantic color tokens:
// Background colors
background, card, popover, muted
// Text colors
foreground, card_foreground, popover_foreground, muted_foreground
// Interactive colors
primary, primary_foreground, secondary, secondary_foreground
accent, accent_foreground, destructive, destructive_foreground
// UI elements
border, input, ring
// Spacing and sizing
radius_sm, radius_md, radius_lg
font_family, font_mono
Vertical and horizontal stack layouts with consistent spacing.
// Vertical stack
VStack::new()
.spacing(px(16.0)) // Gap between children
.align(Align::Center) // Cross-axis alignment
.padding(px(24.0))
.child(Text::new("Item 1"))
.child(Text::new("Item 2"))
// Horizontal stack
HStack::new()
.spacing(px(12.0))
.justify(Justify::Between) // Main-axis justification
.align(Align::Center)
.child(Button::new("cancel", "Cancel"))
.child(Button::new("save", "Save").variant(ButtonVariant::Default))
Grid::new()
.cols(3) // 3 columns
.gap(px(16.0))
.child(card1)
.child(card2)
.child(card3)
Start, Center, End, StretchStart, Center, End, Between, Around, EvenlyHorizontal, Vertical (for wrapping)The Text component provides consistent typography with built-in theming and font handling.
use adabraka_ui::components::text::*;
// Semantic heading variants
h1("Page Title")
h2("Section Title")
h3("Subsection")
h4("Minor Heading")
// Body text variants
body("Regular paragraph text")
body_large("Larger body text for lead paragraphs")
body_small("Smaller body text")
caption("Small text for captions and metadata")
// Label variants
label("Form Label")
label_small("Compact Label")
// Code/monospace text
code("fn main() { }")
code_small("let x = 42;")
// Muted text (secondary color)
muted("Secondary information")
muted_small("Small secondary text")
// Custom styling
Text::new("Custom text")
.size(px(18.0))
.weight(FontWeight::BOLD)
.color(rgb(0x3b82f6).into())
.underline()
.no_wrap() // Single line
.truncate() // Add ellipsis when too long
// Builder pattern with variants
Text::new("Styled text")
.variant(TextVariant::H2)
.color(theme.tokens.primary)
Benefits:
font_family() on every text element// Basic button
Button::new("click-me", "Click me")
.on_click(|_event, _window, _cx| {
println!("Clicked!");
})
// Styled variants
Button::new("primary", "Primary").variant(ButtonVariant::Default)
Button::new("secondary", "Secondary").variant(ButtonVariant::Secondary)
Button::new("outline", "Outline").variant(ButtonVariant::Outline)
Button::new("ghost", "Ghost").variant(ButtonVariant::Ghost)
Button::new("link", "Link").variant(ButtonVariant::Link)
Button::new("destructive", "Destructive").variant(ButtonVariant::Destructive)
// Sizes
Button::new("small", "Small").size(ButtonSize::Sm)
Button::new("medium", "Medium").size(ButtonSize::Md) // default
Button::new("large", "Large").size(ButtonSize::Lg)
// States
Button::new("disabled", "Disabled").disabled(true)
// Icon buttons
IconButton::new(IconSource::Named("search".to_string()))
.size(px(32.0))
.on_click(handler)
// Basic text input
let input_state = cx.new(|cx| InputState::new(cx));
Input::new(input_state.clone(), cx)
.placeholder("Enter text...")
.variant(InputVariant::Default)
.size(InputSize::Md)
// Variants
Input::new(input_state, cx)
.variant(InputVariant::Outline)
.variant(InputVariant::Ghost)
// Password input with eye icon toggle (fixed in v0.2.2!)
Input::new(password_input, cx)
.password(true) // Enables eye icon toggle for show/hide
.placeholder("Enter password")
// With prefix/suffix
Input::new(input, cx)
.prefix(div().child("๐"))
.suffix(Button::new("clear", "Clear").size(ButtonSize::Sm))
Checkbox::new("checkbox-id")
.label("Check me")
.checked(false)
.on_click(cx.listener(|view, checked, _window, cx| {
view.is_checked = *checked;
cx.notify();
}))
// Sizes
Checkbox::new("small").size(CheckboxSize::Sm)
Checkbox::new("medium").size(CheckboxSize::Md)
Checkbox::new("large").size(CheckboxSize::Lg)
// States
Checkbox::new("disabled").disabled(true)
Checkbox::new("indeterminate").indeterminate(true)
Toggle::new("toggle-id")
.label("Enable feature")
.checked(true)
.on_click(cx.listener(|view, checked, _window, cx| {
view.feature_enabled = *checked;
cx.notify();
}))
// Sizes and variants available
Interactive slider for selecting numeric values with horizontal and vertical orientations:
// Create slider state
let slider_state = cx.new(|cx| {
let mut state = SliderState::new(cx);
state.set_min(0.0, cx);
state.set_max(100.0, cx);
state.set_value(50.0, cx);
state.set_step(1.0, cx);
state
});
// Horizontal slider (default)
Slider::new(slider_state.clone())
.size(SliderSize::Md)
.show_value(true)
.on_change(|value, _window, _cx| {
println!("Value changed: {}", value);
})
// Vertical slider
Slider::new(slider_state.clone())
.vertical()
.size(SliderSize::Lg)
.show_value(true)
// Sizes
Slider::new(slider_state.clone()).size(SliderSize::Sm) // Small
Slider::new(slider_state.clone()).size(SliderSize::Md) // Medium (default)
Slider::new(slider_state.clone()).size(SliderSize::Lg) // Large
// Disabled state
Slider::new(slider_state.clone()).disabled(true)
// Full Styled trait support for custom styling
Slider::new(slider_state.clone())
.w(px(400.0))
.bg(rgb(0x1e293b))
.p(px(16.0))
.rounded(px(12.0))
.border_2()
.border_color(rgb(0x3b82f6))
let options = vec![
SelectOption::new("option1", "Option 1"),
SelectOption::new("option2", "Option 2"),
];
Select::new(cx)
.options(options)
.selected_index(Some(0))
.placeholder("Choose an option")
.on_change(cx.listener(|view, selected_value, _window, cx| {
view.selected_option = Some(selected_value.clone());
cx.notify();
}))
// Features
Select::new(cx)
.searchable(true) // Enable search
.clearable(true) // Show clear button
.loading(true) // Show loading state
.disabled(true) // Disable interaction
Textarea::new(textarea_state, cx)
.placeholder("Enter your message...")
.rows(4)
.resize(TextareaResize::Vertical)
.max_length(Some(500))
Advanced search input with filters, case sensitivity, and results count:
use adabraka_ui::components::search_input::*;
// Create search input with filters
let search = cx.new(|cx| SearchInput::new(cx)
.filters(vec![
SearchFilter::new("*.rs", "rs"),
SearchFilter::new("*.toml", "toml"),
SearchFilter::new("*.md", "md"),
], cx)
.on_search(move |query, app_cx| {
// Handle search query
println!("Searching for: {}", query);
}, cx)
.on_filter_toggle(move |index, app_cx| {
// Handle filter toggle
println!("Toggled filter: {}", index);
}, cx)
);
// Update results count from your component
search.update(cx, |search, cx| {
search.state().update(cx, |state, cx| {
state.set_results_count(Some(42), cx);
});
});
// Check filter states
let active_filters = search.read(cx).state().read(cx).active_filters();
let case_sensitive = search.read(cx).state().read(cx).case_sensitive();
Features:
Full-featured color picker with multiple color modes and recent color history:
use adabraka_ui::components::color_picker::*;
// Create color picker state
let color_state = cx.new(|_| ColorPickerState::new(hsla(210.0, 0.7, 0.5, 1.0)));
// Render color picker
ColorPicker::new("picker-id", color_state.clone())
.show_alpha(true)
.on_change({
let state = color_state.clone();
move |color, _window, cx| {
state.update(cx, |s, cx| {
s.set_color(color);
cx.notify();
});
println!("Color changed: {:?}", color);
}
})
// With custom swatches
ColorPicker::new("custom-picker", color_state.clone())
.swatches(vec![
hsla(0.0, 0.8, 0.5, 1.0), // Red
hsla(120.0, 0.7, 0.5, 1.0), // Green
hsla(240.0, 0.8, 0.6, 1.0), // Blue
])
.show_alpha(false)
Features:
Advanced date picker with single date and date range selection:
use adabraka_ui::components::date_picker::*;
use adabraka_ui::components::calendar::*;
// Single date picker
let single_state = cx.new(|cx| DatePickerState::new(cx));
DatePicker::new(single_state.clone())
.placeholder("Select a date")
.on_select(cx.listener(|this, date, _window, cx| {
println!("Selected: {:?}", date);
cx.notify();
}))
// Date range picker
let range_state = cx.new(|cx|
DatePickerState::new_with_mode(DateSelectionMode::Range, cx)
);
DatePicker::new(range_state.clone())
.placeholder("Select date range")
.on_select(cx.listener(|this, _date, _window, cx| {
let state = this.range_state.read(cx);
if let Some(range) = state.selected_range {
println!("Range: {} to {}", range.start, range.end);
}
cx.notify();
}))
// With disabled dates
DatePicker::new(picker_state.clone())
.disable_weekends()
.disable_dates(vec![
DateValue::new(2024, 12, 25), // Christmas
DateValue::new(2024, 1, 1), // New Year
])
Features:
Searchable dropdown with single and multi-select support:
use adabraka_ui::components::combobox::*;
// Create combobox state
let combobox_state = cx.new(|_| ComboboxState::new());
// Single select
Combobox::new(combobox_state.clone())
.items(vec!["Apple", "Banana", "Cherry", "Date"])
.placeholder("Select a fruit...")
.on_select(cx.listener(|this, item, _window, cx| {
println!("Selected: {}", item);
cx.notify();
}))
// Multi-select
Combobox::new(multi_state.clone())
.items(vec!["Rust", "Python", "JavaScript", "Go"])
.placeholder("Select languages...")
.multi_select(true)
.on_select(cx.listener(|this, items, _window, cx| {
println!("Selected items: {:?}", items);
cx.notify();
}))
// With custom display
Combobox::new(custom_state.clone())
.items(users)
.display_fn(|user| format!("{} <{}>", user.name, user.email))
.search_fn(|user, query| {
user.name.contains(query) || user.email.contains(query)
})
Features:
A high-performance multi-line code editor with syntax highlighting, perfect for SQL queries and code editing:
// Create editor state
let editor_state = cx.new(|cx| {
let mut state = EditorState::new(cx);
state.set_language(Language::Sql, cx);
state.set_content("SELECT * FROM users;", cx);
state
});
// Render editor
Editor::new(&editor_state)
.language(Language::Sql, cx)
.min_lines(10)
.show_line_numbers(true, cx)
// Get content
let content = editor.get_content(cx);
Features:
Keyboard Shortcuts:
Ctrl+A / Cmd+A - Select allCtrl+C / Cmd+C - CopyCtrl+X / Cmd+X - CutCtrl+V / Cmd+V - PasteShift + Arrow - Extend selectionHome / End - Jump to line start/endCtrl+Home / Cmd+Up - Jump to document startCtrl+End / Cmd+Down - Jump to document endPage Up / Page Down - Scroll by pageExample:
struct MyApp {
editor_state: Entity<EditorState>,
}
impl MyApp {
fn new(cx: &mut Context<Self>) -> Self {
let editor_state = cx.new(|cx| {
let mut state = EditorState::new(cx);
state.set_language(Language::Sql, cx);
state.set_content(
"-- Sample SQL Query\n\
SELECT id, name, email \n\
FROM users \n\
WHERE created_at >= '2024-01-01';",
cx,
);
state
});
Self { editor_state }
}
}
impl Render for MyApp {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.size_full()
.child(
Editor::new(&self.editor_state)
.language(Language::Sql, cx)
.min_lines(20)
.show_line_numbers(true, cx)
)
}
}
Display progress for long-running operations with progress bars and spinners.
Linear progress indicators with determinate and indeterminate modes:
use adabraka_ui::components::progress::*;
// Determinate progress (0.0 to 1.0)
ProgressBar::new(0.75) // 75% complete
// With label and percentage
ProgressBar::new(0.45)
.label("Uploading files...")
.show_percentage(true)
// Different variants
ProgressBar::new(0.8)
.variant(ProgressVariant::Success)
.label("Upload complete")
ProgressBar::new(0.6)
.variant(ProgressVariant::Warning)
.label("Storage almost full")
ProgressBar::new(0.4)
.variant(ProgressVariant::Destructive)
.label("Critical: Low memory")
// Different sizes
ProgressBar::new(0.5).size(ProgressSize::Sm) // Thin (4px)
ProgressBar::new(0.5).size(ProgressSize::Md) // Default (8px)
ProgressBar::new(0.5).size(ProgressSize::Lg) // Large (12px)
// Indeterminate (loading animation)
ProgressBar::indeterminate()
.label("Loading...")
ProgressBar::indeterminate()
.variant(ProgressVariant::Success)
.label("Syncing data...")
Circular progress indicators and loading spinners:
// Determinate circular progress
CircularProgress::new(0.75) // 75% complete
// Indeterminate spinner
CircularProgress::indeterminate()
// With variants
CircularProgress::indeterminate()
.variant(ProgressVariant::Success)
.size(px(32.0))
CircularProgress::indeterminate()
.variant(ProgressVariant::Warning)
.size(px(48.0))
CircularProgress::indeterminate()
.variant(ProgressVariant::Destructive)
.size(px(64.0))
Features:
Use Cases:
let columns = vec![
Column::new("name", "Name").sortable(true),
Column::new("email", "Email").sortable(true),
Column::new("role", "Role"),
];
Table::new(cx)
.columns(columns)
.data(user_data)
.sortable(true)
.on_row_click(|user, _window, _cx| {
println!("Clicked user: {}", user.name);
})
High-performance table for large datasets:
let columns = vec![
DataTableColumn::new("id", "ID").width(px(80.0)),
DataTableColumn::new("name", "Name").width(px(200.0)),
DataTableColumn::new("email", "Email").width(px(250.0)),
];
DataTable::new(data, columns, cx)
.sortable(true)
.on_row_select(|item, _window, _cx| {
println!("Selected: {:?}", item);
})
Badge::new("New")
Badge::new("12").variant(BadgeVariant::Secondary)
Badge::new("Error").variant(BadgeVariant::Destructive)
Card::new()
.header(div().child("Card Title"))
.content(div().child("Card content goes here"))
.footer(div().child("Card footer"))
let items = vec![
SidebarItem::new("dashboard", "Dashboard")
.with_icon(IconSource::Named("home".to_string())),
SidebarItem::new("settings", "Settings")
.with_icon(IconSource::Named("settings".to_string()))
.with_badge("3".to_string()),
];
Sidebar::new(cx)
.items(items)
.selected_id("dashboard".to_string())
.variant(SidebarVariant::Collapsible)
.position(SidebarPosition::Left)
.expanded_width(px(280.0))
.collapsed_width(px(64.0))
.on_select(cx.listener(|view, id, _window, cx| {
view.current_page = id.clone();
cx.notify();
}))
let tabs = vec![
TabItem::new("tab1", "Overview"),
TabItem::new("tab2", "Settings").with_icon(IconSource::Named("settings".to_string())),
];
let panels = vec![
TabPanel::new("tab1", div().child("Overview content")),
TabPanel::new("tab2", div().child("Settings content")),
];
Tabs::new(cx)
.tabs(tabs)
.panels(panels)
.selected_index(0)
.on_change(cx.listener(|view, index, _window, cx| {
view.active_tab = *index;
cx.notify();
}))
let items = vec![
BreadcrumbItem::new("home", "Home"),
BreadcrumbItem::new("projects", "Projects"),
BreadcrumbItem::new("current", "Current Project"),
];
Breadcrumbs::new(cx)
.items(items)
.on_click(cx.listener(|view, id, _window, cx| {
view.navigate_to(id.clone());
cx.notify();
}))
let nodes = vec![
TreeNode::new("root", "Root")
.with_children(vec![
TreeNode::new("child1", "Child 1"),
TreeNode::new("child2", "Child 2")
.with_children(vec![
TreeNode::new("grandchild", "Grandchild"),
]),
]),
];
Tree::new(cx)
.nodes(nodes)
.selected_id(Some("child1".to_string()))
.expanded_ids(vec!["root".to_string()])
.on_select(cx.listener(|view, id, _window, cx| {
view.selected_item = Some(id.clone());
cx.notify();
}))
A comprehensive menu system for desktop applications with MenuBar, Menu, MenuItem, and ContextMenu components:
// MenuBar - Top-level horizontal menu bar
let file_menu = vec![
MenuItem::new("new", "New File")
.with_icon(IconSource::Named("file-plus".into()))
.with_shortcut("Cmd+N")
.on_click(|_window, _cx| println!("New file")),
MenuItem::separator(),
MenuItem::new("save", "Save")
.with_shortcut("Cmd+S")
.on_click(|_window, _cx| println!("Save")),
];
MenuBar::new(cx, vec![
MenuBarItem::new("File", file_menu),
MenuBarItem::new("Edit", edit_menu),
MenuBarItem::new("View", view_menu),
])
// MenuItem types
MenuItem::new("action", "Regular Action") // Action item
MenuItem::checkbox("check", "Enable Feature", true) // Checkbox
MenuItem::new("parent", "Submenu").with_children(vec![...]) // Submenu
MenuItem::separator() // Visual divider
// Standalone Menu
Menu::new(cx, vec![
MenuItem::new("copy", "Copy")
.with_icon(IconSource::Named("copy".into()))
.with_shortcut("Cmd+C"),
MenuItem::new("paste", "Paste")
.with_icon(IconSource::Named("clipboard".into())),
])
// ContextMenu - Right-click context menu
ContextMenu::new(cx, menu_items, position)
.on_close(|_window, _cx| {
// Handle close
})
Features:
Action bars with icon buttons, groups, and toggle states for desktop applications:
// Create toolbar with groups
let formatting_group = ToolbarGroup::new()
.button(
ToolbarButton::new("bold", IconSource::Named("bold".into()))
.tooltip("Bold")
.variant(ToolbarButtonVariant::Toggle)
.pressed(is_bold)
.on_click(|_window, _cx| toggle_bold())
)
.button(
ToolbarButton::new("italic", IconSource::Named("italic".into()))
.tooltip("Italic")
.variant(ToolbarButtonVariant::Toggle)
.pressed(is_italic)
)
.separator()
.button(
ToolbarButton::new("underline", IconSource::Named("underline".into()))
.tooltip("Underline")
);
Toolbar::new()
.size(ToolbarSize::Md)
.group(formatting_group)
.group(alignment_group)
// Button variants
ToolbarButtonVariant::Default // Regular button
ToolbarButtonVariant::Toggle // Toggle button (pressed/unpressed)
ToolbarButtonVariant::Dropdown // Shows dropdown indicator
// Sizes
ToolbarSize::Sm // 32px buttons
ToolbarSize::Md // 36px buttons (default)
ToolbarSize::Lg // 40px buttons
// Toolbar items
ToolbarItem::Button(button) // Button
ToolbarItem::Separator // Visual separator
ToolbarItem::Spacer // Flexible space (push to right)
Features:
Bottom application status bar with three sections (left, center, right) for displaying app state and contextual information:
use adabraka_ui::navigation::status_bar::*;
// Create status bar with sections (capturing entity for callbacks)
let entity = cx.entity().clone();
cx.new(|_| {
StatusBar::new()
.left(vec![
StatusItem::icon_text(IconSource::Named("file".into()), "main.rs"),
StatusItem::text("Ln 42, Col 15"),
])
.center(vec![
StatusItem::text("Ready"),
])
.right(vec![
StatusItem::icon_badge(
IconSource::Named("bell".into()),
"3"
)
.badge_variant(BadgeVariant::Default)
.tooltip("Notifications")
.on_click({
let entity = entity.clone();
move |_window, app_cx| {
app_cx.update_entity(&entity, |this: &mut MyApp, cx| {
this.show_notifications(cx);
});
}
}),
StatusItem::text("UTF-8")
.tooltip("File Encoding")
.on_click({
let entity = entity.clone();
move |_window, app_cx| {
app_cx.update_entity(&entity, |this: &mut MyApp, cx| {
this.change_encoding(cx);
});
}
}),
StatusItem::icon_text(IconSource::Named("git-branch".into()), "main")
.tooltip("Git Branch"),
])
})
// Status item types
StatusItem::text("Text") // Text only
StatusItem::icon(IconSource::Named(...)) // Icon only
StatusItem::icon_text(icon, "Text") // Icon with text
StatusItem::badge("3", "Tooltip") // Badge only
StatusItem::icon_badge(icon, "3") // Icon with badge
// Item customization
StatusItem::text("Clickable")
.on_click({
let entity = entity.clone();
move |_window, app_cx| {
app_cx.update_entity(&entity, |this: &mut MyApp, cx| {
// Handle click
cx.notify();
});
}
})
.tooltip("Tooltip text")
.disabled(true)
.badge_variant(BadgeVariant::Warning)
Features:
Display and organize keyboard shortcuts by category with platform-specific key formatting:
use adabraka_ui::components::keyboard_shortcuts::*;
cx.new(|_| {
KeyboardShortcuts::new()
.category("File", vec![
ShortcutItem::new("New File", "cmd-n"),
ShortcutItem::new("Open File", "cmd-o"),
ShortcutItem::new("Save", "cmd-s"),
])
.category("Edit", vec![
ShortcutItem::new("Undo", "cmd-z"),
ShortcutItem::new("Redo", "cmd-shift-z"),
ShortcutItem::new("Cut", "cmd-x"),
])
.category("View", vec![
ShortcutItem::new("Toggle Sidebar", "cmd-b"),
ShortcutItem::new("Zoom In", "cmd-="),
])
})
// Custom category
let category = ShortcutCategory::new("Custom", vec![
ShortcutItem::new("Action", "ctrl-alt-k"),
]);
KeyboardShortcuts::new()
.add_category(category)
Features:
A searchable command palette (Cmd+K / Ctrl+K style) for quick access to application commands:
// Create commands
let commands = vec![
Command::new("file.new", "New File")
.icon(IconSource::Named("file-plus".into()))
.description("Create a new file")
.category("File")
.shortcut("Cmd+N")
.on_select(|_window, _cx| create_new_file()),
Command::new("edit.find", "Find")
.icon(IconSource::Named("search".into()))
.description("Find text in current file")
.category("Edit")
.shortcut("Cmd+F"),
];
// Show command palette
if self.show_palette {
CommandPalette::new(window, cx, commands)
.on_close(|_window, _cx| {
// Handle close
})
}
Features:
// Show dialog conditionally
div()
.when(self.show_dialog, |this| {
this.child(
Dialog::new(cx)
.title("Confirm Action")
.content(div().child("Are you sure you want to proceed?"))
.size(DialogSize::Md)
.confirm_button(
Button::new("confirm", "Confirm")
.variant(ButtonVariant::Default)
.on_click(cx.listener(|view, _event, _window, cx| {
view.confirm_action(cx);
}))
)
.cancel_button(
Button::new("cancel", "Cancel")
.on_click(cx.listener(|view, _event, _window, cx| {
view.show_dialog = false;
cx.notify();
}))
)
)
})
Popover::new(cx)
.trigger(Button::new("open-popover", "Open Popover"))
.content(
VStack::new()
.p(px(16.0))
.gap(px(8.0))
.child(div().child("Popover content"))
.child(Button::new("action", "Action"))
)
.position(PopoverPosition::Bottom)
.alignment(PopoverAlignment::Start)
// In your app struct
toast_manager: Entity<ToastManager>,
// Initialize
fn new(cx: &mut App) -> Self {
let toast_manager = cx.new(|cx| ToastManager::new(cx));
Self { toast_manager }
}
// Show toast
fn show_success(&mut self, cx: &mut Context<Self>) {
let toast = ToastItem::new(1, "Success!")
.description("Operation completed successfully")
.variant(ToastVariant::Success);
self.toast_manager.update(cx, |manager, cx| {
manager.add_toast(toast, window, cx);
});
}
// Render toast manager
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.child(self.toast_manager.clone())
// ... other content
}
adabraka-ui includes a professional animation system with smooth easing functions and spring physics:
use adabraka_ui::animations::*;
// Fade animations with cubic easing
div().with_animation("fade", presets::fade_in_normal(), |div, delta| {
div.opacity(delta)
})
// Scale animations with back easing (subtle overshoot)
div().with_animation("scale", presets::scale_up(), |div, delta| {
let scale = 0.5 + (0.5 * delta);
div.size(px(100.0 * scale))
})
// Smooth scale without overshoot
div().with_animation("scale", presets::scale_up_smooth(), |div, delta| {
let scale = 0.8 + (0.2 * delta);
div.size(px(100.0 * scale))
})
// Slide animations with smooth cubic easing
div().with_animation("slide", presets::slide_in_left(), |div, delta| {
div.ml(px(-200.0 * (1.0 - delta)))
})
// Spring-based slide (natural feeling)
div().with_animation("spring", presets::spring_slide_left(), |div, delta| {
div.ml(px(-200.0 * (1.0 - delta)))
})
// Continuous animations
// Smooth pulse with helper function
div().with_animation("pulse", presets::pulse(), |div, delta| {
let scale = pulse_scale(delta, 1.0, 1.15);
div.size(px(80.0 * scale))
})
// Opacity pulse
div().with_animation("pulse-opacity", presets::pulse_slow(), |div, delta| {
let opacity = pulse_opacity(delta, 0.4, 1.0);
div.opacity(opacity)
})
// Shake with natural decay
div().with_animation("shake", presets::shake(), |div, delta| {
let offset = shake_offset(delta, 12.0);
div.ml(px(offset))
})
Animation Features:
Available Easing Functions:
ease_out_cubic - Most natural for UI (default)ease_in_out_cubic - Smooth acceleration and decelerationease_out_quad - Gentle decelerationease_out_quart - Very smooth decelerationease_out_expo - Dramatic decelerationease_out_back - Slight overshoot for emphasisspring - Natural bouncy effectsmooth_spring - Subtle spring (recommended for UI)Animation Presets:
fade_in_quick(), fade_in_normal(), fade_in_slow()scale_up(), scale_up_smooth(), scale_down()slide_in_left/right/top/bottom(), spring_slide_left/right()pulse(), pulse_fast(), pulse_slow()shake(), shake_strong(), bounce_in(), spring()Scrollable containers automatically include padding to ensure content at the bottom is fully visible.
use adabraka_ui::components::scroll::*;
// Default scrolling with 24px padding (recommended)
div()
.size_full()
.child(
scrollable_vertical(
VStack::new()
.gap(px(16.0))
// ... many items
.child(item1)
.child(item2)
// ...
)
)
// Custom padding amount
scrollable_vertical_with_padding(content, px(32.0))
// No padding (use carefully - items may be cut off at the bottom)
Scrollable::new(ScrollbarAxis::Vertical, content)
.no_padding()
// Both directions
scrollable_both(content)
scrollable_both_with_padding(content, px(16.0))
Features:
adabraka-ui provides flexible icon support with both named icons and custom icon paths. Note: Icon assets are not bundled with the library to keep the bundle size small. You need to provide your own icon assets.
To use icons in your application, you need to:
use adabraka_ui::prelude::*;
use gpui::*;
use std::path::PathBuf;
// Define your asset source
struct Assets {
base: PathBuf,
}
impl AssetSource for Assets {
fn load(&self, path: &str) -> Result<Option<std::borrow::Cow<'static, [u8]>>> {
std::fs::read(self.base.join(path))
.map(|data| Some(std::borrow::Cow::Owned(data)))
.map_err(|err| err.into())
}
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
std::fs::read_dir(self.base.join(path))
.map(|entries| {
entries
.filter_map(|entry| {
entry.ok().and_then(|e| {
e.file_name().to_str().map(|s| SharedString::from(s.to_string()))
})
})
.collect()
})
.map_err(|err| err.into())
}
}
fn main() {
Application::new()
.with_assets(Assets {
base: PathBuf::from(env!("CARGO_MANIFEST_DIR")),
})
.run(|cx| {
// Initialize the UI library
adabraka_ui::init(cx);
// Configure where icons are located
// This path is relative to your CARGO_MANIFEST_DIR
adabraka_ui::set_icon_base_path("assets/icons");
// Install theme
install_theme(cx, Theme::dark());
// ... rest of your app
});
}
Once configured, you can use icons in two ways:
Named icons are automatically resolved using the configured base path:
use adabraka_ui::components::icon::IconSource;
// Named icons (resolved to: assets/icons/{name}.svg)
IconSource::Named("search".to_string())
IconSource::Named("home".to_string())
IconSource::Named("settings".to_string())
// Use in components
Button::new("Search")
.prefix(IconSource::Named("search".to_string()))
SidebarItem::new("dashboard", "Dashboard")
.with_icon(IconSource::Named("home".to_string()))
Icon::new("arrow-up")
.size(px(24.0))
.color(theme.tokens.primary)
For custom or one-off icons, use direct file paths:
// Direct file path
IconSource::FilePath("assets/custom/my-icon.svg".into())
// Absolute path
IconSource::FilePath("/path/to/icon.svg".into())
// The library automatically detects paths (contains '/' or ends with '.svg')
Icon::new("assets/custom/logo.svg") // Treated as file path
Icon::new("search") // Treated as named icon
Your icon SVG files should:
stroke="currentColor" to inherit color from the component0 0 24 24)Example icon SVG:
<svg xmlns="http://www.w3.org/2000/svg"
width="24" height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round">
<!-- icon paths -->
</svg>
By not bundling icons, adabraka-ui keeps its package size small (saves ~3,274 icon files). This allows you to:
The Icon component now supports advanced styling and transformations:
Use semantic size names instead of pixel values:
use adabraka_ui::prelude::*;
Icon::new("search")
.size(IconSize::XSmall) // 12px
.size(IconSize::Small) // 14px
.size(IconSize::Medium) // 16px (default)
.size(IconSize::Large) // 24px
.size(IconSize::Custom(px(32.0))) // Custom size
// Backward compatible - Pixels still work:
Icon::new("search").size(px(20.0)) // Auto-converts to Custom
Icons now implement the Styled trait, allowing all GPUI styling methods:
Icon::new("star")
.size(IconSize::Large)
.p_2() // Padding
.bg(gpui::blue()) // Background color
.rounded_md() // Rounded corners
.border_1() // Border
.border_color(gpui::gray())
Perfect for loading spinners, directional arrows, and animated icons:
use gpui::Radians;
// Rotate icon 90 degrees
Icon::new("arrow-right")
.rotate(Radians::from_degrees(90.0))
// Animated loading spinner
Icon::new("loader")
.rotate(Radians::TAU * progress) // Animate with state
Icons support built-in click handlers:
Icon::new("close")
.clickable(true)
.on_click(|window, cx| {
// Handle click
})
.size(IconSize::Large)
use adabraka_ui::components::resizable::*;
h_resizable("layout", resizable_state)
.child(resizable_panel().child(sidebar))
.child(resizable_panel().child(main_content))
// Input with validation
let input_state = cx.new(|cx| InputState::new(cx)
.with_validation_rules(ValidationRules {
required: true,
min_length: Some(3),
max_length: Some(50),
pattern: Some(regex!("^[a-zA-Z0-9]+$")),
}));
Input::new(input_state, cx)
.placeholder("Username")
.on_blur(|_input, _window, cx| {
// Trigger validation on blur
cx.notify();
})
use adabraka_ui::components::drag_drop::*;
Draggable::new("item-1")
.child(div().child("Drag me"))
.on_drag_start(|data, _window, _cx| {
println!("Started dragging: {:?}", data);
})
DropZone::new("drop-zone")
.child(div().child("Drop here"))
.on_drop(|dropped_data, _window, cx| {
println!("Dropped: {:?}", dropped_data);
// Handle the drop
})
The library includes 50+ example applications demonstrating all components and features.
# Comprehensive demo with all components
cargo run --example demo
# Full IDE-style application
cargo run --example ide_demo
# File explorer with tree navigation
cargo run --example file_explorer
# Input & Forms
cargo run --example input_demo
cargo run --example input_validation
cargo run --example password_test
cargo run --example search_input_demo
cargo run --example color_picker_demo
cargo run --example date_picker_demo
cargo run --example combobox_demo
# Navigation
cargo run --example sidebar_demo
cargo run --example tabs_demo
cargo run --example menu_demo
cargo run --example toolbar_demo
cargo run --example app_menu_demo
cargo run --example status_bar_demo
cargo run --example navigation_menu_demo
# Display
cargo run --example data_table_demo
cargo run --example card_demo
cargo run --example accordion_demo
cargo run --example progress_demo
cargo run --example text_demo
# Overlays
cargo run --example overlays_demo
cargo run --example command_palette_demo
# Advanced
cargo run --example editor_demo
cargo run --example drag_drop_demo
cargo run --example animations_demo
cargo run --example transitions_demo
cargo run --example virtual_list_demo
# Layout
cargo run --example layout_demo
cargo run --example complex_layout_demo
cargo run --example scroll_test
# Icons & Assets
cargo run --example icon_showcase
cargo run --example keyboard_shortcuts_demo
# Trees & Lists
cargo run --example tree_list_demo
cargo run --example tree_performance_demo
To see all available examples:
cargo run --example
adabraka-ui is built on top of GPUI with these key principles:
We welcome contributions from the community! Whether you're fixing bugs, adding features, or improving documentation, your help is appreciated.
cargo fmt for formatting and cargo clippy for lintingFor detailed guidelines, see CONTRIBUTING.md.
This project wouldn't be possible without the amazing work of:
Special thanks to the Zed team for open-sourcing GPUI and making it available for building desktop applications in Rust.
MIT License - see LICENSE file for details.
Built with โค๏ธ using GPUI and inspired by shadcn/ui