summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-03-07 06:47:11 -0600
committerAdam <[email protected]>2026-03-07 06:47:11 -0600
commit99d8aab0ac98c37a357908ef9c1a9fb8de17eddf (patch)
treedc4aeaeb64c6d6f932b9350d68c0b247f90b41a4
parent7dd63699524f2230f4aca663bd852ab44ce69d3f (diff)
downloadopencode-99d8aab0ac98c37a357908ef9c1a9fb8de17eddf.tar.gz
opencode-99d8aab0ac98c37a357908ef9c1a9fb8de17eddf.zip
fix(app): can't scroll files
-rw-r--r--packages/app/src/pages/session/file-tabs.tsx4
-rw-r--r--packages/app/src/pages/session/message-timeline.tsx1
-rw-r--r--packages/ui/src/components/scroll-view.css6
-rw-r--r--packages/ui/src/components/scroll-view.tsx24
4 files changed, 22 insertions, 13 deletions
diff --git a/packages/app/src/pages/session/file-tabs.tsx b/packages/app/src/pages/session/file-tabs.tsx
index 77643789d..07df4305f 100644
--- a/packages/app/src/pages/session/file-tabs.tsx
+++ b/packages/app/src/pages/session/file-tabs.tsx
@@ -446,9 +446,9 @@ export function FileTabContent(props: { tab: string }) {
)
return (
- <Tabs.Content value={props.tab} class="mt-3 relative h-full">
+ <Tabs.Content value={props.tab} class="mt-3 relative flex h-full min-h-0 flex-col overflow-hidden contain-strict">
<ScrollView
- class="h-full"
+ class="h-full min-h-0 flex-1"
viewportRef={(el: HTMLDivElement) => {
scroll = el
restoreScroll()
diff --git a/packages/app/src/pages/session/message-timeline.tsx b/packages/app/src/pages/session/message-timeline.tsx
index f0c7573f6..e93ca11a3 100644
--- a/packages/app/src/pages/session/message-timeline.tsx
+++ b/packages/app/src/pages/session/message-timeline.tsx
@@ -347,6 +347,7 @@ export function MessageTimeline(props: {
placeholderTitle={placeholderTitle}
/>
<ScrollView
+ reverse
viewportRef={props.setScrollRef}
onWheel={(e) => {
const root = e.currentTarget
diff --git a/packages/ui/src/components/scroll-view.css b/packages/ui/src/components/scroll-view.css
index a01298f77..a8574cc9f 100644
--- a/packages/ui/src/components/scroll-view.css
+++ b/packages/ui/src/components/scroll-view.css
@@ -9,9 +9,13 @@
overflow-y: auto;
scrollbar-width: none;
outline: none;
+ display: block;
+ overflow-anchor: none;
+}
+
+.scroll-view__viewport[data-reverse="true"] {
display: flex;
flex-direction: column-reverse;
- overflow-anchor: none;
}
.scroll-view__viewport::-webkit-scrollbar {
diff --git a/packages/ui/src/components/scroll-view.tsx b/packages/ui/src/components/scroll-view.tsx
index 16af3d933..a8d3cf0f8 100644
--- a/packages/ui/src/components/scroll-view.tsx
+++ b/packages/ui/src/components/scroll-view.tsx
@@ -5,13 +5,14 @@ import { FAST_SPRING } from "./motion"
export interface ScrollViewProps extends ComponentProps<"div"> {
viewportRef?: (el: HTMLDivElement) => void
+ reverse?: boolean
}
export function ScrollView(props: ScrollViewProps) {
const i18n = useI18n()
const [local, events, rest] = splitProps(
props,
- ["class", "children", "viewportRef", "style"],
+ ["class", "children", "viewportRef", "style", "reverse"],
[
"onScroll",
"onWheel",
@@ -36,6 +37,8 @@ export function ScrollView(props: ScrollViewProps) {
const [thumbTop, setThumbTop] = createSignal(0)
const [showThumb, setShowThumb] = createSignal(false)
+ const reverse = () => local.reverse === true
+
const updateThumb = () => {
if (!viewportRef) return
const { scrollTop, scrollHeight, clientHeight } = viewportRef
@@ -57,10 +60,11 @@ export function ScrollView(props: ScrollViewProps) {
const maxScrollTop = scrollHeight - clientHeight
const maxThumbTop = trackHeight - height
- // With column-reverse: scrollTop=0 is at bottom, negative = scrolled up
- // Normalize so 0 = at top, maxScrollTop = at bottom
- const normalizedScrollTop = maxScrollTop + scrollTop
- const top = maxScrollTop > 0 ? (normalizedScrollTop / maxScrollTop) * maxThumbTop : 0
+ const top = (() => {
+ if (maxScrollTop <= 0) return 0
+ if (!reverse()) return (scrollTop / maxScrollTop) * maxThumbTop
+ return ((maxScrollTop + scrollTop) / maxScrollTop) * maxThumbTop
+ })()
// Ensure thumb stays within bounds
const boundedTop = trackPadding + Math.max(0, Math.min(top, maxThumbTop))
@@ -135,7 +139,8 @@ export function ScrollView(props: ScrollViewProps) {
const limit = (top: number) => {
const max = viewportRef.scrollHeight - viewportRef.clientHeight
- return Math.max(-max, Math.min(0, top))
+ if (reverse()) return Math.max(-max, Math.min(0, top))
+ return Math.max(0, Math.min(max, top))
}
const glide = (top: number) => {
@@ -175,13 +180,11 @@ export function ScrollView(props: ScrollViewProps) {
break
case "Home":
e.preventDefault()
- // With column-reverse, top of content = -(scrollHeight - clientHeight)
- glide(-(viewportRef.scrollHeight - viewportRef.clientHeight))
+ glide(reverse() ? -(viewportRef.scrollHeight - viewportRef.clientHeight) : 0)
break
case "End":
e.preventDefault()
- // With column-reverse, bottom of content = 0
- glide(0)
+ glide(reverse() ? 0 : viewportRef.scrollHeight - viewportRef.clientHeight)
break
case "ArrowUp":
e.preventDefault()
@@ -206,6 +209,7 @@ export function ScrollView(props: ScrollViewProps) {
<div
ref={viewportRef}
class="scroll-view__viewport"
+ data-reverse={reverse() ? "true" : undefined}
onScroll={(e) => {
updateThumb()
if (typeof events.onScroll === "function") events.onScroll(e as any)