mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
fix overscroll behavior in xterm.js terminal and deal with focus in a better way. restore scrollbars for terminals. scroll focused terminals into view.
This commit is contained in:
parent
da710fc4c8
commit
64fc0f335a
45
src/main.tsx
45
src/main.tsx
@ -274,6 +274,16 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width
|
||||
);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
clickTermBlock(e : any, handler : string) {
|
||||
let {sw, line} = this.props;
|
||||
let model = GlobalModel;
|
||||
let termWrap = sw.getTermWrap(line.cmdid);
|
||||
if (termWrap != null) {
|
||||
termWrap.terminal.focus();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let {sw, line, width} = this.props;
|
||||
let model = GlobalModel;
|
||||
@ -325,6 +335,9 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn("terminal-wrapper", {"focus": isFocused})} style={{overflowY: "hidden"}}>
|
||||
<If condition={!isFocused}>
|
||||
<div className="term-block" onClick={this.clickTermBlock}></div>
|
||||
</If>
|
||||
<div className="terminal" id={"term-" + getLineId(line)} data-cmdid={line.cmdid} style={{height: totalHeight}}></div>
|
||||
<If condition={!termLoaded}><div style={{position: "absolute", top: 60, left: 30}}>(loading)</div></If>
|
||||
</div>
|
||||
@ -348,7 +361,7 @@ class Line extends React.Component<{sw : ScreenWindow, line : LineType, width :
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class CmdInput extends React.Component<{}, {}> {
|
||||
class TextAreaInput extends React.Component<{}, {}> {
|
||||
lastTab : boolean = false;
|
||||
lastHistoryUpDown : boolean = false;
|
||||
lastTabCurLine : mobx.IObservableValue<string> = mobx.observable.box(null);
|
||||
@ -464,6 +477,23 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
model.submitRawCommand(commandStr, true);
|
||||
}
|
||||
|
||||
render() {
|
||||
let model = GlobalModel;
|
||||
let inputModel = model.inputModel;
|
||||
let curLine = inputModel.getCurLine();
|
||||
let numLines = curLine.split("\n").length;
|
||||
let displayLines = numLines;
|
||||
if (displayLines > 5) {
|
||||
displayLines = 5;
|
||||
}
|
||||
return (
|
||||
<textarea id="main-cmd-input" rows={displayLines} value={curLine} onKeyDown={this.onKeyDown} onChange={this.onChange} className="textarea"></textarea>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class CmdInput extends React.Component<{}, {}> {
|
||||
getAfterSlash(s : string) : string {
|
||||
if (s.startsWith("^/")) {
|
||||
return s.substr(1);
|
||||
@ -479,14 +509,8 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("render CmdInput");
|
||||
let model = GlobalModel;
|
||||
let inputModel = model.inputModel;
|
||||
let curLine = inputModel.getCurLine();
|
||||
let numLines = curLine.split("\n").length;
|
||||
let displayLines = numLines;
|
||||
if (displayLines > 5) {
|
||||
displayLines = 5;
|
||||
}
|
||||
let win = GlobalModel.getActiveWindow();
|
||||
let ri : RemoteInstanceType = null;
|
||||
let rptr : RemotePtrType = null;
|
||||
@ -494,7 +518,6 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
ri = win.getCurRemoteInstance();
|
||||
rptr = win.curRemote.get();
|
||||
}
|
||||
console.log("cmd-input remote", ri);
|
||||
let remote : RemoteType = null;
|
||||
let remoteState : RemoteStateType = null;
|
||||
if (ri != null) {
|
||||
@ -559,7 +582,7 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
<div className="button is-static">{remoteStr}</div>
|
||||
</div>
|
||||
<div className="control cmd-input-control is-expanded">
|
||||
<textarea id="main-cmd-input" rows={displayLines} value={curLine} onKeyDown={this.onKeyDown} onChange={this.onChange} className="textarea"></textarea>
|
||||
<TextAreaInput/>
|
||||
</div>
|
||||
<div className="control cmd-exec">
|
||||
<div onClick={this.doSubmitCmd} className="button">
|
||||
@ -601,7 +624,7 @@ class ScreenWindowView extends React.Component<{sw : ScreenWindow}, {}> {
|
||||
if (sw && sw.shouldFollow.get() != atBottom) {
|
||||
mobx.action(() => sw.shouldFollow.set(atBottom))();
|
||||
}
|
||||
// console.log("scroll-handler>", atBottom, target.scrollTop, target.scrollHeight);
|
||||
// console.log("scroll-handler (sw)>", atBottom, target.scrollTop, target.scrollHeight, event);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -885,10 +885,6 @@ class Model {
|
||||
return;
|
||||
}
|
||||
termWrap.terminal.focus();
|
||||
let lineElem = document.getElementById("line-" + getLineId(switchLine));
|
||||
if (lineElem != null) {
|
||||
lineElem.scrollIntoView({block: "nearest"});
|
||||
}
|
||||
console.log("arrow-up", this.getFocusedLine(), "=>", switchLine);
|
||||
}
|
||||
|
||||
@ -995,6 +991,9 @@ class Model {
|
||||
this.updateCmd(lineMsg.cmd);
|
||||
}
|
||||
}
|
||||
else if ("cmd" in update) {
|
||||
this.updateCmd(update.cmd);
|
||||
}
|
||||
if ("window" in update) {
|
||||
let winMsg : WindowUpdateType = update;
|
||||
this.updateWindow(winMsg.window, false);
|
||||
|
35
src/sh2.less
35
src/sh2.less
@ -313,6 +313,7 @@ html, body, #main {
|
||||
|
||||
.line.line-cmd {
|
||||
flex-direction: column;
|
||||
scroll-margin-bottom: 20px;
|
||||
|
||||
.avatar {
|
||||
cursor: pointer;
|
||||
@ -343,10 +344,7 @@ html, body, #main {
|
||||
align-self: flex-start;
|
||||
|
||||
&.focus {
|
||||
/* box-shadow: -8px 0 12px -5px #aaa; */
|
||||
/* box-shadow: 0 0 0 4px hsl(0, 0%, 20%);*/
|
||||
/* filter:drop-shadow(3px 3px 10px #555); */
|
||||
box-shadow: 0 0 4px 4px rgba(255, 255, 255, 0.3);
|
||||
box-shadow: 0 0 3px 3px rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -470,6 +468,33 @@ html, body, #main {
|
||||
|
||||
body .xterm .xterm-viewport {
|
||||
overflow-y: auto;
|
||||
width: calc(100% + 5px);
|
||||
}
|
||||
|
||||
.terminal-wrapper {
|
||||
position: relative;
|
||||
|
||||
.term-block {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: transparent;
|
||||
z-index: 10;
|
||||
/* background-color: rgba(50, 50, 240, 0.05); */
|
||||
}
|
||||
}
|
||||
|
||||
.terminal-wrapper.focus .xterm {
|
||||
.xterm-screen {
|
||||
overflow-y: scroll;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.xterm-viewport {
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.cmd-input-info, .xterm-viewport {
|
||||
@ -489,7 +514,7 @@ body .xterm .xterm-viewport {
|
||||
}
|
||||
|
||||
.lines {
|
||||
background-color: black;
|
||||
/* background-color: rgba(50, 50, 240, 0.05); */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 80vh;
|
||||
|
@ -64,7 +64,7 @@ class TermWrap {
|
||||
this.usedRows = mobx.observable.box(termOpts.rows);
|
||||
}
|
||||
let cols = termOpts.cols;
|
||||
let maxCols = Math.trunc((winSize.width - 25) / DefaultCellWidth);
|
||||
let maxCols = Math.trunc((winSize.width - 25) / DefaultCellWidth) - 1;
|
||||
if (maxCols > cols) {
|
||||
cols = maxCols;
|
||||
}
|
||||
@ -105,6 +105,12 @@ class TermWrap {
|
||||
mobx.action(() => {
|
||||
this.isFocused.set(focus);
|
||||
})();
|
||||
if (this.connectedElem != null) {
|
||||
let lineElem = this.connectedElem.closest(".line");
|
||||
if (lineElem != null) {
|
||||
lineElem.scrollIntoView({behavior: "smooth", block: "nearest"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getTermUsedRows() : number {
|
||||
@ -200,7 +206,6 @@ class TermWrap {
|
||||
this.dataUpdates.push({data: data, pos: pos});
|
||||
return;
|
||||
}
|
||||
console.log("pty-update", this.cmdId, this.ptyPos, data.length);
|
||||
if (pos > this.ptyPos) {
|
||||
throw new Error(sprintf("invalid pty-update, data-pos[%d] does not match term-pos[%d] cmdid[%s]", pos, this.ptyPos, this.cmdId));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user