mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7cc22b877c | |||
| 637915554f | |||
| 51ee7bef61 | |||
| 5ce7b92232 | |||
| 81a9e74991 | |||
| 679e85a84a | |||
| da69a0f583 | |||
| 37ab1bca90 | |||
| 40757fa7f4 | |||
| e576f7f07d | |||
| 9e804232c2 | |||
| d3dbac6e77 | |||
| 198de02a65 | |||
| 8f64d6a589 | |||
| 1a51d93a54 | |||
| 9ab3b2a5b7 | |||
| 856c879cdf | |||
| ea3f744d93 | |||
| dd3d52e47d | |||
| 32499f0421 | |||
| b136c915df | |||
| 0648d48ba1 | |||
| b97423268c | |||
| 34ec4ff39f | |||
| 018bb14b6a | |||
| 99f5c094d2 | |||
| f23da6a476 |
@@ -1,9 +1,9 @@
|
||||
name: "Build Helper"
|
||||
on: workflow_dispatch
|
||||
env:
|
||||
WAVETERM_VERSION: 0.6.0
|
||||
GO_VERSION: '1.21.5'
|
||||
NODE_VERSION: '21.5.0'
|
||||
WAVETERM_VERSION: 0.6.1
|
||||
GO_VERSION: "1.21.5"
|
||||
NODE_VERSION: "21.5.0"
|
||||
jobs:
|
||||
runbuild-darwin-x64:
|
||||
name: "Build MacOS x64"
|
||||
@@ -12,23 +12,23 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: |
|
||||
wavesrv/go.sum
|
||||
waveshell/go.sum
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: |
|
||||
wavesrv/go.sum
|
||||
waveshell/go.sum
|
||||
- run: brew tap scripthaus-dev/scripthaus
|
||||
- run: brew install scripthaus
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
cache: 'yarn'
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
cache: "yarn"
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: scripthaus run build-package
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: waveterm-build-darwin-x64
|
||||
path: out/make/zip/darwin/x64/*.zip
|
||||
retention-days: 2
|
||||
name: waveterm-build-darwin-x64
|
||||
path: out/make/zip/darwin/x64/*.zip
|
||||
retention-days: 2
|
||||
runbuild-darwin-arm64:
|
||||
name: "Build MacOS arm64"
|
||||
runs-on: macos-latest-xlarge
|
||||
@@ -36,23 +36,23 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: |
|
||||
wavesrv/go.sum
|
||||
waveshell/go.sum
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: |
|
||||
wavesrv/go.sum
|
||||
waveshell/go.sum
|
||||
- run: brew tap scripthaus-dev/scripthaus
|
||||
- run: brew install scripthaus
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
cache: 'yarn'
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
cache: "yarn"
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: scripthaus run build-package
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: waveterm-build-darwin-arm64
|
||||
path: out/make/zip/darwin/arm64/*.zip
|
||||
retention-days: 2
|
||||
name: waveterm-build-darwin-arm64
|
||||
path: out/make/zip/darwin/arm64/*.zip
|
||||
retention-days: 2
|
||||
runbuild-linux:
|
||||
name: "Build Linux x64"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -61,42 +61,40 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: scripthaus-dev/scripthaus
|
||||
path: scripthaus
|
||||
repository: scripthaus-dev/scripthaus
|
||||
path: scripthaus
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: |
|
||||
wavesrv/go.sum
|
||||
waveshell/go.sum
|
||||
scripthaus/go.sum
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: |
|
||||
wavesrv/go.sum
|
||||
waveshell/go.sum
|
||||
scripthaus/go.sum
|
||||
- run: |
|
||||
go work use ./scripthaus;
|
||||
cd scripthaus;
|
||||
go get ./...;
|
||||
CGO_ENABLED=1 go build -o scripthaus cmd/main.go
|
||||
go work use ./scripthaus;
|
||||
cd scripthaus;
|
||||
go get ./...;
|
||||
CGO_ENABLED=1 go build -o scripthaus cmd/main.go
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
cache: 'yarn'
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
cache: "yarn"
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: ./scripthaus/scripthaus run build-package-linux
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
path: buildtemp
|
||||
merge-multiple: true
|
||||
path: buildtemp
|
||||
- run: |
|
||||
mv out/make/zip/linux/x64/Wave-linux-x64-$WAVETERM_VERSION.zip buildtemp/waveterm-linux-x64-v$WAVETERM_VERSION.zip
|
||||
mv out/make/zip/linux/x64/Wave-linux-x64-$WAVETERM_VERSION.zip buildtemp/waveterm-linux-x64-v$WAVETERM_VERSION.zip
|
||||
- run: (cd buildtemp; zip ../waveterm-builds.zip *)
|
||||
- run: aws s3 cp waveterm-builds.zip s3://waveterm-github-artifacts/
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: "${{ secrets.S3_USERID }}"
|
||||
AWS_SECRET_ACCESS_KEY: "${{ secrets.S3_SECRETKEY }}"
|
||||
AWS_DEFAULT_REGION: us-west-2
|
||||
AWS_ACCESS_KEY_ID: "${{ secrets.S3_USERID }}"
|
||||
AWS_SECRET_ACCESS_KEY: "${{ secrets.S3_SECRETKEY }}"
|
||||
AWS_DEFAULT_REGION: us-west-2
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: waveterm-builds
|
||||
path: buildtemp
|
||||
retention-days: 2
|
||||
|
||||
|
||||
name: waveterm-builds
|
||||
path: buildtemp
|
||||
retention-days: 2
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["esbenp.prettier-vscode", "golang.go", "dbaeumer.vscode-eslint"]
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[less]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
@@ -1131,6 +1131,67 @@ Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## github.com/kevinburke/ssh_config
|
||||
|
||||
* Name: github.com/kevinburke/ssh_config
|
||||
* Version: v0.0.0-20240109090616-36c8da3d7376
|
||||
* License: [MIT](https://github.com/wavetermdev/ssh_config/blob/36c8da3d7376/LICENSE)
|
||||
|
||||
```txt
|
||||
Copyright (c) 2017 Kevin Burke.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
===================
|
||||
|
||||
The lexer and parser borrow heavily from github.com/pelletier/go-toml. The
|
||||
license for that project is copied below.
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# assumes we have Wave-darwin-x64-[version].zip and Wave-darwin-arm64-[version].zip in current directory
|
||||
VERSION=0.6.0
|
||||
VERSION=0.6.1
|
||||
rm -rf temp
|
||||
rm -rf builds
|
||||
mkdir temp
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "waveterm",
|
||||
"author": "Command Line Inc",
|
||||
"productName": "Wave",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"main": "dist/emain.js",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
||||
+16
-12
@@ -99,9 +99,6 @@ body a {
|
||||
|
||||
body code {
|
||||
font-family: @terminal-font;
|
||||
}
|
||||
|
||||
body code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -123,11 +120,19 @@ svg.icon {
|
||||
}
|
||||
|
||||
.hideScrollbarUntillHover {
|
||||
overflow: hidden;
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
overflow: auto;
|
||||
overflow: scroll;
|
||||
|
||||
&::-webkit-scrollbar-thumb,
|
||||
&::-webkit-scrollbar-track {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar-thumb {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,13 +630,13 @@ a.a-block {
|
||||
|
||||
.spin {
|
||||
animation: infiniteRotate 2s linear infinite;
|
||||
|
||||
|
||||
@keyframes infiniteRotate {
|
||||
from {
|
||||
transform:rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform:rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -647,7 +652,6 @@ a.a-block {
|
||||
margin-right: 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(241, 246, 243, 0.08);
|
||||
background: rgba(13, 13, 13, 0.85);
|
||||
|
||||
.header {
|
||||
margin: 24px 18px;
|
||||
|
||||
+12
-4
@@ -30,6 +30,7 @@ type OV<V> = mobx.IObservableValue<V>;
|
||||
@mobxReact.observer
|
||||
class App extends React.Component<{}, {}> {
|
||||
dcWait: OV<boolean> = mobx.observable.box(false, { name: "dcWait" });
|
||||
mainContentRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
@@ -75,6 +76,13 @@ class App extends React.Component<{}, {}> {
|
||||
let hasClientStop = GlobalModel.getHasClientStop();
|
||||
let dcWait = this.dcWait.get();
|
||||
let platform = GlobalModel.getPlatform();
|
||||
let clientData = GlobalModel.clientData.get();
|
||||
|
||||
// Previously, this is done in sidebar.tsx but it causes flicker when clientData is null cos screen-view shifts around.
|
||||
// Doing it here fixes the flicker cos app is not rendered until clientData is populated.
|
||||
if (clientData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (disconnected || hasClientStop) {
|
||||
if (!dcWait) {
|
||||
@@ -82,8 +90,8 @@ class App extends React.Component<{}, {}> {
|
||||
}
|
||||
return (
|
||||
<div id="main" className={"platform-" + platform} onContextMenu={this.handleContextMenu}>
|
||||
<div className="main-content">
|
||||
<MainSideBar />
|
||||
<div ref={this.mainContentRef} className="main-content">
|
||||
<MainSideBar parentRef={this.mainContentRef} clientData={clientData} />
|
||||
<div className="session-view" />
|
||||
</div>
|
||||
<If condition={dcWait}>
|
||||
@@ -102,8 +110,8 @@ class App extends React.Component<{}, {}> {
|
||||
}
|
||||
return (
|
||||
<div id="main" className={"platform-" + platform} onContextMenu={this.handleContextMenu}>
|
||||
<div className="main-content">
|
||||
<MainSideBar />
|
||||
<div ref={this.mainContentRef} className="main-content">
|
||||
<MainSideBar parentRef={this.mainContentRef} clientData={clientData} />
|
||||
<ErrorBoundary>
|
||||
<PluginsView />
|
||||
<WorkspaceView />
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="440" height="440" version="1.1" viewBox="0 0 116.42 116.42" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle id="indicator" cx="58.266" cy="58.266" r="28.311" stroke-width=".19016"/>
|
||||
<path id="spinner" d="m107.2 58.21a48.988 48.988 0 0 1-48.988 48.988" fill="none" stroke="#000" stroke-width="16.856" style="paint-order:normal"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 443 B |
@@ -405,7 +405,7 @@
|
||||
padding: 6px 6px 6px 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
|
||||
pre.selected {
|
||||
outline: 2px solid @term-green;
|
||||
}
|
||||
@@ -609,7 +609,6 @@
|
||||
|
||||
.wave-dropdown {
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
height: 44px;
|
||||
min-width: 150px;
|
||||
width: 100%;
|
||||
@@ -715,9 +714,7 @@
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 0;
|
||||
margin-top: 2px;
|
||||
padding: 0;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
padding: 6px;
|
||||
@@ -775,7 +772,6 @@
|
||||
min-width: 412px;
|
||||
gap: 6px;
|
||||
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));
|
||||
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;
|
||||
@@ -931,7 +927,6 @@
|
||||
background: none;
|
||||
color: inherit;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
outline: inherit;
|
||||
@@ -1156,18 +1151,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
&.error {
|
||||
color: @term-red;
|
||||
}
|
||||
&.success {
|
||||
color: @term-green;
|
||||
}
|
||||
&.output {
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
+177
-43
@@ -9,10 +9,12 @@ import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import cn from "classnames";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { RemoteType, StatusIndicatorLevel } from "../../types/types";
|
||||
import { RemoteType } from "../../types/types";
|
||||
import ReactDOM from "react-dom";
|
||||
import { GlobalModel } from "../../model/model";
|
||||
import { GlobalModel, GlobalCommandRunner } from "../../model/model";
|
||||
import * as appconst from "../appconst";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "../../util/keyutil";
|
||||
import { MagicLayout } from "../magiclayout";
|
||||
|
||||
import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
|
||||
import { ReactComponent as CopyIcon } from "../assets/icons/history/copy.svg";
|
||||
@@ -117,7 +119,7 @@ class Checkbox extends React.Component<
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
checkedInternal: this.props.checked !== undefined ? this.props.checked : Boolean(this.props.defaultChecked),
|
||||
checkedInternal: this.props.checked ?? Boolean(this.props.defaultChecked),
|
||||
};
|
||||
this.generatedId = `checkbox-${Checkbox.idCounter++}`;
|
||||
}
|
||||
@@ -287,15 +289,16 @@ class Button extends React.Component<ButtonProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { leftIcon, rightIcon, theme, children, disabled, variant, color, style } = this.props;
|
||||
const { leftIcon, rightIcon, theme, children, disabled, variant, color, style, autoFocus, className } =
|
||||
this.props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn("wave-button", theme, variant, color, { disabled: disabled })}
|
||||
className={cn("wave-button", theme, variant, color, { disabled: disabled }, className)}
|
||||
onClick={this.handleClick}
|
||||
disabled={disabled}
|
||||
style={style}
|
||||
autoFocus={this.props.autoFocus}
|
||||
autoFocus={autoFocus}
|
||||
>
|
||||
{leftIcon && <span className="icon-left">{leftIcon}</span>}
|
||||
{children}
|
||||
@@ -714,13 +717,14 @@ class InlineSettingsTextEdit extends React.Component<
|
||||
|
||||
@boundMethod
|
||||
handleKeyDown(e: any): void {
|
||||
if (e.code == "Enter") {
|
||||
let waveEvent = adaptFromReactOrNativeKeyEvent(e);
|
||||
if (checkKeyPressed(waveEvent, "Enter")) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.confirmChange();
|
||||
return;
|
||||
}
|
||||
if (e.code == "Escape") {
|
||||
if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.cancelChange();
|
||||
@@ -868,7 +872,7 @@ class Markdown extends React.Component<
|
||||
if (codeSelect) {
|
||||
return <CodeBlockMarkdown codeSelectSelectedIndex={codeSelectIndex}>{props.children}</CodeBlockMarkdown>;
|
||||
} else {
|
||||
let clickHandler = (e: React.MouseEvent<HTMLElement>) => {
|
||||
const clickHandler = (e: React.MouseEvent<HTMLElement>) => {
|
||||
let blockText = (e.target as HTMLElement).innerText;
|
||||
if (blockText) {
|
||||
blockText = blockText.replace(/\n$/, ""); // remove trailing newline
|
||||
@@ -896,7 +900,9 @@ class Markdown extends React.Component<
|
||||
};
|
||||
return (
|
||||
<div className={cn("markdown content", this.props.extraClassName)} style={this.props.style}>
|
||||
<ReactMarkdown children={text} remarkPlugins={[remarkGfm]} components={markdownComponents} />
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||
{text}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1239,38 +1245,6 @@ class Modal extends React.Component<ModalProps> {
|
||||
}
|
||||
}
|
||||
|
||||
interface StatusIndicatorProps {
|
||||
level: StatusIndicatorLevel;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
class StatusIndicator extends React.Component<StatusIndicatorProps> {
|
||||
render() {
|
||||
const statusIndicatorLevel = this.props.level;
|
||||
let statusIndicator = null;
|
||||
if (statusIndicatorLevel != StatusIndicatorLevel.None) {
|
||||
let statusIndicatorClass = null;
|
||||
switch (statusIndicatorLevel) {
|
||||
case StatusIndicatorLevel.Output:
|
||||
statusIndicatorClass = "output";
|
||||
break;
|
||||
case StatusIndicatorLevel.Success:
|
||||
statusIndicatorClass = "success";
|
||||
break;
|
||||
case StatusIndicatorLevel.Error:
|
||||
statusIndicatorClass = "error";
|
||||
break;
|
||||
}
|
||||
statusIndicator = (
|
||||
<div
|
||||
className={`${this.props.className} fa-sharp fa-solid fa-circle-small status-indicator ${statusIndicatorClass}`}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
return statusIndicator;
|
||||
}
|
||||
}
|
||||
|
||||
function ShowWaveShellInstallPrompt(callbackFn: () => void) {
|
||||
let message: string = `
|
||||
In order to use Wave's advanced features like unified history and persistent sessions, Wave installs a small, open-source helper program called WaveShell on your remote machine. WaveShell does not open any external ports and only communicates with your *local* Wave terminal instance over ssh. For more information please see [the docs](https://docs.waveterm.dev/reference/waveshell).
|
||||
@@ -1292,6 +1266,166 @@ In order to use Wave's advanced features like unified history and persistent ses
|
||||
});
|
||||
}
|
||||
|
||||
interface ResizableSidebarProps {
|
||||
parentRef: React.RefObject<HTMLElement>;
|
||||
position: "left" | "right";
|
||||
enableSnap?: boolean;
|
||||
className?: string;
|
||||
children?: (toggleCollapsed: () => void) => React.ReactNode;
|
||||
toggleCollapse?: () => void;
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class ResizableSidebar extends React.Component<ResizableSidebarProps> {
|
||||
resizeStartWidth: number = 0;
|
||||
startX: number = 0;
|
||||
prevDelta: number = 0;
|
||||
prevDragDirection: string = null;
|
||||
disposeReaction: any;
|
||||
|
||||
@boundMethod
|
||||
startResizing(event: React.MouseEvent<HTMLDivElement>) {
|
||||
event.preventDefault();
|
||||
|
||||
const { parentRef, position } = this.props;
|
||||
const parentRect = parentRef.current?.getBoundingClientRect();
|
||||
|
||||
if (!parentRect) return;
|
||||
|
||||
if (position === "right") {
|
||||
this.startX = parentRect.right - event.clientX;
|
||||
} else {
|
||||
this.startX = event.clientX - parentRect.left;
|
||||
}
|
||||
|
||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
||||
const collapsed = mainSidebarModel.getCollapsed();
|
||||
|
||||
this.resizeStartWidth = mainSidebarModel.getWidth();
|
||||
document.addEventListener("mousemove", this.onMouseMove);
|
||||
document.addEventListener("mouseup", this.stopResizing);
|
||||
|
||||
document.body.style.cursor = "col-resize";
|
||||
mobx.action(() => {
|
||||
mainSidebarModel.setTempWidthAndTempCollapsed(this.resizeStartWidth, collapsed);
|
||||
mainSidebarModel.isDragging.set(true);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
onMouseMove(event: MouseEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
const { parentRef, enableSnap, position } = this.props;
|
||||
const parentRect = parentRef.current?.getBoundingClientRect();
|
||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
||||
|
||||
if (!mainSidebarModel.isDragging.get() || !parentRect) return;
|
||||
|
||||
let delta: number, newWidth: number;
|
||||
|
||||
if (position === "right") {
|
||||
delta = parentRect.right - event.clientX - this.startX;
|
||||
} else {
|
||||
delta = event.clientX - parentRect.left - this.startX;
|
||||
}
|
||||
|
||||
newWidth = this.resizeStartWidth + delta;
|
||||
|
||||
if (enableSnap) {
|
||||
const minWidth = MagicLayout.MainSidebarMinWidth;
|
||||
const snapPoint = minWidth + MagicLayout.MainSidebarSnapThreshold;
|
||||
const dragResistance = MagicLayout.MainSidebarDragResistance;
|
||||
let dragDirection: string;
|
||||
|
||||
if (delta - this.prevDelta > 0) {
|
||||
dragDirection = "+";
|
||||
} else if (delta - this.prevDelta == 0) {
|
||||
if (this.prevDragDirection == "+") {
|
||||
dragDirection = "+";
|
||||
} else {
|
||||
dragDirection = "-";
|
||||
}
|
||||
} else {
|
||||
dragDirection = "-";
|
||||
}
|
||||
|
||||
this.prevDelta = delta;
|
||||
this.prevDragDirection = dragDirection;
|
||||
|
||||
if (newWidth - dragResistance > minWidth && newWidth < snapPoint && dragDirection == "+") {
|
||||
newWidth = snapPoint;
|
||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, false);
|
||||
} else if (newWidth + dragResistance < snapPoint && dragDirection == "-") {
|
||||
newWidth = minWidth;
|
||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, true);
|
||||
} else if (newWidth > snapPoint) {
|
||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, false);
|
||||
}
|
||||
} else {
|
||||
if (newWidth <= MagicLayout.MainSidebarMinWidth) {
|
||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, true);
|
||||
} else {
|
||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
stopResizing() {
|
||||
let mainSidebarModel = GlobalModel.mainSidebarModel;
|
||||
|
||||
GlobalCommandRunner.clientSetSidebar(
|
||||
mainSidebarModel.tempWidth.get(),
|
||||
mainSidebarModel.tempCollapsed.get()
|
||||
).finally(() => {
|
||||
mobx.action(() => {
|
||||
mainSidebarModel.isDragging.set(false);
|
||||
})();
|
||||
});
|
||||
|
||||
document.removeEventListener("mousemove", this.onMouseMove);
|
||||
document.removeEventListener("mouseup", this.stopResizing);
|
||||
document.body.style.cursor = "";
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
toggleCollapsed() {
|
||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
||||
|
||||
const tempCollapsed = mainSidebarModel.getCollapsed();
|
||||
const width = mainSidebarModel.getWidth(true);
|
||||
mainSidebarModel.setTempWidthAndTempCollapsed(width, !tempCollapsed);
|
||||
GlobalCommandRunner.clientSetSidebar(width, !tempCollapsed);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, children } = this.props;
|
||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
||||
const width = mainSidebarModel.getWidth();
|
||||
const isCollapsed = mainSidebarModel.getCollapsed();
|
||||
|
||||
return (
|
||||
<div className={cn("sidebar", className, { collapsed: isCollapsed })} style={{ width }}>
|
||||
<div className="sidebar-content">{children(this.toggleCollapsed)}</div>
|
||||
<div
|
||||
className="sidebar-handle"
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
[this.props.position === "left" ? "right" : "left"]: 0,
|
||||
bottom: 0,
|
||||
width: "5px",
|
||||
cursor: "col-resize",
|
||||
}}
|
||||
onMouseDown={this.startResizing}
|
||||
onDoubleClick={this.toggleCollapsed}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
CmdStrCode,
|
||||
Toggle,
|
||||
@@ -1313,6 +1447,6 @@ export {
|
||||
LinkButton,
|
||||
Status,
|
||||
Modal,
|
||||
StatusIndicator,
|
||||
ResizableSidebar,
|
||||
ShowWaveShellInstallPrompt,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
@import "../../../app/common/themes/themes.less";
|
||||
|
||||
.front-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.positional-icon-visible {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.positional-icon-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.positional-icon {
|
||||
display: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
.svg-icon svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.positional-icon-inner {
|
||||
display: flex;
|
||||
width: inherit;
|
||||
height: 100%;
|
||||
line-height: inherit;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
& > div,
|
||||
i,
|
||||
.svg-icon,
|
||||
span {
|
||||
width: 20px;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
.icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The following accounts for a debounce in the status indicator. We will only display the status indicator icon if the parent indicates that it should be visible AND one of the following is true:
|
||||
1. There is a status to be shown.
|
||||
2. There is a spinner to be shown and the required delay has passed.
|
||||
*/
|
||||
.status-indicator-visible {
|
||||
&.spinner-visible,
|
||||
&.output,
|
||||
&.error,
|
||||
&.success {
|
||||
.positional-icon-visible;
|
||||
}
|
||||
|
||||
// This is set by the timeout in the status indicator component.
|
||||
&.spinner-visible {
|
||||
#spinner {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
#spinner,
|
||||
#indicator {
|
||||
visibility: hidden;
|
||||
}
|
||||
.spin #spinner {
|
||||
stroke: @term-white;
|
||||
}
|
||||
&.error #indicator {
|
||||
visibility: visible;
|
||||
fill: @term-red;
|
||||
}
|
||||
&.success #indicator {
|
||||
visibility: visible;
|
||||
fill: @term-green;
|
||||
}
|
||||
&.output #indicator {
|
||||
visibility: visible;
|
||||
fill: @term-white;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
import React from "react";
|
||||
import { StatusIndicatorLevel } from "../../../types/types";
|
||||
import cn from "classnames";
|
||||
import { ReactComponent as SpinnerIndicator } from "../../assets/icons/spinner-indicator.svg";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import * as mobx from "mobx";
|
||||
import * as mobxReact from "mobx-react";
|
||||
|
||||
import { ReactComponent as RotateIconSvg } from "../../assets/icons/line/rotate.svg";
|
||||
|
||||
interface PositionalIconProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
divRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export class FrontIcon extends React.Component<PositionalIconProps> {
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
ref={this.props.divRef}
|
||||
className={cn("front-icon", "positional-icon", this.props.className)}
|
||||
onClick={this.props.onClick}
|
||||
>
|
||||
<div className="positional-icon-inner">{this.props.children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class CenteredIcon extends React.Component<PositionalIconProps> {
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
ref={this.props.divRef}
|
||||
className={cn("centered-icon", "positional-icon", this.props.className)}
|
||||
onClick={this.props.onClick}
|
||||
>
|
||||
<div className="positional-icon-inner">{this.props.children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface ActionsIconProps {
|
||||
onClick: React.MouseEventHandler<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export class ActionsIcon extends React.Component<ActionsIconProps> {
|
||||
render() {
|
||||
return (
|
||||
<CenteredIcon className="actions" onClick={this.props.onClick}>
|
||||
<div className="icon hoverEffect fa-sharp fa-solid fa-1x fa-ellipsis-vertical"></div>
|
||||
</CenteredIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SyncSpin extends React.Component<{
|
||||
classRef?: React.RefObject<HTMLDivElement>;
|
||||
children?: React.ReactNode;
|
||||
shouldSync?: boolean;
|
||||
}> {
|
||||
listenerAdded: boolean = false;
|
||||
|
||||
componentDidMount() {
|
||||
this.syncSpinner();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.syncSpinner();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
const classRef = this.props.classRef;
|
||||
if (classRef.current != null && this.listenerAdded) {
|
||||
const elem = classRef.current;
|
||||
const svgElem = elem.querySelector("svg");
|
||||
if (svgElem != null) {
|
||||
svgElem.removeEventListener("animationstart", this.handleAnimationStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleAnimationStart(e: AnimationEvent) {
|
||||
const classRef = this.props.classRef;
|
||||
if (classRef.current == null) {
|
||||
return;
|
||||
}
|
||||
const svgElem = classRef.current.querySelector("svg");
|
||||
if (svgElem == null) {
|
||||
return;
|
||||
}
|
||||
const animArr = svgElem.getAnimations();
|
||||
if (animArr == null || animArr.length == 0) {
|
||||
return;
|
||||
}
|
||||
animArr[0].startTime = 0;
|
||||
}
|
||||
|
||||
syncSpinner() {
|
||||
const { classRef, shouldSync } = this.props;
|
||||
const shouldSyncVal = shouldSync ?? true;
|
||||
if (!shouldSyncVal || classRef.current == null) {
|
||||
return;
|
||||
}
|
||||
const elem = classRef.current;
|
||||
const svgElem = elem.querySelector("svg");
|
||||
if (svgElem == null) {
|
||||
return;
|
||||
}
|
||||
if (!this.listenerAdded) {
|
||||
svgElem.addEventListener("animationstart", this.handleAnimationStart);
|
||||
this.listenerAdded = true;
|
||||
}
|
||||
const animArr = svgElem.getAnimations();
|
||||
if (animArr == null || animArr.length == 0) {
|
||||
return;
|
||||
}
|
||||
animArr[0].startTime = 0;
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
interface StatusIndicatorProps {
|
||||
/**
|
||||
* The level of the status indicator. This will determine the color of the status indicator.
|
||||
*/
|
||||
level: StatusIndicatorLevel;
|
||||
className?: string;
|
||||
/**
|
||||
* If true, a spinner will be shown around the status indicator.
|
||||
*/
|
||||
runningCommands?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* This component is used to show the status of a command. It will show a spinner around the status indicator if there are running commands. It will also delay showing the spinner for a short time to prevent flickering.
|
||||
*/
|
||||
@mobxReact.observer
|
||||
export class StatusIndicator extends React.Component<StatusIndicatorProps> {
|
||||
iconRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||
spinnerVisible: mobx.IObservableValue<boolean> = mobx.observable.box(false);
|
||||
timeout: NodeJS.Timeout;
|
||||
|
||||
clearSpinnerTimeout() {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
mobx.action(() => {
|
||||
this.spinnerVisible.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
/**
|
||||
* This will apply a delay after there is a running command before showing the spinner. This prevents flickering for commands that return quickly.
|
||||
*/
|
||||
updateMountCallback() {
|
||||
const runningCommands = this.props.runningCommands ?? false;
|
||||
if (runningCommands && !this.timeout) {
|
||||
this.timeout = setTimeout(
|
||||
mobx.action(() => {
|
||||
this.spinnerVisible.set(true);
|
||||
}),
|
||||
100
|
||||
);
|
||||
} else if (!runningCommands) {
|
||||
this.clearSpinnerTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
this.updateMountCallback();
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.updateMountCallback();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.clearSpinnerTimeout();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { level, className, runningCommands } = this.props;
|
||||
const spinnerVisible = this.spinnerVisible.get();
|
||||
let statusIndicator = null;
|
||||
if (level != StatusIndicatorLevel.None || spinnerVisible) {
|
||||
let indicatorLevelClass = null;
|
||||
switch (level) {
|
||||
case StatusIndicatorLevel.Output:
|
||||
indicatorLevelClass = "output";
|
||||
break;
|
||||
case StatusIndicatorLevel.Success:
|
||||
indicatorLevelClass = "success";
|
||||
break;
|
||||
case StatusIndicatorLevel.Error:
|
||||
indicatorLevelClass = "error";
|
||||
break;
|
||||
}
|
||||
|
||||
const spinnerVisibleClass = spinnerVisible ? "spinner-visible" : null;
|
||||
statusIndicator = (
|
||||
<CenteredIcon
|
||||
divRef={this.iconRef}
|
||||
className={cn(className, indicatorLevelClass, spinnerVisibleClass, "status-indicator")}
|
||||
>
|
||||
<SpinnerIndicator className={spinnerVisible ? "spin" : null} />
|
||||
</CenteredIcon>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<SyncSpin classRef={this.iconRef} shouldSync={runningCommands}>
|
||||
{statusIndicator}
|
||||
</SyncSpin>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class RotateIcon extends React.Component<{
|
||||
className?: string;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
}> {
|
||||
iconRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||
render() {
|
||||
return (
|
||||
<SyncSpin classRef={this.iconRef}>
|
||||
<RotateIconSvg className={this.props.className ?? ""} />
|
||||
</SyncSpin>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,12 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.import-edit-warning {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.name-actions-section {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
|
||||
@@ -58,6 +58,10 @@ class EditRemoteConnModal extends React.Component<{}, {}> {
|
||||
return this.selectedRemote?.local;
|
||||
}
|
||||
|
||||
isImportedRemote(): boolean {
|
||||
return this.selectedRemote?.sshconfigsrc == "sshconfig-import";
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
mobx.action(() => {
|
||||
this.tempAlias.set(this.selectedRemote?.remotealias);
|
||||
@@ -259,6 +263,27 @@ class EditRemoteConnModal extends React.Component<{}, {}> {
|
||||
);
|
||||
}
|
||||
|
||||
renderImportedRemoteEditWarning() {
|
||||
return (
|
||||
<div className="import-edit-warning">
|
||||
<Tooltip
|
||||
message={
|
||||
<span>
|
||||
Most options for connections imported from an ssh config file cannot be edited. For these
|
||||
changes, you must edit the config file and import it again. The shell preference can be
|
||||
edited, but will return to the default if you import again. It will stay changed if you
|
||||
follow <a href="https://docs.waveterm.dev/features/sshconfig-imports">this procedure</a>.
|
||||
</span>
|
||||
}
|
||||
icon={<i className="fa-sharp fa-regular fa-fw fa-triangle-exclamation" />}
|
||||
>
|
||||
<i className="fa-sharp fa-regular fa-fw fa-triangle-exclamation" />
|
||||
</Tooltip>
|
||||
SSH Config Import Behavior
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAuthMode() {
|
||||
let authMode = this.tempAuthMode.get();
|
||||
return (
|
||||
@@ -344,6 +369,7 @@ class EditRemoteConnModal extends React.Component<{}, {}> {
|
||||
return null;
|
||||
}
|
||||
let isLocal = this.isLocalRemote();
|
||||
let isImported = this.isImportedRemote();
|
||||
return (
|
||||
<Modal className="erconn-modal">
|
||||
<Modal.Header title="Edit Connection" onClose={this.model.closeModal} />
|
||||
@@ -351,9 +377,10 @@ class EditRemoteConnModal extends React.Component<{}, {}> {
|
||||
<div className="name-actions-section">
|
||||
<div className="name text-primary">{util.getRemoteName(this.selectedRemote)}</div>
|
||||
</div>
|
||||
<If condition={!isLocal}>{this.renderAlias()}</If>
|
||||
<If condition={!isLocal}>{this.renderAuthMode()}</If>
|
||||
<If condition={!isLocal}>{this.renderConnectMode()}</If>
|
||||
<If condition={!isLocal && !isImported}>{this.renderAlias()}</If>
|
||||
<If condition={!isLocal && !isImported}>{this.renderAuthMode()}</If>
|
||||
<If condition={!isLocal && !isImported}>{this.renderConnectMode()}</If>
|
||||
<If condition={isImported}>{this.renderImportedRemoteEditWarning()}</If>
|
||||
{this.renderShellPref()}
|
||||
<If condition={!util.isBlank(this.remoteEdit?.errorstr)}>
|
||||
<div className="settings-field settings-error">Error: {this.remoteEdit?.errorstr}</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@ import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg"
|
||||
import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg";
|
||||
import { ReactComponent as StatusCircleIcon } from "../../assets/icons/statuscircle.svg";
|
||||
|
||||
import "./modals.less";
|
||||
import "./settings.less";
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
|
||||
@@ -209,17 +209,6 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
|
||||
cancelInstallButton = <></>;
|
||||
}
|
||||
if (remote.sshconfigsrc == "sshconfig-import") {
|
||||
updateAuthButton = (
|
||||
<Button theme="secondary" disabled={true}>
|
||||
Edit
|
||||
<Tooltip
|
||||
message={`Connections imported from an ssh config file cannot be edited inside waveterm. To edit these, you must edit the config file and import it again.`}
|
||||
icon={<i className="fa-sharp fa-regular fa-fw fa-ban" />}
|
||||
>
|
||||
<i className="fa-sharp fa-regular fa-fw fa-ban" />
|
||||
</Tooltip>
|
||||
</Button>
|
||||
);
|
||||
archiveButton = (
|
||||
<Button theme="secondary" onClick={() => this.clickArchive()}>
|
||||
Delete
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user