2022-06-08 02:25:35 +02:00
|
|
|
import * as React from "react";
|
|
|
|
import * as mobxReact from "mobx-react";
|
|
|
|
import * as mobx from "mobx";
|
|
|
|
import {sprintf} from "sprintf-js";
|
|
|
|
import {boundMethod} from "autobind-decorator";
|
2022-07-05 07:37:45 +02:00
|
|
|
import dayjs from 'dayjs'
|
2022-06-08 02:25:35 +02:00
|
|
|
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
|
|
|
|
import cn from "classnames"
|
2022-06-17 01:34:46 +02:00
|
|
|
import {TermWrap} from "./term";
|
2022-07-09 10:37:19 +02:00
|
|
|
import type {SessionDataType, LineType, CmdDataType, RemoteType} from "./types";
|
2022-06-18 02:54:14 +02:00
|
|
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
2022-07-13 08:29:39 +02:00
|
|
|
import {GlobalModel, Session, Cmd, Window, Screen, ScreenWindow} from "./model";
|
2022-06-18 02:54:14 +02:00
|
|
|
|
|
|
|
dayjs.extend(localizedFormat)
|
2022-06-13 20:12:39 +02:00
|
|
|
|
2022-07-12 02:55:03 +02:00
|
|
|
function getLineId(line : LineType) : string {
|
|
|
|
return sprintf("%s-%s-%s", line.sessionid, line.windowid, line.lineid);
|
|
|
|
}
|
|
|
|
|
2022-06-08 02:25:35 +02:00
|
|
|
@mobxReact.observer
|
|
|
|
class LineMeta extends React.Component<{line : LineType}, {}> {
|
|
|
|
render() {
|
|
|
|
let line = this.props.line;
|
|
|
|
return (
|
|
|
|
<div className="meta">
|
|
|
|
<div className="lineid">{line.lineid}</div>
|
|
|
|
<div className="user">{line.userid}</div>
|
|
|
|
<div className="ts">{dayjs(line.ts).format("hh:mm:ss a")}</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-18 02:54:14 +02:00
|
|
|
function getLineDateStr(ts : number) : string {
|
|
|
|
let lineDate = new Date(ts);
|
|
|
|
let nowDate = new Date();
|
|
|
|
if (nowDate.getFullYear() != lineDate.getFullYear()) {
|
|
|
|
return dayjs(lineDate).format("ddd L LTS");
|
|
|
|
}
|
|
|
|
else if (nowDate.getMonth() != lineDate.getMonth() || nowDate.getDate() != lineDate.getDate()) {
|
|
|
|
let yesterdayDate = (new Date());
|
|
|
|
yesterdayDate.setDate(yesterdayDate.getDate()-1);
|
|
|
|
if (yesterdayDate.getMonth() == lineDate.getMonth() && yesterdayDate.getDate() == lineDate.getDate()) {
|
|
|
|
return "Yesterday " + dayjs(lineDate).format("LTS");;
|
|
|
|
}
|
|
|
|
return dayjs(lineDate).format("ddd L LTS");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return dayjs(ts).format("LTS");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-08 02:25:35 +02:00
|
|
|
@mobxReact.observer
|
2022-07-13 09:44:19 +02:00
|
|
|
class LineText extends React.Component<{sw : ScreenWindow, line : LineType}, {}> {
|
2022-06-08 02:25:35 +02:00
|
|
|
render() {
|
|
|
|
let line = this.props.line;
|
2022-06-18 02:54:14 +02:00
|
|
|
let formattedTime = getLineDateStr(line.ts);
|
2022-06-08 02:25:35 +02:00
|
|
|
return (
|
|
|
|
<div className="line line-text">
|
|
|
|
<div className="avatar">
|
2022-06-17 01:34:46 +02:00
|
|
|
S
|
2022-06-08 02:25:35 +02:00
|
|
|
</div>
|
|
|
|
<div className="line-content">
|
|
|
|
<div className="meta">
|
|
|
|
<div className="user">{line.userid}</div>
|
2022-06-18 02:54:14 +02:00
|
|
|
<div className="ts">{formattedTime}</div>
|
2022-06-08 02:25:35 +02:00
|
|
|
</div>
|
|
|
|
<div className="text">
|
|
|
|
{line.text}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@mobxReact.observer
|
2022-07-13 09:44:19 +02:00
|
|
|
class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType}, {}> {
|
|
|
|
termLoaded : mobx.IObservableValue<boolean> = mobx.observable.box(false);
|
|
|
|
|
2022-06-16 09:31:54 +02:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
}
|
2022-06-08 02:25:35 +02:00
|
|
|
|
|
|
|
componentDidMount() {
|
2022-07-13 09:44:19 +02:00
|
|
|
let {sw, line} = this.props;
|
2022-07-11 23:43:18 +02:00
|
|
|
let model = GlobalModel;
|
2022-07-12 02:55:03 +02:00
|
|
|
let cmd = model.getCmd(line);
|
|
|
|
if (cmd != null) {
|
|
|
|
let termElem = document.getElementById("term-" + getLineId(line));
|
2022-07-13 09:44:19 +02:00
|
|
|
cmd.connectElem(termElem, sw.screenId, sw.windowId);
|
|
|
|
mobx.action(() => this.termLoaded.set(true))();
|
2022-07-12 02:55:03 +02:00
|
|
|
}
|
2022-06-15 01:02:20 +02:00
|
|
|
}
|
|
|
|
|
2022-07-12 07:43:58 +02:00
|
|
|
componentWillUnmount() {
|
2022-07-13 09:44:19 +02:00
|
|
|
let {sw, line} = this.props;
|
2022-07-12 07:43:58 +02:00
|
|
|
let model = GlobalModel;
|
|
|
|
let cmd = model.getCmd(line);
|
|
|
|
if (cmd != null) {
|
2022-07-13 09:44:19 +02:00
|
|
|
cmd.disconnectElem(sw.screenId, sw.windowId);
|
2022-07-12 07:43:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-18 02:54:14 +02:00
|
|
|
scrollIntoView() {
|
|
|
|
let lineElem = document.getElementById("line-" + getLineId(this.props.line));
|
|
|
|
lineElem.scrollIntoView({block: "end"});
|
2022-06-08 02:25:35 +02:00
|
|
|
}
|
2022-06-13 20:12:39 +02:00
|
|
|
|
|
|
|
@boundMethod
|
|
|
|
doRefresh() {
|
2022-07-13 09:44:19 +02:00
|
|
|
let {sw, line} = this.props;
|
2022-07-12 02:55:03 +02:00
|
|
|
let model = GlobalModel;
|
2022-07-13 09:44:19 +02:00
|
|
|
let cmd = model.getCmd(line);
|
2022-07-12 02:55:03 +02:00
|
|
|
if (cmd != null) {
|
2022-07-13 09:44:19 +02:00
|
|
|
let termWrap = cmd.getTermWrap(sw.screenId, sw.windowId);
|
|
|
|
if (termWrap != null) {
|
|
|
|
termWrap.reloadTerminal(500);
|
|
|
|
}
|
2022-07-12 02:55:03 +02:00
|
|
|
}
|
2022-06-15 01:02:20 +02:00
|
|
|
}
|
|
|
|
|
2022-07-07 22:27:44 +02:00
|
|
|
replaceHomePath(path : string, homeDir : string) : string {
|
|
|
|
if (path == homeDir) {
|
|
|
|
return "~";
|
|
|
|
}
|
|
|
|
if (path.startsWith(homeDir + "/")) {
|
|
|
|
return "~" + path.substr(homeDir.length);
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2022-07-11 23:43:18 +02:00
|
|
|
renderCmdText(cmd : Cmd, remote : RemoteType) : any {
|
2022-07-07 22:27:44 +02:00
|
|
|
if (cmd == null) {
|
|
|
|
return (
|
|
|
|
<div className="metapart-mono cmdtext">
|
|
|
|
<span className="term-bright-green">(cmd not found)</span>
|
|
|
|
</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)";
|
2022-07-12 02:55:03 +02:00
|
|
|
let remoteState = cmd.getRemoteState();
|
|
|
|
if (remoteState && remoteState.cwd) {
|
|
|
|
cwd = remoteState.cwd;
|
2022-07-07 22:27:44 +02:00
|
|
|
}
|
|
|
|
if (remote.remotevars.home) {
|
|
|
|
cwd = this.replaceHomePath(cwd, remote.remotevars.home)
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div className="metapart-mono cmdtext">
|
2022-07-12 02:55:03 +02:00
|
|
|
<span className="term-bright-green">[{promptStr} {cwd}]</span> {cmd.getSingleLineCmdText()}
|
2022-07-07 22:27:44 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2022-06-08 02:25:35 +02:00
|
|
|
|
|
|
|
render() {
|
2022-07-13 09:44:19 +02:00
|
|
|
let {sw, line} = this.props;
|
2022-07-12 02:55:03 +02:00
|
|
|
let model = GlobalModel;
|
2022-06-08 02:25:35 +02:00
|
|
|
let lineid = line.lineid.toString();
|
2022-06-18 02:54:14 +02:00
|
|
|
let formattedTime = getLineDateStr(line.ts);
|
2022-07-12 02:55:03 +02:00
|
|
|
let cmd = model.getCmd(line);
|
|
|
|
if (cmd == null) {
|
|
|
|
return <div className="line line-invalid">[cmd not found '{line.cmdid}']</div>;
|
2022-07-07 22:27:44 +02:00
|
|
|
}
|
2022-07-13 09:44:19 +02:00
|
|
|
let termLoaded = this.termLoaded.get();
|
2022-07-13 10:03:17 +02:00
|
|
|
let cellHeightPx = 16;
|
2022-07-13 09:44:19 +02:00
|
|
|
let usedRows = cmd.getUsedRows(sw.screenId, sw.windowId);
|
|
|
|
let totalHeight = cellHeightPx * usedRows;
|
2022-07-12 02:55:03 +02:00
|
|
|
let remote = model.getRemote(cmd.remoteId);
|
|
|
|
let status = cmd.getStatus();
|
|
|
|
let running = (status == "running");
|
|
|
|
let detached = (status == "detached");
|
|
|
|
let termOpts = cmd.getTermOpts();
|
2022-07-13 09:44:19 +02:00
|
|
|
let isFocused = cmd.getIsFocused(sw.screenId, sw.windowId);
|
2022-06-08 02:25:35 +02:00
|
|
|
return (
|
2022-06-18 02:54:14 +02:00
|
|
|
<div className="line line-cmd" id={"line-" + getLineId(line)}>
|
2022-07-08 06:49:15 +02:00
|
|
|
<div className={cn("avatar",{"num4": lineid.length == 4}, {"num5": lineid.length >= 5}, {"running": running}, {"detached": detached})}>
|
2022-06-08 02:25:35 +02:00
|
|
|
{lineid}
|
|
|
|
</div>
|
|
|
|
<div className="line-content">
|
|
|
|
<div className="meta">
|
2022-06-18 02:54:14 +02:00
|
|
|
<div className="user" style={{display: "none"}}>{line.userid}</div>
|
|
|
|
<div className="ts">{formattedTime}</div>
|
|
|
|
</div>
|
|
|
|
<div className="meta">
|
|
|
|
<div className="metapart-mono" style={{display: "none"}}>
|
|
|
|
{line.cmdid}
|
2022-07-12 02:55:03 +02:00
|
|
|
({termOpts.rows}x{termOpts.cols})
|
2022-06-18 02:54:14 +02:00
|
|
|
</div>
|
2022-07-07 22:27:44 +02:00
|
|
|
{this.renderCmdText(cmd, remote)}
|
2022-06-08 02:25:35 +02:00
|
|
|
</div>
|
2022-07-13 09:44:19 +02:00
|
|
|
<div className={cn("terminal-wrapper", {"focus": isFocused})} style={{overflowY: "hidden"}}>
|
2022-06-21 02:49:14 +02:00
|
|
|
<div className="terminal" id={"term-" + getLineId(line)} data-cmdid={line.cmdid} style={{height: totalHeight}}></div>
|
2022-06-08 02:25:35 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2022-06-20 22:03:20 +02:00
|
|
|
<div onClick={this.doRefresh} className="button refresh-button has-background-black is-small">
|
|
|
|
<span className="icon"><i className="fa fa-refresh"/></span>
|
|
|
|
</div>
|
2022-06-08 02:25:35 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@mobxReact.observer
|
2022-07-13 09:44:19 +02:00
|
|
|
class Line extends React.Component<{sw : ScreenWindow, line : LineType}, {}> {
|
2022-06-08 02:25:35 +02:00
|
|
|
render() {
|
|
|
|
let line = this.props.line;
|
|
|
|
if (line.linetype == "text") {
|
|
|
|
return <LineText {...this.props}/>;
|
|
|
|
}
|
|
|
|
if (line.linetype == "cmd") {
|
|
|
|
return <LineCmd {...this.props}/>;
|
|
|
|
}
|
|
|
|
return <div className="line line-invalid">[invalid line type '{line.linetype}']</div>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@mobxReact.observer
|
2022-07-12 02:55:03 +02:00
|
|
|
class CmdInput extends React.Component<{}, {}> {
|
2022-06-21 01:06:37 +02:00
|
|
|
historyIndex : mobx.IObservableValue<number> = mobx.observable.box(0, {name: "history-index"});
|
|
|
|
modHistory : mobx.IObservableArray<string> = mobx.observable.array([""], {name: "mod-history"});
|
2022-06-08 02:25:35 +02:00
|
|
|
|
|
|
|
@mobx.action @boundMethod
|
|
|
|
onKeyDown(e : any) {
|
|
|
|
mobx.action(() => {
|
2022-07-11 23:43:18 +02:00
|
|
|
let model = GlobalModel;
|
2022-07-12 02:55:03 +02:00
|
|
|
let win = model.getActiveWindow();
|
2022-06-08 02:25:35 +02:00
|
|
|
let ctrlMod = e.getModifierState("Control") || e.getModifierState("Meta") || e.getModifierState("Shift");
|
|
|
|
if (e.code == "Enter" && !ctrlMod) {
|
|
|
|
e.preventDefault();
|
2022-06-13 20:12:39 +02:00
|
|
|
setTimeout(() => this.doSubmitCmd(), 0);
|
2022-06-08 02:25:35 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-06-21 01:06:37 +02:00
|
|
|
if (e.code == "ArrowUp") {
|
|
|
|
e.preventDefault();
|
|
|
|
let hidx = this.historyIndex.get();
|
|
|
|
hidx += 1;
|
2022-07-11 23:43:18 +02:00
|
|
|
if (hidx > win.getNumHistoryItems()) {
|
|
|
|
hidx = win.getNumHistoryItems();
|
2022-06-21 01:06:37 +02:00
|
|
|
}
|
|
|
|
this.historyIndex.set(hidx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (e.code == "ArrowDown") {
|
|
|
|
e.preventDefault();
|
|
|
|
let hidx = this.historyIndex.get();
|
|
|
|
hidx -= 1;
|
|
|
|
if (hidx < 0) {
|
|
|
|
hidx = 0;
|
|
|
|
}
|
|
|
|
this.historyIndex.set(hidx);
|
|
|
|
return;
|
|
|
|
}
|
2022-06-13 20:12:39 +02:00
|
|
|
// console.log(e.code, e.keyCode, e.key, event.which, ctrlMod, e);
|
2022-06-08 02:25:35 +02:00
|
|
|
})();
|
|
|
|
}
|
|
|
|
|
2022-06-21 01:06:37 +02:00
|
|
|
@boundMethod
|
|
|
|
clearCurLine() {
|
|
|
|
mobx.action(() => {
|
|
|
|
this.historyIndex.set(0);
|
|
|
|
this.modHistory.clear();
|
|
|
|
this.modHistory[0] = "";
|
|
|
|
})();
|
|
|
|
}
|
|
|
|
|
|
|
|
@boundMethod
|
|
|
|
getCurLine() : string {
|
2022-07-11 23:43:18 +02:00
|
|
|
let model = GlobalModel;
|
2022-06-21 01:06:37 +02:00
|
|
|
let hidx = this.historyIndex.get();
|
|
|
|
if (hidx < this.modHistory.length && this.modHistory[hidx] != null) {
|
|
|
|
return this.modHistory[hidx];
|
|
|
|
}
|
2022-07-12 02:55:03 +02:00
|
|
|
let win = model.getActiveWindow();
|
|
|
|
if (win == null) {
|
|
|
|
return "";
|
|
|
|
}
|
2022-07-11 23:43:18 +02:00
|
|
|
let hitem = win.getHistoryItem(-hidx);
|
2022-06-21 01:06:37 +02:00
|
|
|
if (hitem == null) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
return hitem.cmdtext;
|
|
|
|
}
|
|
|
|
|
|
|
|
@boundMethod
|
|
|
|
setCurLine(val : string) {
|
|
|
|
let hidx = this.historyIndex.get();
|
|
|
|
this.modHistory[hidx] = val;
|
|
|
|
}
|
|
|
|
|
2022-06-08 02:25:35 +02:00
|
|
|
@boundMethod
|
|
|
|
onChange(e : any) {
|
|
|
|
mobx.action(() => {
|
2022-06-21 01:06:37 +02:00
|
|
|
this.setCurLine(e.target.value);
|
2022-06-08 02:25:35 +02:00
|
|
|
})();
|
|
|
|
}
|
2022-06-13 20:12:39 +02:00
|
|
|
|
|
|
|
@boundMethod
|
|
|
|
doSubmitCmd() {
|
2022-07-11 23:43:18 +02:00
|
|
|
let model = GlobalModel;
|
2022-06-21 01:06:37 +02:00
|
|
|
let commandStr = this.getCurLine();
|
|
|
|
let hitem = {cmdtext: commandStr};
|
|
|
|
this.clearCurLine();
|
2022-07-12 02:55:03 +02:00
|
|
|
model.submitCommand(commandStr);
|
2022-06-13 20:12:39 +02:00
|
|
|
}
|
2022-06-08 02:25:35 +02:00
|
|
|
|
|
|
|
render() {
|
2022-06-21 01:06:37 +02:00
|
|
|
let curLine = this.getCurLine();
|
2022-06-08 02:25:35 +02:00
|
|
|
return (
|
|
|
|
<div className="box cmd-input has-background-black">
|
|
|
|
<div className="cmd-input-context">
|
|
|
|
<div className="has-text-white">
|
2022-06-20 22:03:20 +02:00
|
|
|
<span className="bold term-bright-green">[mike@local ~]</span>
|
2022-06-08 02:25:35 +02:00
|
|
|
</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>
|
|
|
|
<div className="control cmd-input-control is-expanded">
|
2022-07-08 22:45:14 +02:00
|
|
|
<textarea id="main-cmd-input" value={curLine} onKeyDown={this.onKeyDown} onChange={this.onChange} className="input"></textarea>
|
2022-06-08 02:25:35 +02:00
|
|
|
</div>
|
|
|
|
<div className="control cmd-exec">
|
2022-06-13 20:12:39 +02:00
|
|
|
<div onClick={this.doSubmitCmd} className="button">
|
2022-06-08 02:25:35 +02:00
|
|
|
<span className="icon">
|
|
|
|
<i className="fa fa-rocket"/>
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@mobxReact.observer
|
2022-07-13 08:29:39 +02:00
|
|
|
class ScreenWindowView extends React.Component<{sw : ScreenWindow}, {}> {
|
2022-07-12 07:43:58 +02:00
|
|
|
mutObs : any;
|
2022-06-18 02:54:14 +02:00
|
|
|
|
2022-07-12 07:43:58 +02:00
|
|
|
scrollToBottom() {
|
|
|
|
let elem = document.getElementById(this.getLinesId());
|
|
|
|
let oldST = elem.scrollTop;
|
|
|
|
elem.scrollTop = elem.scrollHeight;
|
|
|
|
// console.log("scroll-elem", oldST, elem.scrollHeight, elem.scrollTop, elem.scrollLeft, elem);
|
|
|
|
}
|
|
|
|
|
2022-06-18 02:54:14 +02:00
|
|
|
@boundMethod
|
|
|
|
scrollHandler(event : any) {
|
2022-07-13 08:29:39 +02:00
|
|
|
let {sw} = this.props;
|
2022-06-18 02:54:14 +02:00
|
|
|
let target = event.target;
|
|
|
|
let atBottom = (target.scrollTop + 30 > (target.scrollHeight - target.offsetHeight));
|
2022-07-13 08:29:39 +02:00
|
|
|
if (sw && sw.shouldFollow.get() != atBottom) {
|
|
|
|
mobx.action(() => sw.shouldFollow.set(atBottom));
|
2022-07-12 07:43:58 +02:00
|
|
|
}
|
|
|
|
// console.log("scroll-handler>", atBottom, target.scrollTop, target.scrollHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
let elem = document.getElementById(this.getLinesId());
|
|
|
|
if (elem == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.mutObs = new MutationObserver(this.handleDomMutation.bind(this));
|
|
|
|
this.mutObs.observe(elem, {childList: true});
|
|
|
|
elem.addEventListener("termresize", this.handleTermResize)
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
this.mutObs.disconnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
handleDomMutation(mutations, mutObs) {
|
2022-07-13 08:29:39 +02:00
|
|
|
let {sw} = this.props;
|
|
|
|
if (sw && sw.shouldFollow.get()) {
|
2022-07-12 07:43:58 +02:00
|
|
|
setTimeout(() => this.scrollToBottom(), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getWindow() : Window {
|
2022-07-13 08:29:39 +02:00
|
|
|
let {sw} = this.props;
|
|
|
|
return GlobalModel.getWindowById(sw.sessionId, sw.windowId);
|
2022-07-12 07:43:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getLinesId() {
|
2022-07-13 08:29:39 +02:00
|
|
|
let {sw} = this.props;
|
|
|
|
return "window-lines-" + sw.windowId;
|
2022-06-18 02:54:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@boundMethod
|
2022-07-12 07:43:58 +02:00
|
|
|
handleTermResize(e : any) {
|
2022-07-13 08:29:39 +02:00
|
|
|
let {sw} = this.props;
|
|
|
|
if (sw && sw.shouldFollow.get()) {
|
2022-07-12 07:43:58 +02:00
|
|
|
setTimeout(() => this.scrollToBottom(), 0);
|
2022-06-18 02:54:14 +02:00
|
|
|
}
|
|
|
|
}
|
2022-07-12 07:43:58 +02:00
|
|
|
|
2022-07-13 08:29:39 +02:00
|
|
|
getWindowViewStyle() : any {
|
|
|
|
return {width: "100%", height: "100%"};
|
|
|
|
}
|
|
|
|
|
2022-07-12 07:43:58 +02:00
|
|
|
renderError(message : string) {
|
|
|
|
return (
|
2022-07-13 08:29:39 +02:00
|
|
|
<div className="window-view" style={this.getWindowViewStyle()}>
|
2022-07-12 07:43:58 +02:00
|
|
|
<div className="lines" onScroll={this.scrollHandler} id={this.getLinesId()}>
|
|
|
|
{message}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2022-06-18 02:54:14 +02:00
|
|
|
|
2022-06-08 02:25:35 +02:00
|
|
|
render() {
|
2022-07-13 08:29:39 +02:00
|
|
|
let {sw} = this.props;
|
|
|
|
if (sw == null) {
|
|
|
|
return this.renderError("(no screen window)");
|
2022-07-02 22:32:25 +02:00
|
|
|
}
|
2022-07-13 08:29:39 +02:00
|
|
|
let win = this.getWindow();
|
2022-07-11 23:43:18 +02:00
|
|
|
if (!win.linesLoaded.get()) {
|
2022-07-12 07:43:58 +02:00
|
|
|
return this.renderError("(loading)");
|
2022-07-02 22:32:25 +02:00
|
|
|
}
|
2022-07-05 07:18:36 +02:00
|
|
|
let idx = 0;
|
2022-07-05 07:37:45 +02:00
|
|
|
let line : LineType = null;
|
2022-06-08 02:25:35 +02:00
|
|
|
return (
|
2022-07-13 08:29:39 +02:00
|
|
|
<div className="window-view" style={this.getWindowViewStyle()}>
|
2022-07-12 07:43:58 +02:00
|
|
|
<div className="lines" onScroll={this.scrollHandler} id={this.getLinesId()}>
|
2022-07-11 23:43:18 +02:00
|
|
|
<For each="line" of={win.lines} index="idx">
|
2022-07-13 09:44:19 +02:00
|
|
|
<Line key={line.lineid} line={line} sw={sw}/>
|
2022-06-08 02:25:35 +02:00
|
|
|
</For>
|
|
|
|
</div>
|
2022-07-12 07:43:58 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-13 08:29:39 +02:00
|
|
|
@mobxReact.observer
|
|
|
|
class ScreenView extends React.Component<{screen : Screen}, {}> {
|
|
|
|
render() {
|
|
|
|
let {screen} = this.props;
|
|
|
|
if (screen == null) {
|
|
|
|
return (
|
|
|
|
<div className="screen-view">
|
|
|
|
(no screen)
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let sw = screen.getActiveSW();
|
|
|
|
return (
|
|
|
|
<div className="screen-view">
|
|
|
|
<ScreenWindowView sw={sw}/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@mobxReact.observer
|
|
|
|
class ScreenTabs extends React.Component<{}, {}> {
|
|
|
|
render() {
|
|
|
|
let model = GlobalModel;
|
|
|
|
let session = model.getActiveSession();
|
|
|
|
if (session == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div className="screen-tabs">
|
|
|
|
tabs!
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-12 07:43:58 +02:00
|
|
|
@mobxReact.observer
|
|
|
|
class SessionView extends React.Component<{}, {}> {
|
|
|
|
render() {
|
|
|
|
let model = GlobalModel;
|
|
|
|
let session = model.getActiveSession();
|
|
|
|
if (session == null) {
|
|
|
|
return <div className="session-view">(no active session)</div>;
|
|
|
|
}
|
2022-07-13 08:29:39 +02:00
|
|
|
let activeScreen = session.getActiveScreen();
|
2022-07-12 07:43:58 +02:00
|
|
|
return (
|
|
|
|
<div className="session-view">
|
2022-07-13 08:29:39 +02:00
|
|
|
<ScreenView screen={activeScreen}/>
|
|
|
|
<ScreenTabs/>
|
2022-07-12 02:55:03 +02:00
|
|
|
<CmdInput/>
|
2022-06-08 02:25:35 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-20 22:03:20 +02:00
|
|
|
@mobxReact.observer
|
|
|
|
class MainSideBar extends React.Component<{}, {}> {
|
|
|
|
collapsed : mobx.IObservableValue<boolean> = mobx.observable.box(false);
|
|
|
|
|
|
|
|
@boundMethod
|
|
|
|
toggleCollapsed() {
|
|
|
|
mobx.action(() => {
|
|
|
|
this.collapsed.set(!this.collapsed.get());
|
|
|
|
})();
|
|
|
|
}
|
2022-07-08 22:23:00 +02:00
|
|
|
|
|
|
|
handleSessionClick(sessionId : string) {
|
|
|
|
console.log("click session", sessionId);
|
|
|
|
}
|
2022-07-09 10:37:19 +02:00
|
|
|
|
2022-06-20 22:03:20 +02:00
|
|
|
render() {
|
2022-07-11 23:43:18 +02:00
|
|
|
let model = GlobalModel;
|
2022-07-13 23:16:47 +02:00
|
|
|
let activeSessionId = model.activeSessionId.get();
|
2022-07-12 02:55:03 +02:00
|
|
|
let session : Session = null;
|
2022-06-20 22:03:20 +02:00
|
|
|
return (
|
|
|
|
<div className={cn("main-sidebar", {"collapsed": this.collapsed.get()})}>
|
|
|
|
<div className="collapse-container">
|
|
|
|
<div className="arrow-container" onClick={this.toggleCollapsed}>
|
|
|
|
<If condition={!this.collapsed.get()}><i className="fa fa-arrow-left"/></If>
|
|
|
|
<If condition={this.collapsed.get()}><i className="fa fa-arrow-right"/></If>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="menu">
|
|
|
|
<p className="menu-label">
|
2022-07-08 22:01:37 +02:00
|
|
|
Private Sessions
|
2022-06-20 22:03:20 +02:00
|
|
|
</p>
|
|
|
|
<ul className="menu-list">
|
2022-07-12 02:55:03 +02:00
|
|
|
<If condition={!model.sessionListLoaded.get()}>
|
2022-07-11 23:43:18 +02:00
|
|
|
<li><a>(loading)</a></li>
|
|
|
|
</If>
|
2022-07-12 02:55:03 +02:00
|
|
|
<If condition={model.sessionListLoaded.get()}>
|
2022-07-11 23:43:18 +02:00
|
|
|
<For each="session" of={model.sessionList}>
|
2022-07-13 23:16:47 +02:00
|
|
|
<li key={session.sessionId}><a className={cn({"is-active": activeSessionId == session.sessionId})} onClick={() => this.handleSessionClick(session.sessionId)}>#{session.name.get()}</a></li>
|
2022-07-11 23:43:18 +02:00
|
|
|
</For>
|
|
|
|
<li className="new-session"><a className="new-session"><i className="fa fa-plus"/> New Session</a></li>
|
|
|
|
</If>
|
2022-06-20 22:03:20 +02:00
|
|
|
</ul>
|
|
|
|
<p className="menu-label">
|
2022-07-08 22:01:37 +02:00
|
|
|
Shared Sessions
|
2022-06-20 22:03:20 +02:00
|
|
|
</p>
|
|
|
|
<ul className="menu-list">
|
2022-07-08 22:01:37 +02:00
|
|
|
<li><a>#server-status</a></li>
|
|
|
|
<li><a className="activity">#bug-3458 <div className="tag is-link">3</div></a></li>
|
|
|
|
<li><a>#dev-build</a></li>
|
2022-06-20 22:03:20 +02:00
|
|
|
<li className="new-session"><a className="new-session"><i className="fa fa-plus"/> New Session</a></li>
|
|
|
|
</ul>
|
|
|
|
<p className="menu-label">
|
|
|
|
Direct Messages
|
|
|
|
</p>
|
|
|
|
<ul className="menu-list">
|
|
|
|
<li><a>
|
|
|
|
<i className="user-status status fa fa-circle"/>
|
|
|
|
<img className="avatar" src="https://i.pravatar.cc/48?img=4"/>
|
|
|
|
Mike S <span className="sub-label">you</span>
|
|
|
|
</a></li>
|
|
|
|
<li><a>
|
|
|
|
<i className="user-status status offline fa fa-circle"/>
|
|
|
|
<img className="avatar" src="https://i.pravatar.cc/48?img=8"/>
|
|
|
|
Matt P
|
|
|
|
</a></li>
|
|
|
|
<li><a>
|
|
|
|
<i className="user-status status offline fa fa-circle"/>
|
|
|
|
<img className="avatar" src="https://i.pravatar.cc/48?img=12"/>
|
|
|
|
Adam B
|
|
|
|
</a></li>
|
|
|
|
<li><a className="activity">
|
|
|
|
<i className="user-status status fa fa-circle"/>
|
2022-06-21 01:06:37 +02:00
|
|
|
<img className="avatar" src="https://i.pravatar.cc/48?img=5"/>
|
2022-06-20 22:03:20 +02:00
|
|
|
Michelle T <div className="tag is-link">2</div>
|
|
|
|
</a></li>
|
|
|
|
</ul>
|
|
|
|
<div className="spacer"></div>
|
|
|
|
<p className="menu-label">
|
|
|
|
Remotes
|
|
|
|
</p>
|
|
|
|
<ul className="menu-list">
|
|
|
|
<li><a><i className="status fa fa-circle"/>local</a></li>
|
|
|
|
<li><a><i className="status fa fa-circle"/>local-sudo</a></li>
|
|
|
|
<li><a><i className="status offline fa fa-circle"/>mike@app01.ec2</a></li>
|
|
|
|
<li><a><i className="status fa fa-circle"/>mike@test01.ec2</a></li>
|
|
|
|
<li><a><i className="status offline fa fa-circle"/>root@app01.ec2</a></li>
|
|
|
|
</ul>
|
|
|
|
<div className="bottom-spacer"></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-16 03:12:22 +02:00
|
|
|
@mobxReact.observer
|
2022-06-17 00:51:17 +02:00
|
|
|
class Main extends React.Component<{}, {}> {
|
2022-06-16 03:12:22 +02:00
|
|
|
constructor(props : any) {
|
|
|
|
super(props);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
2022-06-20 22:03:20 +02:00
|
|
|
<div id="main">
|
2022-06-16 03:12:22 +02:00
|
|
|
<h1 className="title scripthaus-logo-small">
|
|
|
|
<div className="title-cursor">█</div>
|
|
|
|
ScriptHaus
|
|
|
|
</h1>
|
2022-06-20 22:03:20 +02:00
|
|
|
<div className="main-content">
|
|
|
|
<MainSideBar/>
|
2022-07-11 23:43:18 +02:00
|
|
|
<SessionView/>
|
2022-06-20 22:03:20 +02:00
|
|
|
</div>
|
2022-06-16 03:12:22 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-08 02:25:35 +02:00
|
|
|
|
|
|
|
export {Main};
|
2022-07-07 22:27:44 +02:00
|
|
|
|