Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(site): open dev container in vscode#17182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
DanielleMaywood merged 3 commits intomainfromdm-open-devcontainer-vscode
Apr 3, 2025
Merged
Show file tree
Hide file tree
Changes fromall 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
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from "@storybook/react";
import {
MockWorkspace,
MockWorkspaceAgent,
MockWorkspaceAgentContainer,
MockWorkspaceAgentContainerPorts,
} from "testHelpers/entities";
Expand All@@ -13,7 +14,7 @@ const meta: Meta<typeof AgentDevcontainerCard> = {
container: MockWorkspaceAgentContainer,
workspace: MockWorkspace,
wildcardHostname: "*.wildcard.hostname",
agentName: "dev",
agent: MockWorkspaceAgent,
},
};

Expand Down
26 changes: 21 additions & 5 deletionssite/src/modules/resources/AgentDevcontainerCard.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
import Link from "@mui/material/Link";
import Tooltip, { type TooltipProps } from "@mui/material/Tooltip";
import type { Workspace, WorkspaceAgentContainer } from "api/typesGenerated";
import type {
Workspace,
WorkspaceAgent,
WorkspaceAgentContainer,
} from "api/typesGenerated";
import { ExternalLinkIcon } from "lucide-react";
import type { FC } from "react";
import { portForwardURL } from "utils/portForward";
import { AgentButton } from "./AgentButton";
import { AgentDevcontainerSSHButton } from "./SSHButton/SSHButton";
import { TerminalLink } from "./TerminalLink/TerminalLink";
import { VSCodeDevContainerButton } from "./VSCodeDevContainerButton/VSCodeDevContainerButton";

type AgentDevcontainerCardProps = {
agent: WorkspaceAgent;
container: WorkspaceAgentContainer;
workspace: Workspace;
wildcardHostname: string;
agentName: string;
};

export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({
agent,
container,
workspace,
agentName,
wildcardHostname,
}) => {
const folderPath = container.labels["devcontainer.local_folder"];
const containerFolder = container.volumes[folderPath];

return (
<section
className="border border-border border-dashed rounded p-6 "
Expand All@@ -40,9 +48,17 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({
<h4 className="m-0 text-xl font-semibold">Forwarded ports</h4>

<div className="flex gap-4 flex-wrap mt-4">
<VSCodeDevContainerButton
userName={workspace.owner_name}
workspaceName={workspace.name}
devContainerName={container.name}
devContainerFolder={containerFolder}
displayApps={agent.display_apps}
/>

<TerminalLink
workspaceName={workspace.name}
agentName={agentName}
agentName={agent.name}
containerName={container.name}
userName={workspace.owner_name}
/>
Expand All@@ -58,7 +74,7 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({
? portForwardURL(
wildcardHostname,
port.host_port!,
agentName,
agent.name,
workspace.name,
workspace.owner_name,
location.protocol === "https" ? "https" : "http",
Expand Down
2 changes: 1 addition & 1 deletionsite/src/modules/resources/AgentRow.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -290,7 +290,7 @@ export const AgentRow: FC<AgentRowProps> = ({
container={container}
workspace={workspace}
wildcardHostname={proxy.preferredWildcardHostname}
agentName={agent.name}
agent={agent}
/>
);
})}
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
import type { Meta, StoryObj } from "@storybook/react";
import { MockWorkspace, MockWorkspaceAgent } from "testHelpers/entities";
import { VSCodeDevContainerButton } from "./VSCodeDevContainerButton";

const meta: Meta<typeof VSCodeDevContainerButton> = {
title: "modules/resources/VSCodeDevContainerButton",
component: VSCodeDevContainerButton,
};

export default meta;
type Story = StoryObj<typeof VSCodeDevContainerButton>;

export const Default: Story = {
args: {
userName: MockWorkspace.owner_name,
workspaceName: MockWorkspace.name,
agentName: MockWorkspaceAgent.name,
devContainerName: "musing_ride",
devContainerFolder: "/workspace/coder",
displayApps: [
"vscode",
"vscode_insiders",
"port_forwarding_helper",
"ssh_helper",
"web_terminal",
],
},
};

export const VSCodeOnly: Story = {
args: {
userName: MockWorkspace.owner_name,
workspaceName: MockWorkspace.name,
agentName: MockWorkspaceAgent.name,
devContainerName: "nifty_borg",
devContainerFolder: "/workspace/coder",
displayApps: [
"vscode",
"port_forwarding_helper",
"ssh_helper",
"web_terminal",
],
},
};

export const InsidersOnly: Story = {
args: {
userName: MockWorkspace.owner_name,
workspaceName: MockWorkspace.name,
agentName: MockWorkspaceAgent.name,
devContainerName: "amazing_swartz",
devContainerFolder: "/workspace/coder",
displayApps: [
"vscode_insiders",
"port_forwarding_helper",
"ssh_helper",
"web_terminal",
],
},
};
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
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 { type FC, useRef, useState } from "react";
import { AgentButton } from "../AgentButton";
import { DisplayAppNameMap } from "../AppLink/AppLink";

export interface VSCodeDevContainerButtonProps {
userName: string;
workspaceName: string;
agentName?: string;
devContainerName: string;
devContainerFolder: string;
displayApps: readonly DisplayApp[];
}

type VSCodeVariant = "vscode" | "vscode-insiders";

const VARIANT_KEY = "vscode-variant";

export const VSCodeDevContainerButton: FC<VSCodeDevContainerButtonProps> = (
props,
) => {
const [isVariantMenuOpen, setIsVariantMenuOpen] = useState(false);
const previousVariant = localStorage.getItem(VARIANT_KEY);
const [variant, setVariant] = useState<VSCodeVariant>(() => {
if (!previousVariant) {
return "vscode";
}
return previousVariant as VSCodeVariant;
});
const menuAnchorRef = useRef<HTMLDivElement>(null);

const selectVariant = (variant: VSCodeVariant) => {
localStorage.setItem(VARIANT_KEY, variant);
setVariant(variant);
setIsVariantMenuOpen(false);
};

const includesVSCodeDesktop = props.displayApps.includes("vscode");
const includesVSCodeInsiders = props.displayApps.includes("vscode_insiders");

return includesVSCodeDesktop && includesVSCodeInsiders ? (
<div>
<ButtonGroup ref={menuAnchorRef} variant="outlined">
{variant === "vscode" ? (
<VSCodeButton {...props} />
) : (
<VSCodeInsidersButton {...props} />
)}

<AgentButton
aria-controls={
isVariantMenuOpen ? "vscode-variant-button-menu" : undefined
}
aria-expanded={isVariantMenuOpen ? "true" : undefined}
aria-label="select VSCode variant"
aria-haspopup="menu"
disableRipple
onClick={() => {
setIsVariantMenuOpen(true);
}}
css={{ paddingLeft: 0, paddingRight: 0 }}
>
<KeyboardArrowDownIcon css={{ fontSize: 16 }} />
</AgentButton>
</ButtonGroup>

<Menu
open={isVariantMenuOpen}
anchorEl={menuAnchorRef.current}
onClose={() => setIsVariantMenuOpen(false)}
css={{
"& .MuiMenu-paper": {
width: menuAnchorRef.current?.clientWidth,
},
}}
>
<MenuItem
css={{ fontSize: 14 }}
onClick={() => {
selectVariant("vscode");
}}
>
<VSCodeIcon css={{ width: 12, height: 12 }} />
{DisplayAppNameMap.vscode}
</MenuItem>
<MenuItem
css={{ fontSize: 14 }}
onClick={() => {
selectVariant("vscode-insiders");
}}
>
<VSCodeInsidersIcon css={{ width: 12, height: 12 }} />
{DisplayAppNameMap.vscode_insiders}
</MenuItem>
</Menu>
</div>
) : includesVSCodeDesktop ? (
<VSCodeButton {...props} />
) : (
<VSCodeInsidersButton {...props} />
);
};

const VSCodeButton: FC<VSCodeDevContainerButtonProps> = ({
userName,
workspaceName,
agentName,
devContainerName,
devContainerFolder,
}) => {
const [loading, setLoading] = useState(false);

return (
<AgentButton
startIcon={<VSCodeIcon />}
disabled={loading}
onClick={() => {
setLoading(true);
API.getApiKey()
.then(({ key }) => {
const query = new URLSearchParams({
owner: userName,
workspace: workspaceName,
url: location.origin,
token: key,
devContainerName,
devContainerFolder,
});
if (agentName) {
query.set("agent", agentName);
}

location.href = `vscode://coder.coder-remote/openDevContainer?${query.toString()}`;
})
.catch((ex) => {
console.error(ex);
})
.finally(() => {
setLoading(false);
});
}}
>
{DisplayAppNameMap.vscode}
</AgentButton>
);
};

const VSCodeInsidersButton: FC<VSCodeDevContainerButtonProps> = ({
userName,
workspaceName,
agentName,
devContainerName,
devContainerFolder,
}) => {
const [loading, setLoading] = useState(false);

return (
<AgentButton
startIcon={<VSCodeInsidersIcon />}
disabled={loading}
onClick={() => {
setLoading(true);
API.getApiKey()
.then(({ key }) => {
const query = new URLSearchParams({
owner: userName,
workspace: workspaceName,
url: location.origin,
token: key,
devContainerName,
devContainerFolder,
});
if (agentName) {
query.set("agent", agentName);
}

location.href = `vscode-insiders://coder.coder-remote/openDevContainer?${query.toString()}`;
})
.catch((ex) => {
console.error(ex);
})
.finally(() => {
setLoading(false);
});
}}
>
{DisplayAppNameMap.vscode_insiders}
</AgentButton>
);
};
Loading

[8]ページ先頭

©2009-2025 Movatter.jp