mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-03-11 13:23:06 +01:00
Merge pull request #119 from wavetermdev/dev-0.5.1
pull dev-0.5.1 branch to main
This commit is contained in:
commit
08762fdfc3
@ -33,7 +33,7 @@ WAVETERM_DEV=1 PCLOUD_ENDPOINT="https://ot2e112zx5.execute-api.us-west-2.amazona
|
||||
```bash
|
||||
# @scripthaus command typecheck
|
||||
# @scripthaus cd :playbook
|
||||
node_modules/.bin/tsc --jsx preserve --noEmit --esModuleInterop --target ES5 --experimentalDecorators --downlevelIteration src/index.ts src/types/custom.d.ts
|
||||
node_modules/.bin/tsc --noEmit
|
||||
```
|
||||
|
||||
```bash
|
||||
|
@ -49,6 +49,14 @@ textarea {
|
||||
}
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
font-family: @text-s1-font;
|
||||
color: @text-primary;
|
||||
}
|
||||
|
||||
.text-standard {
|
||||
font-size: 12.5px;
|
||||
font-weight: 300;
|
||||
@ -80,6 +88,15 @@ textarea {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
body a {
|
||||
color: @wave-green;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
color: @wave-green;
|
||||
}
|
||||
}
|
||||
|
||||
body code {
|
||||
font-family: @terminal-font;
|
||||
}
|
||||
@ -148,10 +165,10 @@ svg.icon {
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
background-color: @button-background !important;
|
||||
color: @prompt-green;
|
||||
color: @wave-green;
|
||||
.hoverEffect;
|
||||
&:hover {
|
||||
color: @prompt-green;
|
||||
color: @wave-green;
|
||||
}
|
||||
&.disabled {
|
||||
color: fade(@disabled-color, 60%);
|
||||
@ -176,12 +193,6 @@ svg.icon {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
a:hover {
|
||||
color: #485fc7;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -219,7 +230,8 @@ a.a-block {
|
||||
|
||||
.history-view,
|
||||
.bookmarks-view,
|
||||
.plugins-view {
|
||||
.plugins-view,
|
||||
.connections-view {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -465,85 +477,99 @@ a.a-block {
|
||||
}
|
||||
|
||||
.icon.color-red {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-red;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-green {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-green;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-orange {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-orange;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-blue {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-blue;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-yellow {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-yellow;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-pink {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-pink;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-mint {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-mint;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-cyan {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-cyan;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-violet {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-violet;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-white {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-white;
|
||||
}
|
||||
}
|
||||
|
||||
.status-icon.status-connected {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @status-connected;
|
||||
}
|
||||
}
|
||||
|
||||
.status-icon.status-connecting {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @status-connecting;
|
||||
}
|
||||
}
|
||||
|
||||
.status-icon.status-disconnected {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @status-disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
.status-icon.status-error {
|
||||
path, circle {
|
||||
path,
|
||||
circle {
|
||||
fill: @status-error;
|
||||
}
|
||||
}
|
||||
|
@ -15,22 +15,16 @@ import { WorkspaceView } from "./workspace/workspaceview";
|
||||
import { PluginsView } from "./pluginsview/pluginsview";
|
||||
import { BookmarksView } from "./bookmarks/bookmarks";
|
||||
import { HistoryView } from "./history/history";
|
||||
import { ConnectionsView } from "./connections/connections";
|
||||
import {
|
||||
ScreenSettingsModal,
|
||||
SessionSettingsModal,
|
||||
LineSettingsModal,
|
||||
ClientSettingsModal,
|
||||
} from "./common/modals/settings";
|
||||
import { RemotesModal } from "./connections/connections";
|
||||
import { TosModal } from "./common/modals/modals";
|
||||
import { MainSideBar } from "./sidebar/sidebar";
|
||||
import {
|
||||
DisconnectedModal,
|
||||
ClientStopModal,
|
||||
AlertModal,
|
||||
AboutModal,
|
||||
CreateRemoteConnModal,
|
||||
} from "./common/modals/modals";
|
||||
import { DisconnectedModal, ClientStopModal, ModalsProvider } from "./common/modals/modals";
|
||||
import { ErrorBoundary } from "./common/error/errorboundary";
|
||||
import "./app.less";
|
||||
|
||||
@ -64,7 +58,7 @@ class App extends React.Component<{}, {}> {
|
||||
opts.showCut = true;
|
||||
}
|
||||
let sel = window.getSelection();
|
||||
if (!isBlank(sel.toString())) {
|
||||
if (!isBlank(sel?.toString())) {
|
||||
GlobalModel.contextEditMenu(e, opts);
|
||||
} else {
|
||||
if (isInNonTermInput) {
|
||||
@ -85,14 +79,12 @@ class App extends React.Component<{}, {}> {
|
||||
let sessionSettingsModal = GlobalModel.sessionSettingsModal.get();
|
||||
let lineSettingsModal = GlobalModel.lineSettingsModal.get();
|
||||
let clientSettingsModal = GlobalModel.clientSettingsModal.get();
|
||||
let remotesModel = GlobalModel.remotesModalModel;
|
||||
let remotesModal = remotesModel.isOpen();
|
||||
let selectedRemoteId = remotesModel.selectedRemoteId.get();
|
||||
let remoteEdit = remotesModel.remoteEdit.get();
|
||||
let remotesModel = GlobalModel.remotesModel;
|
||||
let disconnected = !GlobalModel.ws.open.get() || !GlobalModel.waveSrvRunning.get();
|
||||
let hasClientStop = GlobalModel.getHasClientStop();
|
||||
let dcWait = this.dcWait.get();
|
||||
let platform = GlobalModel.getPlatform();
|
||||
|
||||
if (disconnected || hasClientStop) {
|
||||
if (!dcWait) {
|
||||
setTimeout(() => this.updateDcWait(true), 1500);
|
||||
@ -117,7 +109,6 @@ class App extends React.Component<{}, {}> {
|
||||
if (dcWait) {
|
||||
setTimeout(() => this.updateDcWait(false), 0);
|
||||
}
|
||||
//console.log(`GlobalModel.activeMainView.get() = ${GlobalModel.activeMainView.get()}`); // @mike - if I remove this, I cant see plugins
|
||||
return (
|
||||
<div id="main" className={"platform-" + platform} onContextMenu={this.handleContextMenu}>
|
||||
<div className="main-content">
|
||||
@ -127,18 +118,13 @@ class App extends React.Component<{}, {}> {
|
||||
<WorkspaceView />
|
||||
<HistoryView />
|
||||
<BookmarksView />
|
||||
<ConnectionsView model={remotesModel} />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
<AlertModal />
|
||||
<If condition={GlobalModel.needsTos()}>
|
||||
<TosModal />
|
||||
</If>
|
||||
<If condition={GlobalModel.aboutModalOpen.get()}>
|
||||
<AboutModal />
|
||||
</If>
|
||||
<If condition={remoteEdit !== null && !remoteEdit.old}>
|
||||
<CreateRemoteConnModal model={remotesModel} remoteEdit={remoteEdit} />
|
||||
</If>
|
||||
<ModalsProvider />
|
||||
<If condition={screenSettingsModal != null}>
|
||||
<ScreenSettingsModal
|
||||
key={screenSettingsModal.sessionId + ":" + screenSettingsModal.screenId}
|
||||
@ -155,9 +141,6 @@ class App extends React.Component<{}, {}> {
|
||||
<If condition={clientSettingsModal}>
|
||||
<ClientSettingsModal />
|
||||
</If>
|
||||
<If condition={remotesModal}>
|
||||
<RemotesModal model={GlobalModel.remotesModalModel} />
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
5
src/app/appconst.ts
Normal file
5
src/app/appconst.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const ABOUT = "about";
|
||||
export const CREATE_REMOTE = "createRemote";
|
||||
export const VIEW_REMOTE = "viewRemote";
|
||||
export const EDIT_REMOTE = "editRemote";
|
||||
export const ALERT = "alert";
|
@ -247,7 +247,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.button.is-prompt-green {
|
||||
.button.is-wave-green {
|
||||
background-color: #222;
|
||||
color: @term-white;
|
||||
|
||||
@ -257,33 +257,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.wave-button {
|
||||
display: flex;
|
||||
padding: 6px 16px !important;
|
||||
color: @term-white !important;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 6px !important;
|
||||
height: auto !important;
|
||||
|
||||
&:hover {
|
||||
color: @term-white !important;
|
||||
}
|
||||
}
|
||||
|
||||
.wave-button.is-wave-green {
|
||||
color: @term-bright-white !important;
|
||||
background: @term-green !important;
|
||||
|
||||
&:hover {
|
||||
background-color: @term-green;
|
||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
||||
0px 0px 0.5px 0px rgba(255, 255, 255, 0.8) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.6) inset;
|
||||
color: @term-bright-white !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.button.is-plain,
|
||||
.button.is-prompt-cancel {
|
||||
background-color: #222;
|
||||
@ -637,7 +610,8 @@
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
height: 44px;
|
||||
width: 412px;
|
||||
min-width: 412px;
|
||||
width: 100%;
|
||||
border: 1px solid var(--element-separator, rgba(241, 246, 243, 0.15));
|
||||
border-radius: 6px;
|
||||
background: var(--element-hover-2, rgba(255, 255, 255, 0.06));
|
||||
@ -801,10 +775,18 @@
|
||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
||||
0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
|
||||
&:hover {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
&.focused {
|
||||
border-color: @term-green;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
&.error {
|
||||
border-color: @term-red;
|
||||
}
|
||||
@ -931,3 +913,228 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wave-button {
|
||||
background: none;
|
||||
color: inherit;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
outline: inherit;
|
||||
|
||||
display: flex;
|
||||
padding: 6px 16px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 6px;
|
||||
height: auto;
|
||||
|
||||
&:hover {
|
||||
color: @term-white;
|
||||
}
|
||||
|
||||
i {
|
||||
fill: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
&.primary {
|
||||
color: @term-green;
|
||||
background: none;
|
||||
|
||||
i {
|
||||
fill: @term-green;
|
||||
}
|
||||
|
||||
&.solid {
|
||||
color: @term-bright-white;
|
||||
background: @term-green;
|
||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
||||
0px 0px 0.5px 0px rgba(255, 255, 255, 0.8) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.6) inset;
|
||||
|
||||
i {
|
||||
fill: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
&.outlined {
|
||||
border: 1px solid @term-green;
|
||||
}
|
||||
|
||||
&.ghost {
|
||||
// Styles for .ghost are already defined above
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: @term-bright-white;
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
color: @term-white;
|
||||
background: none;
|
||||
|
||||
&.solid {
|
||||
background: rgba(255, 255, 255, 0.09);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.outlined {
|
||||
border: 1px solid rgba(255, 255, 255, 0.09);
|
||||
}
|
||||
|
||||
&.ghost {
|
||||
padding: 6px 10px;
|
||||
|
||||
i {
|
||||
fill: @term-green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.color-yellow {
|
||||
&.solid {
|
||||
border-color: @warning-yellow;
|
||||
background-color: mix(@warning-yellow, @term-white, 50%);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.outlined {
|
||||
color: @warning-yellow;
|
||||
border-color: @warning-yellow;
|
||||
&:hover {
|
||||
color: @term-white;
|
||||
border-color: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
&.ghost {
|
||||
}
|
||||
}
|
||||
|
||||
&.color-red {
|
||||
&.solid {
|
||||
border-color: @term-red;
|
||||
background-color: mix(@term-red, @term-white, 50%);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.outlined {
|
||||
color: @term-red;
|
||||
border-color: @term-red;
|
||||
}
|
||||
|
||||
&.ghost {
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&.link-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.wave-status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.dot {
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.dot.green {
|
||||
background-color: @status-connected;
|
||||
}
|
||||
|
||||
.dot.red {
|
||||
background-color: @status-error;
|
||||
}
|
||||
|
||||
.dot.gray {
|
||||
background-color: @status-disconnected;
|
||||
}
|
||||
|
||||
.dot.yellow {
|
||||
background-color: @status-connecting;
|
||||
}
|
||||
}
|
||||
|
||||
.wave-modal-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 500;
|
||||
|
||||
.wave-modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(21, 23, 21, 0.7);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.wave-modal {
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
border-radius: 10px;
|
||||
background: #151715;
|
||||
box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.35), 0px 10px 24px 0px rgba(0, 0, 0, 0.45),
|
||||
0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
|
||||
.wave-modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.wave-modal-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 14px 12px 20px;
|
||||
justify-content: space-between;
|
||||
line-height: 20px;
|
||||
border-bottom: 1px solid rgba(250, 250, 250, 0.1);
|
||||
|
||||
button {
|
||||
i {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wave-modal-body {
|
||||
width: 100%;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
|
||||
.wave-modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
padding: 0 20px 20px;
|
||||
|
||||
button:first-child {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ class Toggle extends React.Component<{ checked: boolean; onChange: (value: boole
|
||||
}
|
||||
|
||||
class Checkbox extends React.Component<
|
||||
{ checked: boolean; onChange: (value: boolean) => void; label: string; id: string },
|
||||
{ checked: boolean; onChange: (value: boolean) => void; label: React.ReactNode; id: string },
|
||||
{}
|
||||
> {
|
||||
render() {
|
||||
@ -217,6 +217,115 @@ class Tooltip extends React.Component<TooltipProps, TooltipState> {
|
||||
}
|
||||
}
|
||||
|
||||
type ButtonVariantType = "outlined" | "solid" | "ghost";
|
||||
type ButtonThemeType = "primary" | "secondary";
|
||||
|
||||
interface ButtonProps {
|
||||
theme?: ButtonThemeType;
|
||||
children: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
disabled?: boolean;
|
||||
variant?: ButtonVariantType;
|
||||
leftIcon?: React.ReactNode;
|
||||
rightIcon?: React.ReactNode;
|
||||
color?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
class Button extends React.Component<ButtonProps> {
|
||||
static defaultProps = {
|
||||
theme: "primary",
|
||||
variant: "solid",
|
||||
color: "",
|
||||
style: {},
|
||||
};
|
||||
|
||||
@boundMethod
|
||||
handleClick() {
|
||||
if (this.props.onClick && !this.props.disabled) {
|
||||
this.props.onClick();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { leftIcon, rightIcon, theme, children, disabled, variant, color, style } = this.props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn("wave-button", theme, variant, color, { disabled: disabled })}
|
||||
onClick={this.handleClick}
|
||||
disabled={disabled}
|
||||
style={style}
|
||||
>
|
||||
{leftIcon && <span className="icon-left">{leftIcon}</span>}
|
||||
{children}
|
||||
{rightIcon && <span className="icon-right">{rightIcon}</span>}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class IconButton extends Button {
|
||||
render() {
|
||||
const { children, theme, variant = "solid", ...rest } = this.props;
|
||||
const className = `wave-button icon-button ${theme} ${variant}`;
|
||||
|
||||
return (
|
||||
<button {...rest} className={className}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default IconButton;
|
||||
|
||||
interface LinkButtonProps extends ButtonProps {
|
||||
href: string;
|
||||
target?: string;
|
||||
}
|
||||
|
||||
class LinkButton extends IconButton {
|
||||
render() {
|
||||
// @ts-ignore
|
||||
const { href, target, leftIcon, rightIcon, children, theme, variant }: LinkButtonProps = this.props;
|
||||
|
||||
return (
|
||||
<a href={href} target={target} className={`wave-button link-button`}>
|
||||
<button {...this.props} className={`icon-button ${theme} ${variant}`}>
|
||||
{leftIcon && <span className="icon-left">{leftIcon}</span>}
|
||||
{children}
|
||||
{rightIcon && <span className="icon-right">{rightIcon}</span>}
|
||||
</button>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
interface StatusProps {
|
||||
status: "green" | "red" | "gray" | "yellow";
|
||||
text: string;
|
||||
}
|
||||
|
||||
class Status extends React.Component<StatusProps> {
|
||||
@boundMethod
|
||||
renderDot() {
|
||||
const { status } = this.props;
|
||||
|
||||
return <div className={`dot ${status}`} />;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { text } = this.props;
|
||||
|
||||
return (
|
||||
<div className="wave-status-container">
|
||||
{this.renderDot()}
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface TextFieldDecorationProps {
|
||||
startDecoration?: React.ReactNode;
|
||||
endDecoration?: React.ReactNode;
|
||||
@ -232,6 +341,7 @@ interface TextFieldProps {
|
||||
required?: boolean;
|
||||
maxLength?: number;
|
||||
autoFocus?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface TextFieldState {
|
||||
@ -242,7 +352,6 @@ interface TextFieldState {
|
||||
hasContent: boolean;
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class TextField extends React.Component<TextFieldProps, TextFieldState> {
|
||||
inputRef: React.RefObject<HTMLInputElement>;
|
||||
state: TextFieldState;
|
||||
@ -267,6 +376,22 @@ class TextField extends React.Component<TextFieldProps, TextFieldState> {
|
||||
}
|
||||
}
|
||||
|
||||
// Method to handle focus at the component level
|
||||
@boundMethod
|
||||
handleComponentFocus() {
|
||||
if (this.inputRef.current && !this.inputRef.current.contains(document.activeElement)) {
|
||||
this.inputRef.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Method to handle blur at the component level
|
||||
@boundMethod
|
||||
handleComponentBlur() {
|
||||
if (this.inputRef.current && this.inputRef.current.contains(document.activeElement)) {
|
||||
this.inputRef.current.blur();
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleFocus() {
|
||||
this.setState({ focused: true });
|
||||
@ -311,14 +436,23 @@ class TextField extends React.Component<TextFieldProps, TextFieldState> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { label, value, placeholder, decoration, className, maxLength, autoFocus } = this.props;
|
||||
const { label, value, placeholder, decoration, className, maxLength, autoFocus, disabled } = 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(`wave-textfield ${className || ""}`, { focused: focused, error: error })}>
|
||||
<div
|
||||
className={cn(`wave-textfield ${className || ""}`, {
|
||||
focused: focused,
|
||||
error: error,
|
||||
disabled: disabled,
|
||||
})}
|
||||
onFocus={this.handleComponentFocus}
|
||||
onBlur={this.handleComponentBlur}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{decoration?.startDecoration && <>{decoration.startDecoration}</>}
|
||||
<div className="wave-textfield-inner">
|
||||
<label
|
||||
@ -341,6 +475,7 @@ class TextField extends React.Component<TextFieldProps, TextFieldState> {
|
||||
placeholder={placeholder}
|
||||
maxLength={maxLength}
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
{decoration?.endDecoration && <>{decoration.endDecoration}</>}
|
||||
@ -617,7 +752,7 @@ class InlineSettingsTextEdit extends React.Component<
|
||||
<div
|
||||
onClick={this.confirmChange}
|
||||
title="Confirm (Enter)"
|
||||
className="button is-prompt-green is-outlined is-small"
|
||||
className="button is-wave-green is-outlined is-small"
|
||||
>
|
||||
<span className="icon is-small">
|
||||
<i className="fa-sharp fa-solid fa-check" />
|
||||
@ -964,6 +1099,70 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
}
|
||||
}
|
||||
|
||||
interface ModalHeaderProps {
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const ModalHeader: React.FC<ModalHeaderProps> = ({ onClose, title }) => (
|
||||
<div className="wave-modal-header">
|
||||
{<div>{title}</div>}
|
||||
<IconButton theme="secondary" variant="ghost" onClick={onClose}>
|
||||
<i className="fa-sharp fa-solid fa-xmark"></i>
|
||||
</IconButton>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface ModalFooterProps {
|
||||
onCancel?: () => void;
|
||||
onOk?: () => void;
|
||||
cancelLabel?: string;
|
||||
okLabel?: string;
|
||||
}
|
||||
|
||||
const ModalFooter: React.FC<ModalFooterProps> = ({ onCancel, onOk, cancelLabel = "Cancel", okLabel = "Ok" }) => (
|
||||
<div className="wave-modal-footer">
|
||||
{onCancel && (
|
||||
<Button theme="secondary" onClick={onCancel}>
|
||||
{cancelLabel}
|
||||
</Button>
|
||||
)}
|
||||
{onOk && <Button onClick={onOk}>{okLabel}</Button>}
|
||||
</div>
|
||||
);
|
||||
|
||||
interface ModalProps {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
onClickBackdrop?: () => void;
|
||||
}
|
||||
|
||||
class Modal extends React.Component<ModalProps> {
|
||||
static Header = ModalHeader;
|
||||
static Footer = ModalFooter;
|
||||
|
||||
renderBackdrop(onClick: (() => void) | undefined) {
|
||||
return <div className="wave-modal-backdrop" onClick={onClick}></div>;
|
||||
}
|
||||
|
||||
renderModal() {
|
||||
const { className, children } = this.props;
|
||||
|
||||
return (
|
||||
<div className="wave-modal-container">
|
||||
{this.renderBackdrop(this.props.onClickBackdrop)}
|
||||
<div className={`wave-modal ${className}`}>
|
||||
<div className="wave-modal-content">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return ReactDOM.createPortal(this.renderModal(), document.getElementById("app") as HTMLElement);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
CmdStrCode,
|
||||
Toggle,
|
||||
@ -980,4 +1179,9 @@ export {
|
||||
NumberField,
|
||||
PasswordField,
|
||||
Tooltip,
|
||||
Button,
|
||||
IconButton,
|
||||
LinkButton,
|
||||
Status,
|
||||
Modal,
|
||||
};
|
||||
|
@ -59,22 +59,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.modal.alert-modal {
|
||||
z-index: 205;
|
||||
|
||||
footer {
|
||||
justify-content: center;
|
||||
|
||||
.button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.button:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal.settings-modal {
|
||||
footer {
|
||||
justify-content: center;
|
||||
@ -181,59 +165,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.modal.wave-modal {
|
||||
.wave-modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
border-radius: 10px;
|
||||
background: var(--olive-dark-1, #151715);
|
||||
box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.35), 0px 10px 24px 0px rgba(0, 0, 0, 0.45),
|
||||
0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
|
||||
.wave-modal-content-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
|
||||
.wave-modal-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 20px;
|
||||
justify-content: space-between;
|
||||
line-height: 20px;
|
||||
border-bottom: 1px solid rgba(250, 250, 250, 0.1);
|
||||
|
||||
.wave-modal-title {
|
||||
color: @term-bright-white;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.wave-modal-close {
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.wave-modal-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 24px;
|
||||
width: 87%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal.tos-modal {
|
||||
.modal-content.wave-modal-content {
|
||||
padding: 32px 48px;
|
||||
@ -323,14 +254,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
.modal.about-modal {
|
||||
.about-wave-modal-content {
|
||||
width: 401px;
|
||||
.about-modal {
|
||||
width: 382px;
|
||||
|
||||
.about-wave-modal-body {
|
||||
.wave-modal-content {
|
||||
gap: 24px;
|
||||
|
||||
.wave-modal-body {
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 24px;
|
||||
|
||||
.about-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
|
||||
.wave-modal-section {
|
||||
.logo-wrapper {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
@ -403,7 +346,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.wave-modal-section:nth-child(3) {
|
||||
.about-section:nth-child(3) {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
@ -418,7 +361,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.wave-modal-section:last-child {
|
||||
.about-section:last-child {
|
||||
margin-bottom: 24px;
|
||||
color: @term-white;
|
||||
}
|
||||
@ -426,71 +369,220 @@
|
||||
}
|
||||
}
|
||||
|
||||
.wave-modal.crconn-modal {
|
||||
.wave-modal-content.crconn-wave-modal-content {
|
||||
width: 452px;
|
||||
min-height: 411px;
|
||||
overflow: visible;
|
||||
.crconn-modal {
|
||||
width: 452px;
|
||||
min-height: 411px;
|
||||
|
||||
.wave-modal-content-inner.crconn-wave-modal-content-inner {
|
||||
.wave-modal-content {
|
||||
gap: 24px;
|
||||
|
||||
.wave-modal-body {
|
||||
display: flex;
|
||||
padding-bottom: 0px;
|
||||
padding: 0px 20px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
flex-shrink: 0;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.crconn-wave-modal-body {
|
||||
.screen-settings-modal {
|
||||
width: 640px;
|
||||
min-height: 329px;
|
||||
|
||||
.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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.erconn-modal {
|
||||
width: 502px;
|
||||
min-height: 411px;
|
||||
|
||||
.wave-modal-content {
|
||||
gap: 20px;
|
||||
|
||||
.wave-modal-body {
|
||||
display: flex;
|
||||
padding: 0px 20px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.name-actions-section {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
padding: 0px 20px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.crconn-wave-modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
padding: 0 20px 20px;
|
||||
.name {
|
||||
color: @term-bright-white;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
.header-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-start;
|
||||
|
||||
div.button {
|
||||
margin-right: 8px;
|
||||
.wave-button {
|
||||
padding: 4px 15px;
|
||||
font-size: 11px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wave-button {
|
||||
display: flex;
|
||||
padding: 6px 16px;
|
||||
align-items: center;
|
||||
gap: var(--sizing-2-xs, 4px);
|
||||
border-radius: 6px;
|
||||
height: auto;
|
||||
.alert-modal {
|
||||
.wave-modal-content {
|
||||
.wave-modal-body {
|
||||
padding: 40px 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wave-button.color-green {
|
||||
color: @term-bright-white;
|
||||
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),
|
||||
0px 0px 0.5px 0px rgba(255, 255, 255, 0.8) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.6) inset;
|
||||
.rconndetail-modal {
|
||||
width: 631px;
|
||||
min-height: 565px;
|
||||
|
||||
&:hover {
|
||||
color: @term-bright-white;
|
||||
.wave-modal-content {
|
||||
display: flex;
|
||||
padding-bottom: 0px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.wave-modal-body {
|
||||
display: flex;
|
||||
padding: 0px 20px;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
align-self: stretch;
|
||||
|
||||
.name-header-actions-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
|
||||
.rconndetail-name {
|
||||
color: @term-bright-white;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-start;
|
||||
|
||||
.wave-button {
|
||||
padding: 4px 15px;
|
||||
font-size: 11px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remote-detail {
|
||||
.settings-field {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.settings-label {
|
||||
font-weight: bold;
|
||||
width: 12em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.settings-input {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-field:not(:first-child) {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
height: 30px;
|
||||
padding: 3px 8px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
align-self: stretch;
|
||||
border-radius: 6px;
|
||||
background: rgba(241, 246, 243, 0.08);
|
||||
}
|
||||
|
||||
.terminal-wrapper {
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
|
||||
.terminal-connectelem {
|
||||
height: 163px !important; // Needed to override plugin height
|
||||
|
||||
.xterm-viewport {
|
||||
display: flex;
|
||||
padding: 6px 10px;
|
||||
gap: 8px;
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--element-separator, rgba(241, 246, 243, 0.15));
|
||||
background: #080a08;
|
||||
height: 163px !important; // Needed to override plugin height
|
||||
}
|
||||
|
||||
.xterm-screen {
|
||||
padding: 10px;
|
||||
width: 541px !important; // Needed to override plugin width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wave-button.color-standard {
|
||||
color: @term-white;
|
||||
background: var(--overlays-white-6, rgba(255, 255, 255, 0.12));
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
|
||||
&:hover {
|
||||
color: @term-white;
|
||||
@ -659,31 +751,32 @@
|
||||
fill: @tab-pink;
|
||||
}
|
||||
|
||||
.tab-colors {
|
||||
.tab-colors,
|
||||
.tab-icons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.tab-color-sep {
|
||||
.tab-color-sep,
|
||||
.tab-icon-sep {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tab-color-cur {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.tab-color-icon {
|
||||
.tab-color-icon,
|
||||
.tab-icon-icon {
|
||||
width: 1.1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tab-color-name {
|
||||
.tab-color-name,
|
||||
.tab-icon-name {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.tab-color-select {
|
||||
.tab-color-select,
|
||||
.tab-icon-select {
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
&:hover {
|
||||
|
File diff suppressed because it is too large
Load Diff
22
src/app/common/modals/modalsRegistry.tsx
Normal file
22
src/app/common/modals/modalsRegistry.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import {
|
||||
AboutModal,
|
||||
CreateRemoteConnModal,
|
||||
ViewRemoteConnDetailModal,
|
||||
EditRemoteConnModal,
|
||||
AlertModal,
|
||||
} from "./modals";
|
||||
import * as constants from "../../appconst";
|
||||
|
||||
const modalsRegistry: { [key: string]: () => React.ReactElement } = {
|
||||
[constants.ABOUT]: () => <AboutModal />,
|
||||
[constants.CREATE_REMOTE]: () => <CreateRemoteConnModal />,
|
||||
[constants.VIEW_REMOTE]: () => <ViewRemoteConnDetailModal />,
|
||||
[constants.EDIT_REMOTE]: () => <EditRemoteConnModal />,
|
||||
[constants.ALERT]: () => <AlertModal />,
|
||||
};
|
||||
|
||||
export { modalsRegistry };
|
@ -7,10 +7,18 @@ 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 } from "../../../model/model";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage } from "../common";
|
||||
import {
|
||||
GlobalModel,
|
||||
GlobalCommandRunner,
|
||||
TabColors,
|
||||
MinFontSize,
|
||||
MaxFontSize,
|
||||
TabIcons,
|
||||
Screen,
|
||||
} from "../../../model/model";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage, Modal } from "../common";
|
||||
import { LineType, RendererPluginType, ClientDataType, CommandRtnType } from "../../../types/types";
|
||||
import { ConnectionDropdown } from "../../connections/connections";
|
||||
import { ConnectionDropdown } from "../../connections_deprecated/connections";
|
||||
import { PluginModel } from "../../../plugins/plugins";
|
||||
import * as util from "../../../util/util";
|
||||
import { commandRtnHandler } from "../../../util/util";
|
||||
@ -50,15 +58,16 @@ Are you sure you want to stop web-sharing this tab?
|
||||
`.trim();
|
||||
|
||||
@mobxReact.observer
|
||||
class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId: string; }, {}> {
|
||||
class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId: string }, {}> {
|
||||
shareCopied: OV<boolean> = mobx.observable.box(false, { name: "ScreenSettings-shareCopied" });
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
||||
screen: Screen;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
let { sessionId, screenId } = props;
|
||||
let screen = GlobalModel.getScreenById(sessionId, screenId);
|
||||
if (screen == null) {
|
||||
this.screen = GlobalModel.getScreenById(sessionId, screenId);
|
||||
if (this.screen == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -84,6 +93,15 @@ class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId:
|
||||
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 {
|
||||
let { sessionId, screenId } = this.props;
|
||||
@ -203,105 +221,123 @@ class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId:
|
||||
render() {
|
||||
let { sessionId, screenId } = this.props;
|
||||
let inline = false;
|
||||
let screen = GlobalModel.getScreenById(sessionId, screenId);
|
||||
let screen = this.screen;
|
||||
if (screen == null) {
|
||||
return null;
|
||||
}
|
||||
console.log("screen.getTabIcon()", screen.getTabIcon());
|
||||
let color: string = null;
|
||||
let icon: string = null;
|
||||
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
return (
|
||||
<div className={cn("modal screen-settings-modal settings-modal prompt-modal is-active")}>
|
||||
<div className="modal-background"/>
|
||||
<div className="modal-content">
|
||||
{this.shareCopied.get() && <div className="copied-indicator" />}
|
||||
<header>
|
||||
<div className="modal-title">tab settings ({screen.name.get()})</div>
|
||||
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
||||
<XmarkIcon />
|
||||
</div>
|
||||
</header>
|
||||
<div className="inner-content">
|
||||
<div className="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">
|
||||
<ConnectionDropdown curRemote={curRemote} onSelectRemote={this.selectRemote} allowNewConn={false}/>
|
||||
</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">
|
||||
<div>Archived</div>
|
||||
<InfoMessage width={400}>
|
||||
Archive will hide the tab. Commands and output will be retained in
|
||||
history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<Toggle checked={screen.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 tab, removing all commands and output from history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<div
|
||||
onClick={this.handleDeleteScreen}
|
||||
className="button is-prompt-danger is-outlined is-small"
|
||||
>
|
||||
Delete Tab
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
<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>
|
||||
<footer>
|
||||
<div onClick={this.closeModal} className="button is-prompt-green is-outlined is-small">
|
||||
Close
|
||||
<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>
|
||||
</footer>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Connection</div>
|
||||
<div className="settings-input">
|
||||
<ConnectionDropdown
|
||||
curRemote={curRemote}
|
||||
onSelectRemote={this.selectRemote}
|
||||
allowNewConn={false}
|
||||
/>
|
||||
</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" of={TabIcons}>
|
||||
<div
|
||||
key={color}
|
||||
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">
|
||||
<div>Archived</div>
|
||||
<InfoMessage width={400}>
|
||||
Archive will hide the tab. Commands and output will be retained in history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<Toggle checked={screen.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 tab, removing all commands and output from history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<div
|
||||
onClick={this.handleDeleteScreen}
|
||||
className="button is-prompt-danger is-outlined is-small"
|
||||
>
|
||||
Delete Tab
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
</div>
|
||||
<Modal.Footer cancelLabel="Close" onCancel={this.closeModal} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -436,7 +472,7 @@ class SessionSettingsModal extends React.Component<{ sessionId: string }, {}> {
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
<footer>
|
||||
<div onClick={this.closeModal} className="button is-prompt-green is-outlined is-small">
|
||||
<div onClick={this.closeModal} className="button is-wave-green is-outlined is-small">
|
||||
Close
|
||||
</div>
|
||||
</footer>
|
||||
@ -572,7 +608,7 @@ class LineSettingsModal extends React.Component<{ linenum: number }, {}> {
|
||||
<div style={{ height: 50 }} />
|
||||
</div>
|
||||
<footer>
|
||||
<div onClick={this.closeModal} className="button is-prompt-green is-outlined is-small">
|
||||
<div onClick={this.closeModal} className="button is-wave-green is-outlined is-small">
|
||||
Close
|
||||
</div>
|
||||
</footer>
|
||||
@ -630,7 +666,10 @@ class ClientSettingsModal extends React.Component<{}, {}> {
|
||||
}
|
||||
|
||||
renderFontSizeDropdown(): any {
|
||||
let availableFontSizes = [8, 9, 10, 11, 12, 13, 14, 15];
|
||||
let availableFontSizes = [];
|
||||
for (let s = MinFontSize; s <= MaxFontSize; s++) {
|
||||
availableFontSizes.push(s);
|
||||
}
|
||||
let fsize: number = 0;
|
||||
let curSize = GlobalModel.termFontSize.get();
|
||||
return (
|
||||
@ -767,7 +806,7 @@ class ClientSettingsModal extends React.Component<{}, {}> {
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
<footer>
|
||||
<div onClick={this.closeModal} className="button is-prompt-green is-outlined is-small">
|
||||
<div onClick={this.closeModal} className="button is-wave-green is-outlined is-small">
|
||||
Close
|
||||
</div>
|
||||
</footer>
|
||||
|
@ -7,7 +7,7 @@
|
||||
vertical-align: middle;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
fill: @prompt-green;
|
||||
fill: @wave-green;
|
||||
}
|
||||
|
||||
.term-prompt-branch {
|
||||
|
@ -6,7 +6,7 @@
|
||||
@background-session: rgba(13, 13, 13, 0.85);
|
||||
@background-session-components: rgba(48, 49, 48, 0.6);
|
||||
@background-session-components-solid: rgb(33, 34, 33);
|
||||
@prompt-green: rgb(88, 193, 66);
|
||||
@wave-green: rgb(88, 193, 66);
|
||||
@disabled-background: rgba(76, 81, 75, 1);
|
||||
@disabled-color: #adadad;
|
||||
@scrollbar-background: rgba(21, 23, 21, 1);
|
||||
|
@ -1,408 +1,107 @@
|
||||
@import "../../app/common/themes/themes.less";
|
||||
|
||||
.modal.prompt-modal.remotes-modal {
|
||||
.modal-content {
|
||||
min-width: 850px;
|
||||
}
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: @base-color;
|
||||
margin: 0;
|
||||
}
|
||||
.button {
|
||||
svg {
|
||||
float: right;
|
||||
margin-top: 0.3em;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.dropdown,
|
||||
.button {
|
||||
display: inline-flex;
|
||||
}
|
||||
.dropdown .button {
|
||||
border: none !important;
|
||||
}
|
||||
.inner-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
padding: 0;
|
||||
min-height: 45em;
|
||||
max-height: 45em;
|
||||
|
||||
.remotes-menu {
|
||||
flex: 0 0 200px;
|
||||
border-right: 1px solid @disabled-color;
|
||||
overflow-y: auto;
|
||||
|
||||
.remote-menu-item {
|
||||
border-top: 1px solid @disabled-color;
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
cursor: pointer;
|
||||
|
||||
&.add-remote {
|
||||
padding: 10px 5px 10px 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background-color: @active-menu-color;
|
||||
|
||||
.remote-name .remote-name-secondary {
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.remote-status-light {
|
||||
width: 2em;
|
||||
margin-top: 0.7em;
|
||||
margin-right: 0.7em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.remote-name {
|
||||
flex-grow: 1;
|
||||
|
||||
.remote-name-primary {
|
||||
font-weight: bold;
|
||||
max-width: 10em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.remote-name-secondary {
|
||||
color: @disabled-color;
|
||||
max-width: 14em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remote-detail {
|
||||
padding: 10px;
|
||||
flex-grow: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.settings-field {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
|
||||
* {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-subtitle {
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: @term-white;
|
||||
padding: 0.75em 0;
|
||||
margin-bottom: 0;
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
|
||||
.terminal-wrapper {
|
||||
margin-left: 0;
|
||||
margin-bottom: 0;
|
||||
&.has-message {
|
||||
margin-top: 0;
|
||||
}
|
||||
box-shadow: none;
|
||||
border: 1px solid #777;
|
||||
border-radius: 0 0 5px 5px;
|
||||
.xterm-rows {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.remote-message {
|
||||
margin-top: 5px;
|
||||
padding: 8px;
|
||||
border-radius: 5px 5px 0 0;
|
||||
background-color: #333;
|
||||
border: 1px solid #777;
|
||||
border-bottom: none;
|
||||
|
||||
.message-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
svg {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.remote-status {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-field {
|
||||
.update-auth-button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.update-auth-button {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.hide-hover {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.auth-editing,
|
||||
&.create-remote {
|
||||
.settings-field.align-top {
|
||||
align-items: flex-start;
|
||||
|
||||
.settings-label {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.settings-input {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 12em !important;
|
||||
}
|
||||
|
||||
.settings-field .settings-input .undo-icon {
|
||||
cursor: pointer;
|
||||
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.editremote-dropdown .dropdown-trigger button {
|
||||
width: 120px;
|
||||
justify-content: flex-start;
|
||||
color: @base-color;
|
||||
border: none;
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-field .raw-input {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.settings-input input {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
width: 250px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.dropdown .dropdown-item {
|
||||
padding: 5px 5px 5px 12px;
|
||||
}
|
||||
|
||||
.dropdown .dropdown-content {
|
||||
max-width: 10.6em;
|
||||
}
|
||||
|
||||
.settings-input {
|
||||
.info-message {
|
||||
margin-left: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
.info-message {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.terminal-wrapper {
|
||||
position: relative;
|
||||
padding: 2px 10px 5px 4px;
|
||||
margin: 5px 5px 10px 5px;
|
||||
box-shadow: 0 0 1px 1px rgba(255, 255, 255, 0.3);
|
||||
&.focus {
|
||||
box-shadow: 0 0 3px 3px rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.term-tag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: @term-red;
|
||||
color: @term-white;
|
||||
z-index: 110;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown.conn-dropdown {
|
||||
padding-left: 0;
|
||||
.connections-view {
|
||||
background-color: @background-session;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 10px;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(241, 246, 243, 0.08);
|
||||
border: 1px solid rgba(241, 246, 243, 0.08);
|
||||
background: var(--element-window, rgba(13, 13, 13, 0.85));
|
||||
|
||||
.conn-dd-trigger {
|
||||
.header {
|
||||
margin: 24px 18px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 413px;
|
||||
padding: 6px 8px 6px 12px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 42px;
|
||||
|
||||
.lefticon {
|
||||
margin-right: 8px;
|
||||
margin-top: 4px;
|
||||
position: relative;
|
||||
|
||||
.status-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
stroke-width: 2px;
|
||||
stroke: @status-outline;
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
right: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.dd-control {
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.globe-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.conntext {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
flex: 1 0 0;
|
||||
|
||||
.conntext-solo {
|
||||
color: @text-primary;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.conntext-1 {
|
||||
color: @text-primary;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.conntext-2 {
|
||||
color: @text-secondary;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.connections-title {
|
||||
}
|
||||
}
|
||||
|
||||
.conn-dd-menu {
|
||||
.no-items {
|
||||
display: flex;
|
||||
width: 413px;
|
||||
padding: 6px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
border-radius: 8px;
|
||||
background-color: @dropdown-menu;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
padding: 30px 0 30px 0;
|
||||
border: 1px solid white;
|
||||
border-radius: 3px;
|
||||
margin: 20px 50px 20px 20px;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
padding: 5px 12px 5px 8px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
align-self: stretch;
|
||||
border-radius: 6px;
|
||||
.connections-table {
|
||||
margin: 0px 10px 10px 10px;
|
||||
table-layout: fixed;
|
||||
max-width: 970px;
|
||||
|
||||
.status-div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 3px;
|
||||
|
||||
svg.status-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
colgroup {
|
||||
.first-col {
|
||||
max-width: 650px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
.second-col {
|
||||
max-width: 150px;
|
||||
}
|
||||
.third-col {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.text-standard {
|
||||
thead {
|
||||
border-radius: var(--sizing-2-xs, 4px);
|
||||
border-top: 1px solid rgba(250, 250, 250, 0.1);
|
||||
border-bottom: 1px solid rgba(241, 246, 243, 0.15);
|
||||
background: var(--opacity-zinc-502, rgba(250, 250, 250, 0.02));
|
||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
||||
0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
|
||||
th {
|
||||
height: 32px;
|
||||
padding: 5px 15px 5px 10px;
|
||||
color: @text-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.text-caption {
|
||||
color: @text-caption;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
tr.connections-item {
|
||||
border-bottom: 1px solid rgba(241, 246, 243, 0.15);
|
||||
color: @text-secondary;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(241, 246, 243, 0.08);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
|
||||
td.bookmark i {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
height: 40px;
|
||||
padding: 5px 15px 5px 10px;
|
||||
vertical-align: middle;
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&.hovered {
|
||||
.action-buttons {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.help-entry {
|
||||
margin: 1em 2em;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
408
src/app/connections_deprecated/connections.less
Normal file
408
src/app/connections_deprecated/connections.less
Normal file
@ -0,0 +1,408 @@
|
||||
@import "../../app/common/themes/themes.less";
|
||||
|
||||
.modal.prompt-modal.remotes-modal {
|
||||
.modal-content {
|
||||
min-width: 850px;
|
||||
}
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: @base-color;
|
||||
margin: 0;
|
||||
}
|
||||
.button {
|
||||
svg {
|
||||
float: right;
|
||||
margin-top: 0.3em;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.dropdown,
|
||||
.button {
|
||||
display: inline-flex;
|
||||
}
|
||||
.dropdown .button {
|
||||
border: none !important;
|
||||
}
|
||||
.inner-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
padding: 0;
|
||||
min-height: 45em;
|
||||
max-height: 45em;
|
||||
|
||||
.remotes-menu {
|
||||
flex: 0 0 200px;
|
||||
border-right: 1px solid @disabled-color;
|
||||
overflow-y: auto;
|
||||
|
||||
.remote-menu-item {
|
||||
border-top: 1px solid @disabled-color;
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
cursor: pointer;
|
||||
|
||||
&.add-remote {
|
||||
padding: 10px 5px 10px 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background-color: @active-menu-color;
|
||||
|
||||
.remote-name .remote-name-secondary {
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.remote-status-light {
|
||||
width: 2em;
|
||||
margin-top: 0.7em;
|
||||
margin-right: 0.7em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.remote-name {
|
||||
flex-grow: 1;
|
||||
|
||||
.remote-name-primary {
|
||||
font-weight: bold;
|
||||
max-width: 10em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.remote-name-secondary {
|
||||
color: @disabled-color;
|
||||
max-width: 14em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remote-detail {
|
||||
padding: 10px;
|
||||
flex-grow: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.settings-field {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
|
||||
* {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-subtitle {
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: @term-white;
|
||||
padding: 0.75em 0;
|
||||
margin-bottom: 0;
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
|
||||
.terminal-wrapper {
|
||||
margin-left: 0;
|
||||
margin-bottom: 0;
|
||||
&.has-message {
|
||||
margin-top: 0;
|
||||
}
|
||||
box-shadow: none;
|
||||
border: 1px solid #777;
|
||||
border-radius: 0 0 5px 5px;
|
||||
.xterm-rows {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.remote-message {
|
||||
margin-top: 5px;
|
||||
padding: 8px;
|
||||
border-radius: 5px 5px 0 0;
|
||||
background-color: #333;
|
||||
border: 1px solid #777;
|
||||
border-bottom: none;
|
||||
|
||||
.message-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
svg {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.remote-status {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-field {
|
||||
.update-auth-button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.update-auth-button {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.hide-hover {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.auth-editing,
|
||||
&.create-remote {
|
||||
.settings-field.align-top {
|
||||
align-items: flex-start;
|
||||
|
||||
.settings-label {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.settings-input {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 12em !important;
|
||||
}
|
||||
|
||||
.settings-field .settings-input .undo-icon {
|
||||
cursor: pointer;
|
||||
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.editremote-dropdown .dropdown-trigger button {
|
||||
width: 120px;
|
||||
justify-content: flex-start;
|
||||
color: @base-color;
|
||||
border: none;
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-field .raw-input {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.settings-input input {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
width: 250px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.dropdown .dropdown-item {
|
||||
padding: 5px 5px 5px 12px;
|
||||
}
|
||||
|
||||
.dropdown .dropdown-content {
|
||||
max-width: 10.6em;
|
||||
}
|
||||
|
||||
.settings-input {
|
||||
.info-message {
|
||||
margin-left: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
.info-message {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.terminal-wrapper {
|
||||
position: relative;
|
||||
padding: 2px 10px 5px 4px;
|
||||
margin: 5px 5px 10px 5px;
|
||||
box-shadow: 0 0 1px 1px rgba(255, 255, 255, 0.3);
|
||||
&.focus {
|
||||
box-shadow: 0 0 3px 3px rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.term-tag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: @term-red;
|
||||
color: @term-white;
|
||||
z-index: 110;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown.conn-dropdown {
|
||||
padding-left: 0;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(241, 246, 243, 0.08);
|
||||
|
||||
.conn-dd-trigger {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 413px;
|
||||
padding: 6px 8px 6px 12px;
|
||||
align-items: center;
|
||||
height: 42px;
|
||||
|
||||
.lefticon {
|
||||
margin-right: 8px;
|
||||
margin-top: 4px;
|
||||
position: relative;
|
||||
|
||||
.status-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
stroke-width: 2px;
|
||||
stroke: @status-outline;
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
right: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.dd-control {
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.globe-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.conntext {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
flex: 1 0 0;
|
||||
|
||||
.conntext-solo {
|
||||
color: @text-primary;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.conntext-1 {
|
||||
color: @text-primary;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.conntext-2 {
|
||||
color: @text-secondary;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.conn-dd-menu {
|
||||
display: flex;
|
||||
width: 413px;
|
||||
padding: 6px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
border-radius: 8px;
|
||||
background-color: @dropdown-menu;
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
padding: 5px 12px 5px 8px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
align-self: stretch;
|
||||
border-radius: 6px;
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1300
src/app/connections_deprecated/connections.tsx
Normal file
1300
src/app/connections_deprecated/connections.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@ -188,7 +188,7 @@
|
||||
}
|
||||
|
||||
&.active {
|
||||
border: 1px solid rgba(@prompt-green, 0.8) !important;
|
||||
border: 1px solid rgba(@wave-green, 0.8) !important;
|
||||
box-shadow: 0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
}
|
||||
|
||||
@ -234,7 +234,7 @@
|
||||
}
|
||||
|
||||
.success {
|
||||
fill: @prompt-green;
|
||||
fill: @wave-green;
|
||||
}
|
||||
|
||||
.fail {
|
||||
|
@ -68,7 +68,7 @@ class LinesView extends React.Component<
|
||||
});
|
||||
this.visibleMap = new Map();
|
||||
this.collapsedMap = new Map();
|
||||
this.computeVisibleMap_debounced = debounce(1000, this.computeVisibleMap.bind(this));
|
||||
this.computeVisibleMap_debounced = debounce(100, this.computeVisibleMap.bind(this));
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
|
@ -45,7 +45,7 @@
|
||||
margin-bottom: 1em;
|
||||
border: 1px solid transparent;
|
||||
&.selected {
|
||||
border-color: @prompt-green;
|
||||
border-color: @wave-green;
|
||||
}
|
||||
.plugin-summary-header {
|
||||
display: flex;
|
||||
|
@ -110,6 +110,15 @@ class MainSideBar extends React.Component<{}, {}> {
|
||||
GlobalCommandRunner.bookmarksView();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleConnectionsClick(): void {
|
||||
if (GlobalModel.activeMainView.get() == "connections") {
|
||||
GlobalModel.showSessionView();
|
||||
return;
|
||||
}
|
||||
GlobalCommandRunner.connectionsView();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleWebSharingClick(): void {
|
||||
if (GlobalModel.activeMainView.get() == "webshare") {
|
||||
@ -126,11 +135,6 @@ class MainSideBar extends React.Component<{}, {}> {
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleConnectionsClick(): void {
|
||||
GlobalModel.remotesModalModel.openModal();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
openSessionSettings(e: any, session: Session): void {
|
||||
e.preventDefault();
|
||||
@ -199,14 +203,14 @@ class MainSideBar extends React.Component<{}, {}> {
|
||||
<div className="logo">
|
||||
<If condition={isCollapsed}>
|
||||
<div className="logo-container" onClick={this.toggleCollapsed}>
|
||||
<img src="public/logos/wave-logo.png"/>
|
||||
<img src="public/logos/wave-logo.png" />
|
||||
</div>
|
||||
</If>
|
||||
<If condition={!isCollapsed}>
|
||||
<div className="logo-container">
|
||||
<img src="public/logos/wave-dark.png"/>
|
||||
<img src="public/logos/wave-dark.png" />
|
||||
</div>
|
||||
<div className="spacer"/>
|
||||
<div className="spacer" />
|
||||
<div className="collapse-button" onClick={this.toggleCollapsed}>
|
||||
<LeftChevronIcon className="icon" />
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@
|
||||
border: 1px solid transparent;
|
||||
|
||||
&.active {
|
||||
border: 1px solid rgba(@prompt-green, 0.8) !important;
|
||||
border: 1px solid rgba(@wave-green, 0.8) !important;
|
||||
box-shadow: 0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
}
|
||||
|
||||
@ -73,6 +73,42 @@
|
||||
.cmd-input-context {
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cmd-input-filter {
|
||||
opacity: 0.5;
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin: 0 0.5em;
|
||||
vertical-align: text-top;
|
||||
fill: @base-color;
|
||||
}
|
||||
|
||||
.warning {
|
||||
fill: @warning-yellow;
|
||||
}
|
||||
|
||||
@keyframes infiniteRotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.spin {
|
||||
animation: infiniteRotate 2s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.cmd-input-field {
|
||||
@ -139,7 +175,7 @@
|
||||
height: 2.5em;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
fill: @prompt-green;
|
||||
fill: @wave-green;
|
||||
padding: 0.25em;
|
||||
}
|
||||
.icon.disabled {
|
||||
|
@ -5,18 +5,19 @@ import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { If, Choose, When, Otherwise } from "tsx-control-statements/components";
|
||||
import cn from "classnames";
|
||||
import dayjs from "dayjs";
|
||||
import type { RemoteType, RemoteInstanceType, RemotePtrType } from "../../../types/types";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { GlobalModel, GlobalCommandRunner } from "../../../model/model";
|
||||
import { renderCmdText } from "../../common/common";
|
||||
import { GlobalModel, GlobalCommandRunner, Screen, ScreenLines } from "../../../model/model";
|
||||
import { renderCmdText, Button } from "../../common/common";
|
||||
import { TextAreaInput } from "./textareainput";
|
||||
import { InfoMsg } from "./infomsg";
|
||||
import { HistoryInfo } from "./historyinfo";
|
||||
import { Prompt } from "../../common/prompt/prompt";
|
||||
import { ReactComponent as ExecIcon } from "../../assets/icons/exec.svg";
|
||||
import { ReactComponent as RotateIcon } from "../../assets/icons/line/rotate.svg";
|
||||
import "./cmdinput.less";
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
@ -90,6 +91,13 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
GlobalCommandRunner.connectRemote(remoteId);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
toggleFilter(screen: Screen) {
|
||||
mobx.action(() => {
|
||||
screen.filterRunning.set(!screen.filterRunning.get());
|
||||
})();
|
||||
}
|
||||
|
||||
render() {
|
||||
let model = GlobalModel;
|
||||
let inputModel = model.inputModel;
|
||||
@ -113,11 +121,12 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
let focusVal = inputModel.physicalInputFocused.get();
|
||||
let inputMode: string = inputModel.inputMode.get();
|
||||
let textAreaInputKey = screen == null ? "null" : screen.screenId;
|
||||
let win = GlobalModel.getScreenLinesById(screen.screenId) ?? GlobalModel.loadScreenLines(screen.screenId);
|
||||
let numRunningLines = win.getRunningCmdLines().length;
|
||||
return (
|
||||
<div
|
||||
ref={this.cmdInputRef}
|
||||
className={cn("cmd-input", { "has-info": infoShow }, { active: focusVal })}
|
||||
onClick={this.cmdInputClick}
|
||||
>
|
||||
<If condition={historyShow}>
|
||||
<div className="cmd-input-grow-spacer"></div>
|
||||
@ -131,7 +140,7 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
is {remote.status}
|
||||
<If condition={remote.status != "connecting"}>
|
||||
<div
|
||||
className="button is-prompt-green is-outlined is-small"
|
||||
className="button is-wave-green is-outlined is-small"
|
||||
onClick={() => this.clickConnectRemote(remote.remoteid)}
|
||||
>
|
||||
connect now
|
||||
@ -143,6 +152,14 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
<div className="has-text-white">
|
||||
<span ref={this.promptRef}><Prompt rptr={rptr} festate={feState} /></span>
|
||||
</div>
|
||||
<If condition={numRunningLines > 0}>
|
||||
<div onClick={() => this.toggleFilter(screen)}className="cmd-input-filter">
|
||||
{numRunningLines}
|
||||
<div className="avatar">
|
||||
<RotateIcon className="warning spin" />
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
<div
|
||||
key="input"
|
||||
@ -168,7 +185,7 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
)}
|
||||
{focusVal && (
|
||||
<div onMouseDown={this.clickHistoryHint} className="cmd-btn hoverEffect">
|
||||
{historyShow ? "close (esc)" : "history (crtl-r)"}
|
||||
{historyShow ? "close (esc)" : "history (ctrl-r)"}
|
||||
</div>
|
||||
)}
|
||||
<ExecIcon
|
||||
|
@ -433,6 +433,9 @@ class TextAreaInput extends React.Component<{ onHeightChange: () => void }, {}>
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cutSpot == -1) {
|
||||
cutSpot = 0;
|
||||
}
|
||||
let cutValue = value.slice(cutSpot, selStart);
|
||||
let prevValue = value.slice(0, cutSpot);
|
||||
let restValue = value.slice(selStart);
|
||||
|
@ -98,6 +98,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-running {
|
||||
margin: auto 1rem 0 1rem;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
.screen-settings-inline {
|
||||
|
@ -11,24 +11,19 @@ import cn from "classnames";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import dayjs from "dayjs";
|
||||
import { GlobalCommandRunner, TabColors, TabIcons } from "../../../model/model";
|
||||
import type { LineType, RenderModeType, LineFactoryProps, CommandRtnType } from "../../../types/types";
|
||||
import type { LineType, RenderModeType, LineFactoryProps } from "../../../types/types";
|
||||
import * as T from "../../../types/types";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { InlineSettingsTextEdit, RemoteStatusLight } from "../../common/common";
|
||||
import { Button } from "../../common/common";
|
||||
import { getRemoteStr } from "../../common/prompt/prompt";
|
||||
import { GlobalModel, ScreenLines, Screen, Session } from "../../../model/model";
|
||||
import { Line } from "../../line/linecomps";
|
||||
import { LinesView } from "../../line/linesview";
|
||||
import { ConnectionDropdown } from "../../connections/connections";
|
||||
import { ConnectionDropdown } from "../../connections_deprecated/connections";
|
||||
import * as util from "../../../util/util";
|
||||
import { TextField, InputDecoration } from "../../common/common";
|
||||
import { TextField } from "../../common/common";
|
||||
import { ReactComponent as EllipseIcon } from "../../assets/icons/ellipse.svg";
|
||||
import { ReactComponent as Check12Icon } from "../../assets/icons/check12.svg";
|
||||
import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg";
|
||||
import { ReactComponent as StatusCircleIcon } from "../../assets/icons/statuscircle.svg";
|
||||
import { ReactComponent as ArrowsUpDownIcon } from "../../assets/icons/arrowsupdown.svg";
|
||||
import { ReactComponent as CircleIcon } from "../../assets/icons/circle.svg";
|
||||
import { ReactComponent as AddIcon } from "../../assets/icons/add.svg";
|
||||
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
|
||||
|
||||
import "./screenview.less";
|
||||
@ -101,7 +96,7 @@ class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||
|
||||
@boundMethod
|
||||
clickNewConnection(): void {
|
||||
GlobalModel.remotesModalModel.openModalForEdit({ remoteedit: true, old: false }, true);
|
||||
GlobalModel.remotesModel.openAddModal({ remoteedit: true });
|
||||
}
|
||||
|
||||
renderTabIconSelector(): React.ReactNode {
|
||||
@ -117,7 +112,7 @@ class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||
<div className="text-s1 unselectable">Select the icon</div>
|
||||
<div className="control-iconlist tabicon-list">
|
||||
<div key="square" className="icondiv" title="square" onClick={() => this.selectTabIcon("square")}>
|
||||
<SquareIcon className="icon square-icon"/>
|
||||
<SquareIcon className="icon square-icon" />
|
||||
</div>
|
||||
<For each="icon" of={TabIcons}>
|
||||
<div
|
||||
@ -334,6 +329,22 @@ class ScreenWindowView extends React.Component<{ session: Session; screen: Scree
|
||||
return <Line key={realLine.lineid} screen={screen} line={realLine} {...restProps} />;
|
||||
}
|
||||
|
||||
determineVisibleLines(win: ScreenLines): LineType[] {
|
||||
let { screen } = this.props;
|
||||
if (screen.filterRunning.get()) {
|
||||
return win.getRunningCmdLines();
|
||||
}
|
||||
return win.getNonArchivedLines();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
disableFilter() {
|
||||
let { screen } = this.props;
|
||||
mobx.action(() => {
|
||||
screen.filterRunning.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
render() {
|
||||
let { session, screen } = this.props;
|
||||
let win = this.getScreenLines();
|
||||
@ -351,7 +362,7 @@ class ScreenWindowView extends React.Component<{ session: Session; screen: Scree
|
||||
return this.renderError("loading client data", true);
|
||||
}
|
||||
let isActive = screen.isActive();
|
||||
let lines = win.getNonArchivedLines();
|
||||
let lines = this.determineVisibleLines(win);
|
||||
let renderMode = this.renderMode.get();
|
||||
return (
|
||||
<div className="window-view" ref={this.windowViewRef}>
|
||||
@ -374,7 +385,7 @@ class ScreenWindowView extends React.Component<{ session: Session; screen: Scree
|
||||
<NewTabSettings screen={screen} />
|
||||
</If>
|
||||
<If condition={screen.nextLineNum.get() != 1}>
|
||||
<div className="window-view" ref={this.windowViewRef} data-screenid={screen.screenId}>
|
||||
<div className="window-empty" ref={this.windowViewRef} data-screenid={screen.screenId}>
|
||||
<div key="lines" className="lines"></div>
|
||||
<div key="window-empty" className={cn("window-empty")}>
|
||||
<div>
|
||||
@ -395,14 +406,14 @@ class ScreenWindowView extends React.Component<{ session: Session; screen: Scree
|
||||
<i title="archived" className="fa-sharp fa-solid fa-share-nodes" /> web shared
|
||||
</div>
|
||||
<div className="share-tag-link">
|
||||
<div className="button is-prompt-green is-outlined is-small" onClick={this.copyShareLink}>
|
||||
<div className="button is-wave-green is-outlined is-small" onClick={this.copyShareLink}>
|
||||
<span>copy link</span>
|
||||
<span className="icon">
|
||||
<i className="fa-sharp fa-solid fa-copy" />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="button is-prompt-green is-outlined is-small"
|
||||
className="button is-wave-green is-outlined is-small"
|
||||
onClick={this.openScreenSettings}
|
||||
>
|
||||
<span>open settings</span>
|
||||
@ -422,6 +433,19 @@ class ScreenWindowView extends React.Component<{ session: Session; screen: Scree
|
||||
lineFactory={this.buildLineComponent}
|
||||
/>
|
||||
</If>
|
||||
<If condition={screen.filterRunning.get()}>
|
||||
<div className="filter-running">
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="color-yellow"
|
||||
style={{ borderRadius: "999px" }}
|
||||
onClick={this.disableFilter}
|
||||
>
|
||||
Showing Running Commands
|
||||
<i className="fa-sharp fa-solid fa-xmark" />
|
||||
</Button>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import cn from "classnames";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import dayjs from "dayjs";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { GlobalModel, GlobalCommandRunner, Session, ScreenLines, Screen } from "../../../model/model";
|
||||
import { GlobalModel, GlobalCommandRunner, Session, Screen, TabIcons } from "../../../model/model";
|
||||
import { renderCmdText } from "../../common/common";
|
||||
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
|
||||
import { ReactComponent as ActionsIcon } from "../../assets/icons/tab/actions.svg";
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type React from "react";
|
||||
import * as mobx from "mobx";
|
||||
import { sprintf } from "sprintf-js";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
@ -65,7 +66,6 @@ import type {
|
||||
import * as T from "../types/types";
|
||||
import { WSControl } from "./ws";
|
||||
import {
|
||||
measureText,
|
||||
getMonoFontSize,
|
||||
windowWidthToCols,
|
||||
windowHeightToRows,
|
||||
@ -76,8 +76,9 @@ import dayjs from "dayjs";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||
import { getRendererContext, cmdStatusIsRunning } from "../app/line/lineutil";
|
||||
import { sortAndFilterRemotes } from "../util/util";
|
||||
import { MagicLayout } from "../app/magiclayout";
|
||||
import { modalsRegistry } from "../app/common/modals/modalsRegistry";
|
||||
import * as constants from "../app/appconst";
|
||||
|
||||
dayjs.extend(customParseFormat);
|
||||
dayjs.extend(localizedFormat);
|
||||
@ -91,7 +92,7 @@ const DevServerEndpoint = "http://127.0.0.1:8090";
|
||||
const DevServerWsEndpoint = "ws://127.0.0.1:8091";
|
||||
const DefaultTermFontSize = 12;
|
||||
const MinFontSize = 8;
|
||||
const MaxFontSize = 15;
|
||||
const MaxFontSize = 24;
|
||||
const InputChunkSize = 500;
|
||||
const RemoteColors = ["red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"];
|
||||
const TabColors = ["red", "orange", "yellow", "green", "mint", "cyan", "blue", "violet", "pink", "white"];
|
||||
@ -304,7 +305,6 @@ class Cmd {
|
||||
}
|
||||
|
||||
handleData(data: string, termWrap: TermWrap): void {
|
||||
// console.log("handle data", {data: data});
|
||||
if (!this.isRunning()) {
|
||||
return;
|
||||
}
|
||||
@ -357,6 +357,7 @@ class Screen {
|
||||
renderers: Record<string, RendererModel> = {}; // lineid => RendererModel
|
||||
shareMode: OV<string>;
|
||||
webShareOpts: OV<WebShareOpts>;
|
||||
filterRunning: OV<boolean>;
|
||||
|
||||
constructor(sdata: ScreenDataType) {
|
||||
this.sessionId = sdata.sessionid;
|
||||
@ -393,6 +394,9 @@ class Screen {
|
||||
this.webShareOpts = mobx.observable.box(sdata.webshareopts, {
|
||||
name: "screen-webShareOpts",
|
||||
});
|
||||
this.filterRunning = mobx.observable.box(false, {
|
||||
name: "screen-filter-running",
|
||||
})
|
||||
}
|
||||
|
||||
dispose() {}
|
||||
@ -756,6 +760,22 @@ class Screen {
|
||||
}
|
||||
|
||||
termCustomKeyHandler(e: any, termWrap: TermWrap): boolean {
|
||||
if (e.type == "keypress" && e.code == "KeyC" && e.shiftKey && e.ctrlKey) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
let sel = termWrap.terminal.getSelection();
|
||||
navigator.clipboard.writeText(sel);
|
||||
return false;
|
||||
}
|
||||
if (e.type == "keypress" && e.code == "KeyV" && e.shiftKey && e.ctrlKey) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
let p = navigator.clipboard.readText();
|
||||
p.then((text) => {
|
||||
termWrap.dataHandler?.(text, termWrap);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (termWrap.isRunning) {
|
||||
return true;
|
||||
}
|
||||
@ -2188,6 +2208,14 @@ class HistoryViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionsViewModel {
|
||||
showConnectionsView(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.activeMainView.set("connections");
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
class BookmarksModel {
|
||||
bookmarks: OArr<BookmarkType> = mobx.observable.array([], {
|
||||
name: "Bookmarks",
|
||||
@ -2685,6 +2713,216 @@ class RemotesModalModel {
|
||||
}
|
||||
}
|
||||
|
||||
class RemotesModel {
|
||||
selectedRemoteId: OV<string> = mobx.observable.box(null, {
|
||||
name: "RemotesModel-selectedRemoteId",
|
||||
});
|
||||
remoteTermWrap: TermWrap = null;
|
||||
remoteTermWrapFocus: OV<boolean> = mobx.observable.box(false, {
|
||||
name: "RemotesModel-remoteTermWrapFocus",
|
||||
});
|
||||
showNoInputMsg: OV<boolean> = mobx.observable.box(false, {
|
||||
name: "RemotesModel-showNoInputMg",
|
||||
});
|
||||
showNoInputTimeoutId: any = null;
|
||||
remoteEdit: OV<RemoteEditType> = mobx.observable.box(null, {
|
||||
name: "RemotesModel-remoteEdit",
|
||||
});
|
||||
recentConnAddedState: OV<boolean> = mobx.observable.box(false, {
|
||||
name: "RemotesModel-recentlyAdded",
|
||||
});
|
||||
|
||||
get recentConnAdded(): boolean {
|
||||
return this.recentConnAddedState.get();
|
||||
}
|
||||
|
||||
seRecentConnAdded(value: boolean) {
|
||||
this.recentConnAddedState.set(value);
|
||||
}
|
||||
|
||||
deSelectRemote(): void {
|
||||
mobx.action(() => {
|
||||
this.selectedRemoteId.set(null);
|
||||
this.remoteEdit.set(null);
|
||||
})();
|
||||
}
|
||||
|
||||
openReadModal(remoteId: string): void {
|
||||
mobx.action(() => {
|
||||
this.selectedRemoteId.set(remoteId);
|
||||
this.remoteEdit.set(null);
|
||||
GlobalModel.modalsModel.pushModal(constants.VIEW_REMOTE);
|
||||
})();
|
||||
}
|
||||
|
||||
openAddModal(redit: RemoteEditType): void {
|
||||
mobx.action(() => {
|
||||
this.remoteEdit.set(redit);
|
||||
GlobalModel.modalsModel.pushModal(constants.CREATE_REMOTE);
|
||||
})();
|
||||
}
|
||||
|
||||
openEditModal(redit?: RemoteEditType): void {
|
||||
if (redit == null) {
|
||||
this.startEditAuth();
|
||||
GlobalModel.modalsModel.pushModal(constants.EDIT_REMOTE);
|
||||
} else {
|
||||
mobx.action(() => {
|
||||
this.selectedRemoteId.set(redit?.remoteid);
|
||||
this.remoteEdit.set(redit);
|
||||
GlobalModel.modalsModel.pushModal(constants.EDIT_REMOTE);
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
selectRemote(remoteId: string): void {
|
||||
if (this.selectedRemoteId.get() == remoteId) {
|
||||
return;
|
||||
}
|
||||
mobx.action(() => {
|
||||
this.selectedRemoteId.set(remoteId);
|
||||
this.remoteEdit.set(null);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
startEditAuth(): void {
|
||||
let remoteId = this.selectedRemoteId.get();
|
||||
if (remoteId != null) {
|
||||
GlobalCommandRunner.openEditRemote(remoteId);
|
||||
}
|
||||
}
|
||||
|
||||
isAuthEditMode(): boolean {
|
||||
return this.remoteEdit.get() != null;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.modalsModel.popModal();
|
||||
})();
|
||||
setTimeout(() => GlobalModel.refocus(), 10);
|
||||
}
|
||||
|
||||
disposeTerm(): void {
|
||||
if (this.remoteTermWrap == null) {
|
||||
return;
|
||||
}
|
||||
this.remoteTermWrap.dispose();
|
||||
this.remoteTermWrap = null;
|
||||
mobx.action(() => {
|
||||
this.remoteTermWrapFocus.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
receiveData(remoteId: string, ptyPos: number, ptyData: Uint8Array, reason?: string) {
|
||||
if (this.remoteTermWrap == null) {
|
||||
return;
|
||||
}
|
||||
if (this.remoteTermWrap.getContextRemoteId() != remoteId) {
|
||||
return;
|
||||
}
|
||||
this.remoteTermWrap.receiveData(ptyPos, ptyData);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
setRemoteTermWrapFocus(focus: boolean): void {
|
||||
mobx.action(() => {
|
||||
this.remoteTermWrapFocus.set(focus);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
setShowNoInputMsg(val: boolean) {
|
||||
mobx.action(() => {
|
||||
if (this.showNoInputTimeoutId != null) {
|
||||
clearTimeout(this.showNoInputTimeoutId);
|
||||
this.showNoInputTimeoutId = null;
|
||||
}
|
||||
if (val) {
|
||||
this.showNoInputMsg.set(true);
|
||||
this.showNoInputTimeoutId = setTimeout(() => this.setShowNoInputMsg(false), 2000);
|
||||
} else {
|
||||
this.showNoInputMsg.set(false);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
termKeyHandler(remoteId: string, event: any, termWrap: TermWrap): void {
|
||||
let remote = GlobalModel.getRemote(remoteId);
|
||||
if (remote == null) {
|
||||
return;
|
||||
}
|
||||
if (remote.status != "connecting" && remote.installstatus != "connecting") {
|
||||
this.setShowNoInputMsg(true);
|
||||
return;
|
||||
}
|
||||
let inputPacket: RemoteInputPacketType = {
|
||||
type: "remoteinput",
|
||||
remoteid: remoteId,
|
||||
inputdata64: btoa(event.key),
|
||||
};
|
||||
GlobalModel.sendInputPacket(inputPacket);
|
||||
}
|
||||
|
||||
createTermWrap(elem: HTMLElement): void {
|
||||
this.disposeTerm();
|
||||
let remoteId = this.selectedRemoteId.get();
|
||||
if (remoteId == null) {
|
||||
return;
|
||||
}
|
||||
let termOpts = {
|
||||
rows: RemotePtyRows,
|
||||
cols: RemotePtyCols,
|
||||
flexrows: false,
|
||||
maxptysize: 64 * 1024,
|
||||
};
|
||||
let termWrap = new TermWrap(elem, {
|
||||
termContext: { remoteId: remoteId },
|
||||
usedRows: RemotePtyRows,
|
||||
termOpts: termOpts,
|
||||
winSize: null,
|
||||
keyHandler: (e, termWrap) => {
|
||||
this.termKeyHandler(remoteId, e, termWrap);
|
||||
},
|
||||
focusHandler: this.setRemoteTermWrapFocus.bind(this),
|
||||
isRunning: true,
|
||||
fontSize: GlobalModel.termFontSize.get(),
|
||||
ptyDataSource: getTermPtyData,
|
||||
onUpdateContentHeight: null,
|
||||
});
|
||||
this.remoteTermWrap = termWrap;
|
||||
}
|
||||
}
|
||||
|
||||
class ModalsModel {
|
||||
store: Array<{ id: string; component: React.ComponentType }> = [];
|
||||
|
||||
constructor() {
|
||||
mobx.makeAutoObservable(this);
|
||||
}
|
||||
|
||||
pushModal(modalId: string) {
|
||||
const modalFactory = modalsRegistry[modalId];
|
||||
|
||||
if (modalFactory && !this.store.some((modal) => modal.id === modalId)) {
|
||||
this.store.push({ id: modalId, component: modalFactory });
|
||||
}
|
||||
}
|
||||
|
||||
popModal() {
|
||||
this.store.pop();
|
||||
}
|
||||
|
||||
get activeModals() {
|
||||
return this.store.slice().map((modal) => {
|
||||
return modal.component;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Model {
|
||||
clientId: string;
|
||||
activeSessionId: OV<string> = mobx.observable.box(null, {
|
||||
@ -2714,9 +2952,10 @@ class Model {
|
||||
authKey: string;
|
||||
isDev: boolean;
|
||||
platform: string;
|
||||
activeMainView: OV<"plugins" | "session" | "history" | "bookmarks" | "webshare"> = mobx.observable.box("session", {
|
||||
name: "activeMainView",
|
||||
});
|
||||
activeMainView: OV<"plugins" | "session" | "history" | "bookmarks" | "webshare" | "connections"> =
|
||||
mobx.observable.box("session", {
|
||||
name: "activeMainView",
|
||||
});
|
||||
termFontSize: CV<number>;
|
||||
alertMessage: OV<AlertMessageType> = mobx.observable.box(null, {
|
||||
name: "alertMessage",
|
||||
@ -2738,11 +2977,14 @@ class Model {
|
||||
name: "lineSettingsModal",
|
||||
}); // linenum
|
||||
remotesModalModel: RemotesModalModel;
|
||||
remotesModel: RemotesModel;
|
||||
|
||||
inputModel: InputModel;
|
||||
pluginsModel: PluginsModel;
|
||||
bookmarksModel: BookmarksModel;
|
||||
historyViewModel: HistoryViewModel;
|
||||
connectionViewModel: ConnectionsViewModel;
|
||||
modalsModel: ModalsModel;
|
||||
clientData: OV<ClientDataType> = mobx.observable.box(null, {
|
||||
name: "clientData",
|
||||
});
|
||||
@ -2762,7 +3004,10 @@ class Model {
|
||||
this.pluginsModel = new PluginsModel();
|
||||
this.bookmarksModel = new BookmarksModel();
|
||||
this.historyViewModel = new HistoryViewModel();
|
||||
this.connectionViewModel = new ConnectionsViewModel();
|
||||
this.remotesModalModel = new RemotesModalModel();
|
||||
this.remotesModel = new RemotesModel();
|
||||
this.modalsModel = new ModalsModel();
|
||||
let isWaveSrvRunning = getApi().getWaveSrvStatus();
|
||||
this.waveSrvRunning = mobx.observable.box(isWaveSrvRunning, {
|
||||
name: "model-wavesrv-running",
|
||||
@ -2851,6 +3096,7 @@ class Model {
|
||||
showAlert(alertMessage: AlertMessageType): Promise<boolean> {
|
||||
mobx.action(() => {
|
||||
this.alertMessage.set(alertMessage);
|
||||
GlobalModel.modalsModel.pushModal(constants.ALERT);
|
||||
})();
|
||||
let prtn = new Promise<boolean>((resolve, reject) => {
|
||||
this.alertPromiseResolver = resolve;
|
||||
@ -2861,6 +3107,7 @@ class Model {
|
||||
cancelAlert(): void {
|
||||
mobx.action(() => {
|
||||
this.alertMessage.set(null);
|
||||
GlobalModel.modalsModel.popModal();
|
||||
})();
|
||||
if (this.alertPromiseResolver != null) {
|
||||
this.alertPromiseResolver(false);
|
||||
@ -2871,6 +3118,7 @@ class Model {
|
||||
confirmAlert(): void {
|
||||
mobx.action(() => {
|
||||
this.alertMessage.set(null);
|
||||
GlobalModel.modalsModel.popModal();
|
||||
})();
|
||||
if (this.alertPromiseResolver != null) {
|
||||
this.alertPromiseResolver(true);
|
||||
@ -2988,10 +3236,6 @@ class Model {
|
||||
GlobalModel.screenSettingsModal.set(null);
|
||||
didSomething = true;
|
||||
}
|
||||
if (GlobalModel.remotesModalModel.isOpen()) {
|
||||
GlobalModel.remotesModalModel.closeModal();
|
||||
didSomething = true;
|
||||
}
|
||||
if (GlobalModel.clientSettingsModal.get()) {
|
||||
GlobalModel.clientSettingsModal.set(false);
|
||||
didSomething = true;
|
||||
@ -3131,7 +3375,7 @@ class Model {
|
||||
|
||||
onMenuItemAbout(): void {
|
||||
mobx.action(() => {
|
||||
this.aboutModalOpen.set(true);
|
||||
this.modalsModel.pushModal(constants.ABOUT);
|
||||
})();
|
||||
}
|
||||
|
||||
@ -3198,7 +3442,7 @@ class Model {
|
||||
} else {
|
||||
// remote update
|
||||
let ptyData = base64ToArray(ptyMsg.ptydata64);
|
||||
this.remotesModalModel.receiveData(ptyMsg.remoteid, ptyMsg.ptypos, ptyData);
|
||||
this.remotesModel.receiveData(ptyMsg.remoteid, ptyMsg.ptypos, ptyData);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -3262,6 +3506,10 @@ class Model {
|
||||
this.remotes.clear();
|
||||
}
|
||||
this.updateRemotes(update.remotes);
|
||||
if (update.remotes && update.remotes.length && this.remotesModel.recentConnAddedState.get()) {
|
||||
GlobalModel.remotesModel.closeModal();
|
||||
GlobalModel.remotesModel.openReadModal(update.remotes![0].remoteid);
|
||||
}
|
||||
}
|
||||
if ("mainview" in update) {
|
||||
if (update.mainview == "plugins") {
|
||||
@ -3287,12 +3535,8 @@ class Model {
|
||||
}
|
||||
if (interactive && "remoteview" in update) {
|
||||
let rview: RemoteViewType = update.remoteview;
|
||||
if (rview.remoteshowall) {
|
||||
this.remotesModalModel.openModal();
|
||||
} else if (rview.remoteedit != null) {
|
||||
this.remotesModalModel.openModalForEdit({ ...rview.remoteedit, old: true }, false);
|
||||
} else if (rview.ptyremoteid) {
|
||||
this.remotesModalModel.openModal(rview.ptyremoteid);
|
||||
if (rview.remoteedit != null) {
|
||||
this.remotesModel.openEditModal({ ...rview.remoteedit });
|
||||
}
|
||||
}
|
||||
if ("cmdline" in update) {
|
||||
@ -3514,7 +3758,7 @@ class Model {
|
||||
submitCommand(
|
||||
metaCmd: string,
|
||||
metaSubCmd: string,
|
||||
args: string[] | null,
|
||||
args: string[],
|
||||
kwargs: Record<string, string>,
|
||||
interactive: boolean
|
||||
): Promise<CommandRtnType> {
|
||||
@ -3593,12 +3837,10 @@ class Model {
|
||||
}
|
||||
|
||||
getRemote(remoteId: string): RemoteType {
|
||||
for (let i = 0; i < this.remotes.length; i++) {
|
||||
if (this.remotes[i].remoteid == remoteId) {
|
||||
return this.remotes[i];
|
||||
}
|
||||
if (remoteId == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return this.remotes.find((remote) => remote.remoteid === remoteId);
|
||||
}
|
||||
|
||||
getRemoteNames(): Record<string, string> {
|
||||
@ -3624,11 +3866,15 @@ class Model {
|
||||
}
|
||||
|
||||
getCmd(line: LineType): Cmd {
|
||||
let slines = this.getScreenLinesById(line.screenid);
|
||||
return this.getCmdByScreenLine(line.screenid, line.lineid);
|
||||
}
|
||||
|
||||
getCmdByScreenLine(screenId: string, lineId: string): Cmd {
|
||||
let slines = this.getScreenLinesById(screenId);
|
||||
if (slines == null) {
|
||||
return null;
|
||||
}
|
||||
return slines.getCmd(line.lineid);
|
||||
return slines.getCmd(lineId);
|
||||
}
|
||||
|
||||
getActiveLine(screenId: string, lineid: string): SWLinePtr {
|
||||
@ -4018,6 +4264,10 @@ class CommandRunner {
|
||||
GlobalModel.submitCommand("bookmarks", "show", null, { nohist: "1" }, true);
|
||||
}
|
||||
|
||||
connectionsView() {
|
||||
GlobalModel.connectionViewModel.showConnectionsView();
|
||||
}
|
||||
|
||||
historyView(params: HistorySearchParams) {
|
||||
let kwargs = { nohist: "1" };
|
||||
kwargs["offset"] = String(params.offset);
|
||||
@ -4195,5 +4445,8 @@ export {
|
||||
RemoteColors,
|
||||
getTermPtyData,
|
||||
RemotesModalModel,
|
||||
RemotesModel,
|
||||
MinFontSize,
|
||||
MaxFontSize,
|
||||
};
|
||||
export type { LineContainerModel };
|
||||
|
@ -97,7 +97,7 @@
|
||||
.gutter {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
background: fade(@prompt-green, 40%);
|
||||
background: fade(@wave-green, 40%);
|
||||
max-width: 3px;
|
||||
}
|
||||
.gutter-horizontal {
|
||||
@ -107,10 +107,10 @@
|
||||
cursor: row-resize;
|
||||
}
|
||||
.gutter:hover {
|
||||
background: @prompt-green;
|
||||
background: @wave-green;
|
||||
}
|
||||
.gutter-dragging:hover {
|
||||
background: @prompt-green;
|
||||
background: @wave-green;
|
||||
}
|
||||
|
||||
.pane {
|
||||
|
@ -61,6 +61,7 @@ class TermWrap {
|
||||
onUpdateContentHeight: (termContext: RendererContext, height: number) => void;
|
||||
ptyDataSource: (termContext: TermContextUnion) => Promise<PtyDataType>;
|
||||
initializing: boolean;
|
||||
dataHandler?: (data: string, termWrap: TermWrap) => void;
|
||||
|
||||
constructor(elem: Element, opts: TermWrapOpts) {
|
||||
opts = opts ?? ({} as any);
|
||||
@ -104,6 +105,7 @@ class TermWrap {
|
||||
this.terminal.onKey((e) => opts.keyHandler(e, this));
|
||||
}
|
||||
if (opts.dataHandler != null) {
|
||||
this.dataHandler = opts.dataHandler;
|
||||
this.terminal.onData((e) => opts.dataHandler(e, this));
|
||||
}
|
||||
this.terminal.textarea.addEventListener("focus", () => {
|
||||
|
@ -168,7 +168,7 @@ type FeCmdPacketType = {
|
||||
type: string;
|
||||
metacmd: string;
|
||||
metasubcmd?: string;
|
||||
args: string[] | null;
|
||||
args: string[];
|
||||
kwargs: Record<string, string>;
|
||||
rawstr?: string;
|
||||
uicontext: UIContextType;
|
||||
|
@ -1,10 +1,10 @@
|
||||
{
|
||||
"include": ["src/**/*", "types/**/*"],
|
||||
"exclude": ["src/electron/emain.ts"],
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"jsx": "react",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
@ -12,6 +12,7 @@
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"experimentalDecorators": true
|
||||
"experimentalDecorators": true,
|
||||
"downlevelIteration": true
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
const VERSION = "v0.5.0";
|
||||
const VERSION = "v0.5.1";
|
||||
module.exports = VERSION;
|
||||
|
@ -61,6 +61,9 @@ const MaxEvalDepth = 5
|
||||
const MaxOpenAIAPITokenLen = 100
|
||||
const MaxOpenAIModelLen = 100
|
||||
|
||||
const TermFontSizeMin = 8
|
||||
const TermFontSizeMax = 24
|
||||
|
||||
const TsFormatStr = "2006-01-02 15:04:05"
|
||||
|
||||
const (
|
||||
@ -439,10 +442,10 @@ func SyncCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.
|
||||
runPacket.ReqId = uuid.New().String()
|
||||
runPacket.CK = base.MakeCommandKey(ids.ScreenId, scbase.GenWaveUUID())
|
||||
runPacket.UsePty = true
|
||||
ptermVal := defaultStr(pk.Kwargs["pterm"], DefaultPTERM)
|
||||
ptermVal := defaultStr(pk.Kwargs["wterm"], DefaultPTERM)
|
||||
runPacket.TermOpts, err = GetUITermOpts(pk.UIContext.WinSize, ptermVal)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("/sync error, invalid 'pterm' value %q: %v", ptermVal, err)
|
||||
return nil, fmt.Errorf("/sync error, invalid 'wterm' value %q: %v", ptermVal, err)
|
||||
}
|
||||
runPacket.Command = ":"
|
||||
runPacket.ReturnState = true
|
||||
@ -535,7 +538,7 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U
|
||||
runPacket.ReqId = uuid.New().String()
|
||||
runPacket.CK = base.MakeCommandKey(ids.ScreenId, scbase.GenWaveUUID())
|
||||
runPacket.UsePty = true
|
||||
ptermVal := defaultStr(pk.Kwargs["pterm"], DefaultPTERM)
|
||||
ptermVal := defaultStr(pk.Kwargs["wterm"], DefaultPTERM)
|
||||
runPacket.TermOpts, err = GetUITermOpts(pk.UIContext.WinSize, ptermVal)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("/run error, invalid 'pterm' value %q: %v", ptermVal, err)
|
||||
@ -761,10 +764,6 @@ func ScreenSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
|
||||
}
|
||||
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
|
||||
@ -1532,7 +1531,7 @@ func OpenAICommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor
|
||||
if promptStr == "" {
|
||||
return nil, fmt.Errorf("openai error, prompt string is blank")
|
||||
}
|
||||
ptermVal := defaultStr(pk.Kwargs["pterm"], DefaultPTERM)
|
||||
ptermVal := defaultStr(pk.Kwargs["wterm"], DefaultPTERM)
|
||||
pkTermOpts, err := GetUITermOpts(pk.UIContext.WinSize, ptermVal)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("openai error, invalid 'pterm' value %q: %v", ptermVal, err)
|
||||
@ -1990,15 +1989,6 @@ func validateColor(color string, typeStr string) error {
|
||||
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 {
|
||||
for _, c := range RemoteColorNames {
|
||||
if color == c {
|
||||
@ -3554,8 +3544,8 @@ func ClientSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid termfontsize, must be a number between 8-15: %v", err)
|
||||
}
|
||||
if newFontSize < 8 || newFontSize > 15 {
|
||||
return nil, fmt.Errorf("invalid termfontsize, must be a number between 8-15")
|
||||
if newFontSize < TermFontSizeMin || newFontSize > TermFontSizeMax {
|
||||
return nil, fmt.Errorf("invalid termfontsize, must be a number between %d-%d", TermFontSizeMin, TermFontSizeMax)
|
||||
}
|
||||
feOpts := clientData.FeOpts
|
||||
feOpts.TermFontSize = newFontSize
|
||||
|
@ -179,7 +179,19 @@ func setBracketArgs(argMap map[string]string, bracketStr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var literalRtnStateCommands = []string{".", "source", "unset", "cd", "alias", "unalias", "deactivate", "eval"}
|
||||
var literalRtnStateCommands = []string{
|
||||
".",
|
||||
"source",
|
||||
"unset",
|
||||
"cd",
|
||||
"alias",
|
||||
"unalias",
|
||||
"deactivate",
|
||||
"eval",
|
||||
"asdf",
|
||||
"nvm",
|
||||
"virtualenv",
|
||||
}
|
||||
|
||||
func getCallExprLitArg(callExpr *syntax.CallExpr, argNum int) string {
|
||||
if len(callExpr.Args) <= argNum {
|
||||
|
@ -36,7 +36,7 @@ const WaveLockFile = "waveterm.lock"
|
||||
const WaveDirName = ".waveterm" // must match emain.ts
|
||||
const WaveDevDirName = ".waveterm-dev" // must match emain.ts
|
||||
const WaveAppPathVarName = "WAVETERM_APP_PATH"
|
||||
const WaveVersion = "v0.5.0"
|
||||
const WaveVersion = "v0.5.1"
|
||||
const WaveAuthKeyFileName = "waveterm.authkey"
|
||||
const MShellVersion = "v0.3.0"
|
||||
const DefaultMacOSShell = "/bin/bash"
|
||||
|
@ -1391,17 +1391,9 @@ func ArchiveScreenLines(ctx context.Context, screenId string) (*ModelUpdate, err
|
||||
if !tx.Exists(query, screenId) {
|
||||
return fmt.Errorf("screen does not exist")
|
||||
}
|
||||
fmt.Printf("** archive-screen-lines: %s\n", screenId)
|
||||
if isWebShare(tx, screenId) {
|
||||
query = `INSERT INTO screenupdate (screenid, lineid, updatetype, updatets)
|
||||
SELECT screenid, lineid, ?, ? FROM line WHERE screenid = ? AND archived = 0`
|
||||
tx.Exec(query, UpdateType_LineDel, time.Now().UnixMilli(), screenId)
|
||||
NotifyUpdateWriter()
|
||||
query = `SELECT count(*) FROM line WHERE screenid = ? AND archived = 0`
|
||||
count := tx.GetInt(query, screenId)
|
||||
fmt.Printf("** archive-screen-lines: wrote into screenupdate: %d\n", count)
|
||||
}
|
||||
query = `UPDATE line SET archived = 1 WHERE screenid = ? AND archived = 0`
|
||||
query = `UPDATE line SET archived = 1
|
||||
WHERE line.archived = 0 AND line.screenid = ? AND NOT EXISTS (SELECT * FROM cmd c
|
||||
WHERE line.screenid = c.screenid AND line.lineid = c.lineid AND c.status IN ('running', 'detached'))`
|
||||
tx.Exec(query, screenId)
|
||||
return nil
|
||||
})
|
||||
@ -1709,7 +1701,7 @@ const (
|
||||
ScreenField_SelectedLine = "selectedline" // int
|
||||
ScreenField_Focus = "focustype" // string
|
||||
ScreenField_TabColor = "tabcolor" // string
|
||||
ScreenField_TabIcon = "tabicon" // string
|
||||
ScreenField_TabIcon = "tabicon" // string
|
||||
ScreenField_PTerm = "pterm" // string
|
||||
ScreenField_Name = "name" // string
|
||||
ScreenField_ShareName = "sharename" // string
|
||||
|
Loading…
Reference in New Issue
Block a user