Skip to content

Commit dc37f6a

Browse files
brunobergherroomote[bot]roomote
authored
Add custom Button component with variant system (#9150)
Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> Co-authored-by: Roo Code <[email protected]>
1 parent 2c91724 commit dc37f6a

24 files changed

+123
-128
lines changed

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

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { cn } from "@/lib/utils"
66
import { useExtensionState } from "@/context/ExtensionStateContext"
77
import { useAppTranslation } from "@/i18n/TranslationContext"
88
import { useRooPortal } from "@/components/ui/hooks/useRooPortal"
9-
import { Popover, PopoverContent, PopoverTrigger, StandardTooltip, ToggleSwitch } from "@/components/ui"
9+
import { Popover, PopoverContent, PopoverTrigger, StandardTooltip, ToggleSwitch, Button } from "@/components/ui"
1010
import { AutoApproveSetting, autoApproveSettingsConfig } from "../settings/AutoApproveToggle"
1111
import { useAutoApprovalToggles } from "@/hooks/useAutoApprovalToggles"
1212
import { useAutoApprovalState } from "@/hooks/useAutoApprovalState"
@@ -228,24 +228,21 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
228228
const isEnabled = toggles[key]
229229
return (
230230
<StandardTooltip key={key} content={t(descriptionKey)}>
231-
<button
231+
<Button
232+
variant={isEnabled ? "primary" : "secondary"}
232233
onClick={() => onAutoApproveToggle(key, !isEnabled)}
233234
className={cn(
234-
"flex items-center gap-2 px-2 py-2 rounded text-sm text-left",
235+
"flex items-center gap-2 px-2 py-2 text-sm text-left justify-start h-auto",
235236
"transition-all duration-150",
236-
"opacity-100 hover:opacity-70",
237-
"cursor-pointer",
238237
!effectiveAutoApprovalEnabled &&
239238
"opacity-50 cursor-not-allowed hover:opacity-50",
240-
isEnabled
241-
? "bg-vscode-button-background text-vscode-button-foreground"
242-
: "bg-vscode-button-background/15 text-vscode-foreground hover:bg-vscode-list-hoverBackground",
239+
!isEnabled && "bg-vscode-button-background/15",
243240
)}
244241
disabled={!effectiveAutoApprovalEnabled}
245242
data-testid={`auto-approve-${key}`}>
246243
<span className={`codicon codicon-${icon} text-sm flex-shrink-0`} />
247244
<span className="flex-1 truncate">{t(labelKey)}</span>
248-
</button>
245+
</Button>
249246
</StandardTooltip>
250247
)
251248
})}
@@ -254,44 +251,32 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
254251
{/* Bottom bar with Select All/None buttons */}
255252
<div className="flex flex-row items-center justify-between px-2 py-2 border-t border-vscode-dropdown-border">
256253
<div className="flex flex-row gap-1">
257-
<button
254+
<Button
255+
variant="ghost"
256+
size="sm"
258257
aria-label={t("chat:autoApprove.selectAll")}
259258
onClick={handleSelectAll}
260259
disabled={!effectiveAutoApprovalEnabled}
261260
className={cn(
262-
"relative inline-flex items-center justify-center gap-1",
263-
"bg-transparent border-none px-2 py-1",
264-
"rounded-md text-base font-bold",
265-
"text-vscode-foreground",
266-
"transition-all duration-150",
267-
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)]",
268-
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
269-
"active:bg-[rgba(255,255,255,0.1)]",
270-
"cursor-pointer",
261+
"gap-1 px-2 py-1 text-base font-bold h-auto",
271262
!effectiveAutoApprovalEnabled && "opacity-50 hover:opacity-50 cursor-not-allowed",
272263
)}>
273264
<ListChecks className="w-3.5 h-3.5" />
274265
<span>{t("chat:autoApprove.all")}</span>
275-
</button>
276-
<button
266+
</Button>
267+
<Button
268+
variant="ghost"
269+
size="sm"
277270
aria-label={t("chat:autoApprove.selectNone")}
278271
onClick={handleSelectNone}
279272
disabled={!effectiveAutoApprovalEnabled}
280273
className={cn(
281-
"relative inline-flex items-center justify-center gap-1",
282-
"bg-transparent border-none px-2 py-1",
283-
"rounded-md text-base font-bold",
284-
"text-vscode-foreground",
285-
"transition-all duration-150",
286-
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)]",
287-
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
288-
"active:bg-[rgba(255,255,255,0.1)]",
289-
"cursor-pointer",
274+
"gap-1 px-2 py-1 text-base font-bold h-auto",
290275
!effectiveAutoApprovalEnabled && "opacity-50 hover:opacity-50 cursor-not-allowed",
291276
)}>
292277
<LayoutList className="w-3.5 h-3.5" />
293278
<span>{t("chat:autoApprove.none")}</span>
294-
</button>
279+
</Button>
295280
</div>
296281

297282
<label

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React, { memo, useState } from "react"
2-
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
32
import { Trans } from "react-i18next"
43

54
import type { ClineMessage } from "@roo-code/types"
65

76
import { vscode } from "@src/utils/vscode"
7+
import { Button } from "@src/components/ui"
88

99
type AutoApprovedRequestLimitWarningProps = {
1010
message: ClineMessage
@@ -50,15 +50,15 @@ export const AutoApprovedRequestLimitWarning = memo(({ message }: AutoApprovedRe
5050
<div className="flex justify-between items-center">
5151
<Trans i18nKey={descriptionKey} ns="chat" values={{ count }} />
5252
</div>
53-
<VSCodeButton
53+
<Button
5454
style={{ width: "100%", padding: "6px", borderRadius: "4px" }}
5555
onClick={(e) => {
5656
e.preventDefault()
5757
setButtonClicked(true)
5858
vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" })
5959
}}>
6060
<Trans i18nKey={buttonKey} ns="chat" />
61-
</VSCodeButton>
61+
</Button>
6262
</div>
6363
</>
6464
)

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import React, { memo, useEffect, useMemo, useRef, useState } from "react"
22
import { useSize } from "react-use"
33
import deepEqual from "fast-deep-equal"
44
import { useTranslation } from "react-i18next"
5-
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
65

76
import type { ClineMessage } from "@roo-code/types"
87

98
import { BrowserAction, BrowserActionResult, ClineSayBrowserAction } from "@roo/ExtensionMessage"
109

1110
import { vscode } from "@src/utils/vscode"
1211
import { useExtensionState } from "@src/context/ExtensionStateContext"
12+
import { Button } from "@src/components/ui"
1313

1414
import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
1515
import { ChatRowContent } from "./ChatRow"
@@ -372,16 +372,16 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
372372
{t("chat:browser.navigation.step", { current: currentPageIndex + 1, total: pages.length })}
373373
</div>
374374
<div style={{ display: "flex", gap: "4px" }}>
375-
<VSCodeButton
375+
<Button
376376
disabled={currentPageIndex === 0 || isBrowsing}
377377
onClick={() => setCurrentPageIndex((i) => i - 1)}>
378378
{t("chat:browser.navigation.previous")}
379-
</VSCodeButton>
380-
<VSCodeButton
379+
</Button>
380+
<Button
381381
disabled={currentPageIndex === pages.length - 1 || isBrowsing}
382382
onClick={() => setCurrentPageIndex((i) => i + 1)}>
383383
{t("chat:browser.navigation.next")}
384-
</VSCodeButton>
384+
</Button>
385385
</div>
386386
</div>
387387
)}

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useDeepCompareEffect, useEvent, useMount } from "react-use"
33
import debounce from "debounce"
44
import { Virtuoso, type VirtuosoHandle } from "react-virtuoso"
55
import removeMd from "remove-markdown"
6-
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
6+
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
77
import useSound from "use-sound"
88
import { LRUCache } from "lru-cache"
99
import { Trans } from "react-i18next"
@@ -30,7 +30,7 @@ import { useExtensionState } from "@src/context/ExtensionStateContext"
3030
import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
3131
import RooHero from "@src/components/welcome/RooHero"
3232
import RooTips from "@src/components/welcome/RooTips"
33-
import { StandardTooltip } from "@src/components/ui"
33+
import { StandardTooltip, Button } from "@src/components/ui"
3434
import { CloudUpsellDialog } from "@src/components/cloud/CloudUpsellDialog"
3535

3636
import TelemetryBanner from "../common/TelemetryBanner"
@@ -1464,15 +1464,15 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
14641464
}`}>
14651465
{showScrollToBottom ? (
14661466
<StandardTooltip content={t("chat:scrollToBottom")}>
1467-
<VSCodeButton
1468-
appearance="secondary"
1467+
<Button
1468+
variant="secondary"
14691469
className="flex-[2]"
14701470
onClick={() => {
14711471
scrollToBottomSmooth()
14721472
disableAutoScrollRef.current = false
14731473
}}>
14741474
<span className="codicon codicon-chevron-down"></span>
1475-
</VSCodeButton>
1475+
</Button>
14761476
</StandardTooltip>
14771477
) : (
14781478
<>
@@ -1499,13 +1499,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
14991499
? t("chat:proceedWhileRunning.tooltip")
15001500
: undefined
15011501
}>
1502-
<VSCodeButton
1503-
appearance="primary"
1502+
<Button
1503+
variant="primary"
15041504
disabled={!enableButtons}
15051505
className={secondaryButtonText ? "flex-1 mr-[6px]" : "flex-[2] mr-0"}
15061506
onClick={() => handlePrimaryButtonClick(inputValue, selectedImages)}>
15071507
{primaryButtonText}
1508-
</VSCodeButton>
1508+
</Button>
15091509
</StandardTooltip>
15101510
)}
15111511
{(secondaryButtonText || isStreaming) && (
@@ -1521,13 +1521,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
15211521
? t("chat:terminate.tooltip")
15221522
: undefined
15231523
}>
1524-
<VSCodeButton
1525-
appearance="secondary"
1524+
<Button
1525+
variant="secondary"
15261526
disabled={!enableButtons && !(isStreaming && !didClickCancel)}
15271527
className={isStreaming ? "flex-[2] ml-0" : "flex-1 ml-[6px]"}
15281528
onClick={() => handleSecondaryButtonClick(inputValue, selectedImages)}>
15291529
{isStreaming ? t("chat:cancel.title") : secondaryButtonText}
1530-
</VSCodeButton>
1530+
</Button>
15311531
</StandardTooltip>
15321532
)}
15331533
</>

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
PopoverContent,
4242
Slider,
4343
StandardTooltip,
44+
Button,
4445
} from "@src/components/ui"
4546
import { useRooPortal } from "@src/components/ui/hooks/useRooPortal"
4647
import { useEscapeKey } from "@src/hooks/useEscapeKey"
@@ -1387,21 +1388,21 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
13871388
{currentSettings.codebaseIndexEnabled &&
13881389
(indexingStatus.systemStatus === "Error" ||
13891390
indexingStatus.systemStatus === "Standby") && (
1390-
<VSCodeButton
1391+
<Button
13911392
onClick={() => vscode.postMessage({ type: "startIndexing" })}
13921393
disabled={saveStatus === "saving" || hasUnsavedChanges}>
13931394
{t("settings:codeIndex.startIndexingButton")}
1394-
</VSCodeButton>
1395+
</Button>
13951396
)}
13961397

13971398
{currentSettings.codebaseIndexEnabled &&
13981399
(indexingStatus.systemStatus === "Indexed" ||
13991400
indexingStatus.systemStatus === "Error") && (
14001401
<AlertDialog>
14011402
<AlertDialogTrigger asChild>
1402-
<VSCodeButton appearance="secondary">
1403+
<Button variant="secondary">
14031404
{t("settings:codeIndex.clearIndexDataButton")}
1404-
</VSCodeButton>
1405+
</Button>
14051406
</AlertDialogTrigger>
14061407
<AlertDialogContent>
14071408
<AlertDialogHeader>
@@ -1426,13 +1427,13 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
14261427
)}
14271428
</div>
14281429

1429-
<VSCodeButton
1430+
<Button
14301431
onClick={handleSaveSettings}
14311432
disabled={!hasUnsavedChanges || saveStatus === "saving"}>
14321433
{saveStatus === "saving"
14331434
? t("settings:codeIndex.saving")
14341435
: t("settings:codeIndex.saveSettings")}
1435-
</VSCodeButton>
1436+
</Button>
14361437
</div>
14371438

14381439
{/* Save Status Messages */}

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,7 @@ export const FollowUpSuggest = ({
108108
const isFirstSuggestion = index === 0
109109

110110
return (
111-
<div
112-
key={`${suggestion.answer}-${ts}`}
113-
className="bg-vscode-editor-background rounded-sm w-full relative group">
111+
<div key={`${suggestion.answer}-${ts}`} className="w-full relative group">
114112
<Button
115113
variant="outline"
116114
className="text-left whitespace-normal break-words w-full h-auto px-3 py-2 justify-start pr-8"
@@ -133,7 +131,7 @@ export const FollowUpSuggest = ({
133131
)}
134132
<StandardTooltip content={t("chat:followUpSuggest.copyToInput")}>
135133
<div
136-
className="absolute cursor-pointer top-2 right-3 opacity-0 group-hover:opacity-100 transition-opacity"
134+
className="absolute cursor-pointer top-1.5 right-3 opacity-0 group-hover:opacity-100 transition-opacity"
137135
onClick={(e) => {
138136
e.stopPropagation()
139137
// Cancel the auto-approve timer when edit button is clicked

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export const CheckpointMenu = ({ ts, commitHash, checkpoint, onOpenChange }: Che
135135
) : (
136136
<>
137137
<Button
138-
variant="default"
138+
variant="primary"
139139
onClick={onRestore}
140140
className="grow"
141141
data-testid="confirm-restore-btn">

webview-ui/src/components/cloud/CloudView.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect, useRef, useState } from "react"
2-
import { VSCodeButton, VSCodeProgressRing, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
2+
import { VSCodeProgressRing, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
33

44
import { type CloudUserInfo, type CloudOrganizationMembership, TelemetryEventName } from "@roo-code/types"
55

@@ -261,18 +261,12 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone, orga
261261
</div>
262262

263263
<div className="flex flex-col gap-2 mt-4 pl-4">
264-
<VSCodeButton
265-
appearance="secondary"
266-
onClick={handleVisitCloudWebsite}
267-
className="w-full max-w-80">
264+
<Button variant="secondary" onClick={handleVisitCloudWebsite} className="w-full max-w-80">
268265
{t("cloud:visitCloudWebsite")}
269-
</VSCodeButton>
270-
<VSCodeButton
271-
appearance="secondary"
272-
onClick={handleLogoutClick}
273-
className="w-full max-w-80">
266+
</Button>
267+
<Button variant="secondary" onClick={handleLogoutClick} className="w-full max-w-80">
274268
{t("cloud:logOut")}
275-
</VSCodeButton>
269+
</Button>
276270
</div>
277271
</>
278272
) : (
@@ -281,9 +275,9 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone, orga
281275
<div className={cn(authInProgress && "opacity-50")}>{renderCloudBenefitsContent(t)}</div>
282276

283277
{!authInProgress && (
284-
<VSCodeButton appearance="primary" onClick={handleConnectClick} className="w-full">
278+
<Button variant="primary" onClick={handleConnectClick} className="w-full">
285279
{t("cloud:connect")}
286-
</VSCodeButton>
280+
</Button>
287281
)}
288282

289283
{/* Manual entry section */}
Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
import React from "react"
2-
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
2+
import { Button } from "@src/components/ui"
33

44
interface VSCodeButtonLinkProps {
55
href: string
66
children: React.ReactNode
7+
appearance?: "primary" | "secondary"
78
[key: string]: any
89
}
910

10-
export const VSCodeButtonLink = ({ href, children, ...props }: VSCodeButtonLinkProps) => (
11-
<a
12-
href={href}
13-
style={{
14-
textDecoration: "none",
15-
color: "inherit",
16-
}}>
17-
<VSCodeButton {...props}>{children}</VSCodeButton>
18-
</a>
19-
)
11+
export const VSCodeButtonLink = ({ href, children, appearance, ...props }: VSCodeButtonLinkProps) => {
12+
// Map appearance to variant for the new Button component
13+
const variant = appearance === "primary" ? "primary" : appearance === "secondary" ? "secondary" : undefined
14+
15+
return (
16+
<a
17+
href={href}
18+
style={{
19+
textDecoration: "none",
20+
color: "inherit",
21+
}}>
22+
<Button variant={variant} {...props}>
23+
{children}
24+
</Button>
25+
</a>
26+
)
27+
}

0 commit comments

Comments
 (0)