diff --git a/src/main.tsx b/src/main.tsx index d9e160e2..c0d2e41f 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -10,7 +10,7 @@ import cn from "classnames" import {TermWrap} from "./term"; import type {SessionDataType, LineType, CmdDataType, RemoteType} from "./types"; import localizedFormat from 'dayjs/plugin/localizedFormat'; -import {GlobalModel, Session, Cmd, Window, Screen, ScreenWindow} from "./model"; +import {GlobalModel, GlobalInput, Session, Cmd, Window, Screen, ScreenWindow} from "./model"; dayjs.extend(localizedFormat) @@ -305,7 +305,7 @@ class CmdInput extends React.Component<{}, {}> { let commandStr = this.getCurLine(); let hitem = {cmdtext: commandStr}; this.clearCurLine(); - model.submitCommand(commandStr); + model.submitRawCommand(commandStr); } render() { @@ -543,7 +543,7 @@ class ScreenTabs extends React.Component<{session : Session}, {}> { @boundMethod handleNewScreen() { let {session} = this.props; - GlobalModel.createNewScreen(session, null, true); + GlobalInput.createNewScreen(); } @boundMethod @@ -559,7 +559,7 @@ class ScreenTabs extends React.Component<{session : Session}, {}> { if (screen == null) { return; } - GlobalModel.submitCommand(sprintf("/s %s", screenId)); + GlobalInput.switchScreen(screenId); } render() { @@ -572,7 +572,7 @@ class ScreenTabs extends React.Component<{session : Session}, {}> { return (
-
this.handleSwitchScreen(screen.screenId, index+1)}> +
this.handleSwitchScreen(screen.screenId)}> {screen.name.get()}
⌘{index+1}
diff --git a/src/model.ts b/src/model.ts index c4c25ffd..41cfd1e9 100644 --- a/src/model.ts +++ b/src/model.ts @@ -4,7 +4,7 @@ import {boundMethod} from "autobind-decorator"; import {handleJsonFetchResponse, base64ToArray, genMergeData} 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} from "./types"; +import type {SessionDataType, WindowDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenWindowType, ScreenOptsType, LayoutType, PtyDataUpdateType, SessionUpdateType, WindowUpdateType, UpdateMessage} from "./types"; import {WSControl} from "./ws"; var GlobalUser = "sawka"; @@ -505,7 +505,7 @@ class Model { this.clientId = getApi().getId(); this.loadRemotes(); this.loadSessionList(); - this.ws = new WSControl(this.clientId, this.onWSMessage.bind(this)) + this.ws = new WSControl(this.clientId, this.runUpdate.bind(this)) this.ws.reconnect(); getApi().onTCmd(this.onTCmd.bind(this)); getApi().onICmd(this.onICmd.bind(this)); @@ -515,6 +515,7 @@ class Model { onTCmd(mods : KeyModsType) { console.log("got cmd-t", mods); + GlobalInput.createNewScreen(); } onICmd(mods : KeyModsType) { @@ -534,21 +535,21 @@ class Model { if (newScreenId == null) { return; } - this.submitCommand(sprintf("/s %s", newScreenId)); + GlobalInput.switchScreen(newScreenId); } onDigitCmd(e : any, arg : {digit: number}, mods : KeyModsType) { console.log("switch screen (digit)", arg, mods); - this.submitCommand(sprintf("/s %d", arg.digit)); + GlobalInput.switchScreen(String(arg.digit)); } isConnected() : boolean { return this.ws.open.get(); } - onWSMessage(message : any) { - if ("ptydata64" in message) { - let ptyMsg : PtyDataUpdateType = message; + runUpdate(update : UpdateMessage) { + if ("ptydata64" in update) { + let ptyMsg : PtyDataUpdateType = update; let activeScreen = this.getActiveScreen(); if (!activeScreen || activeScreen.sessionId != ptyMsg.sessionid) { return; @@ -556,8 +557,8 @@ class Model { activeScreen.updatePtyData(ptyMsg); return; } - if ("sessions" in message) { - let sessionUpdateMsg : SessionUpdateType = message; + if ("sessions" in update) { + let sessionUpdateMsg : SessionUpdateType = update; mobx.action(() => { let oldActiveScreen = this.getActiveScreen(); genMergeData(this.sessionList, sessionUpdateMsg.sessions, (s : Session) => s.sessionId, (sdata : SessionDataType) => sdata.sessionid, (sdata : SessionDataType) => new Session(sdata), (s : Session) => s.sessionIdx.get()); @@ -573,7 +574,7 @@ class Model { })(); } - console.log("ws-message", message); + console.log("run-update>", update); } removeSession(sessionId : string) { @@ -664,35 +665,27 @@ class Model { win.addLineCmd(line, cmd, interactive); } - submitCommand(cmdStr : string) { - console.log("submit-command>", cmdStr); + getClientKwargs() : Record { + let session = this.getActiveSession(); let win = this.getActiveWindow(); - if (win == null) { - this.errorHandler("cannot submit command, no active window", null) - return; - } let screen = this.getActiveScreen(); - if (screen == null) { - this.errorHandler("cannot submit command, no active screen", null) - return; + let rtn : Record = {}; + if (session != null) { + rtn.session = session.sessionId; } - let data : FeCmdPacketType = { - type: "fecmd", - sessionid: win.sessionId, - screenid: screen.screenId, - windowid: win.windowId, - cmdstr: cmdStr, - userid: GlobalUser, - remotestate: null - }; - let rstate = win.getCurRemoteInstance(); - if (rstate == null) { - this.errorHandler("cannot submit command, no remote state found", null); - return; + if (screen != null) { + rtn.screen = screen.screenId; } - data.remotestate = {remoteid: rstate.remoteid, remotename: rstate.name, ...rstate.state}; + if (win != null) { + rtn.window = win.windowId; + rtn.remote = win.curRemote.get(); + } + return rtn; + } + + submitCommandPacket(cmdPk : FeCmdPacketType) { let url = sprintf("http://localhost:8080/api/run-command"); - fetch(url, {method: "post", body: JSON.stringify(data)}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => { + fetch(url, {method: "post", body: JSON.stringify(cmdPk)}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => { mobx.action(() => { if (data.data != null && data.data.line != null) { this.addLineCmd(data.data.line, data.data.cmd, true); @@ -703,6 +696,27 @@ class Model { }); } + submitCommand(metaCmd : string, metaSubCmd : string, args : string[], kwargs : Record) { + let pk : FeCmdPacketType = { + type: "fecmd", + metacmd: metaCmd, + metasubcmd: metaSubCmd, + args: args, + kwargs: Object.assign({}, this.getClientKwargs(), kwargs), + }; + this.submitCommandPacket(pk); + } + + submitRawCommand(cmdStr : string) { + let pk : FeCmdPacketType = { + type: "fecmd", + metacmd: "eval", + args: [cmdStr], + kwargs: this.getClientKwargs(), + }; + this.submitCommandPacket(pk) + } + loadSessionList() { let url = new URL("http://localhost:8080/api/get-all-sessions"); fetch(url).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => { @@ -751,23 +765,6 @@ class Model { this.ws.pushMessage({type: "watchscreen", sessionid: curScreen.sessionId, screenid: curScreen.screenId}); } - createNewScreen(session : Session, name : string, activate : boolean) { - let params : Record = {sessionid: session.sessionId}; - if (name != null) { - params.name = name; - } - if (activate) { - params.activate = "1"; - } - let usp = new URLSearchParams(params); - let url = new URL("http://localhost:8080/api/create-screen?" + usp.toString()); - fetch(url).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => { - console.log("created screen", data.data); - }).catch((err) => { - this.errorHandler(sprintf("creating screen session=%s", session.sessionId), err); - }); - } - loadWindow(sessionId : string, windowId : string) : Window { let newWin = new Window(sessionId, windowId); this.windows.set(sessionId + "/" + windowId, newWin); @@ -837,12 +834,32 @@ class Model { } } +class InputClass { + constructor() { + } + + switchScreen(screen : string) { + GlobalModel.submitCommand("screen", null, [screen], null); + } + + createNewScreen() { + GlobalModel.submitCommand("screen", "open", null, null); + } + + closeScreen(screen : string) { + GlobalModel.submitCommand("screen", "close", [screen], null); + } +}; + let GlobalModel : Model = null; +let GlobalInput : InputClass = null; if ((window as any).GlobalModal == null) { (window as any).GlobalModel = new Model(); + (window as any).GlobalInput = new InputClass(); } GlobalModel = (window as any).GlobalModel; +GlobalInput = (window as any).GlobalInput; -export {Model, Session, Window, GlobalModel, Cmd, Screen, ScreenWindow}; +export {Model, Session, Window, GlobalModel, GlobalInput, Cmd, Screen, ScreenWindow}; diff --git a/src/types.ts b/src/types.ts index 381885ef..a838981f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -114,12 +114,10 @@ type CmdRemoteStateType = { type FeCmdPacketType = { type : string, - sessionid : string, - screenid : string, - windowid : string, - userid : string, - cmdstr : string, - remotestate : CmdRemoteStateType, + metacmd : string, + metasubcmd? : string, + args : string[], + kwargs : Record; }; type WatchScreenPacketType = { @@ -177,9 +175,11 @@ type SessionUpdateType = { sessions: SessionDataType[], }; +type UpdateMessage = PtyDataUpdateType | SessionUpdateType; + 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}; +export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, WindowDataType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDonePacketType, CmdDataType, ScreenDataType, ScreenOptsType, ScreenWindowType, LayoutType, PtyDataUpdateType, SessionUpdateType, WindowUpdateType, UpdateMessage}; diff --git a/src/util.ts b/src/util.ts index bfb7306b..d9f89cf3 100644 --- a/src/util.ts +++ b/src/util.ts @@ -5,14 +5,19 @@ function fetchJsonData(resp : any, ctErr : boolean) : Promise { let contentType = resp.headers.get("Content-Type"); if (contentType != null && contentType.startsWith("application/json")) { return resp.text().then((textData) => { + let rtnData : any = null; try { - return JSON.parse(textData); + rtnData = JSON.parse(textData); } catch (err) { let errMsg = sprintf("Unparseable JSON: " + err.message); let rtnErr = new Error(errMsg); throw rtnErr; } + if (rtnData != null && rtnData.error) { + throw new Error(rtnData.error); + } + return rtnData; }); } if (ctErr) {