修复由于webterminal轮询终止的误判,导致需要刷新的问题 (#2)
Some checks failed
backend / cross (arm) (push) Has been cancelled
backend / cross (armhf) (push) Has been cancelled
backend / cross (i686) (push) Has been cancelled
backend / cross (mips) (push) Has been cancelled
backend / cross (mips64) (push) Has been cancelled
backend / cross (mips64el) (push) Has been cancelled
backend / cross (mipsel) (push) Has been cancelled
backend / cross (s390x) (push) Has been cancelled
backend / cross (win32) (push) Has been cancelled
backend / cross (x86_64) (push) Has been cancelled
backend / cross (aarch64) (push) Has been cancelled
docker / build (push) Has been cancelled

webtermnial会在进入容器后看到Successfully connected to the devcontainer后停止轮询
由于第二条docker命令中包含Successfully connected to the devcontainer,还没真正 attach 前,导致这段字符串提前出现,触发了误判
已修改为完整地、仅包含的情况才终止轮询

Co-authored-by: 孟宁 <mengning@mengning.com.cn>
Reviewed-on: #2
Co-authored-by: hwy <1093970372@qq.com>
Co-committed-by: hwy <1093970372@qq.com>
This commit is contained in:
2025-12-08 10:13:30 +00:00
committed by 孟宁
parent b588f113e3
commit 97313a2dc0
3 changed files with 16255 additions and 16211 deletions

1
.gitignore vendored
View File

@@ -10,6 +10,7 @@
# Precompiled Headers
*.gch
*.pch
src/html.h
# Libraries
*.lib

View File

@@ -104,11 +104,14 @@ export class Xterm {
private intervalID: NodeJS.Timeout;
private writeFunc = (data: ArrayBuffer) => this.writeData(new Uint8Array(data));
private connectStatus = false;
private connectionMessageBuffer = "";
private hostTitle = "";
private postAttachCommand = [];
private postAttachCommandStatus = false;
private workdir = "";
private containerStatus = "";
private attachCommandSent = false;
private attachCommandSentAt?: number;
private beforeCommand?: string;
constructor(
private options: XtermOptions,
@@ -367,13 +370,18 @@ export class Xterm {
}
this.containerStatus = data.status;
} else {
if(this.containerStatus !== '4'){
this.writeData("\x1b[31mCreation completed. Please refresh the page to connect to the devcontainer\x1b[0m");
if (this.containerStatus !== '4'){
this.writeData("\x1b[31mCreation completed.\x1b[0m\r\n");
}
this.containerStatus = data.status;
if (data.status === '4') {
const parts = data.command.split('\n');
this.sendData(parts[0]+"\n");
const shouldResend = this.attachCommandSent && this.attachCommandSentAt !== undefined && Date.now() - this.attachCommandSentAt > 5000;
if ((!this.attachCommandSent || shouldResend) && !this.connectStatus && parts[0]) {
this.sendData(parts[0]+"\n");
this.attachCommandSent = true;
this.attachCommandSentAt = Date.now();
}
this.postAttachCommand = parts;
}
}
@@ -428,7 +436,9 @@ export class Xterm {
const data = rawData.slice(1);
switch (cmd) {
case Command.OUTPUT:
console.log('[ttyd] output:', textDecoder.decode(data));
const decodedData = textDecoder.decode(data);
console.log('[ttyd] output:', decodedData);
const compactOutput = decodedData.replace(/\s/g, '');
const options = new URLSearchParams(decodeURIComponent(window.location.search));
const params = new URLSearchParams({
repo: options.get('repoid') as string,
@@ -436,20 +446,33 @@ export class Xterm {
});
if (options.get('type') === 'docker') {
// 保存host的标题
if (this.hostTitle === ""){
this.hostTitle = textDecoder.decode(data).replace(/\s/g, '');
if (this.hostTitle === ''){
this.hostTitle = compactOutput;
}
// 检测是否退出devcontainer标题等于host的标题
if (this.connectStatus && textDecoder.decode(data).replace(/\s/g, '').includes(this.hostTitle)){
this.connectStatus = false
if (this.connectStatus && compactOutput.includes(this.hostTitle)){
this.connectStatus = false;
this.connectionMessageBuffer = '';
this.attachCommandSent = false;
this.attachCommandSentAt = undefined;
this.postAttachCommandStatus = false;
}
// this.connectStatus = true 连接完成
if (
this.connectStatus === false &&
textDecoder.decode(data).replace(/\s/g, '').includes('Successfully connected to the devcontainer'.replace(/\s/g, ''))
) {
this.connectStatus = true;
clearInterval(this.intervalID);
//由于第二条docker命令中包含Successfully connected to the devcontainer,需要过滤否则会导致轮询终止卡在状态2
if (!this.connectStatus) {
const sanitizedOutput = this.stripAnsi(decodedData).replace(/\r/g, '\n');
const combinedOutput = this.connectionMessageBuffer + sanitizedOutput;
const segments = combinedOutput.split(/\n/);
this.connectionMessageBuffer = segments.pop() ?? '';
const hasSuccessLine = segments.some(line => line.trim() === 'Successfully connected to the devcontainer');
if (hasSuccessLine) {
this.connectStatus = true;
this.connectionMessageBuffer = '';
this.attachCommandSentAt = undefined;
if (this.intervalID) {
clearInterval(this.intervalID);
}
}
}
// 连接完成之前不输出标题和docker命令
if (
@@ -460,9 +483,9 @@ export class Xterm {
this.writeFunc(data);
}
// 连接完成且出现容器的标题且没有执行过postAttach命令
if (this.connectStatus && textDecoder.decode(data).replace(/\s/g, '').includes(this.workdir) && !this.postAttachCommandStatus){
if (this.connectStatus && compactOutput.includes(this.workdir) && !this.postAttachCommandStatus){
for (let i = 1; i < this.postAttachCommand.length; i++){
this.sendData(this.postAttachCommand[i]+"\n");
this.sendData(this.postAttachCommand[i]+'\n');
}
this.postAttachCommandStatus = true;
}
@@ -655,4 +678,8 @@ export class Xterm {
break;
}
}
private stripAnsi(input: string): string {
return input.replace(/\u001B\[[0-9;?]*[ -\/]*[@-~]/g, '').replace(/\u0007/g, '');
}
}

32406
src/html.h generated

File diff suppressed because it is too large Load Diff