mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
split settings modals (#272)
This commit is contained in:
parent
3a9f6dec6d
commit
cedebe2196
158
src/app/app.less
158
src/app/app.less
@ -664,3 +664,161 @@ a.a-block {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-field {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
&.settings-field.sub-field {
|
||||
.settings-label {
|
||||
font-weight: normal;
|
||||
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&.settings-error {
|
||||
color: @term-red;
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: #200707;
|
||||
border: 1px solid @term-red;
|
||||
font-weight: bold;
|
||||
|
||||
.error-dismiss {
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
font-weight: bold;
|
||||
width: 12em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.info-message {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-input {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
color: @term-white;
|
||||
|
||||
&.settings-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.inline-edit.edit-active {
|
||||
input.input {
|
||||
padding: 0;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.control {
|
||||
.icon {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-color-icon.color-default path {
|
||||
fill: @tab-green;
|
||||
}
|
||||
.tab-color-icon.color-green path {
|
||||
fill: @tab-green;
|
||||
}
|
||||
.tab-color-icon.color-orange path {
|
||||
fill: @tab-orange;
|
||||
}
|
||||
.tab-color-icon.color-red path {
|
||||
fill: @tab-red;
|
||||
}
|
||||
.tab-color-icon.color-yellow path {
|
||||
fill: @tab-yellow;
|
||||
}
|
||||
.tab-color-icon.color-blue path {
|
||||
fill: @tab-blue;
|
||||
}
|
||||
.tab-color-icon.color-mint path {
|
||||
fill: @tab-mint;
|
||||
}
|
||||
.tab-color-icon.color-cyan path {
|
||||
fill: @tab-cyan;
|
||||
}
|
||||
.tab-color-icon.color-white path {
|
||||
fill: @tab-white;
|
||||
}
|
||||
.tab-color-icon.color-violet path {
|
||||
fill: @tab-violet;
|
||||
}
|
||||
.tab-color-icon.color-pink path {
|
||||
fill: @tab-pink;
|
||||
}
|
||||
|
||||
.tab-colors,
|
||||
.tab-icons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.tab-color-sep,
|
||||
.tab-icon-sep {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tab-color-icon,
|
||||
.tab-icon-icon {
|
||||
width: 1.1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tab-color-name,
|
||||
.tab-icon-name {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.tab-color-select,
|
||||
.tab-icon-select {
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
&:hover {
|
||||
outline: 2px solid white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-text {
|
||||
margin-left: 20px;
|
||||
|
||||
color: @term-red;
|
||||
}
|
||||
|
||||
.settings-share-link {
|
||||
width: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
@ -6,3 +6,6 @@ export { CreateRemoteConnModal } from "./createremoteconn";
|
||||
export { ViewRemoteConnDetailModal } from "./viewremoteconndetail";
|
||||
export { EditRemoteConnModal } from "./editremoteconn";
|
||||
export { TabSwitcherModal } from "./tabswitcher";
|
||||
export { SessionSettingsModal } from "./sessionsettings";
|
||||
export { ScreenSettingsModal } from "./screensettings";
|
||||
export { LineSettingsModal } from "./linesettings";
|
||||
|
23
src/app/common/modals/linesettings.less
Normal file
23
src/app/common/modals/linesettings.less
Normal file
@ -0,0 +1,23 @@
|
||||
@import "../../../app/common/themes/themes.less";
|
||||
|
||||
.line-settings-modal {
|
||||
width: 640px;
|
||||
|
||||
.wave-modal-content {
|
||||
gap: 24px;
|
||||
|
||||
.wave-modal-body {
|
||||
display: flex;
|
||||
padding: 0px 20px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
|
||||
.settings-input .hotkey {
|
||||
color: @text-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
151
src/app/common/modals/linesettings.tsx
Normal file
151
src/app/common/modals/linesettings.tsx
Normal file
@ -0,0 +1,151 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { GlobalModel, GlobalCommandRunner } from "../../../model/model";
|
||||
import { SettingsError, Modal, Dropdown } from "../common";
|
||||
import { LineType, RendererPluginType } from "../../../types/types";
|
||||
import { PluginModel } from "../../../plugins/plugins";
|
||||
import { commandRtnHandler } from "../../../util/util";
|
||||
|
||||
import "./linesettings.less";
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
@mobxReact.observer
|
||||
class LineSettingsModal extends React.Component<{}, {}> {
|
||||
rendererDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "lineSettings-rendererDropdownActive" });
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
||||
linenum: number;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.linenum = GlobalModel.lineSettingsModal.get();
|
||||
if (this.linenum == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.lineSettingsModal.set(null);
|
||||
})();
|
||||
GlobalModel.modalsModel.popModal();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeArchived(val: boolean): void {
|
||||
let line = this.getLine();
|
||||
if (line == null) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.lineArchive(line.lineid, val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
toggleRendererDropdown(): void {
|
||||
mobx.action(() => {
|
||||
this.rendererDropdownActive.set(!this.rendererDropdownActive.get());
|
||||
})();
|
||||
}
|
||||
|
||||
getLine(): LineType {
|
||||
let screen = GlobalModel.getActiveScreen();
|
||||
if (screen == null) {
|
||||
return;
|
||||
}
|
||||
return screen.getLineByNum(this.linenum);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
clickSetRenderer(renderer: string): void {
|
||||
let line = this.getLine();
|
||||
if (line == null) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.lineSet(line.lineid, { renderer: renderer });
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
mobx.action(() => {
|
||||
this.rendererDropdownActive.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
getOptions(plugins: RendererPluginType[]) {
|
||||
// Add label and value to each object in the array
|
||||
const options = plugins.map((item) => ({
|
||||
...item,
|
||||
label: item.name,
|
||||
value: item.name,
|
||||
}));
|
||||
|
||||
// Create an additional object with label "terminal" and value null
|
||||
const terminalItem = {
|
||||
label: "terminal",
|
||||
value: null,
|
||||
name: null,
|
||||
rendererType: null,
|
||||
heightType: null,
|
||||
dataType: null,
|
||||
collapseType: null,
|
||||
globalCss: null,
|
||||
mimeTypes: null,
|
||||
};
|
||||
|
||||
// Create an additional object with label "none" and value none
|
||||
const noneItem = {
|
||||
label: "none",
|
||||
value: "none",
|
||||
name: null,
|
||||
rendererType: null,
|
||||
heightType: null,
|
||||
dataType: null,
|
||||
collapseType: null,
|
||||
globalCss: null,
|
||||
mimeTypes: null,
|
||||
};
|
||||
|
||||
// Combine the options with the terminal item
|
||||
return [terminalItem, ...options, noneItem];
|
||||
}
|
||||
|
||||
render() {
|
||||
let line = this.getLine();
|
||||
if (line == null) {
|
||||
setTimeout(() => {
|
||||
this.closeModal();
|
||||
}, 0);
|
||||
return null;
|
||||
}
|
||||
let plugins = PluginModel.rendererPlugins;
|
||||
let renderer = line.renderer ?? "terminal";
|
||||
|
||||
return (
|
||||
<Modal className="line-settings-modal">
|
||||
<Modal.Header onClose={this.closeModal} title={`line settings (${line.linenum})`} />
|
||||
<div className="wave-modal-body">
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Renderer</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="renderer-dropdown"
|
||||
options={this.getOptions(plugins)}
|
||||
defaultValue={renderer}
|
||||
onChange={this.clickSetRenderer}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
<div style={{ height: 50 }} />
|
||||
</div>
|
||||
<Modal.Footer cancelLabel="Close" onCancel={this.closeModal} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { LineSettingsModal };
|
@ -9,8 +9,10 @@ import {
|
||||
ViewRemoteConnDetailModal,
|
||||
EditRemoteConnModal,
|
||||
TabSwitcherModal,
|
||||
SessionSettingsModal,
|
||||
ScreenSettingsModal,
|
||||
LineSettingsModal,
|
||||
} from "../modals";
|
||||
import { ScreenSettingsModal, SessionSettingsModal, LineSettingsModal } from "./settings";
|
||||
import * as constants from "../../appconst";
|
||||
|
||||
const modalsRegistry: { [key: string]: () => React.ReactElement } = {
|
||||
|
52
src/app/common/modals/screensettings.less
Normal file
52
src/app/common/modals/screensettings.less
Normal file
@ -0,0 +1,52 @@
|
||||
@import "../../../app/common/themes/themes.less";
|
||||
|
||||
.screen-settings-modal {
|
||||
width: 640px;
|
||||
|
||||
.wave-modal-content {
|
||||
gap: 24px;
|
||||
|
||||
.wave-modal-body {
|
||||
display: flex;
|
||||
padding: 0px 20px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
|
||||
.screen-settings-dropdown {
|
||||
width: 412px;
|
||||
|
||||
.lefticon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 16px;
|
||||
transform: translateY(-50%);
|
||||
|
||||
.globe-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
position: absolute;
|
||||
left: 7px;
|
||||
top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.archived-label,
|
||||
.actions-label {
|
||||
div:first-child {
|
||||
margin-right: 5px;
|
||||
}
|
||||
div:last-child i {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
365
src/app/common/modals/screensettings.tsx
Normal file
365
src/app/common/modals/screensettings.tsx
Normal file
@ -0,0 +1,365 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { If, For } from "tsx-control-statements/components";
|
||||
import cn from "classnames";
|
||||
import { GlobalModel, GlobalCommandRunner, TabColors, TabIcons, Screen } from "../../../model/model";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, Modal, Dropdown, Tooltip } from "../common";
|
||||
import { RemoteType } from "../../../types/types";
|
||||
import * as util from "../../../util/util";
|
||||
import { commandRtnHandler } from "../../../util/util";
|
||||
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
|
||||
import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg";
|
||||
import { ReactComponent as StatusCircleIcon } from "../../assets/icons/statuscircle.svg";
|
||||
|
||||
import "./screensettings.less";
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
const ScreenDeleteMessage = `
|
||||
Are you sure you want to delete this tab?
|
||||
|
||||
All commands and output will be deleted. To hide the tab, and retain the commands and output, use 'archive'.
|
||||
`.trim();
|
||||
|
||||
const WebShareConfirmMarkdown = `
|
||||
You are about to share a terminal tab on the web. Please make sure that you do
|
||||
NOT share any private information, keys, passwords, or other sensitive information.
|
||||
You are responsible for what you are sharing, be smart.
|
||||
`.trim();
|
||||
|
||||
const WebStopShareConfirmMarkdown = `
|
||||
Are you sure you want to stop web-sharing this tab?
|
||||
`.trim();
|
||||
|
||||
@mobxReact.observer
|
||||
class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
shareCopied: OV<boolean> = mobx.observable.box(false, { name: "ScreenSettings-shareCopied" });
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
||||
screen: Screen;
|
||||
sessionId: string;
|
||||
screenId: string;
|
||||
remotes: RemoteType[];
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
let screenSettingsModal = GlobalModel.screenSettingsModal.get();
|
||||
let { sessionId, screenId } = screenSettingsModal;
|
||||
this.sessionId = sessionId;
|
||||
this.screenId = screenId;
|
||||
this.screen = GlobalModel.getScreenById(sessionId, screenId);
|
||||
if (this.screen == null || sessionId == null || screenId == null) {
|
||||
return;
|
||||
}
|
||||
this.remotes = GlobalModel.remotes;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
getOptions(): { label: string; value: string }[] {
|
||||
return this.remotes
|
||||
.filter((r) => !r.archived)
|
||||
.map((remote) => ({
|
||||
...remote,
|
||||
label:
|
||||
remote.remotealias && !util.isBlank(remote.remotealias)
|
||||
? `${remote.remotecanonicalname}`
|
||||
: remote.remotecanonicalname,
|
||||
value: remote.remotecanonicalname,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
let connValA = util.getRemoteConnVal(a);
|
||||
let connValB = util.getRemoteConnVal(b);
|
||||
if (connValA !== connValB) {
|
||||
return connValA - connValB;
|
||||
}
|
||||
return a.remoteidx - b.remoteidx;
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.screenSettingsModal.set(null);
|
||||
})();
|
||||
GlobalModel.modalsModel.popModal();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectTabColor(color: string): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (this.screen.getTabColor() == color) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { tabcolor: color }, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectTabIcon(icon: string): void {
|
||||
if (this.screen.getTabIcon() == icon) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screen.screenId, { tabicon: icon }, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeArchived(val: boolean): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (this.screen.archived.get() == val) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenArchive(this.screenId, val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeWebShare(val: boolean): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (this.screen.isWebShared() == val) {
|
||||
return;
|
||||
}
|
||||
let message = val ? WebShareConfirmMarkdown : WebStopShareConfirmMarkdown;
|
||||
let alertRtn = GlobalModel.showAlert({ message: message, confirm: true, markdown: true });
|
||||
alertRtn.then((result) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenWebShare(this.screen.screenId, val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
copyShareLink(): void {
|
||||
if (this.screen == null) {
|
||||
return null;
|
||||
}
|
||||
let shareLink = this.screen.getWebShareUrl();
|
||||
if (shareLink == null) {
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(shareLink);
|
||||
mobx.action(() => {
|
||||
this.shareCopied.set(true);
|
||||
})();
|
||||
setTimeout(() => {
|
||||
mobx.action(() => {
|
||||
this.shareCopied.set(false);
|
||||
})();
|
||||
}, 600);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateName(val: string): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (util.isStrEq(val, this.screen.name.get())) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { name: val }, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateShareName(val: string): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (util.isStrEq(val, this.screen.getShareName())) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { sharename: val }, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
dismissError(): void {
|
||||
mobx.action(() => {
|
||||
this.errorMessage.set(null);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleDeleteScreen(): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (this.screen.getScreenLines().lines.length == 0) {
|
||||
GlobalCommandRunner.screenDelete(this.screenId, false);
|
||||
GlobalModel.modalsModel.popModal();
|
||||
return;
|
||||
}
|
||||
let message = ScreenDeleteMessage;
|
||||
let alertRtn = GlobalModel.showAlert({ message: message, confirm: true, markdown: true });
|
||||
alertRtn.then((result) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenDelete(this.screenId, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
GlobalModel.modalsModel.popModal();
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectRemote(cname: string): void {
|
||||
let prtn = GlobalCommandRunner.screenSetRemote(cname, true, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
render() {
|
||||
let screen = this.screen;
|
||||
if (screen == null) {
|
||||
return null;
|
||||
}
|
||||
let color: string = null;
|
||||
let icon: string = null;
|
||||
let index: number = 0;
|
||||
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
|
||||
return (
|
||||
<Modal className="screen-settings-modal">
|
||||
<Modal.Header onClose={this.closeModal} title={`tab settings (${screen.name.get()})`} />
|
||||
<div className="wave-modal-body">
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Tab Id</div>
|
||||
<div className="settings-input">{screen.screenId}</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Name</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder="name"
|
||||
text={screen.name.get() ?? "(none)"}
|
||||
value={screen.name.get() ?? ""}
|
||||
onChange={this.inlineUpdateName}
|
||||
maxLength={50}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Connection</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="screen-settings-dropdown"
|
||||
label={curRemote.remotealias}
|
||||
options={this.getOptions()}
|
||||
defaultValue={curRemote.remotecanonicalname}
|
||||
onChange={this.selectRemote}
|
||||
decoration={{
|
||||
startDecoration: (
|
||||
<div className="lefticon">
|
||||
<GlobeIcon className="globe-icon" />
|
||||
<StatusCircleIcon
|
||||
className={cn("status-icon", "status-" + curRemote.status)}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Tab Color</div>
|
||||
<div className="settings-input">
|
||||
<div className="tab-colors">
|
||||
<div className="tab-color-cur">
|
||||
<SquareIcon className={cn("tab-color-icon", "color-" + screen.getTabColor())} />
|
||||
<span className="tab-color-name">{screen.getTabColor()}</span>
|
||||
</div>
|
||||
<div className="tab-color-sep">|</div>
|
||||
<For each="color" of={TabColors}>
|
||||
<div
|
||||
key={color}
|
||||
className="tab-color-select"
|
||||
onClick={() => this.selectTabColor(color)}
|
||||
>
|
||||
<SquareIcon className={cn("tab-color-icon", "color-" + color)} />
|
||||
</div>
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Tab Icon</div>
|
||||
<div className="settings-input">
|
||||
<div className="tab-icons">
|
||||
<div className="tab-icon-cur">
|
||||
<If condition={screen.getTabIcon() == "default"}>
|
||||
<SquareIcon className={cn("tab-color-icon", "color-white")} />
|
||||
</If>
|
||||
<If condition={screen.getTabIcon() != "default"}>
|
||||
<i className={`fa-sharp fa-solid fa-${screen.getTabIcon()}`}></i>
|
||||
</If>
|
||||
<span className="tab-icon-name">{screen.getTabIcon()}</span>
|
||||
</div>
|
||||
<div className="tab-icon-sep">|</div>
|
||||
<For each="icon" index="index" of={TabIcons}>
|
||||
<div
|
||||
key={`${color}-${index}`}
|
||||
className="tab-icon-select"
|
||||
onClick={() => this.selectTabIcon(icon)}
|
||||
>
|
||||
<i className={`fa-sharp fa-solid fa-${icon}`}></i>
|
||||
</div>
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label archived-label">
|
||||
<div className="">Archived</div>
|
||||
<Tooltip
|
||||
message={`Archive will hide the tab. Commands and output will be retained in history.`}
|
||||
icon={<i className="fa-sharp fa-regular fa-circle-question" />}
|
||||
className="screen-settings-tooltip"
|
||||
>
|
||||
<i className="fa-sharp fa-regular fa-circle-question" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<Toggle checked={screen.archived.get()} onChange={this.handleChangeArchived} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label actions-label">
|
||||
<div>Actions</div>
|
||||
<Tooltip
|
||||
message={`Delete will remove the tab, removing all commands and output from history.`}
|
||||
icon={<i className="fa-sharp fa-regular fa-circle-question" />}
|
||||
className="screen-settings-tooltip"
|
||||
>
|
||||
<i className="fa-sharp fa-regular fa-circle-question" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<div
|
||||
onClick={this.handleDeleteScreen}
|
||||
className="button is-prompt-danger is-outlined is-small"
|
||||
>
|
||||
Delete Tab
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
<Modal.Footer cancelLabel="Close" onCancel={this.closeModal} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { ScreenSettingsModal };
|
19
src/app/common/modals/sessionsettings.less
Normal file
19
src/app/common/modals/sessionsettings.less
Normal file
@ -0,0 +1,19 @@
|
||||
@import "../../../app/common/themes/themes.less";
|
||||
|
||||
.session-settings-modal {
|
||||
width: 640px;
|
||||
|
||||
.wave-modal-content {
|
||||
gap: 24px;
|
||||
|
||||
.wave-modal-body {
|
||||
display: flex;
|
||||
padding: 0px 20px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
147
src/app/common/modals/sessionsettings.tsx
Normal file
147
src/app/common/modals/sessionsettings.tsx
Normal file
@ -0,0 +1,147 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { GlobalModel, GlobalCommandRunner, Session } from "../../../model/model";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage, Modal } from "../common";
|
||||
import * as util from "../../../util/util";
|
||||
import { commandRtnHandler } from "../../../util/util";
|
||||
|
||||
import "./sessionsettings.less";
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
const SessionDeleteMessage = `
|
||||
Are you sure you want to delete this workspace?
|
||||
|
||||
All commands and output will be deleted. To hide the workspace, and retain the commands and output, use 'archive'.
|
||||
`.trim();
|
||||
|
||||
@mobxReact.observer
|
||||
class SessionSettingsModal extends React.Component<{}, {}> {
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
||||
session: Session;
|
||||
sessionId: string;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.sessionId = GlobalModel.sessionSettingsModal.get();
|
||||
this.session = GlobalModel.getSessionById(this.sessionId);
|
||||
if (this.session == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.sessionSettingsModal.set(null);
|
||||
})();
|
||||
GlobalModel.modalsModel.popModal();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleInlineChangeName(newVal: string): void {
|
||||
if (this.session == null) {
|
||||
return;
|
||||
}
|
||||
if (util.isStrEq(newVal, this.session.name.get())) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.sessionSetSettings(this.sessionId, { name: newVal }, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeArchived(val: boolean): void {
|
||||
if (this.session == null) {
|
||||
return;
|
||||
}
|
||||
if (this.session.archived.get() == val) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.sessionArchive(this.sessionId, val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleDeleteSession(): void {
|
||||
let message = SessionDeleteMessage;
|
||||
let alertRtn = GlobalModel.showAlert({ message: message, confirm: true, markdown: true });
|
||||
alertRtn.then((result) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.sessionDelete(this.sessionId);
|
||||
commandRtnHandler(prtn, this.errorMessage, () => GlobalModel.modalsModel.popModal());
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
dismissError(): void {
|
||||
mobx.action(() => {
|
||||
this.errorMessage.set(null);
|
||||
})();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.session == null) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Modal className="session-settings-modal">
|
||||
<Modal.Header onClose={this.closeModal} title={`workspace settings (${this.session.name.get()})`} />
|
||||
<div className="wave-modal-body">
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Name</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder="name"
|
||||
text={this.session.name.get() ?? "(none)"}
|
||||
value={this.session.name.get() ?? ""}
|
||||
onChange={this.handleInlineChangeName}
|
||||
maxLength={50}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">
|
||||
<div>Archived</div>
|
||||
<InfoMessage width={400}>
|
||||
Archive will hide the workspace from the active menu. Commands and output will be
|
||||
retained in history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<Toggle checked={this.session.archived.get()} onChange={this.handleChangeArchived} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">
|
||||
<div>Actions</div>
|
||||
<InfoMessage width={400}>
|
||||
Delete will remove the workspace, removing all commands and output from history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<div
|
||||
onClick={this.handleDeleteSession}
|
||||
className="button is-prompt-danger is-outlined is-small"
|
||||
>
|
||||
Delete Workspace
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
<Modal.Footer cancelLabel="Close" onCancel={this.closeModal} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { SessionSettingsModal };
|
File diff suppressed because it is too large
Load Diff
@ -1,838 +0,0 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { If, For } from "tsx-control-statements/components";
|
||||
import cn from "classnames";
|
||||
import {
|
||||
GlobalModel,
|
||||
GlobalCommandRunner,
|
||||
TabColors,
|
||||
MinFontSize,
|
||||
MaxFontSize,
|
||||
TabIcons,
|
||||
Screen,
|
||||
Session,
|
||||
} from "../../../model/model";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage, Modal, Dropdown, Tooltip } from "../common";
|
||||
import { LineType, RendererPluginType, ClientDataType, CommandRtnType, RemoteType } from "../../../types/types";
|
||||
import { PluginModel } from "../../../plugins/plugins";
|
||||
import * as util from "../../../util/util";
|
||||
import { commandRtnHandler } from "../../../util/util";
|
||||
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
|
||||
import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg";
|
||||
import { ReactComponent as StatusCircleIcon } from "../../assets/icons/statuscircle.svg";
|
||||
|
||||
import "./settings.less";
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
// @ts-ignore
|
||||
const VERSION = __WAVETERM_VERSION__;
|
||||
// @ts-ignore
|
||||
const BUILD = __WAVETERM_BUILD__;
|
||||
|
||||
const ScreenDeleteMessage = `
|
||||
Are you sure you want to delete this tab?
|
||||
|
||||
All commands and output will be deleted. To hide the tab, and retain the commands and output, use 'archive'.
|
||||
`.trim();
|
||||
|
||||
const SessionDeleteMessage = `
|
||||
Are you sure you want to delete this workspace?
|
||||
|
||||
All commands and output will be deleted. To hide the workspace, and retain the commands and output, use 'archive'.
|
||||
`.trim();
|
||||
|
||||
const WebShareConfirmMarkdown = `
|
||||
You are about to share a terminal tab on the web. Please make sure that you do
|
||||
NOT share any private information, keys, passwords, or other sensitive information.
|
||||
You are responsible for what you are sharing, be smart.
|
||||
`.trim();
|
||||
|
||||
const WebStopShareConfirmMarkdown = `
|
||||
Are you sure you want to stop web-sharing this tab?
|
||||
`.trim();
|
||||
|
||||
@mobxReact.observer
|
||||
class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
shareCopied: OV<boolean> = mobx.observable.box(false, { name: "ScreenSettings-shareCopied" });
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
||||
screen: Screen;
|
||||
sessionId: string;
|
||||
screenId: string;
|
||||
remotes: RemoteType[];
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
let screenSettingsModal = GlobalModel.screenSettingsModal.get();
|
||||
let { sessionId, screenId } = screenSettingsModal;
|
||||
this.sessionId = sessionId;
|
||||
this.screenId = screenId;
|
||||
this.screen = GlobalModel.getScreenById(sessionId, screenId);
|
||||
if (this.screen == null || sessionId == null || screenId == null) {
|
||||
return;
|
||||
}
|
||||
this.remotes = GlobalModel.remotes;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
getOptions(): { label: string; value: string }[] {
|
||||
return this.remotes
|
||||
.filter((r) => !r.archived)
|
||||
.map((remote) => ({
|
||||
...remote,
|
||||
label:
|
||||
remote.remotealias && !util.isBlank(remote.remotealias)
|
||||
? `${remote.remotecanonicalname}`
|
||||
: remote.remotecanonicalname,
|
||||
value: remote.remotecanonicalname,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
let connValA = util.getRemoteConnVal(a);
|
||||
let connValB = util.getRemoteConnVal(b);
|
||||
if (connValA !== connValB) {
|
||||
return connValA - connValB;
|
||||
}
|
||||
return a.remoteidx - b.remoteidx;
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.screenSettingsModal.set(null);
|
||||
})();
|
||||
GlobalModel.modalsModel.popModal();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectTabColor(color: string): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (this.screen.getTabColor() == color) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { tabcolor: color }, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectTabIcon(icon: string): void {
|
||||
if (this.screen.getTabIcon() == icon) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screen.screenId, { tabicon: icon }, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeArchived(val: boolean): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (this.screen.archived.get() == val) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenArchive(this.screenId, val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeWebShare(val: boolean): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (this.screen.isWebShared() == val) {
|
||||
return;
|
||||
}
|
||||
let message = val ? WebShareConfirmMarkdown : WebStopShareConfirmMarkdown;
|
||||
let alertRtn = GlobalModel.showAlert({ message: message, confirm: true, markdown: true });
|
||||
alertRtn.then((result) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenWebShare(this.screen.screenId, val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
copyShareLink(): void {
|
||||
if (this.screen == null) {
|
||||
return null;
|
||||
}
|
||||
let shareLink = this.screen.getWebShareUrl();
|
||||
if (shareLink == null) {
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(shareLink);
|
||||
mobx.action(() => {
|
||||
this.shareCopied.set(true);
|
||||
})();
|
||||
setTimeout(() => {
|
||||
mobx.action(() => {
|
||||
this.shareCopied.set(false);
|
||||
})();
|
||||
}, 600);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateName(val: string): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (util.isStrEq(val, this.screen.name.get())) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { name: val }, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateShareName(val: string): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (util.isStrEq(val, this.screen.getShareName())) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { sharename: val }, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
dismissError(): void {
|
||||
mobx.action(() => {
|
||||
this.errorMessage.set(null);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleDeleteScreen(): void {
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
if (this.screen.getScreenLines().lines.length == 0) {
|
||||
GlobalCommandRunner.screenDelete(this.screenId, false);
|
||||
GlobalModel.modalsModel.popModal();
|
||||
return;
|
||||
}
|
||||
let message = ScreenDeleteMessage;
|
||||
let alertRtn = GlobalModel.showAlert({ message: message, confirm: true, markdown: true });
|
||||
alertRtn.then((result) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenDelete(this.screenId, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
GlobalModel.modalsModel.popModal();
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectRemote(cname: string): void {
|
||||
let prtn = GlobalCommandRunner.screenSetRemote(cname, true, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
render() {
|
||||
let screen = this.screen;
|
||||
if (screen == null) {
|
||||
return null;
|
||||
}
|
||||
let color: string = null;
|
||||
let icon: string = null;
|
||||
let index: number = 0;
|
||||
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
|
||||
return (
|
||||
<Modal className="screen-settings-modal">
|
||||
<Modal.Header onClose={this.closeModal} title={`tab settings (${screen.name.get()})`} />
|
||||
<div className="wave-modal-body">
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Tab Id</div>
|
||||
<div className="settings-input">{screen.screenId}</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Name</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder="name"
|
||||
text={screen.name.get() ?? "(none)"}
|
||||
value={screen.name.get() ?? ""}
|
||||
onChange={this.inlineUpdateName}
|
||||
maxLength={50}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Connection</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="screen-settings-dropdown"
|
||||
label={curRemote.remotealias}
|
||||
options={this.getOptions()}
|
||||
defaultValue={curRemote.remotecanonicalname}
|
||||
onChange={this.selectRemote}
|
||||
decoration={{
|
||||
startDecoration: (
|
||||
<div className="lefticon">
|
||||
<GlobeIcon className="globe-icon" />
|
||||
<StatusCircleIcon
|
||||
className={cn("status-icon", "status-" + curRemote.status)}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Tab Color</div>
|
||||
<div className="settings-input">
|
||||
<div className="tab-colors">
|
||||
<div className="tab-color-cur">
|
||||
<SquareIcon className={cn("tab-color-icon", "color-" + screen.getTabColor())} />
|
||||
<span className="tab-color-name">{screen.getTabColor()}</span>
|
||||
</div>
|
||||
<div className="tab-color-sep">|</div>
|
||||
<For each="color" of={TabColors}>
|
||||
<div
|
||||
key={color}
|
||||
className="tab-color-select"
|
||||
onClick={() => this.selectTabColor(color)}
|
||||
>
|
||||
<SquareIcon className={cn("tab-color-icon", "color-" + color)} />
|
||||
</div>
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Tab Icon</div>
|
||||
<div className="settings-input">
|
||||
<div className="tab-icons">
|
||||
<div className="tab-icon-cur">
|
||||
<If condition={screen.getTabIcon() == "default"}>
|
||||
<SquareIcon className={cn("tab-color-icon", "color-white")} />
|
||||
</If>
|
||||
<If condition={screen.getTabIcon() != "default"}>
|
||||
<i className={`fa-sharp fa-solid fa-${screen.getTabIcon()}`}></i>
|
||||
</If>
|
||||
<span className="tab-icon-name">{screen.getTabIcon()}</span>
|
||||
</div>
|
||||
<div className="tab-icon-sep">|</div>
|
||||
<For each="icon" index="index" of={TabIcons}>
|
||||
<div
|
||||
key={`${color}-${index}`}
|
||||
className="tab-icon-select"
|
||||
onClick={() => this.selectTabIcon(icon)}
|
||||
>
|
||||
<i className={`fa-sharp fa-solid fa-${icon}`}></i>
|
||||
</div>
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label archived-label">
|
||||
<div className="">Archived</div>
|
||||
<Tooltip
|
||||
message={`Archive will hide the tab. Commands and output will be retained in history.`}
|
||||
icon={<i className="fa-sharp fa-regular fa-circle-question" />}
|
||||
className="screen-settings-tooltip"
|
||||
>
|
||||
<i className="fa-sharp fa-regular fa-circle-question" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<Toggle checked={screen.archived.get()} onChange={this.handleChangeArchived} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label actions-label">
|
||||
<div>Actions</div>
|
||||
<Tooltip
|
||||
message={`Delete will remove the tab, removing all commands and output from history.`}
|
||||
icon={<i className="fa-sharp fa-regular fa-circle-question" />}
|
||||
className="screen-settings-tooltip"
|
||||
>
|
||||
<i className="fa-sharp fa-regular fa-circle-question" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<div
|
||||
onClick={this.handleDeleteScreen}
|
||||
className="button is-prompt-danger is-outlined is-small"
|
||||
>
|
||||
Delete Tab
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
<Modal.Footer cancelLabel="Close" onCancel={this.closeModal} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class SessionSettingsModal extends React.Component<{}, {}> {
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
||||
session: Session;
|
||||
sessionId: string;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.sessionId = GlobalModel.sessionSettingsModal.get();
|
||||
this.session = GlobalModel.getSessionById(this.sessionId);
|
||||
if (this.session == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.sessionSettingsModal.set(null);
|
||||
})();
|
||||
GlobalModel.modalsModel.popModal();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleInlineChangeName(newVal: string): void {
|
||||
if (this.session == null) {
|
||||
return;
|
||||
}
|
||||
if (util.isStrEq(newVal, this.session.name.get())) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.sessionSetSettings(this.sessionId, { name: newVal }, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeArchived(val: boolean): void {
|
||||
if (this.session == null) {
|
||||
return;
|
||||
}
|
||||
if (this.session.archived.get() == val) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.sessionArchive(this.sessionId, val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleDeleteSession(): void {
|
||||
let message = SessionDeleteMessage;
|
||||
let alertRtn = GlobalModel.showAlert({ message: message, confirm: true, markdown: true });
|
||||
alertRtn.then((result) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.sessionDelete(this.sessionId);
|
||||
commandRtnHandler(prtn, this.errorMessage, () => GlobalModel.modalsModel.popModal());
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
dismissError(): void {
|
||||
mobx.action(() => {
|
||||
this.errorMessage.set(null);
|
||||
})();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.session == null) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Modal className="session-settings-modal">
|
||||
<Modal.Header onClose={this.closeModal} title={`workspace settings (${this.session.name.get()})`} />
|
||||
<div className="wave-modal-body">
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Name</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder="name"
|
||||
text={this.session.name.get() ?? "(none)"}
|
||||
value={this.session.name.get() ?? ""}
|
||||
onChange={this.handleInlineChangeName}
|
||||
maxLength={50}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">
|
||||
<div>Archived</div>
|
||||
<InfoMessage width={400}>
|
||||
Archive will hide the workspace from the active menu. Commands and output will be
|
||||
retained in history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<Toggle checked={this.session.archived.get()} onChange={this.handleChangeArchived} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">
|
||||
<div>Actions</div>
|
||||
<InfoMessage width={400}>
|
||||
Delete will remove the workspace, removing all commands and output from history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<div
|
||||
onClick={this.handleDeleteSession}
|
||||
className="button is-prompt-danger is-outlined is-small"
|
||||
>
|
||||
Delete Workspace
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
<Modal.Footer cancelLabel="Close" onCancel={this.closeModal} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class LineSettingsModal extends React.Component<{}, {}> {
|
||||
rendererDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "lineSettings-rendererDropdownActive" });
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
||||
linenum: number;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.linenum = GlobalModel.lineSettingsModal.get();
|
||||
if (this.linenum == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.lineSettingsModal.set(null);
|
||||
})();
|
||||
GlobalModel.modalsModel.popModal();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeArchived(val: boolean): void {
|
||||
let line = this.getLine();
|
||||
if (line == null) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.lineArchive(line.lineid, val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
toggleRendererDropdown(): void {
|
||||
mobx.action(() => {
|
||||
this.rendererDropdownActive.set(!this.rendererDropdownActive.get());
|
||||
})();
|
||||
}
|
||||
|
||||
getLine(): LineType {
|
||||
let screen = GlobalModel.getActiveScreen();
|
||||
if (screen == null) {
|
||||
return;
|
||||
}
|
||||
return screen.getLineByNum(this.linenum);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
clickSetRenderer(renderer: string): void {
|
||||
let line = this.getLine();
|
||||
if (line == null) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.lineSet(line.lineid, { renderer: renderer });
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
mobx.action(() => {
|
||||
this.rendererDropdownActive.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
getOptions(plugins: RendererPluginType[]) {
|
||||
// Add label and value to each object in the array
|
||||
const options = plugins.map((item) => ({
|
||||
...item,
|
||||
label: item.name,
|
||||
value: item.name,
|
||||
}));
|
||||
|
||||
// Create an additional object with label "terminal" and value null
|
||||
const terminalItem = {
|
||||
label: "terminal",
|
||||
value: null,
|
||||
name: null,
|
||||
rendererType: null,
|
||||
heightType: null,
|
||||
dataType: null,
|
||||
collapseType: null,
|
||||
globalCss: null,
|
||||
mimeTypes: null,
|
||||
};
|
||||
|
||||
// Create an additional object with label "none" and value none
|
||||
const noneItem = {
|
||||
label: "none",
|
||||
value: "none",
|
||||
name: null,
|
||||
rendererType: null,
|
||||
heightType: null,
|
||||
dataType: null,
|
||||
collapseType: null,
|
||||
globalCss: null,
|
||||
mimeTypes: null,
|
||||
};
|
||||
|
||||
// Combine the options with the terminal item
|
||||
return [terminalItem, ...options, noneItem];
|
||||
}
|
||||
|
||||
render() {
|
||||
let line = this.getLine();
|
||||
if (line == null) {
|
||||
setTimeout(() => {
|
||||
this.closeModal();
|
||||
}, 0);
|
||||
return null;
|
||||
}
|
||||
let plugins = PluginModel.rendererPlugins;
|
||||
let renderer = line.renderer ?? "terminal";
|
||||
|
||||
return (
|
||||
<Modal className="line-settings-modal">
|
||||
<Modal.Header onClose={this.closeModal} title={`line settings (${line.linenum})`} />
|
||||
<div className="wave-modal-body">
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Renderer</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="renderer-dropdown"
|
||||
options={this.getOptions(plugins)}
|
||||
defaultValue={renderer}
|
||||
onChange={this.clickSetRenderer}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
<div style={{ height: 50 }} />
|
||||
</div>
|
||||
<Modal.Footer cancelLabel="Close" onCancel={this.closeModal} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class ClientSettingsModal extends React.Component<{}, {}> {
|
||||
fontSizeDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "clientSettings-fontSizeDropdownActive" });
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ClientSettings-errorMessage" });
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
GlobalModel.modalsModel.popModal();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
dismissError(): void {
|
||||
mobx.action(() => {
|
||||
this.errorMessage.set(null);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeFontSize(fontSize: string): void {
|
||||
let newFontSize = Number(fontSize);
|
||||
this.fontSizeDropdownActive.set(false);
|
||||
if (GlobalModel.termFontSize.get() == newFontSize) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.setTermFontSize(newFontSize, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
togglefontSizeDropdown(): void {
|
||||
mobx.action(() => {
|
||||
this.fontSizeDropdownActive.set(!this.fontSizeDropdownActive.get());
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeTelemetry(val: boolean): void {
|
||||
let prtn: Promise<CommandRtnType> = null;
|
||||
if (val) {
|
||||
prtn = GlobalCommandRunner.telemetryOn(false);
|
||||
} else {
|
||||
prtn = GlobalCommandRunner.telemetryOff(false);
|
||||
}
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeReleaseCheck(val: boolean): void {
|
||||
let prtn: Promise<CommandRtnType> = null;
|
||||
if (val) {
|
||||
prtn = GlobalCommandRunner.releaseCheckAutoOn(false);
|
||||
} else {
|
||||
prtn = GlobalCommandRunner.releaseCheckAutoOff(false);
|
||||
}
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
getFontSizes(): any {
|
||||
let availableFontSizes: { label: string; value: number }[] = [];
|
||||
for (let s = MinFontSize; s <= MaxFontSize; s++) {
|
||||
availableFontSizes.push({ label: s + "px", value: s });
|
||||
}
|
||||
return availableFontSizes;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateOpenAIModel(newModel: string): void {
|
||||
let prtn = GlobalCommandRunner.setClientOpenAISettings({ model: newModel });
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateOpenAIToken(newToken: string): void {
|
||||
let prtn = GlobalCommandRunner.setClientOpenAISettings({ apitoken: newToken });
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateOpenAIMaxTokens(newMaxTokensStr: string): void {
|
||||
let prtn = GlobalCommandRunner.setClientOpenAISettings({ maxtokens: newMaxTokensStr });
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
setErrorMessage(msg: string): void {
|
||||
mobx.action(() => {
|
||||
this.errorMessage.set(msg);
|
||||
})();
|
||||
}
|
||||
|
||||
render() {
|
||||
let cdata: ClientDataType = GlobalModel.clientData.get();
|
||||
let openAIOpts = cdata.openaiopts ?? {};
|
||||
let apiTokenStr = util.isBlank(openAIOpts.apitoken) ? "(not set)" : "********";
|
||||
let maxTokensStr = String(
|
||||
openAIOpts.maxtokens == null || openAIOpts.maxtokens == 0 ? 1000 : openAIOpts.maxtokens
|
||||
);
|
||||
let curFontSize = GlobalModel.termFontSize.get();
|
||||
return (
|
||||
<Modal className="client-settings-modal">
|
||||
<Modal.Header onClose={this.closeModal} title="Client settings" />
|
||||
<div className="wave-modal-body">
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Term Font Size</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="font-size-dropdown"
|
||||
options={this.getFontSizes()}
|
||||
defaultValue={`${curFontSize}px`}
|
||||
onChange={this.handleChangeFontSize}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Client ID</div>
|
||||
<div className="settings-input">{cdata.clientid}</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Client Version</div>
|
||||
<div className="settings-input">
|
||||
{VERSION} {BUILD}
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">DB Version</div>
|
||||
<div className="settings-input">{cdata.dbversion}</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Basic Telemetry</div>
|
||||
<div className="settings-input">
|
||||
<Toggle checked={!cdata.clientopts.notelemetry} onChange={this.handleChangeTelemetry} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Check for Updates</div>
|
||||
<div className="settings-input">
|
||||
<Toggle
|
||||
checked={!cdata.clientopts.noreleasecheck}
|
||||
onChange={this.handleChangeReleaseCheck}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">OpenAI Token</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder=""
|
||||
text={apiTokenStr}
|
||||
value={""}
|
||||
onChange={this.inlineUpdateOpenAIToken}
|
||||
maxLength={100}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">OpenAI Model</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder="gpt-3.5-turbo"
|
||||
text={util.isBlank(openAIOpts.model) ? "gpt-3.5-turbo" : openAIOpts.model}
|
||||
value={openAIOpts.model ?? ""}
|
||||
onChange={this.inlineUpdateOpenAIModel}
|
||||
maxLength={100}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">OpenAI MaxTokens</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder=""
|
||||
text={maxTokensStr}
|
||||
value={maxTokensStr}
|
||||
onChange={this.inlineUpdateOpenAIMaxTokens}
|
||||
maxLength={10}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
<Modal.Footer cancelLabel="Close" onCancel={this.closeModal} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
ScreenSettingsModal,
|
||||
SessionSettingsModal,
|
||||
LineSettingsModal,
|
||||
ClientSettingsModal,
|
||||
WebStopShareConfirmMarkdown,
|
||||
};
|
Loading…
Reference in New Issue
Block a user