Files
warp/crates/warpui_core
Brad Reynolds 1d1c06db0f Fix file picker truncating paths within available width (#9885)
The file picker capped combined filename + path length at 55 characters,
leaving significant horizontal space unused in wider popovers. Drops
that cap for the command palette file picker and switches the path text
to render a leading `…` (instead of a fade) when overflow does happen.

Also fixes a latent paint bug in `Start + Ellipsis` text clipping: the
ellipsis-reservation shifted glyphs leftward without compensating their
origin, so the leftmost visible glyph overlapped the ellipsis at the
same x. Adds regression-protection unit tests for the start-clipping
paint path.

Fixes warpdotdev/warp#8709

## Description
<!-- Please remember to add your design buddy onto the PR for review, if
it contains any UI changes! -->
What is a design buddy?

## Linked Issue
<!--
Link the GitHub issue this PR addresses. Before opening this PR, please
confirm:
-->
- [ ] The linked issue is labeled `ready-to-spec` or
`ready-to-implement`.
- [X] Where appropriate, screenshots or a short video of the
implementation are included below (especially for user-visible or UI
changes).

## Screenshots / Videos
<img width="1774" height="1326" alt="Screenshot_redacted_2"
src="https://github.com/user-attachments/assets/596bb770-d64f-443c-9da9-30e4fe0bf727"
/>

## Testing
<!--
How did you test this change? What automated tests did you add? If you
didn't add any new tests, what's your justification for not adding any?
-->
Look at the PR.

## Agent Mode
- [ ] Warp Agent Mode - This PR was created via Warp's AI Agent Mode

Used claude code. 

<!--
## Changelog Entries for Stable

The entries below will be used when constructing a soft-copy of the
stable release changelog. Leave blank or remove the lines if no entry in
the stable changelog is needed. Entries should be on the same line,
without the `{{` `}}` brackets. You can use multiple lines, even of the
same type. The valid suffixes are:

* NEW-FEATURE: for new, relatively sizable features. Features listed
here will likely have docs / social media posts / marketing launches
associated with them, so use sparingly.
* IMPROVEMENT: for new functionality of existing features.
* BUG-FIX: for fixes related to known bugs or regressions.
* IMAGE: the image specified by the URL (hosted on GCP) will be added to
Dev & Preview releases. For Stable releases, see the pinned doc in the
#release Slack channel.
* OZ: Oz-related updates. Use `CHANGELOG-OZ`. At most 4 Oz updates are
shown in-app per release.

CHANGELOG-NEW-FEATURE: {{text goes here...}}
CHANGELOG-IMPROVEMENT: {{text goes here...}}
CHANGELOG-BUG-FIX: {{text goes here...}}
CHANGELOG-BUG-FIX: {{more text goes here...}}
CHANGELOG-IMAGE: {{GCP-hosted URL goes here...}}
CHANGELOG-OZ: {{text goes here...}}
-->
2026-05-05 17:10:15 -04:00
..
2026-04-28 08:43:33 -05:00
2026-04-28 08:43:33 -05:00

WarpUI

Whirlwind tour

WarpUI contains many interlocking concepts. It's difficult to explain any one part of the system without reference to other parts. Because of this, this guide tries to provide an overview by exploring the relationships between the major concepts before providing a lot of detail on any one of them.

Rust's strict ownership rules are a challenge for user interfaces, where multi-directional dataflow is often critical. If every object has one and only one owner, how do we express things like event handlers?

The global App object, entities, and handles

WarpUI solves this problem with the App object, which is the sole owner of all the views and models in the application. We collectively refer to views and models as entities. Entities can hold references to other entities via handles. A handle provides access to an entity in specific, limited circumstances. Take for example a multi-tabbed terminal. The application window is occupied by a WorkspaceView, and we want this workspace to contain multiple TerminalViews. Rather than holding the TerminalViews directly, the Workspace instead holds a vector of ViewHandle<TerminalView>.

struct WorkspaceView {
    sessions: Vec<ViewHandle<TerminalView>>,
}

On its own, a ViewHandle can't do much. Its existence prevents the referenced view from being discarded by the global App object, but it doesn't provide direct access to the referenced view. A handle is basically a glorified identifier. To convert a handle into an actual reference, you need a reference to an app context object, which will be provided by the global App object at specific points in time.

An example of one of those times could be the render method on WorkspaceView, which will be called by the framework whenever the workspace's on-screen representation is updated. One of the parameters to render is an &AppContext, which can be passed to the as_ref method on a ViewHandle to retrieve a reference to the underlying object.

Many details are elided in the code example below so we can focus on the topic at hand, but imagine we wanted to know the titles of all our terminal sessions so we could render them in tabs:

impl View for WorkspaceView {
    fn render<'a>(&self, ..., ctx: &AppContext) -> ... {
        let titles = self.sessions.iter().map(|handle| handle.as_ref(ctx).title()).collect::<Vec<String>>;
        ...
    }

    ...
}

After we return from the render method and lose access to the &AppContext parameter provided during its call, we no longer have access to the terminal views they reference.

Entities are of course entitled to own any state they like directly as well. Handles are only required when an entity needs to reference an entity. The reasons it would make sense to express a piece of application state as its own entity will become clearer as we sketch in more aspects of the system.

Elements

The framework requires all views to implement the View trait, and a key method of this trait is render, which we showcased above. This method's job is to compute a visual description of the view based on its current state, and it is called whenever the view's state changes.

To describe the view's appearance, render returns an element. While a single view may exist for an arbitrary amount of time, changing its state as the user interacts with the application, an element is designed to exist for only a single frame. More precisely, elements returned by views that haven't changed are recycled across multiple frames, but conceptually you can think of an element as a throwaway object that is discarded and replaced whenever the view that returned it changes.

The framework ships with several elements that can be composed to perform common tasks such as drawing backgrounds and borders, adding padding, rendering label text, laying out elements horizontally and vertically, handling events, etc. The stock elements are loosely based on the Flutter framework. It's also straightforward to define your own custom elements, giving you detailed control over layout and the ability to imperatively paint pixels on scene via the hardware-accelerated Scene API.

Actions

Action handlers

Action dispatch

Views