Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cli/src/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ export const Chat = ({
return isUserCollapsingRef.current
}, [])

const { scrollToLatest, scrollboxProps, isAtBottom } = useChatScrollbox(
const { scrollToLatest, scrollUp, scrollDown, scrollboxProps, isAtBottom } = useChatScrollbox(
scrollRef,
messages,
isUserCollapsing,
Expand Down Expand Up @@ -1275,6 +1275,8 @@ export const Chat = ({
}
})
},
onScrollUp: scrollUp,
onScrollDown: scrollDown,
onOpenBuyCredits: () => {
// If credits have been restored, just return to default mode
if (areCreditsRestored()) {
Expand Down Expand Up @@ -1314,6 +1316,8 @@ export const Chat = ({
inputRef,
handleCtrlC,
clearQueue,
scrollUp,
scrollDown,
],
)

Expand Down
10 changes: 10 additions & 0 deletions cli/src/hooks/use-chat-keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ export type ChatKeyboardHandlers = {
onPasteImagePath: (imagePath: string) => void
onPasteText: (text: string) => void

// Scroll handlers
onScrollUp: () => void
onScrollDown: () => void

// Out of credits handler
onOpenBuyCredits: () => void
}
Expand Down Expand Up @@ -229,6 +233,12 @@ function dispatchAction(
}
return true
}
case 'scroll-up':
handlers.onScrollUp()
return true
case 'scroll-down':
handlers.onScrollDown()
return true
case 'open-buy-credits':
handlers.onOpenBuyCredits()
return true
Expand Down
29 changes: 29 additions & 0 deletions cli/src/hooks/use-scroll-management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const SCROLL_NEAR_BOTTOM_THRESHOLD = 1
const ANIMATION_FRAME_INTERVAL_MS = 16 // ~60fps
const DEFAULT_SCROLL_ANIMATION_DURATION_MS = 200

// Page scroll amount (fraction of viewport height)
const PAGE_SCROLL_FRACTION = 0.8

// Delay before auto-scrolling after content changes
const AUTO_SCROLL_DELAY_MS = 50

Expand Down Expand Up @@ -86,6 +89,30 @@ export const useChatScrollbox = (
animateScrollTo(maxScroll)
}, [scrollRef, animateScrollTo])

const scrollUp = useCallback((): void => {
const scrollbox = scrollRef.current
if (!scrollbox) return

const viewportHeight = scrollbox.viewport.height
const scrollAmount = Math.floor(viewportHeight * PAGE_SCROLL_FRACTION)
const targetScroll = Math.max(0, scrollbox.scrollTop - scrollAmount)
animateScrollTo(targetScroll)
}, [scrollRef, animateScrollTo])

const scrollDown = useCallback((): void => {
const scrollbox = scrollRef.current
if (!scrollbox) return

const viewportHeight = scrollbox.viewport.height
const maxScroll = Math.max(
0,
scrollbox.scrollHeight - viewportHeight,
)
const scrollAmount = Math.floor(viewportHeight * PAGE_SCROLL_FRACTION)
const targetScroll = Math.min(maxScroll, scrollbox.scrollTop + scrollAmount)
animateScrollTo(targetScroll)
}, [scrollRef, animateScrollTo])

useEffect(() => {
const scrollbox = scrollRef.current
if (!scrollbox) return
Expand Down Expand Up @@ -149,6 +176,8 @@ export const useChatScrollbox = (

return {
scrollToLatest,
scrollUp,
scrollDown,
scrollboxProps: {},
isAtBottom,
}
Expand Down
18 changes: 16 additions & 2 deletions cli/src/utils/keyboard-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ export type ChatKeyboardAction =
| { type: 'bash-history-up' }
| { type: 'bash-history-down' }

// Scroll actions
| { type: 'scroll-up' }
| { type: 'scroll-down' }

// Paste action (dispatcher checks clipboard content to route to image or text handler)
| { type: 'paste' }

Expand Down Expand Up @@ -126,6 +130,8 @@ export function resolveChatKeyboardAction(
(key.name === 'return' || key.name === 'enter') &&
!key.shift &&
!hasModifier(key)
const isPageUp = key.name === 'pageup' && !hasModifier(key)
const isPageDown = key.name === 'pagedown' && !hasModifier(key)

// Priority 0: Out of credits mode - Enter opens buy credits page
if (state.inputMode === 'outOfCredits') {
Expand Down Expand Up @@ -312,12 +318,20 @@ export function resolveChatKeyboardAction(
return { type: 'unfocus-agent' }
}

// Priority 13: Paste (ctrl-v)
// Priority 13: Scroll with PageUp/PageDown
if (isPageUp) {
return { type: 'scroll-up' }
}
if (isPageDown) {
return { type: 'scroll-down' }
}

// Priority 14: Paste (ctrl-v)
if (isCtrlV) {
return { type: 'paste' }
}

// Priority 14: Exit app (ctrl-c double-tap)
// Priority 15: Exit app (ctrl-c double-tap)
if (isCtrlC) {
if (state.nextCtrlCWillExit) {
return { type: 'exit-app' }
Expand Down
Loading