summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-03-09 10:43:56 -0500
committerAdam <[email protected]>2026-03-09 10:44:02 -0500
commitb749fa90f23d187d5428de1a2d321cc6497b6667 (patch)
tree46fbaed687ce73fc39f493d81235c89fe761aa7c /packages/ui/src/components
parent8a51cbd25381768be438696d8209eaaef6a6d41e (diff)
downloadopencode-b749fa90f23d187d5428de1a2d321cc6497b6667.tar.gz
opencode-b749fa90f23d187d5428de1a2d321cc6497b6667.zip
fix(app): scroll jitter/loop
Diffstat (limited to 'packages/ui/src/components')
-rw-r--r--packages/ui/src/components/scroll-view.test.ts19
-rw-r--r--packages/ui/src/components/scroll-view.tsx36
2 files changed, 48 insertions, 7 deletions
diff --git a/packages/ui/src/components/scroll-view.test.ts b/packages/ui/src/components/scroll-view.test.ts
new file mode 100644
index 000000000..d28b51fea
--- /dev/null
+++ b/packages/ui/src/components/scroll-view.test.ts
@@ -0,0 +1,19 @@
+import { describe, expect, test } from "bun:test"
+import { scrollKey } from "./scroll-view"
+
+describe("scrollKey", () => {
+ test("maps plain navigation keys", () => {
+ expect(scrollKey({ key: "PageDown", altKey: false, ctrlKey: false, metaKey: false, shiftKey: false })).toBe(
+ "page-down",
+ )
+ expect(scrollKey({ key: "ArrowUp", altKey: false, ctrlKey: false, metaKey: false, shiftKey: false })).toBe("up")
+ })
+
+ test("ignores modified keybinds", () => {
+ expect(
+ scrollKey({ key: "ArrowDown", altKey: false, ctrlKey: false, metaKey: true, shiftKey: false }),
+ ).toBeUndefined()
+ expect(scrollKey({ key: "PageUp", altKey: false, ctrlKey: true, metaKey: false, shiftKey: false })).toBeUndefined()
+ expect(scrollKey({ key: "End", altKey: false, ctrlKey: false, metaKey: false, shiftKey: true })).toBeUndefined()
+ })
+})
diff --git a/packages/ui/src/components/scroll-view.tsx b/packages/ui/src/components/scroll-view.tsx
index 52ed39a46..c3d878af6 100644
--- a/packages/ui/src/components/scroll-view.tsx
+++ b/packages/ui/src/components/scroll-view.tsx
@@ -6,6 +6,25 @@ export interface ScrollViewProps extends ComponentProps<"div"> {
orientation?: "vertical" | "horizontal" // currently only vertical is fully implemented for thumb
}
+export const scrollKey = (event: Pick<KeyboardEvent, "key" | "altKey" | "ctrlKey" | "metaKey" | "shiftKey">) => {
+ if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return
+
+ switch (event.key) {
+ case "PageDown":
+ return "page-down"
+ case "PageUp":
+ return "page-up"
+ case "Home":
+ return "home"
+ case "End":
+ return "end"
+ case "ArrowUp":
+ return "up"
+ case "ArrowDown":
+ return "down"
+ }
+}
+
export function ScrollView(props: ScrollViewProps) {
const i18n = useI18n()
const merged = mergeProps({ orientation: "vertical" }, props)
@@ -133,31 +152,34 @@ export function ScrollView(props: ScrollViewProps) {
return
}
+ const next = scrollKey(e)
+ if (!next) return
+
const scrollAmount = viewportRef.clientHeight * 0.8
const lineAmount = 40
- switch (e.key) {
- case "PageDown":
+ switch (next) {
+ case "page-down":
e.preventDefault()
viewportRef.scrollBy({ top: scrollAmount, behavior: "smooth" })
break
- case "PageUp":
+ case "page-up":
e.preventDefault()
viewportRef.scrollBy({ top: -scrollAmount, behavior: "smooth" })
break
- case "Home":
+ case "home":
e.preventDefault()
viewportRef.scrollTo({ top: 0, behavior: "smooth" })
break
- case "End":
+ case "end":
e.preventDefault()
viewportRef.scrollTo({ top: viewportRef.scrollHeight, behavior: "smooth" })
break
- case "ArrowUp":
+ case "up":
e.preventDefault()
viewportRef.scrollBy({ top: -lineAmount, behavior: "smooth" })
break
- case "ArrowDown":
+ case "down":
e.preventDefault()
viewportRef.scrollBy({ top: lineAmount, behavior: "smooth" })
break