## Break Change
- The `Root::render_notification_layer`, `Root::render_sheet_layer`,
`Root::render_dialog_layer` has been removed, we don't need it now, the
Root element has default rendered them.
- Add `open`, `on_open_change` method to control open state.
- Add `default_open` method.
## Break Change
This PR to rewrite the API of Popover API to make it easy to use.
- The `content` method now can receive an element directly.
```diff
- .content(|window, cx| {
- cx.new(|cx| {
- PopoverContent::new(window, cx, |_, _| {
- div().child("This popover content.")
- })
- })
- })
+ .content(|state, window, cx| {
+ div().child("This popover content.")
+ })
```
- And you can also just use `child` and `children` to add child
elements.
```rs
Popover::new("my-popover")
.trigger(Button::new("trigger").label("Open Popover"))
.child("This popover content.")
```
- Removed `PopoverContent`, and changed `Popover` default paddings to
`p_3`.
## Breaking change
```diff
- Tab::new("Account")
+ Tab::new().label("Account")
```
We can currently create a tab item without a label, such as only an
icon.
Adds the ability to choose between a linear and a logarithmic scale for
the slider. A logarithmic scale is the right and intuitive choice for
many different slider applications. Building this right into the
component has two advantages:
- The user doesn't have to convert at every point where they might use
or update the slider value
- On a logarithmic scale, the distance between steps varies over the
sliders range. This implementation respects that
---------
Co-authored-by: Jason Lee <huacnlee@gmail.com>
When `on_click` is not specified on the button group, this PR allows for
specifying on_click on each individual button.
e.g.
```rs
let buttons = ButtonGroup::new(("buttons", index)).layout(Axis::Vertical)
.child(Button::new(("install", index)).label("Install").icon(download_icon).success().on_click(move |_, _, cx| {
// Install
})
.child(Button::new(("open", index)).label("Open Page").icon(IconName::Globe).info().on_click(move |_, _, cx| {
// Open page
}));
```
This pull request adds a drag event to the slider bar itself, analogous
to the drag handles, in case the slider is not a range slider. This
allows for a more intuitive interaction with the slider, where one can
click and drag on any part of the slider bar container.
https://github.com/user-attachments/assets/4d41ba10-2992-4a42-beb6-0bc376185c5e
This doesn't change the behavior for range sliders, as it's ambiguous
which slider should be dragged in case the event starts between the
sliders. Though if desired, it wouldn't be difficult to implement the
same behavior for clicks below the start or above the end slider.
Adds a vertical option to the button group
Also added an example of the vertical button group to the story
Also fixed button rounding only working when both tl/bl and tr/br are
both set, now they can be set individually
I'm not sure if the justify_center in `.when(self.vertical, |this|
this.flex_col().justify_center())` is needed, but I added it to match
the behaviour of `.items_center()` when the flex direction is row.
Currently, the bounds are zero on the first frame.
Therefore, the extra_rows_count will always be zero on the first frame.
This causes rendering issues for row border & row stripes.
Unfortunately, I don't know how to get the correct height on the first
frame to fix the row stripes.
However, an easy fix for the row border rendering issue is to disable
the table_is_filled check. The check is problematic anyways since the
border affects the height of the element.
I attached a video showing the issue, the border doesn't render on the
first frame and the height of the element also shifts due to the border:
https://github.com/user-attachments/assets/b154c686-cd9c-4b6b-8ba6-c79640297abc
---------
Co-authored-by: Jason Lee <huacnlee@gmail.com>
## Break Change
- Renamed `Drawer` to `Sheet`, also renamed relative method contains
`drawer` to `sheet`.
- Renamed `ContextModal` to `WindowExt`.
```diff
- use gpui_component::drawer::Drawer
+ use gpui_component::sheet::Sheet
- use gpui_component::ContextModal
+ use gpui_component::WindowExt
```
## Break Change
- The original `child` method has been renamed to `item`.
- Now the add new `child` to accept a `DescriptionItem` type like the
`children` method.
## Break Changes
- Refactor `Toggle` new API to require a `id`, and renamed `on_change`
to `on_click.
- Renamed `on_change` method to `on_click` for `ToggleGroup`.
```diff
- Toggle::label("Hello").id("hello").on_change(..)
+ Toggle::new("hello).label("Hello").on_click(..)
- ToggleGroup::new("group1).on_change(..)
+ ToggleGroup::new("group1).on_click(..)
```
https://github.com/longbridge/gpui-component/pull/1502#issuecomment-3485354324
## Break Change
- The `DatePicker` and `Select` has changed `cleanable` default from
`true` to `false`.
- Updated `Input`, `DatePicker` and `Select` the `cleanable` to have a
argument.
```diff
- pub fn cleanable(mut self)
+ pub fn cleanable(mut self, cleanable: bool)
```
---------
Co-authored-by: Jason Lee <huacnlee@gmail.com>
## Break Change
- Removed `set_query_input`, `query_input` and `no_query` method from
List. Now use `List::new(&state).searchable(false)` to intead of
`no_query`.
- Removed `searchable` method from `SelectDelegate`, use
`Select::searchable` instead.
In most applications, when text is selected and backspace/delete is
pressed, the selection is simply deleted.
Currently, gpui-component will ignore the selected text and delete the
previous/next word when Ctrl is pressed.
This can be very confusing especially if you do ctrl+a then
ctrl+backspace, expecting the input to be cleared.
This PR makes it so that if there is an active selection, the active
selection is deleted before doing any extra processing.
This to fix#1468, #1449 changes broken the state (e.g.: `SelectState`
to call `cx.notify()`) update not notify parent element (e.g.: `Select`)
to rerender.
## Break Change
- Like Select, Input API design, now `List`, `Table` also has ListState
and TableState.
```diff
- let table = cx.new(|_| Table::new(delegate, window, cx).stripe(true).border(true))
+ let table = cx.new(|_| TableState::new(delegate, window, cx))
+ Table::new(&table).stripe(true).border(true) // for render
- let list = cx.new(|_| List::new(delegate, window, cx))
+ let list = cx.new(|_| ListState::new(delegate, window, cx))
+ List::new(&list) // for render
```
- ListDelegate, TableDelegate methods has changed some argument type:
- If first argument is `&mut self`, the `cx` has been changed to `&mut
Context<ListState<Self>>` or `&mut Context<TableState<Self>>`.
```diff
- fn confirm(&mut self, _secondary: bool, _: &mut Window, cx: &mut
Context<List<Self>>)
+ fn confirm(&mut self, _secondary: bool, _: &mut Window, cx: &mut
Context<ListState<Self>>)
```
- If first argument is `&self`, the `cx` has been changed to `&mut App`.
```diff
- fn render_item(&self, ix: IndexPath, _: &mut Window, _: &mut
Context<List<Self>>)
+ fn render_item(&self, ix: IndexPath, _: &mut Window, _: &mut App)
```
## Break Change
- Removed `Kbd` from root export, use `gpui_component::kbd::Kbd`
instead.
- Removed `List` from root export, use `gpui_component::list::*`
instead.
- Removed `Tree` from root export, use `gpui_component::tree:*` instead.
Now we can be easy to add a menu item without define `action`, just use
`on_click` callback.
## Break Changes
- Removed complex methods: `menu_element_with_check_and_disabled`,
`menu_element_with_icon_and_disabled`, you can use `item` and
`PopupMenuItem` instead.
## Problem
When scrolling quickly, a distracting background color was appearing in
the scrollbar area, as shown below:
<img
src="https://github.com/user-attachments/assets/c9ebe200-a6b2-47ca-992d-ceaa5ed3db04">
## Root Cause
Several theme files had scrollbar background colors configured with
partial transparency (alpha values like `33`, `44`, `80`, etc.) instead
of full transparency (`00`). This caused a semi-transparent background
to become visible during fast scrolling, creating an unwanted visual
artifact.
## Solution
Updated all `scrollbar.background` color values to use full transparency
by setting the alpha channel to `00`. This ensures the scrollbar
background is completely invisible across all themes, with only the
scrollbar thumb (the draggable indicator) remaining visible.
## Changes
Modified 7 theme files with non-transparent scrollbar backgrounds:
- **alduin.json**: `#28282833` → `#28282800`
- **hybrid.json**: `#E0E0E044` → `#E0E0E000`, `#1D1F2144` → `#1D1F2100`
- **jellybeans.json**: `#26262633` → `#26262600`
- **mellifluous.json**: `#fafafa33` → `#fafafa00`
- **molokai.json**: `#FEFAF933` → `#FEFAF900`
- **solarized.json**: `#EEE8D533` → `#EEE8D500`, `#002b3633` →
`#002b3600`
- **default-theme.json**: `#fafafa80` → `#fafafa00`, `#17171780` →
`#17171700`
All other themes already had transparent scrollbar backgrounds and
remain unchanged.
## Impact
- Eliminates the visual glitch where a background color appears during
fast scrolling
- Provides a consistent, clean scrollbar appearance across all 20 themes
plus the default theme
- No functional changes to scrollbar behavior - only visual appearance
- Total of 37 scrollbar.background entries now fully transparent
Fixes the issue reported by @huacnlee in the comments.
<issue_title>A different background color appears when the scroll bar
scrolls quickly</issue_title>
><issue_description>A different background color appears when the
scroll bar scrolls quickly
>
><img
src="https://github.com/user-attachments/assets/c9ebe200-a6b2-47ca-992d-ceaa5ed3db04"></issue_description>
>
><agent_instructions>Make change to update scrollbar background color
to transparent for all themes.</agent_instructions>
>
> ## Comments on the Issue (you are @copilot in this section)
>
><comments>
><comment_new><author>@huacnlee</author>
> That is the scrollbar background color. I am still on to consider
removing that color.</comment_new>
></comments>
>
Fixeslongbridge/gpui-component#1402
<!-- START COPILOT CODING AGENT SUFFIX -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>A different background color appears when the scroll bar
scrolls quickly</issue_title>
> <issue_description>A different background color appears when the
scroll bar scrolls quickly
>
> <img width="1338" height="387" alt="Image"
src="https://github.com/user-attachments/assets/c9ebe200-a6b2-47ca-992d-ceaa5ed3db04"
/></issue_description>
>
> <agent_instructions>Make change to update scrollbar background color
to transparent for all themes.</agent_instructions>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> <comment_new><author>@huacnlee</author><body>
> That is the scrollbar background color. I am still on to consider
removing that color.</body></comment_new>
> </comments>
>
</details>
Fixeslongbridge/gpui-component#1402
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute
survey](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: huacnlee <5518+huacnlee@users.noreply.github.com>
- Fixed VirtualList example the horizontal scrollbar not appear.
- Fixed VirtualList example scroll slow, this because not call
`self.scroll_handle.set_offset` to update.
## Problem
SyntaxHighlighter panics when highlighting text containing multi-byte
UTF-8 characters (Chinese, Japanese, emoji, etc).
**Error**: `thread 'main' panicked at
ropey-2.0.0-beta.1/src/rope.rs:694:59`
**Root Cause**: tree-sitter may return byte offsets that fall in the
middle of multi-byte UTF-8 characters. When `Rope::slice()` receives
such offsets, it checks `is_char_boundary()` and panics with
`NonCharBoundary` error.
## Solution
Use `RopeExt::clip_offset()` to adjust byte offsets to the nearest char
boundaries before calling `slice()`.
**Changes**:
- Line 395-398: Clip offsets in main highlighting loop
- Line 441-443: Clip offsets when extracting injection content
- Line 478-479: Added explanatory comment for injection highlighting
- Added imports for `RopeExt` and `Bias`
**Safety**:
- `Bias::Left` for start offset ensures we don't skip the beginning of a
character
- `Bias::Right` for end offset ensures we include the full character
- The adjustment is minimal (at most 3 bytes for UTF-8)
- Uses existing project API (`RopeExt::clip_offset`)
## Testing
Tested with:
- ✅ Chinese text: "你好世界"、"**加粗中文**"
- ✅ Japanese text: "こんにちは"、"日本語"
- ✅ Emoji: "😀🎉✨🚀"
- ✅ Markdown syntax highlighting with CJK characters
- ✅ Code blocks with mixed languages
All tests pass without panics. Syntax highlighting works correctly. No
performance degradation observed.
## AI Assistance
🤖 This fix was developed with AI assistance (Claude). The solution
approach (using `clip_offset`) was identified through code analysis and
testing. The AI analyzed:
- ropey source code to understand the panic condition
- Existing usage of `clip_offset` in the codebase
- Tree-sitter byte offset behavior with UTF-8
All code has been reviewed and tested by humans, and further validated
by Gemini AI.
## Checklist
- [x] Follows existing code style
- [x] One PR does one thing (UTF-8 panic fix only)
- [x] All manual tests pass
- [x] Tested with real multi-byte UTF-8 content
- [x] No breaking changes
- [x] No performance regression