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: display builtin apps on workspaces table#17695

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
BrunoQuaresma merged 4 commits intomainfrombq/refact-actions
May 7, 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
54 changes: 54 additions & 0 deletionssite/src/modules/apps/apps.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
typeGetVSCodeHrefParams={
owner:string;
workspace:string;
token:string;
agent?:string;
folder?:string;
};

exportconstgetVSCodeHref=(
app:"vscode"|"vscode-insiders",
{ owner, workspace, token, agent, folder}:GetVSCodeHrefParams,
)=>{
constquery=newURLSearchParams({
owner,
workspace,
url:location.origin,
token,
openRecent:"true",
});
if(agent){
query.set("agent",agent);
}
if(folder){
query.set("folder",folder);
}
return`${app}://coder.coder-remote/open?${query}`;
};

typeGetTerminalHrefParams={
username:string;
workspace:string;
agent?:string;
container?:string;
};

exportconstgetTerminalHref=({
username,
workspace,
agent,
container,
}:GetTerminalHrefParams)=>{
constparams=newURLSearchParams();
if(container){
params.append("container",container);
}
// Always use the primary for the terminal link. This is a relative link.
return`/@${username}/${workspace}${
agent ?`.${agent}` :""
}/terminal?${params}`;
};

exportconstopenAppInNewWindow=(name:string,href:string)=>{
window.open(href,"_blank","width=900,height=600");
};
26 changes: 8 additions & 18 deletionssite/src/modules/resources/TerminalLink/TerminalLink.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
import{TerminalIcon}from"components/Icons/TerminalIcon";
import{getTerminalHref,openAppInNewWindow}from"modules/apps/apps";
importtype{FC,MouseEvent}from"react";
import{generateRandomString}from"utils/random";
import{AgentButton}from"../AgentButton";
import{DisplayAppNameMap}from"../AppLink/AppLink";

constLanguage={
terminalTitle:(identifier:string):string=>`Terminal -${identifier}`,
};

exportinterfaceTerminalLinkProps{
workspaceName:string;
agentName?:string;
Expand All@@ -28,26 +24,20 @@ export const TerminalLink: FC<TerminalLinkProps> = ({
workspaceName,
containerName,
})=>{
constparams=newURLSearchParams();
if(containerName){
params.append("container",containerName);
}
// Always use the primary for the terminal link. This is a relative link.
consthref=`/@${userName}/${workspaceName}${
agentName ?`.${agentName}` :""
}/terminal?${params.toString()}`;
consthref=getTerminalHref({
username:userName,
workspace:workspaceName,
agent:agentName,
container:containerName,
});

return(
<AgentButtonasChild>
<a
href={href}
onClick={(event:MouseEvent<HTMLElement>)=>{
event.preventDefault();
window.open(
href,
Language.terminalTitle(generateRandomString(12)),
"width=900,height=600",
);
openAppInNewWindow("Terminal",href);
}}
>
<TerminalIcon/>
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ import type { DisplayApp } from "api/typesGenerated";
import{VSCodeIcon}from"components/Icons/VSCodeIcon";
import{VSCodeInsidersIcon}from"components/Icons/VSCodeInsidersIcon";
import{ChevronDownIcon}from"lucide-react";
import{getVSCodeHref}from"modules/apps/apps";
import{typeFC,useRef,useState}from"react";
import{AgentButton}from"../AgentButton";
import{DisplayAppNameMap}from"../AppLink/AppLink";
Expand DownExpand Up@@ -118,21 +119,13 @@ const VSCodeButton: FC<VSCodeDesktopButtonProps> = ({
setLoading(true);
API.getApiKey()
.then(({ key})=>{
constquery=newURLSearchParams({
location.href=getVSCodeHref("vscode",{
owner:userName,
workspace:workspaceName,
url:location.origin,
token:key,
openRecent:"true",
agent:agentName,
folder:folderPath,
});
if(agentName){
query.set("agent",agentName);
}
if(folderPath){
query.set("folder",folderPath);
}

location.href=`vscode://coder.coder-remote/open?${query.toString()}`;
})
.catch((ex)=>{
console.error(ex);
Expand DownExpand Up@@ -163,20 +156,13 @@ const VSCodeInsidersButton: FC<VSCodeDesktopButtonProps> = ({
setLoading(true);
API.getApiKey()
.then(({ key})=>{
constquery=newURLSearchParams({
location.href=getVSCodeHref("vscode-insiders",{
owner:userName,
workspace:workspaceName,
url:location.origin,
token:key,
agent:agentName,
folder:folderPath,
});
if(agentName){
query.set("agent",agentName);
}
if(folderPath){
query.set("folder",folderPath);
}

location.href=`vscode-insiders://coder.coder-remote/open?${query.toString()}`;
})
.catch((ex)=>{
console.error(ex);
Expand Down
160 changes: 145 additions & 15 deletionssite/src/pages/WorkspacesPage/WorkspacesTable.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ import Star from "@mui/icons-material/Star";
importCheckboxfrom"@mui/material/Checkbox";
importSkeletonfrom"@mui/material/Skeleton";
import{templateVersion}from"api/queries/templates";
import{apiKey}from"api/queries/users";
import{
cancelBuild,
deleteWorkspace,
Expand All@@ -19,6 +20,8 @@ import { Avatar } from "components/Avatar/Avatar";
import{AvatarData}from"components/Avatar/AvatarData";
import{AvatarDataSkeleton}from"components/Avatar/AvatarDataSkeleton";
import{Button}from"components/Button/Button";
import{VSCodeIcon}from"components/Icons/VSCodeIcon";
import{VSCodeInsidersIcon}from"components/Icons/VSCodeInsidersIcon";
import{InfoTooltip}from"components/InfoTooltip/InfoTooltip";
import{Spinner}from"components/Spinner/Spinner";
import{Stack}from"components/Stack/Stack";
Expand DownExpand Up@@ -49,7 +52,17 @@ import dayjs from "dayjs";
importrelativeTimefrom"dayjs/plugin/relativeTime";
import{useAuthenticated}from"hooks";
import{useClickableTableRow}from"hooks/useClickableTableRow";
import{BanIcon,PlayIcon,RefreshCcwIcon,SquareIcon}from"lucide-react";
import{
BanIcon,
PlayIcon,
RefreshCcwIcon,
SquareTerminalIcon,
}from"lucide-react";
import{
getTerminalHref,
getVSCodeHref,
openAppInNewWindow,
}from"modules/apps/apps";
import{useDashboard}from"modules/dashboard/useDashboard";
import{WorkspaceAppStatus}from"modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus";
import{WorkspaceDormantBadge}from"modules/workspaces/WorkspaceDormantBadge/WorkspaceDormantBadge";
Expand All@@ -59,6 +72,7 @@ import {
useWorkspaceUpdate,
}from"modules/workspaces/WorkspaceUpdateDialogs";
import{abilitiesByWorkspaceStatus}from"modules/workspaces/actions";
importtypeReactfrom"react";
import{
typeFC,
typePropsWithChildren,
Expand DownExpand Up@@ -534,6 +548,10 @@ const WorkspaceActionsCell: FC<WorkspaceActionsCellProps> = ({
return(
<TableCell>
<divclassName="flex gap-1 justify-end">
{workspace.latest_build.status==="running"&&(
<WorkspaceAppsworkspace={workspace}/>
)}

{abilities.actions.includes("start")&&(
<PrimaryAction
onClick={()=>startWorkspaceMutation.mutate({})}
Expand All@@ -557,18 +575,6 @@ const WorkspaceActionsCell: FC<WorkspaceActionsCellProps> = ({
</>
)}

{abilities.actions.includes("stop")&&(
<PrimaryAction
onClick={()=>{
stopWorkspaceMutation.mutate({});
}}
isLoading={stopWorkspaceMutation.isLoading}
label="Stop workspace"
>
<SquareIcon/>
</PrimaryAction>
)}

Comment on lines -560 to -571
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Do we not want users to be able to stop workspaces from the table view anymore? I see in the designs there is a dropdown menu that will have this. Is that going to be a fast follow to this PR?

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yes, the "more options" menu will be implemented in a different PR.

brettkolodny reacted with thumbs up emoji
{abilities.canCancel&&(
<PrimaryAction
onClick={cancelBuildMutation.mutate}
Expand All@@ -594,9 +600,9 @@ const WorkspaceActionsCell: FC<WorkspaceActionsCellProps> = ({
};

typePrimaryActionProps=PropsWithChildren<{
onClick:()=>void;
isLoading:boolean;
label:string;
isLoading?:boolean;
onClick:()=>void;
}>;

constPrimaryAction:FC<PrimaryActionProps>=({
Expand DownExpand Up@@ -626,3 +632,127 @@ const PrimaryAction: FC<PrimaryActionProps> = ({
</TooltipProvider>
);
};

typeWorkspaceAppsProps={
workspace:Workspace;
};

constWorkspaceApps:FC<WorkspaceAppsProps>=({ workspace})=>{
const{data:apiKeyRes}=useQuery(apiKey());
consttoken=apiKeyRes?.key;

/**
* Coder is pretty flexible and allows an enormous variety of use cases, such
* as having multiple resources with many agents, but they are not common. The
* most common scenario is to have one single compute resource with one single
* agent containing all the apps. Lets test this getting the apps for the
* first resource, and first agent - they are sorted to return the compute
* resource first - and see what customers and ourselves, using dogfood, think
* about that.
*/
constagent=workspace.latest_build.resources
.filter((r)=>!r.hide)
.at(0)
?.agents?.at(0);
if(!agent){
returnnull;
}

constbuttons:ReactNode[]=[];

if(agent.display_apps.includes("vscode")){
buttons.push(
<AppLink
isLoading={!token}
label="Open VSCode"
href={getVSCodeHref("vscode",{
owner:workspace.owner_name,
workspace:workspace.name,
agent:agent.name,
token:apiKeyRes?.key??"",
folder:agent.expanded_directory,
})}
>
<VSCodeIcon/>
</AppLink>,
);
}

if(agent.display_apps.includes("vscode_insiders")){
buttons.push(
<AppLink
label="Open VSCode Insiders"
isLoading={!token}
href={getVSCodeHref("vscode-insiders",{
owner:workspace.owner_name,
workspace:workspace.name,
agent:agent.name,
token:apiKeyRes?.key??"",
folder:agent.expanded_directory,
})}
>
<VSCodeInsidersIcon/>
</AppLink>,
);
}

if(agent.display_apps.includes("web_terminal")){
consthref=getTerminalHref({
username:workspace.owner_name,
workspace:workspace.name,
agent:agent.name,
});
buttons.push(
<AppLink
href={href}
onClick={(e)=>{
e.preventDefault();
openAppInNewWindow("Terminal",href);
}}
label="Open Terminal"
>
<SquareTerminalIcon/>
</AppLink>,
);
}

returnbuttons;
};

typeAppLinkProps=PropsWithChildren<{
label:string;
href:string;
isLoading?:boolean;
onClick?:(e:React.MouseEvent<HTMLAnchorElement>)=>void;
}>;

constAppLink:FC<AppLinkProps>=({
href,
isLoading,
label,
children,
onClick,
})=>{
return(
<TooltipProvider>
<Tooltip>
<TooltipTriggerasChild>
<Buttonvariant="outline"size="icon-lg"asChild>
<a
className={isLoading ?"animate-pulse" :""}
href={href}
onClick={(e)=>{
e.stopPropagation();
onClick?.(e);
}}
>
{children}
<spanclassName="sr-only">{label}</span>
</a>
</Button>
</TooltipTrigger>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
</TooltipProvider>
);
};
Loading

[8]ページ先頭

©2009-2025 Movatter.jp