mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
Merge pull request #63 from wavetermdev/dev-0.5.0
Merge dev 0.5.0 branch to main
This commit is contained in:
commit
be9a3c288a
20
package.json
20
package.json
@ -18,16 +18,16 @@
|
|||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
"mobx": "^6.6.0",
|
"mobx": "^6.6.0",
|
||||||
"mobx-react": "^7.5.0",
|
"mobx-react": "^7.5.0",
|
||||||
"monaco-editor": "^0.41.0",
|
"monaco-editor": "^0.44.0",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
"node-fetch": "^3.2.10",
|
"node-fetch": "^3.2.10",
|
||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^18.1.0",
|
"react-dom": "^18.1.0",
|
||||||
"react-json-view": "^1.21.3",
|
"react-json-view": "^1.21.3",
|
||||||
"react-markdown": "^8.0.5",
|
"react-markdown": "^9.0.0",
|
||||||
"remark": "^14.0.2",
|
"remark": "^15.0.1",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^4.0.0",
|
||||||
"sprintf-js": "^1.1.2",
|
"sprintf-js": "^1.1.2",
|
||||||
"throttle-debounce": "^5.0.0",
|
"throttle-debounce": "^5.0.0",
|
||||||
"tsx-control-statements": "^4.1.1",
|
"tsx-control-statements": "^4.1.1",
|
||||||
@ -57,16 +57,18 @@
|
|||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
"@types/classnames": "^2.3.1",
|
"@types/classnames": "^2.3.1",
|
||||||
"@types/electron": "^1.6.10",
|
"@types/electron": "^1.6.10",
|
||||||
"@types/node": "^18.0.3",
|
"@types/node": "^20.4.0",
|
||||||
"@types/papaparse": "^5.3.9",
|
"@types/papaparse": "^5.3.10",
|
||||||
"@types/react": "^18.0.12",
|
"@types/react": "^18.0.12",
|
||||||
"@types/uuid": "9.0.0",
|
"@types/sprintf-js": "^1.1.3",
|
||||||
|
"@types/throttle-debounce": "^5.0.1",
|
||||||
|
"@types/uuid": "9.0.6",
|
||||||
"@types/webpack-env": "^1.18.3",
|
"@types/webpack-env": "^1.18.3",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
"babel-plugin-jsx-control-statements": "^4.1.2",
|
"babel-plugin-jsx-control-statements": "^4.1.2",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"electron": "27.0.0",
|
"electron": "27.0.3",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"less": "^4.1.2",
|
"less": "^4.1.2",
|
||||||
@ -80,7 +82,7 @@
|
|||||||
"typescript": "^4.7.3",
|
"typescript": "^4.7.3",
|
||||||
"webpack": "^5.73.0",
|
"webpack": "^5.73.0",
|
||||||
"webpack-bundle-analyzer": "^4.5.0",
|
"webpack-bundle-analyzer": "^4.5.0",
|
||||||
"webpack-cli": "^4.9.2",
|
"webpack-cli": "^5.1.4",
|
||||||
"webpack-dev-server": "^4.9.1",
|
"webpack-dev-server": "^4.9.1",
|
||||||
"webpack-merge": "^5.8.0"
|
"webpack-merge": "^5.8.0"
|
||||||
},
|
},
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
} from "./common/modals/settings";
|
} from "./common/modals/settings";
|
||||||
import { RemotesModal } from "./connections/connections";
|
import { RemotesModal } from "./connections/connections";
|
||||||
import { TosModal } from "./common/modals/modals";
|
import { TosModal } from "./common/modals/modals";
|
||||||
import { MainSideBar } from "./sidebar/MainSideBar";
|
import { MainSideBar } from "./sidebar/sidebar";
|
||||||
import { DisconnectedModal, ClientStopModal, AlertModal, AboutModal } from "./common/modals/modals";
|
import { DisconnectedModal, ClientStopModal, AlertModal, AboutModal } from "./common/modals/modals";
|
||||||
import { ErrorBoundary } from "./common/error/errorboundary";
|
import { ErrorBoundary } from "./common/error/errorboundary";
|
||||||
import "./app.less";
|
import "./app.less";
|
||||||
|
@ -625,7 +625,81 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textfield {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid @term-white;
|
||||||
|
border-radius: 6px;
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
background-color: transparent;
|
||||||
|
height: 44px;
|
||||||
|
min-width: 412px;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
&.focused {
|
||||||
|
border-color: @term-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
border-color: @term-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textfield-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
.textfield-label {
|
||||||
|
position: absolute;
|
||||||
|
left: 16px;
|
||||||
|
top: 16px;
|
||||||
|
font-size: 12.5px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
color: @term-white;
|
||||||
|
line-height: 10px;
|
||||||
|
|
||||||
|
&.float {
|
||||||
|
font-size: 10px;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.start {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.textfield-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 0 5px 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
outline: none;
|
||||||
|
background-color: transparent;
|
||||||
|
color: @term-bright-white;
|
||||||
|
line-height: 20px;
|
||||||
|
|
||||||
|
&.start {
|
||||||
|
padding: 5px 16px 5px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-decoration {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.inline-edit {
|
.inline-edit {
|
||||||
.icon {
|
.icon {
|
||||||
@ -664,5 +738,4 @@
|
|||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import remarkGfm from "remark-gfm";
|
|||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
import { If } from "tsx-control-statements/components";
|
import { If } from "tsx-control-statements/components";
|
||||||
import type { RemoteType } from "../../types/types";
|
import type { RemoteType } from "../../types/types";
|
||||||
|
import { debounce } from "throttle-debounce";
|
||||||
|
|
||||||
import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
|
import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
|
||||||
import { ReactComponent as CopyIcon } from "../assets/icons/history/copy.svg";
|
import { ReactComponent as CopyIcon } from "../assets/icons/history/copy.svg";
|
||||||
@ -123,6 +124,152 @@ class Checkbox extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface InputDecorationProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mobxReact.observer
|
||||||
|
class InputDecoration extends React.Component<InputDecorationProps, {}> {
|
||||||
|
render() {
|
||||||
|
const { children, onClick } = this.props;
|
||||||
|
|
||||||
|
return <div className="input-decoration">{children}</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TextFieldDecorationProps {
|
||||||
|
startDecoration?: React.ReactNode;
|
||||||
|
endDecoration?: React.ReactNode;
|
||||||
|
}
|
||||||
|
interface TextFieldProps {
|
||||||
|
label: string;
|
||||||
|
value?: string;
|
||||||
|
className?: string;
|
||||||
|
onChange?: (value: string) => void;
|
||||||
|
placeholder?: string;
|
||||||
|
defaultValue?: string;
|
||||||
|
decoration?: TextFieldDecorationProps;
|
||||||
|
required?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TextFieldState {
|
||||||
|
focused: boolean;
|
||||||
|
internalValue: string;
|
||||||
|
error: boolean;
|
||||||
|
showHelpText: boolean;
|
||||||
|
hasContent: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mobxReact.observer
|
||||||
|
class TextField extends React.Component<TextFieldProps, TextFieldState> {
|
||||||
|
inputRef: React.RefObject<HTMLInputElement>;
|
||||||
|
state: TextFieldState;
|
||||||
|
|
||||||
|
constructor(props: TextFieldProps) {
|
||||||
|
super(props);
|
||||||
|
const hasInitialContent = Boolean(props.value || props.defaultValue);
|
||||||
|
this.state = {
|
||||||
|
focused: false,
|
||||||
|
hasContent: hasInitialContent,
|
||||||
|
internalValue: props.defaultValue || "",
|
||||||
|
error: false,
|
||||||
|
showHelpText: false,
|
||||||
|
};
|
||||||
|
this.inputRef = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: TextFieldProps) {
|
||||||
|
// Only update the focus state if using as controlled
|
||||||
|
if (this.props.value !== undefined && this.props.value !== prevProps.value) {
|
||||||
|
this.setState({ focused: Boolean(this.props.value) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
handleFocus() {
|
||||||
|
this.setState({ focused: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
handleBlur() {
|
||||||
|
const { required } = this.props;
|
||||||
|
if (this.inputRef.current) {
|
||||||
|
const value = this.inputRef.current.value;
|
||||||
|
if (required && !value) {
|
||||||
|
this.setState({ error: true, focused: false });
|
||||||
|
} else {
|
||||||
|
this.setState({ error: false, focused: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
handleHelpTextClick() {
|
||||||
|
this.setState((prevState) => ({ showHelpText: !prevState.showHelpText }));
|
||||||
|
}
|
||||||
|
|
||||||
|
debouncedOnChange = debounce(300, (value) => {
|
||||||
|
const { onChange } = this.props;
|
||||||
|
onChange?.(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
const { required } = this.props;
|
||||||
|
const inputValue = e.target.value;
|
||||||
|
|
||||||
|
// Check if value is empty and the field is required
|
||||||
|
if (required && !inputValue) {
|
||||||
|
this.setState({ error: true, hasContent: false });
|
||||||
|
} else {
|
||||||
|
this.setState({ error: false, hasContent: Boolean(inputValue) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the internal state for uncontrolled version
|
||||||
|
if (this.props.value === undefined) {
|
||||||
|
this.setState({ internalValue: inputValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.debouncedOnChange(inputValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { label, value, placeholder, decoration, className } = this.props;
|
||||||
|
const { focused, internalValue, error } = this.state;
|
||||||
|
|
||||||
|
// Decide if the input should behave as controlled or uncontrolled
|
||||||
|
const inputValue = value !== undefined ? value : internalValue;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn(`textfield ${className || ""}`, { focused: focused, error: error })}>
|
||||||
|
{decoration?.startDecoration && <>{decoration.startDecoration}</>}
|
||||||
|
<div className="textfield-inner">
|
||||||
|
<label
|
||||||
|
className={cn("textfield-label", {
|
||||||
|
float: this.state.hasContent || this.state.focused || placeholder,
|
||||||
|
start: decoration?.startDecoration,
|
||||||
|
})}
|
||||||
|
htmlFor={label}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className={cn("textfield-input", { start: decoration?.startDecoration })}
|
||||||
|
ref={this.inputRef}
|
||||||
|
id={label}
|
||||||
|
value={inputValue}
|
||||||
|
onChange={this.handleInputChange}
|
||||||
|
onFocus={this.handleFocus}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
placeholder={placeholder}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{decoration?.endDecoration && <div>{decoration.endDecoration}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class RemoteStatusLight extends React.Component<{ remote: RemoteType }, {}> {
|
class RemoteStatusLight extends React.Component<{ remote: RemoteType }, {}> {
|
||||||
render() {
|
render() {
|
||||||
@ -362,4 +509,6 @@ export {
|
|||||||
InfoMessage,
|
InfoMessage,
|
||||||
Markdown,
|
Markdown,
|
||||||
SettingsError,
|
SettingsError,
|
||||||
|
TextField,
|
||||||
|
InputDecoration,
|
||||||
};
|
};
|
||||||
|
@ -330,7 +330,7 @@
|
|||||||
.about-content {
|
.about-content {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
section {
|
.wave-section {
|
||||||
.logo-wrapper {
|
.logo-wrapper {
|
||||||
width: 72px;
|
width: 72px;
|
||||||
height: 72px;
|
height: 72px;
|
||||||
@ -403,12 +403,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section:nth-child(3) {
|
.wave-section:nth-child(3) {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
.button-link {
|
.wave-button-link {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
@ -418,7 +418,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section:last-child {
|
.wave-section:last-child {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
color: @term-white;
|
color: @term-white;
|
||||||
}
|
}
|
||||||
@ -426,7 +426,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.wave-button {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 6px 16px;
|
padding: 6px 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -435,7 +435,7 @@
|
|||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.color-green {
|
.wave-button.color-green {
|
||||||
color: @term-bright-white;
|
color: @term-bright-white;
|
||||||
background: @term-green !important; // !important is needed to override the default button color
|
background: @term-green !important; // !important is needed to override the default button color
|
||||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
||||||
@ -446,7 +446,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.color-standard {
|
.wave-button.color-standard {
|
||||||
color: @term-white;
|
color: @term-white;
|
||||||
background: var(--overlays-white-6, rgba(255, 255, 255, 0.12));
|
background: var(--overlays-white-6, rgba(255, 255, 255, 0.12));
|
||||||
|
|
||||||
@ -455,7 +455,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-link {
|
.wave-button-link {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 6px 16px;
|
padding: 6px 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -465,14 +465,12 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content {
|
.wave-section {
|
||||||
section {
|
display: flex;
|
||||||
display: flex;
|
align-items: center;
|
||||||
align-items: center;
|
gap: 16px;
|
||||||
gap: 16px;
|
align-self: stretch;
|
||||||
align-self: stretch;
|
width: 100%;
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal.welcome-modal {
|
.modal.welcome-modal {
|
||||||
|
@ -268,15 +268,17 @@ class TosModal extends React.Component<{}, {}> {
|
|||||||
<div className="item-inner">
|
<div className="item-inner">
|
||||||
<div className="item-title">Telemetry</div>
|
<div className="item-title">Telemetry</div>
|
||||||
<div className="item-text">
|
<div className="item-text">
|
||||||
We only collect minimal <i>anonymous</i> telemetry data to help us
|
We only collect minimal <i>anonymous</i> telemetry data to help us understand
|
||||||
understand how many people are using Wave.
|
how many people are using Wave.
|
||||||
</div>
|
</div>
|
||||||
<div className="item-field" style={{marginTop: 2}}>
|
<div className="item-field" style={{ marginTop: 2 }}>
|
||||||
<Toggle
|
<Toggle
|
||||||
checked={!cdata.clientopts.notelemetry}
|
checked={!cdata.clientopts.notelemetry}
|
||||||
onChange={this.handleChangeTelemetry}
|
onChange={this.handleChangeTelemetry}
|
||||||
/>
|
/>
|
||||||
<div className="item-label">Telemetry {cdata.clientopts.notelemetry ? "Disabled" : "Enabled"}</div>
|
<div className="item-label">
|
||||||
|
Telemetry {cdata.clientopts.notelemetry ? "Disabled" : "Enabled"}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -287,8 +289,9 @@ class TosModal extends React.Component<{}, {}> {
|
|||||||
<div className="item-inner">
|
<div className="item-inner">
|
||||||
<div className="item-title">Join our Community</div>
|
<div className="item-title">Join our Community</div>
|
||||||
<div className="item-text">
|
<div className="item-text">
|
||||||
Get help, submit feature requests, report bugs,
|
Get help, submit feature requests, report bugs, or just chat with fellow
|
||||||
or just chat with fellow terminal enthusiasts.<br/>
|
terminal enthusiasts.
|
||||||
|
<br />
|
||||||
<a target="_blank" href={util.makeExternLink("https://discord.gg/XfvZ334gwU")}>
|
<a target="_blank" href={util.makeExternLink("https://discord.gg/XfvZ334gwU")}>
|
||||||
Join the Wave Discord Channel
|
Join the Wave Discord Channel
|
||||||
</a>
|
</a>
|
||||||
@ -305,8 +308,8 @@ class TosModal extends React.Component<{}, {}> {
|
|||||||
<div className="item-inner">
|
<div className="item-inner">
|
||||||
<div className="item-title">Support us on GitHub</div>
|
<div className="item-title">Support us on GitHub</div>
|
||||||
<div className="item-text">
|
<div className="item-text">
|
||||||
We're <i>open source</i> and committed to providing a free terminal for individual
|
We're <i>open source</i> and committed to providing a free terminal for
|
||||||
users. Please show your support us by giving us a star on{" "}
|
individual users. Please show your support us by giving us a star on{" "}
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={util.makeExternLink("https://github.com/wavetermdev/waveterm")}
|
href={util.makeExternLink("https://github.com/wavetermdev/waveterm")}
|
||||||
@ -406,7 +409,7 @@ class AboutModal extends React.Component<{}, {}> {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div className="content about-content">
|
<div className="content about-content">
|
||||||
<section>
|
<section className="wave-section about-section">
|
||||||
<div className="logo-wrapper">
|
<div className="logo-wrapper">
|
||||||
<img src={logo} alt="logo" />
|
<img src={logo} alt="logo" />
|
||||||
</div>
|
</div>
|
||||||
@ -415,10 +418,12 @@ class AboutModal extends React.Component<{}, {}> {
|
|||||||
<div className="text-standard">Modern Terminal for Seamless Workflow</div>
|
<div className="text-standard">Modern Terminal for Seamless Workflow</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="text-standard">{this.getStatus(this.isUpToDate())}</section>
|
<section className="wave-section about-section text-standard">
|
||||||
<section>
|
{this.getStatus(this.isUpToDate())}
|
||||||
|
</section>
|
||||||
|
<section className="wave-section about-section">
|
||||||
<a
|
<a
|
||||||
className="button button-link color-standard"
|
className="wave-button wave-button-link color-standard"
|
||||||
href={util.makeExternLink("https://github.com/wavetermdev/waveterm")}
|
href={util.makeExternLink("https://github.com/wavetermdev/waveterm")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
@ -426,7 +431,7 @@ class AboutModal extends React.Component<{}, {}> {
|
|||||||
Github
|
Github
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
className="button button-link color-standard"
|
className="wave-button wave-button-link color-standard"
|
||||||
href={util.makeExternLink("https://www.commandline.dev/")}
|
href={util.makeExternLink("https://www.commandline.dev/")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
@ -434,7 +439,7 @@ class AboutModal extends React.Component<{}, {}> {
|
|||||||
Website
|
Website
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
className="button button-link color-standard"
|
className="wave-button wave-button-link color-standard"
|
||||||
href={util.makeExternLink(
|
href={util.makeExternLink(
|
||||||
"https://github.com/wavetermdev/waveterm/blob/main/LICENSE"
|
"https://github.com/wavetermdev/waveterm/blob/main/LICENSE"
|
||||||
)}
|
)}
|
||||||
@ -444,7 +449,9 @@ class AboutModal extends React.Component<{}, {}> {
|
|||||||
License
|
License
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
<section className="text-standard">Copyright © 2023 Command Line Inc.</section>
|
<section className="wave-section about-section text-standard">
|
||||||
|
Copyright © 2023 Command Line Inc.
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,15 +19,15 @@
|
|||||||
@textarea-background: #2a2a2a;
|
@textarea-background: #2a2a2a;
|
||||||
|
|
||||||
@text-primary: #fff;
|
@text-primary: #fff;
|
||||||
@text-secondary: #C3C8C2;
|
@text-secondary: #c3c8c2;
|
||||||
@text-caption: #8b918a;
|
@text-caption: #8b918a;
|
||||||
|
|
||||||
@accent-color: #3B3F3A;
|
@accent-color: #3b3f3a;
|
||||||
|
|
||||||
@status-outline: #151715;
|
@status-outline: #151715;
|
||||||
@dropdown-menu: rgba(21, 23, 21, 1);
|
@dropdown-menu: rgba(21, 23, 21, 1);
|
||||||
|
|
||||||
@status-connected: #46A758;
|
@status-connected: #46a758;
|
||||||
@status-connecting: #f5d90a;
|
@status-connecting: #f5d90a;
|
||||||
@status-error: #e54d2e;
|
@status-error: #e54d2e;
|
||||||
@status-disconnected: #c3c8c2;
|
@status-disconnected: #c3c8c2;
|
||||||
@ -53,11 +53,11 @@
|
|||||||
@tab-orange: #ef713b;
|
@tab-orange: #ef713b;
|
||||||
@tab-yellow: #e0b956;
|
@tab-yellow: #e0b956;
|
||||||
@tab-green: #58c142;
|
@tab-green: #58c142;
|
||||||
@tab-mint: #4BFFA9;
|
@tab-mint: #4bffa9;
|
||||||
@tab-cyan: #4BDFFF;
|
@tab-cyan: #4bdfff;
|
||||||
@tab-blue: #3971FF;
|
@tab-blue: #3971ff;
|
||||||
@tab-violet: #BA76FF;
|
@tab-violet: #ba76ff;
|
||||||
@tab-pink: #E05677;
|
@tab-pink: #e05677;
|
||||||
@tab-white: #ffffff;
|
@tab-white: #ffffff;
|
||||||
|
|
||||||
@tab-black-text: #333;
|
@tab-black-text: #333;
|
||||||
|
@ -49,12 +49,12 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: #4e9a06;
|
color: #4e9a06;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.should-fade {
|
&.should-fade {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
animation: fade-in 2.5s;
|
animation: fade-in 2.5s;
|
||||||
@ -119,11 +119,12 @@
|
|||||||
margin: 16px;
|
margin: 16px;
|
||||||
|
|
||||||
.newtab-section {
|
.newtab-section {
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
padding: 16px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 4px;
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
&.conn-section {
|
&.conn-section {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@ -145,13 +146,14 @@
|
|||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|
||||||
.icondiv {
|
.icondiv {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -169,6 +171,53 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 3px;
|
||||||
|
|
||||||
|
svg.status-icon {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
|
||||||
|
svg.add-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
|
||||||
|
path {
|
||||||
|
fill: @text-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-standard {
|
||||||
|
color: @text-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-caption {
|
||||||
|
color: @text-caption;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ellipsis {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(241, 246, 243, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
.icon.color-white + .check-icon {
|
.icon.color-white + .check-icon {
|
||||||
path {
|
path {
|
||||||
fill: black;
|
fill: black;
|
||||||
|
@ -10,7 +10,7 @@ import { If, For } 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 { GlobalCommandRunner, TabColors } from "../../../model/model";
|
import { GlobalCommandRunner, TabColors, TabIcons } from "../../../model/model";
|
||||||
import type { LineType, RenderModeType, LineFactoryProps, CommandRtnType } from "../../../types/types";
|
import type { LineType, RenderModeType, LineFactoryProps, CommandRtnType } from "../../../types/types";
|
||||||
import * as T from "../../../types/types";
|
import * as T from "../../../types/types";
|
||||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||||
@ -20,7 +20,8 @@ import { GlobalModel, ScreenLines, Screen, Session } from "../../../model/model"
|
|||||||
import { Line } from "../../line/linecomps";
|
import { Line } from "../../line/linecomps";
|
||||||
import { LinesView } from "../../line/linesview";
|
import { LinesView } from "../../line/linesview";
|
||||||
import { ConnectionDropdown } from "../../connections/connections";
|
import { ConnectionDropdown } from "../../connections/connections";
|
||||||
import * as util from "../../../util/util";
|
import * as util from "../../../util/util";
|
||||||
|
import { TextField, InputDecoration } from "../../common/common";
|
||||||
import { ReactComponent as EllipseIcon } from "../../assets/icons/ellipse.svg";
|
import { ReactComponent as EllipseIcon } from "../../assets/icons/ellipse.svg";
|
||||||
import { ReactComponent as Check12Icon } from "../../assets/icons/check12.svg";
|
import { ReactComponent as Check12Icon } from "../../assets/icons/check12.svg";
|
||||||
import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg";
|
import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg";
|
||||||
@ -37,7 +38,7 @@ dayjs.extend(localizedFormat);
|
|||||||
type OV<V> = mobx.IObservableValue<V>;
|
type OV<V> = mobx.IObservableValue<V>;
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class ScreenView extends React.Component<{ session: Session, screen: Screen }, {}> {
|
class ScreenView extends React.Component<{ session: Session; screen: Screen }, {}> {
|
||||||
render() {
|
render() {
|
||||||
let { session, screen } = this.props;
|
let { session, screen } = this.props;
|
||||||
if (screen == null) {
|
if (screen == null) {
|
||||||
@ -54,8 +55,9 @@ class ScreenView extends React.Component<{ session: Session, screen: Screen }, {
|
|||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "NewTabSettings-errorMessage" });
|
connDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "NewTabSettings-connDropdownActive" });
|
||||||
|
errorMessage: OV<string | null> = mobx.observable.box(null, { name: "NewTabSettings-errorMessage" });
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
selectTabColor(color: string): void {
|
selectTabColor(color: string): void {
|
||||||
let { screen } = this.props;
|
let { screen } = this.props;
|
||||||
@ -67,15 +69,29 @@ class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
inlineUpdateName(val: string): void {
|
selectTabIcon(icon: string): void {
|
||||||
let { screen } = this.props;
|
let { screen } = this.props;
|
||||||
if (util.isStrEq(val, screen.name.get())) {
|
if (screen.getTabIcon() == icon) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let prtn = GlobalCommandRunner.screenSetSettings(screen.screenId, { tabicon: icon }, false);
|
||||||
|
util.commandRtnHandler(prtn, this.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
updateName(val: string): void {
|
||||||
|
let { screen } = this.props;
|
||||||
let prtn = GlobalCommandRunner.screenSetSettings(screen.screenId, { name: val }, false);
|
let prtn = GlobalCommandRunner.screenSetSettings(screen.screenId, { name: val }, false);
|
||||||
util.commandRtnHandler(prtn, this.errorMessage);
|
util.commandRtnHandler(prtn, this.errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
toggleConnDropdown(): void {
|
||||||
|
mobx.action(() => {
|
||||||
|
this.connDropdownActive.set(!this.connDropdownActive.get());
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
selectRemote(cname: string): void {
|
selectRemote(cname: string): void {
|
||||||
let prtn = GlobalCommandRunner.screenSetRemote(cname, true, false);
|
let prtn = GlobalCommandRunner.screenSetRemote(cname, true, false);
|
||||||
@ -84,62 +100,112 @@ class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
clickNewConnection(): void {
|
clickNewConnection(): void {
|
||||||
GlobalModel.remotesModalModel.openModalForEdit({remoteedit: true}, true);
|
GlobalModel.remotesModalModel.openModalForEdit({ remoteedit: true }, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTabIconSelector(): React.ReactNode {
|
||||||
|
let { screen } = this.props;
|
||||||
|
let curIcon = screen.getTabIcon();
|
||||||
|
if (util.isBlank(curIcon) || curIcon == "default") {
|
||||||
|
curIcon = "square";
|
||||||
|
}
|
||||||
|
let icon: string | null = null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="text-s1 unselectable">Select the icon</div>
|
||||||
|
<div className="control-iconlist">
|
||||||
|
<For each="icon" of={TabIcons}>
|
||||||
|
<div
|
||||||
|
className="icondiv"
|
||||||
|
key={icon}
|
||||||
|
title={icon || ""}
|
||||||
|
onClick={() => this.selectTabIcon(icon || "")}
|
||||||
|
>
|
||||||
|
<i className={`fa-sharp fa-solid fa-${icon}`}></i>
|
||||||
|
</div>
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTabColorSelector(): React.ReactNode {
|
||||||
|
let { screen } = this.props;
|
||||||
|
let curColor = screen.getTabColor();
|
||||||
|
if (util.isBlank(curColor) || curColor == "default") {
|
||||||
|
curColor = "green";
|
||||||
|
}
|
||||||
|
let color: string | null = null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="text-s1 unselectable">Select the color</div>
|
||||||
|
<div className="control-iconlist">
|
||||||
|
<For each="color" of={TabColors}>
|
||||||
|
<div
|
||||||
|
className="icondiv"
|
||||||
|
key={color}
|
||||||
|
title={color || ""}
|
||||||
|
onClick={() => this.selectTabColor(color || "")}
|
||||||
|
>
|
||||||
|
<EllipseIcon className={cn("icon", "color-" + color)} />
|
||||||
|
<If condition={color == curColor}>
|
||||||
|
<Check12Icon className="check-icon" />
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { screen } = this.props;
|
let { screen } = this.props;
|
||||||
let rptr = screen.curRemote.get();
|
let rptr = screen.curRemote.get();
|
||||||
let curColor = screen.getTabColor();
|
|
||||||
if (util.isBlank(curColor) || curColor == "default") {
|
|
||||||
curColor = "green";
|
|
||||||
}
|
|
||||||
let color: string = null;
|
|
||||||
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||||
return (
|
return (
|
||||||
<div className="newtab-container">
|
<div className="newtab-container">
|
||||||
|
<div className="newtab-section name-section">
|
||||||
|
<div className="text-standard">Name</div>
|
||||||
|
<TextField
|
||||||
|
label="Title"
|
||||||
|
required={true}
|
||||||
|
defaultValue={screen.name.get() ?? ""}
|
||||||
|
onChange={this.updateName}
|
||||||
|
decoration={{
|
||||||
|
endDecoration: (
|
||||||
|
<InputDecoration>
|
||||||
|
<i className="fa-sharp fa-regular fa-circle-question"></i>
|
||||||
|
</InputDecoration>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="newtab-spacer" />
|
||||||
<div className="newtab-section conn-section">
|
<div className="newtab-section conn-section">
|
||||||
<div className="text-s1 unselectable">
|
<div className="text-s1 unselectable">
|
||||||
You're connected to [{getRemoteStr(rptr)}]. Do you want to change it?
|
You're connected to [{getRemoteStr(rptr)}]. Do you want to change it?
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ConnectionDropdown curRemote={curRemote} allowNewConn={true} onSelectRemote={this.selectRemote} onNewConn={this.clickNewConnection}/>
|
<ConnectionDropdown
|
||||||
|
curRemote={curRemote}
|
||||||
|
allowNewConn={true}
|
||||||
|
onSelectRemote={this.selectRemote}
|
||||||
|
onNewConn={this.clickNewConnection}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-caption cr-help-text">
|
<div className="text-caption cr-help-text">
|
||||||
To change connection from the command line use `cr [alias|user@host]`
|
To change connection from the command line use `cr [alias|user@host]`
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="newtab-spacer"/>
|
<div className="newtab-spacer" />
|
||||||
<div className="newtab-section settings-field">
|
|
||||||
<div className="text-s1 unselectable">
|
|
||||||
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="newtab-spacer"/>
|
|
||||||
<div className="newtab-section">
|
<div className="newtab-section">
|
||||||
<div className="text-s1 unselectable">
|
<div>{this.renderTabIconSelector()}</div>
|
||||||
Select the color
|
</div>
|
||||||
</div>
|
<div className="newtab-spacer" />
|
||||||
<div className="control-iconlist">
|
<div className="newtab-section">
|
||||||
<For each="color" of={TabColors}>
|
<div>{this.renderTabColorSelector()}</div>
|
||||||
<div className="icondiv" key={color} title={color} onClick={() => this.selectTabColor(color)}>
|
|
||||||
<EllipseIcon className={cn("icon", "color-" + color)}/>
|
|
||||||
<If condition={color == curColor}>
|
|
||||||
<Check12Icon className="check-icon"/>
|
|
||||||
</If>
|
|
||||||
</div>
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -148,7 +214,7 @@ class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
|||||||
|
|
||||||
// screen is not null
|
// screen is not null
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class ScreenWindowView extends React.Component<{ session: Session, screen: Screen }, {}> {
|
class ScreenWindowView extends React.Component<{ session: Session; screen: Screen }, {}> {
|
||||||
rszObs: any;
|
rszObs: any;
|
||||||
windowViewRef: React.RefObject<any>;
|
windowViewRef: React.RefObject<any>;
|
||||||
|
|
||||||
@ -309,13 +375,17 @@ class ScreenWindowView extends React.Component<{ session: Session, screen: Scree
|
|||||||
</div>
|
</div>
|
||||||
<If condition={lines.length == 0}>
|
<If condition={lines.length == 0}>
|
||||||
<If condition={screen.nextLineNum.get() == 1}>
|
<If condition={screen.nextLineNum.get() == 1}>
|
||||||
<NewTabSettings screen={screen}/>
|
<NewTabSettings screen={screen} />
|
||||||
</If>
|
</If>
|
||||||
<If condition={screen.nextLineNum.get() != 1}>
|
<If condition={screen.nextLineNum.get() != 1}>
|
||||||
<div className="window-view" ref={this.windowViewRef} data-screenid={screen.screenId}>
|
<div className="window-view" ref={this.windowViewRef} data-screenid={screen.screenId}>
|
||||||
<div key="lines" className="lines"></div>
|
<div key="lines" className="lines"></div>
|
||||||
<div key="window-empty" className={cn("window-empty")}>
|
<div key="window-empty" className={cn("window-empty")}>
|
||||||
<div><code className="text-standard">[workspace="{session.name.get()}" screen="{screen.name.get()}"]</code></div>
|
<div>
|
||||||
|
<code className="text-standard">
|
||||||
|
[workspace="{session.name.get()}" screen="{screen.name.get()}"]
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</If>
|
</If>
|
||||||
|
@ -8,14 +8,28 @@
|
|||||||
border-radius: 12px 0px 0px 0px;
|
border-radius: 12px 0px 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.color-green, &.color-default {
|
&.color-green,
|
||||||
|
&.color-default {
|
||||||
svg.left-icon path {
|
svg.left-icon path {
|
||||||
fill: @tab-green;
|
fill: @tab-green;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-green;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
border-top: 1px solid @tab-green;
|
border-top: 1px solid @tab-green;
|
||||||
background: linear-gradient(180deg, rgba(88, 193, 66, 0.20) 9.34%, rgba(88, 193, 66, 0.03) 44.16%, rgba(88, 193, 66, 0.00) 86.79%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(88, 193, 66, 0.2) 9.34%,
|
||||||
|
rgba(88, 193, 66, 0.03) 44.16%,
|
||||||
|
rgba(88, 193, 66, 0) 86.79%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-green;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,9 +38,18 @@
|
|||||||
fill: @tab-orange;
|
fill: @tab-orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-orange;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
border-top: 1px solid @tab-orange;
|
border-top: 1px solid @tab-orange;
|
||||||
background: linear-gradient(180deg, rgba(239, 113, 59, 0.20) 9.34%, rgba(239, 113, 59, 0.03) 44.16%, rgba(239, 113, 59, 0.00) 86.79%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(239, 113, 59, 0.2) 9.34%,
|
||||||
|
rgba(239, 113, 59, 0.03) 44.16%,
|
||||||
|
rgba(239, 113, 59, 0) 86.79%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,9 +58,18 @@
|
|||||||
fill: @tab-red;
|
fill: @tab-red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-red;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
border-top: 1px solid @tab-red;
|
border-top: 1px solid @tab-red;
|
||||||
background: linear-gradient(180deg, rgba(229, 77, 46, 0.20) 9.34%, rgba(229, 77, 46, 0.03) 44.16%, rgba(229, 77, 46, 0.00) 86.79%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(229, 77, 46, 0.2) 9.34%,
|
||||||
|
rgba(229, 77, 46, 0.03) 44.16%,
|
||||||
|
rgba(229, 77, 46, 0) 86.79%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,9 +78,18 @@
|
|||||||
fill: @tab-yellow;
|
fill: @tab-yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active {
|
.icon i {
|
||||||
|
color: @tab-yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
border-top: 1px solid @tab-yellow;
|
border-top: 1px solid @tab-yellow;
|
||||||
background: linear-gradient(180deg, rgba(224, 185, 86, 0.20) 9.34%, rgba(224, 185, 86, 0.03) 44.16%, rgba(224, 185, 86, 0.00) 86.79%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(224, 185, 86, 0.2) 9.34%,
|
||||||
|
rgba(224, 185, 86, 0.03) 44.16%,
|
||||||
|
rgba(224, 185, 86, 0) 86.79%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,9 +98,18 @@
|
|||||||
fill: @tab-blue;
|
fill: @tab-blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-blue;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
border-top: 1px solid @tab-blue;
|
border-top: 1px solid @tab-blue;
|
||||||
background: linear-gradient(180deg, rgba(57, 113, 255, 0.20) 9.34%, rgba(57, 113, 255, 0.03) 44.16%, rgba(57, 113, 255, 0.00) 77.18%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(57, 113, 255, 0.2) 9.34%,
|
||||||
|
rgba(57, 113, 255, 0.03) 44.16%,
|
||||||
|
rgba(57, 113, 255, 0) 77.18%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,9 +118,18 @@
|
|||||||
fill: @tab-mint;
|
fill: @tab-mint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-mint;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
border-top: 1px solid @tab-mint;
|
border-top: 1px solid @tab-mint;
|
||||||
background: linear-gradient(180deg, rgba(75, 255, 169, 0.20) 9.34%, rgba(75, 255, 169, 0.03) 44.16%, rgba(75, 255, 169, 0.00) 77.18%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(75, 255, 169, 0.2) 9.34%,
|
||||||
|
rgba(75, 255, 169, 0.03) 44.16%,
|
||||||
|
rgba(75, 255, 169, 0) 77.18%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,9 +138,18 @@
|
|||||||
fill: @tab-cyan;
|
fill: @tab-cyan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-cyan;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
border-top: 1px solid @tab-cyan;
|
border-top: 1px solid @tab-cyan;
|
||||||
background: linear-gradient(180deg, rgba(75, 223, 255, 0.20) 9.34%, rgba(75, 223, 255, 0.03) 44.16%, rgba(58, 186, 214, 0.00) 86.79%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(75, 223, 255, 0.2) 9.34%,
|
||||||
|
rgba(75, 223, 255, 0.03) 44.16%,
|
||||||
|
rgba(58, 186, 214, 0) 86.79%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,9 +158,18 @@
|
|||||||
fill: @tab-white;
|
fill: @tab-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-white;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
border-top: 1px solid @tab-white;
|
border-top: 1px solid @tab-white;
|
||||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.20) 9.34%, rgba(255, 255, 255, 0.03) 44.16%, rgba(255, 255, 255, 0.00) 86.79%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(255, 255, 255, 0.2) 9.34%,
|
||||||
|
rgba(255, 255, 255, 0.03) 44.16%,
|
||||||
|
rgba(255, 255, 255, 0) 86.79%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,9 +178,18 @@
|
|||||||
fill: @tab-violet;
|
fill: @tab-violet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-violet;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
border-top: 1px solid @tab-violet;
|
border-top: 1px solid @tab-violet;
|
||||||
background: linear-gradient(180deg, rgba(186, 118, 255, 0.20) 9.34%, rgba(186, 118, 255, 0.03) 44.16%, rgba(186, 118, 255, 0.00) 86.79%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(186, 118, 255, 0.2) 9.34%,
|
||||||
|
rgba(186, 118, 255, 0.03) 44.16%,
|
||||||
|
rgba(186, 118, 255, 0) 86.79%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +198,18 @@
|
|||||||
fill: @tab-pink;
|
fill: @tab-pink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon i {
|
||||||
|
color: @tab-pink;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
border-top: 1px solid @tab-pink;
|
border-top: 1px solid @tab-pink;
|
||||||
background: linear-gradient(180deg, rgba(255, 136, 165, 0.20) 9.34%, rgba(255, 136, 165, 0.03) 44.16%, rgba(255, 136, 165, 0.00) 86.79%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(255, 136, 165, 0.2) 9.34%,
|
||||||
|
rgba(255, 136, 165, 0.03) 44.16%,
|
||||||
|
rgba(255, 136, 165, 0) 86.79%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,19 @@ class ScreenTabs extends React.Component<{ session: Session }, {}> {
|
|||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTab(screen: Screen, activeScreenId: string, index: number): any {
|
renderTabIcon = (screen: Screen): React.ReactNode => {
|
||||||
|
const tabIcon = screen.getTabIcon();
|
||||||
|
if (tabIcon === "default") {
|
||||||
|
return <SquareIcon className="icon left-icon" />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="icon">
|
||||||
|
<i className={`fa-sharp fa-solid fa-${tabIcon}`}></i>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderTab(screen: Screen, activeScreenId: string, index: number): JSX.Element {
|
||||||
let tabIndex = null;
|
let tabIndex = null;
|
||||||
if (index + 1 <= 9) {
|
if (index + 1 <= 9) {
|
||||||
tabIndex = <div className="tab-index">{renderCmdText(String(index + 1))}</div>;
|
tabIndex = <div className="tab-index">{renderCmdText(String(index + 1))}</div>;
|
||||||
@ -132,7 +144,7 @@ class ScreenTabs extends React.Component<{ session: Session }, {}> {
|
|||||||
onClick={() => this.handleSwitchScreen(screen.screenId)}
|
onClick={() => this.handleSwitchScreen(screen.screenId)}
|
||||||
onContextMenu={(event) => this.openScreenSettings(event, screen)}
|
onContextMenu={(event) => this.openScreenSettings(event, screen)}
|
||||||
>
|
>
|
||||||
<SquareIcon className="icon left-icon" />
|
{this.renderTabIcon(screen)}
|
||||||
<div className="tab-name truncate">
|
<div className="tab-name truncate">
|
||||||
{archived}
|
{archived}
|
||||||
{webShared}
|
{webShared}
|
||||||
@ -149,7 +161,7 @@ class ScreenTabs extends React.Component<{ session: Session }, {}> {
|
|||||||
if (session == null) {
|
if (session == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let screen: Screen = null;
|
let screen: Screen | null = null;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let showingScreens = [];
|
let showingScreens = [];
|
||||||
let activeScreenId = session.activeScreenId.get();
|
let activeScreenId = session.activeScreenId.get();
|
||||||
|
@ -95,6 +95,18 @@ const MaxFontSize = 15;
|
|||||||
const InputChunkSize = 500;
|
const InputChunkSize = 500;
|
||||||
const RemoteColors = ["red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"];
|
const RemoteColors = ["red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"];
|
||||||
const TabColors = ["red", "orange", "yellow", "green", "mint", "cyan", "blue", "violet", "pink", "white"];
|
const TabColors = ["red", "orange", "yellow", "green", "mint", "cyan", "blue", "violet", "pink", "white"];
|
||||||
|
const TabIcons = [
|
||||||
|
"sparkle",
|
||||||
|
"fire",
|
||||||
|
"ghost",
|
||||||
|
"cloud",
|
||||||
|
"compass",
|
||||||
|
"crown",
|
||||||
|
"droplet",
|
||||||
|
"graduation-cap",
|
||||||
|
"heart",
|
||||||
|
"file",
|
||||||
|
];
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const VERSION = __WAVETERM_VERSION__;
|
const VERSION = __WAVETERM_VERSION__;
|
||||||
@ -469,6 +481,15 @@ class Screen {
|
|||||||
return tabColor;
|
return tabColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTabIcon(): string {
|
||||||
|
let tabIcon = "default";
|
||||||
|
let screenOpts = this.opts.get();
|
||||||
|
if (screenOpts != null && !isBlank(screenOpts.tabicon)) {
|
||||||
|
tabIcon = screenOpts.tabicon;
|
||||||
|
}
|
||||||
|
return tabIcon;
|
||||||
|
}
|
||||||
|
|
||||||
getCurRemoteInstance(): RemoteInstanceType {
|
getCurRemoteInstance(): RemoteInstanceType {
|
||||||
let session = GlobalModel.getSessionById(this.sessionId);
|
let session = GlobalModel.getSessionById(this.sessionId);
|
||||||
let rptr = this.curRemote.get();
|
let rptr = this.curRemote.get();
|
||||||
@ -3492,7 +3513,7 @@ class Model {
|
|||||||
submitCommand(
|
submitCommand(
|
||||||
metaCmd: string,
|
metaCmd: string,
|
||||||
metaSubCmd: string,
|
metaSubCmd: string,
|
||||||
args: string[],
|
args: string[] | null,
|
||||||
kwargs: Record<string, string>,
|
kwargs: Record<string, string>,
|
||||||
interactive: boolean
|
interactive: boolean
|
||||||
): Promise<CommandRtnType> {
|
): Promise<CommandRtnType> {
|
||||||
@ -3505,7 +3526,7 @@ class Model {
|
|||||||
uicontext: this.getUIContext(),
|
uicontext: this.getUIContext(),
|
||||||
interactive: interactive,
|
interactive: interactive,
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
console.log(
|
console.log(
|
||||||
"CMD",
|
"CMD",
|
||||||
pk.metacmd + (pk.metasubcmd != null ? ":" + pk.metasubcmd : ""),
|
pk.metacmd + (pk.metasubcmd != null ? ":" + pk.metasubcmd : ""),
|
||||||
@ -3513,7 +3534,7 @@ class Model {
|
|||||||
pk.kwargs,
|
pk.kwargs,
|
||||||
pk.interactive
|
pk.interactive
|
||||||
);
|
);
|
||||||
*/
|
*/
|
||||||
return this.submitCommandPacket(pk, interactive);
|
return this.submitCommandPacket(pk, interactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3950,10 +3971,10 @@ class CommandRunner {
|
|||||||
|
|
||||||
screenSetSettings(
|
screenSetSettings(
|
||||||
screenId: string,
|
screenId: string,
|
||||||
settings: { tabcolor?: string; name?: string; sharename?: string },
|
settings: { tabcolor?: string; tabicon?: string; name?: string; sharename?: string },
|
||||||
interactive: boolean
|
interactive: boolean
|
||||||
): Promise<CommandRtnType> {
|
): Promise<CommandRtnType> {
|
||||||
let kwargs = Object.assign({}, settings);
|
let kwargs: { [key: string]: any } = Object.assign({}, settings);
|
||||||
kwargs["nohist"] = "1";
|
kwargs["nohist"] = "1";
|
||||||
kwargs["screen"] = screenId;
|
kwargs["screen"] = screenId;
|
||||||
return GlobalModel.submitCommand("screen", "set", null, kwargs, interactive);
|
return GlobalModel.submitCommand("screen", "set", null, kwargs, interactive);
|
||||||
@ -4169,6 +4190,7 @@ export {
|
|||||||
Screen,
|
Screen,
|
||||||
riToRPtr,
|
riToRPtr,
|
||||||
TabColors,
|
TabColors,
|
||||||
|
TabIcons,
|
||||||
RemoteColors,
|
RemoteColors,
|
||||||
getTermPtyData,
|
getTermPtyData,
|
||||||
RemotesModalModel,
|
RemotesModalModel,
|
||||||
|
@ -50,6 +50,7 @@ type LineType = {
|
|||||||
|
|
||||||
type ScreenOptsType = {
|
type ScreenOptsType = {
|
||||||
tabcolor?: string;
|
tabcolor?: string;
|
||||||
|
tabicon?: string;
|
||||||
pterm?: string;
|
pterm?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -167,7 +168,7 @@ type FeCmdPacketType = {
|
|||||||
type: string;
|
type: string;
|
||||||
metacmd: string;
|
metacmd: string;
|
||||||
metasubcmd?: string;
|
metasubcmd?: string;
|
||||||
args: string[];
|
args: string[] | null;
|
||||||
kwargs: Record<string, string>;
|
kwargs: Record<string, string>;
|
||||||
rawstr?: string;
|
rawstr?: string;
|
||||||
uicontext: UIContextType;
|
uicontext: UIContextType;
|
||||||
@ -632,6 +633,7 @@ type FileInfoType = {
|
|||||||
|
|
||||||
type ExtBlob = Blob & {
|
type ExtBlob = Blob & {
|
||||||
notFound: boolean;
|
notFound: boolean;
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ExtFile = File & {
|
type ExtFile = File & {
|
||||||
|
@ -390,7 +390,7 @@ function getColorRGB(colorInput) {
|
|||||||
return computedColorStyle;
|
return computedColorStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function commandRtnHandler(prtn: Promise<CommandRtnType>, errorMessage: OV<string>) {
|
function commandRtnHandler(prtn: Promise<CommandRtnType>, errorMessage: OV<string>) {
|
||||||
prtn.then((crtn) => {
|
prtn.then((crtn) => {
|
||||||
if (crtn.success) {
|
if (crtn.success) {
|
||||||
return;
|
return;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true
|
"isolatedModules": true,
|
||||||
|
"experimentalDecorators": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var ColorNames = []string{"yellow", "blue", "pink", "mint", "cyan", "violet", "orange", "green", "red", "white"}
|
var ColorNames = []string{"yellow", "blue", "pink", "mint", "cyan", "violet", "orange", "green", "red", "white"}
|
||||||
|
var TabIcons = []string{"sparkle", "fire", "ghost", "cloud", "compass", "crown", "droplet", "graduation-cap", "heart", "file"}
|
||||||
var RemoteColorNames = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"}
|
var RemoteColorNames = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"}
|
||||||
var RemoteSetArgs = []string{"alias", "connectmode", "key", "password", "autoinstall", "color"}
|
var RemoteSetArgs = []string{"alias", "connectmode", "key", "password", "autoinstall", "color"}
|
||||||
|
|
||||||
@ -81,6 +82,7 @@ var GlobalCmds = []string{"session", "screen", "remote", "set", "client", "telem
|
|||||||
|
|
||||||
var SetVarNameMap map[string]string = map[string]string{
|
var SetVarNameMap map[string]string = map[string]string{
|
||||||
"tabcolor": "screen.tabcolor",
|
"tabcolor": "screen.tabcolor",
|
||||||
|
"tabicon": "screen.tabicon",
|
||||||
"pterm": "screen.pterm",
|
"pterm": "screen.pterm",
|
||||||
"anchor": "screen.anchor",
|
"anchor": "screen.anchor",
|
||||||
"focus": "screen.focus",
|
"focus": "screen.focus",
|
||||||
@ -91,7 +93,7 @@ var SetVarScopes = []SetVarScope{
|
|||||||
SetVarScope{ScopeName: "global", VarNames: []string{}},
|
SetVarScope{ScopeName: "global", VarNames: []string{}},
|
||||||
SetVarScope{ScopeName: "client", VarNames: []string{"telemetry"}},
|
SetVarScope{ScopeName: "client", VarNames: []string{"telemetry"}},
|
||||||
SetVarScope{ScopeName: "session", VarNames: []string{"name", "pos"}},
|
SetVarScope{ScopeName: "session", VarNames: []string{"name", "pos"}},
|
||||||
SetVarScope{ScopeName: "screen", VarNames: []string{"name", "tabcolor", "pos", "pterm", "anchor", "focus", "line"}},
|
SetVarScope{ScopeName: "screen", VarNames: []string{"name", "tabcolor", "tabicon", "pos", "pterm", "anchor", "focus", "line"}},
|
||||||
SetVarScope{ScopeName: "line", VarNames: []string{}},
|
SetVarScope{ScopeName: "line", VarNames: []string{}},
|
||||||
// connection = remote, remote = remoteinstance
|
// connection = remote, remote = remoteinstance
|
||||||
SetVarScope{ScopeName: "connection", VarNames: []string{"alias", "connectmode", "key", "password", "autoinstall", "color"}},
|
SetVarScope{ScopeName: "connection", VarNames: []string{"alias", "connectmode", "key", "password", "autoinstall", "color"}},
|
||||||
@ -757,6 +759,16 @@ func ScreenSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
|
|||||||
varsUpdated = append(varsUpdated, "tabcolor")
|
varsUpdated = append(varsUpdated, "tabcolor")
|
||||||
setNonAnchor = true
|
setNonAnchor = true
|
||||||
}
|
}
|
||||||
|
if pk.Kwargs["tabicon"] != "" {
|
||||||
|
icon := pk.Kwargs["tabicon"]
|
||||||
|
err = validateIcon(icon, "screen tabicon")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
updateMap[sstore.ScreenField_TabIcon] = icon
|
||||||
|
varsUpdated = append(varsUpdated, "tabicon")
|
||||||
|
setNonAnchor = true
|
||||||
|
}
|
||||||
if pk.Kwargs["pos"] != "" {
|
if pk.Kwargs["pos"] != "" {
|
||||||
varsUpdated = append(varsUpdated, "pos")
|
varsUpdated = append(varsUpdated, "pos")
|
||||||
setNonAnchor = true
|
setNonAnchor = true
|
||||||
@ -806,7 +818,7 @@ func ScreenSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(varsUpdated) == 0 {
|
if len(varsUpdated) == 0 {
|
||||||
return nil, fmt.Errorf("/screen:set no updates, can set %s", formatStrs([]string{"name", "pos", "tabcolor", "focus", "anchor", "line", "sharename"}, "or", false))
|
return nil, fmt.Errorf("/screen:set no updates, can set %s", formatStrs([]string{"name", "pos", "tabcolor", "tabicon", "focus", "anchor", "line", "sharename"}, "or", false))
|
||||||
}
|
}
|
||||||
screen, err := sstore.UpdateScreen(ctx, ids.ScreenId, updateMap)
|
screen, err := sstore.UpdateScreen(ctx, ids.ScreenId, updateMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1978,6 +1990,15 @@ func validateColor(color string, typeStr string) error {
|
|||||||
return fmt.Errorf("invalid %s, valid colors are: %s", typeStr, formatStrs(ColorNames, "or", false))
|
return fmt.Errorf("invalid %s, valid colors are: %s", typeStr, formatStrs(ColorNames, "or", false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateIcon(icon string, typeStr string) error {
|
||||||
|
for _, c := range TabIcons {
|
||||||
|
if icon == c {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("invalid %s, valid icons are: %s", typeStr, formatStrs(TabIcons, "or", false))
|
||||||
|
}
|
||||||
|
|
||||||
func validateRemoteColor(color string, typeStr string) error {
|
func validateRemoteColor(color string, typeStr string) error {
|
||||||
for _, c := range RemoteColorNames {
|
for _, c := range RemoteColorNames {
|
||||||
if color == c {
|
if color == c {
|
||||||
|
@ -1709,6 +1709,7 @@ const (
|
|||||||
ScreenField_SelectedLine = "selectedline" // int
|
ScreenField_SelectedLine = "selectedline" // int
|
||||||
ScreenField_Focus = "focustype" // string
|
ScreenField_Focus = "focustype" // string
|
||||||
ScreenField_TabColor = "tabcolor" // string
|
ScreenField_TabColor = "tabcolor" // string
|
||||||
|
ScreenField_TabIcon = "tabicon" // string
|
||||||
ScreenField_PTerm = "pterm" // string
|
ScreenField_PTerm = "pterm" // string
|
||||||
ScreenField_Name = "name" // string
|
ScreenField_Name = "name" // string
|
||||||
ScreenField_ShareName = "sharename" // string
|
ScreenField_ShareName = "sharename" // string
|
||||||
@ -1743,6 +1744,10 @@ func UpdateScreen(ctx context.Context, screenId string, editMap map[string]inter
|
|||||||
query = `UPDATE screen SET screenopts = json_set(screenopts, '$.tabcolor', ?) WHERE screenid = ?`
|
query = `UPDATE screen SET screenopts = json_set(screenopts, '$.tabcolor', ?) WHERE screenid = ?`
|
||||||
tx.Exec(query, tabColor, screenId)
|
tx.Exec(query, tabColor, screenId)
|
||||||
}
|
}
|
||||||
|
if tabIcon, found := editMap[ScreenField_TabIcon]; found {
|
||||||
|
query = `UPDATE screen SET screenopts = json_set(screenopts, '$.tabicon', ?) WHERE screenid = ?`
|
||||||
|
tx.Exec(query, tabIcon, screenId)
|
||||||
|
}
|
||||||
if pterm, found := editMap[ScreenField_PTerm]; found {
|
if pterm, found := editMap[ScreenField_PTerm]; found {
|
||||||
query = `UPDATE screen SET screenopts = json_set(screenopts, '$.pterm', ?) WHERE screenid = ?`
|
query = `UPDATE screen SET screenopts = json_set(screenopts, '$.pterm', ?) WHERE screenid = ?`
|
||||||
tx.Exec(query, pterm, screenId)
|
tx.Exec(query, pterm, screenId)
|
||||||
|
@ -433,6 +433,7 @@ func (h *HistoryItemType) FromMap(m map[string]interface{}) bool {
|
|||||||
|
|
||||||
type ScreenOptsType struct {
|
type ScreenOptsType struct {
|
||||||
TabColor string `json:"tabcolor,omitempty"`
|
TabColor string `json:"tabcolor,omitempty"`
|
||||||
|
TabIcon string `json:"tabicon,omitempty"`
|
||||||
PTerm string `json:"pterm,omitempty"`
|
PTerm string `json:"pterm,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user