mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-28 04:37:37 +00:00
🫥 fix: Hide Quote Popup When Selection Collapses Silently (#13936)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
The "Add to chat" popup lingered over an empty caret after a selection collapsed through a path that fires no mouse/key event — most often a streaming markdown re-render replacing the selected text node. The selection state only updated on mouseup/dblclick/keyup/scroll/resize, so a silent collapse left the button stranded ("showing up with nothing selected").
Add a `selectionchange` listener that hides the popup the instant the selection collapses or empties. It only hides, never shows, so an in-progress drag-select still won't flicker the popup.
Adds an e2e that collapses the selection without a mouse event and asserts the popup disappears.
This commit is contained in:
parent
82662443e5
commit
189cb245c2
2 changed files with 34 additions and 0 deletions
|
|
@ -90,12 +90,24 @@ function QuoteButton({ conversationId }: { conversationId: string }) {
|
|||
setPos(null);
|
||||
};
|
||||
const clearSelection = () => setSelection(null);
|
||||
/** Hide the popup the instant the selection collapses or empties, including
|
||||
* paths that fire no mouse/key event — e.g. a streaming markdown re-render
|
||||
* replacing the selected text node, which would otherwise leave the button
|
||||
* stranded over a now-collapsed caret. Only hides here; showing stays gated
|
||||
* on mouseup/dblclick/keyup so an in-progress drag never flickers it. */
|
||||
const handleSelectionChange = () => {
|
||||
const sel = window.getSelection();
|
||||
if (!sel || sel.rangeCount === 0 || sel.isCollapsed) {
|
||||
setSelection(null);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mouseup', updateSelection);
|
||||
/** Chromium commits a double-click word selection on `dblclick`, after
|
||||
* `mouseup` has already read a still-collapsed range, so listen here too. */
|
||||
document.addEventListener('dblclick', updateSelection);
|
||||
document.addEventListener('keyup', updateSelection);
|
||||
document.addEventListener('selectionchange', handleSelectionChange);
|
||||
document.addEventListener('scroll', clearSelection, true);
|
||||
window.addEventListener('resize', clearSelection);
|
||||
|
||||
|
|
@ -103,6 +115,7 @@ function QuoteButton({ conversationId }: { conversationId: string }) {
|
|||
document.removeEventListener('mouseup', updateSelection);
|
||||
document.removeEventListener('dblclick', updateSelection);
|
||||
document.removeEventListener('keyup', updateSelection);
|
||||
document.removeEventListener('selectionchange', handleSelectionChange);
|
||||
document.removeEventListener('scroll', clearSelection, true);
|
||||
window.removeEventListener('resize', clearSelection);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -168,6 +168,27 @@ test.describe('quote references', () => {
|
|||
await expect(pendingChips(page)).toContainText(/E2E|mock|reply/i);
|
||||
});
|
||||
|
||||
test('hides the popup when the selection collapses without a mouse event', async ({ page }) => {
|
||||
test.setTimeout(120000);
|
||||
await page.goto(NEW_CHAT_PATH, { timeout: 10000 });
|
||||
await selectMockEndpoint(page, MOCK_ENDPOINTS[0]);
|
||||
|
||||
const response = await sendMessage(page, 'seed for collapse');
|
||||
expect(response.ok()).toBeTruthy();
|
||||
await expect(mockReply(page)).toBeVisible({ timeout: 20000 });
|
||||
|
||||
await expect(async () => {
|
||||
await doubleClickWord(page, MOCK_REPLY_TEXT);
|
||||
await expect(addToChat(page)).toBeVisible({ timeout: 3000 });
|
||||
}).toPass({ timeout: 30000 });
|
||||
|
||||
// Collapse the selection the way a streaming markdown re-render does —
|
||||
// dropping the selected text node fires only `selectionchange`, not a
|
||||
// mouse/key event. The popup must not linger over the now-empty caret.
|
||||
await page.evaluate(() => window.getSelection()?.collapseToEnd());
|
||||
await expect(addToChat(page)).toBeHidden({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('collapses multiple selections into one chip with a hover popup, and removes one', async ({
|
||||
page,
|
||||
}) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue