@@ -8,20 +8,12 @@ import type {
88Workspace ,
99WorkspaceAgent ,
1010WorkspaceAgentMetadata ,
11- WorkspaceApp ,
1211} from "api/typesGenerated" ;
1312import { isAxiosError } from "axios" ;
1413import { Button } from "components/Button/Button" ;
1514import { DropdownArrow } from "components/DropdownArrow/DropdownArrow" ;
16- import {
17- DropdownMenu ,
18- DropdownMenuContent ,
19- DropdownMenuItem ,
20- DropdownMenuTrigger ,
21- } from "components/DropdownMenu/DropdownMenu" ;
2215import { Stack } from "components/Stack/Stack" ;
2316import { useProxy } from "contexts/ProxyContext" ;
24- import { Folder } from "lucide-react" ;
2517import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility" ;
2618import { AppStatuses } from "pages/WorkspacePage/AppStatuses" ;
2719import {
@@ -36,15 +28,14 @@ import {
3628import { useQuery } from "react-query" ;
3729import AutoSizer from "react-virtualized-auto-sizer" ;
3830import type { FixedSizeList as List , ListOnScrollProps } from "react-window" ;
39- import { AgentButton } from "./AgentButton " ;
31+ import { Apps , organizeAgentApps } from "./AgentApps/AgentApps " ;
4032import { AgentDevcontainerCard } from "./AgentDevcontainerCard" ;
4133import { AgentLatency } from "./AgentLatency" ;
4234import { AGENT_LOG_LINE_HEIGHT } from "./AgentLogs/AgentLogLine" ;
4335import { AgentLogs } from "./AgentLogs/AgentLogs" ;
4436import { AgentMetadata } from "./AgentMetadata" ;
4537import { AgentStatus } from "./AgentStatus" ;
4638import { AgentVersion } from "./AgentVersion" ;
47- import { AppLink } from "./AppLink/AppLink" ;
4839import { DownloadAgentLogsButton } from "./DownloadAgentLogsButton" ;
4940import { PortForwardButton } from "./PortForwardButton" ;
5041import { AgentSSHButton } from "./SSHButton/SSHButton" ;
@@ -354,93 +345,6 @@ export const AgentRow: FC<AgentRowProps> = ({
354345) ;
355346} ;
356347
357- export type AppSection = {
358- /**
359- * If there is no `group`, just render all of the apps inline. If there is a
360- * group name, show them all in a dropdown.
361- */
362- group ?:string ;
363-
364- apps :WorkspaceApp [ ] ;
365- } ;
366-
367- /**
368- * organizeAgentApps returns an ordering of agent apps that accounts for
369- * grouping. When we receive the list of apps from the backend, they have
370- * already been "ordered" by their `order` attribute, but we are not given that
371- * value. We must be careful to preserve that ordering, while also properly
372- * grouping together all apps of any given group.
373- *
374- * The position of the group overall is determined by the `order` position of
375- * the first app in the group. There may be several sections returned without
376- * a group name, to allow placing grouped apps in between non-grouped apps. Not
377- * every ungrouped section is expected to have a group in between, to make the
378- * algorithm a little simpler to implement.
379- */
380- export function organizeAgentApps ( apps :readonly WorkspaceApp [ ] ) :AppSection [ ] {
381- let currentSection :AppSection | undefined = undefined ;
382- const appGroups :AppSection [ ] = [ ] ;
383- const groupsByName = new Map < string , AppSection > ( ) ;
384-
385- for ( const app of apps ) {
386- if ( app . hidden ) {
387- continue ;
388- }
389-
390- if ( ! currentSection || app . group !== currentSection . group ) {
391- const existingSection = groupsByName . get ( app . group ! ) ;
392- if ( existingSection ) {
393- currentSection = existingSection ;
394- } else {
395- currentSection = {
396- group :app . group ,
397- apps :[ ] ,
398- } ;
399- appGroups . push ( currentSection ) ;
400- if ( app . group ) {
401- groupsByName . set ( app . group , currentSection ) ;
402- }
403- }
404- }
405-
406- currentSection . apps . push ( app ) ;
407- }
408-
409- return appGroups ;
410- }
411-
412- export type AppsProps = {
413- section :AppSection ;
414- agent :WorkspaceAgent ;
415- workspace :Workspace ;
416- } ;
417-
418- export const Apps :FC < AppsProps > = ( { section, agent, workspace} ) => {
419- return section . group ?(
420- < DropdownMenu >
421- < DropdownMenuTrigger asChild >
422- < AgentButton >
423- < Folder />
424- { section . group }
425- </ AgentButton >
426- </ DropdownMenuTrigger >
427- < DropdownMenuContent align = "start" >
428- { section . apps . map ( ( app ) => (
429- < DropdownMenuItem key = { app . slug } >
430- < AppLink grouped app = { app } agent = { agent } workspace = { workspace } />
431- </ DropdownMenuItem >
432- ) ) }
433- </ DropdownMenuContent >
434- </ DropdownMenu >
435- ) :(
436- < >
437- { section . apps . map ( ( app ) => (
438- < AppLink key = { app . slug } app = { app } agent = { agent } workspace = { workspace } />
439- ) ) }
440- </ >
441- ) ;
442- } ;
443-
444348const styles = {
445349agentRow :( theme ) => ( {
446350fontSize :14 ,