mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-13 19:46:16 +03:00
fix(desktop): disable animated wheel scrolling on linux
Chromium animates each mouse-wheel notch on Linux, unlike native Linux apps. Append disable-smooth-scrolling to the desktop app on Linux so wheel deltas apply immediately. Make ScrollView's viewport wheel listener and the session panel's scrollbar-reveal wheel listener passive, so the compositor no longer waits on the main thread before scrolling. Remove the home route's viewport wheel handler, which could never cancel because its target was always inside the viewport. Attach the desktop window wheel listener only while pinch zoom is enabled; main's zoom-changed handler covers Ctrl+wheel otherwise. ScrollView keyboard scrolling (PageUp/Down, Home/End, arrows, Space) now jumps instantly instead of animating.
This commit is contained in:
@@ -37,7 +37,6 @@ export function Home() {
|
||||
thumbHoverTarget={scroll.viewport.hoverTarget()}
|
||||
viewportRef={scroll.viewport.setViewport}
|
||||
onScroll={(event) => scroll.viewport.update(event.currentTarget.scrollTop)}
|
||||
onWheel={scroll.viewport.containOuterWheel}
|
||||
>
|
||||
<div
|
||||
class={`
|
||||
|
||||
@@ -143,12 +143,6 @@ export function createHomeScrollController(groups: Accessor<HomeSessionGroup[]>)
|
||||
},
|
||||
update,
|
||||
containWheel,
|
||||
containOuterWheel: (event: WheelEvent) => {
|
||||
if (!viewport) return
|
||||
|
||||
if (event.target instanceof Node && viewport.contains(event.target)) return
|
||||
containWheel(event)
|
||||
},
|
||||
},
|
||||
header: {
|
||||
setContent: (element: HTMLDivElement) => {
|
||||
|
||||
@@ -355,7 +355,7 @@ function SessionScreenContent(props: {
|
||||
data-scrollbar-hidden={store.timelineScrollbarHidden || store.sideWidthMotion}
|
||||
onPointerMove={revealTimelineScrollbar}
|
||||
onPointerDown={revealTimelineScrollbar}
|
||||
onWheel={revealTimelineScrollbar}
|
||||
on:wheel={{ passive: true, handleEvent: revealTimelineScrollbar }}
|
||||
onKeyDown={revealTimelineScrollbar}
|
||||
onTransitionRun={trackSideWidthMotion}
|
||||
onTransitionEnd={trackSideWidthMotion}
|
||||
|
||||
@@ -26,6 +26,9 @@ export function configureApplication() {
|
||||
const features = app.commandLine.getSwitchValue("enable-features")
|
||||
app.commandLine.appendSwitch("enable-features", features ? `${jsCallStackFeature},${features}` : jsCallStackFeature)
|
||||
|
||||
// Chromium animates each mouse-wheel notch on Linux; native Linux apps scroll by the full delta immediately.
|
||||
if (process.platform === "linux") app.commandLine.appendSwitch("disable-smooth-scrolling")
|
||||
|
||||
if (!app.isPackaged)
|
||||
app.commandLine.appendSwitch("remote-debugging-port", process.env.OPENCODE_DESKTOP_REMOTE_DEBUGGING_PORT ?? "9222")
|
||||
|
||||
|
||||
@@ -70,16 +70,19 @@ api.onZoomFactorChanged((factor) => {
|
||||
|
||||
void api.getPinchZoomEnabled().then((enabled) => {
|
||||
pinchZoomEnabled = enabled
|
||||
syncWheelPinchListener()
|
||||
})
|
||||
|
||||
api.onPinchZoomEnabledChanged((enabled) => {
|
||||
pinchZoomEnabled = enabled
|
||||
resetWheelPinch()
|
||||
syncWheelPinchListener()
|
||||
})
|
||||
|
||||
const setPinchZoomEnabled = (enabled: boolean) => {
|
||||
pinchZoomEnabled = enabled
|
||||
resetWheelPinch()
|
||||
syncWheelPinchListener()
|
||||
|
||||
return api.setPinchZoomEnabled(enabled)
|
||||
}
|
||||
@@ -129,18 +132,24 @@ const updateWheelPinch = (event: WheelEvent) => {
|
||||
applyZoom(clamp(wheelPinch.startZoom - (wheelPinch.totalDelta / WHEEL_PINCH_THRESHOLD) * WHEEL_PINCH_STEP))
|
||||
}
|
||||
|
||||
window.addEventListener(
|
||||
"wheel",
|
||||
(event) => {
|
||||
if (!pinchZoomEnabled) return
|
||||
const onWheelPinch = (event: WheelEvent) => {
|
||||
if (!event.ctrlKey) return
|
||||
|
||||
if (!event.ctrlKey) return
|
||||
event.preventDefault()
|
||||
updateWheelPinch(event)
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
updateWheelPinch(event)
|
||||
},
|
||||
{ passive: false },
|
||||
)
|
||||
// A cancelable window wheel listener makes every scroll gesture wait for the main thread, so it
|
||||
// is only attached while pinch zoom is on. Main's zoom-changed handler covers Ctrl+wheel otherwise.
|
||||
const syncWheelPinchListener = () => {
|
||||
if (pinchZoomEnabled) {
|
||||
window.addEventListener("wheel", onWheelPinch, { passive: false })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
window.removeEventListener("wheel", onWheelPinch)
|
||||
}
|
||||
|
||||
window.addEventListener("keydown", (event) => {
|
||||
if (!(OS_NAME === "macos" ? event.metaKey : event.ctrlKey)) return
|
||||
|
||||
@@ -397,8 +397,7 @@ export function ScrollView(props: ScrollViewProps) {
|
||||
|
||||
// Keybinds implementation
|
||||
// We ensure the viewport has a tabindex so it can receive focus
|
||||
// We can also explicitly catch PageUp/Down if we want smooth scroll or specific behavior,
|
||||
// but native usually handles this perfectly. Let's explicitly ensure it behaves well.
|
||||
// Keyboard scrolling jumps immediately, like the wheel, instead of animating.
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
// If user is focused on an input inside the scroll view, don't hijack keys
|
||||
if (document.activeElement && ["INPUT", "TEXTAREA", "SELECT"].includes(document.activeElement.tagName)) {
|
||||
@@ -434,27 +433,27 @@ export function ScrollView(props: ScrollViewProps) {
|
||||
switch (next) {
|
||||
case "page-down":
|
||||
e.preventDefault()
|
||||
viewportRef.scrollBy({ top: scrollAmount, behavior: "smooth" })
|
||||
viewportRef.scrollBy({ top: scrollAmount })
|
||||
break
|
||||
case "page-up":
|
||||
e.preventDefault()
|
||||
viewportRef.scrollBy({ top: -scrollAmount, behavior: "smooth" })
|
||||
viewportRef.scrollBy({ top: -scrollAmount })
|
||||
break
|
||||
case "home":
|
||||
e.preventDefault()
|
||||
viewportRef.scrollTo({ top: 0, behavior: "smooth" })
|
||||
viewportRef.scrollTo({ top: 0 })
|
||||
break
|
||||
case "end":
|
||||
e.preventDefault()
|
||||
viewportRef.scrollTo({ top: viewportRef.scrollHeight, behavior: "smooth" })
|
||||
viewportRef.scrollTo({ top: viewportRef.scrollHeight })
|
||||
break
|
||||
case "up":
|
||||
e.preventDefault()
|
||||
viewportRef.scrollBy({ top: -lineAmount, behavior: "smooth" })
|
||||
viewportRef.scrollBy({ top: -lineAmount })
|
||||
break
|
||||
case "down":
|
||||
e.preventDefault()
|
||||
viewportRef.scrollBy({ top: lineAmount, behavior: "smooth" })
|
||||
viewportRef.scrollBy({ top: lineAmount })
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -484,13 +483,17 @@ export function ScrollView(props: ScrollViewProps) {
|
||||
|
||||
if (typeof events.onScroll === "function") events.onScroll(e as any)
|
||||
}}
|
||||
onWheel={(e) => {
|
||||
markScrolling()
|
||||
const handler = events.onWheel
|
||||
// Passive so the compositor never waits on the main thread to scroll; onWheel cannot cancel.
|
||||
on:wheel={{
|
||||
passive: true,
|
||||
handleEvent: (e) => {
|
||||
markScrolling()
|
||||
const handler = events.onWheel
|
||||
|
||||
if (typeof handler === "function") handler(e as any)
|
||||
if (typeof handler === "function") handler(e as any)
|
||||
|
||||
if (Array.isArray(handler)) handler[0](handler[1], e as any)
|
||||
if (Array.isArray(handler)) handler[0](handler[1], e as any)
|
||||
},
|
||||
}}
|
||||
onTouchStart={events.onTouchStart as any}
|
||||
onTouchMove={events.onTouchMove as any}
|
||||
|
||||
Reference in New Issue
Block a user