mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-22 21:42:49 +01:00
webshare controls, sync selected, copy
This commit is contained in:
parent
cf7c25e78d
commit
a236348e44
50
src/sh2.less
50
src/sh2.less
@ -3208,9 +3208,37 @@ body.prompt-webshare #main {
|
||||
}
|
||||
}
|
||||
|
||||
.webshare-controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-shrink: 0;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
background-color: darken(@prompt-green, 30%);
|
||||
color: white;
|
||||
|
||||
.screen-sharename {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sync-control {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
color: @term-white;
|
||||
font-size: 13px;
|
||||
align-items: center;
|
||||
|
||||
div:first-child {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prompt-content {
|
||||
flex-grow: 1;
|
||||
padding: 10px;
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
@ -3221,10 +3249,11 @@ body.prompt-webshare #main {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.web-lines {
|
||||
|
||||
.lines {
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3259,4 +3288,19 @@ body.prompt-webshare #main {
|
||||
margin-left: 0px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.lines .line.line-cmd {
|
||||
.line-icon.copy-icon {
|
||||
color: @term-white;
|
||||
font-size: 18px;
|
||||
|
||||
&:hover {
|
||||
color: @term-bright-white;
|
||||
}
|
||||
}
|
||||
|
||||
.copied-indicator {
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import * as util from "./util";
|
||||
import {windowWidthToCols, windowHeightToRows, termHeightFromRows, termWidthFromCols} from "./textmeasure";
|
||||
import {debounce, throttle} from "throttle-debounce";
|
||||
import {LinesView} from "./linesview";
|
||||
import {Toggle} from "./elements";
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
type OArr<V> = mobx.IObservableArray<V>;
|
||||
@ -122,6 +123,11 @@ class WebLineCmdView extends React.Component<{line : T.WebLine, cmd : T.WebCmd,
|
||||
isCmdExpanded : OV<boolean> = mobx.observable.box(false, {name: "cmd-expanded"});
|
||||
isOverflow : OV<boolean> = mobx.observable.box(false, {name: "line-overflow"});
|
||||
cmdTextRef : React.RefObject<any> = React.createRef();
|
||||
copiedIndicator : OV<boolean> = mobx.observable.box(false, {name: "copiedIndicator"});
|
||||
|
||||
componentDidMount() : void {
|
||||
this.checkCmdText();
|
||||
}
|
||||
|
||||
renderSimple() {
|
||||
let {line} = this.props;
|
||||
@ -176,10 +182,36 @@ class WebLineCmdView extends React.Component<{line : T.WebLine, cmd : T.WebCmd,
|
||||
);
|
||||
}
|
||||
|
||||
checkCmdText() {
|
||||
let metaElem = this.cmdTextRef.current;
|
||||
if (metaElem == null || metaElem.childNodes.length == 0) {
|
||||
return;
|
||||
}
|
||||
let metaElemWidth = metaElem.offsetWidth;
|
||||
let metaChild = metaElem.firstChild;
|
||||
let children = metaChild.childNodes;
|
||||
let childWidth = 0;
|
||||
for (let i=0; i<children.length; i++) {
|
||||
let ch = children[i];
|
||||
childWidth += ch.offsetWidth;
|
||||
}
|
||||
let isOverflow = (childWidth > metaElemWidth);
|
||||
if (isOverflow != this.isOverflow.get()) {
|
||||
mobx.action(() => {
|
||||
this.isOverflow.set(isOverflow);
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleHeightChange() : void {
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleClick() : void {
|
||||
WebShareModel.setSelectedLine(this.props.line.linenum);
|
||||
}
|
||||
|
||||
renderMetaWrap() {
|
||||
let {line, cmd} = this.props;
|
||||
let formattedTime = lineutil.getLineDateTimeStr(line.ts);
|
||||
@ -202,11 +234,32 @@ class WebLineCmdView extends React.Component<{line : T.WebLine, cmd : T.WebCmd,
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
copyAllowed() : boolean {
|
||||
return (navigator.clipboard != null);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
clickCopy() : void {
|
||||
if (this.copyAllowed()) {
|
||||
let {cmd} = this.props;
|
||||
navigator.clipboard.writeText(cmd.cmdstr);
|
||||
}
|
||||
mobx.action(() => {
|
||||
this.copiedIndicator.set(true);
|
||||
})();
|
||||
setTimeout(() => {
|
||||
mobx.action(() => {
|
||||
this.copiedIndicator.set(false);
|
||||
})();
|
||||
}, 600);
|
||||
}
|
||||
|
||||
render() {
|
||||
let {line, cmd, topBorder} = this.props;
|
||||
let model = WebShareModel;
|
||||
let isSelected = mobx.computed(() => (model.getSelectedLine() == line.linenum), {name: "computed-isSelected"}).get();
|
||||
let isServerSelected = mobx.computed(() => (model.getServerSelectedLine() == line.linenum), {name: "computed-isServerSelected"}).get();
|
||||
let rendererPlugin : T.RendererPluginType = null;
|
||||
let isNoneRenderer = (line.renderer == "none");
|
||||
if (!isBlank(line.renderer) && line.renderer != "terminal" && !isNoneRenderer) {
|
||||
@ -219,12 +272,23 @@ class WebLineCmdView extends React.Component<{line : T.WebLine, cmd : T.WebCmd,
|
||||
if (width == 0) {
|
||||
width = 1024;
|
||||
}
|
||||
let isExpanded = this.isCmdExpanded.get();
|
||||
return (
|
||||
<div className={mainCn} data-lineid={line.lineid} data-linenum={line.linenum}>
|
||||
<div key="focus" className={cn("focus-indicator", {"selected active": isSelected})}/>
|
||||
<div className="line-header">
|
||||
<div className={mainCn} data-lineid={line.lineid} data-linenum={line.linenum} onClick={this.handleClick}>
|
||||
<If condition={this.copiedIndicator.get()}>
|
||||
<div key="copied" className="copied-indicator">
|
||||
<div>copied</div>
|
||||
</div>
|
||||
</If>
|
||||
<div key="focus" className={cn("focus-indicator", {"selected": isSelected || isServerSelected}, {"active": isSelected})}/>
|
||||
<div className={cn("line-header", {"is-expanded": isExpanded})}>
|
||||
<LineAvatar line={line} cmd={cmd}/>
|
||||
{this.renderMetaWrap()}
|
||||
<If condition={this.copyAllowed()}>
|
||||
<div key="copy" title="Copy Command" className={cn("line-icon copy-icon")} onClick={this.clickCopy} style={{marginLeft: 5}}>
|
||||
<i className="fa-sharp fa-solid fa-copy"/>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
<TerminalRenderer line={line} cmd={cmd} width={width} staticRender={false} visible={visObs} onHeightChange={this.handleHeightChange}/>
|
||||
</div>
|
||||
@ -472,15 +536,16 @@ class WebShareMain extends React.Component<{}, {}> {
|
||||
render() {
|
||||
let screen = WebShareModel.screen.get();
|
||||
let errMessage = WebShareModel.errMessage.get();
|
||||
let shareName = "";
|
||||
if (screen != null) {
|
||||
shareName = isBlank(screen.screen.sharename) ? "(no name)" : screen.screen.sharename;
|
||||
}
|
||||
return (
|
||||
<div id="main">
|
||||
<div className="logo-header">
|
||||
<div className="logo-text">
|
||||
<a target="_blank" href="https://www.getprompt.dev">[prompt]</a>
|
||||
</div>
|
||||
<If condition={screen != null}>
|
||||
<div className="screen-name">{screen.screen.sharename}</div>
|
||||
</If>
|
||||
<div className="flex-spacer"/>
|
||||
<a href="https://getprompt.dev/download/" target="_blank" className="download-button button is-link">
|
||||
<span>Download Prompt</span>
|
||||
@ -489,6 +554,14 @@ class WebShareMain extends React.Component<{}, {}> {
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div className="webshare-controls">
|
||||
<div className="screen-sharename">{shareName}</div>
|
||||
<div className="flex-spacer"/>
|
||||
<div className="sync-control">
|
||||
<div>Sync Selection</div>
|
||||
<Toggle checked={WebShareModel.syncSelectedLine.get()} onChange={(val) => WebShareModel.setSyncSelectedLine(val)}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="prompt-content">
|
||||
<If condition={screen != null}>
|
||||
<WebScreenView/>
|
||||
|
@ -35,6 +35,8 @@ class WebShareModelClass {
|
||||
contentHeightCache : Record<string, number> = {}; // lineid => height
|
||||
wsControl : WebShareWSControl;
|
||||
anchor : {anchorLine : number, anchorOffset : number} = {anchorLine: 0, anchorOffset: 0};
|
||||
selectedLine : OV<number> = mobx.observable.box(0, {name: "selectedLine"});
|
||||
syncSelectedLine : OV<boolean> = mobx.observable.box(true, {name: "syncSelectedLine"});
|
||||
|
||||
constructor() {
|
||||
let urlParams = new URLSearchParams(window.location.search);
|
||||
@ -42,6 +44,7 @@ class WebShareModelClass {
|
||||
this.screenId = urlParams.get("screenid");
|
||||
setTimeout(() => this.loadFullScreenData(), 10);
|
||||
this.wsControl = new WebShareWSControl(getBaseWSUrl(), this.screenId, this.viewKey, this.wsMessageCallback.bind(this));
|
||||
document.addEventListener("keydown", this.docKeyDownHandler.bind(this));
|
||||
}
|
||||
|
||||
setErrMessage(msg : string) : void {
|
||||
@ -50,19 +53,50 @@ class WebShareModelClass {
|
||||
})();
|
||||
}
|
||||
|
||||
setSyncSelectedLine(val : boolean) : void {
|
||||
mobx.action(() => {
|
||||
this.syncSelectedLine.set(val);
|
||||
if (val) {
|
||||
let fullScreen = this.screen.get();
|
||||
if (fullScreen != null) {
|
||||
this.selectedLine.set(fullScreen.screen.selectedline);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
getSelectedLine() : number {
|
||||
return this.selectedLine.get();
|
||||
}
|
||||
|
||||
getServerSelectedLine() : number {
|
||||
let fullScreen = this.screen.get();
|
||||
if (fullScreen != null) {
|
||||
return fullScreen.screen.selectedline;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
setSelectedLine(lineNum : number) : void {
|
||||
mobx.action(() => {
|
||||
this.selectedLine.set(lineNum);
|
||||
})();
|
||||
}
|
||||
|
||||
updateSelectedLineIndex(delta : number) : void {
|
||||
let fullScreen = this.screen.get();
|
||||
if (fullScreen != null) {
|
||||
return fullScreen.screen.selectedline = lineNum;
|
||||
if (fullScreen == null) {
|
||||
return;
|
||||
}
|
||||
let lineIndex = this.getLineIndex(this.selectedLine.get());
|
||||
if (lineIndex == -1) {
|
||||
return;
|
||||
}
|
||||
lineIndex += delta;
|
||||
let lines = fullScreen.lines;
|
||||
if (lineIndex < 0 || lineIndex >= lines.length) {
|
||||
return;
|
||||
}
|
||||
this.setSelectedLine(lines[lineIndex].linenum);
|
||||
}
|
||||
|
||||
setAnchorFields(anchorLine : number, anchorOffset : number, reason : string) : void {
|
||||
@ -152,6 +186,9 @@ class WebShareModelClass {
|
||||
let fullScreen = this.screen.get();
|
||||
if (msg.screen) {
|
||||
fullScreen.screen = msg.screen;
|
||||
if (this.syncSelectedLine.get()) {
|
||||
this.selectedLine.set(msg.screen.selectedline);
|
||||
}
|
||||
}
|
||||
if (msg.lines != null && msg.lines.length > 0) {
|
||||
for (let line of msg.lines) {
|
||||
@ -199,6 +236,9 @@ class WebShareModelClass {
|
||||
}
|
||||
this.screen.set(screen);
|
||||
this.wsControl.reconnect(true);
|
||||
if (this.syncSelectedLine.get()) {
|
||||
this.selectedLine.set(screen.screen.selectedline);
|
||||
}
|
||||
})();
|
||||
|
||||
}
|
||||
@ -373,6 +413,18 @@ class WebShareModelClass {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
docKeyDownHandler(e : any) : void {
|
||||
if (isModKeyPress(e)) {
|
||||
return;
|
||||
}
|
||||
if (e.code == "PageUp" && e.getModifierState("Meta")) {
|
||||
this.updateSelectedLineIndex(-1);
|
||||
}
|
||||
if (e.code == "PageDown" && e.getModifierState("Meta")) {
|
||||
this.updateSelectedLineIndex(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTermPtyData(termContext : T.TermContextUnion) : Promise<T.PtyDataType> {
|
||||
|
Loading…
Reference in New Issue
Block a user