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

Commite7d648f

Browse files
fix: make app tabs scrollable (#19881)
The solution follows the way VSCode manages its tabs. Since many othereditors use a similar approach, this feels familiar and intuitive forour users.**Demo:**https://github.com/user-attachments/assets/b4cfb307-268b-4c8b-ac7f-d01dff4ce60bFix#19438
1 parent6fb4cc6 commite7d648f

File tree

10 files changed

+250
-249
lines changed

10 files changed

+250
-249
lines changed

‎site/src/components/Button/Button.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { cn } from "utils/cn";
1212
constbuttonVariants=cva(
1313
`
1414
inline-flex items-center justify-center gap-1 whitespace-nowrap font-sans
15-
border-solid rounded-md transition-colors
15+
border-solid rounded-md transition-colors shrink-0
1616
text-sm font-medium cursor-pointer no-underline
1717
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-content-link
1818
disabled:pointer-events-none disabled:text-content-disabled

‎site/src/components/ScrollArea/ScrollArea.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const ScrollArea = React.forwardRef<
2424
));
2525
ScrollArea.displayName=ScrollAreaPrimitive.Root.displayName;
2626

27-
constScrollBar=React.forwardRef<
27+
exportconstScrollBar=React.forwardRef<
2828
React.ElementRef<typeofScrollAreaPrimitive.ScrollAreaScrollbar>,
2929
React.ComponentPropsWithoutRef<typeofScrollAreaPrimitive.ScrollAreaScrollbar>
3030
>(({ className, orientation="vertical", ...props},ref)=>(
@@ -41,6 +41,6 @@ const ScrollBar = React.forwardRef<
4141
)}
4242
{...props}
4343
>
44-
<ScrollAreaPrimitive.ScrollAreaThumbclassName="relative flex-1 rounded-full bg-border"/>
44+
<ScrollAreaPrimitive.ScrollAreaThumbclassName="relative flex-1 rounded-full bg-surface-quaternary"/>
4545
</ScrollAreaPrimitive.ScrollAreaScrollbar>
4646
));

‎site/src/modules/apps/useAppLink.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,17 @@ type UseAppLinkParams = {
2020
agent:WorkspaceAgent;
2121
};
2222

23+
typeAppLink={
24+
href:string;
25+
onClick:(e:React.MouseEvent)=>void;
26+
label:string;
27+
hasToken:boolean;
28+
};
29+
2330
exportconstuseAppLink=(
2431
app:WorkspaceApp,
2532
{ agent, workspace}:UseAppLinkParams,
26-
)=>{
33+
):AppLink=>{
2734
constlabel=app.display_name??app.slug;
2835
const{ proxy}=useProxy();
2936
const{data:apiKeyResponse}=useQuery({

‎site/src/modules/tasks/tasks.ts‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
1-
importtype{Workspace}from"api/typesGenerated";
1+
importtype{
2+
Workspace,
3+
WorkspaceAgent,
4+
WorkspaceApp,
5+
}from"api/typesGenerated";
26

37
exportconstAI_PROMPT_PARAMETER_NAME="AI Prompt";
48

59
exporttypeTask={
610
workspace:Workspace;
711
prompt:string;
812
};
13+
14+
exporttypeWorkspaceAppWithAgent=WorkspaceApp&{
15+
agent:WorkspaceAgent;
16+
};
17+
18+
exportfunctiongetTaskApps(task:Task):WorkspaceAppWithAgent[]{
19+
return(
20+
task.workspace.latest_build.resources
21+
.flatMap((r)=>r.agents??[])
22+
.flatMap((agent)=>
23+
agent.apps.map((app)=>({
24+
...app,
25+
agent,
26+
})),
27+
)
28+
// The Chat UI app will be displayed in the sidebar, so we don't want to
29+
// show it as a tab.
30+
.filter(
31+
(app)=>app.id!==task.workspace.latest_build.ai_task_sidebar_app_id,
32+
)
33+
);
34+
}

‎site/src/pages/TaskPage/TaskAppIframe.tsx‎

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
importtype{WorkspaceApp}from"api/typesGenerated";
21
import{Button}from"components/Button/Button";
32
import{
43
DropdownMenu,
@@ -7,55 +6,42 @@ import {
76
DropdownMenuTrigger,
87
}from"components/DropdownMenu/DropdownMenu";
98
import{Spinner}from"components/Spinner/Spinner";
9+
import{useProxy}from"contexts/ProxyContext";
1010
import{EllipsisVertical,ExternalLinkIcon,HouseIcon}from"lucide-react";
1111
import{useAppLink}from"modules/apps/useAppLink";
12-
importtype{Task}from"modules/tasks/tasks";
12+
importtype{Task,WorkspaceAppWithAgent}from"modules/tasks/tasks";
1313
import{typeFC,useRef}from"react";
1414
import{LinkasRouterLink}from"react-router";
1515
import{cn}from"utils/cn";
16+
import{TaskWildcardWarning}from"./TaskWildcardWarning";
1617

1718
typeTaskAppIFrameProps={
1819
task:Task;
19-
app:WorkspaceApp;
20+
app:WorkspaceAppWithAgent;
2021
active:boolean;
21-
pathname?:string;
2222
};
2323

2424
exportconstTaskAppIFrame:FC<TaskAppIFrameProps>=({
2525
task,
2626
app,
2727
active,
28-
pathname,
2928
})=>{
30-
constagent=task.workspace.latest_build.resources
31-
.flatMap((r)=>r.agents)
32-
.filter((a)=>!!a)
33-
.find((a)=>a.apps.some((a)=>a.id===app.id));
34-
35-
if(!agent){
36-
thrownewError(`Agent for app${app.id} not found in task workspace`);
37-
}
38-
3929
constlink=useAppLink(app,{
40-
agent,
30+
agent:app.agent,
4131
workspace:task.workspace,
4232
});
43-
44-
constappHref=():string=>{
45-
try{
46-
consturl=newURL(link.href,location.href);
47-
if(pathname){
48-
url.pathname=pathname;
49-
}
50-
returnurl.toString();
51-
}catch(err){
52-
console.warn(`Failed to parse URL${link.href} for app${app.id}`,err);
53-
returnlink.href;
54-
}
55-
};
56-
33+
constproxy=useProxy();
5734
constframeRef=useRef<HTMLIFrameElement>(null);
58-
constframeSrc=appHref();
35+
constshouldDisplayWildcardWarning=
36+
app.subdomain&&!proxy.proxy?.preferredWildcardHostname;
37+
38+
if(shouldDisplayWildcardWarning){
39+
return(
40+
<divclassName="h-full flex items-center justify-center pb-4">
41+
<TaskWildcardWarning/>
42+
</div>
43+
);
44+
}
5945

6046
return(
6147
<divclassName={cn([active ?"flex" :"hidden","w-full h-full flex-col"])}>
@@ -67,7 +53,7 @@ export const TaskAppIFrame: FC<TaskAppIFrameProps> = ({
6753
onClick={(e)=>{
6854
e.preventDefault();
6955
if(frameRef.current?.contentWindow){
70-
frameRef.current.contentWindow.location.href=appHref();
56+
frameRef.current.contentWindow.location.href=link.href;
7157
}
7258
}}
7359
>
@@ -88,7 +74,7 @@ export const TaskAppIFrame: FC<TaskAppIFrameProps> = ({
8874
</DropdownMenuTrigger>
8975
<DropdownMenuContentalign="end">
9076
<DropdownMenuItemasChild>
91-
<RouterLinkto={frameSrc}target="_blank">
77+
<RouterLinkto={link.href}target="_blank">
9278
<ExternalLinkIcon/>
9379
Open app in new tab
9480
</RouterLink>
@@ -103,7 +89,7 @@ export const TaskAppIFrame: FC<TaskAppIFrameProps> = ({
10389
app.health==="unhealthy" ?(
10490
<iframe
10591
ref={frameRef}
106-
src={frameSrc}
92+
src={link.href}
10793
title={link.label}
10894
loading="eager"
10995
className={"w-full h-full border-0"}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp