Skip to content

Commit 02ee4b7

Browse files
committed
Added horizontal tabs to settings page with auto-scroll and mouse-wheel scroll
1 parent 9acdb36 commit 02ee4b7

File tree

5 files changed

+37
-203
lines changed

5 files changed

+37
-203
lines changed

src/activate/registerCommands.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
8888
// Post original action message
8989
visibleProvider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" })
9090
// Also explicitly post the visibility message to trigger scroll reliably
91-
console.log("[settingsButtonClicked] Posting 'settingsVisible' message.")
92-
visibleProvider.postMessageToWebview({ type: "settingsVisible" })
91+
visibleProvider.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
9392
},
9493
"roo-cline.historyButtonClicked": () => {
9594
const visibleProvider = getVisibleProviderOrLog(outputChannel)
@@ -170,10 +169,7 @@ export const openClineInNewTab = async ({ context, outputChannel }: Omit<Registe
170169
(e) => {
171170
const panel = e.webviewPanel
172171
if (panel.visible) {
173-
console.log("Roo Code tab panel became visible, posting message...")
174-
panel.webview.postMessage({ type: "settingsVisible" }) // Use the same message type as in SettingsView.tsx
175-
} else {
176-
console.log("Roo Code tab panel became hidden.")
172+
panel.webview.postMessage({ type: "action", action: "didBecomeVisible" }) // Use the same message type as in SettingsView.tsx
177173
}
178174
},
179175
null, // First null is for `thisArgs`

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 33 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import React, {
99
useRef,
1010
useState,
1111
WheelEvent,
12-
} from "react" // Add useLayoutEffect
12+
} from "react"
1313
import { useAppTranslation } from "@/i18n/TranslationContext"
1414
import {
1515
CheckCheck,
@@ -322,8 +322,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
322322
// Store direct DOM element refs for each tab
323323
const tabRefs = useRef<Record<SectionName, HTMLButtonElement | null>>({} as any)
324324

325-
// Removed useEffect for pre-populating refs
326-
327325
// Sections definition - no longer includes refs
328326
const sections: { id: SectionName; icon: LucideIcon }[] = useMemo(
329327
() => [
@@ -351,57 +349,35 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
351349
// Function to scroll the active tab into view
352350
const scrollToActiveTab = useCallback(
353351
(checkVisibility = false) => {
354-
console.log(`[scrollToActiveTab] Called. checkVisibility: ${checkVisibility}, activeTab: ${activeTab}`) // Log entry
355-
const activeTabElement = tabRefs.current[activeTab] // Remove ?.current
352+
const activeTabElement = tabRefs.current[activeTab]
356353
const containerElement = scrollContainerRef.current
357354

358-
if (!activeTabElement) {
359-
console.warn(`[scrollToActiveTab] activeTabElement for tab '${activeTab}' not found.`) // Log missing ref
360-
return
361-
}
362-
if (!containerElement) {
363-
console.warn(`[scrollToActiveTab] containerElement not found.`) // Log missing ref
355+
// Don't scroll if refs aren't ready
356+
if (!activeTabElement || !containerElement) {
364357
return
365358
}
366359

367-
// Use nodeName for simpler logging
368-
console.log(
369-
`[scrollToActiveTab] Refs found: activeTabElement=${activeTabElement.nodeName}, containerElement=${containerElement.nodeName}`,
370-
) // Log refs found
371360
let shouldScroll = true
372361
if (checkVisibility) {
373-
console.log(`[scrollToActiveTab] Checking visibility...`) // Log visibility check start
374-
// Calculate the visible range within the scroll container
362+
// Calculate visibility
375363
const visibleLeft = containerElement.scrollLeft
376364
const visibleRight = containerElement.scrollLeft + containerElement.clientWidth
377-
378-
// Calculate the tab's position within the scroll container
379365
const tabLeft = activeTabElement.offsetLeft
380366
const tabRight = activeTabElement.offsetLeft + activeTabElement.offsetWidth
381-
382-
// Check if the tab is fully within the visible range
383367
const isVisible = tabLeft >= visibleLeft && tabRight <= visibleRight
384-
console.log(
385-
`[scrollToActiveTab] Visibility check: tabLeft=${tabLeft}, tabRight=${tabRight}, visibleLeft=${visibleLeft}, visibleRight=${visibleRight}, isVisible=${isVisible}`,
386-
) // Log visibility details
387368
shouldScroll = !isVisible
388-
} else {
389-
console.log(`[scrollToActiveTab] Skipping visibility check (scrolling unconditionally).`) // Log unconditional scroll path
390369
}
370+
// If not checking visibility, shouldScroll remains true (unconditional scroll)
391371

392372
if (shouldScroll) {
393-
console.log(`[scrollToActiveTab] Scrolling tab '${activeTab}' into view.`) // Log scroll action
394373
activeTabElement.scrollIntoView({
395-
behavior: "auto", // Use instant scrolling
374+
behavior: "auto",
396375
block: "nearest",
397376
inline: "center",
398377
})
399-
} else {
400-
console.log(`[scrollToActiveTab] Scroll not needed (shouldScroll is false).`) // Log scroll skipped
401378
}
402-
// Removed redundant 'else' block for ref check, handled by early returns.
403379
},
404-
[activeTab], // Dependency on activeTab ensures the correct tab element is used
380+
[activeTab],
405381
)
406382

407383
// Effect to scroll when the active tab *changes* (e.g., user click)
@@ -415,11 +391,10 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
415391
// Use useLayoutEffect to ensure refs are available after DOM mutations.
416392
useLayoutEffect(() => {
417393
const handleMessage = (event: MessageEvent) => {
418-
const message = event.data // The object sent from postMessage
419-
if (message.type === "settingsVisible") {
420-
console.log("Received settingsVisible message from extension (LayoutEffect).")
421-
// No setTimeout needed, useLayoutEffect runs after DOM updates
422-
scrollToActiveTab(false) // Pass false to scroll unconditionally
394+
const message = event.data
395+
if (message.type === "action" && message.action === "didBecomeVisible") {
396+
// Scroll unconditionally when view becomes visible
397+
scrollToActiveTab(false)
423398
}
424399
}
425400

@@ -513,41 +488,32 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
513488
{/* Tab list with overflow dropdown */}
514489
<div className="flex items-center px-5">
515490
{" "}
516-
{/* Changed pr-5 to px-5 */}
517491
{/* Scrollable tab container */}
518-
<div
519-
ref={scrollContainerRef} // Assign ref
520-
className={cn(settingsTabsContainer, scrollbarHideClasses, "w-full")}
521-
// onWheel prop removed, listener added via useEffect
522-
>
492+
<div ref={scrollContainerRef} className={cn(settingsTabsContainer, scrollbarHideClasses, "w-full")}>
523493
<TabList
524494
value={activeTab}
525495
onValueChange={(value) => handleTabChange(value as SectionName)}
526496
className={cn(settingsTabList, "w-full min-w-max")}
527497
data-testid="settings-tab-list">
528-
{sections.map(
529-
(
530-
{ id, icon: Icon }, // Remove 'ref' from destructuring
531-
) => (
532-
<TabTrigger
533-
key={id}
534-
ref={(element) => (tabRefs.current[id] = element)} // Keep callback ref here
535-
value={id}
536-
className={cn(
537-
activeTab === id
538-
? `${settingsTabTrigger} ${settingsTabTriggerActive}`
539-
: settingsTabTrigger,
540-
"flex-shrink-0", // Prevent tabs from shrinking
541-
"focus:ring-0", // Remove the focus ring styling
542-
)}
543-
data-testid={`tab-${id}`}>
544-
<div className="flex items-center gap-2">
545-
<Icon className="w-4 h-4" />
546-
<span>{t(`settings:sections.${id}`)}</span>
547-
</div>
548-
</TabTrigger>
549-
),
550-
)}
498+
{sections.map(({ id, icon: Icon }) => (
499+
<TabTrigger
500+
key={id}
501+
ref={(element) => (tabRefs.current[id] = element)} // Keep callback ref here
502+
value={id}
503+
className={cn(
504+
activeTab === id
505+
? `${settingsTabTrigger} ${settingsTabTriggerActive}`
506+
: settingsTabTrigger,
507+
"flex-shrink-0", // Prevent tabs from shrinking
508+
"focus:ring-0", // Remove the focus ring styling
509+
)}
510+
data-testid={`tab-${id}`}>
511+
<div className="flex items-center gap-2">
512+
<Icon className="w-4 h-4" />
513+
<span>{t(`settings:sections.${id}`)}</span>
514+
</div>
515+
</TabTrigger>
516+
))}
551517
</TabList>
552518
</div>
553519
{/* "More" dropdown button - always show it */}
@@ -556,7 +522,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
556522
<Button
557523
variant="ghost"
558524
size="icon"
559-
className="ml-1 h-8 w-8 rounded-md flex-shrink-0"
525+
className="ml-1 rounded-md flex-shrink-0"
560526
aria-label={t("settings:common.more")}
561527
data-testid="more-tabs-button">
562528
<MoreHorizontal className="h-4 w-4" />

webview-ui/src/components/settings/styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export const StyledMarkdown = styled.div`
7878
`
7979

8080
// Settings tab styles as CSS class names for use with cn function
81-
export const settingsTabsContainer = "overflow-x-auto" // Changed from overflow-hidden to enable horizontal scrolling
81+
export const settingsTabsContainer = "overflow-x-auto"
8282

8383
export const settingsTabList = "flex flex-nowrap border-b border-vscode-sideBar-background"
8484

webview-ui/src/hooks/useTabOverflow.ts

Lines changed: 0 additions & 126 deletions
This file was deleted.

webview-ui/src/i18n/locales/en/settings.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,9 @@
2727
"notifications": "Notifications",
2828
"contextManagement": "Context Management",
2929
"terminal": "Terminal",
30-
"advanced": "Advanced",
3130
"experimental": "Experimental Features",
3231
"language": "Language",
33-
"about": "About Roo Code",
34-
"interface": "Interface"
32+
"about": "About Roo Code"
3533
},
3634
"autoApprove": {
3735
"description": "Allow Roo to automatically perform operations without requiring approval. Enable these settings only if you fully trust the AI and understand the associated security risks.",

0 commit comments

Comments
 (0)