Desktop Components

GPUI primitives (Toggle, Chip, Meter, …) and app-level composites for Celestia Desktop — APIs, file map, and gallery sections

Desktop Components

Focused guide for the recent celestia-ui additions. For crates, theming, and the full web→desktop map, see Desktop (GPUI). For stores and ambient context, see Desktop State.

Run the gallery:

bash
pnpm desktop

The showcase sidebar is one component per row (search above the nav). Open Button for the dedicated button gallery; other primitives and composites each have their own page under categories like Inputs, Badges, Menus, and Composites.


File overview

PathPurpose
packages/desktop/crates/ui/src/components/primitive/toggle.rsToggle, ToggleGroup, ToggleVariant
packages/desktop/crates/ui/src/components/primitive/aspect_ratio.rsAspectRatio
packages/desktop/crates/ui/src/components/primitive/item.rsItem, slots, ItemGroup, ItemSeparator
packages/desktop/crates/ui/src/components/primitive/chip.rsChip, ChipVariant
packages/desktop/crates/ui/src/components/primitive/status_dot.rsStatusDot, StatusDotTone
packages/desktop/crates/ui/src/components/primitive/caption.rsCaption
packages/desktop/crates/ui/src/components/primitive/typography.rsTypography, Heading, type scale + tones
packages/desktop/crates/ui/src/components/primitive/image_icon.rsImageIcon, sizes / shapes / layout (label optional)
packages/desktop/crates/ui/src/components/primitive/meter.rsMeter, MeterTone
packages/desktop/crates/ui/src/components/primitive/menubar.rsMenubar, MenubarMenu
packages/desktop/crates/ui/src/components/primitive/navigation_menu.rsNavigationMenu, NavigationMenuItem
packages/desktop/crates/ui/src/components/composite/{app_header,toolbar,panel,…}.rsApp composites (see below)
packages/desktop/crates/showcase/src/catalog.rsIndividual component catalog (sidebar + search)
packages/desktop/crates/showcase/src/sections/demo.rsPer-component demo dispatch
packages/desktop/crates/showcase/src/sections/buttons.rsDedicated Button / Kbd / Link demos
packages/desktop/crates/showcase/src/sections/{inputs,tags,menus,composites,…}.rsFocused demos for each catalog entry

Flat imports work for most types (use celestia_ui::components::Toggle). Document tabs stay module-path only: components::composite::tab_bar::TabBar (name clash with primitive::tabs::TabBar).


Typography

Standard type scale for product copy. Prefer Typography / Heading over ad-hoc div().text_sm().font_weight(...).

rust
use celestia_ui::components::primitive::typography::{    Heading, Typography, TypographyTone, TypographyVariant,};
Heading::h1("Page title");Heading::h2("Section");Typography::lead("Supporting intro under a title.");Typography::body("Default reading size.");Typography::muted("Secondary helper line.");Typography::caption("Footnote / meta");Typography::overline("SECTION"); // already uppercased — GPUI has no text-transformTypography::code("pnpm desktop");
Typography::new("Accent body")    .variant(TypographyVariant::Body)    .tone(TypographyTone::Accent);
VariantSizeWeightDefault tone
Display36BoldDefault
H1–H430→16SemiboldDefault
Lead18RegularDefault
Body / BodySm14 / 13RegularDefault
Muted / Caption / Overline14 / 12 / 11Regular / SemiboldMuted
Code13 monoRegularDefault

Tones: Default, Muted, Accent, Destructive, Success, Warning, Inherit. Gallery: Typography category → Typography / Heading.


Toggle & ToggleGroup

Web: toggle.tsx. A pressed on/off button — not a Switch track. Controlled: on_click reports the requested next value; the host writes state and notifies.

rust
use celestia_ui::components::primitive::toggle::{Toggle, ToggleGroup};use celestia_ui::components::primitive::icon::{Phosphor, PhosphorIcon};use gpui::px;
// Outline chip togglesToggle::new("bold")    .outline()    .label("Bold")    .checked(bold)    .on_click(|next, _, cx| { /* set bold = *next; cx.notify() */ });
// Segmented exclusive group (toolbar align)ToggleGroup::new("align")    .outline()    .segmented()    .exclusive()    .child(Toggle::new("l").checked(align == 0).child(        Phosphor::new(PhosphorIcon::TextAlignLeft).size(px(14.0)),    ))    .child(Toggle::new("c").checked(align == 1).child(        Phosphor::new(PhosphorIcon::TextAlignCenter).size(px(14.0)),    ))    .child(Toggle::new("r").checked(align == 2).child(        Phosphor::new(PhosphorIcon::TextAlignRight).size(px(14.0)),    ))    .on_click(|checks, _, cx| {        // checks[i] is the new pressed state of child i    });
BuilderNotes
ToggleVariant::Default / OutlineWeb default / outline
ToggleGroup::segmentedJoins borders into one control
ToggleGroup::exclusiveRadio behavior — one on at a time
Sizable / DisableableSame trait surface as Switch / Checkbox

AspectRatio

Web: aspect-ratio.tsx. Uses GPUI's Styled::aspect_ratio so the box keeps width ÷ height as it flexes.

rust
use celestia_ui::components::primitive::aspect_ratio::AspectRatio;use gpui::{div, relative};
AspectRatio::widescreen() // 16 / 9    .child(        div()            .size_full()            .bg(/* theme.muted */),    );
AspectRatio::square();      // 1 / 1AspectRatio::new(4.0 / 3.0);

Children are absolutely inset to fill the ratio box. Put a width (or flex basis) on a parent when you need a concrete size.


Item

Web: item.tsx. List-row atom with named slots. Distinct from the delegate-based composite::list re-export.

rust
use celestia_ui::components::primitive::item::{    Item, ItemActions, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemTitle,};use celestia_ui::components::primitive::button::{Button, ButtonVariant};use celestia_ui::components::primitive::icon::{Phosphor, PhosphorIcon};use gpui::px;
ItemGroup::new()    .child(        Item::new()            .id("readme")            .outline()            .child(                ItemMedia::new().icon().child(                    Phosphor::new(PhosphorIcon::File).size(px(16.0)),                ),            )            .child(                ItemContent::new()                    .child(ItemTitle::new("README.md"))                    .child(ItemDescription::new("Project overview")),            )            .child(                ItemActions::new().child(                    Button::new("open")                        .variant(ButtonVariant::Ghost)                        .label("Open"),                ),            ),    );
TypeRole
ItemVariantDefault, Outline, Muted
ItemSizeDefault, Sm, Xs
ItemMediaLeading icon / image slot (icon(), image())
ItemHeader / ItemFooterFull-width rows inside an item
ItemSeparatorHorizontal rule between rows

Web: menubar.tsx. Top-of-window File / Edit / View strip. Each MenubarMenu is a ghost trigger that opens a PopupMenu via Button::dropdown_menu.

rust
use celestia_ui::components::primitive::menubar::{Menubar, MenubarMenu};use celestia_ui::components::composite::menu::PopupMenuItem;
Menubar::new("app-menubar")    .menu(MenubarMenu::new("file", "File", |menu, _, _| {        menu.item(PopupMenuItem::new("New").on_click(|_, _, _| {}))            .item(PopupMenuItem::new("Open…").on_click(|_, _, _| {}))            .separator()            .item(PopupMenuItem::new("Quit").on_click(|_, _, _| {}))    }))    .menu(MenubarMenu::new("edit", "Edit", |menu, _, _| {        menu.item(PopupMenuItem::new("Undo").on_click(|_, _, _| {}))    }));

Prefer Menubar for app chrome, ToolbarMenu for icon strips, and Button::dropdown_menu for one-off menus.


Web: navigation-menu.tsx. Horizontal in-content nav with immediate links and flyout dropdowns (same PopupMenu family as Menubar).

rust
use celestia_ui::components::primitive::navigation_menu::{    NavigationMenu, NavigationMenuItem,};use celestia_ui::components::composite::menu::PopupMenuItem;
NavigationMenu::new("site-nav")    .item(NavigationMenuItem::link("home", "Home").active(true))    .item(NavigationMenuItem::flyout("product", "Product", |menu, _, _| {        menu.item(PopupMenuItem::new("Features").on_click(|_, _, _| {}))            .item(PopupMenuItem::new("Pricing").on_click(|_, _, _| {}))    }))    .item(NavigationMenuItem::link("docs", "Docs"));
ConstructorBehavior
NavigationMenuItem::linkImmediate action / route — optional .active(true) / .on_click
NavigationMenuItem::flyoutTrigger + dropdown built with PopupMenuItems

New primitives (batch)

Gallery: Badges & Avatars / Feedback.

PrimitiveRole
Chip / Pill / CountLabelTags, filter pills, numeric counts
StatusDot / Pin / DisclosurePresence, pin, expand chevron
Initials / ColorSwatch / FileGlyphIdentity / colour / file-type tiles
InlineCode / Quote / Truncate / CaptionText atoms
RelativeTime / IconLabel / HotkeyHintMeta labels
Meter / Scrubber / SignalBars / BatteryLevel indicators
NumberStepper / Fab / AccentRail / ScrimControls / chrome atoms
rust
use celestia_ui::components::primitive::chip::Chip;use celestia_ui::components::primitive::initials::Initials;use celestia_ui::components::primitive::number_stepper::NumberStepper;use celestia_ui::components::primitive::meter::Meter;
Chip::new("lang", "Rust").outline().selected(true);Initials::new("av", "Ada Lovelace");NumberStepper::new("qty", 3).range(0, 10);Meter::new("disk").label("Disk").value(0.75);

App composites

Presentational shells under components/composite/. The host owns Entity state; builders take callbacks and element slots.

CompositeUse for
AppHeader / Toolbar / ActionBar / PageHeaderChrome strips
StartBar / StartBarItem / SystemTray / StartMenuWindows-style taskbar (start · apps · tray · menu)
Window / AppMenu / open_windowWindow shell + menu via composite/window. Compose TrafficLights / tabs in title_bar(…)
Wizard / SideNav / WorkspaceSwitcher / UserMenuFlows & nav
ButtonGroup / InputGroup / TagInput / FindReplaceBarGrouped controls
Panel / PropertyList / ProjectCard / IssueCardContent cards
TaskList / Checklist / ConversationList / NotificationCenterLists
ActivityFeed / CommentThread / MediaObjectSocial / activity
TerminalPanel / LogViewer / OutlineView / CodeBlockDev surfaces
CommitRow / BulkActionBar / ChatComposerVCS / compose
CommandPaletteSpotlight-style searchable actions
BreadcrumbBarPath trail with overflow ellipsis
TabBarDocument tabs (module path only — see above)
SplitPaneFixed-ratio dual pane
ResizablePanelGroup / resizable_panelDrag-resizable panels (primitive/resizable)
ResizableSplitTwo-pane drag split (composite/resizable_split)
InspectorPanelProperty sheet rows
FormFieldLabel + control + hint/error
SettingsPage / SettingsSectionSectioned settings surface
SearchFieldSearch chrome + clear + chips
FilterBar / FilterChipToolbar filter chips
KeybindingRecorderShortcut capture + conflict
DataTableToolbarSearch + selection + bulk actions
FileBrowserBreadcrumb + tree + list chrome
FileTree / FileTreeFolder / FileTreeFileWorkspace directory hierarchy (web ai/file-tree.tsx)
KanbanBoard / KanbanColumn / KanbanCardColumns of cards with KanbanCardDrag DnD; accent wash and insert line before release
TimelineVertical event stream
StatCard / StatCardGroupMetric tiles (+ sparkline)
Modal / ModalHeader / ModalTitle / ModalDescription / ModalBody / ModalFooterCentered modal chrome (web dialog.tsx)
SheetPanel / SheetHeader / SheetFooterSide sheet chrome (web sheet.tsx)
Drawer / DrawerHandleEdge drawer + swipe handle (web drawer.tsx)
ConfirmDialogCancel / confirm panel
ToastStack / ToastItemIn-view toast host
BannerPersistent callout strip
OnboardingTipCoachmark with step progress
DragHandle / DropIndicatorGrip affordance + insert line (primitive/)
ItemDrag / ItemDragPreview / Draggable / DropTargetTyped drag payload, ghost, and a target that highlights before release (composite/dnd)
SortableList / SortableItemReorderable rows; insertion line and accent wash before on_reorder
DropZoneFile / item zone — live ExternalPaths with a “Release to drop” hint, optional forced states
rust
use celestia_ui::components::composite::drawer::Drawer;use celestia_ui::components::composite::file_tree::{FileTree, FileTreeFile, FileTreeFolder};use celestia_ui::components::composite::modal::{Modal, ModalSize};use celestia_ui::components::composite::sheet_panel::{SheetPanel, SheetSide};
Modal::new("edit-profile")    .title("Edit profile")    .description("Update how your name appears across Celestia.")    .size(ModalSize::Medium)    .child(/* body */)    .footer(/* actions */)    .on_close(|_, _, _| { /* close */ });
SheetPanel::new("inspector")    .side(SheetSide::Right)    .title("Inspector")    .description("Edit the selection.")    .child(/* body */)    .on_close(|_, _, _| { /* close */ });
Drawer::new("share")    .title("Share")    .child(/* body */)    .footer(/* actions */);
FileTree::new("workspace")    .child(        FileTreeFolder::new("apps", "apps")            .expanded(true)            .child(                FileTreeFolder::new("apps/api", "api")                    .expanded(true)                    .child(FileTreeFile::new("apps/api/src/index.ts", "index.ts").selected(true)),            ),    );

Window chrome

Window is a shell: OS options, a drag title band, and a body. It does not draw traffic lights, tabs, or toolbars — put those in title_bar(…) (TrafficLights, TabBar, buttons, …). Use WindowChrome::Embedded for framed previews and WindowChrome::Native for a real OS root. On macOS, AppKit draws the lights in Native mode — do not stack a second set. Pad custom title-bar content with TITLE_LEADING_PAD / TRAFFIC_INSET.

open_window opens another OS window with those options. bind_window_keys maps New (⌘N), Close (⌘W), Minimize (⌘M), and Enter Full Screen (⌃⌘F).

AppMenu::standard(app_name) starts with the app name, File, Edit, View, and Help. menu / item / without customize it; install(cx) sends the list to cx.set_menus.

rust
use celestia_ui::components::composite::window::{    self as app_window, AppMenu, WindowChrome, bind_window_keys, open_window,};
app_window::Window::new("app", "Celestia")    .chrome(WindowChrome::Native)    .title_bar(/* TrafficLights / tabs / actions */)    .child(/* body */);
AppMenu::standard("Celestia").install(cx);bind_window_keys(cx);open_window("Untitled", cx, |window, cx| { /* Root::new(...) */ });

Modal / SheetPanel / Drawer are presentational. For a live window overlay, use window.open_dialog (primitive::dialog) or window.open_sheet (primitive::sheet) — gpui-component hosts. FileTree is presentational — pass expanded / selected and on_toggle / on_select from host state (web ai/file-tree.tsx twin). ToastStack is an in-view host; window.push_notification drives the Root notification layer and attaches an always-visible close button — see Desktop (GPUI).

Drag and drop

GPUI drag sessions are type-keyed. Use [ItemDrag] for list transfers, [KanbanCardDrag] for boards, and ExternalPaths (via DropZone::on_files) for OS file drops. A valid hover highlights the destination before release: an accent wash (paint_drop_over), an insertion line (drag_insertion_line), and a “Release to drop” or “Drop here” caption (drop_release_hint). Web twins live at @celestia-project/ui/composite/drop-zone and @celestia-project/ui/composite/sortable-list (dnd-kit).

rust
use celestia_ui::components::composite::dnd::{    reorder_vec, DropTarget, Draggable, ItemDrag,};use celestia_ui::components::composite::drop_zone::DropZone;use celestia_ui::components::composite::kanban_board::{    apply_kanban_drop, KanbanCard, KanbanColumn, KanbanDrop,};use celestia_ui::components::composite::sortable_list::{SortableItem, SortableList};
DropZone::new("uploads")    .on_files(|paths, _, _| { /* Vec<PathBuf> */ });
Draggable::new("src", ItemDrag::new("board", "card-1", 0, "Ship it"))    .child(/* label */);
DropTarget::new("lane")    .on_item_drop(|drag, _, _| { /* accept ItemDrag */ });
SortableItem::new("row-0", "tasks", "a", 0, "Inbox")    .on_reorder(|&(from, to), _, cx| {        // host: reorder_vec(&mut items, from, to); cx.notify();    });
KanbanColumn::new("todo", "To do")    .board_id("sprint")    .on_card_drop(|drop: &KanbanDrop, _, _| {        // apply_kanban_drop(&mut columns, drop, |c| c.id.clone());    })    .card(        KanbanCard::new("c1", "Ship it")            .draggable("sprint", "todo", "c1", 0)            .on_card_drop(|drop, _, _| { /* insert before */ }),    );

Environment variables

None. Theme tokens are compile-time embedded (theme.json). Secrets stay in apps/api/.env for the web stack — desktop showcase needs no API keys.


  • Desktop (GPUI) — architecture, theming, full component map
  • Desktop State — StoreHandle / StoreContext
  • Desktop Bridge — HTTP client from GPUI to apps/api
  • Gallery Visual toolbar — click-to-copy primitive/… / composite/… package chips beside the Visual/Docs toggle; Docs tab still has props and usage snippets