mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
fix(desktop): measure dock proximity against the chat surface, not the window
The dock target is the docked composer at the bottom-center of its own surface. In a split, the viewport's bottom-center is somewhere else, so dragging onto the real dock never registered.
This commit is contained in:
parent
df25d77131
commit
88b6a7a49c
1 changed files with 30 additions and 11 deletions
|
|
@ -3,6 +3,7 @@ import { type PointerEvent as ReactPointerEvent, type RefObject, useCallback, us
|
|||
import {
|
||||
POPOUT_ESTIMATED_HEIGHT,
|
||||
POPOUT_WIDTH_REM,
|
||||
type PopoutBounds,
|
||||
type PopoutPosition,
|
||||
type PopoutSize,
|
||||
readPopoutBounds,
|
||||
|
|
@ -35,6 +36,8 @@ interface PressState {
|
|||
|
||||
interface ComposerPopoutGesturesOptions {
|
||||
composerRef: RefObject<HTMLFormElement | null>
|
||||
/** Layout zone this composer belongs to — the scope its float is stored under. */
|
||||
groupId: string
|
||||
onDock: () => void
|
||||
onPopOut: () => void
|
||||
poppedOut: boolean
|
||||
|
|
@ -67,10 +70,17 @@ function isFloatDragPlatform(target: EventTarget | null) {
|
|||
}
|
||||
|
||||
/** 0 (far) → 1 (inside the dock zone). Drives both the dock glow and the
|
||||
* release-to-dock test (which fires at proximity 1). */
|
||||
function dockProximityOf(rect: DOMRect) {
|
||||
const horizontalDist = Math.abs(rect.left + rect.width / 2 - window.innerWidth / 2)
|
||||
const verticalGap = window.innerHeight - DOCK_ZONE_BOTTOM_PX - rect.bottom
|
||||
* release-to-dock test (which fires at proximity 1).
|
||||
*
|
||||
* Measured against THIS surface's area, not the window: the dock target is the
|
||||
* docked composer, which sits at the bottom-center of its own chat surface. In
|
||||
* a split (or any layout where the chat isn't the full window) the viewport's
|
||||
* bottom-center is somewhere else entirely, so dragging onto the real dock
|
||||
* never registered. */
|
||||
function dockProximityOf(rect: DOMRect, area?: PopoutBounds) {
|
||||
const a = area ?? { bottom: window.innerHeight, left: 0, right: window.innerWidth, top: 0 }
|
||||
const horizontalDist = Math.abs(rect.left + rect.width / 2 - (a.left + a.right) / 2)
|
||||
const verticalGap = a.bottom - DOCK_ZONE_BOTTOM_PX - rect.bottom
|
||||
|
||||
const v = verticalGap <= 0 ? 1 : Math.max(0, 1 - verticalGap / DOCK_VERTICAL_FALLOFF_PX)
|
||||
|
||||
|
|
@ -109,6 +119,7 @@ function popoutPositionUnderPointer(
|
|||
*/
|
||||
export function useComposerPopoutGestures({
|
||||
composerRef,
|
||||
groupId,
|
||||
onDock,
|
||||
onPopOut,
|
||||
poppedOut,
|
||||
|
|
@ -142,7 +153,12 @@ export function useComposerPopoutGestures({
|
|||
const beginFloatDrag = useCallback(
|
||||
(state: PressState, clientX: number, clientY: number, next: PopoutPosition, size?: PopoutSize) => {
|
||||
clearTimer()
|
||||
const clamped = setComposerPopoutPosition(next, { area: readPopoutBounds(composerRef.current), size })
|
||||
|
||||
const clamped = setComposerPopoutPosition(groupId, next, {
|
||||
area: readPopoutBounds(composerRef.current),
|
||||
size
|
||||
})
|
||||
|
||||
liveRef.current = clamped
|
||||
|
||||
state.mode = 'float'
|
||||
|
|
@ -154,7 +170,7 @@ export function useComposerPopoutGestures({
|
|||
|
||||
setDragging(true)
|
||||
},
|
||||
[clearTimer, composerRef]
|
||||
[clearTimer, composerRef, groupId]
|
||||
)
|
||||
|
||||
const peelOffFromDock = useCallback(
|
||||
|
|
@ -255,17 +271,19 @@ export function useComposerPopoutGestures({
|
|||
|
||||
const composer = composerRef.current
|
||||
const size = composer ? { height: composer.offsetHeight, width: composer.offsetWidth } : undefined
|
||||
const area = readPopoutBounds(composer)
|
||||
|
||||
liveRef.current = setComposerPopoutPosition(
|
||||
groupId,
|
||||
{
|
||||
bottom: state.startBottom - (pending.y - state.startY),
|
||||
right: state.startRight - (pending.x - state.startX)
|
||||
},
|
||||
{ area: readPopoutBounds(composer), size }
|
||||
{ area, size }
|
||||
)
|
||||
|
||||
if (composer) {
|
||||
setDockProximity(dockProximityOf(composer.getBoundingClientRect()))
|
||||
setDockProximity(dockProximityOf(composer.getBoundingClientRect(), area))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -317,13 +335,14 @@ export function useComposerPopoutGestures({
|
|||
if (state.armed && state.mode === 'float') {
|
||||
const composer = composerRef.current
|
||||
const rect = composer?.getBoundingClientRect()
|
||||
const area = readPopoutBounds(composer)
|
||||
|
||||
if (rect && dockProximityOf(rect) >= 1) {
|
||||
if (rect && dockProximityOf(rect, area) >= 1) {
|
||||
onDock()
|
||||
} else {
|
||||
// Persist the resting position once, on release — never per move.
|
||||
const size = composer ? { height: composer.offsetHeight, width: composer.offsetWidth } : undefined
|
||||
setComposerPopoutPosition(liveRef.current, { area: readPopoutBounds(composer), persist: true, size })
|
||||
setComposerPopoutPosition(groupId, liveRef.current, { area, persist: true, size })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -340,7 +359,7 @@ export function useComposerPopoutGestures({
|
|||
window.removeEventListener('pointerup', handleUp)
|
||||
window.removeEventListener('pointercancel', handleUp)
|
||||
}
|
||||
}, [composerRef, onDock, peelOffFromDock, resetGesture])
|
||||
}, [composerRef, groupId, onDock, peelOffFromDock, resetGesture])
|
||||
|
||||
useEffect(() => clearTimer, [clearTimer])
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue