dark theme, follow content when at bottom, date format

This commit is contained in:
sawka 2022-06-17 17:54:14 -07:00
parent 0cce8ad503
commit 365e9c55a2
4 changed files with 138 additions and 41 deletions

View File

@ -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 (
<div className="line line-text">
<div className="avatar">
@ -35,7 +58,7 @@ class LineText extends React.Component<{line : LineType, session : Session}, {}>
<div className="line-content">
<div className="meta">
<div className="user">{line.userid}</div>
<div className="ts">{dayjs(line.ts).format("hh:mm:ss a")}</div>
<div className="ts">{formattedTime}</div>
</div>
<div className="text">
{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 (
<div className="line line-cmd" id={"line-" + this.getId()}>
<div className="line line-cmd" id={"line-" + getLineId(line)}>
<div className={cn("avatar",{"num4": lineid.length == 4}, {"num5": lineid.length >= 5}, {"running": running})}>
{lineid}
</div>
<div className="line-content">
<div className="meta">
<div className="user">{line.userid}</div>
<div className="ts">{dayjs(line.ts).format("hh:mm:ss a")}</div>
<div className="metapart-mono">{line.cmdid} <If condition={termSize.rows > 0}>({termSize.rows}x{termSize.cols})</If> {termWrap.ptyPos} bytes, v{renderVersion}</div>
<div className="metapart-mono cmdtext">&gt; {this.singleLineCmdText(line.cmdtext)}</div>
<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}
<If condition={termSize.rows > 0}>({termSize.rows}x{termSize.cols})</If>
{termWrap.ptyPos} bytes, v{renderVersion}
</div>
<div className="metapart-mono cmdtext">
<span className="term-bright-green">[mike@local ~]</span> {this.singleLineCmdText(line.cmdtext)}
</div>
</div>
<div className={cn("terminal-wrapper", {"focus": termWrap.isFocused.get()})}>
<div className="terminal" id={this.getId()}></div>
<div className="terminal" id={"term-" + getLineId(line)}></div>
</div>
</div>
<div>
<div onClick={this.doRefresh} className="button">Refresh</div>
</div>
<div onClick={this.doRefresh} className="button has-background-black has-text-white">Refresh</div>
</div>
);
}
@ -183,7 +209,7 @@ class CmdInput extends React.Component<{session : Session, windowid : string}, {
<div className="box cmd-input has-background-black">
<div className="cmd-input-context">
<div className="has-text-white">
<span className="bold term-blue">[ mike@imac27 master ~/work/gopath/src/github.com/sawka/darktile-termutil ]</span>
<span className="bold term-bright-green">[ mike@imac27 master ~/work/gopath/src/github.com/sawka/darktile-termutil ]</span>
</div>
</div>
<div className="cmd-input-field field has-addons">
@ -208,15 +234,41 @@ class CmdInput extends React.Component<{session : Session, windowid : string}, {
@mobxReact.observer
class SessionView extends React.Component<{session : SessionType}, {}> {
shouldFollow : IObservableValue<boolean> = 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 (
<div className="session-view">
<div className="lines">
<For each="line" of={lines}>
<Line key={line.lineid} line={line} session={session}/>
<div className="lines" onScroll={this.scrollHandler}>
<For each="line" of={lines} index="idx">
<Line key={line.lineid} line={line} session={session} changeSizeCallback={this.changeSizeCallback}/>
</For>
</div>
<CmdInput session={session} windowid={window.windowid}/>

View File

@ -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};

View File

@ -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;

View File

@ -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) {