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

Commit060033e

Browse files
fix(site): fix terminal size when displaying alerts (#12444)
Before - The terminal size does not fit the available space so the bottom is hidden.https://github.com/coder/coder/assets/3165839/d08470b9-9fc6-476c-a551-8a3e13fc25bfAfter - The terminal adjusts when there are alert changes.https://github.com/coder/coder/assets/3165839/8cc32bfb-056f-47cb-97f2-3bb18c5fe906Unfortunately, I don't think there is a sane way to automate tests for this but open to suggestions.Close#7914
1 parentd2a5b31 commit060033e

File tree

4 files changed

+104
-15
lines changed

4 files changed

+104
-15
lines changed

‎site/.storybook/preview.jsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ export const parameters = {
6969
},
7070
type:"tablet",
7171
},
72+
terminal:{
73+
name:"Terminal",
74+
styles:{
75+
height:"400",
76+
width:"400",
77+
},
78+
},
7279
},
7380
},
7481
};

‎site/src/@types/storybook.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import type { QueryKey } from "react-query";
33
importtype{Experiments,FeatureName}from"api/typesGenerated";
44

55
declare module"@storybook/react"{
6-
typeWebSocketEvent={event:"message";data:string}|{event:"error"};
6+
typeWebSocketEvent=
7+
|{event:"message";data:string}
8+
|{event:"error"|"close"};
79
interfaceParameters{
810
features?:FeatureName[];
911
experiments?:Experiments;

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,27 @@ export const ConnectionError: Story = {
142142
queries:[...meta.parameters.queries,createWorkspaceWithAgent("ready")],
143143
},
144144
};
145+
146+
// Check if the terminal is not getting hide when the bottom message is shown
147+
// together with the error message
148+
exportconstBottomMessage:Story={
149+
decorators:[withWebSocket],
150+
parameters:{
151+
...meta.parameters,
152+
// Forcing smaller viewport to make it easier to identify the issue
153+
viewport:{
154+
defaultViewport:"terminal",
155+
},
156+
webSocket:[
157+
{
158+
event:"message",
159+
// This outputs text in the bottom left and right corners of the terminal.
160+
data:"\x1b[1000BLEFT\x1b[1000C\x1b[4DRIGHT",
161+
},
162+
{
163+
event:"close",
164+
},
165+
],
166+
queries:[...meta.parameters.queries,createWorkspaceWithAgent("ready")],
167+
},
168+
};

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

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { WebLinksAddon } from "xterm-addon-web-links";
1313
import{WebglAddon}from"xterm-addon-webgl";
1414
import{deploymentConfig}from"api/queries/deployment";
1515
import{workspaceByOwnerAndName}from"api/queries/workspaces";
16+
importtype{WorkspaceAgent}from"api/typesGenerated";
1617
import{useProxy}from"contexts/ProxyContext";
1718
import{ThemeOverride}from"contexts/ThemeProvider";
1819
importthemesfrom"theme";
@@ -34,6 +35,8 @@ export const Language = {
3435
websocketErrorMessagePrefix:"WebSocket failed: ",
3536
};
3637

38+
typeTerminalState="connected"|"disconnected"|"initializing";
39+
3740
constTerminalPage:FC=()=>{
3841
// Maybe one day we'll support a light themed terminal, but terminal coloring
3942
// is notably a pain because of assumptions certain programs might make about your
@@ -45,9 +48,8 @@ const TerminalPage: FC = () => {
4548
constusername=params.username.replace("@","");
4649
constxtermRef=useRef<HTMLDivElement>(null);
4750
const[terminal,setTerminal]=useState<XTerm.Terminal|null>(null);
48-
const[terminalState,setTerminalState]=useState<
49-
"connected"|"disconnected"|"initializing"
50-
>("initializing");
51+
const[terminalState,setTerminalState]=
52+
useState<TerminalState>("initializing");
5153
const[searchParams]=useSearchParams();
5254
constisDebugging=searchParams.has("debug");
5355
// The reconnection token is a unique token that identifies
@@ -67,12 +69,6 @@ const TerminalPage: FC = () => {
6769
constselectedProxy=proxy.proxy;
6870
constlatency=selectedProxy ?proxyLatencies[selectedProxy.id] :undefined;
6971

70-
constlifecycleState=workspaceAgent?.lifecycle_state;
71-
constprevLifecycleState=useRef(lifecycleState);
72-
useEffect(()=>{
73-
prevLifecycleState.current=lifecycleState;
74-
},[lifecycleState]);
75-
7672
constconfig=useQuery(deploymentConfig());
7773
constrenderer=config.data?.config.web_terminal_renderer;
7874

@@ -95,6 +91,7 @@ const TerminalPage: FC = () => {
9591
},[handleWebLink]);
9692

9793
// Create the terminal!
94+
constfitAddonRef=useRef<FitAddon>();
9895
useEffect(()=>{
9996
if(!xtermRef.current||config.isLoading){
10097
return;
@@ -115,6 +112,7 @@ const TerminalPage: FC = () => {
115112
terminal.loadAddon(newCanvasAddon());
116113
}
117114
constfitAddon=newFitAddon();
115+
fitAddonRef.current=fitAddon;
118116
terminal.loadAddon(fitAddon);
119117
terminal.loadAddon(newUnicode11Addon());
120118
terminal.unicode.activeVersion="11";
@@ -303,11 +301,13 @@ const TerminalPage: FC = () => {
303301
</title>
304302
</Helmet>
305303
<divcss={{display:"flex",flexDirection:"column",height:"100vh"}}>
306-
{lifecycleState==="start_error"&&<ErrorScriptAlert/>}
307-
{lifecycleState==="starting"&&<LoadingScriptsAlert/>}
308-
{lifecycleState==="ready"&&
309-
prevLifecycleState.current==="starting"&&<LoadedScriptsAlert/>}
310-
{terminalState==="disconnected"&&<DisconnectedAlert/>}
304+
<TerminalAlerts
305+
agent={workspaceAgent}
306+
state={terminalState}
307+
onAlertChange={()=>{
308+
fitAddonRef.current?.fit();
309+
}}
310+
/>
311311
<divcss={styles.terminal}ref={xtermRef}data-testid="terminal"/>
312312
</div>
313313

@@ -328,6 +328,62 @@ const TerminalPage: FC = () => {
328328
);
329329
};
330330

331+
typeTerminalAlertsProps={
332+
agent:WorkspaceAgent|undefined;
333+
state:TerminalState;
334+
onAlertChange:()=>void;
335+
};
336+
337+
constTerminalAlerts=({
338+
agent,
339+
state,
340+
onAlertChange,
341+
}:TerminalAlertsProps)=>{
342+
constlifecycleState=agent?.lifecycle_state;
343+
constprevLifecycleState=useRef(lifecycleState);
344+
useEffect(()=>{
345+
prevLifecycleState.current=lifecycleState;
346+
},[lifecycleState]);
347+
348+
// We want to observe the children of the wrapper to detect when the alert
349+
// changes. So the terminal page can resize itself.
350+
//
351+
// Would it be possible to just always call fit() when this component
352+
// re-renders instead of using an observer?
353+
//
354+
// This is a good question and the why this does not work is that the .fit()
355+
// needs to run after the render so in this case, I just think the mutation
356+
// observer is more reliable. I could use some hacky setTimeout inside of
357+
// useEffect to do that, I guess, but I don't think it would be any better.
358+
constwrapperRef=useRef<HTMLDivElement>(null);
359+
useEffect(()=>{
360+
if(!wrapperRef.current){
361+
return;
362+
}
363+
constobserver=newMutationObserver(onAlertChange);
364+
observer.observe(wrapperRef.current,{childList:true});
365+
366+
return()=>{
367+
observer.disconnect();
368+
};
369+
},[onAlertChange]);
370+
371+
return(
372+
<divref={wrapperRef}>
373+
{state==="disconnected" ?(
374+
<DisconnectedAlert/>
375+
) :lifecycleState==="start_error" ?(
376+
<ErrorScriptAlert/>
377+
) :lifecycleState==="starting" ?(
378+
<LoadingScriptsAlert/>
379+
) :lifecycleState==="ready"&&
380+
prevLifecycleState.current==="starting" ?(
381+
<LoadedScriptsAlert/>
382+
) :null}
383+
</div>
384+
);
385+
};
386+
331387
conststyles={
332388
terminal:(theme)=>({
333389
width:"100%",

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp