Repository navigation
Releases
gpuicn v0.9.9
gpuicn v0.9.9
Two fixes found while checking v0.9.8 in Matty. The runtime pins are
unchanged: GPUI Kit 0.9.0 on gpui-pre 0.3.11 and gpui-icons 0.6.0.
Fixed
- An
Attachmentimage sized withdimensionsshrinks with a narrow
column and keeps its aspect ratio, instead of running past the column's
edge (matty#683). A message's content also lets wide children shrink
with the row. ShowMore's chevron takes the link color; it was drawn invisible.
gpuicn v0.9.8
gpuicn v0.9.8
Paper-cuts from Matty's jank review (matty#670): overlays close on Escape
even inside an app's own Escape shortcut, the lightbox closes on a click
around the image and stops blowing small images up, sidebar rows get a
label suffix for status emoji, and Markdown code blocks get a copy button.
Long content can collapse behind Show more. The runtime pins are
unchanged: GPUI Kit 0.9.0 on gpui-pre 0.3.11 and gpui-icons 0.6.0.
Added
SidebarItem::label_suffixputs content right after the label, such
as a person's status emoji, where a hover-revealed options button never
covers it. A long label truncates before it.
label_suffix_tooltipshows the text behind it and is read after the
label.- Code block copy button: every
markdown()code block shows its
language and a copy button that copies the raw code (no fences) and
confirms with a check. collapsible::ShowMoreclips content taller than 600 px
(max_height) behind a fade with Show more / Show less. It is
uncontrolled, or controlled withexpandedandon_toggle;
fade_colormatches the surface behind it.LightboxImage::dimensions(width, height): with a known size, the
lightbox fits large images down and shows small ones at their natural
size.primitives::dismiss::on_escapefor your own overlays, theDismiss
action and theDISMISS_CONTEXTkey context.- Strings:
UiText::CopyCode,UiText::Copied.
Fixed
- Escape closes dialogs, popovers, hover cards, menus and the lightbox
when the app binds Escape in a context around them. GPUI runs key
bindings before key listeners, so an app shortcut such as "mark read"
used to take the key first. Escape is now theDismissaction, bound
in the overlay's own, deeper key context. - The lightbox closes on a click on the dark area around a fitted image.
Upgrading
LightboxImage has a new public field, size; code that builds it with
a struct literal adds size: None. Copied components pick these changes
up with gpuicn diff.
gpuicn v0.9.7
gpuicn v0.9.7
Matty's request for translated and right-to-left interfaces (#143, for
matty#225): every built-in string can be replaced without forking a
component, and one setting lays the whole library out right to left. Also a
one-line Sortable bookmarks bar with its own gap (#145), pasted one-time
codes (#147), menus that rebuild from their own clicks (#148) and animated
GIFs (#149). The
runtime pins are unchanged: GPUI Kit 0.9.0 on gpui-pre 0.3.11 and
gpui-icons 0.6.0.
Added
- Strings (
theme::locale):UiTextnames each string a component
writes for you, including the emoji picker's search, section, category
and skin tone names, "Close" on dialogs, drawers, toasts and the
lightbox, empty and loading states, the calendar's months, weekdays and
dates, AM/PM, the text edit menu, and accessible labels and
announcements.locale::set_strings(cx, |text| ...)returns a
translation orNonefor English;UiText::key()is a stable id for a
catalog andUiText::english()the default. - Direction:
locale::set_direction(cx, Direction::Rtl)mirrors rows,
start and end paddings, insets, borders and corners; flips chevrons and
arrows; opens menus, submenus, popovers, date pickers and composer
suggestions from the mirrored edge; moves left drawers, sidebars and
toast stacks to the right; aligns single-line inputs, popovers and
tooltips to the start; and makes Left and Right (with their word and
selection variants) follow the reading direction in inputs, menus,
menubars, tabs, toolbars, toggle groups, the calendar, the emoji picker,
the lightbox and sortable lists.theme.directioncarries it and
survivesUiTheme::setandUiTheme::switch. - Bidi isolation:
locale::isolate(text)wraps a name in
first-strong isolates so mixed-script text keeps its order; a quoted
message's "Originally posted in" uses it. - Helpers:
DirectionalStyled(flex_row_in,mirror_row,
padding_start/end,inset_start/end,border_start_1,
rounded_start/end, ...) andDirection::{placement, align, anchor, icon, logical_key}for your own views.DrawerSide,SidebarSideand
ToastPlacementgainphysical(direction). - Sortable:
wrap(false)keeps a horizontal list on one line at its
content width, so a bookmarks bar scrolls sideways instead of wrapping,
andgap(px)sets the space between items, with the drop line centred in
it (#145). - OtpField paste: Cmd/Ctrl+V, or a
Pasteaction from the Edit menu,
fills the cells from the clipboard's digits, ignoring spaces, dashes and
other text. A full code replaces the value and a partial one continues
it; the state emitsChange, thenCompletewhen the last cell fills.
otp_field::paste_code(&state, text, window, cx)does the same from your
own code (#147). - Animated pictures:
primitives::image(source)is animgwith an id
derived from its source, which GPUI needs before it advances GIF and WebP
frames.clipped_image,round_image, attachments, avatars, custom
emoji in Markdown, messages and the emoji picker, Markdown pictures and
the lightbox use it, so animated pictures play instead of showing their
first frame. Reduced motion still holds them still (#149). - Site:
?dir=rtlon a component page previews it right to left.
See Strings and layout direction.
Fixed
MenuItem::on_clickhandlers run after the menu closes and its state
update returns, so a handler can callMenuState::set_items, directly or
through your own entity, to redraw radio items. It used to panic with a
double lease (#148).
Changed
- Disabled calendar days, command items and sidebar items read
", Unavailable", matching the other unavailable controls, instead of
", unavailable".
Not mirrored yet
The carousel track, slider and multi-line text areas belong to GPUI Kit and
stay left to right. Shortcut key caps and the time field's segment order
stay left to right on purpose.
Compatibility
UiTheme gains a public direction field, so code that builds UiTheme
with a struct literal needs direction: Direction::Ltr. Nothing else
changes for left-to-right apps: every default string is the same English as
before.
Upgrading
cargo install gpuicn-cli --version 0.9.7 --locked
gpuicn update --to 0.9.7Crate users: cargo update -p gpuicn.
gpuicn v0.9.6
gpuicn v0.9.6
Fourteen requests from Matty: five new components for forms and scheduling,
interactive attachments, spelling, async selects, theme tokens and
multi-message copy. The runtime pins are unchanged: GPUI Kit 0.9.0 on
gpui-pre 0.3.11 and gpui-icons 0.6.0.
New components
- Alert: an inline callout with an icon, title, description and actions,
in Default, Destructive, Warning, Info and Success. Destructive and warning
alerts are announced (#135). - Avatar Group: overlapping avatars with a
+Nchip, a hover tooltip and
one accessible name that lists everyone (#140). - Calendar: a month grid with keyboard navigation, limits, today and a
configurable first weekday, over a smallCalendarDatetype (#140). - Date Picker: an outline trigger that opens a
Calendar(#140). - Date Time Picker: a calendar and a keyboard time field in one popover,
with a 12- or 24-hour clock from the locale and date-and-time limits (#138).
Added
- Avatar: a status dot on a resized avatar scales with it, and
status_sizesets it (#127). - Button:
tint(color)derives fill, hover and readable text from one
integration color (#134). - Command:
CommandItem::descriptionadds muted trailing text (#133). - Composer: triggers inside inline code and fenced blocks are ignored
(#129);open_suggestions(range, items)opens the popup for a range the
app chose, and accepting it emitsComposerEvent::AcceptRange(#136);
set_misspellingsunderlines words (#139). - Field:
Field::from_controltakes aCheckboxor aRadioGroup(#135). - Input: focused inputs and textareas take an opaque fill (#135);
Textarea::misspellingsdraws wavy underlines and a native suggestions
menu (#139). - Markdown:
MarkdownImages::placeholdershows a click-to-load
ImagePlaceholder, andimage_sizereserves an image's own box (#131). - Message:
copy_prefixmakes a copy across messages read one line per
message, such asAda [12:24]: Thanks!; a copy inside one message is
unchanged.selection_groupdoes the same for other regions (#128). - MessageEmbed:
actionadds buttons and selects inside the card (#134). - Select:
set_itemsreplaces items while open for async results,
keeping the query, value and highlight;set_loadingshows a status row
(#141). - Sidebar:
sidebar_mention_badge, and group labels use the new muted
token (#132). - Sortable:
horizontal()for rows of chips, with a vertical drop line
and Alt+Shift+Left and Right (#137). - Theme: optional
link,mention_highlight,
mention_highlight_foreground,new_messages,sidebar_muted_foreground,
sidebar_badgeandsidebar_badge_foregroundtokens that fall back to
their base tokens, settable from shadcn CSS;UiTheme::code_themepicks a
code block palette (GitHub, Monokai, Solarized);UiTheme::opaque(#132).
Compatibility
UiColors and CommandItem gain public fields and ComposerEvent gains a
variant. Code that builds UiColors or CommandItem with a struct literal,
or matches ComposerEvent without a wildcard, needs the new fields (they
are None) or arm. FieldControl::field_focus_handle now has a default.
Upgrading
cargo install gpuicn-cli --version 0.9.6 --locked
gpuicn update --to 0.9.6Crate users: cargo update -p gpuicn.
gpuicn v0.9.5
gpuicn v0.9.5
Two more requests from Matty: a clickable author name on messages, and toasts
that tell the app when they leave. Everything is additive; the runtime pins
are unchanged: GPUI Kit 0.9.0 on gpui-pre 0.3.11 and gpui-icons 0.6.0.
Added
- Message: the author name can open something, such as a profile card
(#123).on_author_click(handler)draws the name as a link-styled button that
takes focus with Tab, runs on click, Enter or Space and underlines on
hover. The handler gets the click and the name's bounds in window
coordinates, to anchor a popover.author_element(element)puts your own element in place of the name,
such as thehover_popovertrigger you already wrap around the avatar.
It inherits the name's weight, size and color.- Both work in standard and compact density.
- Toast: the app can follow a notification's life (#122).
Notice::on_dismiss(handler)runs once when a notice starts to leave,
with aNoticeDismissal:Action,Cancel(such as "Later"),Close,
Timeout,Dismissed(the app calleddismiss) orReplaced(the app
pushed the same ID again).NoticeDismissal::by_readeris true for Cancel
and Close. The handler runs after the current update, so it may push
again.ToastState::contains(id)says whether a notice is queued or on screen
and not already leaving.ToastState::set_placement(ToastPlacement)moves the stack to any corner
or the middle of the top or bottom edge (bottom left stays the default),
andset_inset(horizontal, vertical)sets its distance from the window
edges, such as to clear a composer.
Upgrading
cargo install gpuicn-cli --version 0.9.5 --locked
gpuicn update --to 0.9.5Crate users: cargo update -p gpuicn.
gpuicn v0.9.4
gpuicn v0.9.4
Six requests from Matty: presence dots on avatars, menus that fit the window,
hover profile cards, quoted posts, accessible-name audits and sortable lists.
Everything is additive; the runtime pins are unchanged: GPUI Kit 0.9.0 on
gpui-pre 0.3.11 and gpui-icons 0.6.0.
Added
- Avatar:
status(AvatarStatus)draws a presence dot over the
bottom-right corner, sized to the avatar (8, 10 or 12px), in a ring of the
surface color (#115).- Online, Away and Dnd use the
success,warninganddestructive
tokens; Offline is a hollow ring;Custom { color, label }covers the
rest. Set the tokens in your theme to restyle every dot. ring_colormatches the ring to the surface, such ascolors.sidebar.- The status joins the accessible name: "Ana Lopez, away".
- Online, Away and Dnd use the
- Popover:
hover_popover(id, label)for profile cards (#118).- The trigger is one focusable button named
label, usable on an avatar
or an inline name;trigger_style_withrestyles it. - Resting the pointer opens it after 500ms without moving focus, and the
pointer can move into the card; it closes 300ms after the pointer leaves
(open_on_hoversets both delays). - Click, Enter or Space opens it with focus inside, and it stays until
Escape (which returns focus to the trigger), a click outside or the
trigger again.
- The trigger is one focusable button named
- Embed:
QuotedMessagefor pasted permalinks and forwards (#119): the
author's avatar, name and time with a Jump link, "Originally posted in
{channel}", a body clamped to four lines with "Show more", and an optional
attachment strip. It takesdensitylike the other embeds and is named
"Quoted message from {author}". - Sortable, a new component (#121):
Sortable,SortableGroupand
SortableRow.- Dragging a row shows a copy of it at reduced opacity and a line where it
will land; an empty or collapsed group highlights and takes the row at
its end. - With focus in a row, Alt+Shift+Up and Down move it one place, across
groups at either end, and announce the new position. reorder_groups(true)lets headers drag and take the same keys.on_reorderreports aSortableMove;SortableMove::applyand
move_groupapply moves to your data. Nothing animates.
- Dragging a row shows a copy of it at reduced opacity and a line where it
- Accessibility audits (#120): with the new
test-supportfeature,
gpuicn::audit::unnamed_controls(window)lists interactive elements with
no accessible name in the last frame, andA11yAuditOverlayoutlines them
in the destructive color and logs their paths. Buttons, toggles,
checkboxes, switches, selects, menus, tabs, inputs, the composer, sidebar
rows and dialog triggers already registered; this release adds the
remaining interactive parts.
Changed
- Menu panels are as tall as the window allows before they scroll, on
whichever side of the trigger has more room; context menus and submenus
use the window height. Submenus open after 200ms of hover, and a sibling
row closes them (#117).
Upgrading
cargo install gpuicn-cli --version 0.9.4 --locked
gpuicn update --to 0.9.4Crate users: cargo update -p gpuicn. For the audit, enable the feature in
your dev-dependencies: gpuicn = { version = "0.9.4", features = ["test-support"] }.
gpuicn v0.9.3
gpuicn v0.9.3
A request from Matty: embed cards can hold a click-to-load placeholder where
their image goes, for applications that ask before loading remote images.
The change is additive; the runtime pins are unchanged: GPUI Kit 0.9.0 on
gpui-pre 0.3.11 and gpui-icons 0.6.0.
Added
- Embed:
ImagePlaceholder, passed toLinkPreview::image_placeholder,
MessageEmbed::image_placeholderorMessageEmbed::thumbnail_placeholder
in place of the image (#111).- It takes the box the image would take, so nothing moves when the image
loads: the card's inner width at the pixel size from.size(w, h), or
16:9 when the size is unknown, large below the text or a thumbnail on the
right by the same rules asimage. - A muted surface shows the image icon, "Load image" and the host, and
fills with the accent color on hover. - It is a button named "Load image from {host}" with the focus ring. Click,
Enter or Space runson_loadonce; fetch the image there and pass it
throughimageon the next render.labelandaria_labelreplace the
text, such as for a translation.
- It takes the box the image would take, so nothing moves when the image
Upgrading
cargo install gpuicn-cli --version 0.9.3 --locked
gpuicn update --to 0.9.3Crate users: cargo update -p gpuicn.
gpuicn v0.9.2
gpuicn v0.9.2
Two requests from Matty: a new Embed component for cards under a chat
message, and dialogs taller than the window now scroll under the pointer.
Every change is additive; the runtime pins are unchanged: GPUI Kit 0.9.0 on
gpui-pre 0.3.11 and gpui-icons 0.6.0.
Added
- Embed (
gpuicn add embed): two cards for what a chat server attaches to
a post (#108).LinkPreviewis an Open Graph preview: a muted site name, a title that
wraps to two lines and opens the link, a description clamped to three
lines, and an image. The image is large below the text, or a thumbnail
on the right when it is narrower than 150px or roughly square. The caller
passes its pixel size, so the card holds the box before the bytes load.
on_toggleadds a "Collapse preview" / "Expand preview" chevron that
hides the image, andon_removeadds a "Remove preview" button.MessageEmbedis the integration attachment: a colored bar (any color,
a hex string, orgood,warninganddanger), pretext, an author line
with an icon, a title, a body slot for your Markdown, a fields grid with
short fields two to a row, an image, a thumbnail and a footer with an
icon and a time.- Both follow
MessageDensity, read every color from the theme, and are
groups named by their title. Linked titles take focus and open on Enter.
Fixed
- Dialog: a dialog taller than the window scrolls with the wheel over its
surface.dialog_popupusedocclude(), which hid the wheel from the
dialog's scrolling viewport, so the content stayed clipped. It now uses
block_mouse_except_scroll(): clicks and hover still stop at the surface,
and the backdrop still keeps the wheel from the page behind. If you built
your own surface fordialogwithocclude(), switch it the same way
(#109).
Upgrading
cargo install gpuicn-cli --version 0.9.2 --locked
gpuicn update --to 0.9.2Crate users: cargo update -p gpuicn.
gpuicn v0.9.1
gpuicn v0.9.1
Seven additions Matty asked for while building its chat client. Every change
is additive: existing code compiles and draws as before, and installed source
from v0.9.0 changes only in the files below (gpuicn diff lists them). The
runtime pins are unchanged: GPUI Kit 0.9.0 on gpui-pre 0.3.11 and gpui-icons
0.6.0.
Added
- Message:
timestamp_tooltipshows the full date and time when the
reader hovers the time, on header, continuation and compact rows (#94).
on_gutter_clickmakes the avatar column a click target for selecting the
row, while clicks on the body stay free for text selection and links (#98). - Toast:
Notice::action(label, handler)andNotice::cancel(label)add
buttons to a notification. Pressing either dismisses it; the action runs its
handler first. Tab reaches them before the close button, and hovering the
stack pauses the timeout as before (#95). - MessageList:
on_jumphands the "Jump to latest" pill to the
application, which can load the newest page after a jump into the past
instead of scrolling to the loaded end.show_jump(true)keeps the pill
visible at the loaded end of such a window (#97). - Attachment:
dimensions(width, height)sizes an image thumbnail from the
file's metadata, keeping its aspect ratio insidemax_size(360 × 240 by
default), and holds that box with a muted fill while the image loads, so the
transcript does not jump (#99). - Sidebar:
roving_focus(true)makes the navigation rows one tab stop.
Tab lands on the focused or selected row and the next Tab leaves; Up and
Down move between rows (wrapping, skipping disabled ones), Home and End jump
to the ends, and the focused row scrolls into view (#100). - Composer:
ComposerState::set_max_rowsandmax_rowschange the growth
cap without rebuilding the state, andComposer::max_height(px)caps the
text box in pixels, for example at 40% of the window, following resizes and
keeping the draft (#101). - ScrollArea:
track_scroll(&ScrollHandle)shares the scroll position
with the caller.
Upgrading
cargo install gpuicn-cli --version 0.9.1 --locked
gpuicn update --to 0.9.1Crate users: cargo update -p gpuicn.
gpuicn v0.9.0
gpuicn v0.9.0
The consistency release. Every component now reads every color, size, radius,
shadow and duration from the theme through one set of primitives, a lint keeps
it that way, and setting up a themed app is one command. The runtime pins are
unchanged: GPUI Kit 0.9.0 on gpui-pre 0.3.11 and gpui-icons 0.6.0.
One-command setup
cargo install gpuicn-cli --version 0.9.0 --locked
gpuicn new my-app --base-color zinc --appearance dark
cd my-app && cargo runui::app::run: the startermain.rsis a window title, a theme pair and
a root view.app::run(AppConfig::new("My App").themes(light, dark), |_, _| Root)
opens the platform with the Lucide assets, loads the bundled Geist fonts,
installs the pair, follows the operating system's appearance and opens the
window.AppConfig::installdoes the same without a window.- Theme pairs:
UiTheme::set_pair,UiTheme::set_appearancewith
Appearance::{Light, Dark, System}, andtheme::follow_system. - shadcn's base colors:
gpuicn initwritesassets/theme.cssfrom
neutral, stone, zinc, mauve, olive, mist or taupe and generates
src/ui/app_theme.rs.gpuicn theme set --base-color stone --radius 0.5rem --appearance darkchanges it;gpuicn theme import theme.csstakes a theme
from the shadcn theme editor or tweakcn;gpuicn theme previewprints every
token. gpuicn updatemoves an app to a release (config, Cargo pins, fonts,
theme files, thenstatus);gpuicn doctorchecks everything against
the CLI's release and prints a fix per finding;gpuicn remove --unused
deletes components your code never names.gpuicn.tomlis version 2 with a[theme]table. Version 1 files still load.
Consistent by construction
- Primitives in
theme::primitives:text,icon,focus,disabled,
surface,control,clipped_image,dismiss,hover_reveal_in_group.
Every component uses them; the registry lint incrates/ui/testsfails a
change that reintroduces a raw color, text size, opacity, mode branch,
popover background, hover state, key glyph orpub(crate)item. - Focus is one 3px outer ring in
ringat 50%, shown through
focus_visible, on every control. - Tokens: status colors (
success,warning,info, with foregrounds),
destructive_foreground,typography(xs to xl with line heights),
breakpoints, and derivedcontrol()colors per mode. Every GPUI Kit
component color follows a token (a test enumerates them). - Motion:
transition_withandpresence_withover the theme durations,
UiEasing::Emphasized; dialogs fade in and no longer scroll themselves. - Input and Textarea share one
InputCore; Message hover actions use a group
instead of state.
Reliability
- MessageList has a position-based scrollbar (
MessageListScrollbar)
that estimates unmeasured rows from the measured average, so the thumb
tracks position in long histories instead of jumping as rows are measured
(#83). Passscrollbar_handleto share one across re-renders. - Toast schedules on deadlines instead of polling every 50ms.
- Tooltip installs one Escape interceptor for the application.
- Every component is drawn in both themes by a test; interactive ones have
behavior tests incrates/ui/tests/catalog.rs.
Message
density(MessageDensity::Compact): one line per post, the time always in
the gutter, the name inline, half the row padding (#88).
For contributors
- One preview file per component under
site/src/previews/;
cargo xtask new-component <name>scaffolds a component everywhere the
catalog expects one.docs/design.mdstates the rules. - Releases ship
catalog-screenshots-<tag>.zip, the light and dark sheet of
every preview, anddocs/screenshots/v0.9.0holds this release's.
Breaking changes
UiTheme::shadowsentries areArc<[BoxShadow]>; call.to_vec()where a
Vecis needed.sidebar_is_mobile(window, cx)takes the window.UiColors::slotis public but#[doc(hidden)].
Upgrading
cargo install gpuicn-cli --version 0.9.0 --locked --force
gpuicn update --to 0.9.0 # config, pins, fonts, theme files; then `gpuicn diff`
gpuicn update --overwrite # replace installed files once your edits are reviewed
gpuicn doctorApps that keep their own main.rs can switch to ui::app::run or keep
calling ui::theme::init(cx); doctor accepts both.



