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

Commit417b053

Browse files
feat: implement terminal reconnection UI components (Phase 2)
- Create TerminalRetryConnection component with countdown display and retry button- Add comprehensive Storybook stories covering all retry states- Integrate component with TerminalAlerts for proper positioning- Use consistent TerminalAlert styling for seamless integration- Ensure proper resize handling through existing MutationObserverImplements Phase 2 of terminal reconnection feature as outlined in:coder/internal#659Co-authored-by: BrunoQuaresma <3165839+BrunoQuaresma@users.noreply.github.com>
1 parentdd7adda commit417b053

File tree

3 files changed

+231
-1
lines changed

3 files changed

+231
-1
lines changed

‎site/src/pages/TerminalPage/TerminalAlerts.tsx‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,29 @@ import { Button } from "components/Button/Button";
55
import{typeFC,useEffect,useRef,useState}from"react";
66
import{docs}from"utils/docs";
77
importtype{ConnectionStatus}from"./types";
8+
import{TerminalRetryConnection}from"./TerminalRetryConnection";
89

910
typeTerminalAlertsProps={
1011
agent:WorkspaceAgent|undefined;
1112
status:ConnectionStatus;
1213
onAlertChange:()=>void;
14+
// Retry connection props
15+
isRetrying?:boolean;
16+
timeUntilNextRetry?:number|null;
17+
attemptCount?:number;
18+
maxAttempts?:number;
19+
onRetryNow?:()=>void;
1320
};
1421

1522
exportconstTerminalAlerts=({
1623
agent,
1724
status,
1825
onAlertChange,
26+
isRetrying=false,
27+
timeUntilNextRetry=null,
28+
attemptCount=0,
29+
maxAttempts=10,
30+
onRetryNow,
1931
}:TerminalAlertsProps)=>{
2032
constlifecycleState=agent?.lifecycle_state;
2133
constprevLifecycleState=useRef(lifecycleState);
@@ -49,7 +61,13 @@ export const TerminalAlerts = ({
4961
return(
5062
<divref={wrapperRef}>
5163
{status==="disconnected" ?(
52-
<DisconnectedAlert/>
64+
<TerminalRetryConnection
65+
isRetrying={isRetrying}
66+
timeUntilNextRetry={timeUntilNextRetry}
67+
attemptCount={attemptCount}
68+
maxAttempts={maxAttempts}
69+
onRetryNow={onRetryNow||(()=>{})}
70+
/>
5371
) :lifecycleState==="start_error" ?(
5472
<ErrorScriptAlert/>
5573
) :lifecycleState==="starting" ?(
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
importtype{Meta,StoryObj}from"@storybook/react";
2+
import{TerminalRetryConnection}from"./TerminalRetryConnection";
3+
4+
constmeta:Meta<typeofTerminalRetryConnection>={
5+
title:"pages/TerminalPage/TerminalRetryConnection",
6+
component:TerminalRetryConnection,
7+
parameters:{
8+
layout:"padded",
9+
},
10+
args:{
11+
onRetryNow:()=>{
12+
console.log("Retry now clicked");
13+
},
14+
maxAttempts:10,
15+
},
16+
};
17+
18+
exportdefaultmeta;
19+
typeStory=StoryObj<typeofTerminalRetryConnection>;
20+
21+
// Hidden state - component returns null
22+
exportconstHidden:Story={
23+
args:{
24+
isRetrying:false,
25+
timeUntilNextRetry:null,
26+
attemptCount:0,
27+
},
28+
};
29+
30+
// Currently retrying state
31+
exportconstRetrying:Story={
32+
args:{
33+
isRetrying:true,
34+
timeUntilNextRetry:null,
35+
attemptCount:1,
36+
},
37+
};
38+
39+
// Countdown to next retry - first attempt (1 second)
40+
exportconstCountdownFirstAttempt:Story={
41+
args:{
42+
isRetrying:false,
43+
timeUntilNextRetry:1000,// 1 second
44+
attemptCount:1,
45+
},
46+
};
47+
48+
// Countdown to next retry - second attempt (2 seconds)
49+
exportconstCountdownSecondAttempt:Story={
50+
args:{
51+
isRetrying:false,
52+
timeUntilNextRetry:2000,// 2 seconds
53+
attemptCount:2,
54+
},
55+
};
56+
57+
// Countdown to next retry - longer delay (15 seconds)
58+
exportconstCountdownLongerDelay:Story={
59+
args:{
60+
isRetrying:false,
61+
timeUntilNextRetry:15000,// 15 seconds
62+
attemptCount:5,
63+
},
64+
};
65+
66+
// Countdown with 1 second remaining (singular)
67+
exportconstCountdownOneSecond:Story={
68+
args:{
69+
isRetrying:false,
70+
timeUntilNextRetry:1000,// 1 second
71+
attemptCount:3,
72+
},
73+
};
74+
75+
// Countdown with less than 1 second remaining
76+
exportconstCountdownLessThanOneSecond:Story={
77+
args:{
78+
isRetrying:false,
79+
timeUntilNextRetry:500,// 0.5 seconds (should show "1 second")
80+
attemptCount:3,
81+
},
82+
};
83+
84+
// Max attempts reached - no more automatic retries
85+
exportconstMaxAttemptsReached:Story={
86+
args:{
87+
isRetrying:false,
88+
timeUntilNextRetry:null,
89+
attemptCount:10,
90+
maxAttempts:10,
91+
},
92+
};
93+
94+
// Connection lost but no retry scheduled yet
95+
exportconstConnectionLostNoRetry:Story={
96+
args:{
97+
isRetrying:false,
98+
timeUntilNextRetry:null,
99+
attemptCount:1,
100+
maxAttempts:10,
101+
},
102+
};
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import{Alert,typeAlertProps}from"components/Alert/Alert";
2+
import{Button}from"components/Button/Button";
3+
import{Spinner}from"components/Spinner/Spinner";
4+
importtype{FC}from"react";
5+
6+
interfaceTerminalRetryConnectionProps{
7+
/**
8+
* Whether a retry is currently in progress
9+
*/
10+
isRetrying:boolean;
11+
/**
12+
* Time in milliseconds until the next automatic retry (null if not scheduled)
13+
*/
14+
timeUntilNextRetry:number|null;
15+
/**
16+
* Number of retry attempts made
17+
*/
18+
attemptCount:number;
19+
/**
20+
* Maximum number of retry attempts
21+
*/
22+
maxAttempts:number;
23+
/**
24+
* Callback to manually trigger a retry
25+
*/
26+
onRetryNow:()=>void;
27+
}
28+
29+
/**
30+
* Formats milliseconds into a human-readable countdown
31+
*/
32+
functionformatCountdown(ms:number):string{
33+
constseconds=Math.ceil(ms/1000);
34+
return`${seconds} second${seconds!==1 ?"s" :""}`;
35+
}
36+
37+
/**
38+
* Terminal-specific alert component with consistent styling
39+
*/
40+
constTerminalAlert:FC<AlertProps>=(props)=>{
41+
return(
42+
<Alert
43+
{...props}
44+
css={(theme)=>({
45+
borderRadius:0,
46+
borderWidth:0,
47+
borderBottomWidth:1,
48+
borderBottomColor:theme.palette.divider,
49+
backgroundColor:theme.palette.background.paper,
50+
borderLeft:`3px solid${theme.palette[props.severity!].light}`,
51+
marginBottom:1,
52+
})}
53+
/>
54+
);
55+
};
56+
57+
exportconstTerminalRetryConnection:FC<TerminalRetryConnectionProps>=({
58+
isRetrying,
59+
timeUntilNextRetry,
60+
attemptCount,
61+
maxAttempts,
62+
onRetryNow,
63+
})=>{
64+
// Don't show anything if we're not in a retry state
65+
if(!isRetrying&&timeUntilNextRetry===null){
66+
returnnull;
67+
}
68+
69+
// Show different messages based on state
70+
letmessage:string;
71+
letshowRetryButton=true;
72+
73+
if(isRetrying){
74+
message="Reconnecting to terminal...";
75+
showRetryButton=false;// Don't show button while actively retrying
76+
}elseif(timeUntilNextRetry!==null){
77+
constcountdown=formatCountdown(timeUntilNextRetry);
78+
message=`Connection lost. Retrying in${countdown}...`;
79+
}elseif(attemptCount>=maxAttempts){
80+
message="Connection failed after multiple attempts.";
81+
}else{
82+
message="Connection lost.";
83+
}
84+
85+
return(
86+
<TerminalAlert
87+
severity="warning"
88+
actions={
89+
showRetryButton ?(
90+
<Button
91+
variant="outline"
92+
size="sm"
93+
onClick={onRetryNow}
94+
disabled={isRetrying}
95+
css={{
96+
display:"flex",
97+
alignItems:"center",
98+
gap:"0.5rem",
99+
}}
100+
>
101+
{isRetrying&&<Spinnersize="sm"/>}
102+
Retry now
103+
</Button>
104+
) :null
105+
}
106+
>
107+
{message}
108+
</TerminalAlert>
109+
);
110+
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp