checkpoint, moving to model

This commit is contained in:
sawka
2022-07-11 17:55:03 -07:00
parent 77bd3ed5bf
commit 49c8b34a7c
6 changed files with 337 additions and 174 deletions
+49 -44
View File
@@ -9,10 +9,14 @@ import cn from "classnames"
import {TermWrap} from "./term";
import type {SessionDataType, LineType, CmdDataType, RemoteType} from "./types";
import localizedFormat from 'dayjs/plugin/localizedFormat';
import {GlobalMode, Cmd, Window} from "./model";
import {GlobalModel, Session, Cmd, Window} from "./model";
dayjs.extend(localizedFormat)
function getLineId(line : LineType) : string {
return sprintf("%s-%s-%s", line.sessionid, line.windowid, line.lineid);
}
@mobxReact.observer
class LineMeta extends React.Component<{line : LineType}, {}> {
render() {
@@ -79,10 +83,11 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
componentDidMount() {
let {line} = this.props;
let model = GlobalModel;
let termElem = document.getElementById("term-" + getLineId(line));
let termWrap = session.getTermWrapByLine(line);
termWrap.changeSizeCallback = this.props.changeSizeCallback;
termWrap.connectToElem(termElem);
let cmd = model.getCmd(line);
if (cmd != null) {
let termElem = document.getElementById("term-" + getLineId(line));
cmd.connectToElem(termElem);
}
if (line.isnew) {
setTimeout(() => this.scrollIntoView(), 100);
line.isnew = false;
@@ -96,9 +101,11 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
@boundMethod
doRefresh() {
let {session, line} = this.props;
let termWrap = session.getTermWrapByLine(line);
termWrap.reloadTerminal(true, 500);
let model = GlobalModel;
let cmd = model.getCmd(this.props.line);
if (cmd != null) {
cmd.reloadTerminal(true, 500);
}
}
replaceHomePath(path : string, homeDir : string) : string {
@@ -136,40 +143,36 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
}
}
let cwd = "(unknown)";
if (cmd.remotestate && cmd.remotestate.cwd) {
cwd = cmd.remotestate.cwd;
let remoteState = cmd.getRemoteState();
if (remoteState && remoteState.cwd) {
cwd = remoteState.cwd;
}
if (remote.remotevars.home) {
cwd = this.replaceHomePath(cwd, remote.remotevars.home)
}
return (
<div className="metapart-mono cmdtext">
<span className="term-bright-green">[{promptStr} {cwd}]</span> {this.singleLineCmdText(cmd.cmdstr)}
<span className="term-bright-green">[{promptStr} {cwd}]</span> {cmd.getSingleLineCmdText()}
</div>
);
}
render() {
let {session, line} = this.props;
let {line} = this.props;
let model = GlobalModel;
let lineid = line.lineid.toString();
let running = false;
let detached = false;
let rows = 0;
let cols = 0;
let termWrap = session.getTermWrapByLine(line);
let renderVersion = termWrap.getRenderVersion();
termWrap.resizeToContent();
let termSize = termWrap.getSize();
let formattedTime = getLineDateStr(line.ts);
let cellHeightPx = 17;
let totalHeight = cellHeightPx * termWrap.usedRows;
let cmd : CmdDataType = session.getCmd(line.cmdid);
let remote : RemoteType = null;
if (cmd != null) {
remote = session.getRemote(cmd.remoteid);
running = (cmd.status == "running");
detached = (cmd.status == "detached");
let cmd = model.getCmd(line);
if (cmd == null) {
return <div className="line line-invalid">[cmd not found '{line.cmdid}']</div>;
}
let cellHeightPx = 17;
let totalHeight = cellHeightPx * cmd.usedRows.get();
let remote = model.getRemote(cmd.remoteId);
let status = cmd.getStatus();
let running = (status == "running");
let detached = (status == "detached");
let termOpts = cmd.getTermOpts();
return (
<div className="line line-cmd" id={"line-" + getLineId(line)}>
<div className={cn("avatar",{"num4": lineid.length == 4}, {"num5": lineid.length >= 5}, {"running": running}, {"detached": detached})}>
@@ -183,12 +186,11 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
<div className="meta">
<div className="metapart-mono" style={{display: "none"}}>
{line.cmdid}
<If condition={termSize.rows > 0}>({termSize.rows}x{termSize.cols})</If>
{termWrap.ptyPos} bytes, v{renderVersion}
({termOpts.rows}x{termOpts.cols})
</div>
{this.renderCmdText(cmd, remote)}
</div>
<div className={cn("terminal-wrapper", {"focus": termWrap.isFocused.get()})} style={{overflowY: "hidden"}}>
<div className={cn("terminal-wrapper", {"focus": cmd.isFocused.get()})} style={{overflowY: "hidden"}}>
<div className="terminal" id={"term-" + getLineId(line)} data-cmdid={line.cmdid} style={{height: totalHeight}}></div>
</div>
</div>
@@ -201,7 +203,7 @@ class LineCmd extends React.Component<{line : LineType}, {}> {
}
@mobxReact.observer
class Line extends React.Component<{line : LineType, session : Session, changeSizeCallback? : (term : TermWrap) => void}, {}> {
class Line extends React.Component<{line : LineType, changeSizeCallback? : (term : TermWrap) => void}, {}> {
render() {
let line = this.props.line;
if (line.linetype == "text") {
@@ -215,7 +217,7 @@ class Line extends React.Component<{line : LineType, session : Session, changeSi
}
@mobxReact.observer
class CmdInput extends React.Component<{windowid : string}, {}> {
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"});
@@ -223,7 +225,7 @@ class CmdInput extends React.Component<{windowid : string}, {}> {
onKeyDown(e : any) {
mobx.action(() => {
let model = GlobalModel;
let win = getActiveWindow();
let win = model.getActiveWindow();
let ctrlMod = e.getModifierState("Control") || e.getModifierState("Meta") || e.getModifierState("Shift");
if (e.code == "Enter" && !ctrlMod) {
e.preventDefault();
@@ -270,6 +272,10 @@ class CmdInput extends React.Component<{windowid : string}, {}> {
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 "";
@@ -292,12 +298,11 @@ class CmdInput extends React.Component<{windowid : string}, {}> {
@boundMethod
doSubmitCmd() {
let {windowid} = this.props;
let model = GlobalModel;
let commandStr = this.getCurLine();
let hitem = {cmdtext: commandStr};
this.clearCurLine();
model.submitCommand(windowid, commandStr);
model.submitCommand(commandStr);
}
render() {
@@ -343,10 +348,9 @@ class SessionView extends React.Component<{}, {}> {
@boundMethod
changeSizeCallback(term : TermWrap) {
if (this.shouldFollow.get()) {
let session = this.props.session;
let window = session.getActiveWindow();
let window = GlobalModel.getActiveWindow();
let lines = window.lines;
if (lines == null) {
if (lines == null || lines.length == 0) {
return;
}
let lastLine = lines[lines.length-1];
@@ -359,7 +363,7 @@ class SessionView extends React.Component<{}, {}> {
let model = GlobalModel;
let win = model.getActiveWindow();
if (win == null) {
return <div className="session-view">(no active window {session.activeWindowId.get()})</div>;
return <div className="session-view">(no active window)</div>;
}
if (!win.linesLoaded.get()) {
return <div className="session-view">(loading)</div>;
@@ -373,7 +377,7 @@ class SessionView extends React.Component<{}, {}> {
<Line key={line.lineid} line={line} changeSizeCallback={this.changeSizeCallback}/>
</For>
</div>
<CmdInput windowid={win.windowid}/>
<CmdInput/>
</div>
);
}
@@ -397,6 +401,7 @@ class MainSideBar extends React.Component<{}, {}> {
render() {
let model = GlobalModel;
let curSessionId = model.curSessionId.get();
let session : Session = null;
return (
<div className={cn("main-sidebar", {"collapsed": this.collapsed.get()})}>
<div className="collapse-container">
@@ -410,12 +415,12 @@ class MainSideBar extends React.Component<{}, {}> {
Private Sessions
</p>
<ul className="menu-list">
<If condition={!model.sessionListLoaded()}>
<If condition={!model.sessionListLoaded.get()}>
<li><a>(loading)</a></li>
</If>
<If condition={model.sessionListLoaded()}>
<If condition={model.sessionListLoaded.get()}>
<For each="session" of={model.sessionList}>
<li key={session.sessionid}><a className={cn({"is-active": curSessionId == session.sessionid})} onClick={() => this.handleSessionClick(session.sessionid)}>#{session.name}</a></li>
<li key={session.sessionId}><a className={cn({"is-active": curSessionId == session.sessionId})} onClick={() => this.handleSessionClick(session.sessionId)}>#{session.name.get()}</a></li>
</For>
<li className="new-session"><a className="new-session"><i className="fa fa-plus"/> New Session</a></li>
</If>
+256 -18
View File
@@ -4,25 +4,71 @@ import {boundMethod} from "autobind-decorator";
import {handleJsonFetchResponse} from "./util";
import {TermWrap} from "./term";
import {v4 as uuidv4} from "uuid";
import type {SessionDataType, WindowDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, CmdDataType, FeCmdPacketType} from "./types";
import type {SessionDataType, WindowDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType} from "./types";
import {WSControl} from "./ws";
type OV<V> = mobx.IObservableValue<V>;
type OArr<V> = mobx.IObservableArray<V>;
function isBlank(s : string) {
return (s == null || s == "");
}
class Cmd {
sessionId : string;
windowId : string;
remoteId : string;
cmdId : string;
data : OV<CmdDataType>;
terminal : any;
termWrap : TermWrap;
ptyPos : number = 0;
atRowMax : boolean = false;
usedRowsUpdated : () => void = null;
watching : boolean = false;
isFocused : OV<boolean> = mobx.observable.box(false, {name: "focus"});
usedRows : OV<number>;
constructor(cmd : CmdDataType) {
constructor(cmd : CmdDataType, windowId : string) {
this.sessionId = cmd.sessionid;
this.windowId = windowId;
this.cmdId = cmd.cmdid;
this.remoteId = cmd.remoteid;
this.data = mobx.observable.box(cmd, {deep: false});
if (cmd.termopts.flexrows) {
this.atRowMax = false;
this.usedRows = mobx.observable.box(2, {name: "usedRows"});
}
else {
this.atRowMax = true;
this.usedRows = mobx.observable.box(cmd.termopts.rows, {name: "usedRows"});
}
}
connectToElem(elem : Element) {
this.termWrap.connectToElem(elem, {
setFocus: this.setFocus.bind(this),
handleKey: this.handleKey.bind(this),
});
}
reloadTerminal(startTail : boolean, delayMs : number) {
if (this.termWrap == null) {
return;
}
this.termWrap.terminal.clear();
let url = sprintf("http://localhost:8080/api/ptyout?sessionid=%s&cmdid=%s", this.sessionId, this.cmdId);
fetch(url).then((resp) => {
if (!resp.ok) {
throw new Error(sprintf("Bad fetch response for /api/ptyout: %d %s", resp.status, resp.statusText));
}
return resp.arrayBuffer()
}).then((buf) => {
setTimeout(() => {
this.ptyPos = 0;
this.updatePtyData(0, new Uint8Array(buf), buf.byteLength);
}, delayMs);
});
}
setCmd(cmd : CmdDataType) {
@@ -31,6 +77,40 @@ class Cmd {
});
}
getStatus() : string {
return this.data.get().status;
}
getTermOpts() : TermOptsType {
return this.data.get().termopts;
}
getCmdStr() : string {
return this.data.get().cmdstr;
}
getRemoteState() : RemoteStateType {
return this.data.get().remotestate;
}
updateUsedRows() {
if (this.atRowMax) {
return;
}
let tur = this.termWrap.getTermUsedRows();
if (tur >= this.termWrap.terminal.rows) {
this.atRowMax = true;
}
if (tur > this.usedRows.get()) {
mobx.action(() => {
let data = this.data.get();
let oldUsedRows = this.usedRows.get();
this.usedRows.set(tur);
GlobalModel.termChangeSize(this.sessionId, this.windowId, this.cmdId, oldUsedRows, tur);
})();
}
}
getSingleLineCmdText() {
let cmdText = this.data.get().cmdstr;
if (cmdText == null) {
@@ -46,9 +126,46 @@ class Cmd {
}
return cmdText;
}
isRunning() : boolean {
let data = this.data.get();
return data.status == "running" || data.status == "detached";
}
updatePtyData(pos : number, data : string | Uint8Array, 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;
this.termWrap.terminal.write(data, () => {
this.updateUsedRows();
});
}
setFocus(focus : boolean) {
mobx.action(() => {
this.isFocused.set(focus);
})();
}
handleKey(event : any) {
console.log("onkey", event);
if (!this.isRunning()) {
return;
}
let data = this.data.get();
let inputPacket = {
type: "input",
ck: this.sessionId + "/" + this.cmdId,
inputdata: btoa(event.key),
remoteid: this.remoteId,
};
GlobalModel.sendInputPacket(inputPacket);
}
};
class Window {
sessionId : string;
windowId : string;
name : OV<string>;
curRemote : OV<string>;
@@ -56,20 +173,47 @@ class Window {
lines : OArr<LineType> = mobx.observable.array([]);
linesLoaded : OV<boolean> = mobx.observable.box(false);
history : any[] = [];
cmds : Record<string, Cmd> = {};
constructor(wdata : WindowDataType) {
this.sessionId = wdata.sessionid;
this.windowId = wdata.windowid;
this.name = mobx.observable.box(wdata.name);
this.curRemote = mobx.observable.box(wdata.curremote);
}
getNumHistoryItems() : number {
return 0;
}
getHistoryItem() : any {
getHistoryItem(hnum : number) : any {
return null
}
updateWindow(win : WindowDataType, isActive : boolean) {
mobx.action(() => {
if (!isBlank(win.name)) {
this.name.set(win.name)
}
if (!isBlank(win.curremote)) {
this.curRemote.set(win.curremote);
}
if (!isActive) {
return;
}
this.linesLoaded.set(true);
this.lines.replace(win.lines || []);
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], this.windowId);
}
})();
}
getCmd(cmdId : string) {
return this.cmds[cmdId];
}
};
class Session {
@@ -92,18 +236,41 @@ class Session {
this.curWindowId = mobx.observable.box((wins.length == 0 ? null : wins[0].windowId));
}
getActiveWindow() : Window {
let cwin = this.curWindowId.get();
if (cwin == null) {
updateWindow(win : WindowDataType, isActive : boolean) {
mobx.action(() => {
for (let i=0; i<this.windows.length; i++) {
let foundWin = this.windows[i];
if (foundWin.windowId != win.windowid) {
continue;
}
if (win.remove) {
this.windows.splice(i, 1);
return;
}
foundWin.updateWindow(win, isActive);
return;
}
let newWindow = new Window(win);
newWindow.updateWindow(win, isActive);
this.windows.push(newWindow);
})();
}
getWindowById(windowId : string) : Window {
if (windowId == null) {
return null;
}
for (let i=0; i<this.windows.length; i++) {
if (this.windows[i].windowId == cwin) {
if (this.windows[i].windowId == windowId) {
return this.windows[i];
}
}
return null;
}
getActiveWindow() : Window {
return this.getWindowById(this.curWindowId.get());
}
}
class Model {
@@ -111,11 +278,13 @@ class Model {
curSessionId : OV<string> = mobx.observable.box(null);
sessionListLoaded : OV<boolean> = mobx.observable.box(false);
sessionList : OArr<Session> = mobx.observable.array([], {name: "SessionList"});
cmds : Record<string, Cmd> = {};
ws : WSControl;
remotes : OArr<RemoteType> = mobx.observable.array([], {deep: false});
remotesLoaded : OV<boolean> = mobx.observable.box(false);
constructor() {
this.clientId = uuidv4();
this.loadRemotes();
this.loadSessionList();
this.ws = new WSControl(this.clientId, this.onMessage.bind(this))
this.ws.reconnect();
@@ -129,12 +298,15 @@ class Model {
}
getActiveSession() : Session {
let sid = this.curSessionId.get();
if (sid == null) {
return this.getSessionById(this.curSessionId.get());
}
getSessionById(sessionId : string) : Session {
if (sessionId == null) {
return null;
}
for (let i=0; i<this.sessionList.length; i++) {
if (this.sessionList[i].sessionId == sid) {
if (this.sessionList[i].sessionId == sessionId) {
return this.sessionList[i];
}
}
@@ -149,11 +321,16 @@ class Model {
return session.getActiveWindow();
}
getCmd(cmdId : string) : Cmd {
return this.cmds[cmdId];
submitCommand(cmdStr : string) {
}
submitCommand(windowId : string, cmdStr : string) {
updateWindow(win : WindowDataType) {
let session = this.getSessionById(win.sessionid);
if (session == null) {
return;
}
let isActive = (win.sessionid == this.curSessionId.get());
session.updateWindow(win, isActive);
}
loadSessionList() {
@@ -174,12 +351,72 @@ class Model {
this.sessionList.replace(slist);
this.sessionListLoaded.set(true)
this.curSessionId.set(defaultSessionId);
let win = this.getActiveWindow();
if (win != null) {
this.loadWindow(win.sessionId, win.windowId);
}
})();
}).catch((err) => {
console.log("error getting session list");
this.errorHandler("getting session list", err);
});
}
loadWindow(sessionId : string, windowId : string) {
let usp = new URLSearchParams({sessionid: sessionId, windowid: windowId});
let url = new URL(sprintf("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");
return;
}
this.updateWindow(data.data);
return;
}).catch((err) => {
this.errorHandler(sprintf("getting window=%s", windowId), err);
});
}
loadRemotes() {
let url = new URL("http://localhost:8080/api/get-remotes");
fetch(url).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => {
mobx.action(() => {
this.remotes.replace(data.data || [])
this.remotesLoaded.set(true);
})();
}).catch((err) => {
this.errorHandler("calling get-remotes", err)
});
}
getRemote(remoteId : string) : RemoteType {
for (let i=0; i<this.remotes.length; i++) {
if (this.remotes[i].remoteid == remoteId) {
return this.remotes[i];
}
}
return null;
}
getCmd(line : LineType) : Cmd {
let session = this.getSessionById(line.sessionid);
if (session == null) {
return null;
}
let window = session.getWindowById(line.windowid);
if (window == null) {
return null;
}
return window.getCmd(line.cmdid);
}
termChangeSize(sessionId : string, windowId : string, cmdId : string, oldUsedRows : number, newUsedRows : number) {
console.log("change-size", sessionId + "/" + windowId + "/" + cmdId, oldUsedRows, "=>", newUsedRows);
}
errorHandler(str : string, err : any) {
console.log("[error]", str, err);
}
sendInputPacket(inputPacket : any) {
this.ws.pushMessage(inputPacket);
}
@@ -191,7 +428,7 @@ if ((window as any).GlobalModal == null) {
}
GlobalModel = (window as any).GlobalModel;
export {Model, Window, GlobalModel, Cmd};
export {Model, Session, Window, GlobalModel, Cmd};
// GlobalWS.registerAndSendGetCmd(getCmdPacket, (dataPacket) => {
@@ -228,3 +465,4 @@ reloadTerminal(startTail : boolean, delayMs : number) {
return this.cmdStatus == "running" || this.cmdStatus == "detached";
}
*/
+1 -1
View File
@@ -156,7 +156,7 @@ class Session {
this.loadWindowLines(windowid);
}
submitCommand(windowid : string, commandStr : string) {
submitCommand(windowId : string, commandStr : string) {
let url = sprintf("http://localhost:8080/api/run-command");
let data : FeCmdPacketType = {type: "fecmd", sessionid: this.sessionId, windowid: windowid, cmdstr: commandStr, userid: GlobalUser, remotestate: null};
let curWindow = this.getCurWindow();
+3
View File
@@ -1,3 +1,4 @@
import * as mobx from "mobx";
import * as React from "react";
import {createRoot} from 'react-dom/client';
import {sprintf} from "sprintf-js";
@@ -17,4 +18,6 @@ document.addEventListener("DOMContentLoaded", () => {
root.render(reactElem);
});
(window as any).mobx = mobx;
console.log("SCRIPTHAUS", VERSION)
+21 -110
View File
@@ -4,6 +4,7 @@ import {sprintf} from "sprintf-js";
import {boundMethod} from "autobind-decorator";
import {v4 as uuidv4} from "uuid";
import {GlobalModel} from "./model";
import type {TermOptsType} from "./types";
function loadPtyOut(term : Terminal, sessionId : string, cmdId : string, delayMs : number, callback?: (number) => void) {
term.clear()
@@ -18,86 +19,35 @@ function loadPtyOut(term : Terminal, sessionId : string, cmdId : string, delayMs
});
}
type TermEventHandler = {
setFocus : (focus : boolean) => void,
handleKey : (event : any) => void,
};
class TermWrap {
terminal : any;
termId : string;
sessionId : string;
cmdId : string;
ptyPos : number = 0;
runPos : number = 0;
runData : string = "";
renderVersion : mobx.IObservableValue<number> = mobx.observable.box(1, {name: "renderVersion"});
isFocused : mobx.IObservableValue<boolean> = mobx.observable.box(false, {name: "focus"});
flexRows : boolean = true;
maxRows : number = 25;
atRowMax : boolean = false;
initialized : boolean = false;
changeSizeCallback : (TermWrap) => void = null;
usedRows : number;
flexRows : boolean;
tailReqId : string;
cmdStatus : string;
remoteId : string;
constructor(sessionId : string, cmdId : string, remoteId : string, status : string) {
this.termId = uuidv4();
this.sessionId = sessionId;
this.cmdId = cmdId;
this.remoteId = remoteId;
this.cmdStatus = status;
this.terminal = new Terminal({rows: 25, cols: 80, theme: {foreground: "#d3d7cf"}});
constructor(termOpts : TermOptsType) {
this.terminal = new Terminal({rows: termOpts.rows, cols: termOpts.cols, theme: {foreground: "#d3d7cf"}});
this.flexRows = termOpts.flexrows;
this.usedRows = 2;
}
destroy() {
}
isRunning() : boolean {
return this.cmdStatus == "running" || this.cmdStatus == "detached";
}
@boundMethod
onKeyHandler(event : any) {
console.log("onkey", event);
if (!this.isRunning()) {
return;
}
let inputPacket = {
type: "input",
ck: this.sessionId + "/" + this.cmdId,
inputdata: btoa(event.key),
remoteid: this.remoteId,
};
GlobalModel.sendInputPacket(inputPacket);
}
// 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;
this.terminal.write(data, () => {
mobx.action(() => {
this.resizeToContent();
this.incRenderVersion();
})();
});
}
resizeToContent() {
if (this.atRowMax) {
return;
}
getTermUsedRows() : number {
let term = this.terminal;
let termBuf = term._core.buffer;
let termNumLines = termBuf.lines.length;
let termYPos = termBuf.y;
let usedRows = 2;
if (termNumLines > term.rows) {
this.usedRows = term.rows;
this.atRowMax = true;
return;
if (termNumLines >= term.rows) {
return term.rows;
}
let usedRows = 2;
if (termYPos >= usedRows) {
usedRows = termYPos + 1;
}
@@ -107,58 +57,19 @@ class TermWrap {
usedRows = i+1;
}
}
if (this.usedRows != usedRows) {
this.usedRows = usedRows;
if (this.changeSizeCallback != null) {
let cb = this.changeSizeCallback;
setTimeout(() => cb(this), 0);
}
}
return;
return usedRows;
}
setSize(rows : number, cols : number, flexRows : boolean) {
this.flexRows = true;
this.maxRows = rows;
if (!flexRows) {
this.terminal.resize(rows, cols);
setTimeout(() => this.incRenderVersion(), 10);
return;
}
this.resizeToContent();
}
getSize() : {rows : number, cols : number} {
return {rows: this.terminal.rows, cols: this.terminal.cols};
}
@boundMethod
setFocus(val : boolean) {
mobx.action(() => this.isFocused.set(val))();
}
getRenderVersion() : number {
return this.renderVersion.get();
}
@boundMethod
incRenderVersion() {
mobx.action(() => this.renderVersion.set(this.renderVersion.get() + 1))();
}
connectToElem(elem : Element) {
connectToElem(elem : Element, eventHandler : TermEventHandler) {
this.terminal.open(elem);
if (this.isRunning()) {
if (eventHandler != null) {
this.terminal.textarea.addEventListener("focus", () => {
this.setFocus(true);
eventHandler.setFocus(true);
});
this.terminal.textarea.addEventListener("blur", () => {
this.setFocus(false);
eventHandler.setFocus(false);
});
this.terminal.onKey(this.onKeyHandler);
}
else {
this.terminal.onKey(this.onKeyHandler);
this.terminal.onKey(eventHandler.handleKey);
}
}
}
+7 -1
View File
@@ -5,6 +5,7 @@ type SessionDataType = {
name : string,
windows : WindowDataType[],
cmds : CmdDataType[],
remove : boolean,
};
type LineType = {
@@ -40,6 +41,7 @@ type RemoteInstanceType = {
remoteid : string,
sessionscope : boolean,
state : RemoteStateType,
version : number,
}
type WindowDataType = {
@@ -48,11 +50,15 @@ type WindowDataType = {
name : string,
curremote : string,
lines : LineType[],
history : HistoryItem[],
cmds : CmdDataType[],
remotes : RemoteInstanceType[],
version : number,
remove : boolean,
};
type HistoryItem = {
cmdtext : string,
cmdstr : string,
};
type CmdRemoteStateType = {