mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-21 16:18:55 +00:00
refactor(desktop): extract shared rafCoalesce helper for sash drags
This commit is contained in:
parent
1dffe0e670
commit
358e26a1c2
3 changed files with 46 additions and 48 deletions
|
|
@ -7,6 +7,7 @@ import { Tip } from '@/components/ui/tooltip'
|
|||
import { type Translations, useI18n } from '@/i18n'
|
||||
import { isDesktopFsRemoteMode } from '@/lib/desktop-fs'
|
||||
import { Bug } from '@/lib/icons'
|
||||
import { rafCoalesce } from '@/lib/raf-coalesce'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { notify, notifyError } from '@/store/notifications'
|
||||
import { $previewServerRestart, failPreviewServerRestart, type PreviewTarget } from '@/store/preview'
|
||||
|
|
@ -172,26 +173,16 @@ export function PreviewPane({
|
|||
document.body.style.cursor = 'row-resize'
|
||||
document.body.style.userSelect = 'none'
|
||||
|
||||
// Coalesce height writes to one per frame — pointermove outpaces 60fps and
|
||||
// each setHeight reflows the webview + console split.
|
||||
let raf: null | number = null
|
||||
let pendingHeight: null | number = null
|
||||
|
||||
const flushHeight = () => {
|
||||
raf = null
|
||||
|
||||
if (pendingHeight !== null) {
|
||||
consoleState.setHeight(pendingHeight)
|
||||
}
|
||||
}
|
||||
// pointermove outpaces 60fps and each setHeight reflows the webview +
|
||||
// console split, so coalesce to one apply per frame (commits on cleanup).
|
||||
const resize = rafCoalesce((height: number) => consoleState.setHeight(height))
|
||||
|
||||
const handleMove = (moveEvent: PointerEvent) => {
|
||||
if (!active) {
|
||||
return
|
||||
}
|
||||
|
||||
pendingHeight = clampConsoleHeight(startHeight + startY - moveEvent.clientY)
|
||||
raf ??= requestAnimationFrame(flushHeight)
|
||||
resize.push(clampConsoleHeight(startHeight + startY - moveEvent.clientY))
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
|
|
@ -200,16 +191,7 @@ export function PreviewPane({
|
|||
}
|
||||
|
||||
active = false
|
||||
|
||||
if (raf !== null) {
|
||||
cancelAnimationFrame(raf)
|
||||
raf = null
|
||||
}
|
||||
|
||||
if (pendingHeight !== null) {
|
||||
consoleState.setHeight(pendingHeight) // commit the final height
|
||||
}
|
||||
|
||||
resize.finish()
|
||||
document.body.style.cursor = previousCursor
|
||||
document.body.style.userSelect = previousUserSelect
|
||||
handle.releasePointerCapture?.(pointerId)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { useStore } from '@nanostores/react'
|
|||
import { type PointerEvent as ReactPointerEvent, useCallback, useMemo, useRef, useSyncExternalStore } from 'react'
|
||||
|
||||
import { useContributions } from '@/contrib/react/use-contributions'
|
||||
import { rafCoalesce } from '@/lib/raf-coalesce'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { $paneStates, type PaneStateSnapshot, setPaneHeightOverride, setPaneWidthOverride } from '@/store/panes'
|
||||
|
||||
|
|
@ -234,12 +235,8 @@ export function TreeSplit({ node, root, rootRow }: { node: SplitNode; root?: boo
|
|||
document.body.style.cursor = horizontal ? 'col-resize' : 'row-resize'
|
||||
document.body.style.userSelect = 'none'
|
||||
|
||||
// Coalesce store writes to one per frame: pointermove fires far faster than
|
||||
// 60fps and each write relayouts the whole pane tree. Stash the latest
|
||||
// clamped shift, apply it in a rAF (as drag-session / use-popout-drag do).
|
||||
let raf: null | number = null
|
||||
let pendingShift: null | number = null
|
||||
|
||||
// pointermove outpaces 60fps and each write relayouts the whole pane tree,
|
||||
// so coalesce to one apply per frame (rafCoalesce commits on cleanup).
|
||||
const applyShift = (shiftPx: number) => {
|
||||
if (a.fixed) {
|
||||
a.paneIds.forEach(id => setOverride(id, Math.round(a0px + shiftPx)))
|
||||
|
|
@ -258,29 +255,14 @@ export function TreeSplit({ node, root, rootRow }: { node: SplitNode; root?: boo
|
|||
}
|
||||
}
|
||||
|
||||
const flush = () => {
|
||||
raf = null
|
||||
|
||||
if (pendingShift !== null) {
|
||||
applyShift(pendingShift)
|
||||
}
|
||||
}
|
||||
const resize = rafCoalesce(applyShift)
|
||||
|
||||
const onMove = (ev: PointerEvent) => {
|
||||
pendingShift = Math.max(lo, Math.min(hi, (horizontal ? ev.clientX : ev.clientY) - start))
|
||||
raf ??= requestAnimationFrame(flush)
|
||||
resize.push(Math.max(lo, Math.min(hi, (horizontal ? ev.clientX : ev.clientY) - start)))
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
if (raf !== null) {
|
||||
cancelAnimationFrame(raf)
|
||||
raf = null
|
||||
}
|
||||
|
||||
if (pendingShift !== null) {
|
||||
applyShift(pendingShift) // commit the final position
|
||||
}
|
||||
|
||||
resize.finish()
|
||||
document.body.style.cursor = restoreCursor
|
||||
document.body.style.userSelect = restoreSelect
|
||||
|
||||
|
|
|
|||
34
apps/desktop/src/lib/raf-coalesce.ts
Normal file
34
apps/desktop/src/lib/raf-coalesce.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/** Coalesce a stream of values (pointermove positions, resize deltas) to one
|
||||
* `apply` per animation frame, so a drag can't drive several layouts per frame.
|
||||
* `push` records the latest value and schedules a frame; `finish` commits the
|
||||
* last value and cancels any pending frame (call it on pointerup/cancel).
|
||||
* `null` is the empty sentinel, so `T` must never legitimately be `null`. */
|
||||
export function rafCoalesce<T>(apply: (value: T) => void): { finish: () => void; push: (value: T) => void } {
|
||||
let frame: null | number = null
|
||||
let pending: null | T = null
|
||||
|
||||
const flush = () => {
|
||||
frame = null
|
||||
|
||||
if (pending !== null) {
|
||||
apply(pending)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
finish() {
|
||||
if (frame !== null) {
|
||||
cancelAnimationFrame(frame)
|
||||
frame = null
|
||||
}
|
||||
|
||||
if (pending !== null) {
|
||||
apply(pending)
|
||||
}
|
||||
},
|
||||
push(value) {
|
||||
pending = value
|
||||
frame ??= requestAnimationFrame(flush)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue