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-14 08:11:45 +02:00
import { v4 as uuidv4 } from "uuid" ;
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-08-31 02:05:35 +02:00
import type { SessionDataType , LineType , CmdDataType , RemoteType , RemoteStateType , RemoteInstanceType , RemotePtrType , HistoryItem } from "./types" ;
2022-06-18 02:54:14 +02:00
import localizedFormat from 'dayjs/plugin/localizedFormat' ;
2022-08-31 02:05:35 +02:00
import { GlobalModel , GlobalCommandRunner , Session , Cmd , Window , Screen , ScreenWindow , riToRPtr } from "./model" ;
2022-06-18 02:54:14 +02:00
dayjs . extend ( localizedFormat )
2022-06-13 20:12:39 +02:00
2022-08-13 03:34:56 +02:00
type InterObsValue = {
sessionid : string ,
windowid : string ,
lineid : string ,
cmdid : string ,
visible : mobx.IObservableValue < boolean > ,
timeoutid? : any ,
} ;
let globalLineWeakMap = new WeakMap < any , InterObsValue > ( ) ;
2022-08-18 09:39:06 +02:00
function isBlank ( s : string ) : boolean {
return ( s == null || s == "" ) ;
}
2022-08-29 22:54:11 +02:00
function windowLinesDOMId ( windowid : string ) {
return "window-lines-" + windowid ;
}
function scrollDiv ( div : any , amt : number ) {
if ( div == null ) {
return ;
}
let newScrollTop = div . scrollTop + amt ;
if ( newScrollTop < 0 ) {
newScrollTop = 0 ;
}
div . scrollTo ( { top : newScrollTop , behavior : "smooth" } ) ;
}
2022-08-30 00:42:50 +02:00
function pageSize ( div : any ) : number {
if ( div == null ) {
return 300 ;
}
let size = div . clientHeight ;
if ( size > 500 ) {
size = size - 100 ;
} else if ( size > 200 ) {
size = size - 30 ;
}
return size ;
}
2022-08-13 03:34:56 +02:00
function interObsCallback ( entries ) {
let now = Date . now ( ) ;
entries . forEach ( ( entry ) = > {
let line = globalLineWeakMap . get ( entry . target ) ;
if ( ( line . timeoutid != null ) && ( line . visible . get ( ) == entry . isIntersecting ) ) {
clearTimeout ( line . timeoutid ) ;
line . timeoutid = null ;
return ;
}
if ( line . visible . get ( ) != entry . isIntersecting && line . timeoutid == null ) {
line . timeoutid = setTimeout ( ( ) = > {
line . timeoutid = null ;
mobx . action ( ( ) = > {
line . visible . set ( entry . isIntersecting ) ;
} ) ( ) ;
} , 250 ) ;
return ;
}
} ) ;
}
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-08-24 22:19:59 +02:00
function makeFullRemoteRef ( ownerName : string , remoteRef : string , name : string ) : string {
if ( isBlank ( ownerName ) && isBlank ( name ) ) {
return remoteRef ;
2022-08-11 03:35:18 +02:00
}
2022-08-24 22:19:59 +02:00
if ( ! isBlank ( ownerName ) && isBlank ( name ) ) {
return ownerName + ":" + remoteRef ;
2022-08-11 03:35:18 +02:00
}
2022-08-24 22:19:59 +02:00
if ( isBlank ( ownerName ) && ! isBlank ( name ) ) {
return remoteRef + ":" + name ;
2022-08-11 03:35:18 +02:00
}
2022-08-24 22:19:59 +02:00
return ownerName + ":" + remoteRef + ":" + name ;
}
function getRemoteStr ( rptr : RemotePtrType ) : string {
if ( rptr == null || isBlank ( rptr . remoteid ) ) {
return "(invalid remote)" ;
2022-08-17 22:06:47 +02:00
}
2022-08-24 22:19:59 +02:00
let username = ( isBlank ( rptr . ownerid ) ? null : GlobalModel . resolveUserIdToName ( rptr . ownerid ) ) ;
let remoteRef = GlobalModel . resolveRemoteIdToRef ( rptr . remoteid ) ;
let fullRef = makeFullRemoteRef ( username , remoteRef , rptr . name ) ;
return fullRef ;
2022-08-11 03:35:18 +02:00
}
function replaceHomePath ( path : string , homeDir : string ) : string {
if ( path == homeDir ) {
return "~" ;
}
if ( path . startsWith ( homeDir + "/" ) ) {
return "~" + path . substr ( homeDir . length ) ;
}
return path ;
}
function getCwdStr ( remote : RemoteType , state : RemoteStateType ) : string {
if ( ( state == null || state . cwd == null ) && remote != null ) {
return "~" ;
}
let cwd = "(unknown)" ;
if ( state && state . cwd ) {
cwd = state . cwd ;
}
if ( remote && remote . remotevars . home ) {
cwd = replaceHomePath ( cwd , remote . remotevars . home )
}
return cwd ;
}
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 (
2022-08-17 00:59:28 +02:00
< div className = "line line-text" data - lineid = { line . lineid } data - windowid = { line . windowid } >
2022-06-08 02:25:35 +02:00
< 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 >
) ;
}
}
2022-08-24 22:19:59 +02:00
@mobxReact . observer
class Prompt extends React . Component < { rptr : RemotePtrType , rstate : RemoteStateType } , { } > {
render() {
let remote : RemoteType = null ;
if ( this . props . rptr && ! isBlank ( this . props . rptr . remoteid ) ) {
remote = GlobalModel . getRemote ( this . props . rptr . remoteid ) ;
}
let remoteStr = getRemoteStr ( this . props . rptr ) ;
let cwd = getCwdStr ( remote , this . props . rstate ) ;
let isRoot = false ;
if ( remote && remote . remotevars ) {
if ( remote . remotevars [ "sudo" ] || remote . remotevars [ "bestuser" ] == "root" ) {
isRoot = true ;
}
}
let className = ( isRoot ? "term-bright-red" : "term-bright-green" ) ;
return (
< span className = "term-bright-green" > [ { remoteStr } ] { cwd } { isRoot ? "#" : "$" } < / span >
) ;
}
}
2022-06-08 02:25:35 +02:00
@mobxReact . observer
2022-08-17 00:59:28 +02:00
class LineCmd extends React . Component < { sw : ScreenWindow , line : LineType , width : number , interObs : IntersectionObserver , initVis : boolean , cmdRefNum : number } , { } > {
2022-07-13 09:44:19 +02:00
termLoaded : mobx.IObservableValue < boolean > = mobx . observable . box ( false ) ;
2022-08-13 03:34:56 +02:00
lineRef : React.RefObject < any > = React . createRef ( ) ;
iobsVal : InterObsValue = null ;
autorunDisposer : ( ) = > void = null ;
2022-07-13 09:44:19 +02:00
2022-06-16 09:31:54 +02:00
constructor ( props ) {
super ( props ) ;
2022-08-13 03:34:56 +02:00
let line = props . line ;
let ival : InterObsValue = {
sessionid : line.sessionid ,
windowid : line.windowid ,
lineid : line.lineid ,
cmdid : line.cmdid ,
visible : mobx.observable.box ( this . props . initVis ) ,
} ;
this . iobsVal = ival ;
2022-06-16 09:31:54 +02:00
}
2022-08-13 03:34:56 +02:00
visibilityChanged ( vis : boolean ) : void {
if ( vis && ! this . termLoaded . get ( ) ) {
this . loadTerminal ( ) ;
}
else if ( ! vis && this . termLoaded . get ( ) ) {
let { line } = this . props ;
}
}
loadTerminal ( ) : void {
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 ) ;
2022-08-13 03:34:56 +02:00
if ( cmd == null ) {
return ;
}
let termId = "term-" + getLineId ( line ) ;
let termElem = document . getElementById ( termId ) ;
if ( termElem == null ) {
console . log ( "cannot load terminal, no term elem found" , termId ) ;
return ;
}
sw . connectElem ( termElem , cmd , this . props . width ) ;
mobx . action ( ( ) = > this . termLoaded . set ( true ) ) ( ) ;
}
componentDidMount() {
let { line } = this . props ;
if ( this . lineRef . current == null || this . props . interObs == null ) {
console . log ( "LineCmd lineRef current is null or interObs is null" , line , this . lineRef . current , this . props . interObs ) ;
}
else {
globalLineWeakMap . set ( this . lineRef . current , this . iobsVal ) ;
this . props . interObs . observe ( this . lineRef . current ) ;
this . autorunDisposer = mobx . autorun ( ( ) = > {
let vis = this . iobsVal . visible . get ( ) ;
this . visibilityChanged ( vis ) ;
} ) ;
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 ;
2022-08-13 03:34:56 +02:00
if ( this . termLoaded . get ( ) ) {
sw . disconnectElem ( line . cmdid ) ;
}
if ( this . lineRef . current != null && this . props . interObs != null ) {
this . props . interObs . unobserve ( this . lineRef . current ) ;
}
if ( this . autorunDisposer != null ) {
this . autorunDisposer ( ) ;
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-08-13 03:34:56 +02:00
let termWrap = sw . getTermWrap ( line . cmdid ) ;
if ( termWrap != null ) {
termWrap . reloadTerminal ( 500 ) ;
2022-07-12 02:55:03 +02:00
}
2022-06-15 01:02:20 +02:00
}
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 >
) ;
}
2022-08-24 22:19:59 +02:00
let remoteStr = getRemoteStr ( cmd . remote ) ;
2022-08-11 03:35:18 +02:00
let cwd = getCwdStr ( remote , cmd . getRemoteState ( ) ) ;
2022-07-07 22:27:44 +02:00
return (
< div className = "metapart-mono cmdtext" >
2022-08-24 22:19:59 +02:00
< Prompt rptr = { cmd . remote } rstate = { cmd . getRemoteState ( ) } / > { cmd . getSingleLineCmdText ( ) }
2022-07-07 22:27:44 +02:00
< / div >
) ;
}
2022-08-25 04:00:03 +02:00
@boundMethod
2022-08-25 21:12:56 +02:00
clickTermBlock ( e : any ) {
2022-08-25 04:00:03 +02:00
let { sw , line } = this . props ;
let model = GlobalModel ;
let termWrap = sw . getTermWrap ( line . cmdid ) ;
if ( termWrap != null ) {
termWrap . terminal . focus ( ) ;
}
}
2022-06-08 02:25:35 +02:00
render() {
2022-08-13 03:34:56 +02:00
let { sw , line , width } = this . props ;
2022-07-12 02:55:03 +02:00
let model = GlobalModel ;
2022-08-17 00:59:28 +02:00
let lineid = line . lineid ;
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 ) {
2022-08-13 03:34:56 +02:00
return (
< div className = "line line-invalid" id = { "line-" + getLineId ( line ) } ref = { this . lineRef } >
[ 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-14 09:54:31 +02:00
let cellWidthPx = 8 ;
2022-08-13 03:34:56 +02:00
let termWidth = Math . max ( Math . trunc ( ( width - 20 ) / cellWidthPx ) , 10 ) ;
let usedRows = sw . getUsedRows ( cmd , width ) ;
2022-07-13 09:44:19 +02:00
let totalHeight = cellHeightPx * usedRows ;
2022-07-12 02:55:03 +02:00
let remote = model . getRemote ( cmd . remoteId ) ;
let status = cmd . getStatus ( ) ;
let termOpts = cmd . getTermOpts ( ) ;
2022-08-13 03:34:56 +02:00
let isFocused = sw . getIsFocused ( line . cmdid ) ;
2022-08-17 00:59:28 +02:00
let cmdRefNumStr = ( this . props . cmdRefNum == null ? "?" : this . props . cmdRefNum . toString ( ) ) ;
2022-06-08 02:25:35 +02:00
return (
2022-08-17 00:59:28 +02:00
< div className = { cn ( "line" , "line-cmd" , { "focus" : isFocused } ) } id = { "line-" + getLineId ( line ) } ref = { this . lineRef } style = { { position : "relative" } } data - lineid = { line . lineid } data - windowid = { line . windowid } data - cmdid = { line . cmdid } >
2022-07-14 09:54:31 +02:00
< div className = "line-header" >
2022-08-23 22:14:57 +02:00
< div className = { cn ( "avatar" , { "num4" : cmdRefNumStr . length == 4 } , { "num5" : cmdRefNumStr . length >= 5 } , "status-" + status , { "ephemeral" : line . ephemeral } ) } onClick = { this . doRefresh } >
2022-08-17 00:59:28 +02:00
{ cmdRefNumStr }
2022-08-20 01:35:38 +02:00
< If condition = { status == "hangup" || status == "error" } >
< i className = "fa fa-exclamation-triangle status-icon" / >
< / If >
< If condition = { status == "detached" } >
< i className = "fa fa-refresh status-icon" / >
< / If >
2022-06-18 02:54:14 +02:00
< / div >
2022-07-14 09:54:31 +02:00
< div className = "meta-wrap" >
< div className = "meta" >
< 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 }
( { termOpts . rows } x { termOpts . cols } )
< / div >
{ this . renderCmdText ( cmd , remote ) }
2022-06-18 02:54:14 +02:00
< / div >
2022-06-08 02:25:35 +02:00
< / div >
< / div >
2022-07-14 09:54:31 +02:00
< div className = { cn ( "terminal-wrapper" , { "focus" : isFocused } ) } style = { { overflowY : "hidden" } } >
2022-08-25 04:00:03 +02:00
< If condition = { ! isFocused } >
< div className = "term-block" onClick = { this . clickTermBlock } > < / div >
< / If >
2022-07-14 09:54:31 +02:00
< div className = "terminal" id = { "term-" + getLineId ( line ) } data - cmdid = { line . cmdid } style = { { height : totalHeight } } > < / div >
2022-08-13 03:34:56 +02:00
< If condition = { ! termLoaded } > < div style = { { position : "absolute" , top : 60 , left : 30 } } > ( loading ) < / div > < / If >
2022-07-14 09:54:31 +02:00
< / div >
2022-06-08 02:25:35 +02:00
< / div >
) ;
}
}
@mobxReact . observer
2022-08-17 00:59:28 +02:00
class Line extends React . Component < { sw : ScreenWindow , line : LineType , width : number , interObs : IntersectionObserver , initVis : boolean , cmdRefNum : number } , { } > {
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-08-25 04:00:03 +02:00
class TextAreaInput extends React . Component < { } , { } > {
2022-08-12 20:44:29 +02:00
lastTab : boolean = false ;
lastHistoryUpDown : boolean = false ;
2022-08-11 03:35:18 +02:00
lastTabCurLine : mobx.IObservableValue < string > = mobx . observable . box ( null ) ;
2022-08-25 04:00:03 +02:00
2022-08-24 02:27:12 +02:00
componentDidMount() {
let input = document . getElementById ( "main-cmd-input" ) ;
if ( input != null ) {
input . focus ( ) ;
}
}
2022-08-12 20:44:29 +02:00
isModKeyPress ( e : any ) {
return e . code . match ( /^(Control|Meta|Alt|Shift)(Left|Right)$/ ) ;
}
getLinePos ( elem : any ) : { numLines : number , linePos : number } {
let numLines = elem . value . split ( "\n" ) . length ;
let linePos = elem . value . substr ( 0 , elem . selectionStart ) . split ( "\n" ) . length ;
return { numLines , linePos } ;
}
2022-06-08 02:25:35 +02:00
@mobx . action @boundMethod
onKeyDown ( e : any ) {
mobx . action ( ( ) = > {
2022-08-12 20:44:29 +02:00
if ( this . isModKeyPress ( e ) ) {
return ;
}
2022-07-11 23:43:18 +02:00
let model = GlobalModel ;
2022-08-11 03:35:18 +02:00
let inputModel = model . inputModel ;
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" ) ;
2022-08-11 03:35:18 +02:00
let curLine = inputModel . getCurLine ( ) ;
2022-08-12 20:44:29 +02:00
let lastTab = this . lastTab ;
this . lastTab = ( e . code == "Tab" ) ;
let lastHist = this . lastHistoryUpDown ;
this . lastHistoryUpDown = false ;
2022-08-11 03:35:18 +02:00
if ( e . code == "Tab" ) {
e . preventDefault ( ) ;
if ( lastTab ) {
2022-08-29 20:23:20 +02:00
GlobalModel . submitCommand ( "compgen" , null , [ curLine ] , { "comppos" : String ( curLine . length ) , "compshow" : "1" , "nohist" : "1" } , true ) ;
2022-08-11 03:35:18 +02:00
return ;
}
else {
2022-08-29 20:23:20 +02:00
GlobalModel . submitCommand ( "compgen" , null , [ curLine ] , { "comppos" : String ( curLine . length ) , "nohist" : "1" } , true ) ;
2022-08-11 03:35:18 +02:00
return ;
}
}
2022-08-12 20:44:29 +02:00
if ( e . code == "Enter" ) {
2022-06-08 02:25:35 +02:00
e . preventDefault ( ) ;
2022-08-31 02:05:35 +02:00
if ( inputModel . historyShow . get ( ) ) {
inputModel . grabSelectedHistoryItem ( ) ;
return ;
}
2022-08-12 20:44:29 +02:00
if ( ! ctrlMod ) {
2022-08-25 21:12:56 +02:00
setTimeout ( ( ) = > GlobalModel . inputModel . uiSubmitCommand ( ) , 0 ) ;
2022-08-12 20:44:29 +02:00
return ;
}
e . target . setRangeText ( "\n" , e . target . selectionStart , e . target . selectionEnd , "end" ) ;
GlobalModel . inputModel . setCurLine ( e . target . value ) ;
2022-06-08 02:25:35 +02:00
return ;
}
2022-08-11 03:35:18 +02:00
if ( e . code == "Escape" ) {
2022-08-09 01:22:36 +02:00
e . preventDefault ( ) ;
2022-08-30 21:22:42 +02:00
GlobalModel . inputModel . toggleInfoMsg ( ) ;
2022-08-11 03:35:18 +02:00
return ;
}
if ( e . code == "KeyC" && e . getModifierState ( "Control" ) ) {
e . preventDefault ( ) ;
inputModel . clearCurLine ( ) ;
2022-08-09 01:22:36 +02:00
return ;
}
2022-08-31 02:05:35 +02:00
if ( e . code == "KeyR" && e . getModifierState ( "Control" ) ) {
e . preventDefault ( ) ;
GlobalCommandRunner . openHistory ( ) ;
return ;
}
2022-08-12 20:44:29 +02:00
if ( e . code == "ArrowUp" || e . code == "ArrowDown" ) {
2022-08-31 02:05:35 +02:00
if ( inputModel . historyShow . get ( ) ) {
inputModel . moveHistorySelection ( e . code == "ArrowUp" ? - 1 : 1 ) ;
return ;
}
2022-08-12 20:44:29 +02:00
let linePos = this . getLinePos ( e . target ) ;
if ( e . code == "ArrowUp" ) {
if ( ! lastHist && linePos . linePos > 1 ) {
// regular arrow
return ;
}
e . preventDefault ( ) ;
inputModel . prevHistoryItem ( ) ;
this . lastHistoryUpDown = true ;
return ;
}
if ( e . code == "ArrowDown" ) {
if ( ! lastHist && linePos . linePos < linePos . numLines ) {
// regular arrow
return ;
}
e . preventDefault ( ) ;
inputModel . nextHistoryItem ( ) ;
this . lastHistoryUpDown = true ;
return ;
}
2022-06-21 01:06:37 +02:00
}
2022-08-29 22:54:11 +02:00
if ( e . code == "PageUp" || e . code == "PageDown" ) {
e . preventDefault ( ) ;
2022-08-31 02:05:35 +02:00
if ( inputModel . historyShow . get ( ) ) {
inputModel . moveHistorySelection ( e . code == "PageUp" ? - 10 : 10 ) ;
return ;
}
let infoScroll = inputModel . hasScrollingInfoMsg ( ) ;
2022-08-29 22:54:11 +02:00
if ( infoScroll ) {
2022-08-30 00:42:50 +02:00
let div = document . querySelector ( ".cmd-input-info" ) ;
let amt = pageSize ( div ) ;
scrollDiv ( div , ( e . code == "PageUp" ? - amt : amt ) ) ;
2022-08-29 22:54:11 +02:00
}
else {
let win = GlobalModel . getActiveWindow ( ) ;
if ( win == null ) {
return ;
}
let id = windowLinesDOMId ( win . windowId ) ;
let div = document . getElementById ( id ) ;
2022-08-30 00:42:50 +02:00
let amt = pageSize ( div ) ;
scrollDiv ( div , ( e . code == "PageUp" ? - amt : amt ) ) ;
2022-08-29 22:54:11 +02:00
}
}
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
} ) ( ) ;
}
@boundMethod
onChange ( e : any ) {
mobx . action ( ( ) = > {
2022-08-11 03:35:18 +02:00
GlobalModel . inputModel . setCurLine ( e . target . value ) ;
2022-06-08 02:25:35 +02:00
} ) ( ) ;
}
2022-06-13 20:12:39 +02:00
2022-08-25 04:00:03 +02:00
render() {
let model = GlobalModel ;
let inputModel = model . inputModel ;
let curLine = inputModel . getCurLine ( ) ;
let numLines = curLine . split ( "\n" ) . length ;
let displayLines = numLines ;
if ( displayLines > 5 ) {
displayLines = 5 ;
}
return (
< textarea id = "main-cmd-input" rows = { displayLines } value = { curLine } onKeyDown = { this . onKeyDown } onChange = { this . onChange } className = "textarea" > < / textarea >
) ;
}
}
2022-08-31 00:25:51 +02:00
@mobxReact . observer
class HistoryInfo extends React . Component < { } , { } > {
2022-08-31 02:05:35 +02:00
lastClickHNum : string = null ;
lastClickTs : number = 0 ;
2022-08-31 00:25:51 +02:00
componentDidMount() {
let inputModel = GlobalModel . inputModel ;
let selNum = inputModel . historySelectedNum . get ( ) ;
if ( selNum != null ) {
2022-08-31 02:05:35 +02:00
inputModel . scrollHistoryItemIntoView ( selNum ) ;
2022-08-31 00:25:51 +02:00
}
}
2022-08-31 02:05:35 +02:00
@boundMethod
handleItemClick ( hitem : HistoryItem ) {
let inputModel = GlobalModel . inputModel ;
let selNum = inputModel . historySelectedNum . get ( ) ;
if ( this . lastClickHNum == hitem . historynum && selNum == hitem . historynum ) {
inputModel . grabSelectedHistoryItem ( ) ;
return ;
}
inputModel . focusCmdInput ( ) ;
inputModel . setHistorySelectionNum ( hitem . historynum ) ;
let now = Date . now ( ) ;
this . lastClickHNum = hitem . historynum ;
this . lastClickTs = now ;
setTimeout ( ( ) = > {
if ( this . lastClickTs == now ) {
this . lastClickHNum = null ;
this . lastClickTs = 0 ;
}
} , 3000 ) ;
}
renderHItem ( hitem : HistoryItem , selNum : string ) : any {
2022-08-31 00:25:51 +02:00
let lines = hitem . cmdstr . split ( "\n" ) ;
let line : string = "" ;
let idx = 0 ;
return (
2022-08-31 02:05:35 +02:00
< div key = { hitem . historynum } className = { cn ( "history-item" , { "is-selected" : selNum == hitem . historynum } , "hnum-" + hitem . historynum ) } onClick = { ( ) = > this . handleItemClick ( hitem ) } >
2022-08-31 00:25:51 +02:00
< div className = "history-line" > { ( selNum == hitem . historynum ? "*" : " " ) } { sprintf ( "%5s" , hitem . historynum ) } { lines [ 0 ] } < / div >
< For each = "line" index = "index" of = { lines . slice ( 1 ) } >
< div key = { idx } className = "history-line" > { line } < / div >
< / For >
< / div >
) ;
}
2022-08-31 02:05:35 +02:00
@boundMethod
handleClose() {
GlobalModel . inputModel . toggleInfoMsg ( ) ;
}
2022-08-31 00:25:51 +02:00
render() {
let inputModel = GlobalModel . inputModel ;
let idx : number = 0 ;
let selNum = inputModel . historySelectedNum . get ( ) ;
2022-08-31 02:05:35 +02:00
let hitems = inputModel . getFilteredHistoryItems ( ) ;
2022-08-31 00:25:51 +02:00
hitems = hitems . slice ( ) . reverse ( ) ;
2022-08-31 02:05:35 +02:00
let hitem : HistoryItem = null ;
2022-08-31 00:25:51 +02:00
return (
< div className = "cmd-history" >
< div className = "history-title" >
2022-08-31 02:05:35 +02:00
history
2022-08-31 00:25:51 +02:00
{ " " }
< span className = "term-bright-white" > [ containing '' ] < / span >
{ " " }
< span className = "term-bright-white" > [ this session & # x2318 ; S ] < / span >
{ " " }
< span className = "term-bright-white" > [ this window & # x2318 ; W ] < / span >
{ " " }
< span className = "term-bright-white" > [ this remote & # x2318 ; R ] < / span >
{ " " }
< span className = "term-bright-white" > [ including metacmds & # x2318 ; M ] < / span >
2022-08-31 02:05:35 +02:00
{ " " } < span className = "history-clickable-opt" onClick = { this . handleClose } > ( close ESC ) < / span >
2022-08-31 00:25:51 +02:00
< / div >
< div className = "history-items" >
< If condition = { hitems . length == 0 } >
[ no history ]
< / If >
< If condition = { hitems . length > 0 } >
< For each = "hitem" index = "idx" of = { hitems } >
{ this . renderHItem ( hitem , selNum ) }
< / For >
< / If >
< / div >
< / div >
) ;
}
}
2022-08-25 04:00:03 +02:00
@mobxReact . observer
class CmdInput extends React . Component < { } , { } > {
2022-08-11 19:22:43 +02:00
getAfterSlash ( s : string ) : string {
2022-08-24 02:27:12 +02:00
if ( s . startsWith ( "^/" ) ) {
return s . substr ( 1 ) ;
}
2022-08-11 19:22:43 +02:00
let slashIdx = s . lastIndexOf ( "/" ) ;
if ( slashIdx == s . length - 1 ) {
slashIdx = s . lastIndexOf ( "/" , slashIdx - 1 ) ;
}
if ( slashIdx == - 1 ) {
return s ;
}
return s . substr ( slashIdx + 1 ) ;
}
2022-06-08 02:25:35 +02:00
render() {
2022-08-11 03:35:18 +02:00
let model = GlobalModel ;
2022-08-30 21:22:42 +02:00
let inputModel = model . inputModel ;
2022-08-11 03:35:18 +02:00
let win = GlobalModel . getActiveWindow ( ) ;
let ri : RemoteInstanceType = null ;
2022-08-24 22:19:59 +02:00
let rptr : RemotePtrType = null ;
2022-08-11 03:35:18 +02:00
if ( win != null ) {
ri = win . getCurRemoteInstance ( ) ;
2022-08-24 22:19:59 +02:00
rptr = win . curRemote . get ( ) ;
2022-08-11 03:35:18 +02:00
}
let remote : RemoteType = null ;
let remoteState : RemoteStateType = null ;
if ( ri != null ) {
remote = GlobalModel . getRemote ( ri . remoteid ) ;
remoteState = ri . state ;
}
2022-08-24 22:19:59 +02:00
let remoteStr = getRemoteStr ( rptr ) ;
2022-08-11 03:35:18 +02:00
let cwdStr = getCwdStr ( remote , remoteState ) ;
2022-08-30 21:22:42 +02:00
let infoMsg = inputModel . infoMsg . get ( ) ;
let infoShow = inputModel . infoShow . get ( ) ;
let historyShow = ! infoShow && inputModel . historyShow . get ( ) ;
2022-08-11 03:35:18 +02:00
let istr : string = null ;
let istrIdx : number = 0 ;
2022-08-23 03:54:01 +02:00
let line : string = null ;
2022-08-23 22:14:57 +02:00
let idx : number = 0 ;
2022-06-08 02:25:35 +02:00
return (
2022-08-30 21:58:06 +02:00
< div className = { cn ( "box cmd-input has-background-black" , { "has-info" : infoShow || historyShow } ) } >
2022-08-31 00:25:51 +02:00
< If condition = { historyShow } >
< HistoryInfo / >
< / If >
2022-08-11 03:35:18 +02:00
< div className = "cmd-input-info" style = { { display : ( infoShow ? "block" : "none" ) } } >
< If condition = { infoMsg && infoMsg . infotitle != null } >
< div className = "info-title" >
{ infoMsg . infotitle }
< / div >
< / If >
< If condition = { infoMsg && infoMsg . infomsg != null } >
< div className = "info-msg" >
{ infoMsg . infomsg }
< / div >
< / If >
2022-08-23 03:54:01 +02:00
< If condition = { infoMsg && infoMsg . infolines != null } >
< div className = "info-lines" >
2022-08-23 22:14:57 +02:00
< For index = "idx" each = "line" of = { infoMsg . infolines } >
2022-08-24 02:27:12 +02:00
< div key = { idx } > { line == "" ? " " : line } < / div >
2022-08-23 03:54:01 +02:00
< / For >
< / div >
< / If >
2022-08-11 19:22:43 +02:00
< If condition = { infoMsg && infoMsg . infocomps != null && infoMsg . infocomps . length > 0 } >
< div className = "info-comps" >
< For each = "istr" index = "istrIdx" of = { infoMsg . infocomps } >
2022-08-24 02:27:12 +02:00
< div key = { istrIdx } className = { cn ( "info-comp" , { "metacmd-comp" : istr . startsWith ( "^" ) } ) } >
2022-08-11 19:22:43 +02:00
{ this . getAfterSlash ( istr ) }
2022-08-11 03:35:18 +02:00
< / div >
< / For >
2022-08-11 19:22:43 +02:00
< If condition = { infoMsg . infocompsmore } >
< div key = "more" className = "info-comp" >
2022-08-11 03:35:18 +02:00
. . .
< / div >
< / If >
< / div >
< / If >
< If condition = { infoMsg && infoMsg . infoerror != null } >
< div className = "info-error" >
2022-08-27 07:07:12 +02:00
[ error ] { infoMsg . infoerror }
2022-08-11 03:35:18 +02:00
< / div >
< / If >
< / div >
2022-06-08 02:25:35 +02:00
< div className = "cmd-input-context" >
< div className = "has-text-white" >
2022-08-24 22:19:59 +02:00
< Prompt rptr = { rptr } rstate = { remoteState } / >
2022-06-08 02:25:35 +02:00
< / div >
< / div >
< div className = "cmd-input-field field has-addons" >
< div className = "control cmd-quick-context" >
2022-08-24 22:19:59 +02:00
< div className = "button is-static" > { remoteStr } < / div >
2022-06-08 02:25:35 +02:00
< / div >
< div className = "control cmd-input-control is-expanded" >
2022-08-25 04:00:03 +02:00
< TextAreaInput / >
2022-06-08 02:25:35 +02:00
< / div >
< div className = "control cmd-exec" >
2022-08-25 21:12:56 +02:00
< div onClick = { GlobalModel . inputModel . uiSubmitCommand } 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-08-13 03:34:56 +02:00
rszObs : any ;
interObs : IntersectionObserver ;
2022-07-14 08:11:45 +02:00
randomId : string ;
2022-07-14 09:54:31 +02:00
width : mobx.IObservableValue < number > = mobx . observable . box ( 0 ) ;
2022-08-11 19:41:08 +02:00
lastHeight : number = null ;
2022-06-18 02:54:14 +02:00
2022-07-14 08:11:45 +02:00
scrollToBottom ( reason : string ) {
2022-07-14 09:54:31 +02:00
let elem = document . getElementById ( this . getLinesDOMId ( ) ) ;
2022-07-14 08:11:45 +02:00
if ( elem == null ) {
return ;
}
2022-07-12 07:43:58 +02:00
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 ) {
2022-08-13 03:34:56 +02:00
mobx . action ( ( ) = > sw . shouldFollow . set ( atBottom ) ) ( ) ;
2022-07-12 07:43:58 +02:00
}
2022-08-25 04:00:03 +02:00
// console.log("scroll-handler (sw)>", atBottom, target.scrollTop, target.scrollHeight, event);
2022-07-12 07:43:58 +02:00
}
componentDidMount() {
2022-07-14 09:54:31 +02:00
let elem = document . getElementById ( this . getLinesDOMId ( ) ) ;
if ( elem != null ) {
this . mutObs = new MutationObserver ( this . handleDomMutation . bind ( this ) ) ;
this . mutObs . observe ( elem , { childList : true } ) ;
elem . addEventListener ( "termresize" , this . handleTermResize ) ;
let { sw } = this . props ;
if ( sw && sw . shouldFollow . get ( ) ) {
setTimeout ( ( ) = > this . scrollToBottom ( "mount" ) , 0 ) ;
}
2022-08-13 03:34:56 +02:00
this . interObs = new IntersectionObserver ( interObsCallback , {
root : elem ,
rootMargin : "800px" ,
threshold : 0.0 ,
} ) ;
2022-07-12 07:43:58 +02:00
}
2022-07-14 09:54:31 +02:00
let wvElem = document . getElementById ( this . getWindowViewDOMId ( ) ) ;
if ( wvElem != null ) {
this . rszObs = new ResizeObserver ( this . handleResize . bind ( this ) ) ;
this . rszObs . observe ( wvElem ) ;
2022-07-14 08:11:45 +02:00
}
2022-07-12 07:43:58 +02:00
}
2022-07-14 09:54:31 +02:00
updateWidth ( width : number ) {
mobx . action ( ( ) = > {
this . width . set ( width ) ;
} ) ( ) ;
}
2022-07-12 07:43:58 +02:00
componentWillUnmount() {
2022-07-14 08:11:45 +02:00
if ( this . mutObs ) {
this . mutObs . disconnect ( ) ;
}
2022-07-14 09:54:31 +02:00
if ( this . rszObs ) {
this . rszObs . disconnect ( ) ;
}
2022-08-13 03:34:56 +02:00
if ( this . interObs ) {
this . interObs . disconnect ( ) ;
}
2022-07-14 09:54:31 +02:00
}
handleResize ( entries : any ) {
if ( entries . length == 0 ) {
return ;
}
let entry = entries [ 0 ] ;
let width = entry . target . offsetWidth ;
this . updateWidth ( width ) ;
2022-08-11 19:41:08 +02:00
if ( this . lastHeight == null ) {
this . lastHeight = entry . target . offsetHeight ;
return ;
}
if ( this . lastHeight != entry . target . offsetHeight ) {
this . lastHeight = entry . target . offsetHeight ;
this . doConditionalScrollToBottom ( "resize-height" ) ;
}
2022-07-12 07:43:58 +02:00
}
handleDomMutation ( mutations , mutObs ) {
2022-08-11 19:41:08 +02:00
this . doConditionalScrollToBottom ( "mut" ) ;
}
doConditionalScrollToBottom ( reason : string ) {
2022-07-13 08:29:39 +02:00
let { sw } = this . props ;
if ( sw && sw . shouldFollow . get ( ) ) {
2022-08-11 19:41:08 +02:00
setTimeout ( ( ) = > this . scrollToBottom ( reason ) , 0 ) ;
2022-07-12 07:43:58 +02:00
}
}
getWindow ( ) : Window {
2022-07-13 08:29:39 +02:00
let { sw } = this . props ;
2022-07-15 03:41:49 +02:00
if ( sw == null ) {
return null ;
}
let win = GlobalModel . getWindowById ( sw . sessionId , sw . windowId ) ;
if ( win == null ) {
win = GlobalModel . loadWindow ( sw . sessionId , sw . windowId ) ;
}
return win ;
2022-07-12 07:43:58 +02:00
}
2022-07-14 09:54:31 +02:00
getLinesDOMId() {
2022-08-29 22:54:11 +02:00
return windowLinesDOMId ( this . getWindowId ( ) ) ;
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-14 08:11:45 +02:00
setTimeout ( ( ) = > this . scrollToBottom ( "termresize" ) , 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 {
2022-07-14 09:54:31 +02:00
// return {width: "100%", height: "100%"};
return { position : "absolute" , width : "100%" , height : "100%" , overflowX : "hidden" } ;
}
getWindowId ( ) : string {
let { sw } = this . props ;
if ( sw == null ) {
if ( ! this . randomId ) {
this . randomId = uuidv4 ( ) ;
}
return this . randomId ;
}
return sw . windowId ;
}
getWindowViewDOMId() {
return sprintf ( "window-view-%s" , this . getWindowId ( ) ) ;
2022-07-13 08:29:39 +02:00
}
2022-07-12 07:43:58 +02:00
renderError ( message : string ) {
2022-07-14 08:11:45 +02:00
let { sw } = this . props ;
2022-07-12 07:43:58 +02:00
return (
2022-07-14 09:54:31 +02:00
< div className = "window-view" style = { this . getWindowViewStyle ( ) } id = { this . getWindowViewDOMId ( ) } >
2022-07-14 08:11:45 +02:00
< div key = "window-tag" className = "window-tag" >
< If condition = { sw != null } >
< span > { sw . name . get ( ) } { sw . shouldFollow . get ( ) ? "*" : "" } < / span >
< / If >
< / div >
2022-07-14 09:54:31 +02:00
< div key = "lines" className = "lines" id = { this . getLinesDOMId ( ) } > < / div >
2022-07-14 08:11:45 +02:00
< div key = "window-empty" className = "window-empty" >
< div > { message } < / div >
2022-07-12 07:43:58 +02:00
< / 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-15 03:41:49 +02:00
if ( win == null || ! win . loaded . get ( ) ) {
2022-07-12 07:43:58 +02:00
return this . renderError ( "(loading)" ) ;
2022-07-02 22:32:25 +02:00
}
2022-07-15 03:41:49 +02:00
if ( win . loadError . get ( ) != null ) {
return this . renderError ( sprintf ( "(%s)" , win . loadError . get ( ) ) ) ;
}
2022-07-14 09:54:31 +02:00
if ( this . width . get ( ) == 0 ) {
return this . renderError ( "" ) ;
}
2022-07-05 07:18:36 +02:00
let idx = 0 ;
2022-07-05 07:37:45 +02:00
let line : LineType = null ;
2022-07-14 08:11:45 +02:00
let screen = GlobalModel . getScreenById ( sw . sessionId , sw . screenId ) ;
let session = GlobalModel . getSessionById ( sw . sessionId ) ;
let linesStyle : any = { } ;
if ( win . lines . length == 0 ) {
linesStyle . display = "none" ;
}
2022-08-17 00:59:28 +02:00
let cmdRefMap : Record < string , number > = { } ;
let cmdNum = 1 ;
for ( let i = 0 ; i < win.lines.length ; i + + ) {
let line = win . lines [ i ] ;
if ( line . cmdid != null ) {
cmdRefMap [ line . lineid ] = cmdNum ;
cmdNum ++ ;
}
}
2022-06-08 02:25:35 +02:00
return (
2022-07-14 09:54:31 +02:00
< div className = "window-view" style = { this . getWindowViewStyle ( ) } id = { this . getWindowViewDOMId ( ) } >
2022-07-14 08:11:45 +02:00
< div key = "window-tag" className = "window-tag" >
< span > { sw . name . get ( ) } { sw . shouldFollow . get ( ) ? "*" : "" } < / span >
< / div >
2022-07-14 09:54:31 +02:00
< div key = "lines" className = "lines" onScroll = { this . scrollHandler } id = { this . getLinesDOMId ( ) } style = { linesStyle } >
2022-08-27 07:07:12 +02:00
< div className = "lines-spacer" > < / div >
2022-07-11 23:43:18 +02:00
< For each = "line" of = { win . lines } index = "idx" >
2022-08-17 00:59:28 +02:00
< Line key = { line . lineid } line = { line } sw = { sw } width = { this . width . get ( ) } interObs = { this . interObs } initVis = { idx > win . lines . length - 1 - 7 } cmdRefNum = { cmdRefMap [ line . lineid ] ? ? 0 } / >
2022-06-08 02:25:35 +02:00
< / For >
< / div >
2022-07-14 08:11:45 +02:00
< If condition = { win . lines . length == 0 } >
< div key = "window-empty" className = "window-empty" >
< div > < code > [ session = "{session.name.get()}" screen = "{screen.name.get()}" window = "{sw.name.get()}" ] < / code > < / div >
< / div >
< / If >
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" >
2022-07-14 08:11:45 +02:00
< ScreenWindowView key = { sw . windowId } sw = { sw } / >
2022-07-13 08:29:39 +02:00
< / div >
) ;
}
}
@mobxReact . observer
2022-07-14 08:11:45 +02:00
class ScreenTabs extends React . Component < { session : Session } , { } > {
@boundMethod
handleNewScreen() {
let { session } = this . props ;
2022-08-31 02:05:35 +02:00
GlobalCommandRunner . createNewScreen ( ) ;
2022-07-14 08:11:45 +02:00
}
@boundMethod
handleSwitchScreen ( screenId : string ) {
let { session } = this . props ;
if ( session == null ) {
return ;
}
if ( session . activeScreenId . get ( ) == screenId ) {
return ;
}
let screen = session . getScreenById ( screenId ) ;
if ( screen == null ) {
return ;
}
2022-08-31 02:05:35 +02:00
GlobalCommandRunner . switchScreen ( screenId ) ;
2022-07-14 08:11:45 +02:00
}
2022-08-09 01:22:36 +02:00
handleContextMenu ( e : any , screenId : string ) : void {
e . preventDefault ( ) ;
console . log ( "handle context menu!" , screenId ) ;
let model = GlobalModel ;
2022-08-11 20:49:46 +02:00
model . contextScreen ( e , screenId ) ;
2022-08-09 01:22:36 +02:00
}
2022-08-27 06:43:48 +02:00
2022-07-13 08:29:39 +02:00
render() {
2022-07-14 08:11:45 +02:00
let { session } = this . props ;
2022-07-13 08:29:39 +02:00
if ( session == null ) {
return null ;
}
2022-07-14 08:11:45 +02:00
let screen : Screen = null ;
2022-07-15 03:41:49 +02:00
let index = 0 ;
2022-07-13 08:29:39 +02:00
return (
< div className = "screen-tabs" >
2022-07-15 03:41:49 +02:00
< For each = "screen" index = "index" of = { session . screens } >
2022-08-27 06:43:48 +02:00
< div key = { screen . screenId } className = { cn ( "screen-tab" , { "is-active" : session . activeScreenId . get ( ) == screen . screenId } , "color-" + screen . getTabColor ( ) ) } onClick = { ( ) = > this . handleSwitchScreen ( screen . screenId ) } onContextMenu = { ( event ) = > this . handleContextMenu ( event , screen . screenId ) } >
2022-07-14 08:11:45 +02:00
{ screen . name . get ( ) }
2022-07-15 03:41:49 +02:00
< If condition = { index + 1 <= 9 } >
< div className = "tab-index" > & # x2318 ; { index + 1 } < / div >
< / If >
2022-07-14 08:11:45 +02:00
< / div >
< / For >
< div key = "new-screen" className = "screen-tab new-screen" onClick = { this . handleNewScreen } >
+
< / div >
2022-07-13 08:29:39 +02:00
< / 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 } / >
2022-07-14 08:11:45 +02:00
< ScreenTabs session = { session } / >
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 ) {
2022-08-31 02:05:35 +02:00
GlobalCommandRunner . switchSession ( sessionId ) ;
2022-08-09 01:22:36 +02:00
}
handleNewSession() {
2022-08-31 02:05:35 +02:00
GlobalCommandRunner . createNewSession ( ) ;
2022-07-08 22:23:00 +02:00
}
2022-07-09 10:37:19 +02:00
2022-08-18 09:39:06 +02:00
clickRemotes() {
mobx . action ( ( ) = > {
GlobalModel . remotesModalOpen . set ( true ) ;
} ) ( ) ;
}
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-08-17 22:06:47 +02:00
let remotes = model . remotes ;
let remote : RemoteType = null ;
2022-08-27 02:28:56 +02:00
let idx : number = 0 ;
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-08-27 02:28:56 +02:00
< For each = "session" index = "idx" of = { model . sessionList } >
< li key = { session . sessionId } > < a className = { cn ( { "is-active" : activeSessionId == session . sessionId } ) } onClick = { ( ) = > this . handleSessionClick ( session . sessionId ) } >
< span className = "session-num" > { idx + 1 } & nbsp ; < / span >
{ session . name . get ( ) }
< / a > < / li >
2022-07-11 23:43:18 +02:00
< / For >
2022-08-09 01:22:36 +02:00
< li className = "new-session" > < a className = "new-session" onClick = { ( ) = > this . handleNewSession ( ) } > < i className = "fa fa-plus" / > New Session < / a > < / li >
2022-07-11 23:43:18 +02:00
< / 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-08-27 02:28:56 +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" >
2022-08-18 09:39:06 +02:00
< a onClick = { ( ) = > this . clickRemotes ( ) } > Remotes < / a >
2022-06-20 22:03:20 +02:00
< / p >
< ul className = "menu-list" >
2022-08-17 22:06:47 +02:00
< For each = "remote" of = { remotes } >
2022-08-18 09:39:06 +02:00
< li key = { remote . remoteid } > < a > < i className = { cn ( "remote-status fa fa-circle" , "status-" + remote . status ) } / > { remote . remotealias ? ? remote . remotecanonicalname } < / a > < / li >
2022-08-17 22:06:47 +02:00
< / For >
2022-06-20 22:03:20 +02:00
< / ul >
< div className = "bottom-spacer" > < / div >
< / div >
< / div >
) ;
}
}
2022-08-18 09:39:06 +02:00
@mobxReact . observer
class RemoteModal extends React . Component < { } , { } > {
@boundMethod
handleModalClose ( ) : void {
mobx . action ( ( ) = > {
GlobalModel . remotesModalOpen . set ( false ) ;
} ) ( ) ;
}
@boundMethod
handleAddRemote ( ) : void {
console . log ( "add-remote" ) ;
}
render() {
let model = GlobalModel ;
let remotes = model . remotes ;
let remote : RemoteType = null ;
return (
< div className = "remote-modal modal is-active" >
< div onClick = { this . handleModalClose } className = "modal-background" > < / div >
< div className = "modal-content message" >
< div className = "message-header" >
< p > Remotes < / p >
< / div >
< div className = "remotes-content" >
< table className = "table" >
< thead >
< tr >
< th className = "status-header" > Status < / th >
< th > Alias < / th >
< th > User @Host < / th >
2022-08-21 21:26:10 +02:00
< th > Connect < / th >
2022-08-18 09:39:06 +02:00
< / tr >
< / thead >
< tbody >
< For each = "remote" of = { remotes } >
< tr >
< td className = "status-cell" >
< div > < i className = { cn ( "remote-status fa fa-circle" , "status-" + remote . status ) } / > < / div >
< / td >
< td >
{ remote . remotealias }
< If condition = { isBlank ( remote . remotealias ) } >
-
< / If >
< / td >
< td >
{ remote . remotecanonicalname }
< / td >
< td >
2022-08-21 21:26:10 +02:00
{ remote . connectmode }
2022-08-18 09:39:06 +02:00
< / td >
< / tr >
< / For >
< / tbody >
< / table >
< / div >
< div className = "remotes-footer" >
< button onClick = { this . handleAddRemote } className = "button is-primary" >
< span className = "icon" >
< i className = "fa fa-plus" / >
< / span >
< span > Add Remote < / span >
< / button >
< div className = "spacer" > < / div >
< button onClick = { this . handleModalClose } className = "button" > Close < / button >
< / div >
< / div >
< button onClick = { this . handleModalClose } className = "modal-close is-large" aria - label = "close" > < / button >
< / 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" > & # 9608 ; < / 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-08-18 09:39:06 +02:00
< If condition = { GlobalModel . remotesModalOpen . get ( ) } >
< RemoteModal / >
< / If >
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