Text within 44 px of the window top cannot be selected — clicks are swallowed as a title-bar drag
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
**Describe the bug**
Text near the top of the window cannot be selected. Click-dragging across it produces no selection at all — the cursor stays an I-beam and the click registers, but nothing highlights and Cmd+C copies nothing. Scrolling the same text further down the window makes it selectable again.
It reads as "copy is broken, I have to quit and reopen", because until you happen to scroll there is no feedback that the failure is positional.
Cause looks like the geometric fallback in `isWindowDragHandleEvent`:
```ts
// desktop/src/app/AppShell.helpers.ts:69
export function isWindowDragHandleEvent(event: MouseEvent | PointerEvent) {
if (isTauriDragRegionEvent(event)) return true;
if (event.clientY > WINDOW_DRAG_HANDLE_HEIGHT) return false; // 44
const target = event.target;
return !(target instanceof Element && target.closest(WINDOW_DRAG_INTERACTIVE_SELECTOR));
}
```
Any left-click within 44 px of the window top, on anything that is not a `button, a, input, textarea, select, label, summary, [role=…], [contenteditable=true], [tabindex]:not([tabindex="-1"])`, is treated as a title-bar drag. Message text is none of those. `useTauriWindowDrag` then does:
```ts
// desktop/src/app/useTauriWindowDrag.ts:20-22
event.preventDefault();
void getCurrentWindow().startDragging();
```
The `preventDefault()` cancels the pointer gesture, so a selection can never begin there. The test is purely geometric — it never checks that the point is actually over the top chrome.
**Two concrete consequences**
1. **Off-by-4 dead band.** `WINDOW_DRAG_HANDLE_HEIGHT` is `44` (`AppShell.helpers.ts:14`) but the rendered chrome is `TOP_CHROME_HEIGHT_DEFAULT = "40px"` (`shared/layout/chromeLayout.ts:5`, applied as `h-(--buzz-top-chrome-height,40px)`; nothing overrides that variable at runtime). So a 4 px strip of real content directly below the chrome swallows left-clicks as window drags.
2. **Views without the chrome lose the full 44 px.** `AppTopChrome` is only rendered when `!settingsOpen && !isHuddleRoom` (`AppShell.tsx:770`). In those views the fallback covers 44 px of ordinary content with no chrome above it at all.
**Steps to reproduce**
1. Open a channel and scroll so that a message's text sits within ~44 px of the top edge of the window (not the top of the message pane — the window).
2. Click-drag across that text.
3. Nothing highlights. The window may also start moving, since `startDragging()` fired.
4. Scroll the same message down the page and drag across it again — it selects normally.
**Expected behavior**
Text should be selectable anywhere it is visible. Only clicks actually over the title-bar chrome should start a window drag.
**Version and platform**
- Buzz version: 0.5.18 (`desktop-v0.5.18`)
- OS: macOS 26.5.1, Apple Silicon
**Suggested fix**
Deleting the geometric fallback outright is probably wrong — it appears to exist because `isTauriDragRegionEvent` only returns true when the click target *is* the element carrying `data-tauri-drag-region` (`attr === "" || attr === "true"` → `item === directTarget`), so unmarked descendants inside the chrome would otherwise miss.
Bounding the fallback to the chrome subtree fixes that without reaching into content:
```ts
if (event.clientY > WINDOW_DRAG_HANDLE_HEIGHT) return false;
const target = event.target;
if (!(target instanceof Element)) return false;
if (!target.closest('[data-testid="app-top-chrome"]')) return false; // ← new
return !target.closest(WINDOW_DRAG_INTERACTIVE_SELECTOR);
```
Or set `data-tauri-drag-region="deep"` on the chrome and drop the geometric branch. Either way `WINDOW_DRAG_HANDLE_HEIGHT` should be derived from `--buzz-top-chrome-height` rather than hardcoded 4 px larger. Same constants are duplicated in `shared/ui/StartupWindowDragRegion.tsx:6-7`.
**Additional context**
Reported from a screen recording: I-beam cursor over the text, click indicator firing, zero highlight, and a stale clipboard on paste. Confirmed by the reporter that scrolling the content down the window restores selection, and that selection then works normally again in both the main channel and the thread panel.
Not reproduced from a clean state by the person filing, so treat the exact pixel band as the suspected mechanism rather than a confirmed one — but the code path above is unambiguous on its own.
**Adjacent finding (happy to split into its own issue)**
`features/home/useResizableInboxListWidth.ts:60-84` sets `document.body.style.userSelect = "none"` on pointer-down and restores it only on `pointerup`. It registers no `pointercancel` handler, unlike the two sibling resizers — `shared/hooks/useThreadPanelWidth.ts:127` and `shared/ui/sidebar.tsx:480` both handle cancel. Cancel that gesture and `user-select: none` stays on `` for the session; it also leaks the `pointermove` listener, and the next drag captures the stuck `"none"` as its `previousUserSelect`, so a second resize cannot clear it either. That would produce a genuinely restart-only broken-selection state (with an arrow cursor, unlike the report above). Untouched since `de3f21ab` (2026-06-22).
Contributor guide
Assessment
This issue has not been assessed yet.