mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
dark theme, follow content when at bottom, date format
This commit is contained in:
parent
0cce8ad503
commit
365e9c55a2
104
src/main.tsx
104
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 (
|
||||
<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(() => {
|
||||
setTimeout(() => this.scrollIntoView(), 100);
|
||||
line.isnew = false;
|
||||
})();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
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">> {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}/>
|
||||
|
@ -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};
|
||||
|
59
src/sh2.less
59
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;
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user