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

Commitab8c437

Browse files
feat(site): open dev container in vscode (#17182)
Closes#16426Adds a new button `VSCodeDevContainerButton` for connecting to a devcontainer with VSCode.
1 parentaa3d71d commitab8c437

File tree

5 files changed

+281
-7
lines changed

5 files changed

+281
-7
lines changed

‎site/src/modules/resources/AgentDevcontainerCard.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
importtype{Meta,StoryObj}from"@storybook/react";
22
import{
33
MockWorkspace,
4+
MockWorkspaceAgent,
45
MockWorkspaceAgentContainer,
56
MockWorkspaceAgentContainerPorts,
67
}from"testHelpers/entities";
@@ -13,7 +14,7 @@ const meta: Meta<typeof AgentDevcontainerCard> = {
1314
container:MockWorkspaceAgentContainer,
1415
workspace:MockWorkspace,
1516
wildcardHostname:"*.wildcard.hostname",
16-
agentName:"dev",
17+
agent:MockWorkspaceAgent,
1718
},
1819
};
1920

‎site/src/modules/resources/AgentDevcontainerCard.tsx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
11
importLinkfrom"@mui/material/Link";
22
importTooltip,{typeTooltipProps}from"@mui/material/Tooltip";
3-
importtype{Workspace,WorkspaceAgentContainer}from"api/typesGenerated";
3+
importtype{
4+
Workspace,
5+
WorkspaceAgent,
6+
WorkspaceAgentContainer,
7+
}from"api/typesGenerated";
48
import{ExternalLinkIcon}from"lucide-react";
59
importtype{FC}from"react";
610
import{portForwardURL}from"utils/portForward";
711
import{AgentButton}from"./AgentButton";
812
import{AgentDevcontainerSSHButton}from"./SSHButton/SSHButton";
913
import{TerminalLink}from"./TerminalLink/TerminalLink";
14+
import{VSCodeDevContainerButton}from"./VSCodeDevContainerButton/VSCodeDevContainerButton";
1015

1116
typeAgentDevcontainerCardProps={
17+
agent:WorkspaceAgent;
1218
container:WorkspaceAgentContainer;
1319
workspace:Workspace;
1420
wildcardHostname:string;
15-
agentName:string;
1621
};
1722

1823
exportconstAgentDevcontainerCard:FC<AgentDevcontainerCardProps>=({
24+
agent,
1925
container,
2026
workspace,
21-
agentName,
2227
wildcardHostname,
2328
})=>{
29+
constfolderPath=container.labels["devcontainer.local_folder"];
30+
constcontainerFolder=container.volumes[folderPath];
31+
2432
return(
2533
<section
2634
className="border border-border border-dashed rounded p-6 "
@@ -40,9 +48,17 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({
4048
<h4className="m-0 text-xl font-semibold">Forwarded ports</h4>
4149

4250
<divclassName="flex gap-4 flex-wrap mt-4">
51+
<VSCodeDevContainerButton
52+
userName={workspace.owner_name}
53+
workspaceName={workspace.name}
54+
devContainerName={container.name}
55+
devContainerFolder={containerFolder}
56+
displayApps={agent.display_apps}
57+
/>
58+
4359
<TerminalLink
4460
workspaceName={workspace.name}
45-
agentName={agentName}
61+
agentName={agent.name}
4662
containerName={container.name}
4763
userName={workspace.owner_name}
4864
/>
@@ -58,7 +74,7 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({
5874
?portForwardURL(
5975
wildcardHostname,
6076
port.host_port!,
61-
agentName,
77+
agent.name,
6278
workspace.name,
6379
workspace.owner_name,
6480
location.protocol==="https" ?"https" :"http",

‎site/src/modules/resources/AgentRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ export const AgentRow: FC<AgentRowProps> = ({
290290
container={container}
291291
workspace={workspace}
292292
wildcardHostname={proxy.preferredWildcardHostname}
293-
agentName={agent.name}
293+
agent={agent}
294294
/>
295295
);
296296
})}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
importtype{Meta,StoryObj}from"@storybook/react";
2+
import{MockWorkspace,MockWorkspaceAgent}from"testHelpers/entities";
3+
import{VSCodeDevContainerButton}from"./VSCodeDevContainerButton";
4+
5+
constmeta:Meta<typeofVSCodeDevContainerButton>={
6+
title:"modules/resources/VSCodeDevContainerButton",
7+
component:VSCodeDevContainerButton,
8+
};
9+
10+
exportdefaultmeta;
11+
typeStory=StoryObj<typeofVSCodeDevContainerButton>;
12+
13+
exportconstDefault:Story={
14+
args:{
15+
userName:MockWorkspace.owner_name,
16+
workspaceName:MockWorkspace.name,
17+
agentName:MockWorkspaceAgent.name,
18+
devContainerName:"musing_ride",
19+
devContainerFolder:"/workspace/coder",
20+
displayApps:[
21+
"vscode",
22+
"vscode_insiders",
23+
"port_forwarding_helper",
24+
"ssh_helper",
25+
"web_terminal",
26+
],
27+
},
28+
};
29+
30+
exportconstVSCodeOnly:Story={
31+
args:{
32+
userName:MockWorkspace.owner_name,
33+
workspaceName:MockWorkspace.name,
34+
agentName:MockWorkspaceAgent.name,
35+
devContainerName:"nifty_borg",
36+
devContainerFolder:"/workspace/coder",
37+
displayApps:[
38+
"vscode",
39+
"port_forwarding_helper",
40+
"ssh_helper",
41+
"web_terminal",
42+
],
43+
},
44+
};
45+
46+
exportconstInsidersOnly:Story={
47+
args:{
48+
userName:MockWorkspace.owner_name,
49+
workspaceName:MockWorkspace.name,
50+
agentName:MockWorkspaceAgent.name,
51+
devContainerName:"amazing_swartz",
52+
devContainerFolder:"/workspace/coder",
53+
displayApps:[
54+
"vscode_insiders",
55+
"port_forwarding_helper",
56+
"ssh_helper",
57+
"web_terminal",
58+
],
59+
},
60+
};
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
importKeyboardArrowDownIconfrom"@mui/icons-material/KeyboardArrowDown";
2+
importButtonGroupfrom"@mui/material/ButtonGroup";
3+
importMenufrom"@mui/material/Menu";
4+
importMenuItemfrom"@mui/material/MenuItem";
5+
import{API}from"api/api";
6+
importtype{DisplayApp}from"api/typesGenerated";
7+
import{VSCodeIcon}from"components/Icons/VSCodeIcon";
8+
import{VSCodeInsidersIcon}from"components/Icons/VSCodeInsidersIcon";
9+
import{typeFC,useRef,useState}from"react";
10+
import{AgentButton}from"../AgentButton";
11+
import{DisplayAppNameMap}from"../AppLink/AppLink";
12+
13+
exportinterfaceVSCodeDevContainerButtonProps{
14+
userName:string;
15+
workspaceName:string;
16+
agentName?:string;
17+
devContainerName:string;
18+
devContainerFolder:string;
19+
displayApps:readonlyDisplayApp[];
20+
}
21+
22+
typeVSCodeVariant="vscode"|"vscode-insiders";
23+
24+
constVARIANT_KEY="vscode-variant";
25+
26+
exportconstVSCodeDevContainerButton:FC<VSCodeDevContainerButtonProps>=(
27+
props,
28+
)=>{
29+
const[isVariantMenuOpen,setIsVariantMenuOpen]=useState(false);
30+
constpreviousVariant=localStorage.getItem(VARIANT_KEY);
31+
const[variant,setVariant]=useState<VSCodeVariant>(()=>{
32+
if(!previousVariant){
33+
return"vscode";
34+
}
35+
returnpreviousVariantasVSCodeVariant;
36+
});
37+
constmenuAnchorRef=useRef<HTMLDivElement>(null);
38+
39+
constselectVariant=(variant:VSCodeVariant)=>{
40+
localStorage.setItem(VARIANT_KEY,variant);
41+
setVariant(variant);
42+
setIsVariantMenuOpen(false);
43+
};
44+
45+
constincludesVSCodeDesktop=props.displayApps.includes("vscode");
46+
constincludesVSCodeInsiders=props.displayApps.includes("vscode_insiders");
47+
48+
returnincludesVSCodeDesktop&&includesVSCodeInsiders ?(
49+
<div>
50+
<ButtonGroupref={menuAnchorRef}variant="outlined">
51+
{variant==="vscode" ?(
52+
<VSCodeButton{...props}/>
53+
) :(
54+
<VSCodeInsidersButton{...props}/>
55+
)}
56+
57+
<AgentButton
58+
aria-controls={
59+
isVariantMenuOpen ?"vscode-variant-button-menu" :undefined
60+
}
61+
aria-expanded={isVariantMenuOpen ?"true" :undefined}
62+
aria-label="select VSCode variant"
63+
aria-haspopup="menu"
64+
disableRipple
65+
onClick={()=>{
66+
setIsVariantMenuOpen(true);
67+
}}
68+
css={{paddingLeft:0,paddingRight:0}}
69+
>
70+
<KeyboardArrowDownIconcss={{fontSize:16}}/>
71+
</AgentButton>
72+
</ButtonGroup>
73+
74+
<Menu
75+
open={isVariantMenuOpen}
76+
anchorEl={menuAnchorRef.current}
77+
onClose={()=>setIsVariantMenuOpen(false)}
78+
css={{
79+
"& .MuiMenu-paper":{
80+
width:menuAnchorRef.current?.clientWidth,
81+
},
82+
}}
83+
>
84+
<MenuItem
85+
css={{fontSize:14}}
86+
onClick={()=>{
87+
selectVariant("vscode");
88+
}}
89+
>
90+
<VSCodeIconcss={{width:12,height:12}}/>
91+
{DisplayAppNameMap.vscode}
92+
</MenuItem>
93+
<MenuItem
94+
css={{fontSize:14}}
95+
onClick={()=>{
96+
selectVariant("vscode-insiders");
97+
}}
98+
>
99+
<VSCodeInsidersIconcss={{width:12,height:12}}/>
100+
{DisplayAppNameMap.vscode_insiders}
101+
</MenuItem>
102+
</Menu>
103+
</div>
104+
) :includesVSCodeDesktop ?(
105+
<VSCodeButton{...props}/>
106+
) :(
107+
<VSCodeInsidersButton{...props}/>
108+
);
109+
};
110+
111+
constVSCodeButton:FC<VSCodeDevContainerButtonProps>=({
112+
userName,
113+
workspaceName,
114+
agentName,
115+
devContainerName,
116+
devContainerFolder,
117+
})=>{
118+
const[loading,setLoading]=useState(false);
119+
120+
return(
121+
<AgentButton
122+
startIcon={<VSCodeIcon/>}
123+
disabled={loading}
124+
onClick={()=>{
125+
setLoading(true);
126+
API.getApiKey()
127+
.then(({ key})=>{
128+
constquery=newURLSearchParams({
129+
owner:userName,
130+
workspace:workspaceName,
131+
url:location.origin,
132+
token:key,
133+
devContainerName,
134+
devContainerFolder,
135+
});
136+
if(agentName){
137+
query.set("agent",agentName);
138+
}
139+
140+
location.href=`vscode://coder.coder-remote/openDevContainer?${query.toString()}`;
141+
})
142+
.catch((ex)=>{
143+
console.error(ex);
144+
})
145+
.finally(()=>{
146+
setLoading(false);
147+
});
148+
}}
149+
>
150+
{DisplayAppNameMap.vscode}
151+
</AgentButton>
152+
);
153+
};
154+
155+
constVSCodeInsidersButton:FC<VSCodeDevContainerButtonProps>=({
156+
userName,
157+
workspaceName,
158+
agentName,
159+
devContainerName,
160+
devContainerFolder,
161+
})=>{
162+
const[loading,setLoading]=useState(false);
163+
164+
return(
165+
<AgentButton
166+
startIcon={<VSCodeInsidersIcon/>}
167+
disabled={loading}
168+
onClick={()=>{
169+
setLoading(true);
170+
API.getApiKey()
171+
.then(({ key})=>{
172+
constquery=newURLSearchParams({
173+
owner:userName,
174+
workspace:workspaceName,
175+
url:location.origin,
176+
token:key,
177+
devContainerName,
178+
devContainerFolder,
179+
});
180+
if(agentName){
181+
query.set("agent",agentName);
182+
}
183+
184+
location.href=`vscode-insiders://coder.coder-remote/openDevContainer?${query.toString()}`;
185+
})
186+
.catch((ex)=>{
187+
console.error(ex);
188+
})
189+
.finally(()=>{
190+
setLoading(false);
191+
});
192+
}}
193+
>
194+
{DisplayAppNameMap.vscode_insiders}
195+
</AgentButton>
196+
);
197+
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp