From 365e9c55a23da5dcf5b81333a670df1e196e51e1 Mon Sep 17 00:00:00 2001 From: sawka Date: Fri, 17 Jun 2022 17:54:14 -0700 Subject: [PATCH] dark theme, follow content when at bottom, date format --- src/main.tsx | 106 ++++++++++++++++++++++++++++++++++++------------- src/session.ts | 10 ++++- src/sh2.less | 59 +++++++++++++++++++++------ src/term.ts | 4 ++ 4 files changed, 138 insertions(+), 41 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index ec8707f46..d3ac22751 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -7,7 +7,10 @@ import * as dayjs from 'dayjs' import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components"; import cn from "classnames" import {TermWrap} from "./term"; -import {getDefaultSession} from "./session"; +import {getDefaultSession, getLineId} from "./session"; +import localizedFormat from 'dayjs/plugin/localizedFormat'; + +dayjs.extend(localizedFormat) @mobxReact.observer class LineMeta extends React.Component<{line : LineType}, {}> { @@ -23,10 +26,30 @@ class LineMeta extends React.Component<{line : LineType}, {}> { } } +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"); + } +} + @mobxReact.observer class LineText extends React.Component<{line : LineType, session : Session}, {}> { render() { let line = this.props.line; + let formattedTime = getLineDateStr(line.ts); return (
@@ -35,7 +58,7 @@ class LineText extends React.Component<{line : LineType, session : Session}, {}>
{line.userid}
-
{dayjs(line.ts).format("hh:mm:ss a")}
+
{formattedTime}
{line.text} @@ -47,30 +70,26 @@ class LineText extends React.Component<{line : LineType, session : Session}, {}> } @mobxReact.observer -class LineCmd extends React.Component<{line : LineType, session : Session}, {}> { +class LineCmd extends React.Component<{line : LineType, session : Session, changeSizeCallback : () => void}, {}> { constructor(props) { super(props); } componentDidMount() { let {session, line} = this.props; - let termElem = document.getElementById(this.getId()); + let termElem = document.getElementById("term-" + getLineId(line)); let termWrap = session.getTermWrapByLine(line); + termWrap.changeSizeCallback = this.props.changeSizeCallback; termWrap.connectToElem(termElem); if (line.isnew) { - setTimeout(() => { - let lineElem = document.getElementById("line-" + this.getId()); - lineElem.scrollIntoView({block: "end"}); - mobx.action(() => { - line.isnew = false; - })(); - }, 100); + setTimeout(() => this.scrollIntoView(), 100); + line.isnew = false; } } - getId() : string { - let {line} = this.props; - return "cmd-" + line.lineid + "-" + line.cmdid; + scrollIntoView() { + let lineElem = document.getElementById("line-" + getLineId(this.props.line)); + lineElem.scrollIntoView({block: "end"}); } @boundMethod @@ -106,25 +125,32 @@ class LineCmd extends React.Component<{line : LineType, session : Session}, {}> let renderVersion = termWrap.getRenderVersion(); termWrap.resizeToContent(); let termSize = termWrap.getSize(); + let formattedTime = getLineDateStr(line.ts); return ( -
+
= 5}, {"running": running})}> {lineid}
-
{line.userid}
-
{dayjs(line.ts).format("hh:mm:ss a")}
-
{line.cmdid} 0}>({termSize.rows}x{termSize.cols}) {termWrap.ptyPos} bytes, v{renderVersion}
-
> {this.singleLineCmdText(line.cmdtext)}
+
{line.userid}
+
{formattedTime}
+
+
+
+ {line.cmdid} + 0}>({termSize.rows}x{termSize.cols}) + {termWrap.ptyPos} bytes, v{renderVersion} +
+
+ [mike@local ~] {this.singleLineCmdText(line.cmdtext)} +
-
+
-
-
Refresh
-
+
Refresh
); } @@ -183,7 +209,7 @@ class CmdInput extends React.Component<{session : Session, windowid : string}, {
- [ mike@imac27 master ~/work/gopath/src/github.com/sawka/darktile-termutil ] + [ mike@imac27 master ~/work/gopath/src/github.com/sawka/darktile-termutil ]
@@ -208,15 +234,41 @@ class CmdInput extends React.Component<{session : Session, windowid : string}, { @mobxReact.observer class SessionView extends React.Component<{session : SessionType}, {}> { + shouldFollow : IObservableValue = mobx.observable.box(true); + + @boundMethod + scrollHandler(event : any) { + let target = event.target; + let atBottom = (target.scrollTop + 30 > (target.scrollHeight - target.offsetHeight)); + mobx.action(() => this.shouldFollow.set(atBottom))(); + } + + @boundMethod + changeSizeCallback(term : TermWrap) { + console.log("changesize", term); + if (this.shouldFollow.get()) { + let session = this.props.session; + let window = session.getActiveWindow(); + let lines = window.lines; + if (lines == null || lines.length == 0) { + return; + } + let lastLine = lines[lines.length-1]; + let lineElem = document.getElementById("line-" + getLineId(lastLine)); + setTimeout(() => lineElem.scrollIntoView({block: "end"}), 0); + } + } + render() { let session = this.props.session; let window = session.getActiveWindow(); let lines = window.lines; + let idx = 0; return (
-
- - +
+ +
diff --git a/src/session.ts b/src/session.ts index edd7200e0..99f2c4373 100644 --- a/src/session.ts +++ b/src/session.ts @@ -10,8 +10,10 @@ var GSessionId = "47445c53-cfcf-4943-8339-2c04447f20a1"; var GWindowId = "1"; var GlobalLines = mobx.observable.box([ - {sessionid: GSessionId, windowid: GWindowId, lineid: 1, userid: "sawka", ts: 1654631122000, linetype: "text", text: "hello"}, + {sessionid: GSessionId, windowid: GWindowId, lineid: 1, userid: "sawka", ts: 1424631125000, linetype: "text", text: "hello"}, {sessionid: GSessionId, windowid: GWindowId, lineid: 2, userid: "sawka", ts: 1654631125000, linetype: "text", text: "again"}, + {sessionid: GSessionId, windowid: GWindowId, lineid: 3, userid: "sawka", ts: 1655403002683, linetype: "text", text: "more..."}, + {sessionid: GSessionId, windowid: GWindowId, lineid: 4, userid: "sawka", ts: 1655513202683, linetype: "cmd", cmdid: "e74a7db7-58f5-47ef-b351-364c7ba2bfbb", cmdtext: "ls"}, ]); function makeTermKey(sessionId : string, cmdId : string, windowId : string, lineid : number) : string { @@ -31,6 +33,10 @@ type LineType = { isnew : boolean, }; +function getLineId(line : LineType) : string { + return sprintf("%s-%s-%s", line.sessionid, line.windowid, line.lineid); +} + type WindowType = { sessionid : string; windowid : string; @@ -112,5 +118,5 @@ function getDefaultSession() : Session { window.getDefaultSession = getDefaultSession; -export {Session, getDefaultSession}; +export {Session, getDefaultSession, getLineId}; export type {LineType, WindowType}; diff --git a/src/sh2.less b/src/sh2.less index a55906312..3452934c3 100644 --- a/src/sh2.less +++ b/src/sh2.less @@ -21,15 +21,26 @@ font-size: 1rem; } } + + .session-view { + background-color: black; + } } .line { - margin: 5px; + padding: 10px 5px 5px 5px; + margin: 0px 5px 5px 5px; border-radius: 5px; - padding: 5px; display: flex; flex-direction: row; line-height: 1.25; + border-top: 1px solid #777; + + &:first-child { + margin: 0px 5px 5px 5px; + padding: 0px 5px 5px 5px; + border-top: none; + } .avatar { height: 38px; @@ -69,33 +80,39 @@ margin-top: -4px; .user { + color: lighten(#729fcf, 10%); font-weight: bold; + margin-top: 1px; + margin-right: 10px; } .ts { - margin-left: 10px; - margin-top: 4px; - font-size: 0.75rem; - color: #777; + color: #ddd; + margin-top: 5px; + font-size: 12px; } .metapart-mono { - color: #777; + color: #ddd; margin-left: 8px; - font-size: 0.75rem; - margin-top: 5px; + font-size: 14px; + margin-top: 4px; font-family: 'JetBrains Mono', monospace; font-weight: 400; } .cmdtext { - color: black; + color: #fff; + font-size: 16px; + font-weight: bold; overflow: hidden; + margin-left: 0; } } .text { font-size: 1rem; + color: #ddd; } } @@ -107,11 +124,24 @@ align-self: flex-start; &.focus { - outline: 3px solid blue; + box-shadow: -8px 0 12px -5px #aaa; } } } +body .xterm .xterm-viewport { + overflow-y: auto; +} + +.xterm-viewport::-webkit-scrollbar { + background-color: #777; + width: 5px; +} + +.xterm-viewport::-webkit-scrollbar-thumb { + background: white; +} + .line.line-cmd { } @@ -121,13 +151,14 @@ } .lines { - background-color: #f7f7f7; + background-color: black; display: flex; flex-direction: column; height: 80vh; overflow-y: scroll; padding-bottom: 10px; padding-top: 10px; + padding-right: 15px; border-top: 2px solid #ddd; } @@ -204,6 +235,10 @@ color: #fff; } +.term-bright-green { + color: #8ae234; +} + .monofont-thin { font-family: 'JetBrains Mono', monospace; font-weight: 200; diff --git a/src/term.ts b/src/term.ts index b3d5f86e7..f4dc46bf7 100644 --- a/src/term.ts +++ b/src/term.ts @@ -33,6 +33,7 @@ class TermWrap { cols : number = 80; atRowMax : boolean = false; initialized : boolean = false; + changeSizeCallback : (TermWrap) => void = null; constructor(sessionId : string, cmdId : string) { this.termId = uuidv4(); @@ -104,6 +105,9 @@ class TermWrap { return; } term.resize(this.cols, newRows); + if (this.changeSizeCallback) { + setTimeout(() => this.changeSizeCallback(this), 0); + } } setSize(rows : number, cols : number, flexRows : boolean) {