1934 Commits
Author SHA1 Message Date
GintsandJason Lee 49d1bef84c input: Allow to set selection for input (#2545)
Closes #2544

## Description

PR to solve selection update on editor component.

## Screenshot


## Break Changes

No breaking changes

## How to Test

Execute test crates/ui/src/input/state.rs - test_set_selection

## Checklist

- [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
- [x] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
- [x] Passed `cargo run` for story tests related to the changes.
- [x] _Tested macOS_ 
- [ ] , Windows and Linux platforms performance (if the change is
platform-specific)

---------

Co-authored-by: Jason Lee <huacnlee@gmail.com>
2026-07-08 13:15:16 +00:00
Andreas JohanssonandJason Lee b3273963e9 input: Refactor wrap cache to SumTree and skip foreground parsing on large files (#2546)
## Description

These two changes are related, so I kept them in the same PR but
different commits, so it can be reviewed as two.
This is done to improve editing in larger files, but improving this also
improves overall editing.
After this change, editing the example json file is faster than Zed
(ironically since this uses their sum_tree crate πŸ˜„). Feels on par
with Vscode when forcefully enabled highlighting but uses less memory.

### Wrapping refactored to a SumTree

Replaces the `Vec<LineItem>` with a `SumTree<LineItem>`. The vec
required O(n) scans for things like row offset, cursor placement or
finding the longest line. With SumTree it becomes O(log n) or even O(1)
depending on if it's cached. Similar to diagnostics that is already
backed by a SumTree. One of the biggest wins is editing though since we
no longer need to splice the vec in text_wrapper
(`self.lines.splice(rows_range, new_lines);`)

Call sites that previously did `display_map.lines().get(row)` or
`.iter().enumerate()` now go through `display_map.line(row)` and
`buffer_line_to_display_row(row)`, which resolve via the tree.

### Skip foreground parsing on large files

For large files I've added a threshold for foreground parsing, since we
don't even need to attempt it if the file is big enough. It will always
time out anyway.. Also attempts to cancel any previous parses, this
could be further improved with a parse lock to absolutely prevent
parallel parsing.

## Screenshot

### Before


https://github.com/user-attachments/assets/58f94c40-a61c-4c8b-8562-fea428e6f17d

### After


https://github.com/user-attachments/assets/dc3f5ce7-b790-4835-8c49-0b514797b185

### Zed comparison



https://github.com/user-attachments/assets/5b7351fd-9f66-497f-a0d7-93eab827842f



This is with the document color provider and linting disabled in the
editor. Also folding is disabled because it still is a bit slow for
these huge files.

## How to Test

- Generate test file 
```sh
$ (echo '['; for i in $(seq 1 999999); do echo "{\"id\": $i, \"name\": \"user_$i\"},"; done; echo '{"id": 1000000, "name": "user_1000000"}'; echo ']') > big_test.json
```
- Disable/comment lint_document and document_color_provider in editor.rs
```diff
--- a/crates/story/examples/editor.rs
+++ b/crates/story/examples/editor.rs
@@ -713,7 +713,7 @@ impl Example {
             editor.lsp.code_action_providers = vec![lsp_store.clone(), Rc::new(TextConvertor)];
             editor.lsp.hover_provider = Some(lsp_store.clone());
             editor.lsp.definition_provider = Some(lsp_store.clone());
-            editor.lsp.document_color_provider = Some(lsp_store.clone());
+            // editor.lsp.document_color_provider = Some(lsp_store.clone());
 
             editor
         });
@@ -731,7 +731,7 @@ impl Example {
         Self::load_files(tree_state.clone(), PathBuf::from("./"), cx);
 
         let _subscriptions = vec![cx.subscribe(&editor, |this, _editor, _: &InputEvent, cx| {
-            this.lint_document(cx);
+            // this.lint_document(cx);
         })];
 
         Self {
```
- Run editor cargo run --release --example editor
- Disable folding (still a bit slow on these large files)

## Checklist

- [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
- [x] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
- [x] Passed `cargo run` for story tests related to the changes.
- [ ] Tested macOS, Windows and Linux platforms performance (if the
change is platform-specific)

---------

Co-authored-by: Jason Lee <huacnlee@gmail.com>
2026-07-08 12:58:26 +00:00
Floyd Wang 52dfda3388 scroll: Fix Scrollable height collapse in auto-sized containers (#2547)
Continue #2509.
2026-07-08 11:07:57 +00:00
cyfung1031 dbf57ad994 scroll: Revised Scrollable design for better interoperability (#2509)
Closes #2508

## Description

Fix `.overflow_y_scrollbar()` / `.overflow_scrollbar()` layout behavior
when used with layout styles such as `.gap()`.

Previously, `Scrollable` rendered an additional internal scroll-area
`div` and inserted the caller's element as a child of that wrapper. This
changed the layout structure and could cause styles applied to the
original element, such as flex gap, to no longer behave as expected.

This PR changes `Scrollable` so the original element itself becomes the
scroll-tracked container. The outer wrapper is only used to preserve
sizing and overlay the scrollbar. This keeps the caller's layout styles
intact while still rendering the scrollbar.

Main changes:

* Preserve the source element as the actual scroll area.
* Move scroll tracking and overflow behavior directly onto the source
element.
* Keep the outer wrapper responsible for sizing, positioning, and
scrollbar overlay.
* Use generated element IDs instead of fixed string IDs for scrollable
parts.
* Generalize the `InteractiveElement` implementation for
`Scrollable<E>`.

## Screenshot

| Before                        | After                           |
| ----------------------------- | ------------------------------- |
| See issue #2508 actual result | See issue #2508 expected result |

## How to Test

1. Run a story or example that uses `.overflow_y_scrollbar()` with a
flex layout and `.gap()`, for example:

```rust
v_flex()
    .flex_1()
    .gap(px(30.))
    .overflow_y_scrollbar()
    .px(px(12.))
    .pb(px(16.))
    .children(cards)
```

2. Confirm that the gap between children is preserved.
3. Confirm that the vertical scrollbar still appears and scrolls
correctly.
4. Confirm that existing `.overflow_scrollbar()`,
`.overflow_x_scrollbar()`, and `.overflow_y_scrollbar()` usages still
work.

## Checklist

* [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
* [x] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
* [x] Passed `cargo run` for story tests related to the changes.
* [ ] Tested macOS, Windows and Linux platforms performance (if the
change is platform-specific)
2026-07-07 15:19:06 +08:00
YlinandClaude Fable 5 c9506e8410 slider: Support reverse to fill the track from thumb to max end (#2541)
## Description

Adds a `reverse()` builder method to `Slider`. By default the track is
filled from the min end to the thumb; with `reverse`, the fill goes from
the thumb to the max end instead β€” useful when the slider represents a
remaining amount (e.g. time left in a media player).

```rust
Slider::new(&state).horizontal().reverse()
```

This only changes the visual fill; values, events and interactions are
unaffected. It applies to single-value sliders and is ignored for range
sliders (a range slider's fill is already bounded by both thumbs).

Also adds a "Reverse Slider" section to the slider story to showcase the
new option.

No breaking changes.

<img width="1642" height="1106" alt="image"
src="https://github.com/user-attachments/assets/c107d7e3-9856-4d27-a3c2-360d2d7ac0df"
/>

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 02:36:27 +00:00
Floyd Wang bc8b093e88 plot: Defer only the tooltip box (#2542) 2026-07-07 02:34:48 +00:00
Jason Lee a183e4622b input: Support content type for Input. (#2495) 2026-07-06 11:55:03 +00:00
f0abdd9f85 a11y: Accessibility support for all components
## Summary

- Adds `Role`, `aria_*` attributes, and `on_a11y_action` handlers to all
major UI components using the AccessKit integration in the latest GPUI
- Covers interactive controls, navigation, overlays, data display,
containers, and form components (~25 component files)
- Adds `Root` component `Role::Application` landmark so the
accessibility tree has a proper application root below Window
- Adds `script/run-story-macos` helper to build and launch Story gallery
as a signed macOS `.app` bundle for VoiceOver testing

## Component role mapping

| Component | Role | Extra attributes |
|-----------|------|-----------------|
| `Button` | `Button` / `Link` | `aria_label`, `aria_selected` |
| `Toggle` / `ToggleGroup` | `Button` + `aria_toggled`, `Toolbar` | β€” |
| `Checkbox` | `CheckBox` | `aria_toggled`, `aria_label` |
| `Radio` / `RadioGroup` | `RadioButton`, `RadioGroup` |
`aria_selected`, `position_in_set`, `size_of_set` |
| `Input` | `TextInput` / `MultilineTextInput` | β€” |
| `NumberInput` | `SpinButton` | `aria_numeric_value` |
| `Slider` | `Slider` | `aria_numeric_value`, min/max,
`aria_orientation`, Increment/Decrement a11y actions |
| `Tab` / `TabBar` | `Tab` + `aria_selected`, `TabList` | β€” |
| `PopupMenu` / `AppMenuBar` | `Menu`, `MenuBar` | β€” |
| `MenuItem` | `MenuItem` | `aria_selected` |
| `Dialog` / `AlertDialog` | `Dialog` (configurable), `AlertDialog` | β€”
|
| `Alert` | `Alert` | β€” |
| `Progress` | `ProgressIndicator` | `aria_numeric_value`, min/max |
| `List` / `ListItem` | `List`, `ListItem` | `aria_position_in_set`,
`aria_size_of_set`, `aria_selected` |
| `Table` / Header / Body / Row / Cell | `Table`, `RowGroup`, `Row`,
`ColumnHeader`, `Cell` | `aria_row_index`, `aria_column_index` |
| `Accordion` / item header | `Button` + `aria_expanded` | β€” |
| `Combobox` | `ComboBox` | `aria_expanded` |
| `Stepper` / `StepperItem` | `ListItem` | `aria_position_in_set` |
| `BreadcrumbItem` | `Link` / `ListItem` | β€” |
| `Root` | `Application` | β€” |

## Verification guide

### Prerequisites

AccessKit only builds the full accessibility tree when an assistive
technology is **active**. Accessibility Inspector alone is not
sufficient β€” you must enable VoiceOver first.

### macOS β€” VoiceOver + Accessibility Inspector

```bash
# 1. Build and launch Story gallery as a signed .app bundle
./script/run-story-macos
```

```
# 2. Enable VoiceOver  (this activates AccessKit)
Cmd + F5

# 3. Open Accessibility Inspector
#    Xcode β†’ Xcode menu β†’ Open Developer Tool β†’ Accessibility Inspector
#    (or: open -a "Accessibility Inspector")

# 4. In Accessibility Inspector, click the target crosshair icon,
#    then hover over or click elements in the Story window.
#    You should see the full hierarchy:
#    Application > Window > Application > Button / CheckBox / Tab / ...

# 5. Tab through interactive elements β€” VoiceOver announces each one.

# 6. Test Slider keyboard control:
#    Focus the Slider, then press Up/Down arrows (Increment/Decrement a11y actions).
```

<img width="1407" height="828" alt="SCR-20260706-pzbf"
src="https://github.com/user-attachments/assets/32e7b567-c075-4c9f-a672-0daf3964c9a3"
/>

https://github.com/user-attachments/assets/d5793e31-e902-42cc-be2b-dc6781ec62dc

### Key things to verify

- **Button**: role `AXButton`, label matches button text
- **Checkbox**: role `AXCheckBox`, value changes on click
- **Toggle**: role `AXButton`, `AXValue` reflects checked state
- **Radio group**: each item reports `AXPositionInSet` / `AXSizeOfSet`
- **Tab bar**: `AXTabGroup` containing `AXTab` items, selected tab has
`AXValue = 1`
- **Dialog**: role `AXDialog` when open, focus trapped inside
- **Slider**: `AXSlider` with `AXMinValue` / `AXMaxValue` / `AXValue`,
responds to VoiceOver Increment/Decrement

### Windows β€” NVDA

```powershell
cargo run
# Enable NVDA, Tab through the window.
# NVDA should announce component type and label on focus.
```

### Linux β€” AT-SPI (Orca)

```bash
cargo run
# Enable Orca, Tab through the window.
```

---

> **Note**: `active_flag` starts `false` and becomes `true` only on
first AT query.
> If Inspector shows only `Window` with no children, ensure VoiceOver is
on,
> then wait ~1 second and click **Refresh** in Accessibility Inspector.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Codex <codex@openai.com>
2026-07-06 19:20:29 +08:00
Jason LeeandClaude Opus 4.8 dbc038c891 docs: Clarify that anchor is the Popover's origin, not the display side (#2537)
## Description

Closes #2534.

Users read the `anchor` option as "which side of the trigger the popover
shows on" and reported it as inverted. It actually uses GPUI's
corner-anchoring model, and the docs described it in a way that
reinforced the wrong mental model.

This PR only clarifies documentation, no behavior change. Instead of
explaining the mechanics, it uses a concrete image: imagine the popover
has a pointer tip (like a speech bubble's tail) β€” the anchor is where
that tip sits relative to the trigger (`Anchor::TopLeft` β†’ the trigger's
top-left corner, `Anchor::BottomRight` β†’ the bottom-right, and so on),
and the popover hangs off that point.

- Rewrite the `anchor()` doc comments on `Popover` and `HoverCard`.
- Update the Popover and HoverCard docs (en + zh-CN) with the same
explanation.
- Drop stale wording that implied two separate `Anchor` types.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 17:05:05 +08:00
11399efc26 input: add InputState::refresh to apply runtime LSP provider changes (#2491)
Closes #2476

## Description

The `InputState.lsp` provider fields are public, but assigning them at
runtime does not refresh the editor until the text next changes.

This adds `InputState::refresh(cx)`, which flags a pending update so the
next render re-runs syntax highlighting and the LSP providers (not just
a redraw).

```rust
input.update(cx, |state, cx| {
    state.lsp.hover_provider = Some(provider);
    state.refresh(cx);
});
```

## How to Test

Create an InputState, assign an `lsp` provider at runtime, then call
`refresh` and watch the input update without editing text.

- Attach an LSP provider (hover / document color / semantic tokens) at
runtime, call `refresh`, confirm it takes effect without editing text
- Swap providers at runtime and `refresh`; confirm old results clear and
the new provider runs

## Checklist

- [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
- [x] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
- [x] Passed `cargo run` for story tests related to the changes.
- [ ] Tested macOS, Windows and Linux platforms performance (if the
change is platform-specific)

Co-authored-by: Jason Lee <huacnlee@gmail.com>
Co-authored-by: ScottCUSA <ScottCUSA@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 08:07:09 +00:00
Third-ThingandJason Lee 3bb69651d8 highlighter: Fix markdown inline highlighting across list items (#2535)
### Problem

Markdown inline highlighting could break across list items. Code span
highlighting could stop after the first list item, then apply to later
plain text until another code span appeared.

### Solution

Preserve the separator text between combined markdown inline ranges
before parsing them. This keeps each list item separated during
highlighting. Added a regression test for code spans in markdown list
items.

### Testing

- All highlighter tests (including a new one)
- Manual testing via `cargo run -p gpui-component-story --example
markdown`

---

Implemented-by: Codex 5.5
Reviewed-by: Claude Opus 4.8

---------

Co-authored-by: Jason Lee <huacnlee@gmail.com>
2026-07-06 05:51:08 +00:00
372446c007 chore: Make tree-sitter optional (#2450)
## Description

Make tree-sitter optional
## How to Test

```sh
cargo check -p gpui-component --no-default-features
cargo check -p gpui-component --features tree-sitter-languages
cargo check -p gpui-component --target wasm32-unknown-unknown
cargo check -p gpui-component --target wasm32-unknown-unknown --features tree-sitter-languages
cargo check --workspace
cargo test -p gpui-component --no-default-features
cargo test -p gpui-component --features tree-sitter-languages
```

## Checklist

- [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
- [x] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
- [x] Passed `cargo run` for story tests related to the changes.
- [ ] Tested macOS, Windows and Linux platforms performance (if the
change is platform-specific)

---------

Co-authored-by: Jason Lee <huacnlee@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 03:37:52 +00:00
Floyd Wang b7e63cc290 tab: Fix Segmented/Pill/Underline tabs shrinking when overflowing (#2530)
Closes #2528.
2026-07-03 15:04:01 +08:00
Alex Shi 951e3dea91 input: Support replace_all to replace input value while preserving undo history (#2523)
## Description

Add new function `replace_all` to replace input value while preserving
undo history.

## Break Changes

Add new API `replace_all` to input state.

## How to Test

Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce.
```
test_replace_all_single_line
test_replace_all_multi_line
test_replace_all_preserves_undo_history
```

## Checklist

- [ ] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
- [ ] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
- [ ] Passed `cargo run` for story tests related to the changes.
- [ ] Tested macOS, Windows and Linux platforms performance (if the
change is platform-specific)
2026-07-03 13:44:07 +08:00
YlinandClaude Fable 5 8fbae813c5 resizable: Avoid NaN panel sizes when total size is zero or non-finite (#2529)
## Description

Fixes a potential `NaN` propagation in
`ResizableState::adjust_to_container_size`.

When all panel sizes sum to `0` (e.g. before the first layout pass, or
when panels were collapsed to zero), the `size / total_size` ratio
becomes `NaN` (`0 / 0`), and every panel size gets poisoned with `NaN`
values that then persist in the state and break subsequent resizing.

Now the adjustment is skipped when the total size is not finite or not
positive, keeping the previous sizes until valid measurements are
available.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:42:41 +08:00
George Waters a9a7341c35 input: Fix soft-wrap width being applied one frame late (#2524)
Reorders some of the layout code in `TextElement::prepaint` to compute
the soft wrap width and update the display map so the rest of the layout
and painting have the correct soft wrapping in the current frame.

Closes #2520 

## Screenshot

| Before                       | After                       |
| ---------------------------- | --------------------------- |
| <video
src="https://github.com/user-attachments/assets/474f6dd1-8f82-4ad0-a1a1-764b0e4498b4"></video>
| <video
src="https://github.com/user-attachments/assets/62c6a66e-91d1-4a6a-83ce-271dd7a71809"></video>
|

*Notice in the top textbox the text no longer jumps into place after the
mouse is moved.*

## Checklist

- [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
- [x] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
- [x] Passed `cargo run` for story tests related to the changes.
- [ ] Tested macOS, Windows and Linux platforms performance (if the
change is platform-specific)
2026-07-02 17:18:13 +08:00
Floyd WangandClaude Opus 4.8 1505b14871 text_view: Don't allow TextView selection behind a Dialog/Sheet (#2521)
Closes #2518.

Each selectable `TextView` is now tagged with the modal layer it paints
under, via a new `.selection_scope(scope)` element modifier that
pushes/pops a scope during paint (mirroring the existing
`text_view_state_stack` idiom). Window selection only considers views in
the active layer β€” the topmost dialog, else the active sheet, else the
base window β€” and is cleared when a modal opens/closes. Covers both
`Dialog` and `Sheet`.

| Before | After |
| - | - |
| <img width="960" height="828" alt="Dialog1"
src="https://github.com/user-attachments/assets/2e91c56b-558a-4e55-a05c-7a1bda241a77"
/> | <img width="962" height="827" alt="Dialog2"
src="https://github.com/user-attachments/assets/29b472fa-1ed8-4088-b300-4643bde88afa"
/> |
| <img width="960" height="828" alt="Sheet1"
src="https://github.com/user-attachments/assets/e59b5b6a-ddd8-42b9-a844-f316cb8f1dec"
/> | <img width="962" height="827" alt="Sheet2"
src="https://github.com/user-attachments/assets/b822fa9e-4042-479e-82a6-7d1fd857dc8f"
/> |

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 09:46:47 +08:00
berestadevandFloyd Wang c90fab4de0 input: skip notify when hover state unchanged on mouse move (#2519)
## Description

Problem: gpui-component's InputState::handle_mouse_move calls
cx.notify() unconditionally, and GPUI's mark_view_dirty() propagates
this up the dispatch tree to MainView.

Cause: InputState::handle_mouse_move in gpui-component calls cx.notify()
on every mouse movement pixel, even when no LSP hover state changes.
GPUI's mark_view_dirty() walks the dispatch tree upward, marking all
ancestors dirty - causing MainView to re-render the entire tree on each
mouse move.
Fix: Only cx.notify() when hover_definition or hover_popover actually
changed.

## Checklist

- [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
- [x] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
- [x] Passed `cargo run` for story tests related to the changes.
- [x] Tested macOS, Windows and Linux platforms performance (if the
change is platform-specific)

---------

Co-authored-by: Floyd Wang <gassnake999@gmail.com>
2026-06-29 11:41:52 +08:00
YlinandClaude Opus 4.8 063e55bbc4 text: Support HTML <mark> tag with highlight color (#2515)
## Description

Adds support for the HTML `<mark>` tag in the text/HTML renderer,
rendering marked text with a background highlight.

The highlight color is customizable via the `color` attribute or the
`background-color` style declaration, parsed by the existing
`try_parse_color` helper:

```html
<mark>highlighted</mark>                                   <!-- default light yellow -->
<mark color="blue-200">blue tint</mark>                   <!-- Tailwind scale -->
<mark color="amber/30">amber, 30% opacity</mark>          <!-- opacity modifier -->
<mark style="background-color: #fca5a5">custom hex</mark>  <!-- hex -->
```

- Accepts hex (`#3366ff`), named colors (`blue`), Tailwind scales
(`blue-200`), and opacity (`amber/30`).
- Defaults to a light yellow highlight when no color is given.

`TextMark` gains an additive `highlight: Option<Hsla>` field (private
module, not part of the public API). The `<mark>` demos were added to
the HTML example fixture, with a unit test covering the tag.

> Note: AI-generated code, refactored to match project style.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 04:05:56 +00:00
Floyd WangandClaude Opus 4.8 be4c5d30e0 plot: Polish hover crosshair color and fix horizontal bar band (#2513)
- Darken the dashed crosshair by blending `border` toward `foreground`,
so it reads a touch deeper than the (border-colored) grid lines in both
light and dark themes.
- Fix the horizontal bar hover band overflowing the value axis: it
called `.span()` (which sets the vertical span) after `.horizontal()`,
so the horizontal line fell back to `w_full()`. Use `.h_span()` to
confine the band to the bar region.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 09:49:38 +00:00
ba681dfc88 theme: Fix invisible active/selection backgrounds from double opacity (#2512)
## Description

Fixes the Table selection background becoming invisible after #2484.
`Background::opacity` *multiplies* the existing alpha, but the token
clamping in `ThemeColor` passed the absolute target alpha as the
multiplier factor. The default `table.active.background` (`#bfdbfe33`)
already carries `0.2` alpha, so it was attenuated twice (`0.2 Γ— 0.2 =
0.04`) and the active row highlight became nearly invisible.

The fix passes a factor (`target / base`) instead, so the final alpha
lands on the clamped target regardless of the base alpha, and gradient
stops keep their relative opacity. The same latent bug applied to
`list_active` (only invisible because components read the `Hsla`, not
the token background) and `selection` (correct only by luck, since its
default base alpha is `1.0`), so all three now go through one shared
`clamp_alpha` helper.

| Before | After |
| - | - |
| <img width="2802" height="1764" alt="image"
src="https://github.com/user-attachments/assets/3afe3087-229c-45b6-aa6c-4ea51edcb613"
/> | <img width="2802" height="1764" alt="image"
src="https://github.com/user-attachments/assets/d85e10b2-c29d-4d8e-9135-9974279c32a5"
/> |

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Floyd Wang <gassnake999@gmail.com>
2026-06-25 16:00:50 +08:00
Alex ShiandFloyd Wang 49f4b4fb57 input: Show the start of a long value after set_value (#2510)
## Description

Ensure the cursor is at the start when setting text. This fixes the
issue when an input value is long, it shows the latter part of the value
after setting value.

## How to Test
`test_set_value_move_cursor_to_start`

## Checklist

- [ ] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and
followed the guidelines.
- [ ] Reviewed the changes in this PR and confirmed AI generated code
(If any) is accurate.
- [ ] Passed `cargo run` for story tests related to the changes.
- [ ] Tested macOS, Windows and Linux platforms performance (if the
change is platform-specific)

---------

Co-authored-by: Floyd Wang <gassnake999@gmail.com>
2026-06-25 03:42:19 +00:00
Floyd WangandClaude Opus 4.8 a0ae3a37b9 plot: Fix and polish chart hover tooltips (#2507)
Description

Follow-up fixes and polish for the interactive chart hover tooltips
added in #2500.

- **Tooltip vertical stretch** β€” a stray `top_0()` on the tooltip `base`
stretched the box vertically; removed it.
- **Tooltip clipped under later siblings** β€” the overlay was painted
inline during the plot's `paint`, so siblings drawn afterwards (e.g. a
chart card footer) covered the part of the box overflowing the plot
bounds. It's now wrapped in `gpui::deferred(...)` so it paints on top;
`prepaint_as_root` keeps the plot-origin offset, so positioning is
unchanged.
- **Downstream clippy lint** β€” the `IntoPlot` derive emitted
`let...else`, which tripped `clippy::question_mark` in crates that
derive it. It now emits `?` so generated code is lint-clean.
- **`CrossLine::both()` crosshair** β€” both axes shared a single span, so
confining the crosshair with `height()`/`span()` clipped the horizontal
line to the vertical extent. Each axis now confines independently.

## Breaking Changes

- `CrossLine::height()` / `span()` now confine only the **vertical**
line. Use the new `width()` / `h_span()` to confine the **horizontal**
line.

```diff
- CrossLine::new(point).horizontal().height(120.)
+ CrossLine::new(point).horizontal().width(120.)
```

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:05:30 +08:00
Floyd WangandClaude Opus 4.8 725dc36010 tab: Fix Pill/Underline double active background on switch animation (#2505)
Closes #2499.



https://github.com/user-attachments/assets/02a2a672-33c7-476f-9f51-8a482273b530

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 11:00:36 +08:00
John Yani e2fcc41508 chore: Replace tokio sleep with background executor timer (#2504)
# Fix Async Patterns in GPUI Documentation


## Description

This PR fixes the async spawn examples in
`skills/gpui/references/async.md` that contained incorrect patterns
leading to runtime panics. The examples were using tokio's sleep
functions (`tokio::time::sleep`) within GPUI's async context, which is
incorrect. The correct pattern is to use GPUI's built-in timer
functionality via `cx.background_executor().timer()`.

**Problem Solved:**
- Removed dependency on tokio for sleep functionality in GPUI async
examples

**Benefits:**
- Example will run without panics
- Demonstrates the correct GPUI async pattern

## Screenshot

| Before                       | After                       |
| ---------------------------- | --------------------------- |
| [Runtime panic on async spawn] | [Successfully runs and updates UI] |

## Break Changes

None. This is purely a documentation fix that corrects the example code.


---

**Correct Async Pattern Example:**
```diff
- // ❌ Incorrect: Uses tokio sleep and wrong spawn signature
- cx.spawn(...{
-     tokio::time::sleep(std::time::Duration::from_secs(2)).await;
-     cx.update(|cx| {
...
-     }).ok();
- });

+ // βœ… Correct: Uses GPUI's timer and proper spawn signature
+ cx.spawn(async move |this, cx| {
+     cx.background_executor()
+         .timer(std::time::Duration::from_secs(2))
+         .await;
+     
+     this.update(cx, |this, cx| {
...
+     }).ok();
+ });
```
2026-06-24 09:32:09 +08:00