7.2 KiB
TECH.md — APP-3904: Don't override pane/tab representation for expanded edit tool call
Problem
When an agent edit tool call is expanded into a pane (via ExpandEditToPane), the vertical tabs sidebar changes its representation for that tab from the original pane (e.g. "Create Korean Poem File" with agent icon and metadata) to a generic "Requested Edit" code-diff pane. The entire sidebar row — title, icon, subtitle, badge — changes to reflect the replacement CodeDiffPane instead of the original agent conversation pane.
The sidebar should continue showing the original pane's full representation while a temporary replacement is active.
Relevant code
app/src/pane_group/pane/code_diff_pane.rs:22-45—CodeDiffPane::from_viewhardcodesPaneConfigurationtitle to "Requested Edit"app/src/workspace/view.rs:6428-6460—open_code_diffcreates theCodeDiffPaneand callsreplace_panewithis_temporary: trueapp/src/pane_group/tree.rs:55-112—HiddenPane/HiddenPaneReason::TemporaryReplacementtracks the original→replacement mappingapp/src/pane_group/tree.rs:368-372—PaneData::is_temporary_replacementchecks if a pane is a replacementapp/src/pane_group/mod.rs:4456-4502—PaneGroup::replace_paneorchestrates temporary replacement; original stays inpane_contentsapp/src/workspace/view/vertical_tabs.rs:2065-2136—PaneProps::newresolves display properties (typed, title, subtitle, icon, badge) from the pane's configuration and typeapp/src/workspace/view/vertical_tabs.rs:2348-2391—PaneGroup::resolve_pane_typemapsPaneId→TypedPanewhich drives icon/badge/kind
Current state
The ExpandEditToPane feature flag controls how code diff views are opened:
- Enabled: The focused pane is temporarily replaced with a
CodeDiffPane. The original pane is hidden viaHiddenPaneReason::TemporaryReplacement(replacement_id)and kept inpane_contentsfor later restoration. - Disabled: The diff opens in a new tab.
When the sidebar renders tab rows, PaneProps::new resolves all display properties from the visible pane. For a temporary replacement, the visible pane is the CodeDiffPane, so the sidebar shows:
- Icon:
WarpIcon::Diff(instead of the original terminal/agent icon) - Title: "Requested Edit" (hardcoded in
CodeDiffPane::from_view) - Type:
TypedPane::CodeDiff(loses all terminal-specific metadata like conversation title, working directory, git branch) - Badge/subtitle: empty
The pane header ("Requested Edit" with Refine/Done/Accept buttons) shown inside the pane content area is correct and is rendered by CodeDiffView::render_header_content — that is not affected by this change.
Proposed changes
1. Add original_pane_for_replacement lookup to PaneData
In app/src/pane_group/tree.rs, add a method that returns the original hidden pane's ID given a replacement pane ID:
pub fn original_pane_for_replacement(&self, replacement_pane_id: PaneId) -> Option<PaneId>
This scans hidden_panes for a TemporaryReplacement entry whose associated replacement ID matches. It follows the same pattern as the existing is_temporary_replacement method.
2. Expose through PaneGroup
In app/src/pane_group/mod.rs, add a thin delegation method:
pub fn original_pane_for_replacement(&self, replacement_pane_id: PaneId) -> Option<PaneId>
3. Update PaneProps::new to use original pane for display
In app/src/workspace/view/vertical_tabs.rs, modify PaneProps::new so that when the requested pane_id is a temporary replacement, it resolves the PaneConfiguration and TypedPane from the original hidden pane:
let display_pane_id = pane_group
.original_pane_for_replacement(pane_id)
.unwrap_or(pane_id);
let display_pane = pane_group.pane_by_id(display_pane_id)?;
let pane_configuration = display_pane.pane_configuration();
let typed = pane_group.resolve_pane_type(display_pane_id, app);
This makes the sidebar row render the original pane's icon, title, subtitle, badge, and all terminal-specific metadata (conversation title, working directory, git branch, status indicators).
Fields that should still use the replacement pane_id:
pane_id— click/focus targets the visible replacement paneis_focused— the replacement pane is what actually holds focusis_being_dragged— drag state belongs to the visible pane
End-to-end flow
- User is in an agent conversation (terminal pane, sidebar shows "Create Korean Poem File" with agent icon)
- Agent produces an edit tool call; user expands it
open_code_diffcreates aCodeDiffPaneand callsreplace_pane(focused_pane_id, new_pane, true), which:- Adds the original terminal pane to
hidden_panesasTemporaryReplacement(replacement_id) - Swaps the tree node to the
CodeDiffPane
- Adds the original terminal pane to
- Sidebar re-renders.
PaneProps::newreceives the replacementpane_id:- Looks up
original_pane_for_replacement(pane_id)→ finds the hidden terminal pane - Resolves
typed,pane_configuration, title, icon, etc. from the original terminal pane - Sidebar row shows "Create Korean Poem File" with agent icon and metadata (unchanged)
- Looks up
- User accepts/rejects the edit →
close_temporary_replacement_panereverts to the original pane - Sidebar naturally shows the original pane again (no special handling needed)
Risks and mitigations
- Original pane removed prematurely: For temporary replacements,
replace_paneexplicitly skips removing the original frompane_contents. The original pane is guaranteed to exist for lookup. No new risk. - Multiple temporary replacements: If multiple diffs are expanded in sequence on the same pane, the previous replacement is reverted first (via
close_temporary_replacement_pane) before a new one is created. The lookup remains 1:1. - Non-
ExpandEditToPanepath: When the flag is disabled, diffs open in a new tab (not a replacement).original_pane_for_replacementreturnsNone, andPaneProps::newfalls through to the existing behavior. No regression. - Detail sidecar: The detail sidecar (hover popup) also resolves from
PaneProps. Using the original pane's type means the sidecar will show terminal-specific detail (working directory, git branch, etc.) instead of code-diff detail. This is the correct behavior since the tab still conceptually represents the agent conversation.
Testing and validation
- Manual: expand an agent edit tool call with
ExpandEditToPaneenabled. Verify the sidebar row keeps the original pane's icon, title, subtitle, and any badges. Verify clicking the sidebar row still focuses the diff pane. Verify accept/reject restores the original pane normally. - Manual: repeat with
ExpandEditToPanedisabled. Verify no regression — diff opens in a new tab with "Requested Edit" title as before. - Unit test: Add a test in
tree_tests.rsfororiginal_pane_for_replacement— verify it returnsSome(original_id)after a temporary replacement andNoneotherwise.
Follow-ups
- Consider whether
CodeDiffPanestill needs a hardcoded "Requested Edit" title at all, since the pane header text comes fromCodeDiffView::render_header_contentindependently. ThePaneConfigurationtitle is only relevant when the pane is shown in a new tab (non-replacement path). Leaving it as-is is safe.