feat: group external apps on task page (#18107)

https://github.com/user-attachments/assets/ac03f9ad-9b89-400b-aabe-fade2997319b
This commit is contained in:
Bruno Quaresma
2025-05-29 14:10:27 -03:00
committed by GitHub
parent a85351f2bf
commit e0718a600e
3 changed files with 77 additions and 19 deletions
@@ -111,7 +111,7 @@ export const DropdownMenuItem = forwardRef<
[
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-2 text-sm text-content-secondary font-medium outline-none transition-colors",
"focus:bg-surface-secondary focus:text-content-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"[&>svg]:size-4 [&>svg]:shrink-0 no-underline",
"[&>svg]:size-4 [&>svg]:shrink-0 [&>img]:size-4 [&>img]:shrink-0 no-underline",
inset && "pl-8",
],
className,
+7 -1
View File
@@ -131,9 +131,15 @@ export const Active: Story = {
{
...MockWorkspaceApp,
id: "vscode",
display_name: "VSCode",
display_name: "VS Code Web",
icon: "/icon/code.svg",
},
{
...MockWorkspaceApp,
id: "zed",
display_name: "Zed",
icon: "/icon/zed.svg",
},
],
},
],
+69 -17
View File
@@ -2,6 +2,12 @@ import { API } from "api/api";
import { getErrorDetail, getErrorMessage } from "api/errors";
import type { WorkspaceApp, WorkspaceStatus } from "api/typesGenerated";
import { Button } from "components/Button/Button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "components/DropdownMenu/DropdownMenu";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
import { Loader } from "components/Loader/Loader";
import { Margins } from "components/Margins/Margins";
@@ -14,7 +20,12 @@ import {
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useProxy } from "contexts/ProxyContext";
import { ArrowLeftIcon, LayoutGridIcon, RotateCcwIcon } from "lucide-react";
import {
ArrowLeftIcon,
ChevronDownIcon,
LayoutGridIcon,
RotateCcwIcon,
} from "lucide-react";
import { AppStatusIcon } from "modules/apps/AppStatusIcon";
import { getAppHref } from "modules/apps/apps";
import { useAppLink } from "modules/apps/useAppLink";
@@ -312,26 +323,67 @@ const TaskApps: FC<TaskAppsProps> = ({ task }) => {
return src;
});
const embeddedApps = apps.filter((app) => !app.external);
const externalApps = apps.filter((app) => app.external);
return (
<main className="flex-1 flex flex-col">
<div className="border-0 border-b border-border border-solid w-full p-1 flex gap-2">
{apps.map((app) => (
<TaskAppButton
key={app.id}
task={task}
app={app}
active={app.id === activeAppId}
onClick={(e) => {
if (app.external) {
return;
}
{embeddedApps
.filter((app) => !app.external)
.map((app) => (
<TaskAppButton
key={app.id}
task={task}
app={app}
active={app.id === activeAppId}
onClick={(e) => {
if (app.external) {
return;
}
e.preventDefault();
setActiveAppId(app.id);
setIframeSrc(e.currentTarget.href);
}}
/>
))}
e.preventDefault();
setActiveAppId(app.id);
setIframeSrc(e.currentTarget.href);
}}
/>
))}
{externalApps.length > 0 && (
<div className="ml-auto">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="sm" variant="subtle">
Open in IDE
<ChevronDownIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{externalApps
.filter((app) => app.external)
.map((app) => {
const link = useAppLink(app, {
agent,
workspace: task.workspace,
});
return (
<DropdownMenuItem key={app.id} asChild>
<RouterLink to={link.href}>
{app.icon ? (
<ExternalImage src={app.icon} />
) : (
<LayoutGridIcon />
)}
{link.label}
</RouterLink>
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
)}
</div>
<div className="flex-1">