show differences
Some checks failed
backend / cross (aarch64) (push) Waiting to run
backend / cross (arm) (push) Waiting to run
backend / cross (armhf) (push) Waiting to run
backend / cross (i686) (push) Waiting to run
backend / cross (mips) (push) Waiting to run
backend / cross (mips64) (push) Waiting to run
backend / cross (mips64el) (push) Waiting to run
backend / cross (mipsel) (push) Waiting to run
backend / cross (s390x) (push) Waiting to run
backend / cross (win32) (push) Waiting to run
backend / cross (x86_64) (push) Waiting to run
frontend / build (push) Waiting to run
docker / build (push) Has been cancelled

This commit is contained in:
2025-10-18 16:35:39 +08:00
parent 6dbe2f6e20
commit fd338d2dad
6 changed files with 24752 additions and 27571 deletions

View File

@@ -1 +1,2 @@
dist/ dist/
src/

View File

@@ -6,9 +6,10 @@ import type { ITerminalOptions, ITheme } from '@xterm/xterm';
import type { ClientOptions, FlowControl } from './terminal/xterm'; import type { ClientOptions, FlowControl } from './terminal/xterm';
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const path = window.location.pathname.replace(/[/]+$/, ''); const path = window.location.pathname.replace(/[/]+$/, '');
const wsUrl = [protocol, '//', window.location.host, path, '/ws', window.location.search].join(''); const wsUrl = [protocol, '//', window.location.host, path,'/ws'].join('');
const tokenUrl = [window.location.protocol, '//', window.location.host, path, '/token'].join(''); const tokenUrl = [window.location.protocol, '//', window.location.host, path,'/token'].join('');
const clientOptions = { const clientOptions = {
rendererType: 'webgl', rendererType: 'webgl',
disableLeaveAlert: false, disableLeaveAlert: false,
@@ -23,6 +24,7 @@ const clientOptions = {
const termOptions = { const termOptions = {
fontSize: 13, fontSize: 13,
fontFamily: 'Consolas,Liberation Mono,Menlo,Courier,monospace', fontFamily: 'Consolas,Liberation Mono,Menlo,Courier,monospace',
cursorBlink: true,
theme: { theme: {
foreground: '#d2d2d2', foreground: '#d2d2d2',
background: '#2b2b2b', background: '#2b2b2b',

View File

@@ -16,7 +16,22 @@ interface State {
export class Terminal extends Component<Props, State> { export class Terminal extends Component<Props, State> {
private container: HTMLElement; private container: HTMLElement;
private xterm: Xterm; private xterm: Xterm;
private intervalID: NodeJS.Timeout;
private currentDevcontainer = {
title: 'Devcontainer Info',
detail: 'No Devcontainer Created yet',
port: '',
ip:'',
steps: [
// {
// summary: '',
// duration: '',
// status: '',
// logs:{
// },
// }
],
};
constructor(props: Props) { constructor(props: Props) {
super(); super();
this.xterm = new Xterm(props, this.showModal); this.xterm = new Xterm(props, this.showModal);
@@ -24,8 +39,42 @@ export class Terminal extends Component<Props, State> {
async componentDidMount() { async componentDidMount() {
await this.xterm.refreshToken(); await this.xterm.refreshToken();
const options = new URLSearchParams(decodeURIComponent(window.location.search));
const params = new URLSearchParams({
repo: options.get('repoid') as string,
user: options.get('userid') as string,
});
fetch('http://' +
options.get('domain') +
':'+
options.get('port') +
'/' +
options.get('user') +
'/' +
options.get('repo') +
'/devcontainer/status?' +
params
)
.then(response => response.json())
.then(data => {
if (data.status !== '-1') {
if (options.get('type') === 'docker') {
this.xterm.open(this.container); this.xterm.open(this.container);
this.xterm.connect(); this.xterm.connect();
} else {
this.intervalID = setInterval(this.loadOutput, 3000);
this.xterm.open(this.container);
this.xterm.changeUrl(this.currentDevcontainer.ip, this.currentDevcontainer.port)
this.xterm.changeStatus(true);
this.xterm.connect();
}
}
})
.catch(error => {
console.error('Error:', error);
});
} }
componentWillUnmount() { componentWillUnmount() {
@@ -56,4 +105,48 @@ export class Terminal extends Component<Props, State> {
const files = (event.target as HTMLInputElement).files; const files = (event.target as HTMLInputElement).files;
if (files) this.xterm.sendFile(files); if (files) this.xterm.sendFile(files);
} }
@bind
private loadOutput() {
const options = new URLSearchParams(decodeURIComponent(window.location.search));
const params = new URLSearchParams({
repo: options.get('repoid') as string,
user: options.get('userid') as string,
});
fetch(
'http://' + options.get('domain') + ':'+ options.get('port') +'/' +
options.get('user') +
'/' +
options.get('repo') +
'/devcontainer/output?' +
params
)
.then(response => response.json())
.then(job => {
if (!job) {
clearInterval(this.intervalID);
this.intervalID = null as any;
return;
}
if(this.currentDevcontainer.steps.length < job.currentDevcontainer.steps.length){
for(let i = this.currentDevcontainer.steps.length; i < job.currentDevcontainer.steps.length; i++) {
this.xterm.writeData(job.currentDevcontainer.steps[i].summary);
this.xterm.writeData('\r\n');
for(let j = 0; j < job.currentDevcontainer.steps[i].logs.length; j++) {
this.xterm.writeData(job.currentDevcontainer.steps[i].logs[j].message.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n'));
this.xterm.writeData('\r\n');
}
}
}
this.currentDevcontainer = job.currentDevcontainer;
if (this.currentDevcontainer.detail === '4' && this.intervalID) {
clearInterval(this.intervalID);
this.intervalID = null as any;
}
})
.catch(error => {
console.error('Error:', error);
});
}
} }

View File

@@ -101,9 +101,13 @@ export class Xterm {
private reconnect = true; private reconnect = true;
private doReconnect = true; private doReconnect = true;
private closeOnDisconnect = false; private closeOnDisconnect = false;
private intervalID: NodeJS.Timeout;
private writeFunc = (data: ArrayBuffer) => this.writeData(new Uint8Array(data)); private writeFunc = (data: ArrayBuffer) => this.writeData(new Uint8Array(data));
private status = false;
private titleStatus = false;
private checkStatus = false;
private connectStatus = false;
private beforeCommand?: string;
constructor( constructor(
private options: XtermOptions, private options: XtermOptions,
private sendCb: () => void private sendCb: () => void
@@ -179,7 +183,16 @@ export class Xterm {
} }
}) })
); );
register(terminal.onData(data => sendData(data))); register(
terminal.onData(data =>
{
if (this.status) {
sendData(data);
} else {
this.writeData('\b \b');
}
})
);
register(terminal.onBinary(data => sendData(Uint8Array.from(data, v => v.charCodeAt(0))))); register(terminal.onBinary(data => sendData(Uint8Array.from(data, v => v.charCodeAt(0)))));
register( register(
terminal.onResize(({ cols, rows }) => { terminal.onResize(({ cols, rows }) => {
@@ -244,6 +257,18 @@ export class Xterm {
} }
} }
@bind
public changeUrl(ip: string, port: string) {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
this.options.wsUrl = [protocol, '//' + ip + ':' + port +'/ws', window.location.search].join('');
this.options.tokenUrl = [window.location.protocol, '//' + ip + ':' + port +'/token'].join('');
}
@bind
public changeStatus(v: boolean){
this.status = v;
}
@bind @bind
public connect() { public connect() {
this.socket = new WebSocket(this.options.wsUrl, ['tty']); this.socket = new WebSocket(this.options.wsUrl, ['tty']);
@@ -254,6 +279,10 @@ export class Xterm {
register(addEventListener(socket, 'message', this.onSocketData as EventListener)); register(addEventListener(socket, 'message', this.onSocketData as EventListener));
register(addEventListener(socket, 'close', this.onSocketClose as EventListener)); register(addEventListener(socket, 'close', this.onSocketClose as EventListener));
register(addEventListener(socket, 'error', () => (this.doReconnect = false))); register(addEventListener(socket, 'error', () => (this.doReconnect = false)));
const options = new URLSearchParams(decodeURIComponent(window.location.search));
if (options.get('type') === 'docker') {
this.intervalID = setInterval(this.loadCommand, 3000);
}
} }
@bind @bind
@@ -305,6 +334,52 @@ export class Xterm {
} }
} }
@bind
private loadCommand() {
const options = new URLSearchParams(decodeURIComponent(window.location.search));
const params = new URLSearchParams({
repo: options.get('repoid') as string,
user: options.get('userid') as string,
});
fetch(
'http://' + options.get('domain') + ':'+ options.get('port') +'/' +
options.get('user') +
'/' +
options.get('repo') +
'/devcontainer/command?' +
params
)
.then(response => response.json())
.then(data => {
if (data.status !== '4' && data.status !== '0') {
this.sendData(data.command);
} else {
clearInterval(this.intervalID);
if (data.status === '4') {
fetch(
'http://' + options.get('domain') + ':'+ options.get('port') +'/' +
options.get('user') +
'/' +
options.get('repo') +
'/devcontainer/command?' +
params
)
.then(response => response.json())
.then(data => {
this.sendData(data.command);
})
.catch(error => {
console.error('Error:', error);
});
}
}
})
.catch(error => {
console.error('Error:', error);
});
}
@bind @bind
private parseOptsFromUrlQuery(query: string): Preferences { private parseOptsFromUrlQuery(query: string): Preferences {
const { terminal } = this; const { terminal } = this;
@@ -345,16 +420,60 @@ export class Xterm {
const rawData = event.data as ArrayBuffer; const rawData = event.data as ArrayBuffer;
const cmd = String.fromCharCode(new Uint8Array(rawData)[0]); const cmd = String.fromCharCode(new Uint8Array(rawData)[0]);
const data = rawData.slice(1); const data = rawData.slice(1);
switch (cmd) { switch (cmd) {
case Command.OUTPUT: case Command.OUTPUT:
console.log(this.status + ': ' + textDecoder.decode(data) + this.connectStatus + ' ' + this.checkStatus + ' ' + this.titleStatus );
const options = new URLSearchParams(decodeURIComponent(window.location.search));
if (options.get('type') === 'docker') {
if (
this.status === false &&
textDecoder.decode(data).replace(/\s/g, '').includes('Successfully connected to the container'.replace(/\s/g, ''))
) {
if(this.connectStatus == true){
this.status = true;
}
this.connectStatus = true;
}
if (this.checkStatus) {
if (textDecoder.decode(data).replace(/\s/g, '').includes('You have out the container. Please refresh the terminal to reconnect.'.replace(/\s/g, ''))) {
this.checkStatus = false;
this.status = false;
this.connectStatus = false;
}
if(textDecoder.decode(data).includes('\x1b')){
this.checkStatus = false;
}
}
if (this.titleStatus && textDecoder.decode(data).includes('\x1b')) {
this.titleStatus = false;
this.checkStatus = true;
this.sendData(
`echo $WEB_TERMINAL\n`
);
}
if (
!(this.status === false && (textDecoder.decode(data).includes('\x1b') || textDecoder.decode(data).replace(/\s/g, '').includes('docker'))) &&
this.titleStatus !== true &&
this.checkStatus !== true
){
this.writeFunc(data); this.writeFunc(data);
}
if (textDecoder.decode(data).replace(/\s/g, '').includes('exit')) {
this.titleStatus = true;
}
} else {
this.writeFunc(data);
}
console.log(this.status + ': ' + textDecoder.decode(data) + this.connectStatus + ' ' + this.checkStatus + ' ' + this.titleStatus );
break; break;
case Command.SET_WINDOW_TITLE: case Command.SET_WINDOW_TITLE:
console.log('SET_WINDOW_TITLESET_WINDOW_TITLE');
this.title = textDecoder.decode(data); this.title = textDecoder.decode(data);
document.title = this.title; document.title = this.title;
break; break;
case Command.SET_PREFERENCES: case Command.SET_PREFERENCES:
console.log('SET_PREFERENCESSET_PREFERENCESSET_PREFERENCES');
this.applyPreferences({ this.applyPreferences({
...this.options.clientOptions, ...this.options.clientOptions,
...JSON.parse(textDecoder.decode(data)), ...JSON.parse(textDecoder.decode(data)),

File diff suppressed because it is too large Load Diff

32640
src/html.h generated

File diff suppressed because it is too large Load Diff