SIGN IN SIGN UP

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