input: Split Input, Textarea, and Editor APIs (#2691)
## Summary
This PR splits the public `gpui-component` text-editing interface into
three focused controls:
- `Input / InputState`: single-line text, masks, validation, and number
stepping.
- `Textarea / TextareaState`: ordinary multiline text, rows, soft
wrapping, and auto-grow.
- `Editor / EditorState`: code editing, highlighting, line numbers,
folding, decorations, diagnostics, search, and LSP.
It also fixes multiline scrollbar placement, removes Editor focus-border
highlighting, aligns the gutter/current-line backgrounds, and
reorganizes the internal input implementation by `base`, `input`,
`textarea`, and `editor`.
## Breaking Changes
### InputState is now single-line only
Existing single-line inputs remain source-compatible:
```rust
use gpui_component::input::{Input, InputState};
let state = cx.new(|cx| InputState::new(window, cx).placeholder("Name"));
Input::new(&state)
```
Code using `InputState` as a multiline field or code editor must migrate
to the corresponding state and control.
### Multiline Input → Textarea
Imports:
```diff
- use gpui_component::input::{Input, InputState};
+ use gpui_component::input::{Textarea, TextareaState};
```
Auto-growing textarea:
```diff
- let state = cx.new(|cx| {
- InputState::new(window, cx)
- .multi_line(true)
- .auto_grow(3, 8)
- .default_value("Notes")
- });
- Input::new(&state)
+ let state = cx.new(|cx| {
+ TextareaState::new(window, cx)
+ .auto_grow(3, 8)
+ .default_value("Notes")
+ });
+ Textarea::new(&state)
```
Fixed-height textarea:
```diff
- let state = cx.new(|cx| {
- InputState::new(window, cx)
- .multi_line(true)
- .default_value("Description")
- });
- Input::new(&state).h(px(120.))
+ let state = cx.new(|cx| {
+ TextareaState::new(window, cx)
+ .rows(5)
+ .default_value("Description")
+ });
+ Textarea::new(&state).h(px(120.))
```
### Code-editor Input → Editor
Imports:
```diff
- use gpui_component::input::{Input, InputState};
+ use gpui_component::input::{Editor, EditorState};
```
Editor construction:
```diff
- let state = cx.new(|cx| {
- InputState::new(window, cx)
- .code_editor("rust")
- .line_number(true)
- .folding(true)
- .default_value(source)
- });
- Input::new(&state)
+ let state = cx.new(|cx| {
+ EditorState::new("rust", window, cx)
+ .line_number(true)
+ .folding(true)
+ .default_value(source)
+ });
+ Editor::new(&state)
```
Editor decorations:
```diff
- let state = cx.new(|cx| InputState::new(window, cx).default_value(source));
+ let state = cx.new(|cx| EditorState::new("text", window, cx).default_value(source));
let decorations = state.update(cx, |state, cx| {
state.create_decorations_collection(styles, cx)
});
- Input::new(&state)
+ Editor::new(&state)
```
### Input-only adornments are not Editor/Textarea properties
`prefix`, `suffix`, mask toggle, and clear-button behavior remain on the
single-line `Input`. Compose actions beside an Editor or Textarea
instead of configuring them as input adornments.
```diff
- Input::new(&editor_state).suffix(reset_button)
+ v_flex()
+ .child(Editor::new(&editor_state))
+ .child(reset_button)
```
## Test Plan
- `RUSTFLAGS="-D warnings" cargo test --all`
- `cargo check -p gpui-base -p gpui-component -p gpui-component-story`
- `cargo check -p gpui-base --examples`
- `cargo check -p gpui-component-story --examples`
- `cargo fmt --all --check`
- `git diff --check`
- GitHub Actions pass on macOS, Linux, and Windows.
## AI Assistance
Implementation, migration, tests, documentation, and review were
completed with Codex assistance.
---------
Co-authored-by: Codex <codex@openai.com> J
Jason Lee committed
7b3426db7b357fd7fa77729ebc4dcbcb25656026
Parent: 55b6bb8
Committed by GitHub <noreply@github.com>
on 8/13/2026, 6:48:58 AM