Skip to content

warpui_core: backend-neutral view hierarchy for a shared responder chain#12413

Merged
zachbai merged 2 commits into
masterfrom
zb/tui-backend/02-tui-view-layer
Jun 18, 2026
Merged

warpui_core: backend-neutral view hierarchy for a shared responder chain#12413
zachbai merged 2 commits into
masterfrom
zb/tui-backend/02-tui-view-layer

Conversation

@zachbai

@zachbai zachbai commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Description

First PR in a stack that adds an optional TUI backend to warpui. This PR is only the backend-neutral groundwork — no TUI code, no behavioral change.

Today the view responder chain (focus/blur, action dispatch, ancestor walks) is derived from the GUI presenter's layout-time parent map. This PR moves that ownership into AppContext so any backend can drive it:

  • New AppContext.view_parents — a per-window child→parent view map.
  • Fed from two sources: creation-time structural parentage (record_view_parent) and the active backend's render pass (report_view_embeddings).
  • Walked by view_ancestors / view_parent_map; all responder-chain / focus / dispatch sites now read this map instead of presenter.ancestors().
  • The GUI presenter is updated to feed report_view_embeddings, so GUI behavior is unchanged.

This is a behavior-preserving, GUI-only refactor. The TUI presenter feeds the same map later in the stack (#12601).

Reviewing (3 files): app.rs is the substance — the map, its accessors, and the call-site swaps. presenter.rs wires the GUI render pass to report embeddings. The test swaps presenter.ancestors()ctx.view_ancestors().

Stack

master#12413 (this)#12633#12634#12601

Testing

  • cargo check -p warpui_core -p warp -p warpui clean; cargo nextest -p warpui_core 279 passed / 7 skipped.
  • No manual run: no behavioral change.

Agent Mode

  • Warp Agent Mode

CHANGELOG-NONE

zachbai commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vorporeal vorporeal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code structure suggestion (agent can probably clean this up pretty easily):

might be nice to have a core/backend folder that contains gui and tui subfolders - we then can have parallel gui/view and tui/view hierarchies.

so something like:

  • crates/warpui_core/src/core/backend.rs
  • crates/warpui_core/src/core/backend/gui.rs
  • crates/warpui_core/src/core/backend/gui/view/context.rs

etc.

Comment thread crates/warpui_core/src/elements/gui/flex/mod.rs
@zachbai zachbai force-pushed the zb/tui-backend/02-tui-view-layer branch 8 times, most recently from 037ea72 to 60492af Compare June 12, 2026 01:31
@zachbai zachbai changed the base branch from zb/tui-backend/01-core-backend-param to graphite-base/12413 June 13, 2026 00:49
@zachbai zachbai force-pushed the zb/tui-backend/02-tui-view-layer branch from 60492af to 0a1c519 Compare June 13, 2026 00:49
@zachbai zachbai force-pushed the graphite-base/12413 branch from afea471 to d098332 Compare June 13, 2026 00:49
@zachbai zachbai changed the base branch from graphite-base/12413 to master June 13, 2026 00:49
@zachbai zachbai force-pushed the zb/tui-backend/02-tui-view-layer branch from 0a1c519 to 7eb80a2 Compare June 15, 2026 03:57
@zachbai zachbai changed the title warpui_core: add TuiBackend + TuiView layer behind the tui feature warpui_core: backend-neutral core with an additive tui feature Jun 15, 2026
@zachbai zachbai force-pushed the zb/tui-backend/02-tui-view-layer branch 2 times, most recently from cb839d6 to 68de666 Compare June 15, 2026 19:58
@zachbai zachbai force-pushed the zb/tui-backend/02-tui-view-layer branch 3 times, most recently from 21e266f to 2f3a0e9 Compare June 15, 2026 22:51
Collapses the former 01 (Backend-trait parameterization, since deleted) and
02 (neutral core + view unification + module structure) into one foundation:
the GUI backend is compiled and aliased unconditionally, the tui feature is
purely additive, and there is no Backend type parameter.

Co-Authored-By: Oz <oz-agent@warp.dev>
@zachbai zachbai force-pushed the zb/tui-backend/02-tui-view-layer branch from 2f3a0e9 to a2e8bfc Compare June 15, 2026 23:59
@zachbai zachbai changed the title warpui_core: backend-neutral core with an additive tui feature warpui_core: backend-neutral view hierarchy for a shared responder chain Jun 16, 2026
@zachbai zachbai requested a review from vorporeal June 16, 2026 00:53
@zachbai zachbai marked this pull request as ready for review June 16, 2026 19:22
@oz-for-oss

oz-for-oss Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@zachbai

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR moves the WarpUI responder-chain/view-parent hierarchy out of the GUI presenter and into AppContext, with the GUI presenter reporting layout-time embeddings into the shared map. The implementation generally follows the described backend-neutral refactor, and no approved spec context was provided. The supplemental security pass did not identify security-specific issues in the changed lines.

Concerns

  • transfer_view_to_window now removes the source-window parent-map entry before validating the target window. If the transfer rolls back because the target window is missing, the view is restored to the source window but its responder-chain ancestry is not, leaving future focus/action ancestor walks incorrect.

Verdict

Found: 0 critical, 1 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz


// The view's parentage in the source window no longer applies; the
// target window's render pass will report its new embedding.
if let Some(parents) = self.view_parents.get_mut(&source_window_id) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This removes the source-window parent entry before the target window is validated; if the target is missing, the rollback reinserts the view but leaves view_parents without its ancestry, so later responder-chain/focus walks collapse to just the view. Move this after the target-window check or restore the old parent on rollback.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed with the bot here; we should push this logic to run after all early-returns (e.g.: the one right below this)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// The view's parentage in the source window no longer applies; the
// target window's render pass will report its new embedding.
if let Some(parents) = self.view_parents.get_mut(&source_window_id) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed with the bot here; we should push this logic to run after all early-returns (e.g.: the one right below this)

/// child-view embeddings it discovers while laying out a frame
/// ([`Self::report_view_embeddings`]). Entries are removed when views are
/// dropped or transferred out of the window, and when the window closes.
view_parents: HashMap<WindowId, HashMap<EntityId, EntityId>>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the reason we're keying this on WindowId that it allows us to easily drop all ancestry information for views in a particular window? given that we now support moving views between windows, i wonder if the strict window->view association is still worth keeping. (entity IDs are globally unique, so we don't need to namespace them with a window ID)

i don't think we necessarily need to change this, mostly just curious.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that is the case. previously we had this info on presenter, which is keyed by window. Just cause I think its not that consequential right now + for the sake expediency gonna proceed with this as is

Comment thread crates/warpui_core/src/core/app.rs Outdated
Comment on lines +2263 to +2366
where
T: View + TypedActionView,
F: FnOnce(&mut ViewContext<T>) -> T,
{
self.insert_window(options, build_root_view)
}

fn insert_window<T, F>(
&mut self,
add_window_options: AddWindowOptions,
build_root_view: F,
) -> (WindowId, ViewHandle<T>)
where
T: View + TypedActionView,
F: FnOnce(&mut ViewContext<T>) -> T,
{
let (window_id, _root_view_id) =
self.insert_window_internal(None, add_window_options, |window_id, ctx| {
self.insert_window_internal(None, options, |window_id, ctx| {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i assume all of these changes leaked into this PR from a future one in the stack?

Comment thread crates/warpui_core/src/core/app.rs Outdated
/// This is useful when transferring a component like a tab that contains
/// multiple nested views. The view tree is determined by the presenter's
/// parent-child relationships.
/// multiple nested views. The view tree is determined by the neutral view

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol neutral makes no sense here outside of "this data used to exist in a presenter that we're now scoping to the GUI backend"

(don't need to change, just sighing at AI comment writing)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread crates/warpui_core/src/core/app.rs Outdated
/// This operation is destructive: It will clear the caches for both manual and autotracked
/// invalidations.
///
/// GUI-only for now; the M8 TUI runtime may move this back into shared code

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

M8? is this the milestones in your plan document leaking into doc comments?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@zachbai zachbai merged commit 1acbff1 into master Jun 18, 2026
29 checks passed
@zachbai zachbai deleted the zb/tui-backend/02-tui-view-layer branch June 18, 2026 00:26
@kevinyang372 kevinyang372 mentioned this pull request Jun 18, 2026
4 tasks
@harryalbert harryalbert mentioned this pull request Jun 18, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants