fix(web): keep configuration chat launcher visible (#3062)

This commit is contained in:
Carlos Florêncio
2026-08-26 15:53:56 +00:00
committed by GitHub
parent 99c37aa096
commit 49dd944bd3
7 changed files with 230 additions and 10 deletions
@@ -12,7 +12,6 @@ import { QuickChatSessionView } from "@/components/quick-chat/quick-chat-session
import { isQuickChatSetupSessionId } from "@/lib/state/slices/ui/quick-chat-session";
import { ConfigChatSetup } from "./config-chat-setup";
import { useConfigChat } from "./use-config-chat";
import { cn } from "@/lib/utils";
function useConfigChatPanelStore() {
return useAppStore(
@@ -155,12 +154,7 @@ export const ConfigChatPanel = memo(function ConfigChatPanel({
<PopoverTrigger asChild>
<Button
size="icon"
aria-hidden={panel.isOpen}
tabIndex={panel.isOpen ? -1 : undefined}
className={cn(
"fixed bottom-[calc(1.5rem+var(--app-status-bar-height))] right-6 z-50 h-12 w-12 cursor-pointer rounded-full shadow-lg",
panel.isOpen && "pointer-events-none opacity-0",
)}
className="fixed bottom-[calc(1.5rem+var(--app-status-bar-height))] right-6 z-50 h-12 w-12 cursor-pointer rounded-full shadow-lg"
aria-label={t("common:configurationChat")}
>
<IconSparkles className="h-6 w-6" />
@@ -140,6 +140,19 @@ test.describe("Configuration Chat", () => {
}
});
test("keeps the floating launcher visible and operable while open", async ({ testPage }) => {
const popover = await openConfigChatFromSettings(testPage);
const launcher = testPage.getByRole("button", { name: "Configuration Chat", exact: true });
await expect(launcher).toBeVisible();
await expect(launcher).toBeEnabled();
await expect(launcher).not.toHaveAttribute("aria-hidden", "true");
await expect(launcher).not.toHaveAttribute("tabindex", "-1");
await launcher.click();
await expect(popover).not.toBeVisible();
});
test("starts floating, expands the same session, restores, and continues", async ({
testPage,
}) => {
@@ -1,6 +1,31 @@
import { test, expect } from "../../fixtures/test-base";
test.describe("Configuration Chat on mobile", () => {
test("keeps the floating launcher visible and operable while open", async ({ testPage }) => {
await testPage.goto("/settings/agents");
await testPage.getByRole("button", { name: "Configuration Chat" }).tap();
const popover = testPage.getByTestId("config-chat-popover");
const launcher = testPage.getByRole("button", { name: "Configuration Chat", exact: true });
await expect(popover).toBeVisible({ timeout: 10_000 });
await expect(launcher).toBeVisible();
await expect(launcher).toBeEnabled();
await expect(launcher).not.toHaveAttribute("aria-hidden", "true");
await expect(launcher).not.toHaveAttribute("tabindex", "-1");
const viewport = testPage.viewportSize();
const launcherBox = await launcher.boundingBox();
expect(viewport).not.toBeNull();
expect(launcherBox).not.toBeNull();
expect(launcherBox!.x).toBeGreaterThanOrEqual(0);
expect(launcherBox!.x + launcherBox!.width).toBeLessThanOrEqual(viewport!.width);
expect(launcherBox!.y).toBeGreaterThanOrEqual(0);
expect(launcherBox!.y + launcherBox!.height).toBeLessThanOrEqual(viewport!.height);
await launcher.tap();
await expect(popover).not.toBeVisible();
});
test("starts floating, expands full-screen, and answers a clarification", async ({
testPage,
apiClient,
@@ -0,0 +1,88 @@
---
created: 2026-08-26
status: done
requirements:
- REQ-UI-QUICK-TERMINAL-001
system_design:
- ../../specs/ui/system-design/quick-terminal.md
legacy_specs: []
---
# Implementation Plan: Configuration Chat Launcher Visibility
## Overview
Keep the Configuration Chat floating launcher visible and operable while its Settings popover is
open. The current component deliberately hides and disables the trigger in the open state even
though the popover is already positioned above it. One focused TDD work order adds desktop and
mobile regression coverage before removing that open-state suppression.
The UI system owns this repair because the defect affects the presentation contract of an existing
launcher. Task-backed configuration-chat identity, persistence, and lifecycle remain unchanged.
## Scope
### In scope
- Preserve the floating launcher below the open Configuration Chat panel on Settings routes.
- Let pointer, keyboard, and touch activation of the visible launcher close the open panel through
the existing popover state transition.
- Prove the same launcher outcome in desktop Chromium and the mobile Chrome project.
### Out of scope
- Configuration-chat creation, persistence, agent-profile selection, or session restoration.
- Popover dimensions, panel content, the header close action, or Quick Chat expansion behavior.
- Settings floating-save placement and behavior.
## Technical approach
### Launcher state
Update `ConfigChatPanel` in
`apps/web/components/config-chat/config-chat-panel.tsx` so the floating `PopoverTrigger` retains
its normal accessibility, focus, pointer, and opacity state while `panel.isOpen` is true. Preserve
the controlled `Popover`, its existing open-change handler, fixed launcher geometry, and tooltip
suppression. Activating the retained trigger then closes the same popover without adding another
state path.
### Responsive contract
Desktop and mobile keep the current fixed bottom-right entry point and top-aligned popover. The
launcher is the primary thumb-reachable floating action on phones. The panel remains the only
scroll surface. The change does not affect safe areas or breakpoints. The nearest mobile exemplar
is `components/kanban/mobile-fab.tsx` for the persistent, touch-sized floating action. The existing
Configuration Chat popover remains the source for the surface geometry.
## Tests
- `AC-UI-QUICK-TERMINAL-001.2`: extend
`apps/web/e2e/tests/settings/config-chat-popover.spec.ts` to assert that the launcher remains
visible and enabled after opening the popover, then closes the panel when activated again.
## E2E tests
- Desktop Chromium: run the focused Configuration Chat popover spec and prove launcher visibility,
operability, and toggle-close behavior.
- Mobile Chrome: extend `apps/web/e2e/tests/settings/mobile-configuration-chat.spec.ts` with the
same touch path, including full launcher containment in the viewport after the panel opens.
## Work orders
- [x] [Task 01: Keep the Configuration Chat launcher visible](task-01-keep-launcher-visible.md)
## Verification results
Passed on 2026-08-26:
- `cd apps && pnpm install --frozen-lockfile`
- `cd apps/web && pnpm run typecheck`
- `cd apps/web && pnpm e2e:run e2e/tests/settings/config-chat-popover.spec.ts` (6 passed)
- `cd apps/web && pnpm e2e:run --project mobile-chrome e2e/tests/settings/mobile-configuration-chat.spec.ts` (2 passed)
## Risks
- The visible trigger shares the popover's anchor position. Regression coverage must prove that it
does not block panel controls or move outside a narrow viewport.
- A tooltip must not open over the active panel when focus returns to or hovers the retained
launcher.
@@ -0,0 +1,93 @@
---
id: "01-keep-launcher-visible"
title: "Keep the Configuration Chat launcher visible"
status: done
wave: 1
depends_on: []
plan: "plan.md"
requirements:
- REQ-UI-QUICK-TERMINAL-001
acceptance_criteria:
- AC-UI-QUICK-TERMINAL-001.2
system_design:
- ../../specs/ui/system-design/quick-terminal.md
---
# Task 01: Keep the Configuration Chat launcher visible
## Summary
Add a failing desktop and mobile regression for the Settings Configuration Chat launcher, then
remove the component's open-state visibility and interaction suppression. Keep the existing
controlled popover and responsive geometry as the only open/close mechanism.
## In scope
- Add desktop E2E assertions that the launcher stays visible and enabled with the popover open and
closes the panel when activated again.
- Add the equivalent touch and viewport-containment assertions to the mobile Configuration Chat
flow.
- Remove the launcher's open-state `aria-hidden`, tab-order removal, pointer-event suppression, and
zero-opacity styling while preserving tooltip suppression during the open state.
## Out of scope
- Changing Configuration Chat content, session behavior, or the header close action.
- Repositioning or resizing the popover, floating-save host, or launcher.
- Adding new user-facing copy or translation keys.
## Acceptance
- The floating Configuration Chat launcher remains visible, enabled, keyboard-focusable, and
touch-operable while its Settings popover is open.
- Activating the retained launcher closes the popover through the existing `onOpenChange` path.
- Focused desktop and mobile E2E tests pass without document horizontal overflow or launcher
clipping.
## Verification
```bash
cd apps && pnpm install --frozen-lockfile
cd apps/web && pnpm run typecheck
cd apps/web && pnpm e2e:run e2e/tests/settings/config-chat-popover.spec.ts
cd apps/web && pnpm e2e:run --project mobile-chrome e2e/tests/settings/mobile-configuration-chat.spec.ts
```
## Files likely touched
- `apps/web/components/config-chat/config-chat-panel.tsx`
- `apps/web/e2e/tests/settings/config-chat-popover.spec.ts`
- `apps/web/e2e/tests/settings/mobile-configuration-chat.spec.ts`
## Dependencies
None.
## Risks
- Radix trigger focus and tooltip behavior can overlap the open popover if tooltip suppression is
removed accidentally.
## Parallelism
`sequential`
## Inputs
- `REQ-UI-QUICK-TERMINAL-001` and `AC-UI-QUICK-TERMINAL-001.2`.
- `docs/specs/ui/system-design/quick-terminal.md` launcher and responsive behavior.
- Existing Configuration Chat desktop and mobile Playwright specs.
## Results
Implemented on 2026-08-26. The floating trigger no longer receives open-state `aria-hidden`,
tab-order removal, pointer-event suppression, or zero-opacity styling. Tooltip suppression remains
active while the popover is open. Desktop and mobile E2E coverage verifies visibility, enabled state,
accessibility attributes, viewport containment on mobile, and toggle-close behavior.
Verification passed:
- `cd apps && pnpm install --frozen-lockfile`
- `cd apps/web && pnpm run typecheck`
- `cd apps/web && pnpm e2e:run e2e/tests/settings/config-chat-popover.spec.ts` (6 passed)
- `cd apps/web && pnpm e2e:run --project mobile-chrome e2e/tests/settings/mobile-configuration-chat.spec.ts` (2 passed)
+2 -2
View File
@@ -2,7 +2,7 @@
status: active
system: ui
created: 2026-08-03
updated: 2026-08-06
updated: 2026-08-26
owners:
- kandev
---
@@ -21,7 +21,7 @@ Quick Chat and Quick Terminal are both short-lived utilities reached from the sa
#### Acceptance criteria
- **AC-UI-QUICK-TERMINAL-001.1:** Quick Chat is the single responsive dialog for ordinary chats, configuration chats, and host terminal tabs. Quick Terminal no longer opens a separate dialog.
- **AC-UI-QUICK-TERMINAL-001.2:** Existing conversation launchers preserve their kind-specific behavior: generic Quick Chat shortcuts select an ordinary chat, configuration entry points select the workspace's configuration chat, and either opens its existing setup when no matching conversation exists.
- **AC-UI-QUICK-TERMINAL-001.2:** Existing conversation launchers preserve their kind-specific behavior. A generic Quick Chat launcher selects an ordinary chat. A configuration entry point selects the workspace's configuration chat. Each launcher opens its setup when no matching conversation exists. On Settings routes, the Configuration Chat floating launcher remains visible and operable while its panel is open. Another activation closes the panel.
- **AC-UI-QUICK-TERMINAL-001.3:** The existing Quick Terminal launchers use a reuse-or-create policy scoped to the active workspace: they open the most recently activated terminal tab when one exists, and create the first terminal tab otherwise.
- **AC-UI-QUICK-TERMINAL-001.4:** The tab-strip plus button opens a creation menu grouped like the task-detail Dockview add menu: an **Agents** section with **New Agent**, a separator, and a **Terminals** section with **New Terminal**. Existing tabs remain directly selectable in the tab strip rather than being duplicated in the creation menu. Because the plus button sits at the leading edge of the tab strip, its menu opens toward the trailing edge (aligned to the button's start) so it does not overhang the workspace edge.
- **AC-UI-QUICK-TERMINAL-001.5:** Choosing **New Agent** preserves the current ordinary/configuration setup flow. Choosing **New Terminal** always creates and activates a distinct host-shell terminal, even when another terminal exists.
@@ -4,7 +4,7 @@ system: ui
requirements:
- REQ-UI-QUICK-TERMINAL-001
created: 2026-08-03
updated: 2026-08-06
updated: 2026-08-26
owners:
- kandev
---
@@ -36,6 +36,10 @@ them without losing work, and return to the most recent terminal without managin
- Existing conversation launchers preserve their kind-specific behavior: generic Quick Chat
shortcuts select an ordinary chat, configuration entry points select the workspace's
configuration chat, and either opens its existing setup when no matching conversation exists.
- `ConfigChatPanel` keeps its floating `PopoverTrigger` mounted, visible, and operable while the
controlled popover is open. The existing top-aligned popover geometry leaves the trigger below
the panel on desktop and phone viewports, and a second trigger activation follows the same close
path as the panel header. The trigger tooltip stays suppressed while the panel is open.
- The existing Quick Terminal launchers use a reuse-or-create policy scoped to the active workspace:
they open the most recently activated terminal tab when one exists, and create the first terminal
tab otherwise.
@@ -257,6 +261,9 @@ checks, and Agents-page authorization behavior remain unchanged.
- **GIVEN** a terminal and an ordinary chat both exist, **WHEN** the user alternates the Quick
Terminal and generic Quick Chat launchers, **THEN** each launcher opens the most recently active
matching tab without changing configuration-chat launcher behavior.
- **GIVEN** Configuration Chat is open on a Settings route, **WHEN** the floating launcher renders
below the panel, **THEN** it remains visible, keyboard-focusable, and touch-operable, and another
activation closes the panel.
- **GIVEN** an active terminal tab with sibling tabs, **WHEN** the user closes it, **THEN** only its
PTY is stopped and the nearest remaining same-workspace tab becomes active.
- **GIVEN** terminal tabs belong to two workspaces, **WHEN** the active workspace changes, **THEN**