diff --git a/src/main.tsx b/src/main.tsx index 737e0faf5..1ce310d52 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -9,7 +9,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} from "./model"; +import {GlobalModel, Session, Cmd, Window, Screen, ScreenWindow} from "./model"; dayjs.extend(localizedFormat) @@ -340,7 +340,7 @@ class CmdInput extends React.Component<{}, {}> { } @mobxReact.observer -class WindowView extends React.Component<{windowId : string}, {}> { +class ScreenWindowView extends React.Component<{sw : ScreenWindow}, {}> { mutObs : any; scrollToBottom() { @@ -352,11 +352,11 @@ class WindowView extends React.Component<{windowId : string}, {}> { @boundMethod scrollHandler(event : any) { + let {sw} = this.props; let target = event.target; let atBottom = (target.scrollTop + 30 > (target.scrollHeight - target.offsetHeight)); - let win = this.getWindow(); - if (win.shouldFollow.get() != atBottom) { - mobx.action(() => win.shouldFollow.set(atBottom)); + if (sw && sw.shouldFollow.get() != atBottom) { + mobx.action(() => sw.shouldFollow.set(atBottom)); } // console.log("scroll-handler>", atBottom, target.scrollTop, target.scrollHeight); } @@ -376,42 +376,37 @@ class WindowView extends React.Component<{windowId : string}, {}> { } handleDomMutation(mutations, mutObs) { - let win = this.getWindow(); - if (win && win.shouldFollow.get()) { + let {sw} = this.props; + if (sw && sw.shouldFollow.get()) { setTimeout(() => this.scrollToBottom(), 0); } } getWindow() : Window { - let {windowId} = this.props; - if (windowId == null) { - return null; - } - let model = GlobalModel; - let session = model.getActiveSession(); - if (session == null) { - return null; - } - let win = session.getWindowById(windowId); - return win; + let {sw} = this.props; + return GlobalModel.getWindowById(sw.sessionId, sw.windowId); } getLinesId() { - let {windowId} = this.props; - return "window-lines-" + windowId; + let {sw} = this.props; + return "window-lines-" + sw.windowId; } @boundMethod handleTermResize(e : any) { - let win = this.getWindow(); - if (win && win.shouldFollow.get()) { + let {sw} = this.props; + if (sw && sw.shouldFollow.get()) { setTimeout(() => this.scrollToBottom(), 0); } } + getWindowViewStyle() : any { + return {width: "100%", height: "100%"}; + } + renderError(message : string) { return ( -
+
{message}
@@ -420,17 +415,18 @@ class WindowView extends React.Component<{windowId : string}, {}> { } render() { - let win = this.getWindow(); - if (win == null) { - return this.renderError("(no window)"); + let {sw} = this.props; + if (sw == null) { + return this.renderError("(no screen window)"); } + let win = this.getWindow(); if (!win.linesLoaded.get()) { return this.renderError("(loading)"); } let idx = 0; let line : LineType = null; return ( -
+
@@ -441,6 +437,42 @@ class WindowView extends React.Component<{windowId : string}, {}> { } } +@mobxReact.observer +class ScreenView extends React.Component<{screen : Screen}, {}> { + render() { + let {screen} = this.props; + if (screen == null) { + return ( +
+ (no screen) +
+ ); + } + let sw = screen.getActiveSW(); + return ( +
+ +
+ ); + } +} + +@mobxReact.observer +class ScreenTabs extends React.Component<{}, {}> { + render() { + let model = GlobalModel; + let session = model.getActiveSession(); + if (session == null) { + return null; + } + return ( +
+ tabs! +
+ ); + } +} + @mobxReact.observer class SessionView extends React.Component<{}, {}> { render() { @@ -449,10 +481,11 @@ class SessionView extends React.Component<{}, {}> { if (session == null) { return
(no active session)
; } - let curWindowId = session.curWindowId.get(); + let activeScreen = session.getActiveScreen(); return (
- + +
); diff --git a/src/model.ts b/src/model.ts index f48f45028..c3e139b88 100644 --- a/src/model.ts +++ b/src/model.ts @@ -4,7 +4,7 @@ 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, TermOptsType, RemoteStateType} from "./types"; +import type {SessionDataType, WindowDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenWindowType, ScreenOptsType, LayoutType} from "./types"; import {WSControl} from "./ws"; var GlobalUser = "sawka"; @@ -26,6 +26,14 @@ function getApi() : ElectronApi { return (window as any).api; } +// clean empty string +function ces(s : string) { + if (s == "") { + return null; + } + return s; +} + class Cmd { sessionId : string; windowId : string; @@ -199,23 +207,90 @@ class Cmd { } }; +class Screen { + sessionId : string; + screenId : string; + opts : OV; + name : OV; + activeWindowId : OV; + windows : OArr; + + constructor(sdata : ScreenDataType) { + this.sessionId = sdata.sessionid; + this.screenId = sdata.screenid; + this.name = mobx.observable.box(sdata.name); + this.opts = mobx.observable.box(sdata.screenopts); + this.activeWindowId = mobx.observable.box(ces(sdata.activewindowid)); + let swArr : ScreenWindow[] = []; + let wins = sdata.windows || []; + for (let i=0; i; + layout : OV; + shouldFollow : OV = mobx.observable.box(true); + + constructor(swdata : ScreenWindowType) { + this.sessionId = swdata.sessionid; + this.screenId = swdata.screenid; + this.windowId = swdata.windowid; + this.name = mobx.observable.box(swdata.name); + this.layout = mobx.observable.box(swdata.layout); + } + + getWindow() : Window { + return GlobalModel.getWindowById(this.sessionId, this.windowId); + } +} + + class Window { sessionId : string; windowId : string; - name : OV; curRemote : OV; loaded : OV = mobx.observable.box(false); - lines : OArr = mobx.observable.array([]); + lines : OArr = mobx.observable.array([], {deep: false}); linesLoaded : OV = mobx.observable.box(false); history : any[] = []; cmds : Record = {}; - shouldFollow : OV = mobx.observable.box(true); remoteInstances : OArr = mobx.observable.array([]); 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); } @@ -229,9 +304,6 @@ class Window { 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); } @@ -277,7 +349,7 @@ class Window { let remote = GlobalModel.getRemoteByName(rname); if (remote != null) { return {riid: "", sessionid: this.sessionId, windowid: this.windowId, remoteid: remote.remoteid, - name: rname, state: remote.defaultstate, sessionscope: false, version: 0}; + name: rname, state: remote.defaultstate, sessionscope: false}; } return null; } @@ -314,7 +386,8 @@ class Window { class Session { sessionId : string; name : OV; - curWindowId : OV; + activeScreenId : OV; + screens : OArr; windows : OArr; notifyNum : OV = mobx.observable.box(0); remoteInstances : OArr = mobx.observable.array([]); @@ -328,8 +401,15 @@ class Session { let win = new Window(winData[i]); wins.push(win); } - this.windows = mobx.observable.array(wins); - this.curWindowId = mobx.observable.box((wins.length == 0 ? null : wins[0].windowId)); + this.windows = mobx.observable.array(wins, {deep: false}); + let screenData = sdata.screens || []; + let screens : Screen[] = []; + for (let i=0; i = mobx.observable.box(null); sessionListLoaded : OV = mobx.observable.box(false); - sessionList : OArr = mobx.observable.array([], {name: "SessionList"}); + sessionList : OArr = mobx.observable.array([], {name: "SessionList", deep: false}); ws : WSControl; remotes : OArr = mobx.observable.array([], {deep: false}); remotesLoaded : OV = mobx.observable.box(false); @@ -442,12 +534,28 @@ class Model { return null; } + getWindowById(sessionId : string, windowId : string) : Window { + let session = this.getSessionById(sessionId); + if (session == null) { + return null; + } + return session.getWindowById(windowId); + } + getActiveWindow() : Window { + let screen = this.getActiveScreen(); + if (screen == null) { + return null; + } + return screen.getActiveWindow(); + } + + getActiveScreen() : Screen { let session = this.getActiveSession(); if (session == null) { return null; } - return session.getActiveWindow(); + return session.getActiveScreen(); } addLineCmd(line : LineType, cmd : CmdDataType, interactive : boolean) { @@ -592,6 +700,6 @@ if ((window as any).GlobalModal == null) { } GlobalModel = (window as any).GlobalModel; -export {Model, Session, Window, GlobalModel, Cmd}; +export {Model, Session, Window, GlobalModel, Cmd, Screen, ScreenWindow}; diff --git a/src/sh2.less b/src/sh2.less index bc5a99728..90d7a8fc9 100644 --- a/src/sh2.less +++ b/src/sh2.less @@ -17,12 +17,20 @@ html, body, #main { flex-grow: 1; display: flex; flex-direction: column; + } - .window-view { - flex-grow: 1; - display: flex; - flex-direction: column; - } + .screen-tabs { + height: 50px; + background-color: red; + } + + .screen-view { + flex-grow: 1; + } + + .window-view { + display: flex; + flex-direction: column; } } } diff --git a/src/types.ts b/src/types.ts index b9719868f..cba9380cf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,7 +3,10 @@ import * as mobx from "mobx"; type SessionDataType = { sessionid : string, name : string, + activescreenid : string, windows : WindowDataType[], + screens : ScreenDataType[], + screenwindows : ScreenWindowType[], cmds : CmdDataType[], remove : boolean, }; @@ -19,6 +22,41 @@ type LineType = { cmdid : string, }; +type ScreenOptsType = { + tabcolor? : string, +} + +type ScreenDataType = { + sessionid : string, + screenid : string, + screenidx : number, + activewindowid : string, + name : string, + windows : ScreenWindowType[], + screenopts : ScreenOptsType, +}; + +type LayoutType = { + type : string, + parent? : string, + zindex? : number, + float? : boolean, + top? : string, + bottom? : string, + left? : string, + right? : string, + width? : string, + height? : string, +}; + +type ScreenWindowType = { + sessionid : string, + screenid : string, + windowid : string, + name : string, + layout : LayoutType, +}; + type RemoteType = { remotetype : string, remoteid : string, @@ -40,19 +78,16 @@ type RemoteInstanceType = { remoteid : string, sessionscope : boolean, state : RemoteStateType, - version : number, } type WindowDataType = { sessionid : string, windowid : string, - name : string, curremote : string, lines : LineType[], history : HistoryItem[], cmds : CmdDataType[], remotes : RemoteInstanceType[], - version : number, remove : boolean, }; @@ -78,7 +113,7 @@ type FeCmdPacketType = { type TermOptsType = { rows : number, cols : number, - flexrows : boolean, + flexrows? : boolean, }; type CmdStartPacketType = { @@ -112,4 +147,4 @@ type CmdDataType = { usedrows : number, }; -export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, WindowDataType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDonePacketType, CmdDataType}; +export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, WindowDataType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDonePacketType, CmdDataType, ScreenDataType, ScreenOptsType, ScreenWindowType, LayoutType};