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
7 changes: 4 additions & 3 deletions webview-ui/src/components/common/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const TabHeader = ({ className, children, ...props }: TabProps) => (
</div>
)

export const TabContent = ({ className, children, ...props }: TabProps) => {
export const TabContent = forwardRef<HTMLDivElement, TabProps>(({ className, children, ...props }, ref) => {
const { renderContext } = useExtensionState()

const onWheel = useCallback(
Expand All @@ -40,11 +40,12 @@ export const TabContent = ({ className, children, ...props }: TabProps) => {
)

return (
<div className={cn("flex-1 overflow-auto p-5", className)} onWheel={onWheel} {...props}>
<div ref={ref} className={cn("flex-1 overflow-auto p-5", className)} onWheel={onWheel} {...props}>
{children}
</div>
)
}
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The forwardRef component should have a display name for better debugging and React DevTools support. Consider adding:

Suggested change
})
TabContent.displayName = 'TabContent'

right after the component definition.

TabContent.displayName = "TabContent"

export const TabList = forwardRef<
HTMLDivElement,
Expand Down
19 changes: 16 additions & 3 deletions webview-ui/src/components/settings/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
: "providers",
)

const scrollPositions = useRef<Record<SectionName, number>>(
Object.fromEntries(sectionNames.map((s) => [s, 0])) as Record<SectionName, number>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scroll positions are stored indefinitely in the ref. While not critical for a small number of tabs, consider if any cleanup is needed when the component unmounts to prevent potential memory leaks in long-running sessions.

)
const contentRef = useRef<HTMLDivElement | null>(null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a more descriptive variable name like tabContentScrollRef instead of contentRef to better indicate its specific purpose of tracking scroll positions.


const prevApiConfigName = useRef(currentApiConfigName)
const confirmDialogHandler = useRef<() => void>()

Expand Down Expand Up @@ -398,12 +403,20 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
// Handle tab changes with unsaved changes check
const handleTabChange = useCallback(
(newTab: SectionName) => {
// Directly switch tab without checking for unsaved changes
if (contentRef.current) {
scrollPositions.current[activeTab] = contentRef.current.scrollTop
}
setActiveTab(newTab)
},
[], // No dependency on isChangeDetected needed anymore
[activeTab],
)

useLayoutEffect(() => {
if (contentRef.current) {
contentRef.current.scrollTop = scrollPositions.current[activeTab] ?? 0
}
}, [activeTab])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding test coverage for the scroll position preservation functionality. The PR checklist indicates "Testing" is unchecked. Adding tests would help ensure this feature works correctly and prevent regressions. You could test:

  • Scroll position is saved when switching tabs
  • Scroll position is restored when returning to a tab
  • Initial scroll position is 0 for unvisited tabs


// Store direct DOM element refs for each tab
const tabRefs = useRef<Record<SectionName, HTMLButtonElement | null>>(
Object.fromEntries(sectionNames.map((name) => [name, null])) as Record<SectionName, HTMLButtonElement | null>,
Expand Down Expand Up @@ -579,7 +592,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
</TabList>

{/* Content area */}
<TabContent className="p-0 flex-1 overflow-auto">
<TabContent ref={contentRef} className="p-0 flex-1 overflow-auto">
{/* Providers Section */}
{activeTab === "providers" && (
<div>
Expand Down
Loading