@@ -2,6 +2,12 @@ import { API } from "api/api";
2
2
import { getErrorDetail , getErrorMessage } from "api/errors" ;
3
3
import type { WorkspaceApp , WorkspaceStatus } from "api/typesGenerated" ;
4
4
import { Button } from "components/Button/Button" ;
5
+ import {
6
+ DropdownMenu ,
7
+ DropdownMenuContent ,
8
+ DropdownMenuItem ,
9
+ DropdownMenuTrigger ,
10
+ } from "components/DropdownMenu/DropdownMenu" ;
5
11
import { ExternalImage } from "components/ExternalImage/ExternalImage" ;
6
12
import { Loader } from "components/Loader/Loader" ;
7
13
import { Margins } from "components/Margins/Margins" ;
@@ -14,7 +20,12 @@ import {
14
20
TooltipTrigger ,
15
21
} from "components/Tooltip/Tooltip" ;
16
22
import { useProxy } from "contexts/ProxyContext" ;
17
- import { ArrowLeftIcon , LayoutGridIcon , RotateCcwIcon } from "lucide-react" ;
23
+ import {
24
+ ArrowLeftIcon ,
25
+ ChevronDownIcon ,
26
+ LayoutGridIcon ,
27
+ RotateCcwIcon ,
28
+ } from "lucide-react" ;
18
29
import { AppStatusIcon } from "modules/apps/AppStatusIcon" ;
19
30
import { getAppHref } from "modules/apps/apps" ;
20
31
import { useAppLink } from "modules/apps/useAppLink" ;
@@ -312,26 +323,67 @@ const TaskApps: FC<TaskAppsProps> = ({ task }) => {
312
323
return src ;
313
324
} ) ;
314
325
326
+ const embeddedApps = apps . filter ( ( app ) => ! app . external ) ;
327
+ const externalApps = apps . filter ( ( app ) => app . external ) ;
328
+
315
329
return (
316
330
< main className = "flex-1 flex flex-col" >
317
331
< div className = "border-0 border-b border-border border-solid w-full p-1 flex gap-2" >
318
- { apps . map ( ( app ) => (
319
- < TaskAppButton
320
- key = { app . id }
321
- task = { task }
322
- app = { app }
323
- active = { app . id === activeAppId }
324
- onClick = { ( e ) => {
325
- if ( app . external ) {
326
- return ;
327
- }
328
-
329
- e . preventDefault ( ) ;
330
- setActiveAppId ( app . id ) ;
331
- setIframeSrc ( e . currentTarget . href ) ;
332
- } }
333
- />
334
- ) ) }
332
+ { embeddedApps
333
+ . filter ( ( app ) => ! app . external )
334
+ . map ( ( app ) => (
335
+ < TaskAppButton
336
+ key = { app . id }
337
+ task = { task }
338
+ app = { app }
339
+ active = { app . id === activeAppId }
340
+ onClick = { ( e ) => {
341
+ if ( app . external ) {
342
+ return ;
343
+ }
344
+
345
+ e . preventDefault ( ) ;
346
+ setActiveAppId ( app . id ) ;
347
+ setIframeSrc ( e . currentTarget . href ) ;
348
+ } }
349
+ />
350
+ ) ) }
351
+
352
+ { externalApps . length > 0 && (
353
+ < div className = "ml-auto" >
354
+ < DropdownMenu >
355
+ < DropdownMenuTrigger asChild >
356
+ < Button size = "sm" variant = "subtle" >
357
+ Open in IDE
358
+ < ChevronDownIcon />
359
+ </ Button >
360
+ </ DropdownMenuTrigger >
361
+ < DropdownMenuContent >
362
+ { externalApps
363
+ . filter ( ( app ) => app . external )
364
+ . map ( ( app ) => {
365
+ const link = useAppLink ( app , {
366
+ agent,
367
+ workspace : task . workspace ,
368
+ } ) ;
369
+
370
+ return (
371
+ < DropdownMenuItem key = { app . id } asChild >
372
+ < RouterLink to = { link . href } >
373
+ { app . icon ? (
374
+ < ExternalImage src = { app . icon } />
375
+ ) : (
376
+ < LayoutGridIcon />
377
+ ) }
378
+ { link . label }
379
+ </ RouterLink >
380
+ </ DropdownMenuItem >
381
+ ) ;
382
+ } ) }
383
+ </ DropdownMenuContent >
384
+ </ DropdownMenu >
385
+ </ div >
386
+ ) }
335
387
</ div >
336
388
337
389
< div className = "flex-1" >
0 commit comments