fix: prevent tab maximize while renaming (#3192)
This commit is contained in:
@@ -40,6 +40,7 @@ export function TabRenameInput({
|
||||
className="flex h-full items-center gap-1 px-2"
|
||||
// Stop the click from selecting the tab while we type.
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onDoubleClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
{seqBadge != null && (
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { test, expect } from "../../fixtures/test-base";
|
||||
import { SessionPage } from "../../pages/session-page";
|
||||
|
||||
test.describe("Session tab rename", () => {
|
||||
// @covers AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.1 through .3
|
||||
test("keeps rename-input double-clicks out of the maximize handler", async ({
|
||||
testPage,
|
||||
apiClient,
|
||||
seedData,
|
||||
}) => {
|
||||
const task = await apiClient.createTaskWithAgent(
|
||||
seedData.workspaceId,
|
||||
"Agent Tab Rename Double-click",
|
||||
seedData.agentProfileId,
|
||||
{
|
||||
description: "/e2e:simple-message",
|
||||
workflow_id: seedData.workflowId,
|
||||
workflow_step_id: seedData.startStepId,
|
||||
repository_ids: [seedData.repositoryId],
|
||||
},
|
||||
);
|
||||
if (!task.session_id) throw new Error("createTaskWithAgent did not return a session_id");
|
||||
|
||||
await testPage.goto(`/t/${task.id}`);
|
||||
const session = new SessionPage(testPage);
|
||||
await session.waitForLoad();
|
||||
await session.waitForChatIdle({ timeout: 30_000 });
|
||||
|
||||
const tab = session.sessionTabBySessionId(task.session_id);
|
||||
await expect(tab).toBeVisible();
|
||||
const header = testPage.locator(
|
||||
`.dv-tabs-and-actions-container:has([data-testid="session-tab-${task.session_id}"])`,
|
||||
);
|
||||
const maximizeButton = header.getByTestId("dockview-maximize-btn");
|
||||
await expect(maximizeButton.locator(".tabler-icon-arrows-maximize")).toBeVisible();
|
||||
|
||||
await tab.click({ button: "right" });
|
||||
await session.contextMenuItem("Rename").click();
|
||||
const input = testPage.getByTestId("session-tab-rename-input");
|
||||
await input.click();
|
||||
await expect(input).toBeFocused();
|
||||
await input.fill("implementation");
|
||||
await input.dblclick();
|
||||
await expect(maximizeButton.locator(".tabler-icon-arrows-maximize")).toBeVisible();
|
||||
const selection = await input.evaluate((element: HTMLInputElement) => ({
|
||||
start: element.selectionStart,
|
||||
end: element.selectionEnd,
|
||||
length: element.value.length,
|
||||
}));
|
||||
expect(selection).toEqual({ start: 0, end: 14, length: 14 });
|
||||
|
||||
await testPage.keyboard.press("Escape");
|
||||
await expect(input).not.toBeVisible();
|
||||
await tab.dblclick();
|
||||
await expect(maximizeButton.locator(".tabler-icon-arrows-minimize")).toBeVisible();
|
||||
|
||||
await tab.click({ button: "right" });
|
||||
await session.contextMenuItem("Rename").click();
|
||||
await input.click();
|
||||
await expect(input).toBeFocused();
|
||||
await input.fill("implementation");
|
||||
await input.dblclick();
|
||||
await expect(maximizeButton.locator(".tabler-icon-arrows-minimize")).toBeVisible();
|
||||
await testPage.keyboard.press("Escape");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
created: 2026-08-31
|
||||
status: done
|
||||
requirements:
|
||||
- REQ-UI-TASK-AGENT-TAB-RECONCILIATION-002
|
||||
system_design:
|
||||
- ../../specs/ui/system-design/task-agent-tab-reconciliation.md
|
||||
legacy_specs: []
|
||||
---
|
||||
|
||||
# Implementation Plan: Isolate Agent Tab Rename Double-clicks
|
||||
|
||||
## Overview
|
||||
|
||||
Keep text-selection double-clicks inside the shared tab rename editor so they
|
||||
cannot reach the surrounding Agent-tab maximize handler. One work order adds a
|
||||
browser regression first, applies the shared event-boundary correction, and
|
||||
runs the focused frontend checks.
|
||||
|
||||
## Scope
|
||||
|
||||
### In scope
|
||||
|
||||
- Preserve native double-click text selection in an active Agent-tab rename
|
||||
input.
|
||||
- Prevent that double-click from maximizing or restoring the Dockview group.
|
||||
- Preserve tab-level double-click maximize behavior outside rename mode.
|
||||
- Apply the isolation through the shared task tab rename editor.
|
||||
|
||||
### Out of scope
|
||||
|
||||
- Session-name persistence, validation, commit, cancel, or error behavior.
|
||||
- Dockview maximize state management or layout persistence.
|
||||
- Phone and tablet session-control composition.
|
||||
- Quick Chat tab rename behavior, which uses a different editor.
|
||||
|
||||
## Confirmed root cause
|
||||
|
||||
`SessionTab` mounts `TabRenameInput` inside a `ContextMenuTrigger` whose
|
||||
`onDoubleClick` calls `useTabMaximizeOnDoubleClick`. The editor stops bubbling
|
||||
`mousedown` and `click`, but browsers emit a separate bubbling `dblclick` event.
|
||||
That event reaches the parent maximize handler, which prevents the default text
|
||||
selection and toggles the Dockview group.
|
||||
|
||||
## Technical approach
|
||||
|
||||
- Add a focused Chromium Playwright scenario in
|
||||
`apps/web/e2e/tests/session/session-tab-rename.spec.ts`. Enter Agent-tab rename
|
||||
mode from the context menu, double-click the input, assert native selection,
|
||||
and assert that the group remains unmaximized.
|
||||
- Update `TabRenameInput` in
|
||||
`apps/web/components/task/tab-rename-input.tsx` to stop propagation of the
|
||||
shared editor's `dblclick` event without calling `preventDefault`.
|
||||
- Leave `SessionTab` and `useTabMaximizeOnDoubleClick` unchanged so ordinary
|
||||
tab-surface double-clicks retain their current maximize-or-restore behavior.
|
||||
|
||||
## Tests
|
||||
|
||||
- `AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.2`: the Playwright regression
|
||||
verifies selected input text and unchanged maximize state after an editor
|
||||
double-click.
|
||||
- `AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.3`: the same scenario verifies the
|
||||
existing tab-surface maximize path remains available outside the editor.
|
||||
|
||||
## E2E tests
|
||||
|
||||
- Chromium:
|
||||
`apps/web/e2e/tests/session/session-tab-rename.spec.ts` covers the affected
|
||||
fine-pointer Dockview workflow.
|
||||
- No mobile E2E change is required because phone and tablet task surfaces do
|
||||
not mount desktop Dockview Agent tabs or their maximize gesture.
|
||||
|
||||
## Work orders
|
||||
|
||||
- [x] [Task 01: Isolate Rename Input Double-clicks](task-01-isolate-rename-input-double-clicks.md)
|
||||
|
||||
## Verification results
|
||||
|
||||
Completed on 2026-08-31.
|
||||
|
||||
- The Chromium regression failed before the fix because the rename-input
|
||||
double-click removed the group's maximize icon by maximizing the group.
|
||||
- The rebuilt production E2E bundle passed the focused scenario after the
|
||||
shared editor stopped the bubbling `dblclick` event.
|
||||
- Web typecheck, focused ESLint, and `git diff --check` passed.
|
||||
- No mobile test changed because phone and tablet do not mount the affected
|
||||
Dockview Agent-tab surface.
|
||||
|
||||
## Risks
|
||||
|
||||
- Calling `preventDefault` at the editor boundary would fix maximize while also
|
||||
breaking the text-selection behavior being protected. The correction must
|
||||
stop propagation only.
|
||||
- Testing only the two individual click events would miss the separately
|
||||
dispatched `dblclick` that causes the regression.
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
---
|
||||
id: "01-isolate-rename-input-double-clicks"
|
||||
title: "Isolate Rename Input Double-clicks"
|
||||
status: done
|
||||
wave: 1
|
||||
depends_on: []
|
||||
plan: "plan.md"
|
||||
requirements:
|
||||
- REQ-UI-TASK-AGENT-TAB-RECONCILIATION-002
|
||||
acceptance_criteria:
|
||||
- AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.1
|
||||
- AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.2
|
||||
- AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.3
|
||||
system_design:
|
||||
- ../../specs/ui/system-design/task-agent-tab-reconciliation.md
|
||||
---
|
||||
|
||||
# Task 01: Isolate Rename Input Double-clicks
|
||||
|
||||
## Summary
|
||||
|
||||
Add a failing desktop browser regression for the Agent-tab rename interaction,
|
||||
then make the shared task tab rename editor contain double-clicks without
|
||||
disabling native input selection. Preserve the existing maximize behavior for
|
||||
double-clicks outside rename mode.
|
||||
|
||||
## In scope
|
||||
|
||||
- Add the focused Agent-tab rename Playwright regression.
|
||||
- Stop bubbling `dblclick` events at `TabRenameInput`.
|
||||
- Verify native selection and unchanged Dockview maximize state.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Rename persistence and backend session APIs.
|
||||
- Dockview layout or maximize-state refactors.
|
||||
- Quick Chat rename behavior.
|
||||
- Phone and tablet UI changes.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- The new Playwright scenario fails on the current code because an editor
|
||||
double-click maximizes the Agent-tab group.
|
||||
- Double-clicking the active rename input selects text without changing the
|
||||
Dockview group's maximize state.
|
||||
- Double-clicking the non-editing Agent-tab surface still toggles maximize.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
pnpm e2e:run tests/session/session-tab-rename.spec.ts
|
||||
pnpm run typecheck
|
||||
pnpm exec eslint components/task/tab-rename-input.tsx e2e/tests/session/session-tab-rename.spec.ts
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Run the first three commands from `apps/web`. Run `git diff --check` from the
|
||||
repository root.
|
||||
|
||||
## Files likely touched
|
||||
|
||||
- `apps/web/e2e/tests/session/session-tab-rename.spec.ts`
|
||||
- `apps/web/components/task/tab-rename-input.tsx`
|
||||
|
||||
## Dependencies
|
||||
|
||||
None.
|
||||
|
||||
## Risks
|
||||
|
||||
- Preventing the default `dblclick` action would suppress native input text
|
||||
selection.
|
||||
- A selector that observes a different Dockview group could allow the browser
|
||||
regression to pass while the Agent group still toggles.
|
||||
|
||||
## Parallelism
|
||||
|
||||
`sequential`
|
||||
|
||||
## Inputs
|
||||
|
||||
- `REQ-UI-TASK-AGENT-TAB-RECONCILIATION-002`
|
||||
- `AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.1` through `.3`
|
||||
- `docs/specs/ui/system-design/task-agent-tab-reconciliation.md`
|
||||
- `apps/web/components/task/session-tab.tsx`
|
||||
- `apps/web/components/task/tab-rename-input.tsx`
|
||||
- `apps/web/components/task/use-tab-maximize.ts`
|
||||
|
||||
## Results
|
||||
|
||||
Completed on 2026-08-31.
|
||||
|
||||
- RED: `pnpm e2e:run --no-build tests/session/session-tab-rename.spec.ts`
|
||||
failed on the expected maximize-state assertion after an editor
|
||||
double-click.
|
||||
- GREEN: `pnpm e2e:run tests/session/session-tab-rename.spec.ts` rebuilt the
|
||||
production frontend and passed 1 Chromium test.
|
||||
- `pnpm run typecheck` passed.
|
||||
- `pnpm exec eslint components/task/tab-rename-input.tsx e2e/tests/session/session-tab-rename.spec.ts`
|
||||
passed.
|
||||
- `git diff --check` passed.
|
||||
@@ -43,13 +43,36 @@ task workbench becomes usable.
|
||||
shall continue to project the same task session membership through their
|
||||
existing session controls without mounting desktop workbench tabs.
|
||||
|
||||
### REQ-UI-TASK-AGENT-TAB-RECONCILIATION-002: Agent Tab Rename Isolation
|
||||
|
||||
**Intent:** Keep inline Agent-tab name editing independent from the desktop
|
||||
workbench commands attached to the surrounding tab.
|
||||
|
||||
#### Acceptance criteria
|
||||
|
||||
- **AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.1:** When a fine-pointer user
|
||||
chooses **Rename** for an Agent tab, the tab shall show a focused inline input
|
||||
with its current name selected.
|
||||
- **AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.2:** While the inline rename input
|
||||
is active, double-clicking its text shall preserve native text selection and
|
||||
shall not maximize or restore the surrounding Dockview group.
|
||||
- **AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.3:** Outside rename mode,
|
||||
double-clicking an eligible desktop workbench tab shall retain the existing
|
||||
maximize-or-restore behavior.
|
||||
- **AC-UI-TASK-AGENT-TAB-RECONCILIATION-002.4:** Phone and tablet session
|
||||
controls shall retain their existing rename and navigation behavior without
|
||||
exposing desktop Dockview maximize gestures.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Changing session creation, lifecycle, ordering, or backend APIs.
|
||||
- Changing saved desktop layout geometry or the active-panel rules for valid
|
||||
non-Agent panels.
|
||||
- Changing session-name persistence or the rename commit, cancel, and failure
|
||||
behavior.
|
||||
- Redesigning phone or tablet task navigation and session controls.
|
||||
|
||||
## Implementation plan
|
||||
|
||||
- [Command-center task focus repair](../../../plans/command-center-task-focus/plan.md)
|
||||
- [Agent tab rename double-click isolation](../../../plans/agent-tab-rename-double-click/plan.md)
|
||||
|
||||
@@ -3,6 +3,7 @@ status: current
|
||||
system: ui
|
||||
requirements:
|
||||
- REQ-UI-TASK-AGENT-TAB-RECONCILIATION-001
|
||||
- REQ-UI-TASK-AGENT-TAB-RECONCILIATION-002
|
||||
---
|
||||
|
||||
# Task Agent Tab Reconciliation System Design
|
||||
@@ -19,6 +20,7 @@ the desktop workbench.
|
||||
| Requirement | Design section |
|
||||
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `REQ-UI-TASK-AGENT-TAB-RECONCILIATION-001` | [Components and responsibilities](#components-and-responsibilities), [Control flow](#control-flow), [Responsive behavior](#responsive-behavior) |
|
||||
| `REQ-UI-TASK-AGENT-TAB-RECONCILIATION-002` | [Inline rename event boundary](#inline-rename-event-boundary), [Responsive behavior](#responsive-behavior), [Verification design](#verification-design) |
|
||||
|
||||
## Components and responsibilities
|
||||
|
||||
@@ -34,6 +36,27 @@ the desktop workbench.
|
||||
- `runAutoSessionTabEffect` reads the current application state, removes stale
|
||||
Agent panels, ensures the active session panel, and adds current sibling
|
||||
session panels without activating them.
|
||||
- `SessionTab` owns the fine-pointer Agent-tab context menu and attaches the
|
||||
shared Dockview maximize handler to the tab surface.
|
||||
- `TabRenameInput` owns the inline editor's pointer and keyboard event boundary.
|
||||
It is shared with task terminal-tab rename mode.
|
||||
- `useTabMaximizeOnDoubleClick` owns maximize-or-restore behavior outside an
|
||||
active inline editor.
|
||||
|
||||
## Inline rename event boundary
|
||||
|
||||
`SessionTab` renders `TabRenameInput` inside the same `ContextMenuTrigger` that
|
||||
receives tab-level double-clicks. Browsers dispatch `dblclick` as a separate
|
||||
bubbling event after the individual click events. Stopping only `mousedown` and
|
||||
`click` therefore does not isolate the editor from the tab-level maximize
|
||||
handler.
|
||||
|
||||
The shared rename editor stops propagation of `mousedown`, `click`, and
|
||||
`dblclick`. Its `dblclick` boundary does not prevent the default browser action,
|
||||
so native input text selection remains available. The parent tab maximize
|
||||
handler remains unchanged and continues to own double-clicks that originate
|
||||
outside the editor. This boundary also protects the shared task terminal rename
|
||||
editor without changing terminal naming behavior.
|
||||
|
||||
## Data and contracts
|
||||
|
||||
@@ -69,7 +92,8 @@ the effect reads the latest session list.
|
||||
Desktop uses Dockview Agent tabs. Phone and tablet layouts do not mount the
|
||||
desktop Dockview workbench. Their existing session controls continue to read
|
||||
the same application-store membership. No mobile composition or touch target
|
||||
changes.
|
||||
changes. Inline rename event isolation applies only where the desktop Dockview
|
||||
tab and editor are mounted.
|
||||
|
||||
## Verification design
|
||||
|
||||
@@ -77,4 +101,8 @@ A React hook regression test hydrates a multi-session task while the Dockview
|
||||
API is null. The test publishes the API later and verifies reconciliation.
|
||||
Existing pure reconciliation tests continue to cover active-tab and sibling
|
||||
behavior. A Playwright scenario opens a multi-session task from Cmd+K and
|
||||
verifies that every Agent tab appears without a reload.
|
||||
verifies that every Agent tab appears without a reload. A focused desktop
|
||||
Playwright regression enters Agent-tab rename mode, double-clicks the input,
|
||||
and verifies that native text selection remains active while the Dockview group
|
||||
stays unmaximized. Mobile coverage is unchanged because phone and tablet do not
|
||||
mount the affected Dockview tab surface.
|
||||
|
||||
Reference in New Issue
Block a user