mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
checkpoint, session, window, termmap
This commit is contained in:
parent
a9f96dd983
commit
71c67a4cc2
61
src/main.tsx
61
src/main.tsx
@ -7,9 +7,11 @@ import * as dayjs from 'dayjs'
|
|||||||
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
|
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
|
||||||
import cn from "classnames"
|
import cn from "classnames"
|
||||||
import {GlobalWS} from "./ws";
|
import {GlobalWS} from "./ws";
|
||||||
import {TermWrap} from "./term";
|
import {TermWrap, getOrCreateTermWrap} from "./term";
|
||||||
|
|
||||||
type LineType = {
|
type LineType = {
|
||||||
|
sessionid : string,
|
||||||
|
windowid : string,
|
||||||
lineid : number,
|
lineid : number,
|
||||||
ts : number,
|
ts : number,
|
||||||
userid : string,
|
userid : string,
|
||||||
@ -20,9 +22,25 @@ type LineType = {
|
|||||||
isnew : boolean,
|
isnew : boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SessionType = {
|
||||||
|
sessionid : string;
|
||||||
|
name : string;
|
||||||
|
windows : WindowType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type WindowType = {
|
||||||
|
windowid : string;
|
||||||
|
name : string;
|
||||||
|
lines : LineType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
var GlobalUser = "sawka";
|
||||||
|
var GSessionId = "47445c53-cfcf-4943-8339-2c04447f20a1";
|
||||||
|
var GWindowId = "1";
|
||||||
|
|
||||||
var GlobalLines = mobx.observable.box([
|
var GlobalLines = mobx.observable.box([
|
||||||
{lineid: 1, userid: "sawka", ts: 1654631122000, linetype: "text", text: "hello"},
|
{sessionid: GSessionId, windowid: GWindowId, lineid: 1, userid: "sawka", ts: 1654631122000, linetype: "text", text: "hello"},
|
||||||
{lineid: 2, userid: "sawka", ts: 1654631125000, linetype: "text", text: "again"},
|
{sessionid: GSessionId, windowid: GWindowId, lineid: 2, userid: "sawka", ts: 1654631125000, linetype: "text", text: "again"},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function fetchJsonData(resp : any, ctErr : boolean) : Promise<any> {
|
function fetchJsonData(resp : any, ctErr : boolean) : Promise<any> {
|
||||||
@ -101,12 +119,12 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
let {line, sessionid} = this.props;
|
let {line} = this.props;
|
||||||
this.termWrap = new TermWrap(sessionid, line.cmdid);
|
this.termWrap = new TermWrap(line.sessionid, line.cmdid, line.windowid, line.lineid);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let {line, sessionid} = this.props;
|
let {line} = this.props;
|
||||||
let termElem = document.getElementById(this.getId());
|
let termElem = document.getElementById(this.getId());
|
||||||
this.termWrap.connectToElem(termElem);
|
this.termWrap.connectToElem(termElem);
|
||||||
this.termWrap.reloadTerminal(0);
|
this.termWrap.reloadTerminal(0);
|
||||||
@ -168,8 +186,8 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
|
|||||||
<div className="meta">
|
<div className="meta">
|
||||||
<div className="user">{line.userid}</div>
|
<div className="user">{line.userid}</div>
|
||||||
<div className="ts">{dayjs(line.ts).format("hh:mm:ss a")}</div>
|
<div className="ts">{dayjs(line.ts).format("hh:mm:ss a")}</div>
|
||||||
<div className="cmdid">{line.cmdid} <If condition={termSize.rows > 0}>({termSize.rows}x{termSize.cols})</If> v{renderVersion}</div>
|
<div className="metapart-mono">{line.cmdid} <If condition={termSize.rows > 0}>({termSize.rows}x{termSize.cols})</If> {this.termWrap.ptyPos} bytes, v{renderVersion}</div>
|
||||||
<div className="cmdtext">> {this.singleLineCmdText(line.cmdtext)}</div>
|
<div className="metapart-mono cmdtext">> {this.singleLineCmdText(line.cmdtext)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={cn("terminal-wrapper", {"focus": this.termWrap.isFocused.get()})}>
|
<div className={cn("terminal-wrapper", {"focus": this.termWrap.isFocused.get()})}>
|
||||||
<div className="terminal" id={this.getId()}></div>
|
<div className="terminal" id={this.getId()}></div>
|
||||||
@ -198,7 +216,7 @@ class Line extends React.Component<{line : LineType}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class CmdInput extends React.Component<{line : LineType, sessionid : string}, {}> {
|
class CmdInput extends React.Component<{sessionid : string, windowid : string}, {}> {
|
||||||
curLine : mobx.IObservableValue<string> = mobx.observable("", {name: "command-line"});
|
curLine : mobx.IObservableValue<string> = mobx.observable("", {name: "command-line"});
|
||||||
|
|
||||||
@mobx.action @boundMethod
|
@mobx.action @boundMethod
|
||||||
@ -223,12 +241,13 @@ class CmdInput extends React.Component<{line : LineType, sessionid : string}, {}
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
doSubmitCmd() {
|
doSubmitCmd() {
|
||||||
|
let {sessionid, windowid} = this.props;
|
||||||
let commandStr = this.curLine.get();
|
let commandStr = this.curLine.get();
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
this.curLine.set("");
|
this.curLine.set("");
|
||||||
})();
|
})();
|
||||||
let url = sprintf("http://localhost:8080/api/run-command");
|
let url = sprintf("http://localhost:8080/api/run-command");
|
||||||
let data = {sessionid: this.props.sessionid, command: commandStr};
|
let data = {sessionid: sessionid, windowid: windowid, command: commandStr, userid: GlobalUser};
|
||||||
fetch(url, {method: "post", body: JSON.stringify(data)}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => {
|
fetch(url, {method: "post", body: JSON.stringify(data)}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => {
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
let lines = GlobalLines.get();
|
let lines = GlobalLines.get();
|
||||||
@ -269,36 +288,46 @@ class CmdInput extends React.Component<{line : LineType, sessionid : string}, {}
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class SessionView extends React.Component<{sessionid : string}, {}> {
|
class SessionView extends React.Component<{session : SessionType}, {}> {
|
||||||
render() {
|
render() {
|
||||||
let lines = GlobalLines.get();
|
let session = this.props.session;
|
||||||
|
let window = session.windows[0];
|
||||||
|
let lines = window.lines;
|
||||||
return (
|
return (
|
||||||
<div className="session-view">
|
<div className="session-view">
|
||||||
<div className="lines">
|
<div className="lines">
|
||||||
<For each="line" of={lines}>
|
<For each="line" of={lines}>
|
||||||
<Line key={line.lineid} line={line} sessionid={this.props.sessionid}/>
|
<Line key={line.lineid} line={line}/>
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
<CmdInput sessionid={this.props.sessionid}/>
|
<CmdInput sessionid={session.sessionid} windowid={window.windowid}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class Main extends React.Component<{sessionid : string}, {}> {
|
class Main extends React.Component<{}, {}> {
|
||||||
constructor(props : any) {
|
constructor(props : any) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let windowLines = GlobalLines.get();
|
||||||
|
let session : SessionType = {
|
||||||
|
sessionid: GSessionId,
|
||||||
|
name: "default",
|
||||||
|
windows: [
|
||||||
|
{windowid: GWindowId, name: "default", lines: windowLines},
|
||||||
|
],
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="main">
|
<div className="main">
|
||||||
<h1 className="title scripthaus-logo-small">
|
<h1 className="title scripthaus-logo-small">
|
||||||
<div className="title-cursor">█</div>
|
<div className="title-cursor">█</div>
|
||||||
ScriptHaus
|
ScriptHaus
|
||||||
</h1>
|
</h1>
|
||||||
<SessionView sessionid={this.props.sessionid}/>
|
<SessionView session={session}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
color: #777;
|
color: #777;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cmdid {
|
.metapart-mono {
|
||||||
color: #777;
|
color: #777;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
@ -89,11 +89,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cmdtext {
|
.cmdtext {
|
||||||
font-family: 'JetBrains Mono', monospace;
|
color: black;
|
||||||
font-weight: 400;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-left: 8px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,10 @@ import {Main} from "./main";
|
|||||||
import {GlobalWS} from "./ws";
|
import {GlobalWS} from "./ws";
|
||||||
|
|
||||||
let VERSION = __SHVERSION__;
|
let VERSION = __SHVERSION__;
|
||||||
let terminal = null;
|
|
||||||
let sessionId = "47445c53-cfcf-4943-8339-2c04447f20a1";
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
GlobalWS.reconnect();
|
GlobalWS.reconnect();
|
||||||
let reactElem = React.createElement(Main, {sessionid: sessionId}, null);
|
let reactElem = React.createElement(Main, null, null);
|
||||||
let elem = document.getElementById("main");
|
let elem = document.getElementById("main");
|
||||||
let root = createRoot(elem);
|
let root = createRoot(elem);
|
||||||
root.render(reactElem);
|
root.render(reactElem);
|
||||||
|
59
src/term.ts
59
src/term.ts
@ -4,8 +4,23 @@ import {sprintf} from "sprintf-js";
|
|||||||
import {boundMethod} from "autobind-decorator";
|
import {boundMethod} from "autobind-decorator";
|
||||||
import {GlobalWS} from "./ws";
|
import {GlobalWS} from "./ws";
|
||||||
|
|
||||||
|
//
|
||||||
var TermMap : Record<string, TermWrap>;
|
var TermMap : Record<string, TermWrap>;
|
||||||
|
|
||||||
|
function getOrCreateTermWrap(sessionId : string, cmdId : string, windowId : string, lineid : number) : TermWrap {
|
||||||
|
let termKey = makeTermKey(sessionId, cmdId, windowId, lineid);
|
||||||
|
let termWrap = TermMap[termKey];
|
||||||
|
if (termWrap != null) {
|
||||||
|
return termWrap;
|
||||||
|
}
|
||||||
|
termWrap = new TermWrap(sessionId, cmdId, windowId, lineid);
|
||||||
|
return termWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeTermKey(sessionId : string, cmdId : string, windowId : string, lineid : number) : string {
|
||||||
|
return sprintf("%s/%s/%s/%s", sessionId, cmdId, windowId, lineid);
|
||||||
|
}
|
||||||
|
|
||||||
function loadPtyOut(term : Terminal, sessionId : string, cmdId : string, delayMs : number, callback?: (number) => void) {
|
function loadPtyOut(term : Terminal, sessionId : string, cmdId : string, delayMs : number, callback?: (number) => void) {
|
||||||
term.clear()
|
term.clear()
|
||||||
let url = sprintf("http://localhost:8080/api/ptyout?sessionid=%s&cmdid=%s", sessionId, cmdId);
|
let url = sprintf("http://localhost:8080/api/ptyout?sessionid=%s&cmdid=%s", sessionId, cmdId);
|
||||||
@ -23,26 +38,24 @@ class TermWrap {
|
|||||||
terminal : Terminal;
|
terminal : Terminal;
|
||||||
sessionId : string;
|
sessionId : string;
|
||||||
cmdId : string;
|
cmdId : string;
|
||||||
ptyPos : number;
|
windowId : string;
|
||||||
runPos : number;
|
lineid : number;
|
||||||
runData : string;
|
ptyPos : number = 0;
|
||||||
|
runPos : number = 0;
|
||||||
|
runData : string = "";
|
||||||
renderVersion : mobx.IObservableValue<number> = mobx.observable.box(1, {name: "renderVersion"});
|
renderVersion : mobx.IObservableValue<number> = mobx.observable.box(1, {name: "renderVersion"});
|
||||||
isFocused : mobx.IObservableValue<boolean> = mobx.observable.box(false, {name: "focus"});
|
isFocused : mobx.IObservableValue<boolean> = mobx.observable.box(false, {name: "focus"});
|
||||||
flexRows : boolean;
|
flexRows : boolean = true;
|
||||||
maxRows : number;
|
maxRows : number = 25;
|
||||||
cols : number;
|
cols : number = 80;
|
||||||
atRowMax : boolean;
|
atRowMax : boolean = false;
|
||||||
|
initialized : boolean = false;
|
||||||
|
|
||||||
constructor(sessionId : string, cmdId : string) {
|
constructor(sessionId : string, cmdId : string, windowId : string, lineid : number) {
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
this.cmdId = cmdId;
|
this.cmdId = cmdId;
|
||||||
this.ptyPos = 0;
|
this.windowId = windowId;
|
||||||
this.runPos = 0;
|
this.lineid = lineid;
|
||||||
this.runData = "";
|
|
||||||
this.maxRows = 25;
|
|
||||||
this.cols = 80;
|
|
||||||
this.flexRows = true;
|
|
||||||
this.atRowMax = false;
|
|
||||||
this.terminal = new Terminal({rows: 2, cols: 80});
|
this.terminal = new Terminal({rows: 2, cols: 80});
|
||||||
TermMap[cmdId] = this;
|
TermMap[cmdId] = this;
|
||||||
}
|
}
|
||||||
@ -51,6 +64,20 @@ class TermWrap {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// datalen is passed because data could be utf-8 and data.length is not the actual *byte* length
|
||||||
|
updatePtyData(pos : number, data : string, datalen : number) {
|
||||||
|
if (pos != this.ptyPos) {
|
||||||
|
throw new Error(sprintf("invalid pty-update, data-pos[%d] does not match term-pos[%d]", pos, this.ptyPos));
|
||||||
|
}
|
||||||
|
this.ptyPos += datalen;
|
||||||
|
term.write(data, () => {
|
||||||
|
mobx.action(() => {
|
||||||
|
this.resizeToContent();
|
||||||
|
this.incRenderVersion();
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
resizeToContent() {
|
resizeToContent() {
|
||||||
if (this.atRowMax) {
|
if (this.atRowMax) {
|
||||||
return;
|
return;
|
||||||
@ -128,4 +155,4 @@ if (window.TermMap == null) {
|
|||||||
window.TermMap = TermMap;
|
window.TermMap = TermMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {TermWrap, TermMap};
|
export {TermWrap, TermMap, makeTermKey, getOrCreateTermWrap};
|
||||||
|
Loading…
Reference in New Issue
Block a user