mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
implement info panel, more control in cmd-input, completions, errors
This commit is contained in:
parent
1f4ac87a9a
commit
5082330dcf
226
src/main.tsx
226
src/main.tsx
@ -18,6 +18,51 @@ function getLineId(line : LineType) : string {
|
||||
return sprintf("%s-%s-%s", line.sessionid, line.windowid, line.lineid);
|
||||
}
|
||||
|
||||
function getRemoteStr(remote : RemoteType) : string {
|
||||
if (remote == null) {
|
||||
return "(no remote)";
|
||||
}
|
||||
if (remote.remotevars.local) {
|
||||
return sprintf("%s@%s", remote.remotevars.remoteuser, "local")
|
||||
}
|
||||
else if (remote.remotevars.remotehost) {
|
||||
return sprintf("%s@%s", remote.remotevars.remoteuser, remote.remotevars.remotehost);
|
||||
}
|
||||
else {
|
||||
let host = remote.remotevars.host || "unknown";
|
||||
if (remote.remotevars.user) {
|
||||
return sprintf("%s@%s", remote.remotevars.user, host)
|
||||
}
|
||||
else {
|
||||
return host;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function replaceHomePath(path : string, homeDir : string) : string {
|
||||
if (path == homeDir) {
|
||||
return "~";
|
||||
}
|
||||
if (path.startsWith(homeDir + "/")) {
|
||||
return "~" + path.substr(homeDir.length);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
function getCwdStr(remote : RemoteType, state : RemoteStateType) : string {
|
||||
if ((state == null || state.cwd == null) && remote != null) {
|
||||
return "~";
|
||||
}
|
||||
let cwd = "(unknown)";
|
||||
if (state && state.cwd) {
|
||||
cwd = state.cwd;
|
||||
}
|
||||
if (remote && remote.remotevars.home) {
|
||||
cwd = replaceHomePath(cwd, remote.remotevars.home)
|
||||
}
|
||||
return cwd;
|
||||
}
|
||||
|
||||
function getLineDateStr(ts : number) : string {
|
||||
let lineDate = new Date(ts);
|
||||
let nowDate = new Date();
|
||||
@ -107,16 +152,6 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width
|
||||
}
|
||||
}
|
||||
|
||||
replaceHomePath(path : string, homeDir : string) : string {
|
||||
if (path == homeDir) {
|
||||
return "~";
|
||||
}
|
||||
if (path.startsWith(homeDir + "/")) {
|
||||
return "~" + path.substr(homeDir.length);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
renderCmdText(cmd : Cmd, remote : RemoteType) : any {
|
||||
if (cmd == null) {
|
||||
return (
|
||||
@ -125,30 +160,8 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width
|
||||
</div>
|
||||
);
|
||||
}
|
||||
let promptStr = "";
|
||||
if (remote.remotevars.local) {
|
||||
promptStr = sprintf("%s@%s", remote.remotevars.remoteuser, "local")
|
||||
}
|
||||
else if (remote.remotevars.remotehost) {
|
||||
promptStr = sprintf("%s@%s", remote.remotevars.remoteuser, remote.remotevars.remotehost)
|
||||
}
|
||||
else {
|
||||
let host = remote.remotevars.host || "unknown";
|
||||
if (remote.remotevars.user) {
|
||||
promptStr = sprintf("%s@%s", remote.remotevars.user, host)
|
||||
}
|
||||
else {
|
||||
promptStr = host;
|
||||
}
|
||||
}
|
||||
let cwd = "(unknown)";
|
||||
let remoteState = cmd.getRemoteState();
|
||||
if (remoteState && remoteState.cwd) {
|
||||
cwd = remoteState.cwd;
|
||||
}
|
||||
if (remote.remotevars.home) {
|
||||
cwd = this.replaceHomePath(cwd, remote.remotevars.home)
|
||||
}
|
||||
let promptStr = getRemoteStr(remote);
|
||||
let cwd = getCwdStr(remote, cmd.getRemoteState());
|
||||
return (
|
||||
<div className="metapart-mono cmdtext">
|
||||
<span className="term-bright-green">[{promptStr} {cwd}]</span> {cmd.getSingleLineCmdText()}
|
||||
@ -221,110 +234,143 @@ class Line extends React.Component<{sw : ScreenWindow, line : LineType, width :
|
||||
|
||||
@mobxReact.observer
|
||||
class CmdInput extends React.Component<{}, {}> {
|
||||
historyIndex : mobx.IObservableValue<number> = mobx.observable.box(0, {name: "history-index"});
|
||||
modHistory : mobx.IObservableArray<string> = mobx.observable.array([""], {name: "mod-history"});
|
||||
lastTabCurLine : mobx.IObservableValue<string> = mobx.observable.box(null);
|
||||
|
||||
@mobx.action @boundMethod
|
||||
onKeyDown(e : any) {
|
||||
mobx.action(() => {
|
||||
let model = GlobalModel;
|
||||
let inputModel = model.inputModel;
|
||||
let win = model.getActiveWindow();
|
||||
let ctrlMod = e.getModifierState("Control") || e.getModifierState("Meta") || e.getModifierState("Shift");
|
||||
let curLine = inputModel.getCurLine();
|
||||
let ltCurLine = this.lastTabCurLine.get();
|
||||
if (e.code == "Tab") {
|
||||
e.preventDefault();
|
||||
let lastTab = (ltCurLine != null && curLine == ltCurLine);
|
||||
if (lastTab) {
|
||||
GlobalModel.submitCommand("compgen", null, [curLine], {"comppos": String(curLine.length), "compshow": "1"});
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.lastTabCurLine.set(curLine);
|
||||
GlobalModel.submitCommand("compgen", null, [curLine], {"comppos": String(curLine.length)});
|
||||
GlobalModel.clearInfoMsg(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ltCurLine != null && curLine != ltCurLine) {
|
||||
this.lastTabCurLine.set(null);
|
||||
}
|
||||
if (e.code == "Enter" && !ctrlMod) {
|
||||
e.preventDefault();
|
||||
setTimeout(() => this.doSubmitCmd(), 0);
|
||||
return;
|
||||
}
|
||||
if (e.code == "Tab") {
|
||||
if (e.code == "Escape") {
|
||||
e.preventDefault();
|
||||
this.setCurLine(this.getCurLine() + "[tab]");
|
||||
GlobalModel.toggleInfoMsg();
|
||||
return;
|
||||
}
|
||||
if (e.code == "KeyC" && e.getModifierState("Control")) {
|
||||
e.preventDefault();
|
||||
inputModel.clearCurLine();
|
||||
return;
|
||||
}
|
||||
if (e.code == "ArrowUp") {
|
||||
e.preventDefault();
|
||||
let hidx = this.historyIndex.get();
|
||||
hidx += 1;
|
||||
if (hidx > win.getNumHistoryItems()) {
|
||||
hidx = win.getNumHistoryItems();
|
||||
}
|
||||
this.historyIndex.set(hidx);
|
||||
inputModel.prevHistoryItem();
|
||||
return;
|
||||
}
|
||||
if (e.code == "ArrowDown") {
|
||||
e.preventDefault();
|
||||
let hidx = this.historyIndex.get();
|
||||
hidx -= 1;
|
||||
if (hidx < 0) {
|
||||
hidx = 0;
|
||||
}
|
||||
this.historyIndex.set(hidx);
|
||||
inputModel.nextHistoryItem();
|
||||
return;
|
||||
}
|
||||
// console.log(e.code, e.keyCode, e.key, event.which, ctrlMod, e);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
clearCurLine() {
|
||||
mobx.action(() => {
|
||||
this.historyIndex.set(0);
|
||||
this.modHistory.clear();
|
||||
this.modHistory[0] = "";
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
getCurLine() : string {
|
||||
let model = GlobalModel;
|
||||
let hidx = this.historyIndex.get();
|
||||
if (hidx < this.modHistory.length && this.modHistory[hidx] != null) {
|
||||
return this.modHistory[hidx];
|
||||
}
|
||||
let win = model.getActiveWindow();
|
||||
if (win == null) {
|
||||
return "";
|
||||
}
|
||||
let hitem = win.getHistoryItem(-hidx);
|
||||
if (hitem == null) {
|
||||
return "";
|
||||
}
|
||||
return hitem.cmdtext;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
setCurLine(val : string) {
|
||||
let hidx = this.historyIndex.get();
|
||||
this.modHistory[hidx] = val;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
onChange(e : any) {
|
||||
mobx.action(() => {
|
||||
this.setCurLine(e.target.value);
|
||||
GlobalModel.inputModel.setCurLine(e.target.value);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
doSubmitCmd() {
|
||||
let model = GlobalModel;
|
||||
let commandStr = this.getCurLine();
|
||||
let inputModel = model.inputModel;
|
||||
let commandStr = inputModel.getCurLine();
|
||||
let hitem = {cmdtext: commandStr};
|
||||
this.clearCurLine();
|
||||
inputModel.clearCurLine();
|
||||
GlobalModel.clearInfoMsg(true);
|
||||
model.submitRawCommand(commandStr);
|
||||
}
|
||||
|
||||
render() {
|
||||
let curLine = this.getCurLine();
|
||||
let model = GlobalModel;
|
||||
let inputModel = model.inputModel;
|
||||
let curLine = inputModel.getCurLine();
|
||||
let win = GlobalModel.getActiveWindow();
|
||||
let ri : RemoteInstanceType = null;
|
||||
if (win != null) {
|
||||
ri = win.getCurRemoteInstance();
|
||||
}
|
||||
let remote : RemoteType = null;
|
||||
let remoteState : RemoteStateType = null;
|
||||
if (ri != null) {
|
||||
remote = GlobalModel.getRemote(ri.remoteid);
|
||||
remoteState = ri.state;
|
||||
}
|
||||
let promptStr = getRemoteStr(remote);
|
||||
let cwdStr = getCwdStr(remote, remoteState);
|
||||
let infoMsg = GlobalModel.infoMsg.get();
|
||||
let infoShow = GlobalModel.infoShow.get();
|
||||
let istr : string = null;
|
||||
let istrIdx : number = 0;
|
||||
return (
|
||||
<div className="box cmd-input has-background-black">
|
||||
<div className={cn("box cmd-input has-background-black", {"has-info": infoShow})}>
|
||||
<div className="cmd-input-info" style={{display: (infoShow ? "block" : "none")}}>
|
||||
<If condition={infoMsg && infoMsg.infotitle != null}>
|
||||
<div className="info-title">
|
||||
{infoMsg.infotitle}
|
||||
</div>
|
||||
</If>
|
||||
<If condition={infoMsg && infoMsg.infomsg != null}>
|
||||
<div className="info-msg">
|
||||
{infoMsg.infomsg}
|
||||
</div>
|
||||
</If>
|
||||
<If condition={infoMsg && infoMsg.infostrings != null && infoMsg.infostrings.length > 0}>
|
||||
<div className="info-strings">
|
||||
<For each="istr" index="istrIdx" of={infoMsg.infostrings}>
|
||||
<div key={istrIdx} className="info-string">
|
||||
{istr}
|
||||
</div>
|
||||
</For>
|
||||
<If condition={infoMsg.infostringsmore}>
|
||||
<div key="more" className="info-string">
|
||||
...
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
</If>
|
||||
<If condition={infoMsg && infoMsg.infoerror != null}>
|
||||
<div className="info-error">
|
||||
{infoMsg.infoerror}
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
<div className="cmd-input-context">
|
||||
<div className="has-text-white">
|
||||
<span className="bold term-bright-green">[mike@local ~]</span>
|
||||
<span className="bold term-bright-green">[{promptStr} {cwdStr}]</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="cmd-input-field field has-addons">
|
||||
<div className="control cmd-quick-context">
|
||||
<div className="button is-static">mike@local</div>
|
||||
<div className="button is-static">{promptStr}</div>
|
||||
</div>
|
||||
<div className="control cmd-input-control is-expanded">
|
||||
<textarea id="main-cmd-input" value={curLine} onKeyDown={this.onKeyDown} onChange={this.onChange} className="input"></textarea>
|
||||
|
178
src/model.ts
178
src/model.ts
@ -1,7 +1,7 @@
|
||||
import * as mobx from "mobx";
|
||||
import {sprintf} from "sprintf-js";
|
||||
import {boundMethod} from "autobind-decorator";
|
||||
import {handleJsonFetchResponse, base64ToArray, genMergeData} from "./util";
|
||||
import {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData} from "./util";
|
||||
import {TermWrap} from "./term";
|
||||
import {v4 as uuidv4} from "uuid";
|
||||
import type {SessionDataType, WindowDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenWindowType, ScreenOptsType, LayoutType, PtyDataUpdateType, SessionUpdateType, WindowUpdateType, UpdateMessage, LineCmdUpdateType} from "./types";
|
||||
@ -296,12 +296,13 @@ class Window {
|
||||
if (load) {
|
||||
this.loaded.set(true);
|
||||
}
|
||||
this.lines.replace(win.lines || []);
|
||||
genMergeSimpleData(this.lines, win.lines, (l) => String(l.lineid), (l) => l.lineid);
|
||||
this.history = win.history || [];
|
||||
let cmds = win.cmds || [];
|
||||
for (let i=0; i<cmds.length; i++) {
|
||||
this.cmds[cmds[i].cmdid] = new Cmd(cmds[i]);
|
||||
}
|
||||
genMergeSimpleData(this.remoteInstances, win.remotes, (r) => r.riid, null);
|
||||
})();
|
||||
}
|
||||
|
||||
@ -321,6 +322,9 @@ class Window {
|
||||
|
||||
getCurRemoteInstance() : RemoteInstanceType {
|
||||
let rname = this.curRemote.get();
|
||||
if (rname == null) {
|
||||
return null;
|
||||
}
|
||||
let sessionScope = false;
|
||||
if (rname.startsWith("^")) {
|
||||
rname = rname.substr(1);
|
||||
@ -385,7 +389,7 @@ class Session {
|
||||
sessionIdx : OV<number>;
|
||||
screens : OArr<Screen>;
|
||||
notifyNum : OV<number> = mobx.observable.box(0);
|
||||
remoteInstances : OArr<RemoteInstanceType> = mobx.observable.array([]);
|
||||
remoteInstances : OArr<RemoteInstanceType>;
|
||||
|
||||
constructor(sdata : SessionDataType) {
|
||||
this.sessionId = sdata.sessionid;
|
||||
@ -399,6 +403,8 @@ class Session {
|
||||
}
|
||||
this.screens = mobx.observable.array(screens, {deep: false});
|
||||
this.activeScreenId = mobx.observable.box(ces(sdata.activescreenid));
|
||||
let remotes = sdata.remotes || [];
|
||||
this.remoteInstances = mobx.observable.array(remotes);
|
||||
}
|
||||
|
||||
dispose() : void {
|
||||
@ -492,6 +498,91 @@ class Session {
|
||||
}
|
||||
}
|
||||
|
||||
type InfoType = {
|
||||
infotitle : string;
|
||||
infomsg : string;
|
||||
infoerror : string;
|
||||
infostrings : string[];
|
||||
};
|
||||
|
||||
type CmdLineUpdateType = {
|
||||
insertchars : string,
|
||||
insertpos : number,
|
||||
};
|
||||
|
||||
class InputModel {
|
||||
historyIndex : mobx.IObservableValue<number> = mobx.observable.box(0, {name: "history-index"});
|
||||
modHistory : mobx.IObservableArray<string> = mobx.observable.array([""], {name: "mod-history"});
|
||||
|
||||
updateCmdLine(cmdLine : CmdLineUpdateType) {
|
||||
mobx.action(() => {
|
||||
let curLine = this.getCurLine();
|
||||
if (curLine.length < cmdLine.insertpos) {
|
||||
return;
|
||||
}
|
||||
let pos = cmdLine.insertpos;
|
||||
curLine = curLine.substr(0, pos) + cmdLine.insertchars + curLine.substr(pos);
|
||||
this.setCurLine(curLine);
|
||||
})();
|
||||
}
|
||||
|
||||
setCurLine(val : string) {
|
||||
let hidx = this.historyIndex.get();
|
||||
this.modHistory[hidx] = val;
|
||||
}
|
||||
|
||||
clearCurLine() {
|
||||
mobx.action(() => {
|
||||
this.historyIndex.set(0);
|
||||
this.modHistory.clear();
|
||||
this.modHistory[0] = "";
|
||||
})();
|
||||
}
|
||||
|
||||
getCurLine() : string {
|
||||
let model = GlobalModel;
|
||||
let hidx = this.historyIndex.get();
|
||||
if (hidx < this.modHistory.length && this.modHistory[hidx] != null) {
|
||||
return this.modHistory[hidx];
|
||||
}
|
||||
let win = model.getActiveWindow();
|
||||
if (win == null) {
|
||||
return "";
|
||||
}
|
||||
let hitem = win.getHistoryItem(-hidx);
|
||||
if (hitem == null) {
|
||||
return "";
|
||||
}
|
||||
return hitem.cmdtext;
|
||||
}
|
||||
|
||||
prevHistoryItem() : void {
|
||||
let model = GlobalModel;
|
||||
let win = model.getActiveWindow();
|
||||
let hidx = this.historyIndex.get();
|
||||
hidx += 1;
|
||||
if (hidx > win.getNumHistoryItems()) {
|
||||
hidx = win.getNumHistoryItems();
|
||||
}
|
||||
mobx.action(() => {
|
||||
this.historyIndex.set(hidx);
|
||||
})();
|
||||
return;
|
||||
}
|
||||
|
||||
nextHistoryItem() : void {
|
||||
let hidx = this.historyIndex.get();
|
||||
hidx -= 1;
|
||||
if (hidx < 0) {
|
||||
hidx = 0;
|
||||
}
|
||||
mobx.action(() => {
|
||||
this.historyIndex.set(hidx);
|
||||
})();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
class Model {
|
||||
clientId : string;
|
||||
activeSessionId : OV<string> = mobx.observable.box(null);
|
||||
@ -501,6 +592,10 @@ class Model {
|
||||
remotes : OArr<RemoteType> = mobx.observable.array([], {deep: false});
|
||||
remotesLoaded : OV<boolean> = mobx.observable.box(false);
|
||||
windows : OMap<string, Window> = mobx.observable.map({}, {deep: false});
|
||||
infoShow : OV<boolean> = mobx.observable.box(false);
|
||||
infoMsg : OV<InfoType> = mobx.observable.box(null);
|
||||
infoTimeoutId : any = null;
|
||||
inputModel : InputModel;
|
||||
|
||||
constructor() {
|
||||
this.clientId = getApi().getId();
|
||||
@ -508,6 +603,7 @@ class Model {
|
||||
this.loadSessionList();
|
||||
this.ws = new WSControl(this.clientId, (message : any) => this.runUpdate(message, false));
|
||||
this.ws.reconnect();
|
||||
this.inputModel = new InputModel();
|
||||
getApi().onTCmd(this.onTCmd.bind(this));
|
||||
getApi().onICmd(this.onICmd.bind(this));
|
||||
getApi().onBracketCmd(this.onBracketCmd.bind(this));
|
||||
@ -519,6 +615,47 @@ class Model {
|
||||
getApi().contextScreen({screenId: screenId}, {x: e.x, y: e.y});
|
||||
}
|
||||
|
||||
flashInfoMsg(info : InfoType, timeoutMs : number) {
|
||||
if (this.infoTimeoutId != null) {
|
||||
clearTimeout(this.infoTimeoutId);
|
||||
this.infoTimeoutId = null;
|
||||
}
|
||||
mobx.action(() => {
|
||||
this.infoMsg.set(info);
|
||||
this.infoShow.set(info != null);
|
||||
})();
|
||||
if (info != null && timeoutMs) {
|
||||
this.infoTimeoutId = setTimeout(() => {
|
||||
this.clearInfoMsg(false);
|
||||
}, timeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
clearInfoMsg(setNull : boolean) {
|
||||
this.infoTimeoutId = null;
|
||||
mobx.action(() => {
|
||||
this.infoShow.set(false);
|
||||
if (setNull) {
|
||||
this.infoMsg.set(null);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
toggleInfoMsg() {
|
||||
this.infoTimeoutId = null;
|
||||
mobx.action(() => {
|
||||
let isShowing = this.infoShow.get();
|
||||
if (isShowing) {
|
||||
this.infoShow.set(false);
|
||||
}
|
||||
else {
|
||||
if (this.infoMsg.get() != null) {
|
||||
this.infoShow.set(true);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
onTCmd(mods : KeyModsType) {
|
||||
console.log("got cmd-t", mods);
|
||||
GlobalInput.createNewScreen();
|
||||
@ -561,7 +698,6 @@ class Model {
|
||||
return;
|
||||
}
|
||||
activeScreen.updatePtyData(ptyMsg);
|
||||
return;
|
||||
}
|
||||
if ("sessions" in update) {
|
||||
let sessionUpdateMsg : SessionUpdateType = update;
|
||||
@ -588,13 +724,20 @@ class Model {
|
||||
let lineMsg : LineCmdUpdateType = update;
|
||||
this.addLineCmd(lineMsg.line, lineMsg.cmd, interactive);
|
||||
}
|
||||
if ("window" in update) {
|
||||
let winMsg : WindowUpdateType = update;
|
||||
this.updateWindow(winMsg.window, false);
|
||||
}
|
||||
if ("info" in update) {
|
||||
let info = update.info;
|
||||
this.flashInfoMsg(info, info.timeoutms);
|
||||
}
|
||||
if ("cmdline" in update) {
|
||||
this.inputModel.updateCmdLine(update.cmdline);
|
||||
}
|
||||
console.log("run-update>", interactive, update);
|
||||
}
|
||||
|
||||
removeSession(sessionId : string) {
|
||||
console.log("removeSession not implemented");
|
||||
}
|
||||
|
||||
getActiveSession() : Session {
|
||||
return this.getSessionById(this.activeSessionId.get());
|
||||
}
|
||||
@ -631,7 +774,7 @@ class Model {
|
||||
let existingWin = this.windows.get(winKey);
|
||||
if (existingWin == null) {
|
||||
if (!load) {
|
||||
console.log("cannot update window that does not exist");
|
||||
console.log("cannot update window that does not exist", winKey);
|
||||
return;
|
||||
}
|
||||
let newWindow = new Window(win.sessionid, win.windowid);
|
||||
@ -707,7 +850,7 @@ class Model {
|
||||
}
|
||||
})();
|
||||
}).catch((err) => {
|
||||
this.errorHandler("calling run-command", err);
|
||||
this.errorHandler("calling run-command", err, true);
|
||||
});
|
||||
}
|
||||
|
||||
@ -756,7 +899,7 @@ class Model {
|
||||
}
|
||||
})();
|
||||
}).catch((err) => {
|
||||
this.errorHandler("getting session list", err);
|
||||
this.errorHandler("getting session list", err, false);
|
||||
});
|
||||
}
|
||||
|
||||
@ -805,7 +948,7 @@ class Model {
|
||||
this.updateWindow(data.data, true);
|
||||
return;
|
||||
}).catch((err) => {
|
||||
this.errorHandler(sprintf("getting window=%s", windowId), err);
|
||||
this.errorHandler(sprintf("getting window=%s", windowId), err, false);
|
||||
});
|
||||
return newWin;
|
||||
}
|
||||
@ -818,7 +961,7 @@ class Model {
|
||||
this.remotesLoaded.set(true);
|
||||
})();
|
||||
}).catch((err) => {
|
||||
this.errorHandler("calling get-remotes", err)
|
||||
this.errorHandler("calling get-remotes", err, false)
|
||||
});
|
||||
}
|
||||
|
||||
@ -852,8 +995,15 @@ class Model {
|
||||
return window.getCmd(line.cmdid);
|
||||
}
|
||||
|
||||
errorHandler(str : string, err : any) {
|
||||
errorHandler(str : string, err : any, interactive : boolean) {
|
||||
console.log("[error]", str, err);
|
||||
if (interactive) {
|
||||
let errMsg = "error running command";
|
||||
if (err != null && err.message) {
|
||||
errMsg = err.message;
|
||||
}
|
||||
this.flashInfoMsg({infoerror: errMsg}, null);
|
||||
}
|
||||
}
|
||||
|
||||
sendInputPacket(inputPacket : any) {
|
||||
|
42
src/sh2.less
42
src/sh2.less
@ -464,6 +464,10 @@ body .xterm .xterm-viewport {
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-bottom-right-radius: 10px;
|
||||
|
||||
&.has-info {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.cmd-input-context {
|
||||
color: #fff;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
@ -491,6 +495,44 @@ body .xterm .xterm-viewport {
|
||||
color: #d3d7cf;
|
||||
}
|
||||
}
|
||||
|
||||
.cmd-input-info {
|
||||
.info-msg {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #729fcf;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.info-title {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #729fcf;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.info-strings {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
padding-bottom: 5px;
|
||||
|
||||
.info-string {
|
||||
min-width: 200px;
|
||||
color: #d3d7cf;
|
||||
}
|
||||
}
|
||||
|
||||
.info-error {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #cc0000;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bold {
|
||||
|
@ -87,6 +87,8 @@ type RemoteInstanceType = {
|
||||
remoteid : string,
|
||||
sessionscope : boolean,
|
||||
state : RemoteStateType,
|
||||
|
||||
remove? : boolean,
|
||||
}
|
||||
|
||||
type WindowDataType = {
|
||||
@ -185,7 +187,6 @@ type UpdateMessage = PtyDataUpdateType | SessionUpdateType | LineCmdUpdateType;
|
||||
|
||||
type WindowUpdateType = {
|
||||
window: WindowDataType,
|
||||
remove: boolean,
|
||||
}
|
||||
|
||||
export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, WindowDataType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDonePacketType, CmdDataType, ScreenDataType, ScreenOptsType, ScreenWindowType, LayoutType, PtyDataUpdateType, SessionUpdateType, WindowUpdateType, UpdateMessage, LineCmdUpdateType};
|
||||
|
36
src/util.ts
36
src/util.ts
@ -58,6 +58,40 @@ interface IObjType<DataType> {
|
||||
mergeData : (data : DataType) => void,
|
||||
}
|
||||
|
||||
interface ISimpleDataType {
|
||||
remove? : boolean;
|
||||
}
|
||||
|
||||
function genMergeSimpleData<T extends ISimpleDataType>(objs : mobx.IObservableArray<T>, dataArr : T, idFn : (obj : T) => string, sortIdxFn : (obj : T) => number) {
|
||||
if (dataArr == null || dataArr.length == 0) {
|
||||
return;
|
||||
}
|
||||
let objMap : Record<string, T> = {};
|
||||
for (let i=0; i<objs.length; i++) {
|
||||
let obj = objs[i];
|
||||
let id = idFn(obj);
|
||||
objMap[id] = obj;
|
||||
}
|
||||
for (let i=0; i<dataArr.length; i++) {
|
||||
let dataItem = dataArr[i];
|
||||
let id = idFn(dataItem);
|
||||
if (dataItem.remove) {
|
||||
delete objMap[id];
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
objMap[id] = dataItem;
|
||||
}
|
||||
}
|
||||
let newObjs = Object.values(objMap);
|
||||
if (sortIdxFn) {
|
||||
newObjs.sort((a, b) => {
|
||||
return sortIdxFn(a) - sortIdxFn(b);
|
||||
});
|
||||
}
|
||||
objs.replace(newObjs);
|
||||
}
|
||||
|
||||
function genMergeData<ObjType extends IObjType<DataType>, DataType extends IDataType>(
|
||||
objs : mobx.IObservableArray<ObjType>,
|
||||
dataArr : DataType[],
|
||||
@ -106,4 +140,4 @@ function genMergeData<ObjType extends IObjType<DataType>, DataType extends IData
|
||||
objs.replace(newObjs);
|
||||
}
|
||||
|
||||
export {handleJsonFetchResponse, base64ToArray, genMergeData};
|
||||
export {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData};
|
||||
|
Loading…
Reference in New Issue
Block a user