Annie/UI fixes#11
Conversation
| [beginDrag], | ||
| ); | ||
|
|
||
| const handlePanelPointerDown = useCallback( |
There was a problem hiding this comment.
This starts a drag with surface: "panel", but the shared drag logic only updates iconPos. Since the panel itself has fixed top/height and only docks based on dockRight, dragging the panel/header can move the hidden launcher position and make the panel jump sides instead of actually dragging the panel.
| const dockRight = iconPos.x + ICON_W / 2 >= window.innerWidth / 2; | ||
|
|
||
| useEffect(() => { | ||
| const handler = () => setIsDismissed(false); |
There was a problem hiding this comment.
This only restores the floating launcher after dismissal, but it does not reopen the panel. The PR description says users can close the extension and reopen it through the toolbar, so I’d expect toolbar click to show the panel UI directly. Should this also call setIsOpen(true)?
| "absolute top-0 left-[3px] -translate-y-1/2", | ||
| "flex h-[18px] w-[18px] items-center justify-center", | ||
| "rounded-full bg-white shadow-[0_1px_4px_rgba(0,0,0,0.22)]", | ||
| "opacity-0 transition-opacity duration-150 group-hover:opacity-100", |
There was a problem hiding this comment.
The dismiss button is invisible until hover, but it remains keyboard-focusable. A keyboard user can tab to a button they cannot see. Can we add a focus-visible state like focus-visible:opacity-100, or otherwise avoid making the hidden control tabbable?
| onClick={() => setIsOpen(true)} | ||
| {/* ── Floating tab ───────────────────────────────────────────────────── */} | ||
| <div | ||
| role="button" |
There was a problem hiding this comment.
Since this is implementing button keyboard behavior manually, Space should call e.preventDefault() before opening the panel. Otherwise pressing Space while focused can also scroll the host page.
|
|
||
| const handleIconPointerDown = useCallback( | ||
| (e: React.PointerEvent) => { | ||
| if ((e.target as HTMLElement).closest("[data-dismiss-btn]")) return; |
There was a problem hiding this comment.
Can we avoid this cast by checking e.target instanceof Element first? That would be safer and match the helper functions above.
| }} | ||
| onPointerDown={(e) => e.stopPropagation()} | ||
| className={[ | ||
| "absolute top-0 left-[3px] -translate-y-1/2", |
There was a problem hiding this comment.
The icon is mirrored when docked left, but the dismiss button is always positioned with left-[3px]. Should this flip based on dockRight so the close button stays in the expected outside/top corner on both sides?
- Panel drag now uses panelDragLeft state so the panel visually follows the cursor; iconPos is only updated at snap time, eliminating the side-flicker described in review comment #1. - LOOP_SHOW_PANEL handler now calls setIsOpen(true) in addition to clearing isDismissed, so toolbar clicks re-open the panel directly. - Dismiss button gains focus-visible:opacity-100 so keyboard users can see the control they are focused on. - Space key on the launcher div now calls e.preventDefault() to prevent the host page from scrolling. - Replaced (e.target as HTMLElement) cast with an instanceof Element guard in handleIconPointerDown. - Dismiss button position flips based on dockRight so the X always appears at the outer/viewport-edge top corner on both sides. - Add grab/grabbing cursor affordance to the panel header drag zone via content.css. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
This PR implements some UI changes suggested by May:
Remaining TODOs:
Test Plan
Notes
Breaking Changes