mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
add session history
This commit is contained in:
parent
15c3f2fc10
commit
c3626a6547
68
src/main.tsx
68
src/main.tsx
@ -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>
|
||||||
|
@ -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];
|
||||||
|
323
src/sh2.less
323
src/sh2.less
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user