Skip to content

Commit 9f40c4d

Browse files
committed
App tab layout fixes
1 parent 85dd1a1 commit 9f40c4d

File tree

16 files changed

+198
-183
lines changed

16 files changed

+198
-183
lines changed

.env.sample

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
# PostHog API Keys for telemetry
2-
POSTHOG_API_KEY=key-goes-here
1+
POSTHOG_API_KEY=key-goes-here

webview-ui/src/App.tsx

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,34 @@ import { HumanRelayDialog } from "./components/human-relay/HumanRelayDialog"
1717

1818
type Tab = "settings" | "history" | "mcp" | "prompts" | "chat"
1919

20+
type HumanRelayDialogState = {
21+
isOpen: boolean
22+
requestId: string
23+
promptText: string
24+
}
25+
2026
const tabsByMessageAction: Partial<Record<NonNullable<ExtensionMessage["action"]>, Tab>> = {
2127
chatButtonClicked: "chat",
2228
settingsButtonClicked: "settings",
2329
promptsButtonClicked: "prompts",
2430
mcpButtonClicked: "mcp",
2531
historyButtonClicked: "history",
2632
}
33+
2734
const App = () => {
2835
const { didHydrateState, showWelcome, shouldShowAnnouncement, telemetrySetting, telemetryKey, machineId } =
2936
useExtensionState()
37+
3038
const [showAnnouncement, setShowAnnouncement] = useState(false)
3139
const [tab, setTab] = useState<Tab>("chat")
32-
const settingsRef = useRef<SettingsViewRef>(null)
33-
34-
// Human Relay Dialog Status
35-
const [humanRelayDialogState, setHumanRelayDialogState] = useState<{
36-
isOpen: boolean
37-
requestId: string
38-
promptText: string
39-
}>({
40+
const [humanRelayDialogState, setHumanRelayDialogState] = useState<HumanRelayDialogState>({
4041
isOpen: false,
4142
requestId: "",
4243
promptText: "",
4344
})
4445

46+
const settingsRef = useRef<SettingsViewRef>(null)
47+
4548
const switchTab = useCallback((newTab: Tab) => {
4649
if (settingsRef.current?.checkUnsaveChanges) {
4750
settingsRef.current.checkUnsaveChanges(() => setTab(newTab))
@@ -74,23 +77,6 @@ const App = () => {
7477
[switchTab],
7578
)
7679

77-
// Processing Human Relay Dialog Submission
78-
const handleHumanRelaySubmit = (requestId: string, text: string) => {
79-
vscode.postMessage({
80-
type: "humanRelayResponse",
81-
requestId,
82-
text,
83-
})
84-
}
85-
86-
// Handle Human Relay dialog box cancel
87-
const handleHumanRelayCancel = (requestId: string) => {
88-
vscode.postMessage({
89-
type: "humanRelayCancel",
90-
requestId,
91-
})
92-
}
93-
9480
useEvent("message", onMessage)
9581

9682
useEffect(() => {
@@ -106,7 +92,7 @@ const App = () => {
10692
}
10793
}, [telemetrySetting, telemetryKey, machineId, didHydrateState])
10894

109-
// Tell Extension that we are ready to receive messages
95+
// Tell the extension that we are ready to receive messages.
11096
useEffect(() => {
11197
vscode.postMessage({ type: "webviewDidLaunch" })
11298
}, [])
@@ -121,24 +107,23 @@ const App = () => {
121107
<WelcomeView />
122108
) : (
123109
<>
124-
{tab === "settings" && <SettingsView ref={settingsRef} onDone={() => setTab("chat")} />}
125-
{tab === "history" && <HistoryView onDone={() => switchTab("chat")} />}
126-
{tab === "mcp" && <McpView onDone={() => switchTab("chat")} />}
127110
{tab === "prompts" && <PromptsView onDone={() => switchTab("chat")} />}
111+
{tab === "mcp" && <McpView onDone={() => switchTab("chat")} />}
112+
{tab === "history" && <HistoryView onDone={() => switchTab("chat")} />}
113+
{tab === "settings" && <SettingsView ref={settingsRef} onDone={() => setTab("chat")} />}
128114
<ChatView
129115
isHidden={tab !== "chat"}
130116
showAnnouncement={showAnnouncement}
131117
hideAnnouncement={() => setShowAnnouncement(false)}
132118
showHistoryView={() => switchTab("history")}
133119
/>
134-
{/* Human Relay Dialog */}
135120
<HumanRelayDialog
136121
isOpen={humanRelayDialogState.isOpen}
137122
requestId={humanRelayDialogState.requestId}
138123
promptText={humanRelayDialogState.promptText}
139124
onClose={() => setHumanRelayDialogState((prev) => ({ ...prev, isOpen: false }))}
140-
onSubmit={handleHumanRelaySubmit}
141-
onCancel={handleHumanRelayCancel}
125+
onSubmit={(requestId, text) => vscode.postMessage({ type: "humanRelayResponse", requestId, text })}
126+
onCancel={(requestId) => vscode.postMessage({ type: "humanRelayCancel", requestId })}
142127
/>
143128
</>
144129
)
@@ -147,6 +132,7 @@ const App = () => {
147132
const AppWithProviders = () => (
148133
<ExtensionStateContextProvider>
149134
<App />
135+
<div id="roo-portal" />
150136
</ExtensionStateContextProvider>
151137
)
152138

webview-ui/src/components/chat/Announcement.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
3333
</p>
3434

3535
<h3 style={{ margin: "12px 0 8px" }}>What's New</h3>
36-
<p style={{ margin: "5px 0px" }}>
36+
<div style={{ margin: "5px 0px" }}>
3737
<ul style={{ margin: "4px 0 6px 20px", padding: 0 }}>
3838
<li>• Faster asynchronous checkpoints</li>
3939
<li>• Support for .rooignore files</li>
@@ -44,7 +44,7 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
4444
<li>• Updated DeepSeek provider</li>
4545
<li>• New "Human Relay" provider</li>
4646
</ul>
47-
</p>
47+
</div>
4848

4949
<p style={{ margin: "10px 0px 0px" }}>
5050
Get more details and discuss in{" "}

webview-ui/src/components/chat/checkpoints/CheckpointMenu.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect, useCallback } from "react"
1+
import { useState, useCallback } from "react"
22
import { CheckIcon, Cross2Icon } from "@radix-ui/react-icons"
33

44
import { Button, Popover, PopoverContent, PopoverTrigger } from "@/components/ui"
@@ -14,7 +14,6 @@ type CheckpointMenuProps = {
1414
}
1515

1616
export const CheckpointMenu = ({ ts, commitHash, currentHash, checkpoint }: CheckpointMenuProps) => {
17-
const [portalContainer, setPortalContainer] = useState<HTMLElement>()
1817
const [isOpen, setIsOpen] = useState(false)
1918
const [isConfirming, setIsConfirming] = useState(false)
2019

@@ -42,15 +41,6 @@ export const CheckpointMenu = ({ ts, commitHash, currentHash, checkpoint }: Chec
4241
setIsOpen(false)
4342
}, [ts, commitHash])
4443

45-
useEffect(() => {
46-
// The dropdown menu uses a portal from @shadcn/ui which by default renders
47-
// at the document root. This causes the menu to remain visible even when
48-
// the parent ChatView component is hidden (during settings/history view).
49-
// By moving the portal inside ChatView, the menu will properly hide when
50-
// its parent is hidden.
51-
setPortalContainer(document.getElementById("chat-view-portal") || undefined)
52-
}, [])
53-
5444
return (
5545
<div className="flex flex-row gap-1">
5646
{isDiffAvailable && (
@@ -70,7 +60,7 @@ export const CheckpointMenu = ({ ts, commitHash, currentHash, checkpoint }: Chec
7060
<span className="codicon codicon-history" />
7161
</Button>
7262
</PopoverTrigger>
73-
<PopoverContent align="end" container={portalContainer}>
63+
<PopoverContent align="end">
7464
<div className="flex flex-col gap-2">
7565
{!isCurrent && (
7666
<div className="flex flex-col gap-1 group hover:text-foreground">
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { cn } from "@/lib/utils"
2+
import { HTMLAttributes } from "react"
3+
4+
type AlertProps = HTMLAttributes<HTMLDivElement>
5+
6+
export const Alert = ({ className, children, ...props }: AlertProps) => (
7+
<div
8+
className={cn(
9+
"text-vscode-inputValidation-infoForeground bg-vscode-inputValidation-infoBackground border border-vscode-inputValidation-infoBorder rounded-xs p-2",
10+
className,
11+
)}
12+
{...props}>
13+
{children}
14+
</div>
15+
)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { HTMLAttributes } from "react"
2+
3+
import { cn } from "@/lib/utils"
4+
5+
type TabProps = HTMLAttributes<HTMLDivElement>
6+
7+
export const Tab = ({ className, children, ...props }: TabProps) => (
8+
<div className={cn("fixed inset-0 flex flex-col overflow-hidden", className)} {...props}>
9+
{children}
10+
</div>
11+
)
12+
13+
export const TabHeader = ({ className, children, ...props }: TabProps) => (
14+
<div className={cn("px-5 py-2.5 border-b border-vscode-panel-border", className)} {...props}>
15+
{children}
16+
</div>
17+
)
18+
19+
export const TabContent = ({ className, children, ...props }: TabProps) => (
20+
<div className={cn("flex-1 overflow-auto p-5", className)} {...props}>
21+
{children}
22+
</div>
23+
)

webview-ui/src/components/history/HistoryView.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { formatLargeNumber, formatDate } from "@/utils/format"
99
import { cn } from "@/lib/utils"
1010
import { Button } from "@/components/ui"
1111

12+
import { Tab, TabContent, TabHeader } from "../common/Tab"
1213
import { useTaskSearch } from "./useTaskSearch"
1314
import { ExportButton } from "./ExportButton"
1415
import { CopyButton } from "./CopyButton"
@@ -25,8 +26,8 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
2526
const [deleteTaskId, setDeleteTaskId] = useState<string | null>(null)
2627

2728
return (
28-
<div className="fixed inset-0 flex flex-col">
29-
<div className="flex flex-col gap-2 px-5 py-2.5 border-b border-vscode-panel-border">
29+
<Tab>
30+
<TabHeader className="flex flex-col gap-2">
3031
<div className="flex justify-between items-center">
3132
<h3 className="text-vscode-foreground m-0">History</h3>
3233
<VSCodeButton onClick={onDone}>Done</VSCodeButton>
@@ -81,8 +82,9 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
8182
</VSCodeRadio>
8283
</VSCodeRadioGroup>
8384
</div>
84-
</div>
85-
<div style={{ flexGrow: 1, overflowY: "auto", margin: 0 }}>
85+
</TabHeader>
86+
87+
<TabContent className="p-0">
8688
<Virtuoso
8789
style={{
8890
flexGrow: 1,
@@ -312,11 +314,12 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
312314
</div>
313315
)}
314316
/>
315-
</div>
317+
</TabContent>
318+
316319
{deleteTaskId && (
317320
<DeleteTaskDialog taskId={deleteTaskId} onOpenChange={(open) => !open && setDeleteTaskId(null)} open />
318321
)}
319-
</div>
322+
</Tab>
320323
)
321324
}
322325

webview-ui/src/components/mcp/McpView.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useState } from "react"
12
import {
23
VSCodeButton,
34
VSCodeCheckbox,
@@ -6,14 +7,17 @@ import {
67
VSCodePanelTab,
78
VSCodePanelView,
89
} from "@vscode/webview-ui-toolkit/react"
9-
import { useState } from "react"
10-
import { vscode } from "../../utils/vscode"
11-
import { useExtensionState } from "../../context/ExtensionStateContext"
10+
1211
import { McpServer } from "../../../../src/shared/mcp"
12+
13+
import { vscode } from "@/utils/vscode"
14+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui"
15+
16+
import { useExtensionState } from "../../context/ExtensionStateContext"
17+
import { Tab, TabContent, TabHeader } from "../common/Tab"
1318
import McpToolRow from "./McpToolRow"
1419
import McpResourceRow from "./McpResourceRow"
1520
import McpEnabledToggle from "./McpEnabledToggle"
16-
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "../ui/dialog"
1721

1822
type McpViewProps = {
1923
onDone: () => void
@@ -29,12 +33,13 @@ const McpView = ({ onDone }: McpViewProps) => {
2933
} = useExtensionState()
3034

3135
return (
32-
<div className="fixed inset-0 flex flex-col">
33-
<div className="flex justify-between items-center px-5 py-2.5 border-b border-vscode-panel-border">
36+
<Tab>
37+
<TabHeader className="flex justify-between items-center">
3438
<h3 className="text-vscode-foreground m-0">MCP Servers</h3>
3539
<VSCodeButton onClick={onDone}>Done</VSCodeButton>
36-
</div>
37-
<div className="flex-1 overflow-auto p-5">
40+
</TabHeader>
41+
42+
<TabContent>
3843
<div
3944
style={{
4045
color: "var(--vscode-foreground)",
@@ -103,12 +108,11 @@ const McpView = ({ onDone }: McpViewProps) => {
103108
</div>
104109
</>
105110
)}
106-
</div>
107-
</div>
111+
</TabContent>
112+
</Tab>
108113
)
109114
}
110115

111-
// Server Row Component
112116
const ServerRow = ({ server, alwaysAllowMcp }: { server: McpServer; alwaysAllowMcp?: boolean }) => {
113117
const [isExpanded, setIsExpanded] = useState(false)
114118
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)

webview-ui/src/components/prompts/PromptsView.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242

4343
import { TOOL_GROUPS, GROUP_DISPLAY_NAMES, ToolGroup } from "../../../../src/shared/tool-groups"
4444
import { vscode } from "../../utils/vscode"
45+
import { Tab, TabContent, TabHeader } from "../common/Tab"
4546

4647
// Get all available groups that should show in prompts view
4748
const availableGroups = (Object.keys(TOOL_GROUPS) as ToolGroup[]).filter((group) => !TOOL_GROUPS[group].alwaysAvailable)
@@ -406,12 +407,13 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
406407
}
407408

408409
return (
409-
<div className="fixed inset-0 flex flex-col">
410-
<div className="flex justify-between items-center px-5 py-2.5 border-b border-vscode-panel-border">
410+
<Tab>
411+
<TabHeader className="flex justify-between items-center">
411412
<h3 className="text-vscode-foreground m-0">Prompts</h3>
412413
<VSCodeButton onClick={onDone}>Done</VSCodeButton>
413-
</div>
414-
<div className="flex-1 overflow-auto p-5">
414+
</TabHeader>
415+
416+
<TabContent>
415417
<div className="pb-5 border-b border-vscode-input-border">
416418
<div className="mb-5">
417419
<div className="font-bold mb-1">Preferred Language</div>
@@ -934,6 +936,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
934936
</div>
935937
</div>
936938
</div>
939+
937940
<div
938941
style={{
939942
paddingBottom: "40px",
@@ -1172,7 +1175,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
11721175
)}
11731176
</div>
11741177
</div>
1175-
</div>
1178+
</TabContent>
1179+
11761180
{isCreateModeDialogOpen && (
11771181
<div
11781182
style={{
@@ -1396,6 +1400,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
13961400
</div>
13971401
</div>
13981402
)}
1403+
13991404
{isDialogOpen && (
14001405
<div
14011406
style={{
@@ -1463,6 +1468,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
14631468
</div>
14641469
</div>
14651470
)}
1471+
14661472
{isCustomLanguage && (
14671473
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
14681474
<div className="bg-[var(--vscode-editor-background)] p-6 rounded-lg w-96">
@@ -1497,7 +1503,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
14971503
</div>
14981504
</div>
14991505
)}
1500-
</div>
1506+
</Tab>
15011507
)
15021508
}
15031509

0 commit comments

Comments
 (0)