mirror of
https://github.com/coder/coder.git
synced 2026-09-22 13:10:21 +08:00
refactor: update app buttons to use the new button component (#17684)
Related to https://github.com/coder/coder/issues/17311 - Replaces the MUI Buttons by the new shadcn/ui buttons. This change allows the reuse of app links, and terminal buttons using the `asChild` capability from the Radix components - Uses the new [proposed design](https://www.figma.com/design/OR75XeUI0Z3ksqt1mHsNQw/Workspace-views?node-id=1014-8242&t=wtUXJRN1SfyZiFKn-0) - Updates the button styles to support image tags as icons - Uses the new Tooltip component for the app buttons **Before:** <img width="1243" alt="Screenshot 2025-05-05 at 17 55 49" src="https://github.com/user-attachments/assets/e689e9dc-d8e1-4c9d-ba09-ef1479a501f1" /> **After:** <img width="1264" alt="Screenshot 2025-05-05 at 18 05 38" src="https://github.com/user-attachments/assets/8fafbe20-f063-46ab-86cf-2e0381bba889" />
This commit is contained in:
+3
-1
@@ -1042,7 +1042,9 @@ export async function openTerminalWindow(
|
||||
): Promise<Page> {
|
||||
// Wait for the web terminal to open in a new tab
|
||||
const pagePromise = context.waitForEvent("page");
|
||||
await page.getByTestId("terminal").click({ timeout: 60_000 });
|
||||
await page
|
||||
.getByRole("link", { name: /terminal/i })
|
||||
.click({ timeout: 60_000 });
|
||||
const terminal = await pagePromise;
|
||||
await terminal.waitForLoadState("domcontentloaded");
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ const buttonVariants = cva(
|
||||
text-sm font-semibold font-medium cursor-pointer no-underline
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-content-link
|
||||
disabled:pointer-events-none disabled:text-content-disabled
|
||||
[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:p-0.5`,
|
||||
[&:is(a):not([href])]:pointer-events-none [&:is(a):not([href])]:text-content-disabled
|
||||
[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:p-0.5
|
||||
[&>img]:pointer-events-none [&>img]:shrink-0 [&>img]:p-0.5`,
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -28,11 +30,11 @@ const buttonVariants = cva(
|
||||
},
|
||||
|
||||
size: {
|
||||
lg: "min-w-20 h-10 px-3 py-2 [&_svg]:size-icon-lg",
|
||||
sm: "min-w-20 h-8 px-2 py-1.5 text-xs [&_svg]:size-icon-sm",
|
||||
lg: "min-w-20 h-10 px-3 py-2 [&_svg]:size-icon-lg [&>img]:size-icon-lg",
|
||||
sm: "min-w-20 h-8 px-2 py-1.5 text-xs [&_svg]:size-icon-sm [&>img]:size-icon-sm",
|
||||
xs: "min-w-8 py-1 px-2 text-2xs rounded-md",
|
||||
icon: "size-8 px-1.5 [&_svg]:size-icon-sm",
|
||||
"icon-lg": "size-10 px-2 [&_svg]:size-icon-lg",
|
||||
icon: "size-8 px-1.5 [&_svg]:size-icon-sm [&>img]:size-icon-sm",
|
||||
"icon-lg": "size-10 px-2 [&_svg]:size-icon-lg [&>img]:size-icon-lg",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -5,15 +5,15 @@ import { getExternalImageStylesFromUrl } from "theme/externalImages";
|
||||
export const ExternalImage = forwardRef<
|
||||
HTMLImageElement,
|
||||
ImgHTMLAttributes<HTMLImageElement>
|
||||
>((attrs, ref) => {
|
||||
>((props, ref) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
// biome-ignore lint/a11y/useAltText: no reasonable alt to provide
|
||||
// biome-ignore lint/a11y/useAltText: alt should be passed in as a prop
|
||||
<img
|
||||
ref={ref}
|
||||
css={getExternalImageStylesFromUrl(theme.externalImages, attrs.src)}
|
||||
{...attrs}
|
||||
css={getExternalImageStylesFromUrl(theme.externalImages, props.src)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -81,32 +81,25 @@ export const ProxyMenu: FC<ProxyMenuProps> = ({ proxyContextValue }) => {
|
||||
</span>
|
||||
|
||||
{selectedProxy ? (
|
||||
<div css={{ display: "flex", gap: 8, alignItems: "center" }}>
|
||||
<div css={{ width: 16, height: 16, lineHeight: 0 }}>
|
||||
<img
|
||||
// Empty alt text used because we don't want to double up on
|
||||
// screen reader announcements from visually-hidden span
|
||||
alt=""
|
||||
src={selectedProxy.icon_url}
|
||||
css={{
|
||||
objectFit: "contain",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<>
|
||||
<img
|
||||
// Empty alt text used because we don't want to double up on
|
||||
// screen reader announcements from visually-hidden span
|
||||
alt=""
|
||||
src={selectedProxy.icon_url}
|
||||
/>
|
||||
|
||||
<Latency
|
||||
latency={latencies?.[selectedProxy.id]?.latencyMS}
|
||||
isLoading={proxyLatencyLoading(selectedProxy)}
|
||||
size={24}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
"Select Proxy"
|
||||
)}
|
||||
|
||||
<ChevronDownIcon className="text-content-primary !size-icon-xs" />
|
||||
<ChevronDownIcon className="text-content-primary !size-icon-sm" />
|
||||
</Button>
|
||||
|
||||
<Menu
|
||||
|
||||
@@ -62,25 +62,23 @@ export const OrganizationSidebarView: FC<
|
||||
<Button
|
||||
variant="outline"
|
||||
aria-expanded={isPopoverOpen}
|
||||
className="w-60 justify-between p-2 h-11"
|
||||
className="w-60 gap-2 justify-start"
|
||||
>
|
||||
<div className="flex flex-row gap-2 items-center p-2 truncate">
|
||||
{activeOrganization ? (
|
||||
<>
|
||||
<Avatar
|
||||
size="sm"
|
||||
src={activeOrganization.icon}
|
||||
fallback={activeOrganization.display_name}
|
||||
/>
|
||||
<span className="truncate">
|
||||
{activeOrganization.display_name || activeOrganization.name}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span className="truncate">No organization selected</span>
|
||||
)}
|
||||
</div>
|
||||
<ChevronDown />
|
||||
{activeOrganization ? (
|
||||
<>
|
||||
<Avatar
|
||||
size="sm"
|
||||
src={activeOrganization.icon}
|
||||
fallback={activeOrganization.display_name}
|
||||
/>
|
||||
<span className="truncate">
|
||||
{activeOrganization.display_name || activeOrganization.name}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span className="truncate">No organization selected</span>
|
||||
)}
|
||||
<ChevronDown className="ml-auto !size-icon-sm" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="start" className="w-60">
|
||||
|
||||
@@ -1,31 +1,8 @@
|
||||
import Button, { type ButtonProps } from "@mui/material/Button";
|
||||
import { Button, type ButtonProps } from "components/Button/Button";
|
||||
import { forwardRef } from "react";
|
||||
|
||||
export const AgentButton = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(props, ref) => {
|
||||
const { children, ...buttonProps } = props;
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...buttonProps}
|
||||
color="neutral"
|
||||
size="xlarge"
|
||||
variant="contained"
|
||||
ref={ref}
|
||||
css={(theme) => ({
|
||||
padding: "12px 20px",
|
||||
color: theme.palette.text.primary,
|
||||
// Making them smaller since those icons don't have a padding around them
|
||||
"& .MuiButton-startIcon, & .MuiButton-endIcon": {
|
||||
width: 16,
|
||||
height: 16,
|
||||
|
||||
"& svg, & img": { width: "100%", height: "100%" },
|
||||
},
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
return <Button variant="outline" ref={ref} {...props} />;
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import Link from "@mui/material/Link";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import type {
|
||||
Workspace,
|
||||
WorkspaceAgent,
|
||||
WorkspaceAgentContainer,
|
||||
} from "api/typesGenerated";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "components/Tooltip/Tooltip";
|
||||
import { ExternalLinkIcon } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { portForwardURL } from "utils/portForward";
|
||||
@@ -74,7 +78,7 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({
|
||||
const linkDest = hasHostBind
|
||||
? portForwardURL(
|
||||
wildcardHostname,
|
||||
port.host_port!,
|
||||
port.host_port,
|
||||
agent.name,
|
||||
workspace.name,
|
||||
workspace.owner_name,
|
||||
@@ -82,21 +86,19 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({
|
||||
)
|
||||
: "";
|
||||
return (
|
||||
<Tooltip key={portLabel} title={helperText}>
|
||||
<span>
|
||||
<Link
|
||||
key={portLabel}
|
||||
color="inherit"
|
||||
component={AgentButton}
|
||||
underline="none"
|
||||
startIcon={<ExternalLinkIcon className="size-icon-sm" />}
|
||||
disabled={!hasHostBind}
|
||||
href={linkDest}
|
||||
>
|
||||
{portLabel}
|
||||
</Link>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<TooltipProvider key={portLabel}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<AgentButton disabled={!hasHostBind} asChild>
|
||||
<a href={linkDest}>
|
||||
<ExternalLinkIcon />
|
||||
{portLabel}
|
||||
</a>
|
||||
</AgentButton>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{helperText}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
|
||||
import CircularProgress from "@mui/material/CircularProgress";
|
||||
import Link from "@mui/material/Link";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import { API } from "api/api";
|
||||
import type * as TypesGen from "api/typesGenerated";
|
||||
import { displayError } from "components/GlobalSnackbar/utils";
|
||||
import { Spinner } from "components/Spinner/Spinner";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "components/Tooltip/Tooltip";
|
||||
import { useProxy } from "contexts/ProxyContext";
|
||||
import { type FC, type MouseEvent, useState } from "react";
|
||||
import { type FC, useState } from "react";
|
||||
import { createAppLinkHref } from "utils/apps";
|
||||
import { generateRandomString } from "utils/random";
|
||||
import { AgentButton } from "../AgentButton";
|
||||
@@ -75,21 +79,7 @@ export const AppLink: FC<AppLinkProps> = ({ app, workspace, agent }) => {
|
||||
|
||||
let primaryTooltip = "";
|
||||
if (app.health === "initializing") {
|
||||
icon = (
|
||||
// This is a hack to make the spinner appear in the center of the start
|
||||
// icon space
|
||||
<span
|
||||
css={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<CircularProgress size={14} />
|
||||
</span>
|
||||
);
|
||||
icon = <Spinner loading />;
|
||||
primaryTooltip = "Initializing...";
|
||||
}
|
||||
if (app.health === "unhealthy") {
|
||||
@@ -112,22 +102,13 @@ export const AppLink: FC<AppLinkProps> = ({ app, workspace, agent }) => {
|
||||
canClick = false;
|
||||
}
|
||||
|
||||
const isPrivateApp = app.sharing_level === "owner";
|
||||
const canShare = app.sharing_level !== "owner";
|
||||
|
||||
return (
|
||||
<Tooltip title={primaryTooltip}>
|
||||
<Link
|
||||
color="inherit"
|
||||
component={AgentButton}
|
||||
startIcon={icon}
|
||||
endIcon={isPrivateApp ? undefined : <ShareIcon app={app} />}
|
||||
disabled={!canClick}
|
||||
href={href}
|
||||
css={{
|
||||
pointerEvents: canClick ? undefined : "none",
|
||||
textDecoration: "none !important",
|
||||
}}
|
||||
onClick={async (event: MouseEvent<HTMLElement>) => {
|
||||
const button = (
|
||||
<AgentButton asChild>
|
||||
<a
|
||||
href={canClick ? href : undefined}
|
||||
onClick={async (event) => {
|
||||
if (!canClick) {
|
||||
return;
|
||||
}
|
||||
@@ -187,8 +168,23 @@ export const AppLink: FC<AppLinkProps> = ({ app, workspace, agent }) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
{appDisplayName}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
{canShare && <ShareIcon app={app} />}
|
||||
</a>
|
||||
</AgentButton>
|
||||
);
|
||||
|
||||
if (primaryTooltip) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{button}</TooltipTrigger>
|
||||
<TooltipContent>{primaryTooltip}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
return button;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Link from "@mui/material/Link";
|
||||
import { TerminalIcon } from "components/Icons/TerminalIcon";
|
||||
import type { FC, MouseEvent } from "react";
|
||||
import { generateRandomString } from "utils/random";
|
||||
@@ -39,23 +38,21 @@ export const TerminalLink: FC<TerminalLinkProps> = ({
|
||||
}/terminal?${params.toString()}`;
|
||||
|
||||
return (
|
||||
<Link
|
||||
underline="none"
|
||||
color="inherit"
|
||||
component={AgentButton}
|
||||
startIcon={<TerminalIcon />}
|
||||
href={href}
|
||||
onClick={(event: MouseEvent<HTMLElement>) => {
|
||||
event.preventDefault();
|
||||
window.open(
|
||||
href,
|
||||
Language.terminalTitle(generateRandomString(12)),
|
||||
"width=900,height=600",
|
||||
);
|
||||
}}
|
||||
data-testid="terminal"
|
||||
>
|
||||
{DisplayAppNameMap.web_terminal}
|
||||
</Link>
|
||||
<AgentButton asChild>
|
||||
<a
|
||||
href={href}
|
||||
onClick={(event: MouseEvent<HTMLElement>) => {
|
||||
event.preventDefault();
|
||||
window.open(
|
||||
href,
|
||||
Language.terminalTitle(generateRandomString(12)),
|
||||
"width=900,height=600",
|
||||
);
|
||||
}}
|
||||
>
|
||||
<TerminalIcon />
|
||||
{DisplayAppNameMap.web_terminal}
|
||||
</a>
|
||||
</AgentButton>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||
import ButtonGroup from "@mui/material/ButtonGroup";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { API } from "api/api";
|
||||
import type { DisplayApp } from "api/typesGenerated";
|
||||
import { VSCodeIcon } from "components/Icons/VSCodeIcon";
|
||||
import { VSCodeInsidersIcon } from "components/Icons/VSCodeInsidersIcon";
|
||||
import { ChevronDownIcon } from "lucide-react";
|
||||
import { type FC, useRef, useState } from "react";
|
||||
import { AgentButton } from "../AgentButton";
|
||||
import { DisplayAppNameMap } from "../AppLink/AppLink";
|
||||
@@ -43,8 +42,8 @@ export const VSCodeDesktopButton: FC<VSCodeDesktopButtonProps> = (props) => {
|
||||
const includesVSCodeInsiders = props.displayApps.includes("vscode_insiders");
|
||||
|
||||
return includesVSCodeDesktop && includesVSCodeInsiders ? (
|
||||
<div>
|
||||
<ButtonGroup ref={menuAnchorRef} variant="outlined">
|
||||
<>
|
||||
<div ref={menuAnchorRef} className="flex items-center gap-1">
|
||||
{variant === "vscode" ? (
|
||||
<VSCodeButton {...props} />
|
||||
) : (
|
||||
@@ -58,15 +57,14 @@ export const VSCodeDesktopButton: FC<VSCodeDesktopButtonProps> = (props) => {
|
||||
aria-expanded={isVariantMenuOpen ? "true" : undefined}
|
||||
aria-label="select VSCode variant"
|
||||
aria-haspopup="menu"
|
||||
disableRipple
|
||||
onClick={() => {
|
||||
setIsVariantMenuOpen(true);
|
||||
}}
|
||||
css={{ paddingLeft: 0, paddingRight: 0 }}
|
||||
size="icon-lg"
|
||||
>
|
||||
<KeyboardArrowDownIcon css={{ fontSize: 16 }} />
|
||||
<ChevronDownIcon />
|
||||
</AgentButton>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
<Menu
|
||||
open={isVariantMenuOpen}
|
||||
@@ -97,7 +95,7 @@ export const VSCodeDesktopButton: FC<VSCodeDesktopButtonProps> = (props) => {
|
||||
{DisplayAppNameMap.vscode_insiders}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
</>
|
||||
) : includesVSCodeDesktop ? (
|
||||
<VSCodeButton {...props} />
|
||||
) : (
|
||||
@@ -115,7 +113,6 @@ const VSCodeButton: FC<VSCodeDesktopButtonProps> = ({
|
||||
|
||||
return (
|
||||
<AgentButton
|
||||
startIcon={<VSCodeIcon />}
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
@@ -145,6 +142,7 @@ const VSCodeButton: FC<VSCodeDesktopButtonProps> = ({
|
||||
});
|
||||
}}
|
||||
>
|
||||
<VSCodeIcon />
|
||||
{DisplayAppNameMap.vscode}
|
||||
</AgentButton>
|
||||
);
|
||||
@@ -160,7 +158,6 @@ const VSCodeInsidersButton: FC<VSCodeDesktopButtonProps> = ({
|
||||
|
||||
return (
|
||||
<AgentButton
|
||||
startIcon={<VSCodeInsidersIcon />}
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
@@ -189,6 +186,7 @@ const VSCodeInsidersButton: FC<VSCodeDesktopButtonProps> = ({
|
||||
});
|
||||
}}
|
||||
>
|
||||
<VSCodeInsidersIcon />
|
||||
{DisplayAppNameMap.vscode_insiders}
|
||||
</AgentButton>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||
import ButtonGroup from "@mui/material/ButtonGroup";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { API } from "api/api";
|
||||
import type { DisplayApp } from "api/typesGenerated";
|
||||
import { VSCodeIcon } from "components/Icons/VSCodeIcon";
|
||||
import { VSCodeInsidersIcon } from "components/Icons/VSCodeInsidersIcon";
|
||||
import { ChevronDownIcon } from "lucide-react";
|
||||
import { type FC, useRef, useState } from "react";
|
||||
import { AgentButton } from "../AgentButton";
|
||||
import { DisplayAppNameMap } from "../AppLink/AppLink";
|
||||
@@ -46,8 +45,8 @@ export const VSCodeDevContainerButton: FC<VSCodeDevContainerButtonProps> = (
|
||||
const includesVSCodeInsiders = props.displayApps.includes("vscode_insiders");
|
||||
|
||||
return includesVSCodeDesktop && includesVSCodeInsiders ? (
|
||||
<div>
|
||||
<ButtonGroup ref={menuAnchorRef} variant="outlined">
|
||||
<>
|
||||
<div ref={menuAnchorRef} className="flex items-center gap-1">
|
||||
{variant === "vscode" ? (
|
||||
<VSCodeButton {...props} />
|
||||
) : (
|
||||
@@ -61,15 +60,14 @@ export const VSCodeDevContainerButton: FC<VSCodeDevContainerButtonProps> = (
|
||||
aria-expanded={isVariantMenuOpen ? "true" : undefined}
|
||||
aria-label="select VSCode variant"
|
||||
aria-haspopup="menu"
|
||||
disableRipple
|
||||
onClick={() => {
|
||||
setIsVariantMenuOpen(true);
|
||||
}}
|
||||
css={{ paddingLeft: 0, paddingRight: 0 }}
|
||||
size="icon-lg"
|
||||
>
|
||||
<KeyboardArrowDownIcon css={{ fontSize: 16 }} />
|
||||
<ChevronDownIcon />
|
||||
</AgentButton>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
<Menu
|
||||
open={isVariantMenuOpen}
|
||||
@@ -100,7 +98,7 @@ export const VSCodeDevContainerButton: FC<VSCodeDevContainerButtonProps> = (
|
||||
{DisplayAppNameMap.vscode_insiders}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
</>
|
||||
) : includesVSCodeDesktop ? (
|
||||
<VSCodeButton {...props} />
|
||||
) : (
|
||||
@@ -119,7 +117,6 @@ const VSCodeButton: FC<VSCodeDevContainerButtonProps> = ({
|
||||
|
||||
return (
|
||||
<AgentButton
|
||||
startIcon={<VSCodeIcon />}
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
@@ -147,6 +144,7 @@ const VSCodeButton: FC<VSCodeDevContainerButtonProps> = ({
|
||||
});
|
||||
}}
|
||||
>
|
||||
<VSCodeIcon />
|
||||
{DisplayAppNameMap.vscode}
|
||||
</AgentButton>
|
||||
);
|
||||
@@ -163,7 +161,6 @@ const VSCodeInsidersButton: FC<VSCodeDevContainerButtonProps> = ({
|
||||
|
||||
return (
|
||||
<AgentButton
|
||||
startIcon={<VSCodeInsidersIcon />}
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
@@ -191,6 +188,7 @@ const VSCodeInsidersButton: FC<VSCodeDevContainerButtonProps> = ({
|
||||
});
|
||||
}}
|
||||
>
|
||||
<VSCodeInsidersIcon />
|
||||
{DisplayAppNameMap.vscode_insiders}
|
||||
</AgentButton>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import type { CSSObject } from "@emotion/react";
|
||||
|
||||
type ExternalImageMode = keyof ExternalImageModeStyles;
|
||||
|
||||
export interface ExternalImageModeStyles {
|
||||
/**
|
||||
* monochrome icons will be flattened to a neutral, theme-appropriate color.
|
||||
|
||||
Reference in New Issue
Block a user