diff --git a/src/emain.ts b/src/emain.ts
index a53e66e0f..c05cb238a 100644
--- a/src/emain.ts
+++ b/src/emain.ts
@@ -4,7 +4,7 @@ import * as path from "path";
import * as fs from "fs";
let app = electron.app;
-app.setAppLogsPath(__dirname, "../logs");
+app.setName("ScriptHaus");
let lock : File;
try {
@@ -14,7 +14,29 @@ catch (e) {
app.exit(0);
}
-console.log("ACQUIRED LOCK");
+let menuTemplate = [
+ {
+ role: "appMenu",
+ },
+ {
+ role: "fileMenu",
+ },
+ {
+ role: "editMenu",
+ },
+ {
+ role: "viewMenu",
+ },
+ {
+ role: "windowMenu",
+ },
+ {
+ role: "help",
+ },
+];
+
+let menu = electron.Menu.buildFromTemplate(menuTemplate);
+electron.Menu.setApplicationMenu(menu);
let MainWindow = null;
@@ -27,12 +49,19 @@ function createWindow() {
},
});
win.loadFile("../static/index.html");
+ win.webContents.on("before-input-event", (e, input) => {
+ if (input.type != "keyDown") {
+ return;
+ }
+ if (input.code == "KeyT" && input.meta) {
+ win.webContents.send("cmt-t");
+ }
+ });
return win;
}
app.whenReady().then(() => {
MainWindow = createWindow();
- MainWindow.webContents.openDevTools();
app.on('activate', () => {
if (electron.BrowserWindow.getAllWindows().length === 0) {
@@ -45,10 +74,8 @@ app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
});
-electron.ipcMain.on("relaunch", (event) => {
- console.log("RELAUNCH!");
- app.relaunch();
- app.exit(0);
- console.log("test", event);
+electron.ipcMain.on("get-id", (event) => {
+ return event.processId;
});
+
diff --git a/src/main.tsx b/src/main.tsx
index 790ea10fe..66d2cbd00 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -7,9 +7,9 @@ import dayjs from 'dayjs'
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
import cn from "classnames"
import {TermWrap} from "./term";
-import {getCurrentSession, getLineId, Session, newSession, getAllSessions, getCurrentSessionId} from "./session";
import type {SessionDataType, LineType, CmdDataType, RemoteType} from "./types";
import localizedFormat from 'dayjs/plugin/localizedFormat';
+import {GlobalMode, Cmd, Window} from "./model";
dayjs.extend(localizedFormat)
@@ -47,7 +47,7 @@ function getLineDateStr(ts : number) : string {
}
@mobxReact.observer
-class LineText extends React.Component<{line : LineType, session : Session}, {}> {
+class LineText extends React.Component<{line : LineType}, {}> {
render() {
let line = this.props.line;
let formattedTime = getLineDateStr(line.ts);
@@ -71,13 +71,14 @@ class LineText extends React.Component<{line : LineType, session : Session}, {}>
}
@mobxReact.observer
-class LineCmd extends React.Component<{line : LineType, session : Session, changeSizeCallback? : (term : TermWrap) => void}, {}> {
+class LineCmd extends React.Component<{line : LineType}, {}> {
constructor(props) {
super(props);
}
componentDidMount() {
- let {session, line} = this.props;
+ let {line} = this.props;
+ let model = GlobalModel;
let termElem = document.getElementById("term-" + getLineId(line));
let termWrap = session.getTermWrapByLine(line);
termWrap.changeSizeCallback = this.props.changeSizeCallback;
@@ -100,22 +101,6 @@ class LineCmd extends React.Component<{line : LineType, session : Session, chang
termWrap.reloadTerminal(true, 500);
}
- @boundMethod
- singleLineCmdText(cmdText : string) {
- if (cmdText == null) {
- return "(none)";
- }
- cmdText = cmdText.trim();
- let nlIdx = cmdText.indexOf("\n");
- if (nlIdx != -1) {
- cmdText = cmdText.substr(0, nlIdx) + "...";
- }
- if (cmdText.length > 80) {
- cmdText = cmdText.substr(0, 77) + "...";
- }
- return cmdText;
- }
-
replaceHomePath(path : string, homeDir : string) : string {
if (path == homeDir) {
return "~";
@@ -126,7 +111,7 @@ class LineCmd extends React.Component<{line : LineType, session : Session, chang
return path;
}
- renderCmdText(cmd : CmdDataType, remote : RemoteType) : any {
+ renderCmdText(cmd : Cmd, remote : RemoteType) : any {
if (cmd == null) {
return (
@@ -230,33 +215,15 @@ class Line extends React.Component<{line : LineType, session : Session, changeSi
}
@mobxReact.observer
-class CmdInput extends React.Component<{session : Session, windowid : string}, {}> {
+class CmdInput extends React.Component<{windowid : string}, {}> {
historyIndex : mobx.IObservableValue
= mobx.observable.box(0, {name: "history-index"});
modHistory : mobx.IObservableArray = mobx.observable.array([""], {name: "mod-history"});
- elistener : any;
-
- componentDidMount() {
- this.elistener = this.handleKeyPress.bind(this);
- document.addEventListener("keypress", this.elistener);
- }
-
- componentWillUnmount() {
- document.removeEventListener("keypress", this.elistener);
- }
-
- handleKeyPress(event : any) {
- if (event.code == "KeyI" && event.metaKey) {
- let elem = document.getElementById("main-cmd-input");
- if (elem != null) {
- elem.focus();
- }
- }
- }
@mobx.action @boundMethod
onKeyDown(e : any) {
mobx.action(() => {
- let {session} = this.props;
+ let model = GlobalModel;
+ let win = getActiveWindow();
let ctrlMod = e.getModifierState("Control") || e.getModifierState("Meta") || e.getModifierState("Shift");
if (e.code == "Enter" && !ctrlMod) {
e.preventDefault();
@@ -267,8 +234,8 @@ class CmdInput extends React.Component<{session : Session, windowid : string}, {
e.preventDefault();
let hidx = this.historyIndex.get();
hidx += 1;
- if (hidx > session.getNumHistoryItems()) {
- hidx = session.getNumHistoryItems();
+ if (hidx > win.getNumHistoryItems()) {
+ hidx = win.getNumHistoryItems();
}
this.historyIndex.set(hidx);
return;
@@ -298,12 +265,12 @@ class CmdInput extends React.Component<{session : Session, windowid : string}, {
@boundMethod
getCurLine() : string {
- let {session} = this.props;
+ let model = GlobalModel;
let hidx = this.historyIndex.get();
if (hidx < this.modHistory.length && this.modHistory[hidx] != null) {
return this.modHistory[hidx];
}
- let hitem = session.getHistoryItem(-hidx);
+ let hitem = win.getHistoryItem(-hidx);
if (hitem == null) {
return "";
}
@@ -325,12 +292,12 @@ class CmdInput extends React.Component<{session : Session, windowid : string}, {
@boundMethod
doSubmitCmd() {
- let {session, windowid} = this.props;
+ let {windowid} = this.props;
+ let model = GlobalModel;
let commandStr = this.getCurLine();
let hitem = {cmdtext: commandStr};
- session.addToHistory(hitem);
this.clearCurLine();
- session.submitCommand(windowid, commandStr);
+ model.submitCommand(windowid, commandStr);
}
render() {
@@ -363,7 +330,7 @@ class CmdInput extends React.Component<{session : Session, windowid : string}, {
}
@mobxReact.observer
-class SessionView extends React.Component<{session : Session}, {}> {
+class SessionView extends React.Component<{}, {}> {
shouldFollow : mobx.IObservableValue = mobx.observable.box(true);
@boundMethod
@@ -389,12 +356,12 @@ class SessionView extends React.Component<{session : Session}, {}> {
}
render() {
- let session = this.props.session;
- let window = session.getActiveWindow();
- if (window == null) {
+ let model = GlobalModel;
+ let win = model.getActiveWindow();
+ if (win == null) {
return (no active window {session.activeWindowId.get()})
;
}
- if (session.loading.get() || window.linesLoading.get()) {
+ if (!win.linesLoaded.get()) {
return (loading)
;
}
let idx = 0;
@@ -402,11 +369,11 @@ class SessionView extends React.Component<{session : Session}, {}> {
return (
);
}
@@ -427,15 +394,9 @@ class MainSideBar extends React.Component<{}, {}> {
console.log("click session", sessionId);
}
- handleReload() {
- console.log("reload");
- window.api.relaunch();
- }
-
render() {
- let curSessionId = getCurrentSessionId();
- let sessions = getAllSessions();
- let session : SessionDataType = null;
+ let model = GlobalModel;
+ let curSessionId = model.curSessionId.get();
return (
@@ -449,10 +410,15 @@ class MainSideBar extends React.Component<{}, {}> {
Private Sessions
Shared Sessions
@@ -499,9 +465,6 @@ class MainSideBar extends React.Component<{}, {}> {
mike@test01.ec2
root@app01.ec2
-
- Relaunch
-
@@ -516,7 +479,6 @@ class Main extends React.Component<{}, {}> {
}
render() {
- let session = getCurrentSession();
return (
@@ -525,7 +487,7 @@ class Main extends React.Component<{}, {}> {
-
+
);
diff --git a/src/model.ts b/src/model.ts
new file mode 100644
index 000000000..4037b4aa3
--- /dev/null
+++ b/src/model.ts
@@ -0,0 +1,230 @@
+import * as mobx from "mobx";
+import {sprintf} from "sprintf-js";
+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 {WSControl} from "./ws";
+
+type OV = mobx.IObservableValue;
+type OArr = mobx.IObservableArray;
+
+class Cmd {
+ cmdId : string;
+ data : OV;
+
+ terminal : any;
+ ptyPos : number = 0;
+ atRowMax : boolean = false;
+ usedRowsUpdated : () => void = null;
+ watching : boolean = false;
+
+ constructor(cmd : CmdDataType) {
+ this.cmdId = cmd.cmdid;
+ this.data = mobx.observable.box(cmd, {deep: false});
+ }
+
+ setCmd(cmd : CmdDataType) {
+ mobx.action(() => {
+ this.data.set(cmd);
+ });
+ }
+
+ getSingleLineCmdText() {
+ let cmdText = this.data.get().cmdstr;
+ if (cmdText == null) {
+ return "(none)";
+ }
+ cmdText = cmdText.trim();
+ let nlIdx = cmdText.indexOf("\n");
+ if (nlIdx != -1) {
+ cmdText = cmdText.substr(0, nlIdx) + "...";
+ }
+ if (cmdText.length > 80) {
+ cmdText = cmdText.substr(0, 77) + "...";
+ }
+ return cmdText;
+ }
+};
+
+class Window {
+ windowId : string;
+ name : OV;
+ curRemote : OV;
+ loaded : OV = mobx.observable.box(false);
+ lines : OArr = mobx.observable.array([]);
+ linesLoaded : OV = mobx.observable.box(false);
+ history : any[] = [];
+
+ constructor(wdata : WindowDataType) {
+ this.windowId = wdata.windowid;
+ this.name = mobx.observable.box(wdata.name);
+ this.curRemote = mobx.observable.box(wdata.curremote);
+ }
+
+ getNumHistoryItems() : number {
+ return 0;
+ }
+
+ getHistoryItem() : any {
+ return null
+ }
+};
+
+class Session {
+ sessionId : string;
+ name : OV;
+ curWindowId : OV;
+ windows : OArr;
+ notifyNum : OV = mobx.observable.box(0);
+
+ constructor(sdata : SessionDataType) {
+ this.sessionId = sdata.sessionid;
+ this.name = mobx.observable.box(sdata.name);
+ let winData = sdata.windows || [];
+ let wins : Window[] = [];
+ for (let i=0; i = mobx.observable.box(null);
+ sessionListLoaded : OV = mobx.observable.box(false);
+ sessionList : OArr = mobx.observable.array([], {name: "SessionList"});
+ cmds : Record = {};
+ ws : WSControl;
+
+ constructor() {
+ this.clientId = uuidv4();
+ this.loadSessionList();
+ this.ws = new WSControl(this.clientId, this.onMessage.bind(this))
+ this.ws.reconnect();
+ }
+
+ isConnected() : boolean {
+ return this.ws.open.get();
+ }
+
+ onMessage(message : any) {
+ }
+
+ getActiveSession() : Session {
+ let sid = this.curSessionId.get();
+ if (sid == null) {
+ return null;
+ }
+ for (let i=0; i handleJsonFetchResponse(url, resp)).then((data) => {
+ mobx.action(() => {
+ let sdatalist : SessionDataType[] = data.data || [];
+ let slist : Session[] = [];
+ let defaultSessionId = null;
+ for (let i=0; i {
+ console.log("error getting session list");
+ });
+ }
+
+ sendInputPacket(inputPacket : any) {
+ this.ws.pushMessage(inputPacket);
+ }
+}
+
+let GlobalModel : Model = null;
+if ((window as any).GlobalModal == null) {
+ (window as any).GlobalModel = new Model();
+}
+GlobalModel = (window as any).GlobalModel;
+
+export {Model, Window, GlobalModel, Cmd};
+
+
+// GlobalWS.registerAndSendGetCmd(getCmdPacket, (dataPacket) => {
+// let realData = atob(dataPacket.ptydata64);
+// this.updatePtyData(this.ptyPos, realData, dataPacket.ptydatalen);
+// });
+
+
+/*
+reloadTerminal(startTail : boolean, delayMs : number) {
+ loadPtyOut(this.terminal, this.sessionId, this.cmdId, delayMs, (ptyoutLen) => {
+ mobx.action(() => {
+ this.incRenderVersion();
+ this.ptyPos = ptyoutLen;
+ })();
+ if (startTail) {
+ this.startPtyTail();
+ }
+ });
+}
+
+ setCmdStatus(status : string) {
+ if (this.cmdStatus == status) {
+ return;
+ }
+ this.cmdStatus = status;
+ if (!this.isRunning() && this.tailReqId) {
+ this.stopPtyTail();
+ }
+ }
+}
+
+ isRunning() : boolean {
+ return this.cmdStatus == "running" || this.cmdStatus == "detached";
+ }
+*/
diff --git a/src/preload.js b/src/preload.js
index f0f949d11..942150cdf 100644
--- a/src/preload.js
+++ b/src/preload.js
@@ -3,5 +3,6 @@ let {contextBridge, ipcRenderer} = require("electron");
console.log("RUNNING PRELOAD");
contextBridge.exposeInMainWorld("api", {
- relaunch: () => ipcRenderer.send("relaunch"),
+ getId: () => ipcRenderer.sendSync("get-id"),
+ onCmdT: (callback) => ipcRenderer.on("cmd-t"),
});
diff --git a/src/sh2.ts b/src/sh2.ts
index ad0fdc801..c59c4ebfc 100644
--- a/src/sh2.ts
+++ b/src/sh2.ts
@@ -3,18 +3,14 @@ import {createRoot} from 'react-dom/client';
import {sprintf} from "sprintf-js";
import {Terminal} from 'xterm';
import {Main} from "./main";
-import {GlobalWS} from "./ws";
+import {WSControl} from "./ws";
+import {GlobalModel} from "./model";
import {v4 as uuidv4} from "uuid";
-import {loadSessionList} from "./session";
// @ts-ignore
let VERSION = __SHVERSION__;
-(window as any).ScriptHausClientId = uuidv4();
-
document.addEventListener("DOMContentLoaded", () => {
- loadSessionList(true);
- GlobalWS.reconnect();
let reactElem = React.createElement(Main, null, null);
let elem = document.getElementById("app");
let root = createRoot(elem);
diff --git a/src/term.ts b/src/term.ts
index cf8062614..063fa10ff 100644
--- a/src/term.ts
+++ b/src/term.ts
@@ -2,8 +2,8 @@ import * as mobx from "mobx";
import {Terminal} from 'xterm';
import {sprintf} from "sprintf-js";
import {boundMethod} from "autobind-decorator";
-import {GlobalWS} from "./ws";
import {v4 as uuidv4} from "uuid";
+import {GlobalModel} from "./model";
function loadPtyOut(term : Terminal, sessionId : string, cmdId : string, delayMs : number, callback?: (number) => void) {
term.clear()
@@ -49,17 +49,6 @@ class TermWrap {
}
destroy() {
- this.stopPtyTail();
- }
-
- setCmdStatus(status : string) {
- if (this.cmdStatus == status) {
- return;
- }
- this.cmdStatus = status;
- if (!this.isRunning() && this.tailReqId) {
- this.stopPtyTail();
- }
}
isRunning() : boolean {
@@ -68,49 +57,17 @@ class TermWrap {
@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,
};
- GlobalWS.pushMessage(inputPacket);
- }
-
- stopPtyTail() {
- if (this.tailReqId == null) {
- return;
- }
- let untailCmdPacket = {
- type: "untailcmd",
- reqid: uuidv4(),
- ck: this.sessionId + "/" + this.cmdId,
- };
- GlobalWS.sendMessage(untailCmdPacket);
- GlobalWS.unregisterReq(this.tailReqId);
- this.tailReqId = null;
- }
-
- startPtyTail() {
- if (this.tailReqId != null) {
- return;
- }
- if (!this.isRunning()) {
- return;
- }
- this.tailReqId = uuidv4();
- let getCmdPacket = {
- type: "getcmd",
- reqid: this.tailReqId,
- ck: this.sessionId + "/" + this.cmdId,
- ptypos: this.ptyPos,
- tail: true,
- ptyonly: true,
- };
- GlobalWS.registerAndSendGetCmd(getCmdPacket, (dataPacket) => {
- let realData = atob(dataPacket.ptydata64);
- this.updatePtyData(this.ptyPos, realData, dataPacket.ptydatalen);
- });
+ GlobalModel.sendInputPacket(inputPacket);
}
// datalen is passed because data could be utf-8 and data.length is not the actual *byte* length
@@ -189,18 +146,6 @@ class TermWrap {
mobx.action(() => this.renderVersion.set(this.renderVersion.get() + 1))();
}
- reloadTerminal(startTail : boolean, delayMs : number) {
- loadPtyOut(this.terminal, this.sessionId, this.cmdId, delayMs, (ptyoutLen) => {
- mobx.action(() => {
- this.incRenderVersion();
- this.ptyPos = ptyoutLen;
- })();
- if (startTail) {
- this.startPtyTail();
- }
- });
- }
-
connectToElem(elem : Element) {
this.terminal.open(elem);
if (this.isRunning()) {
@@ -212,6 +157,9 @@ class TermWrap {
});
this.terminal.onKey(this.onKeyHandler);
}
+ else {
+ this.terminal.onKey(this.onKeyHandler);
+ }
}
}
diff --git a/src/types.ts b/src/types.ts
index 0a9ed3f26..37a42ed2a 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -104,6 +104,7 @@ type CmdDataType = {
startpk : CmdStartPacketType,
donepk : CmdDonePacketType,
runout : any[],
+ usedrows : number,
};
export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, WindowDataType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDonePacketType, CmdDataType};
diff --git a/src/ws.ts b/src/ws.ts
index f0d2f4e35..17d46610a 100644
--- a/src/ws.ts
+++ b/src/ws.ts
@@ -2,17 +2,18 @@ import * as mobx from "mobx";
import {sprintf} from "sprintf-js";
import {boundMethod} from "autobind-decorator";
-declare var window : any;
-
class WSControl {
wsConn : any;
open : mobx.IObservableValue;
opening : boolean = false;
reconnectTimes : number = 0;
msgQueue : any[] = [];
- reqMap : Record void> = {};
+ clientId : string;
+ messageCallback : (any) => void = null;
- constructor() {
+ constructor(clientId : string, messageCallback : (any) => void) {
+ this.messageCallback = messageCallback;
+ this.clientId = clientId;
this.open = mobx.observable.box(false, {name: "WSOpen"});
setInterval(this.sendPing, 5000);
}
@@ -22,20 +23,9 @@ class WSControl {
this.open.set(val);
}
- registerAndSendGetCmd(pk : any, callback : (dataPacket : any) => void) {
- if (pk.reqid) {
- this.reqMap[pk.reqid] = callback;
- }
- this.pushMessage(pk)
- }
-
- unregisterReq(reqid : string) {
- delete this.reqMap[reqid];
- }
-
reconnect() {
if (this.open.get()) {
- this.wsConn.close();
+ this.wsConn.close(); // this will force a reconnect
return;
}
this.reconnectTimes++;
@@ -54,7 +44,7 @@ class WSControl {
setTimeout(() => {
console.log(sprintf("websocket reconnect(%d)", this.reconnectTimes));
this.opening = true;
- this.wsConn = new WebSocket("ws://localhost:8081/ws?clientid=" + window.ScriptHausClientId);
+ this.wsConn = new WebSocket("ws://localhost:8081/ws?clientid=" + this.clientId);
this.wsConn.onopen = this.onopen;
this.wsConn.onmessage = this.onmessage;
this.wsConn.onerror = this.onerror;
@@ -126,16 +116,9 @@ class WSControl {
this.reconnectTimes = 0;
return;
}
- if (eventData.type == "cmddata") {
- let cb = this.reqMap[eventData.respid];
- if (!cb) {
- console.log(sprintf("websocket cmddata req=%s -- no callback", eventData.respid));
- return;
- }
- cb(eventData);
- return;
+ if (this.messageCallback) {
+ this.messageCallback(eventData);
}
- console.log("websocket message", eventData);
}
@boundMethod
@@ -162,10 +145,4 @@ class WSControl {
}
}
-var GlobalWS : WSControl;
-if (window.GlobalWS == null) {
- GlobalWS = new WSControl();
- window.GlobalWS = GlobalWS;
-}
-
-export {GlobalWS};
+export {WSControl};