diff --git a/src/main.tsx b/src/main.tsx index 30b50126..656d96a7 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -189,6 +189,8 @@ class Prompt extends React.Component<{rptr : RemotePtrType, rstate : RemoteState class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width : number, staticRender : boolean, visible : OV, onHeightChange : HeightChangeCallbackType}, {}> { termLoaded : mobx.IObservableValue = mobx.observable.box(false); lineRef : React.RefObject = React.createRef(); + rtnStateDiff : mobx.IObservableValue = mobx.observable.box(null); + rtnStateDiffFetched : boolean = false; constructor(props) { super(props); @@ -209,6 +211,28 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width } } + checkStateDiffLoad() : void { + let {line, staticRender, visible} = this.props; + if (staticRender) { + return; + } + if (!visible) { + if (this.rtnStateDiffFetched) { + this.rtnStateDiffFetched = false; + this.setRtnStateDiff(null); + } + return; + } + let cmd = GlobalModel.getCmd(line); + if (cmd == null || !cmd.getRtnState() || this.rtnStateDiffFetched) { + return; + } + if (cmd.getStatus() != "done") { + return; + } + this.fetchRtnStateDiff(); + } + loadTerminal() : void { let {sw, line} = this.props; let model = GlobalModel; @@ -239,6 +263,32 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width } } + fetchRtnStateDiff() : void { + if (this.rtnStateDiffFetched) { + return; + } + let {line} = this.props; + this.rtnStateDiffFetched = true; + let usp = new URLSearchParams({sessionid: line.sessionid, cmdid: line.cmdid}); + let url = "http://localhost:8080/api/rtnstate?" + usp.toString(); + fetch(url).then((resp) => { + if (!resp.ok) { + throw new Error(sprintf("Bad fetch response for /api/rtnstate: %d %s", resp.status, resp.statusText)); + } + return resp.text(); + }).then((text) => { + this.setRtnStateDiff(text ?? ""); + }).catch((err) => { + this.setRtnStateDiff("ERROR " + err.toString()) + }); + } + + setRtnStateDiff(val : string) : void { + mobx.action(() => { + this.rtnStateDiff.set(val); + })(); + } + componentDidMount() { } @@ -310,6 +360,7 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width this.props.onHeightChange(line.linenum, curHeight, snapshot.height); } this.checkLoad(); + this.checkStateDiffLoad(); } render() { @@ -340,7 +391,7 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width let isFgFocused = isPhysicalFocused && swFocusType == "cmd-fg"; let isStatic = staticRender; return ( -
+
@@ -366,13 +417,26 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width
-
+
(loading)
+ +
+ +
state unchanged
+
+
+ +
new state
+
+
{this.rtnStateDiff.get()}
+
+
+
); } diff --git a/src/model.ts b/src/model.ts index d35a4d48..784ed3dc 100644 --- a/src/model.ts +++ b/src/model.ts @@ -138,6 +138,10 @@ class Cmd { })(); } + getRtnState() : boolean { + return this.data.get().rtnstate; + } + getStatus() : string { return this.data.get().status; } @@ -1374,8 +1378,8 @@ class InputModel { resetInputMode() : void { mobx.action(() => { - inputModel.setInputMode(null); - inputModel.setCurLine(""); + this.setInputMode(null); + this.setCurLine(""); })(); } @@ -2018,7 +2022,7 @@ class Model { _loadWindowAsync(newWin : Window) { this.windows.set(newWin.sessionId + "/" + newWin.windowId, newWin); let usp = new URLSearchParams({sessionid: newWin.sessionId, windowid: newWin.windowId}); - let url = new URL(sprintf("http://localhost:8080/api/get-window?") + usp.toString()); + let url = new URL("http://localhost:8080/api/get-window?" + usp.toString()); fetch(url).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => { if (data.data == null) { console.log("null window returned from get-window"); diff --git a/src/sh2.less b/src/sh2.less index c775691b..65da406e 100644 --- a/src/sh2.less +++ b/src/sh2.less @@ -410,6 +410,11 @@ html, body, #main { background-color: #000; } + &.has-rtnstate .terminal-wrapper { + padding-bottom: 0; + margin-bottom: -5px; + } + .terminal-wrapper { background-color: #000; padding: 2px 10px 5px 4px; @@ -418,6 +423,10 @@ html, body, #main { margin-top: 4px; align-self: flex-start; + &.zero-height { + display: none; + } + &.focus { /* box-shadow: 0 0 3px 3px rgba(255, 255, 255, 0.3); */ } @@ -435,6 +444,37 @@ html, body, #main { display: none; } } + + .cmd-rtnstate { + position: relative; + + .cmd-rtnstate-label { + position: relative; + z-index: 2; + .mono-font(9px); + margin-left: 10px; + padding: 2px 5px 2px 5px; + color: #666; + background-color: black; + display: inline-block; + } + + .cmd-rtnstate-sep { + height: 1px; + border-bottom: 1px solid #222; + position: relative; + top: -8px; + width: min(300px, 50%); + margin-bottom: -3px; + } + + .cmd-rtnstate-diff { + color: @term-white; + .mono-font(12px); + white-space: pre; + margin-left: 15px; + } + } } .cmd-input-info { diff --git a/src/term.ts b/src/term.ts index 0b1a2a85..c5e99332 100644 --- a/src/term.ts +++ b/src/term.ts @@ -254,7 +254,7 @@ class TermWrap { if (ptyOffsetStr != null && !isNaN(parseInt(ptyOffsetStr))) { ptyOffset = parseInt(ptyOffsetStr); } - return resp.arrayBuffer() + return resp.arrayBuffer(); }).then((buf) => { setTimeout(() => { this.reloading = false; diff --git a/src/types.ts b/src/types.ts index 076c929f..7d40de87 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,6 +26,7 @@ type LineType = { linetype : string, text : string, cmdid : string, + contentheight : number, ephemeral? : boolean, remove? : boolean, }; @@ -246,7 +247,7 @@ type CmdDataType = { startpk : CmdStartPacketType, donepk : CmdDonePacketType, runout : any[], - usedrows : number, + rtnstate : boolean, remove? : boolean, };