Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/core/src/client/AgentPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,7 @@
<DesignSystemTooltip
trigger={
<button
onClick={() => switchMode("chat")}

Check warning on line 1355 in packages/core/src/client/AgentPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useCallback has a missing dependency: 'switchMode'
aria-label={t("agentPanel.chatMode")}
className={cn(
"flex items-center gap-1 rounded-md px-2 py-1 text-[12px] leading-none",
Expand Down Expand Up @@ -1696,7 +1696,7 @@
) : null}
{mode === "chat" &&
newTabMode !== "cli" &&
onNewCliTab &&

Check warning on line 1699 in packages/core/src/client/AgentPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useCallback has missing dependencies: 'onNewCliTab', and 'toggleSidebarHint'
newCliTabLabel ? (
<DropdownMenuItem onSelect={onNewCliTab}>
<IconTerminal2 size={14} className="shrink-0" />
Expand Down Expand Up @@ -1870,47 +1870,47 @@
)}
</div>
),
[
activeCliTab,
addCliTab,
availableClis,
canUseCodeTools,
closeHeaderMenuForOverlay,
closeAllCliTabs,
closeAllTabsHint,
closeCliTab,
closeOtherCliTabs,
closeTabHint,
feedbackOpen,
feedbackEnabled,
getChatThreadShareUrl,
headerMenuOpen,
isWideDrawer,
mode,
newTabMode,
newCliTabLabel,
newUiTabLabel,
agentPageHref,
fullViewAction,
onCollapse,
onFullViewRequest,
onExitWideDrawer,
onSnapTo75Percent,
openRunThread,
openNewCliTab,
openNewUiTab,
renderCliTab,
selectCli,
selectedCli,
shareFromMenuOpen,
showCollapseButton,
storageKey,
switchMode,
t,
wideDrawerAction,
wideDrawerLabel,
widenChatHint,
],

Check warning on line 1913 in packages/core/src/client/AgentPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useCallback has unnecessary dependency: onExitWideDrawer

Check warning on line 1913 in packages/core/src/client/AgentPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useCallback has unnecessary dependency: onSnapTo75Percent

Check warning on line 1913 in packages/core/src/client/AgentPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useCallback has unnecessary dependency: switchMode

Check warning on line 1913 in packages/core/src/client/AgentPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useCallback has unnecessary dependency: addCliTab

Check warning on line 1913 in packages/core/src/client/AgentPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useCallback has unnecessary dependency: agentPageHref

Check warning on line 1913 in packages/core/src/client/AgentPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useCallback has unnecessary dependency: isWideDrawer
);

const renderPageChatOverlay = useCallback(
Expand Down Expand Up @@ -2582,7 +2582,8 @@
<style
dangerouslySetInnerHTML={{
__html:
".agent-tab-close{opacity:0}.agent-tab:hover .agent-tab-close{opacity:1}" +
".agent-tab-close{opacity:0;pointer-events:none}" +
".agent-tab:hover .agent-tab-close,.agent-tab-close:focus-visible{opacity:1;pointer-events:auto}" +
".agent-tabs-scroll{scrollbar-width:none;-ms-overflow-style:none;}" +
".agent-tabs-scroll::-webkit-scrollbar{display:none;}" +
`[data-agent-fullscreen='true'] .agent-thread-content,` +
Expand Down
47 changes: 47 additions & 0 deletions packages/core/src/client/MultiTabAssistantChat.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2140,6 +2140,53 @@ describe("MultiTabAssistantChat tab close/open lifecycle", () => {
]);
});

// Regression test: a click anywhere in a tab's close-button hit zone used to
// close it even though nothing was visibly clickable there. Each tab must
// render its own labeled close button, and switching tabs must never fire
// the close handler for the tab that was clicked to switch to.
it("renders a labeled close button per tab and only closes the tab whose close button is clicked", async () => {
threadMocks.activeThreadId = "thread-1";
threadMocks.threads = [makeThread("thread-1"), makeThread("thread-2")];
window.localStorage.setItem(
openTabsStorageKey("close-button-test"),
JSON.stringify(["thread-1", "thread-2"]),
);

await act(async () => {
root.render(<MultiTabAssistantChat storageKey="close-button-test" />);
});
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});

const closeButtons = () =>
Array.from(
container.querySelectorAll<HTMLButtonElement>(
'button[aria-label="Close tab"]',
),
);
expect(closeButtons()).toHaveLength(2);

// The switch button is a separate element from the close button, so
// clicking it must never remove the tab.
const secondTabSwitchButton = container.querySelectorAll<HTMLButtonElement>(
".agent-tab > button:first-child",
)[1];
expect(secondTabSwitchButton).toBeTruthy();
act(() => {
secondTabSwitchButton.click();
});
expect(closeButtons()).toHaveLength(2);
expect(threadMocks.switchThread).toHaveBeenCalledWith("thread-2");

// Clicking the explicit, labeled close button removes only that tab.
act(() => {
closeButtons()[1].click();
});
expect(closeButtons()).toHaveLength(1);
});

it("migrates legacy open tabs and sub-agent metadata into this browser tab", async () => {
const storageKey = "legacy-tab-migration-test";
const child = makeThread("thread-child");
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/client/MultiTabAssistantChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,7 @@
threadModelRef.current.get(threadId) ?? persistedModelSelection,
availableModels,
),
[availableModels, persistedModelSelection, modelSelectionVersion],

Check warning on line 1219 in packages/core/src/client/MultiTabAssistantChat.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useCallback has unnecessary dependency: modelSelectionVersion
);

const persistModelSelection = useCallback(
Expand Down Expand Up @@ -1520,7 +1520,7 @@
openTabsKeyRef.current = OPEN_TABS_KEY;
initializedRef.current = false;
if (!restoreActiveThread) {
setOpenTabIds(activeThreadId ? [activeThreadId] : []);

Check warning on line 1523 in packages/core/src/client/MultiTabAssistantChat.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React Hook useEffect has a missing dependency: 'setOpenTabIds'
return;
}
const restored = readStoredOpenTabs();
Expand Down Expand Up @@ -2710,7 +2710,8 @@
<style
dangerouslySetInnerHTML={{
__html:
".agent-tab-close{opacity:0}.agent-tab:hover .agent-tab-close{opacity:1}" +
".agent-tab-close{opacity:0;pointer-events:none}" +
".agent-tab:hover .agent-tab-close,.agent-tab-close:focus-visible{opacity:1;pointer-events:auto}" +
".agent-tabs-scroll{scrollbar-width:none;-ms-overflow-style:none;}" +
".agent-tabs-scroll::-webkit-scrollbar{display:none;}",
}}
Expand Down
Loading