run commands via run-command

This commit is contained in:
sawka 2022-06-13 11:12:39 -07:00
parent 751914196b
commit df3b7a3bb0
3 changed files with 95 additions and 22 deletions

View File

@ -16,8 +16,47 @@ type LineType = {
text : string, text : string,
cmdid : string, cmdid : string,
cmdtext : string, cmdtext : string,
isnew : boolean,
}; };
var GlobalLines = mobx.observable.box([
{lineid: 1, userid: "sawka", ts: 1654631122000, linetype: "text", text: "hello"},
{lineid: 2, userid: "sawka", ts: 1654631125000, linetype: "text", text: "again"},
]);
function fetchJsonData(resp : any, ctErr : boolean) : Promise<any> {
let contentType = resp.headers.get("Content-Type");
if (contentType != null && contentType.startsWith("application/json")) {
return resp.text().then((textData) => {
try {
return JSON.parse(textData);
}
catch (err) {
let errMsg = sprintf("Unparseable JSON: " + err.message);
let rtnErr = new Error(errMsg);
throw rtnErr;
}
});
}
if (ctErr) {
throw new Error("non-json content-type");
}
}
function handleJsonFetchResponse(url : URL, resp : any) : Promise<any> {
if (!resp.ok) {
let errData = fetchJsonData(resp, false);
if (errData && errData["error"]) {
throw new Error(errData["error"])
}
let errMsg = sprintf("Bad status code response from fetch '%s': %d %s", url.toString(), resp.status, resp.statusText);
let rtnErr = new Error(errMsg);
throw rtnErr;
}
let rtnData = fetchJsonData(resp, true);
return rtnData;
}
@mobxReact.observer @mobxReact.observer
class LineMeta extends React.Component<{line : LineType}, {}> { class LineMeta extends React.Component<{line : LineType}, {}> {
render() { render() {
@ -56,6 +95,7 @@ class LineText extends React.Component<{line : LineType}, {}> {
} }
function loadPtyOut(term : Terminal, sessionId : string, cmdId : string) { function loadPtyOut(term : Terminal, sessionId : string, cmdId : string) {
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);
fetch(url).then((resp) => { fetch(url).then((resp) => {
if (!resp.ok) { if (!resp.ok) {
@ -63,7 +103,6 @@ function loadPtyOut(term : Terminal, sessionId : string, cmdId : string) {
} }
return resp.text() return resp.text()
}).then((resptext) => { }).then((resptext) => {
console.log(resptext);
term.write(resptext); term.write(resptext);
}); });
} }
@ -75,11 +114,10 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
componentDidMount() { componentDidMount() {
let {line, sessionid} = this.props; let {line, sessionid} = this.props;
console.log("load terminal", sessionid, line.cmdid);
this.terminal = new Terminal(); this.terminal = new Terminal();
this.terminal.open(document.getElementById(this.getId())); let termElem = document.getElementById(this.getId());
this.terminal.open(termElem);
loadPtyOut(this.terminal, sessionid, line.cmdid); loadPtyOut(this.terminal, sessionid, line.cmdid);
console.log(this.terminal, this.terminal.element);
this.terminal.textarea.addEventListener("focus", () => { this.terminal.textarea.addEventListener("focus", () => {
mobx.action(() => { mobx.action(() => {
this.focus.set(true); this.focus.set(true);
@ -90,19 +128,37 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
this.focus.set(false); this.focus.set(false);
})(); })();
}); });
if (line.isnew) {
setTimeout(() => {
let lineElem = document.getElementById("line-" + this.getId());
lineElem.scrollIntoView({block: "end"});
mobx.action(() => {
line.isnew = false;
})();
}, 100);
setTimeout(() => {
loadPtyOut(this.terminal, sessionid, line.cmdid);
}, 1000);
}
} }
getId() : string { getId() : string {
let {line} = this.props; let {line} = this.props;
return "cmd-" + line.lineid + "-" + line.cmdid; return "cmd-" + line.lineid + "-" + line.cmdid;
} }
@boundMethod
doRefresh() {
let {line, sessionid} = this.props;
loadPtyOut(this.terminal, sessionid, line.cmdid);
}
render() { render() {
let {line} = this.props; let {line} = this.props;
let lineid = line.lineid.toString(); let lineid = line.lineid.toString();
let running = false; let running = false;
return ( return (
<div className="line line-cmd"> <div className="line line-cmd" id={"line-" + this.getId()}>
<div className={cn("avatar",{"num4": lineid.length == 4}, {"num5": lineid.length >= 5}, {"running": running})}> <div className={cn("avatar",{"num4": lineid.length == 4}, {"num5": lineid.length >= 5}, {"running": running})}>
{lineid} {lineid}
</div> </div>
@ -116,6 +172,9 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
<div className="terminal" id={this.getId()}></div> <div className="terminal" id={this.getId()}></div>
</div> </div>
</div> </div>
<div>
<div onClick={this.doRefresh} className="button">Refresh</div>
</div>
</div> </div>
); );
} }
@ -136,7 +195,7 @@ class Line extends React.Component<{line : LineType}, {}> {
} }
@mobxReact.observer @mobxReact.observer
class CmdInput extends React.Component<{line : LineType}, {}> { class CmdInput extends React.Component<{line : LineType, sessionid : 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
@ -144,13 +203,11 @@ class CmdInput extends React.Component<{line : LineType}, {}> {
mobx.action(() => { mobx.action(() => {
let ctrlMod = e.getModifierState("Control") || e.getModifierState("Meta") || e.getModifierState("Shift"); let ctrlMod = e.getModifierState("Control") || e.getModifierState("Meta") || e.getModifierState("Shift");
if (e.code == "Enter" && !ctrlMod) { if (e.code == "Enter" && !ctrlMod) {
let cmdLine = this.curLine.get();
this.curLine.set("");
console.log("START COMMAND", cmdLine);
e.preventDefault(); e.preventDefault();
setTimeout(() => this.doSubmitCmd(), 0);
return; return;
} }
console.log(e.code, e.keyCode, e.key, event.which, ctrlMod, e); // console.log(e.code, e.keyCode, e.key, event.which, ctrlMod, e);
})(); })();
} }
@ -160,6 +217,26 @@ class CmdInput extends React.Component<{line : LineType}, {}> {
this.curLine.set(e.target.value); this.curLine.set(e.target.value);
})(); })();
} }
@boundMethod
doSubmitCmd() {
let commandStr = this.curLine.get();
mobx.action(() => {
this.curLine.set("");
})();
let url = sprintf("http://localhost:8080/api/run-command");
let data = {sessionid: this.props.sessionid, command: commandStr};
fetch(url, {method: "post", body: JSON.stringify(data)}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => {
console.log("got success data", data);
mobx.action(() => {
let lines = GlobalLines.get();
data.data.line.isnew = true;
lines.push(data.data.line);
})();
}).catch((err) => {
console.log("error calling run-command", err)
});
}
render() { render() {
return ( return (
@ -177,7 +254,7 @@ class CmdInput extends React.Component<{line : LineType}, {}> {
<textarea value={this.curLine.get()} onKeyDown={this.onKeyDown} onChange={this.onChange} className="input" type="text"></textarea> <textarea value={this.curLine.get()} onKeyDown={this.onKeyDown} onChange={this.onChange} className="input" type="text"></textarea>
</div> </div>
<div className="control cmd-exec"> <div className="control cmd-exec">
<div className="button"> <div onClick={this.doSubmitCmd} className="button">
<span className="icon"> <span className="icon">
<i className="fa fa-rocket"/> <i className="fa fa-rocket"/>
</span> </span>
@ -192,13 +269,8 @@ class CmdInput extends React.Component<{line : LineType}, {}> {
@mobxReact.observer @mobxReact.observer
class Main extends React.Component<{sessionid : string}, {}> { class Main extends React.Component<{sessionid : string}, {}> {
render() { render() {
let lines = [ let lines = GlobalLines.get();
{lineid: 1, userid: "sawka", ts: 1654631122000, linetype: "text", text: "hello"}, console.log("main-lines", mobx.toJS(lines));
{lineid: 2, userid: "sawka", ts: 1654631125000, linetype: "text", text: "again"},
{lineid: 3, userid: "sawka", ts: 1654631125000, linetype: "??", text: "again"},
{lineid: 4, userid: "sawka", ts: 1654631125000, linetype: "cmd", cmdid: "47445c53-cfcf-4943-8339-2c04447f20a1", cmdtext: "ls -l"},
{lineid: 5, userid: "sawka", ts: 1654631135000, linetype: "cmd", cmdid: "792a66ab-577c-4fe1-88f4-862703bdb42d", cmdtext: "ls -l | grep go"},
];
return ( return (
<div className="main"> <div className="main">
<h1 className="title scripthaus-logo-small"> <h1 className="title scripthaus-logo-small">
@ -210,7 +282,7 @@ class Main extends React.Component<{sessionid : string}, {}> {
<Line key={line.lineid} line={line} sessionid={this.props.sessionid}/> <Line key={line.lineid} line={line} sessionid={this.props.sessionid}/>
</For> </For>
</div> </div>
<CmdInput/> <CmdInput sessionid={this.props.sessionid}/>
</div> </div>
); );
} }

View File

@ -96,10 +96,10 @@
.terminal-wrapper { .terminal-wrapper {
background-color: #000; background-color: #000;
padding: 5px; padding: 5px 15px 5px 5px;
width: calc(100% - 8px);
margin-right: 8px; margin-right: 8px;
margin-top: 2px; margin-top: 2px;
align-self: flex-start;
&.focus { &.focus {
outline: 3px solid blue; outline: 3px solid blue;

View File

@ -6,9 +6,10 @@ import {Main} from "./main";
let VERSION = __SHVERSION__; let VERSION = __SHVERSION__;
let terminal = null; let terminal = null;
let sessionId = "47445c53-cfcf-4943-8339-2c04447f20a1";
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
let reactElem = React.createElement(Main, {sessionid: "AQ45MM"}, null); let reactElem = React.createElement(Main, {sessionid: sessionId}, 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);