Skip to content

Commit 69f9391

Browse files
committed
feat: tabbing to toggle auto-scrolls to it
1 parent 09f20ec commit 69f9391

File tree

1 file changed

+46
-0
lines changed

1 file changed

+46
-0
lines changed

npm-app/src/cli-handlers/chat.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ interface ChatState {
112112
currentStreamingMessageId?: string
113113
currentlyStreamingNodeId?: string
114114
inputBarFocused: boolean
115+
shouldScrollToFocusedToggle: boolean
115116
}
116117

117118
// State
@@ -128,6 +129,7 @@ let chatState: ChatState = {
128129
currentStreamingMessageId: undefined,
129130
currentlyStreamingNodeId: undefined,
130131
inputBarFocused: true, // Start with input bar focused
132+
shouldScrollToFocusedToggle: false,
131133
}
132134

133135
// Cached date formatter for performance
@@ -219,6 +221,43 @@ function scrollToBottom(): void {
219221
chatState.userHasScrolled = false
220222
}
221223

224+
function scrollToToggle(toggleNodeId: string, messageId: string): void {
225+
// We'll find the focused toggle after content is updated by looking for the highlighted toggle
226+
// Set a flag to scroll to the focused toggle after the next render
227+
chatState.shouldScrollToFocusedToggle = true
228+
}
229+
230+
function scrollToFocusedToggle(): void {
231+
if (!chatState.shouldScrollToFocusedToggle) return
232+
233+
const metrics = getTerminalMetrics()
234+
235+
// Find the line number where the highlighted toggle appears
236+
let toggleLineIndex = -1
237+
for (let i = 0; i < chatState.contentLines.length; i++) {
238+
const line = chatState.contentLines[i]
239+
// Look for the highlighted toggle pattern (\x1b[7m[+]\x1b[27m or \x1b[7m[-]\x1b[27m)
240+
if (line.includes('\x1b[7m[') && line.includes(']\x1b[27m')) {
241+
toggleLineIndex = i
242+
break
243+
}
244+
}
245+
246+
if (toggleLineIndex !== -1) {
247+
// Position the toggle near the top (about 3-4 lines down from the visible area)
248+
const targetTopOffset = 4
249+
const newScrollOffset = Math.max(0, toggleLineIndex - targetTopOffset)
250+
251+
// Clamp the scroll offset to valid bounds
252+
const maxScrollOffset = computeMaxScrollOffset(metrics)
253+
chatState.scrollOffset = Math.min(newScrollOffset, maxScrollOffset)
254+
chatState.userHasScrolled = true
255+
}
256+
257+
// Clear the flag
258+
chatState.shouldScrollToFocusedToggle = false
259+
}
260+
222261
function formatQueuePreview(
223262
message: string,
224263
queueCount: string,
@@ -248,6 +287,7 @@ function resetChatState(): void {
248287
currentStreamingMessageId: undefined,
249288
currentlyStreamingNodeId: undefined,
250289
inputBarFocused: true, // Start with input bar focused
290+
shouldScrollToFocusedToggle: false,
251291
}
252292
}
253293

@@ -631,6 +671,9 @@ function renderChat() {
631671
const maxContentLines = computeMaxContentLines(metrics)
632672
const maxScrollOffset = computeMaxScrollOffset(metrics)
633673

674+
// Handle scroll to focused toggle if requested
675+
scrollToFocusedToggle()
676+
634677
// Auto-scroll to bottom to show latest messages only if user hasn't manually scrolled
635678
if (!chatState.userHasScrolled) {
636679
chatState.scrollOffset = maxScrollOffset
@@ -1626,6 +1669,9 @@ function handleTabNavigation(key: any): boolean {
16261669
)
16271670
if (targetMessage && targetMessage.subagentUIState) {
16281671
targetMessage.subagentUIState.focusNodeId = targetNode.nodeId
1672+
1673+
// Auto-scroll to position the focused toggle near the top of the chat
1674+
scrollToToggle(targetNode.nodeId, targetMessage.id)
16291675
}
16301676
}
16311677

0 commit comments

Comments
 (0)