add session history

This commit is contained in:
sawka 2022-06-20 16:06:37 -07:00
parent 15c3f2fc10
commit c3626a6547
3 changed files with 254 additions and 175 deletions

View File

@ -174,39 +174,91 @@ class Line extends React.Component<{line : LineType, session : Session}, {}> {
@mobxReact.observer @mobxReact.observer
class CmdInput extends React.Component<{session : Session, windowid : string}, {}> { class CmdInput extends React.Component<{session : Session, windowid : string}, {}> {
curLine : mobx.IObservableValue<string> = mobx.observable("", {name: "command-line"}); historyIndex : mobx.IObservableValue<number> = mobx.observable.box(0, {name: "history-index"});
modHistory : mobx.IObservableArray<string> = mobx.observable.array([""], {name: "mod-history"});
@mobx.action @boundMethod @mobx.action @boundMethod
onKeyDown(e : any) { onKeyDown(e : any) {
mobx.action(() => { mobx.action(() => {
let {session} = this.props;
let ctrlMod = e.getModifierState("Control") || e.getModifierState("Meta") || e.getModifierState("Shift"); let ctrlMod = e.getModifierState("Control") || e.getModifierState("Meta") || e.getModifierState("Shift");
if (e.code == "Enter" && !ctrlMod) { if (e.code == "Enter" && !ctrlMod) {
e.preventDefault(); e.preventDefault();
setTimeout(() => this.doSubmitCmd(), 0); setTimeout(() => this.doSubmitCmd(), 0);
return; return;
} }
if (e.code == "ArrowUp") {
e.preventDefault();
let hidx = this.historyIndex.get();
hidx += 1;
if (hidx > session.getNumHistoryItems()) {
hidx = session.getNumHistoryItems();
}
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;
}
// console.log(e.code, e.keyCode, e.key, event.which, ctrlMod, e); // console.log(e.code, e.keyCode, e.key, event.which, ctrlMod, e);
})(); })();
} }
@boundMethod
clearCurLine() {
mobx.action(() => {
this.historyIndex.set(0);
this.modHistory.clear();
this.modHistory[0] = "";
})();
}
@boundMethod
getCurLine() : string {
let {session} = this.props;
let hidx = this.historyIndex.get();
if (hidx < this.modHistory.length && this.modHistory[hidx] != null) {
return this.modHistory[hidx];
}
let hitem = session.getHistoryItem(-hidx);
if (hitem == null) {
return "";
}
return hitem.cmdtext;
}
@boundMethod
setCurLine(val : string) {
let hidx = this.historyIndex.get();
this.modHistory[hidx] = val;
}
@boundMethod @boundMethod
onChange(e : any) { onChange(e : any) {
mobx.action(() => { mobx.action(() => {
this.curLine.set(e.target.value); this.setCurLine(e.target.value);
})(); })();
} }
@boundMethod @boundMethod
doSubmitCmd() { doSubmitCmd() {
let {session, windowid} = this.props; let {session, windowid} = this.props;
let commandStr = this.curLine.get(); let commandStr = this.getCurLine();
mobx.action(() => { let hitem = {cmdtext: commandStr};
this.curLine.set(""); session.addToHistory(hitem);
})(); this.clearCurLine();
session.submitCommand(windowid, commandStr); session.submitCommand(windowid, commandStr);
} }
render() { render() {
let curLine = this.getCurLine();
return ( return (
<div className="box cmd-input has-background-black"> <div className="box cmd-input has-background-black">
<div className="cmd-input-context"> <div className="cmd-input-context">
@ -219,7 +271,7 @@ class CmdInput extends React.Component<{session : Session, windowid : string}, {
<div className="button is-static">mike@local</div> <div className="button is-static">mike@local</div>
</div> </div>
<div className="control cmd-input-control is-expanded"> <div className="control cmd-input-control is-expanded">
<textarea value={this.curLine.get()} onKeyDown={this.onKeyDown} onChange={this.onChange} className="input" type="text"></textarea> <textarea value={curLine} onKeyDown={this.onKeyDown} onChange={this.onChange} className="input" type="text"></textarea>
</div> </div>
<div className="control cmd-exec"> <div className="control cmd-exec">
<div onClick={this.doSubmitCmd} className="button"> <div onClick={this.doSubmitCmd} className="button">
@ -338,7 +390,7 @@ class MainSideBar extends React.Component<{}, {}> {
</a></li> </a></li>
<li><a className="activity"> <li><a className="activity">
<i className="user-status status fa fa-circle"/> <i className="user-status status fa fa-circle"/>
<img className="avatar" src="https://i.pravatar.cc/48?img=6"/> <img className="avatar" src="https://i.pravatar.cc/48?img=5"/>
Michelle T <div className="tag is-link">2</div> Michelle T <div className="tag is-link">2</div>
</a></li> </a></li>
</ul> </ul>

View File

@ -38,10 +38,14 @@ function getLineId(line : LineType) : string {
} }
type WindowType = { type WindowType = {
sessionid : string; sessionid : string,
windowid : string; windowid : string,
name : string; name : string,
lines : LineType[]; lines : LineType[],
};
type HistoryItem = {
cmdtext : string,
}; };
class Session { class Session {
@ -51,6 +55,7 @@ class Session {
activeWindowId : string; activeWindowId : string;
termMap : Record<string, TermWrap> = {}; termMap : Record<string, TermWrap> = {};
termMapById : Record<string, TermWrap> = {}; termMapById : Record<string, TermWrap> = {};
history : HistoryItem[] = [];
constructor() { constructor() {
} }
@ -69,6 +74,31 @@ class Session {
}); });
} }
addToHistory(hitem : HistoryItem) {
this.history.push(hitem);
}
getNumHistoryItems() : number {
return this.history.length;
}
getHistoryItem(index : number) : HistoryItem {
if (index == 0) {
return null;
}
if (index > 0) {
if (index > this.history.length-1) {
return null;
}
return this.history[index];
}
let absIndex = Math.abs(index);
if (absIndex > this.history.length) {
return null;
}
return this.history[this.history.length-absIndex];
}
getTermWrapByLine(line : LineType) : TermWrap { getTermWrapByLine(line : LineType) : TermWrap {
let termKey = makeTermKey(line.sessionid, line.cmdid, line.windowid, line.lineid); let termKey = makeTermKey(line.sessionid, line.cmdid, line.windowid, line.lineid);
let termWrap = this.termMap[termKey]; let termWrap = this.termMap[termKey];

View File

@ -2,30 +2,6 @@
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.title.scripthaus-logo-small {
padding-left: 10px;
padding-top: 8px;
padding-bottom: 8px;
margin-bottom: 0;
font-family: 'JetBrains Mono', monospace;
font-weight: 400;
background-color: black;
color: rgb(0, 177, 10);
position: relative;
font-size: 1.5rem;
border-bottom: 2px solid #ddd;
.title-cursor {
position: absolute;
left: 157px;
bottom: 12px;
color: rgb(0, 177, 10);
font-family: 'JetBrains Mono', monospace;
font-weight: 400;
font-size: 1rem;
}
}
.main-content { .main-content {
display: flex; display: flex;
@ -33,145 +9,6 @@
background-color: black; background-color: black;
flex-grow: 1; flex-grow: 1;
.main-sidebar {
border-right: 1px solid #aaa;
padding: 0 5px 5px 5px;
width: 200px;
overflow-x: auto;
display: flex;
flex-direction: column;
color: #ddd;
position: relative;
.menu {
padding-top: 10px;
display: flex;
flex-direction: column;
height: 100%;
.spacer {
flex-grow: 1;
}
.bottom-spacer {
height: 10px;
}
}
.menu-label {
color: #bbb;
}
p.menu-label {
margin-bottom: 0px;
}
.menu-list li.new-session {
a.new-session {
color: #666;
font-size: 13px;
}
.fa {
font-size: 10px;
}
}
.menu-list li a {
color: #bbb;
white-space: nowrap;
padding: 5px 5px 5px 12px;
vertical-align: middle;
position: relative;
.user-status {
position: absolute !important;
top: 24px !important;
left: 32px !important;
}
.avatar {
width: 24px;
height: 24px;
margin-right: 5px;
vertical-align: middle;
}
&.is-active {
color: #ddd;
font-weight: bold;
}
.sub-label {
font-size: 12px;
font-style: italic;
}
&.activity {
font-weight: bold;
color: #ddd;
.tag {
margin-left: 4px;
padding: 0 5px 0 5px;
position: relative;
top: -1px;
}
}
&.is-active:hover {
background-color: #3273dc;
}
&:hover {
background-color: #444;
color: #ddd;
}
.status {
font-size: 8px;
margin-right: 5px;
position: relative;
top: -3px;
color: #4e9a06;
&.offline {
color: #cc0000;
}
}
}
&.collapsed {
width: 50px;
.collapse-container {
right: 12px;
}
.menu {
display: none;
}
}
.collapse-container {
position: absolute;
right: 3px;
top: -4px;
.arrow-container {
color: #777;
padding: 5px;
cursor: pointer;
&:hover {
color: #ddd;
}
}
}
}
.session-view { .session-view {
flex-grow: 1; flex-grow: 1;
@ -181,7 +18,167 @@
} }
} }
.title.scripthaus-logo-small {
padding-left: 10px;
padding-top: 8px;
padding-bottom: 8px;
margin-bottom: 0;
font-family: 'JetBrains Mono', monospace;
font-weight: 400;
background-color: darken(rgb(0, 177, 10), 30%);
color: rgb(0, 177, 10);
position: relative;
font-size: 1.5rem;
border-bottom: 2px solid #ddd;
.title-cursor {
position: absolute;
left: 157px;
bottom: 12px;
color: rgb(0, 177, 10);
font-family: 'JetBrains Mono', monospace;
font-weight: 400;
font-size: 1rem;
}
}
.main-sidebar { .main-sidebar {
border-right: 1px solid #aaa;
padding: 0 5px 5px 5px;
width: 200px;
overflow-x: auto;
display: flex;
flex-direction: column;
color: #ddd;
position: relative;
background-color: darken(rgb(0, 177, 10), 30%);
.menu {
padding-top: 10px;
display: flex;
flex-direction: column;
height: 100%;
.spacer {
flex-grow: 1;
}
.bottom-spacer {
height: 10px;
}
}
.menu-label {
color: #bbb;
}
p.menu-label {
margin-bottom: 0px;
}
.menu-list li.new-session {
a.new-session {
color: #666;
font-size: 13px;
}
.fa {
font-size: 10px;
}
}
.menu-list li a {
color: #bbb;
white-space: nowrap;
padding: 3px 5px 3px 12px;
vertical-align: middle;
position: relative;
font-size: 13px;
.user-status {
position: absolute !important;
top: 24px !important;
left: 32px !important;
}
.avatar {
width: 24px;
height: 24px;
margin-right: 5px;
vertical-align: middle;
}
&.is-active {
color: #ddd;
font-weight: bold;
}
.sub-label {
font-size: 12px;
font-style: italic;
}
&.activity {
font-weight: bold;
color: #ddd;
.tag {
margin-left: 4px;
padding: 0 5px 0 5px;
position: relative;
top: -1px;
}
}
&.is-active:hover {
background-color: #3273dc;
}
&:hover {
background-color: #444;
color: #ddd;
}
.status {
font-size: 8px;
margin-right: 5px;
position: relative;
top: -3px;
color: #4e9a06;
&.offline {
color: #cc0000;
}
}
}
&.collapsed {
width: 50px;
.collapse-container {
right: 12px;
}
.menu {
display: none;
}
}
.collapse-container {
position: absolute;
right: 3px;
top: -4px;
.arrow-container {
color: #777;
padding: 5px;
cursor: pointer;
&:hover {
color: #ddd;
}
}
}
} }
.line { .line {