mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
PE 125 new tab flow (#43)
* please sync * can select connection. now need add conection * latest for discussion * solved "autoselect connection" issue * ready for PR * pair programming, fix some issues with new tab flow * final updates for new connection flow. integrate add new connection with error messages. add an option to cr to not create the line/history (non-interactive use) --------- Co-authored-by: sawka
This commit is contained in:
parent
4000baa6c5
commit
6016302814
@ -58,7 +58,7 @@ svg.icon {
|
|||||||
|
|
||||||
.hover-effect-base:hover {
|
.hover-effect-base:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.hover-effect-target {
|
.hover-effect-target {
|
||||||
box-shadow: 0px 2px 2px 0 rgba(255, 255, 255, 0.1), 0px 4px 5px 0 rgba(255, 255, 255, 0.2),
|
box-shadow: 0px 2px 2px 0 rgba(255, 255, 255, 0.1), 0px 4px 5px 0 rgba(255, 255, 255, 0.2),
|
||||||
0px 0px 5px 2.5px rgba(255, 255, 255, 0.5);
|
0px 0px 5px 2.5px rgba(255, 255, 255, 0.5);
|
||||||
|
@ -158,8 +158,6 @@
|
|||||||
display: inline;
|
display: inline;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
margin: 0 0.25em 0 1em;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
}
|
||||||
&.is-hidden {
|
&.is-hidden {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -10,6 +10,7 @@ import cn from "classnames";
|
|||||||
import { GlobalModel, GlobalCommandRunner, TabColors } from "../../../model/model";
|
import { GlobalModel, GlobalCommandRunner, TabColors } from "../../../model/model";
|
||||||
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage } from "../common";
|
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage } from "../common";
|
||||||
import { LineType, RendererPluginType, ClientDataType, CommandRtnType } from "../../../types/types";
|
import { LineType, RendererPluginType, ClientDataType, CommandRtnType } from "../../../types/types";
|
||||||
|
import { RemotesSelector } from "../../connections/connections";
|
||||||
import { PluginModel } from "../../../plugins/plugins";
|
import { PluginModel } from "../../../plugins/plugins";
|
||||||
import * as util from "../../../util/util";
|
import * as util from "../../../util/util";
|
||||||
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
|
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
|
||||||
@ -59,7 +60,7 @@ function commandRtnHandler(prtn: Promise<CommandRtnType>, errorMessage: OV<strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId: string }, {}> {
|
class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId: string; inline?: boolean }, {}> {
|
||||||
shareCopied: OV<boolean> = mobx.observable.box(false, { name: "ScreenSettings-shareCopied" });
|
shareCopied: OV<boolean> = mobx.observable.box(false, { name: "ScreenSettings-shareCopied" });
|
||||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
||||||
|
|
||||||
@ -204,30 +205,38 @@ class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId:
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { sessionId, screenId } = this.props;
|
let { sessionId, screenId, inline } = this.props;
|
||||||
let screen = GlobalModel.getScreenById(sessionId, screenId);
|
let screen = GlobalModel.getScreenById(sessionId, screenId);
|
||||||
if (screen == null) {
|
if (screen == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let color: string = null;
|
let color: string = null;
|
||||||
return (
|
return (
|
||||||
<div className={cn("modal screen-settings-modal settings-modal prompt-modal is-active")}>
|
<div
|
||||||
<div className="modal-background" />
|
className={
|
||||||
<div className="modal-content">
|
inline
|
||||||
<If condition={this.shareCopied.get()}>
|
? "screen-settings-inline"
|
||||||
<div className="copied-indicator" />
|
: cn("modal screen-settings-modal settings-modal prompt-modal is-active")
|
||||||
</If>
|
}
|
||||||
<header>
|
>
|
||||||
<div className="modal-title">screen settings ({screen.name.get()})</div>
|
{!inline && <div className="modal-background" />}
|
||||||
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
<div className={inline ? "inline-content" : "modal-content"}>
|
||||||
<XmarkIcon />
|
{this.shareCopied.get() && <div className="copied-indicator" />}
|
||||||
</div>
|
{!inline && (
|
||||||
</header>
|
<header>
|
||||||
|
<div className="modal-title">screen settings ({screen.name.get()})</div>
|
||||||
|
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
||||||
|
<XmarkIcon />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
)}
|
||||||
<div className="inner-content">
|
<div className="inner-content">
|
||||||
<div className="settings-field">
|
{!inline && (
|
||||||
<div className="settings-label">Screen Id</div>
|
<div className="settings-field">
|
||||||
<div className="settings-input">{screen.screenId}</div>
|
<div className="settings-label">Screen Id</div>
|
||||||
</div>
|
<div className="settings-input">{screen.screenId}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="settings-field">
|
<div className="settings-field">
|
||||||
<div className="settings-label">Name</div>
|
<div className="settings-label">Name</div>
|
||||||
<div className="settings-input">
|
<div className="settings-input">
|
||||||
@ -263,39 +272,52 @@ class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="settings-field">
|
<div className="settings-field">
|
||||||
<div className="settings-label">
|
<div className="settings-label">Connection</div>
|
||||||
<div>Archived</div>
|
|
||||||
<InfoMessage width={400}>
|
|
||||||
Archive will hide the screen tab. Commands and output will be retained in history.
|
|
||||||
</InfoMessage>
|
|
||||||
</div>
|
|
||||||
<div className="settings-input">
|
<div className="settings-input">
|
||||||
<Toggle checked={screen.archived.get()} onChange={this.handleChangeArchived} />
|
<RemotesSelector model={GlobalModel.remotesModalModel} isChangeRemoteOnSelect={true} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="settings-field">
|
{!inline && (
|
||||||
<div className="settings-label">
|
<div className="settings-field">
|
||||||
<div>Actions</div>
|
<div className="settings-label">
|
||||||
<InfoMessage width={400}>
|
<div>Archived</div>
|
||||||
Delete will remove the screen, removing all commands and output from history.
|
<InfoMessage width={400}>
|
||||||
</InfoMessage>
|
Archive will hide the screen tab. Commands and output will be retained in
|
||||||
</div>
|
history.
|
||||||
<div className="settings-input">
|
</InfoMessage>
|
||||||
<div
|
</div>
|
||||||
onClick={this.handleDeleteScreen}
|
<div className="settings-input">
|
||||||
className="button is-prompt-danger is-outlined is-small"
|
<Toggle checked={screen.archived.get()} onChange={this.handleChangeArchived} />
|
||||||
>
|
|
||||||
Delete Screen
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
{!inline && (
|
||||||
|
<div className="settings-field">
|
||||||
|
<div className="settings-label">
|
||||||
|
<div>Actions</div>
|
||||||
|
<InfoMessage width={400}>
|
||||||
|
Delete will remove the screen, removing all commands and output from history.
|
||||||
|
</InfoMessage>
|
||||||
|
</div>
|
||||||
|
<div className="settings-input">
|
||||||
|
<div
|
||||||
|
onClick={this.handleDeleteScreen}
|
||||||
|
className="button is-prompt-danger is-outlined is-small"
|
||||||
|
>
|
||||||
|
Delete Screen
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<SettingsError errorMessage={this.errorMessage} />
|
<SettingsError errorMessage={this.errorMessage} />
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
{!inline && (
|
||||||
<div onClick={this.closeModal} className="button is-prompt-green is-outlined is-small">
|
<footer>
|
||||||
Close
|
<div onClick={this.closeModal} className="button is-prompt-green is-outlined is-small">
|
||||||
</div>
|
Close
|
||||||
</footer>
|
</div>
|
||||||
|
</footer>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -628,7 +650,7 @@ class ClientSettingsModal extends React.Component<{}, {}> {
|
|||||||
renderFontSizeDropdown(): any {
|
renderFontSizeDropdown(): any {
|
||||||
let availableFontSizes = [8, 9, 10, 11, 12, 13, 14, 15];
|
let availableFontSizes = [8, 9, 10, 11, 12, 13, 14, 15];
|
||||||
let fsize: number = 0;
|
let fsize: number = 0;
|
||||||
let curSize = GlobalModel.termFontSize.get()
|
let curSize = GlobalModel.termFontSize.get();
|
||||||
return (
|
return (
|
||||||
<div className={cn("dropdown", "font-size-dropdown", { "is-active": this.fontSizeDropdownActive.get() })}>
|
<div className={cn("dropdown", "font-size-dropdown", { "is-active": this.fontSizeDropdownActive.get() })}>
|
||||||
<div className="dropdown-trigger">
|
<div className="dropdown-trigger">
|
||||||
|
@ -67,6 +67,7 @@
|
|||||||
.remote-status-light {
|
.remote-status-light {
|
||||||
width: 2em;
|
width: 2em;
|
||||||
margin-top: 0.7em;
|
margin-top: 0.7em;
|
||||||
|
margin-right: 0.7em;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +77,7 @@
|
|||||||
.remote-name-primary {
|
.remote-name-primary {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
max-width: 10em;
|
max-width: 10em;
|
||||||
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.remote-name-secondary {
|
.remote-name-secondary {
|
||||||
@ -267,3 +269,66 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.remotes-inline {
|
||||||
|
.icon {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
fill: @base-color;
|
||||||
|
margin: 0 0 0 1em !important;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.dropdown {
|
||||||
|
margin-top: 1em;
|
||||||
|
.button {
|
||||||
|
color: @base-color;
|
||||||
|
border: none !important;
|
||||||
|
padding: 0 1em 0 0.2em;
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.remote-name {
|
||||||
|
vertical-align: bottom;
|
||||||
|
.remote-status {
|
||||||
|
top: -2px;
|
||||||
|
left: 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.dropdown-content {
|
||||||
|
background: @background-session-components-solid;
|
||||||
|
}
|
||||||
|
.dropdown-item {
|
||||||
|
min-width: max-content;
|
||||||
|
}
|
||||||
|
&.is-active:hover {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.remote-status-light {
|
||||||
|
display: inline;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
.remote-name {
|
||||||
|
display: inline;
|
||||||
|
flex-grow: 1;
|
||||||
|
vertical-align: super;
|
||||||
|
|
||||||
|
.remote-name-primary {
|
||||||
|
color: @base-color;
|
||||||
|
max-width: inherit;
|
||||||
|
margin-right: 1em;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remote-name-secondary {
|
||||||
|
color: @disabled-color;
|
||||||
|
max-width: inherit;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -226,7 +226,29 @@ class CreateRemote extends React.Component<{ model: RemotesModalModel; remoteEdi
|
|||||||
kwargs["autoinstall"] = this.tempAutoInstall.get() ? "1" : "0";
|
kwargs["autoinstall"] = this.tempAutoInstall.get() ? "1" : "0";
|
||||||
kwargs["visual"] = "1";
|
kwargs["visual"] = "1";
|
||||||
kwargs["submit"] = "1";
|
kwargs["submit"] = "1";
|
||||||
GlobalCommandRunner.createRemote(cname, kwargs);
|
let model = this.props.model;
|
||||||
|
let shouldCr = model.onlyAddNewRemote.get();
|
||||||
|
let prtn = GlobalCommandRunner.createRemote(cname, kwargs, false);
|
||||||
|
prtn.then((crtn) => {
|
||||||
|
if (crtn.success) {
|
||||||
|
if (shouldCr) {
|
||||||
|
let crRtn = GlobalCommandRunner.screenSetRemote(cname, true, false);
|
||||||
|
crRtn.then((crcrtn) => {
|
||||||
|
if (crcrtn.success) {
|
||||||
|
model.closeModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mobx.action(() => {
|
||||||
|
this.errorStr.set(crcrtn.error);
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mobx.action(() => {
|
||||||
|
this.errorStr.set(crtn.error);
|
||||||
|
})();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
@ -1105,7 +1127,7 @@ class RemotesModal extends React.Component<{ model: RemotesModalModel }, {}> {
|
|||||||
renderRemoteMenuItem(remote: RemoteType, selectedId: string): any {
|
renderRemoteMenuItem(remote: RemoteType, selectedId: string): any {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={remote.remoteid}
|
key={remote.remotecanonicalname}
|
||||||
onClick={() => this.selectRemote(remote.remoteid)}
|
onClick={() => this.selectRemote(remote.remoteid)}
|
||||||
className={cn("remote-menu-item", { "is-selected": remote.remoteid == selectedId })}
|
className={cn("remote-menu-item", { "is-selected": remote.remoteid == selectedId })}
|
||||||
>
|
>
|
||||||
@ -1153,6 +1175,7 @@ class RemotesModal extends React.Component<{ model: RemotesModalModel }, {}> {
|
|||||||
let isAuthEditMode = model.isAuthEditMode();
|
let isAuthEditMode = model.isAuthEditMode();
|
||||||
let selectedRemote = GlobalModel.getRemote(selectedRemoteId);
|
let selectedRemote = GlobalModel.getRemote(selectedRemoteId);
|
||||||
let remoteEdit = model.remoteEdit.get();
|
let remoteEdit = model.remoteEdit.get();
|
||||||
|
let onlyAddNewRemote = model.onlyAddNewRemote.get();
|
||||||
return (
|
return (
|
||||||
<div className={cn("modal remotes-modal settings-modal prompt-modal is-active")}>
|
<div className={cn("modal remotes-modal settings-modal prompt-modal is-active")}>
|
||||||
<div className="modal-background" />
|
<div className="modal-background" />
|
||||||
@ -1164,19 +1187,21 @@ class RemotesModal extends React.Component<{ model: RemotesModalModel }, {}> {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div className="inner-content">
|
<div className="inner-content">
|
||||||
<div className="remotes-menu">
|
<If condition={!onlyAddNewRemote}>
|
||||||
{this.renderAddRemoteMenuItem()}
|
<div className="remotes-menu">
|
||||||
<For each="remote" of={allRemotes}>
|
{this.renderAddRemoteMenuItem()}
|
||||||
{this.renderRemoteMenuItem(remote, selectedRemoteId)}
|
<For each="remote" of={allRemotes}>
|
||||||
</For>
|
{this.renderRemoteMenuItem(remote, selectedRemoteId)}
|
||||||
</div>
|
</For>
|
||||||
|
</div>{" "}
|
||||||
|
</If>
|
||||||
<If condition={selectedRemote == null}>
|
<If condition={selectedRemote == null}>
|
||||||
<If condition={remoteEdit != null}>
|
<If condition={remoteEdit != null}>
|
||||||
<CreateRemote model={model} remoteEdit={remoteEdit} />
|
<CreateRemote model={model} remoteEdit={remoteEdit} />
|
||||||
</If>
|
</If>
|
||||||
<If condition={remoteEdit == null}>{this.renderEmptyDetail()}</If>
|
<If condition={remoteEdit == null}>{this.renderEmptyDetail()}</If>
|
||||||
</If>
|
</If>
|
||||||
<If condition={selectedRemote != null}>
|
<If condition={selectedRemote != null && !onlyAddNewRemote}>
|
||||||
<If condition={!isAuthEditMode}>
|
<If condition={!isAuthEditMode}>
|
||||||
<RemoteDetailView
|
<RemoteDetailView
|
||||||
key={"remotedetail-" + selectedRemoteId}
|
key={"remotedetail-" + selectedRemoteId}
|
||||||
@ -1200,4 +1225,95 @@ class RemotesModal extends React.Component<{ model: RemotesModalModel }, {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { RemotesModal };
|
@mobxReact.observer
|
||||||
|
class RemotesSelector extends React.Component<{ model: RemotesModalModel; isChangeRemoteOnSelect?: boolean }, { isOpen: boolean }> {
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isOpen: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
selectRemote(remoteid: string, remotecanonicalname: string): void {
|
||||||
|
this.props.model.selectRemote(remoteid);
|
||||||
|
if (this.props.isChangeRemoteOnSelect) {
|
||||||
|
let prtn = GlobalCommandRunner.screenSetRemote(remotecanonicalname, true, false);
|
||||||
|
// TODO: see settings.tsx. use prtn to set error message
|
||||||
|
}
|
||||||
|
this.setState({ isOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
clickAddRemote(): void {
|
||||||
|
GlobalModel.remotesModalModel.openModalForEdit({remoteedit: true}, true);
|
||||||
|
this.setState({ isOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRemoteMenuItem(remote: RemoteType, selectedId: string): any {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={remote.remoteid}
|
||||||
|
onClick={() => this.selectRemote(remote.remoteid, remote.remotecanonicalname)}
|
||||||
|
className={cn("dropdown-item remote-menu-item hoverEffect", {
|
||||||
|
"is-selected": remote.remoteid == selectedId,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className="remote-status-light">
|
||||||
|
<RemoteStatusLight remote={remote} />
|
||||||
|
</div>
|
||||||
|
<If condition={util.isBlank(remote.remotealias)}>
|
||||||
|
<div className="remote-name">
|
||||||
|
<div className="remote-name-primary">{remote.remotecanonicalname}</div>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
<If condition={!util.isBlank(remote.remotealias)}>
|
||||||
|
<div className="remote-name">
|
||||||
|
<div className="remote-name-primary">{remote.remotealias}</div>
|
||||||
|
<div className="remote-name-secondary">{remote.remotecanonicalname}</div>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const allRemotes = util.sortAndFilterRemotes(GlobalModel.remotes.slice());
|
||||||
|
const remote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||||
|
const selectedRemoteDiv = (
|
||||||
|
<div className="remote-name">
|
||||||
|
<div className="remote-status-light">
|
||||||
|
<RemoteStatusLight remote={remote} />
|
||||||
|
</div>
|
||||||
|
<div className="remote-name-primary">{remote.remotealias}</div>
|
||||||
|
<div className="remote-name-secondary">{remote.remotecanonicalname}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div className={"remotes-inline"}>
|
||||||
|
<div className="remotes-menu">
|
||||||
|
<div className={`dropdown ${this.state.isOpen ? "is-active" : ""}`}>
|
||||||
|
<div className="dropdown-trigger">
|
||||||
|
<button className="button" onClick={() => this.setState({ isOpen: !this.state.isOpen })}>
|
||||||
|
{selectedRemoteDiv}
|
||||||
|
<AngleDownIcon className="icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="dropdown-menu" id="dropdown-menu3" role="menu">
|
||||||
|
<div className="dropdown-content">
|
||||||
|
{allRemotes
|
||||||
|
.filter(({ remoteid }) => remoteid !== remote.remoteid)
|
||||||
|
.map((remote) => this.renderRemoteMenuItem(remote, remote.remoteid))}
|
||||||
|
<div onClick={this.clickAddRemote} className=".dropdown-item hoverEffect">
|
||||||
|
<AddIcon className="icon" /> Add SSH Connection
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { RemotesModal, RemotesSelector };
|
||||||
|
@ -76,11 +76,6 @@ class MainSideBar extends React.Component<{}, {}> {
|
|||||||
GlobalCommandRunner.showRemote(remote.remoteid);
|
GlobalCommandRunner.showRemote(remote.remoteid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
|
||||||
handleAddRemote(): void {
|
|
||||||
GlobalCommandRunner.openCreateRemote();
|
|
||||||
}
|
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
handleHistoryClick(): void {
|
handleHistoryClick(): void {
|
||||||
if (GlobalModel.activeMainView.get() == "history") {
|
if (GlobalModel.activeMainView.get() == "history") {
|
||||||
@ -200,7 +195,7 @@ class MainSideBar extends React.Component<{}, {}> {
|
|||||||
let mainView = GlobalModel.activeMainView.get();
|
let mainView = GlobalModel.activeMainView.get();
|
||||||
return (
|
return (
|
||||||
<div className={cn("main-sidebar", { collapsed: isCollapsed }, { "is-dev": GlobalModel.isDev })}>
|
<div className={cn("main-sidebar", { collapsed: isCollapsed }, { "is-dev": GlobalModel.isDev })}>
|
||||||
<div className="title-bar-drag"/>
|
<div className="title-bar-drag" />
|
||||||
<div className="arrow-container hoverEffect" onClick={this.toggleCollapsed}>
|
<div className="arrow-container hoverEffect" onClick={this.toggleCollapsed}>
|
||||||
<LeftChevronIcon className="icon" />
|
<LeftChevronIcon className="icon" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,25 +78,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.window-empty {
|
.screen-settings-inline {
|
||||||
display: flex;
|
padding: 2em;
|
||||||
align-items: center;
|
.settings-field {
|
||||||
justify-content: center;
|
display: block;
|
||||||
width: 100%;
|
padding: 1.5em 1em;
|
||||||
padding: 10px;
|
margin-top: 0;
|
||||||
height: 100%;
|
line-height: 2.5em;
|
||||||
color: @term-white;
|
border-top: 1px solid @base-border;
|
||||||
|
&:first-child {
|
||||||
code {
|
border-top: none;
|
||||||
color: @prompt-green;
|
|
||||||
background-color: transparent;
|
|
||||||
font-family: @fixed-font;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.should-fade {
|
|
||||||
opacity: 1;
|
|
||||||
animation: fade-in 2.5s;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,18 @@ import * as mobxReact from "mobx-react";
|
|||||||
import * as mobx from "mobx";
|
import * as mobx from "mobx";
|
||||||
import { sprintf } from "sprintf-js";
|
import { sprintf } from "sprintf-js";
|
||||||
import { boundMethod } from "autobind-decorator";
|
import { boundMethod } from "autobind-decorator";
|
||||||
import { If, For } from "tsx-control-statements/components";
|
import { If } from "tsx-control-statements/components";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
import { debounce } from "throttle-debounce";
|
import { debounce } from "throttle-debounce";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import type { LineType, RenderModeType, LineFactoryProps } from "../../../types/types";
|
import { GlobalCommandRunner } from "../../../model/model";
|
||||||
|
import type { LineType, RenderModeType, LineFactoryProps, CommandRtnType } from "../../../types/types";
|
||||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||||
|
import { InlineSettingsTextEdit } from "../../common/common";
|
||||||
import { GlobalModel, ScreenLines, Screen } from "../../../model/model";
|
import { GlobalModel, ScreenLines, Screen } from "../../../model/model";
|
||||||
import { Line } from "../../line/linecomps";
|
import { Line } from "../../line/linecomps";
|
||||||
import { LinesView } from "../../line/linesview";
|
import { LinesView } from "../../line/linesview";
|
||||||
|
import { ScreenSettingsModal } from "../../common/modals/settings";
|
||||||
|
|
||||||
import "./screenview.less";
|
import "./screenview.less";
|
||||||
import "./tabs.less";
|
import "./tabs.less";
|
||||||
@ -181,11 +184,7 @@ class ScreenWindowView extends React.Component<{ screen: Screen }, {}> {
|
|||||||
if (cdata == null) {
|
if (cdata == null) {
|
||||||
return this.renderError("loading client data", true);
|
return this.renderError("loading client data", true);
|
||||||
}
|
}
|
||||||
let idx = 0;
|
|
||||||
let line: LineType = null;
|
|
||||||
let session = GlobalModel.getSessionById(screen.sessionId);
|
|
||||||
let isActive = screen.isActive();
|
let isActive = screen.isActive();
|
||||||
let selectedLine = screen.getSelectedLine();
|
|
||||||
let lines = win.getNonArchivedLines();
|
let lines = win.getNonArchivedLines();
|
||||||
let renderMode = this.renderMode.get();
|
let renderMode = this.renderMode.get();
|
||||||
return (
|
return (
|
||||||
@ -204,6 +203,14 @@ class ScreenWindowView extends React.Component<{ screen: Screen }, {}> {
|
|||||||
</If>
|
</If>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<If condition={lines.length == 0}>
|
||||||
|
<ScreenSettingsModal
|
||||||
|
key={screen.sessionId + ":" + screen.screenId}
|
||||||
|
sessionId={screen.sessionId}
|
||||||
|
screenId={screen.screenId}
|
||||||
|
inline={true}
|
||||||
|
/>
|
||||||
|
</If>
|
||||||
<If condition={screen.isWebShared()}>
|
<If condition={screen.isWebShared()}>
|
||||||
<div key="share-tag" className="share-tag">
|
<div key="share-tag" className="share-tag">
|
||||||
<If condition={this.shareCopied.get()}>
|
<If condition={this.shareCopied.get()}>
|
||||||
@ -240,13 +247,6 @@ class ScreenWindowView extends React.Component<{ screen: Screen }, {}> {
|
|||||||
lineFactory={this.buildLineComponent}
|
lineFactory={this.buildLineComponent}
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
<If condition={lines.length == 0}>
|
|
||||||
<div key="window-empty" className="window-empty">
|
|
||||||
<div>
|
|
||||||
<code>[workspace="{session.name.get()}" screen="{screen.name.get()}"]</code>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</If>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -194,11 +194,6 @@ class ScreenTabs extends React.Component<{ session: Session }, {}> {
|
|||||||
<AddIcon className="icon hoverEffect" />
|
<AddIcon className="icon hoverEffect" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/**<div className="cmd-hints">
|
|
||||||
<div className="hint-item color-green">move left {renderCmdText("[")}</div>
|
|
||||||
<div className="hint-item color-green">move right {renderCmdText("]")}</div>
|
|
||||||
<div className="hint-item color-green">new tab {renderCmdText("T")}</div>
|
|
||||||
</div>*/}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@ import dayjs from "dayjs";
|
|||||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
import { getRendererContext, cmdStatusIsRunning } from "../app/line/lineutil";
|
import { getRendererContext, cmdStatusIsRunning } from "../app/line/lineutil";
|
||||||
|
import { sortAndFilterRemotes } from "../util/util";
|
||||||
|
|
||||||
dayjs.extend(customParseFormat);
|
dayjs.extend(customParseFormat);
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
@ -2426,6 +2427,9 @@ class RemotesModalModel {
|
|||||||
openState: OV<boolean> = mobx.observable.box(false, {
|
openState: OV<boolean> = mobx.observable.box(false, {
|
||||||
name: "RemotesModalModel-isOpen",
|
name: "RemotesModalModel-isOpen",
|
||||||
});
|
});
|
||||||
|
onlyAddNewRemote: OV<boolean> = mobx.observable.box(false, {
|
||||||
|
name: "RemotesModalModel-onlyAddNewRemote",
|
||||||
|
});
|
||||||
selectedRemoteId: OV<string> = mobx.observable.box(null, {
|
selectedRemoteId: OV<string> = mobx.observable.box(null, {
|
||||||
name: "RemotesModalModel-selectedRemoteId",
|
name: "RemotesModalModel-selectedRemoteId",
|
||||||
});
|
});
|
||||||
@ -2467,8 +2471,9 @@ class RemotesModalModel {
|
|||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
openModalForEdit(redit: RemoteEditType): void {
|
openModalForEdit(redit: RemoteEditType, onlyAddNewRemote: boolean): void {
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
|
this.onlyAddNewRemote.set(onlyAddNewRemote);
|
||||||
this.openState.set(true);
|
this.openState.set(true);
|
||||||
this.selectedRemoteId.set(redit.remoteid);
|
this.selectedRemoteId.set(redit.remoteid);
|
||||||
this.remoteEdit.set(redit);
|
this.remoteEdit.set(redit);
|
||||||
@ -2497,6 +2502,11 @@ class RemotesModalModel {
|
|||||||
cancelEditAuth(): void {
|
cancelEditAuth(): void {
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
this.remoteEdit.set(null);
|
this.remoteEdit.set(null);
|
||||||
|
if (this.onlyAddNewRemote.get()) {
|
||||||
|
this.onlyAddNewRemote.set(false);
|
||||||
|
this.openState.set(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.selectedRemoteId.get() == null) {
|
if (this.selectedRemoteId.get() == null) {
|
||||||
this.openModal();
|
this.openModal();
|
||||||
}
|
}
|
||||||
@ -2519,6 +2529,7 @@ class RemotesModalModel {
|
|||||||
this.openState.set(false);
|
this.openState.set(false);
|
||||||
this.selectedRemoteId.set(null);
|
this.selectedRemoteId.set(null);
|
||||||
this.remoteEdit.set(null);
|
this.remoteEdit.set(null);
|
||||||
|
this.onlyAddNewRemote.set(false);
|
||||||
})();
|
})();
|
||||||
setTimeout(() => GlobalModel.refocus(), 10);
|
setTimeout(() => GlobalModel.refocus(), 10);
|
||||||
}
|
}
|
||||||
@ -3204,7 +3215,7 @@ class Model {
|
|||||||
if (rview.remoteshowall) {
|
if (rview.remoteshowall) {
|
||||||
this.remotesModalModel.openModal();
|
this.remotesModalModel.openModal();
|
||||||
} else if (rview.remoteedit != null) {
|
} else if (rview.remoteedit != null) {
|
||||||
this.remotesModalModel.openModalForEdit(rview.remoteedit);
|
this.remotesModalModel.openModalForEdit(rview.remoteedit, false);
|
||||||
} else if (rview.ptyremoteid) {
|
} else if (rview.ptyremoteid) {
|
||||||
this.remotesModalModel.openModal(rview.ptyremoteid);
|
this.remotesModalModel.openModal(rview.ptyremoteid);
|
||||||
}
|
}
|
||||||
@ -3441,7 +3452,15 @@ class Model {
|
|||||||
uicontext: this.getUIContext(),
|
uicontext: this.getUIContext(),
|
||||||
interactive: interactive,
|
interactive: interactive,
|
||||||
};
|
};
|
||||||
// console.log("CMD", pk.metacmd + (pk.metasubcmd != null ? ":" + pk.metasubcmd : ""), pk.args, pk.kwargs, pk.interactive);
|
/**
|
||||||
|
console.log(
|
||||||
|
"CMD",
|
||||||
|
pk.metacmd + (pk.metasubcmd != null ? ":" + pk.metasubcmd : ""),
|
||||||
|
pk.args,
|
||||||
|
pk.kwargs,
|
||||||
|
pk.interactive
|
||||||
|
);
|
||||||
|
*/
|
||||||
return this.submitCommandPacket(pk, interactive);
|
return this.submitCommandPacket(pk, interactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3455,7 +3474,7 @@ class Model {
|
|||||||
interactive: interactive,
|
interactive: interactive,
|
||||||
rawstr: cmdStr,
|
rawstr: cmdStr,
|
||||||
};
|
};
|
||||||
if (!addToHistory) {
|
if (!addToHistory && pk.kwargs) {
|
||||||
pk.kwargs["nohist"] = "1";
|
pk.kwargs["nohist"] = "1";
|
||||||
}
|
}
|
||||||
return this.submitCommandPacket(pk, interactive);
|
return this.submitCommandPacket(pk, interactive);
|
||||||
@ -3810,14 +3829,28 @@ class CommandRunner {
|
|||||||
GlobalModel.submitCommand("remote", "installcancel", null, { nohist: "1", remote: remoteid }, true);
|
GlobalModel.submitCommand("remote", "installcancel", null, { nohist: "1", remote: remoteid }, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
createRemote(cname: string, kwargsArg: Record<string, string>) {
|
createRemote(cname: string, kwargsArg: Record<string, string>, interactive: boolean): Promise<CommandRtnType> {
|
||||||
let kwargs = Object.assign({}, kwargsArg);
|
let kwargs = Object.assign({}, kwargsArg);
|
||||||
kwargs["nohist"] = "1";
|
kwargs["nohist"] = "1";
|
||||||
GlobalModel.submitCommand("remote", "new", [cname], kwargs, true);
|
return GlobalModel.submitCommand("remote", "new", [cname], kwargs, interactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
openCreateRemote(): void {
|
openCreateRemote(): void {
|
||||||
GlobalModel.submitCommand("remote", "new", null, { nohist: "1", visual: "1" }, true);
|
GlobalModel.submitCommand(
|
||||||
|
"remote",
|
||||||
|
"new",
|
||||||
|
null,
|
||||||
|
{ nohist: "1", visual: "1" },
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
screenSetRemote(remoteArg: string, nohist: boolean, interactive: boolean): Promise<CommandRtnType> {
|
||||||
|
let kwargs = {};
|
||||||
|
if (nohist) {
|
||||||
|
kwargs["nohist"] = "1";
|
||||||
|
}
|
||||||
|
return GlobalModel.submitCommand("connect", null, [remoteArg], kwargs, interactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
editRemote(remoteid: string, kwargsArg: Record<string, string>): void {
|
editRemote(remoteid: string, kwargsArg: Record<string, string>): void {
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
|
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
|
||||||
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
||||||
"github.com/wavetermdev/waveterm/waveshell/pkg/shexec"
|
"github.com/wavetermdev/waveterm/waveshell/pkg/shexec"
|
||||||
@ -33,7 +34,6 @@ import (
|
|||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/scpacket"
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/scpacket"
|
||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/utilfn"
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/utilfn"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -1116,7 +1116,7 @@ func RemoteNewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
|
|||||||
}
|
}
|
||||||
editArgs, err := parseRemoteEditArgs(true, pk, false)
|
editArgs, err := parseRemoteEditArgs(true, pk, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeRemoteEditErrorReturn_new(visualEdit, fmt.Errorf("/remote:new %v", err))
|
return nil, fmt.Errorf("/remote:new %v", err)
|
||||||
}
|
}
|
||||||
r := &sstore.RemoteType{
|
r := &sstore.RemoteType{
|
||||||
RemoteId: scbase.GenPromptUUID(),
|
RemoteId: scbase.GenPromptUUID(),
|
||||||
@ -1134,7 +1134,7 @@ func RemoteNewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
|
|||||||
}
|
}
|
||||||
err = remote.AddRemote(ctx, r, true)
|
err = remote.AddRemote(ctx, r, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeRemoteEditErrorReturn_new(visualEdit, fmt.Errorf("cannot create remote %q: %v", r.RemoteCanonicalName, err))
|
return nil, fmt.Errorf("cannot create remote %q: %v", r.RemoteCanonicalName, err)
|
||||||
}
|
}
|
||||||
// SUCCESS
|
// SUCCESS
|
||||||
return &sstore.ModelUpdate{
|
return &sstore.ModelUpdate{
|
||||||
@ -1580,6 +1580,18 @@ func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("/%s error: cannot update curremote: %w", GetCmdStr(pk), err)
|
return nil, fmt.Errorf("/%s error: cannot update curremote: %w", GetCmdStr(pk), err)
|
||||||
}
|
}
|
||||||
|
noHist := resolveBool(pk.Kwargs["nohist"], false)
|
||||||
|
if noHist {
|
||||||
|
screen, err := sstore.GetScreenById(ctx, ids.ScreenId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("/% error: cannot resolve screen for update: %w", err)
|
||||||
|
}
|
||||||
|
update := &sstore.ModelUpdate{
|
||||||
|
Screens: []*sstore.ScreenType{screen},
|
||||||
|
Interactive: pk.Interactive,
|
||||||
|
}
|
||||||
|
return update, nil
|
||||||
|
}
|
||||||
outputStr := fmt.Sprintf("connected to %s", GetFullRemoteDisplayName(rptr, rstate))
|
outputStr := fmt.Sprintf("connected to %s", GetFullRemoteDisplayName(rptr, rstate))
|
||||||
cmd, err := makeStaticCmd(ctx, GetCmdStr(pk), ids, pk.GetRawStr(), []byte(outputStr))
|
cmd, err := makeStaticCmd(ctx, GetCmdStr(pk), ids, pk.GetRawStr(), []byte(outputStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,7 +8,7 @@ module.exports = {
|
|||||||
prompt: ["./src/index.ts", "./src/app/app.less"],
|
prompt: ["./src/index.ts", "./src/app/app.less"],
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, "dist"),
|
path: path.resolve(__dirname, "../dist"),
|
||||||
filename: "[name].js",
|
filename: "[name].js",
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
|
@ -16,7 +16,7 @@ const BUILD = makeBuildStr();
|
|||||||
let merged = merge.merge(common, {
|
let merged = merge.merge(common, {
|
||||||
mode: "production",
|
mode: "production",
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, "dist"),
|
path: path.resolve(__dirname, "../dist"),
|
||||||
filename: "[name].js",
|
filename: "[name].js",
|
||||||
},
|
},
|
||||||
devtool: "source-map",
|
devtool: "source-map",
|
||||||
|
@ -21,7 +21,7 @@ if (process.env.WEBPACK_ANALYZE) {
|
|||||||
let merged = merge.merge(common, {
|
let merged = merge.merge(common, {
|
||||||
mode: "production",
|
mode: "production",
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, "dist"),
|
path: path.resolve(__dirname, "../dist"),
|
||||||
filename: "[name].js",
|
filename: "[name].js",
|
||||||
},
|
},
|
||||||
devtool: false,
|
devtool: false,
|
||||||
|
Loading…
Reference in New Issue
Block a user