mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
Merge remote-tracking branch 'origin/dev-0.5.0'
This commit is contained in:
commit
bda9943ed7
18
housekeeping/wave-migrate.sh
Normal file
18
housekeeping/wave-migrate.sh
Normal file
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d ~/prompt ]; then
|
||||
echo "~/prompt directory does not exist, will not migrate"
|
||||
exit 1;
|
||||
fi
|
||||
if [ -d ~/.wave ]; then
|
||||
echo "~/.wave directory already exists, will not migrate"
|
||||
exit 1;
|
||||
fi
|
||||
mv ~/prompt ~/.wave
|
||||
cd ~/.wave
|
||||
mv prompt.db wave.db
|
||||
mv prompt.db-wal wave.db-wal
|
||||
mv prompt.db-shm wave.db-shm
|
||||
mv prompt.authkey wave.authkey
|
||||
|
||||
|
6
public/fontawesome/css/brands.min.css
vendored
Normal file
6
public/fontawesome/css/brands.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
9
public/fontawesome/css/fontawesome.min.css
vendored
Normal file
9
public/fontawesome/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
6
public/fontawesome/css/sharp-regular.min.css
vendored
Normal file
6
public/fontawesome/css/sharp-regular.min.css
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
* Copyright 2023 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-sharp:"Font Awesome 6 Sharp";--fa-font-sharp-regular:normal 400 1em/1 "Font Awesome 6 Sharp"}@font-face{font-family:"Font Awesome 6 Sharp";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-sharp-regular-400.woff2) format("woff2"),url(../webfonts/fa-sharp-regular-400.ttf) format("truetype")}.fa-regular,.fasr{font-weight:400}
|
6
public/fontawesome/css/sharp-solid.min.css
vendored
Normal file
6
public/fontawesome/css/sharp-solid.min.css
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
* Copyright 2023 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-sharp:"Font Awesome 6 Sharp";--fa-font-sharp-solid:normal 900 1em/1 "Font Awesome 6 Sharp"}@font-face{font-family:"Font Awesome 6 Sharp";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-sharp-solid-900.woff2) format("woff2"),url(../webfonts/fa-sharp-solid-900.ttf) format("truetype")}.fa-solid,.fass{font-weight:900}
|
6
public/fontawesome/css/solid.min.css
vendored
Normal file
6
public/fontawesome/css/solid.min.css
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
* Copyright 2023 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-classic:"Font Awesome 6 Pro";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Pro"}@font-face{font-family:"Font Awesome 6 Pro";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}
|
BIN
public/fontawesome/webfonts/fa-brands-400.woff2
Normal file
BIN
public/fontawesome/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
public/fontawesome/webfonts/fa-sharp-regular-400.woff2
Normal file
BIN
public/fontawesome/webfonts/fa-sharp-regular-400.woff2
Normal file
Binary file not shown.
BIN
public/fontawesome/webfonts/fa-sharp-solid-900.woff2
Normal file
BIN
public/fontawesome/webfonts/fa-sharp-solid-900.woff2
Normal file
Binary file not shown.
BIN
public/fontawesome/webfonts/fa-solid-900.woff2
Normal file
BIN
public/fontawesome/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
public/fonts/fontawesome-webfont-4.7.woff2
Normal file
BIN
public/fonts/fontawesome-webfont-4.7.woff2
Normal file
Binary file not shown.
@ -5,6 +5,10 @@
|
||||
<base href="../" />
|
||||
<script charset="UTF-8" src="dist-dev/waveterm.js"></script>
|
||||
<link rel="stylesheet" href="public/bulma-0.9.4.min.css" />
|
||||
<link rel="stylesheet" href="public/fontawesome/css/fontawesome.min.css">
|
||||
<link rel="stylesheet" href="public/fontawesome/css/brands.min.css">
|
||||
<link rel="stylesheet" href="public/fontawesome/css/sharp-solid.min.css">
|
||||
<link rel="stylesheet" href="public/fontawesome/css/sharp-regular.min.css">
|
||||
<link rel="stylesheet" href="dist-dev/waveterm.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -5,6 +5,10 @@
|
||||
<base href="../" />
|
||||
<script charset="UTF-8" src="dist/waveterm.js"></script>
|
||||
<link rel="stylesheet" href="public/bulma-0.9.4.min.css" />
|
||||
<link rel="stylesheet" href="public/fontawesome/css/fontawesome.min.css">
|
||||
<link rel="stylesheet" href="public/fontawesome/css/brands.min.css">
|
||||
<link rel="stylesheet" href="public/fontawesome/css/sharp-solid.min.css">
|
||||
<link rel="stylesheet" href="public/fontawesome/css/sharp-regular.min.css">
|
||||
<link rel="stylesheet" href="dist/waveterm.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -27,7 +27,7 @@ node_modules/.bin/electron-rebuild
|
||||
```bash
|
||||
# @scripthaus command electron
|
||||
# @scripthaus cd :playbook
|
||||
PROMPT_DEV=1 PCLOUD_ENDPOINT="https://ot2e112zx5.execute-api.us-west-2.amazonaws.com/dev" node_modules/.bin/electron dist-dev/emain.js
|
||||
WAVETERM_DEV=1 PCLOUD_ENDPOINT="https://ot2e112zx5.execute-api.us-west-2.amazonaws.com/dev" node_modules/.bin/electron dist-dev/emain.js
|
||||
```
|
||||
|
||||
```bash
|
||||
@ -60,8 +60,7 @@ node_modules/.bin/electron-forge make
|
||||
rm -rf dist/
|
||||
rm -rf bin/
|
||||
rm -rf build/
|
||||
node_modules/.bin/webpack --config webpack.prod.js
|
||||
node_modules/.bin/webpack --config webpack.electron.prod.js
|
||||
node_modules/.bin/webpack --env prod
|
||||
GO_LDFLAGS="-s -w -X main.BuildTime=$(date +'%Y%m%d%H%M')"
|
||||
(cd waveshell; GOOS=darwin GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-darwin.amd64 main-waveshell.go)
|
||||
(cd waveshell; GOOS=darwin GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-darwin.arm64 main-waveshell.go)
|
||||
@ -113,22 +112,6 @@ rm *.dmg
|
||||
"out/Wave-darwin-arm64/Wave.app"
|
||||
```
|
||||
|
||||
```bash
|
||||
# @scripthaus command sync-webshare-dev
|
||||
# @scripthaus cd :playbook
|
||||
# no-cache for dev
|
||||
aws --profile prompt-s3 s3 sync webshare/static s3://prompt-devshare-static/static --cache-control 'no-cache'
|
||||
aws --profile prompt-s3 s3 sync webshare/dist-dev s3://prompt-devshare-static/dist-dev --cache-control 'no-cache'
|
||||
```
|
||||
|
||||
```bash
|
||||
# @scripthaus command sync-webshare
|
||||
# @scripthaus cd :playbook
|
||||
# no-cache for dev
|
||||
aws --profile prompt-s3 s3 sync webshare/static s3://prompt-share-static/static --cache-control 'no-cache'
|
||||
aws --profile prompt-s3 s3 sync webshare/dist s3://prompt-share-static/dist --cache-control 'no-cache'
|
||||
```
|
||||
|
||||
```bash
|
||||
# @scripthaus command build-wavesrv
|
||||
cd wavesrv
|
||||
|
@ -84,6 +84,10 @@ body code {
|
||||
font-family: @terminal-font;
|
||||
}
|
||||
|
||||
body code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
svg.icon {
|
||||
fill: @base-color;
|
||||
width: 100%;
|
||||
@ -94,20 +98,11 @@ svg.icon {
|
||||
.hoverEffect {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 2px 2px 0 rgba(255, 255, 255, 0.1), 0px 4px 5px 0 rgba(255, 255, 255, 0.2),
|
||||
0px 0px 5px 2.5px rgba(255, 255, 255, 0.5);
|
||||
transition: box-shadow 0.2s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.hover-effect-base:hover {
|
||||
cursor: pointer;
|
||||
|
||||
.hover-effect-target {
|
||||
box-shadow: 0px 2px 2px 0 rgba(255, 255, 255, 0.1), 0px 4px 5px 0 rgba(255, 255, 255, 0.2),
|
||||
0px 0px 5px 2.5px rgba(255, 255, 255, 0.5);
|
||||
transition: box-shadow 0.2s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.hideScrollbarUntillHover {
|
||||
@ -500,9 +495,9 @@ a.a-block {
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-magenta {
|
||||
.icon.color-mint {
|
||||
path, circle {
|
||||
fill: @tab-magenta;
|
||||
fill: @tab-mint;
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,3 +542,7 @@ a.a-block {
|
||||
fill: @status-error;
|
||||
}
|
||||
}
|
||||
|
||||
.unselectable {
|
||||
user-select: none;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import {
|
||||
import { RemotesModal } from "./connections/connections";
|
||||
import { TosModal } from "./common/modals/modals";
|
||||
import { MainSideBar } from "./sidebar/MainSideBar";
|
||||
import { DisconnectedModal, ClientStopModal, AlertModal, WelcomeModal } from "./common/modals/modals";
|
||||
import { DisconnectedModal, ClientStopModal, AlertModal, AboutModal } from "./common/modals/modals";
|
||||
import { ErrorBoundary } from "./common/error/errorboundary";
|
||||
import "./app.less";
|
||||
|
||||
@ -124,8 +124,8 @@ class App extends React.Component<{}, {}> {
|
||||
<If condition={GlobalModel.needsTos()}>
|
||||
<TosModal />
|
||||
</If>
|
||||
<If condition={GlobalModel.welcomeModalOpen.get()}>
|
||||
<WelcomeModal />
|
||||
<If condition={GlobalModel.aboutModalOpen.get()}>
|
||||
<AboutModal />
|
||||
</If>
|
||||
<If condition={screenSettingsModal != null}>
|
||||
<ScreenSettingsModal
|
||||
|
6
src/app/assets/icons/checked-checkbox.svg
Normal file
6
src/app/assets/icons/checked-checkbox.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Checkbox">
|
||||
<rect width="16" height="16" rx="4" fill="#58C142"/>
|
||||
<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M12.1757 4.76285C12.5828 5.13604 12.6104 5.76861 12.2372 6.17573L7.79324 11.0236C7.19873 11.6722 6.17628 11.6722 5.58177 11.0236L3.76285 9.03937C3.38966 8.63225 3.41716 7.99968 3.82428 7.62649C4.2314 7.2533 4.86397 7.2808 5.23716 7.68792L6.68751 9.27011L10.7629 4.82428C11.136 4.41716 11.7686 4.38966 12.1757 4.76285Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 574 B |
5
src/app/assets/icons/close.svg
Normal file
5
src/app/assets/icons/close.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="cancel">
|
||||
<path id="Vector" d="M13.0261 2.96991C13.319 3.26281 13.319 3.73768 13.0261 4.03057L9.05864 7.998L13.0261 11.9654C13.3189 12.2583 13.3189 12.7332 13.0261 13.0261C12.7332 13.319 12.2583 13.319 11.9654 13.0261L7.99798 9.05866L4.03059 13.0261C3.73769 13.3189 3.26282 13.3189 2.96993 13.0261C2.67703 12.7332 2.67703 12.2583 2.96993 11.9654L6.93732 7.998L2.96991 4.03059C2.67702 3.7377 2.67702 3.26282 2.96991 2.96993C3.26281 2.67704 3.73768 2.67704 4.03057 2.96993L7.99798 6.93734L11.9654 2.96991C12.2583 2.67702 12.7332 2.67702 13.0261 2.96991Z" fill="#C3C8C2"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 684 B |
@ -1,367 +0,0 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { If, For } from "tsx-control-statements/components";
|
||||
import cn from "classnames";
|
||||
import dayjs from "dayjs";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { GlobalModel, GlobalCommandRunner } from "../../../model/model";
|
||||
import { Markdown } from "../common";
|
||||
import * as util from "../../../util/util";
|
||||
|
||||
import { ReactComponent as XmarkIcon } from "../../assets/icons/line/xmark.svg";
|
||||
import { ReactComponent as WarningIcon } from "../../assets/icons/line/triangle-exclamation.svg";
|
||||
import { ReactComponent as ShieldCheck } from "../../assets/icons/line/shield_check.svg";
|
||||
import { ReactComponent as Help } from "../../assets/icons/line/help_filled.svg";
|
||||
import { ReactComponent as Github } from "../../assets/icons/line/github.svg";
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
@mobxReact.observer
|
||||
class DisconnectedModal extends React.Component<{}, {}> {
|
||||
logRef: any = React.createRef();
|
||||
showLog: mobx.IObservableValue<boolean> = mobx.observable.box(false);
|
||||
|
||||
@boundMethod
|
||||
restartServer() {
|
||||
GlobalModel.restartWaveSrv();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
tryReconnect() {
|
||||
GlobalModel.ws.connectNow("manual");
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.logRef.current != null) {
|
||||
this.logRef.current.scrollTop = this.logRef.current.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.logRef.current != null) {
|
||||
this.logRef.current.scrollTop = this.logRef.current.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleShowLog(): void {
|
||||
mobx.action(() => {
|
||||
this.showLog.set(!this.showLog.get());
|
||||
})();
|
||||
}
|
||||
|
||||
render() {
|
||||
let model = GlobalModel;
|
||||
let logLine: string = null;
|
||||
let idx: number = 0;
|
||||
return (
|
||||
<div className="prompt-modal disconnected-modal modal is-active">
|
||||
<div className="modal-background"></div>
|
||||
<div className="modal-content">
|
||||
<div className="message-header">
|
||||
<div className="modal-title">Prompt Client Disconnected</div>
|
||||
</div>
|
||||
<If condition={this.showLog.get()}>
|
||||
<div className="inner-content">
|
||||
<div className="ws-log" ref={this.logRef}>
|
||||
<For each="logLine" index="idx" of={GlobalModel.ws.wsLog}>
|
||||
<div key={idx} className="ws-logline">
|
||||
{logLine}
|
||||
</div>
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
<footer>
|
||||
<div className="footer-text-link" style={{ marginLeft: 10 }} onClick={this.handleShowLog}>
|
||||
<If condition={!this.showLog.get()}>
|
||||
<i className="fa-sharp fa-solid fa-plus" /> Show Log
|
||||
</If>
|
||||
<If condition={this.showLog.get()}>
|
||||
<i className="fa-sharp fa-solid fa-minus" /> Hide Log
|
||||
</If>
|
||||
</div>
|
||||
<div className="flex-spacer" />
|
||||
<button onClick={this.tryReconnect} className="button">
|
||||
<span className="icon">
|
||||
<i className="fa-sharp fa-solid fa-rotate" />
|
||||
</span>
|
||||
<span>Try Reconnect</span>
|
||||
</button>
|
||||
<button onClick={this.restartServer} className="button is-danger" style={{ marginLeft: 10 }}>
|
||||
<WarningIcon className="icon" />
|
||||
<span>Restart Server</span>
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class ClientStopModal extends React.Component<{}, {}> {
|
||||
@boundMethod
|
||||
refreshClient() {
|
||||
GlobalModel.refreshClient();
|
||||
}
|
||||
|
||||
render() {
|
||||
let model = GlobalModel;
|
||||
let cdata = model.clientData.get();
|
||||
let title = "Client Not Ready";
|
||||
return (
|
||||
<div className="prompt-modal client-stop-modal modal is-active">
|
||||
<div className="modal-background"></div>
|
||||
<div className="modal-content">
|
||||
<div className="message-header">
|
||||
<div className="modal-title">[prompt] {title}</div>
|
||||
</div>
|
||||
<div className="inner-content">
|
||||
<If condition={cdata == null}>
|
||||
<div>Cannot get client data.</div>
|
||||
</If>
|
||||
</div>
|
||||
<footer>
|
||||
<button onClick={this.refreshClient} className="button">
|
||||
<span className="icon">
|
||||
<i className="fa-sharp fa-solid fa-rotate" />
|
||||
</span>
|
||||
<span>Hard Refresh Client</span>
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class LoadingSpinner extends React.Component<{}, {}> {
|
||||
render() {
|
||||
return (
|
||||
<div className="loading-spinner">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class AlertModal extends React.Component<{}, {}> {
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
GlobalModel.cancelAlert();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleOK(): void {
|
||||
GlobalModel.confirmAlert();
|
||||
}
|
||||
|
||||
render() {
|
||||
let message = GlobalModel.alertMessage.get();
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
let title = message.title ?? (message.confirm ? "Confirm" : "Alert");
|
||||
let isConfirm = message.confirm;
|
||||
return (
|
||||
<div className="modal prompt-modal wave-modal is-active alert-modal">
|
||||
<div className="modal-background" />
|
||||
<div className="modal-content">
|
||||
<header>
|
||||
<p className="modal-title">
|
||||
<WarningIcon className="icon" />
|
||||
{title}
|
||||
</p>
|
||||
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
||||
<XmarkIcon />
|
||||
</div>
|
||||
</header>
|
||||
<If condition={message.markdown}>
|
||||
<Markdown text={message.message} extraClassName="inner-content" />
|
||||
</If>
|
||||
<If condition={!message.markdown}>
|
||||
<div className="inner-content content">
|
||||
<p>{message.message}</p>
|
||||
</div>
|
||||
</If>
|
||||
<footer>
|
||||
<If condition={isConfirm}>
|
||||
<div onClick={this.closeModal} className="button is-prompt-cancel is-outlined is-small">
|
||||
Cancel
|
||||
</div>
|
||||
<div onClick={this.handleOK} className="button is-prompt-green is-outlined is-small">
|
||||
OK
|
||||
</div>
|
||||
</If>
|
||||
<If condition={!isConfirm}>
|
||||
<div onClick={this.handleOK} className="button is-prompt-green is-small">
|
||||
OK
|
||||
</div>
|
||||
</If>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class WelcomeModal extends React.Component<{}, {}> {
|
||||
totalPages: number = 3;
|
||||
pageNum: OV<number> = mobx.observable.box(1, { name: "welcome-pagenum" });
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.welcomeModalOpen.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
goNext(): void {
|
||||
mobx.action(() => {
|
||||
this.pageNum.set(this.pageNum.get() + 1);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
goPrev(): void {
|
||||
mobx.action(() => {
|
||||
this.pageNum.set(this.pageNum.get() - 1);
|
||||
})();
|
||||
}
|
||||
|
||||
renderDot(num: number): any {
|
||||
if (num == this.pageNum.get()) {
|
||||
return <i key={String(num)} className="fa-sharp fa-solid fa-circle" />;
|
||||
}
|
||||
return <i key={String(num)} className="fa-sharp fa-regular fa-circle" />;
|
||||
}
|
||||
|
||||
renderDots(): any {
|
||||
let elems: any = [];
|
||||
for (let i = 1; i <= this.totalPages; i++) {
|
||||
let elem = this.renderDot(i);
|
||||
elems.push(elem);
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
|
||||
render() {
|
||||
let pageNum = this.pageNum.get();
|
||||
return (
|
||||
<div className={cn("modal welcome-modal prompt-modal is-active")}>
|
||||
<div className="modal-background" onClick={this.closeModal} />
|
||||
<div className="modal-content">
|
||||
<header>
|
||||
<div className="modal-title">welcome to [prompt]</div>
|
||||
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
||||
<XmarkIcon />
|
||||
</div>
|
||||
</header>
|
||||
<div className={cn("inner-content content", { "is-hidden": pageNum != 1 })}>
|
||||
<p>
|
||||
Prompt is a new terminal to help save you time and keep your command-line life organized.
|
||||
Here's a couple quick tips to get your started!
|
||||
</p>
|
||||
</div>
|
||||
<footer>
|
||||
<If condition={pageNum > 1}>
|
||||
<button className={cn("button is-dark prev-button is-small")} onClick={this.goPrev}>
|
||||
<span className="icon is-small">
|
||||
<i className="fa-sharp fa-regular fa-angle-left" />
|
||||
</span>
|
||||
<span>Prev</span>
|
||||
</button>
|
||||
</If>
|
||||
<If condition={pageNum == 1}>
|
||||
<div className="prev-spacer" />
|
||||
</If>
|
||||
<div className="flex-spacer" />
|
||||
<div className="dots">{this.renderDots()}</div>
|
||||
<div className="flex-spacer" />
|
||||
<If condition={pageNum < this.totalPages}>
|
||||
<button className="button is-dark next-button is-small" onClick={this.goNext}>
|
||||
<span>Next</span>
|
||||
<span className="icon is-small">
|
||||
<i className="fa-sharp fa-regular fa-angle-right" />
|
||||
</span>
|
||||
</button>
|
||||
</If>
|
||||
<If condition={pageNum == this.totalPages}>
|
||||
<button className="button is-dark next-button is-small" onClick={this.closeModal}>
|
||||
<span>Done</span>
|
||||
</button>
|
||||
</If>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class TosModal extends React.Component<{}, {}> {
|
||||
@boundMethod
|
||||
acceptTos(): void {
|
||||
GlobalCommandRunner.clientAcceptTos();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={cn("modal tos-modal wave-modal is-active")}>
|
||||
<div className="modal-background" />
|
||||
<div className="modal-content">
|
||||
<div className="modal-content-wrapper">
|
||||
<header>
|
||||
<div className="modal-title">Welcome to Wave Terminal!</div>
|
||||
<div className="modal-subtitle">Lets set everything for you</div>
|
||||
</header>
|
||||
<div className="content">
|
||||
<div className="item">
|
||||
<ShieldCheck />
|
||||
<div className="item-inner">
|
||||
<div className="item-title">Telemetry</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="item">
|
||||
<Help />
|
||||
<div className="item-inner"></div>
|
||||
</div>
|
||||
<div className="item">
|
||||
<Github />
|
||||
<div className="item-inner"></div>
|
||||
</div>
|
||||
{/* <p>
|
||||
<a target="_blank" href={util.makeExternLink("https://www.commandline.dev/tos")}>
|
||||
Full Terms of Service
|
||||
</a>
|
||||
</p> */}
|
||||
</div>
|
||||
<footer>
|
||||
<div className="flex-spacer" />
|
||||
<div onClick={this.acceptTos} className="button is-prompt-green is-outlined is-small">
|
||||
Accept Terms of Service
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { WelcomeModal, LoadingSpinner, ClientStopModal, AlertModal, DisconnectedModal, TosModal };
|
29
src/app/assets/waveterm-logo-with-bg.svg
Normal file
29
src/app/assets/waveterm-logo-with-bg.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<svg width="74" height="73" viewBox="0 0 74 73" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon Center Image Artwork">
|
||||
<g id="Guide - Hide">
|
||||
<rect x="1" width="44.7379" height="44.7379" fill="#EA33EC" fill-opacity="0.1"/>
|
||||
<rect x="3.18447" y="2.18447" width="40.3689" height="40.3689" stroke="#EA33EC" stroke-opacity="0.09" stroke-width="4.36893"/>
|
||||
</g>
|
||||
<g id="wave-logo_appicon 1">
|
||||
<path id="Vector" d="M73.8156 -0.808594H0.191406V72.8156H73.8156V-0.808594Z" fill="black"/>
|
||||
<g id="Group">
|
||||
<g id="Group_2">
|
||||
<path id="Vector_2" d="M27.2969 34.6218C24.7516 34.6218 23.3523 36.2741 22.5859 39.9726L11.125 38.3132C12.5242 26.2194 17.2352 20.3624 26.5305 20.3624C33.407 20.3624 40.157 25.5866 43.4688 25.5866C46.0141 25.5866 47.4133 23.8007 48.1797 20.2358L59.6406 21.8882C58.368 33.9819 53.5305 39.846 44.2352 39.846C37.225 39.846 30.7352 34.6218 27.2969 34.6218Z" fill="url(#paint0_linear_1373_32879)"/>
|
||||
</g>
|
||||
<g id="Group_3">
|
||||
<path id="Vector_3" d="M30.5312 46.4133C27.9859 46.4133 26.5867 48.0656 25.8203 51.7641L14.3594 50.1047C15.7586 38.0109 20.4695 32.1539 29.7648 32.1539C36.6414 32.1539 43.3914 37.3781 46.7031 37.3781C49.2484 37.3781 50.6477 35.5922 51.4141 32.0273L62.875 33.6797C61.6023 45.7734 56.7648 51.6375 47.4695 51.6375C40.4664 51.6375 33.9695 46.4133 30.5312 46.4133Z" fill="url(#paint1_linear_1373_32879)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1373_32879" x1="11.1241" y1="30.1035" x2="59.6372" y2="30.1035" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.1418" stop-color="#1F4D22"/>
|
||||
<stop offset="0.8656" stop-color="#418D31"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1373_32879" x1="14.3628" y1="41.8964" x2="62.8759" y2="41.8964" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.2223" stop-color="#418D31"/>
|
||||
<stop offset="0.7733" stop-color="#58C142"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
@ -258,14 +258,12 @@
|
||||
}
|
||||
|
||||
.button.is-wave-green {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
padding: 6px 16px !important;
|
||||
color: @term-bright-white !important;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 6px !important;
|
||||
font-size: 14px !important;
|
||||
height: auto !important;
|
||||
background: @term-green !important;
|
||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
||||
@ -626,3 +624,45 @@
|
||||
fill: #cc0000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.inline-edit {
|
||||
.icon {
|
||||
display: inline;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-left: 1em;
|
||||
vertical-align: middle;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&.edit-not-active {
|
||||
cursor: pointer;
|
||||
|
||||
i.fa-pen {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dotted;
|
||||
}
|
||||
}
|
||||
|
||||
&.edit-active {
|
||||
input.input {
|
||||
padding: 0;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,8 @@ import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
|
||||
import { ReactComponent as CopyIcon } from "../assets/icons/history/copy.svg";
|
||||
import { ReactComponent as CircleIcon } from "../assets/icons/circle.svg";
|
||||
import { ReactComponent as KeyIcon } from "../assets/icons/key.svg";
|
||||
import { ReactComponent as XMarkIcon } from "../assets/icons/line/xmark.svg";
|
||||
import { ReactComponent as RotateIcon } from "../assets/icons/rotate_left.svg";
|
||||
import { ReactComponent as CircleInfoIcon } from "../assets/icons/circle_info.svg";
|
||||
import { ReactComponent as PenIcon } from "../assets/icons/favourites/pen.svg";
|
||||
|
||||
import "./common.less";
|
||||
|
||||
@ -243,7 +241,7 @@ class InlineSettingsTextEdit extends React.Component<
|
||||
title="Cancel (Esc)"
|
||||
className="button is-prompt-danger is-outlined is-small"
|
||||
>
|
||||
<XMarkIcon className="icon" />
|
||||
<span className="icon is-small"><i className="fa-sharp fa-solid fa-xmark"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="control">
|
||||
@ -252,7 +250,7 @@ class InlineSettingsTextEdit extends React.Component<
|
||||
title="Confirm (Enter)"
|
||||
className="button is-prompt-green is-outlined is-small"
|
||||
>
|
||||
<CheckIcon className="icon" />
|
||||
<span className="icon is-small"><i className="fa-sharp fa-solid fa-check"/></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -263,7 +261,7 @@ class InlineSettingsTextEdit extends React.Component<
|
||||
<div onClick={this.clickEdit} className={cn("settings-input inline-edit", "edit-not-active")}>
|
||||
{this.props.text}
|
||||
<If condition={this.props.showIcon}>
|
||||
<PenIcon className="icon" />
|
||||
<i className="fa-sharp fa-solid fa-pen"/>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
|
@ -184,10 +184,9 @@
|
||||
.modal.wave-modal {
|
||||
.modal-content {
|
||||
display: flex;
|
||||
padding: 32px 48px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
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),
|
||||
@ -201,28 +200,26 @@
|
||||
width: 100%;
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--sizing-sm, 12px);
|
||||
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);
|
||||
|
||||
.modal-title {
|
||||
color: @term-bright-white;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.modal-subtitle {
|
||||
color: @term-white;
|
||||
text-align: center;
|
||||
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: 20px;
|
||||
.close-icon-wrapper {
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,40 +227,77 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 32px;
|
||||
gap: 24px;
|
||||
width: 87%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal.tos-modal {
|
||||
.modal-content.tos-modal-content {
|
||||
padding: 32px 48px;
|
||||
gap: 8px;
|
||||
|
||||
header.tos-header {
|
||||
flex-direction: column;
|
||||
gap: var(--sizing-sm, 12px);
|
||||
border-bottom: none;
|
||||
padding: 0;
|
||||
|
||||
.modal-title {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.modal-subtitle {
|
||||
color: @term-white;
|
||||
text-align: center;
|
||||
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.content.tos-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 32px;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
.item {
|
||||
.item-inner {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
flex: 1 0 0;
|
||||
|
||||
.item-inner {
|
||||
.item-title {
|
||||
color: @term-bright-white;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
color: @term-white;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.item-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
flex: 1 0 0;
|
||||
|
||||
.item-title {
|
||||
color: @term-bright-white;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
color: @term-white;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.item-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -276,6 +310,11 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
button {
|
||||
font-size: 12.5px !important;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
button.disabled-button {
|
||||
cursor: default;
|
||||
}
|
||||
@ -284,6 +323,156 @@
|
||||
}
|
||||
}
|
||||
|
||||
.modal.about-modal {
|
||||
.modal-content.about-modal-content {
|
||||
width: 401px;
|
||||
|
||||
.about-content {
|
||||
margin-bottom: 0;
|
||||
|
||||
section {
|
||||
.logo-wrapper {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
flex-shrink: 0;
|
||||
|
||||
img {
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.text-wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
|
||||
div:first-child {
|
||||
color: @term-bright-white;
|
||||
font-size: 14.5px;
|
||||
}
|
||||
|
||||
div:last-child {
|
||||
color: @term-white;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
|
||||
i {
|
||||
font-size: 16px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
div:first-child + div {
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
.status.updated {
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
|
||||
i {
|
||||
color: @term-green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status.outdated {
|
||||
div {
|
||||
i {
|
||||
color: @term-yellow;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section:nth-child(3) {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
|
||||
.button-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
i {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section:last-child {
|
||||
margin-bottom: 24px;
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
padding: 6px 16px;
|
||||
align-items: center;
|
||||
gap: var(--sizing-2-xs, 4px);
|
||||
border-radius: 6px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
&:hover {
|
||||
color: @term-bright-white;
|
||||
}
|
||||
}
|
||||
|
||||
.button.color-standard {
|
||||
color: @term-white;
|
||||
background: var(--overlays-white-6, rgba(255, 255, 255, 0.12));
|
||||
|
||||
&:hover {
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
.button-link {
|
||||
display: flex;
|
||||
padding: 6px 16px;
|
||||
align-items: center;
|
||||
gap: var(--sizing-2-xs, 4px);
|
||||
border-radius: 6px;
|
||||
background: var(--overlays-white-6, rgba(255, 255, 255, 0.12));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal.welcome-modal {
|
||||
footer {
|
||||
.prev-button {
|
||||
@ -366,29 +555,6 @@
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
&.inline-edit {
|
||||
.icon {
|
||||
display: inline;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-left: 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&.inline-edit.edit-not-active {
|
||||
cursor: pointer;
|
||||
|
||||
i.fa-pen {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dotted;
|
||||
}
|
||||
}
|
||||
|
||||
&.settings-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -435,8 +601,8 @@
|
||||
.tab-color-icon.color-blue path {
|
||||
fill: @tab-blue;
|
||||
}
|
||||
.tab-color-icon.color-magenta path {
|
||||
fill: @tab-magenta;
|
||||
.tab-color-icon.color-mint path {
|
||||
fill: @tab-mint;
|
||||
}
|
||||
.tab-color-icon.color-cyan path {
|
||||
fill: @tab-cyan;
|
||||
|
@ -15,14 +15,19 @@ import * as util from "../../../util/util";
|
||||
import { Toggle, Checkbox } from "../common";
|
||||
import { ClientDataType } from "../../../types/types";
|
||||
|
||||
import { ReactComponent as XmarkIcon } from "../../assets/icons/line/xmark.svg";
|
||||
import close from "../../assets/icons/close.svg";
|
||||
import { ReactComponent as WarningIcon } from "../../assets/icons/line/triangle-exclamation.svg";
|
||||
import { ReactComponent as XmarkIcon } from "../../assets/icons/line/xmark.svg";
|
||||
import shield from "../../assets/icons/shield_check.svg";
|
||||
import help from "../../assets/icons/help_filled.svg";
|
||||
import github from "../../assets/icons/github.svg";
|
||||
import logo from "../../assets/waveterm-logo-with-bg.svg";
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
|
||||
// @ts-ignore
|
||||
const VERSION = __WAVETERM_VERSION__;
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
@mobxReact.observer
|
||||
@ -68,7 +73,7 @@ class DisconnectedModal extends React.Component<{}, {}> {
|
||||
<div className="modal-background"></div>
|
||||
<div className="modal-content">
|
||||
<div className="message-header">
|
||||
<div className="modal-title">Prompt Client Disconnected</div>
|
||||
<div className="modal-title">Wave Client Disconnected</div>
|
||||
</div>
|
||||
<If condition={this.showLog.get()}>
|
||||
<div className="inner-content">
|
||||
@ -124,7 +129,7 @@ class ClientStopModal extends React.Component<{}, {}> {
|
||||
<div className="modal-background"></div>
|
||||
<div className="modal-content">
|
||||
<div className="message-header">
|
||||
<div className="modal-title">[prompt] {title}</div>
|
||||
<div className="modal-title">{title}</div>
|
||||
</div>
|
||||
<div className="inner-content">
|
||||
<If condition={cdata == null}>
|
||||
@ -220,101 +225,6 @@ class AlertModal extends React.Component<{}, {}> {
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class WelcomeModal extends React.Component<{}, {}> {
|
||||
totalPages: number = 3;
|
||||
pageNum: OV<number> = mobx.observable.box(1, { name: "welcome-pagenum" });
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.welcomeModalOpen.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
goNext(): void {
|
||||
mobx.action(() => {
|
||||
this.pageNum.set(this.pageNum.get() + 1);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
goPrev(): void {
|
||||
mobx.action(() => {
|
||||
this.pageNum.set(this.pageNum.get() - 1);
|
||||
})();
|
||||
}
|
||||
|
||||
renderDot(num: number): any {
|
||||
if (num == this.pageNum.get()) {
|
||||
return <i key={String(num)} className="fa-sharp fa-solid fa-circle" />;
|
||||
}
|
||||
return <i key={String(num)} className="fa-sharp fa-regular fa-circle" />;
|
||||
}
|
||||
|
||||
renderDots(): any {
|
||||
let elems: any = [];
|
||||
for (let i = 1; i <= this.totalPages; i++) {
|
||||
let elem = this.renderDot(i);
|
||||
elems.push(elem);
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
|
||||
render() {
|
||||
let pageNum = this.pageNum.get();
|
||||
return (
|
||||
<div className={cn("modal welcome-modal prompt-modal is-active")}>
|
||||
<div className="modal-background" onClick={this.closeModal} />
|
||||
<div className="modal-content">
|
||||
<header>
|
||||
<div className="modal-title">welcome to [prompt]</div>
|
||||
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
||||
<XmarkIcon />
|
||||
</div>
|
||||
</header>
|
||||
<div className={cn("inner-content content", { "is-hidden": pageNum != 1 })}>
|
||||
<p>
|
||||
Prompt is a new terminal to help save you time and keep your command-line life organized.
|
||||
Here's a couple quick tips to get your started!
|
||||
</p>
|
||||
</div>
|
||||
<footer>
|
||||
<If condition={pageNum > 1}>
|
||||
<button className={cn("button is-dark prev-button is-small")} onClick={this.goPrev}>
|
||||
<span className="icon is-small">
|
||||
<i className="fa-sharp fa-regular fa-angle-left" />
|
||||
</span>
|
||||
<span>Prev</span>
|
||||
</button>
|
||||
</If>
|
||||
<If condition={pageNum == 1}>
|
||||
<div className="prev-spacer" />
|
||||
</If>
|
||||
<div className="flex-spacer" />
|
||||
<div className="dots">{this.renderDots()}</div>
|
||||
<div className="flex-spacer" />
|
||||
<If condition={pageNum < this.totalPages}>
|
||||
<button className="button is-dark next-button is-small" onClick={this.goNext}>
|
||||
<span>Next</span>
|
||||
<span className="icon is-small">
|
||||
<i className="fa-sharp fa-regular fa-angle-right" />
|
||||
</span>
|
||||
</button>
|
||||
</If>
|
||||
<If condition={pageNum == this.totalPages}>
|
||||
<button className="button is-dark next-button is-small" onClick={this.closeModal}>
|
||||
<span>Done</span>
|
||||
</button>
|
||||
</If>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class TosModal extends React.Component<{}, {}> {
|
||||
state = {
|
||||
@ -346,62 +256,68 @@ class TosModal extends React.Component<{}, {}> {
|
||||
return (
|
||||
<div className={cn("modal tos-modal wave-modal is-active")}>
|
||||
<div className="modal-background" />
|
||||
<div className="modal-content">
|
||||
<div className="modal-content tos-modal-content">
|
||||
<div className="modal-content-wrapper">
|
||||
<header>
|
||||
<header className="tos-header unselectable">
|
||||
<div className="modal-title">Welcome to Wave Terminal!</div>
|
||||
<div className="modal-subtitle">Lets set everything for you</div>
|
||||
</header>
|
||||
<div className="content">
|
||||
<div className="content tos-content unselectable">
|
||||
<div className="item">
|
||||
<img src={shield} alt="Privacy" />
|
||||
<div className="item-inner">
|
||||
<div className="item-title">Telemetry</div>
|
||||
<div className="item-text">
|
||||
We don’t collect any personal info, only crash logs and IP address to make Wave
|
||||
better. If you like, you can disable telemetry now or late.
|
||||
We only collect minimal <i>anonymous</i> telemetry data to help us
|
||||
understand how many people are using Wave.
|
||||
</div>
|
||||
<div className="item-field">
|
||||
<div className="item-field" style={{marginTop: 2}}>
|
||||
<Toggle
|
||||
checked={!cdata.clientopts.notelemetry}
|
||||
onChange={this.handleChangeTelemetry}
|
||||
/>
|
||||
<div className="item-label">Basic Telemetry</div>
|
||||
<div className="item-label">Telemetry {cdata.clientopts.notelemetry ? "Disabled" : "Enabled"}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="item">
|
||||
<img src={help} alt="Help" />
|
||||
<a target="_blank" href={util.makeExternLink("https://discord.gg/XfvZ334gwU")}>
|
||||
<img src={help} alt="Help" />
|
||||
</a>
|
||||
<div className="item-inner">
|
||||
<div className="item-title">Help</div>
|
||||
<div className="item-title">Join our Community</div>
|
||||
<div className="item-text">
|
||||
If you need any help or you have feature request, you can join{" "}
|
||||
Get help, submit feature requests, report bugs,
|
||||
or just chat with fellow terminal enthusiasts.<br/>
|
||||
<a target="_blank" href={util.makeExternLink("https://discord.gg/XfvZ334gwU")}>
|
||||
our Discord
|
||||
Join the Wave Discord Channel
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="item">
|
||||
<img src={github} alt="Github" />
|
||||
<a
|
||||
target="_blank"
|
||||
href={util.makeExternLink("https://github.com/wavetermdev/waveterm")}
|
||||
>
|
||||
<img src={github} alt="Github" />
|
||||
</a>
|
||||
<div className="item-inner">
|
||||
<div className="item-title">Like Wave? Give us a star</div>
|
||||
<div className="item-title">Support us on GitHub</div>
|
||||
<div className="item-text">
|
||||
Rankings are very important for small startups like us, it helps other people to
|
||||
know about us. If you like Wave, please consider giving us a star on our{" "}
|
||||
We're <i>open source</i> and committed to providing a free terminal for individual
|
||||
users. Please show your support us by giving us a star on{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
href={util.makeExternLink("https://github.com/wavetermdev/waveterm")}
|
||||
>
|
||||
Github Repository
|
||||
Github (wavetermdev/waveterm)
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<footer className="unselectable">
|
||||
<div>
|
||||
<Checkbox
|
||||
checked={this.state.isChecked}
|
||||
@ -429,4 +345,112 @@ class TosModal extends React.Component<{}, {}> {
|
||||
}
|
||||
}
|
||||
|
||||
export { WelcomeModal, LoadingSpinner, ClientStopModal, AlertModal, DisconnectedModal, TosModal };
|
||||
@mobxReact.observer
|
||||
class AboutModal extends React.Component<{}, {}> {
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.aboutModalOpen.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
isUpToDate(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
updateApp(): void {
|
||||
// GlobalCommandRunner.updateApp();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
getStatus(isUpToDate: boolean): JSX.Element {
|
||||
if (isUpToDate) {
|
||||
return (
|
||||
<div className="status updated">
|
||||
<div>
|
||||
<i className="fa-sharp fa-solid fa-circle-check" />
|
||||
<span>Up to Date</span>
|
||||
</div>
|
||||
<div>Client Version v0.4.0 20231016-110014</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="status outdated">
|
||||
<div>
|
||||
<i className="fa-sharp fa-solid fa-triangle-exclamation" />
|
||||
<span>Outdated Version</span>
|
||||
</div>
|
||||
<div>Client Version v0.4.0 20231016-110014</div>
|
||||
<div>
|
||||
<button onClick={this.updateApp} className="button color-green text-secondary">
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={cn("modal about-modal wave-modal is-active")}>
|
||||
<div className="modal-background" />
|
||||
<div className="modal-content about-modal-content">
|
||||
<div className="modal-content-wrapper">
|
||||
<header className="common-header">
|
||||
<div className="modal-title">About</div>
|
||||
<div className="close-icon-wrapper" onClick={this.closeModal}>
|
||||
<img src={close} alt="Close (Escape)" />
|
||||
</div>
|
||||
</header>
|
||||
<div className="content about-content">
|
||||
<section>
|
||||
<div className="logo-wrapper">
|
||||
<img src={logo} alt="logo" />
|
||||
</div>
|
||||
<div className="text-wrapper">
|
||||
<div>Wave Terminal</div>
|
||||
<div className="text-standard">Modern Terminal for Seamless Workflow</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="text-standard">{this.getStatus(this.isUpToDate())}</section>
|
||||
<section>
|
||||
<a
|
||||
className="button button-link color-standard"
|
||||
href={util.makeExternLink("https://github.com/wavetermdev/waveterm")}
|
||||
target="_blank"
|
||||
>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
Github
|
||||
</a>
|
||||
<a
|
||||
className="button button-link color-standard"
|
||||
href={util.makeExternLink("https://www.commandline.dev/")}
|
||||
target="_blank"
|
||||
>
|
||||
<i className="fa-sharp fa-light fa-globe"></i>
|
||||
Website
|
||||
</a>
|
||||
<a
|
||||
className="button button-link color-standard"
|
||||
href={util.makeExternLink(
|
||||
"https://github.com/wavetermdev/waveterm/blob/main/LICENSE"
|
||||
)}
|
||||
target="_blank"
|
||||
>
|
||||
<i className="fa-sharp fa-light fa-book-blank"></i>
|
||||
License
|
||||
</a>
|
||||
</section>
|
||||
<section className="text-standard">Copyright © 2023 Command Line Inc.</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { LoadingSpinner, ClientStopModal, AlertModal, DisconnectedModal, TosModal, AboutModal };
|
||||
|
@ -10,7 +10,7 @@ import cn from "classnames";
|
||||
import { GlobalModel, GlobalCommandRunner, TabColors } from "../../../model/model";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage } from "../common";
|
||||
import { LineType, RendererPluginType, ClientDataType, CommandRtnType } from "../../../types/types";
|
||||
import { RemotesSelector } from "../../connections/connections";
|
||||
import { ConnectionDropdown } from "../../connections/connections";
|
||||
import { PluginModel } from "../../../plugins/plugins";
|
||||
import * as util from "../../../util/util";
|
||||
import { commandRtnHandler } from "../../../util/util";
|
||||
@ -23,9 +23,9 @@ import "./modals.less";
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
// @ts-ignore
|
||||
const VERSION = __PROMPT_VERSION__;
|
||||
const VERSION = __WAVETERM_VERSION__;
|
||||
// @ts-ignore
|
||||
const BUILD = __PROMPT_BUILD__;
|
||||
const BUILD = __WAVETERM_BUILD__;
|
||||
|
||||
const ScreenDeleteMessage = `
|
||||
Are you sure you want to delete this screen/tab?
|
||||
@ -50,7 +50,7 @@ Are you sure you want to stop web-sharing this screen?
|
||||
`.trim();
|
||||
|
||||
@mobxReact.observer
|
||||
class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId: string; inline?: boolean }, {}> {
|
||||
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" });
|
||||
|
||||
@ -194,39 +194,37 @@ class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId:
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectRemote(cname: string): void {
|
||||
let prtn = GlobalCommandRunner.screenSetRemote(cname, true, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
render() {
|
||||
let { sessionId, screenId, inline } = this.props;
|
||||
let { sessionId, screenId } = this.props;
|
||||
let inline = false;
|
||||
let screen = GlobalModel.getScreenById(sessionId, screenId);
|
||||
if (screen == null) {
|
||||
return null;
|
||||
}
|
||||
let color: string = null;
|
||||
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
inline
|
||||
? "screen-settings-inline"
|
||||
: cn("modal screen-settings-modal settings-modal prompt-modal is-active")
|
||||
}
|
||||
>
|
||||
{!inline && <div className="modal-background" />}
|
||||
<div className={inline ? "inline-content" : "modal-content"}>
|
||||
<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" />}
|
||||
{!inline && (
|
||||
<header>
|
||||
<div className="modal-title">screen settings ({screen.name.get()})</div>
|
||||
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
||||
<XmarkIcon />
|
||||
</div>
|
||||
</header>
|
||||
)}
|
||||
<header>
|
||||
<div className="modal-title">screen settings ({screen.name.get()})</div>
|
||||
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
||||
<XmarkIcon />
|
||||
</div>
|
||||
</header>
|
||||
<div className="inner-content">
|
||||
{!inline && (
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Screen Id</div>
|
||||
<div className="settings-input">{screen.screenId}</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Screen Id</div>
|
||||
<div className="settings-input">{screen.screenId}</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Name</div>
|
||||
<div className="settings-input">
|
||||
@ -240,6 +238,12 @@ class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId:
|
||||
/>
|
||||
</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">
|
||||
@ -261,47 +265,41 @@ class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId:
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!inline && (
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">
|
||||
<div>Archived</div>
|
||||
<InfoMessage width={400}>
|
||||
Archive will hide the screen tab. Commands and output will be retained in
|
||||
history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<Toggle checked={screen.archived.get()} onChange={this.handleChangeArchived} />
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">
|
||||
<div>Archived</div>
|
||||
<InfoMessage width={400}>
|
||||
Archive will hide the screen tab. Commands and output will be retained in
|
||||
history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<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 screen, removing all commands and output from history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<div
|
||||
onClick={this.handleDeleteScreen}
|
||||
className="button is-prompt-danger is-outlined is-small"
|
||||
>
|
||||
Delete Screen
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!inline && (
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">
|
||||
<div>Actions</div>
|
||||
<InfoMessage width={400}>
|
||||
Delete will remove the screen, removing all commands and output from history.
|
||||
</InfoMessage>
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
<div
|
||||
onClick={this.handleDeleteScreen}
|
||||
className="button is-prompt-danger is-outlined is-small"
|
||||
>
|
||||
Delete Screen
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
{!inline && (
|
||||
<footer>
|
||||
<div onClick={this.closeModal} className="button is-prompt-green is-outlined is-small">
|
||||
Close
|
||||
</div>
|
||||
</footer>
|
||||
)}
|
||||
<footer>
|
||||
<div onClick={this.closeModal} className="button is-prompt-green is-outlined is-small">
|
||||
Close
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
@base-color: #eceeec;
|
||||
@base-background: rgba(21, 23, 21, 1);
|
||||
@base-background-transparent: rgba(21, 23, 21, 0.7);
|
||||
@base-background-dev: rgba(57, 0, 78, 0.7);
|
||||
@base-background-dev: rgba(21, 23, 48, 0.7);
|
||||
@base-border: rgba(241, 246, 243, 0.08);
|
||||
@background-session: rgba(13, 13, 13, 0.85);
|
||||
@background-session-components: rgba(48, 49, 48, 0.6);
|
||||
@ -22,6 +22,8 @@
|
||||
@text-secondary: #C3C8C2;
|
||||
@text-caption: #8b918a;
|
||||
|
||||
@accent-color: #3B3F3A;
|
||||
|
||||
@status-outline: #151715;
|
||||
@dropdown-menu: rgba(21, 23, 21, 1);
|
||||
|
||||
@ -48,14 +50,14 @@
|
||||
@term-bright-white: #ffffff;
|
||||
|
||||
@tab-red: #e54d2e;
|
||||
@tab-green: #58c142;
|
||||
@tab-orange: #ef713b;
|
||||
@tab-violet: #8b46d0;
|
||||
@tab-cyan: #3abab6;
|
||||
@tab-magenta: #fc3651;
|
||||
@tab-pink: #e05677;
|
||||
@tab-blue: #5460cf;
|
||||
@tab-yellow: #e0b956;
|
||||
@tab-green: #58c142;
|
||||
@tab-mint: #4BFFA9;
|
||||
@tab-cyan: #4BDFFF;
|
||||
@tab-blue: #3971FF;
|
||||
@tab-violet: #BA76FF;
|
||||
@tab-pink: #E05677;
|
||||
@tab-white: #ffffff;
|
||||
|
||||
@tab-black-text: #333;
|
||||
|
@ -270,65 +270,139 @@
|
||||
}
|
||||
}
|
||||
|
||||
.remotes-inline {
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: @base-color;
|
||||
margin: 0 0 0 1em !important;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.dropdown {
|
||||
margin-top: 1em;
|
||||
.button {
|
||||
color: @base-color;
|
||||
border: none !important;
|
||||
padding: 0 1em 0 0.2em;
|
||||
&:hover,
|
||||
&:focus {
|
||||
border: none !important;
|
||||
box-shadow: none;
|
||||
.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;
|
||||
}
|
||||
.remote-name {
|
||||
vertical-align: bottom;
|
||||
.remote-status {
|
||||
top: -2px;
|
||||
left: 4px;
|
||||
vertical-align: middle;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dropdown-content {
|
||||
background: @background-session-components-solid;
|
||||
}
|
||||
.dropdown-item {
|
||||
min-width: max-content;
|
||||
}
|
||||
&.is-active:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.remote-status-light {
|
||||
display: inline;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.remote-name {
|
||||
display: inline;
|
||||
flex-grow: 1;
|
||||
vertical-align: super;
|
||||
|
||||
.remote-name-primary {
|
||||
color: @base-color;
|
||||
max-width: inherit;
|
||||
margin-right: 1em;
|
||||
display: inline;
|
||||
}
|
||||
.add-div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
.remote-name-secondary {
|
||||
color: @disabled-color;
|
||||
max-width: inherit;
|
||||
display: inline;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { If, For } from "tsx-control-statements/components";
|
||||
import cn from "classnames";
|
||||
import { GlobalModel, GlobalCommandRunner, RemotesModalModel } from "../../model/model";
|
||||
import { Toggle, RemoteStatusLight, InfoMessage } from "../common/common";
|
||||
import { RemoteType, RemoteEditType } from "../../types/types";
|
||||
import * as T from "../../types/types";
|
||||
import * as util from "../../util/util";
|
||||
import * as textmeasure from "../../util/textmeasure";
|
||||
|
||||
@ -17,6 +17,10 @@ import { ReactComponent as XmarkIcon } from "../assets/icons/line/xmark.svg";
|
||||
import { ReactComponent as AngleDownIcon } from "../assets/icons/history/angle-down.svg";
|
||||
import { ReactComponent as RotateLeftIcon } from "../assets/icons/rotate_left.svg";
|
||||
import { ReactComponent as AddIcon } from "../assets/icons/add.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 "./connections.less";
|
||||
|
||||
@ -28,14 +32,14 @@ const RemotePtyRows = 8;
|
||||
const RemotePtyCols = 80;
|
||||
const PasswordUnchangedSentinel = "--unchanged--";
|
||||
|
||||
function getRemoteCNWithPort(remote: RemoteType) {
|
||||
function getRemoteCNWithPort(remote: T.RemoteType) {
|
||||
if (util.isBlank(remote.remotevars.port) || remote.remotevars.port == "22") {
|
||||
return remote.remotecanonicalname;
|
||||
}
|
||||
return remote.remotecanonicalname + ":" + remote.remotevars.port;
|
||||
}
|
||||
|
||||
function getRemoteTitle(remote: RemoteType) {
|
||||
function getRemoteTitle(remote: T.RemoteType) {
|
||||
if (!util.isBlank(remote.remotealias)) {
|
||||
return remote.remotealias + " (" + remote.remotecanonicalname + ")";
|
||||
}
|
||||
@ -144,7 +148,7 @@ class ConnectModeDropdown extends React.Component<{ tempVal: OV<string> }, {}> {
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class CreateRemote extends React.Component<{ model: RemotesModalModel; remoteEdit: RemoteEditType }, {}> {
|
||||
class CreateRemote extends React.Component<{ model: RemotesModalModel; remoteEdit: T.RemoteEditType }, {}> {
|
||||
tempAlias: OV<string>;
|
||||
tempHostName: OV<string>;
|
||||
tempPort: OV<string>;
|
||||
@ -408,13 +412,13 @@ class CreateRemote extends React.Component<{ model: RemotesModalModel; remoteEdi
|
||||
<InfoMessage width={350}>
|
||||
<ul>
|
||||
<li>
|
||||
<b>startup</b> - connect when [prompt] starts.
|
||||
<b>startup</b> - Connect when Wave Terminal starts.
|
||||
</li>
|
||||
<li>
|
||||
<b>auto</b> - connect when you first run a command using this connection.
|
||||
<b>auto</b> - Connect when you first run a command using this connection.
|
||||
</li>
|
||||
<li>
|
||||
<b>manual</b> - connect manually. Note, if your connection requires manual input,
|
||||
<b>manual</b> - Connect manually. Note, if your connection requires manual input,
|
||||
like an OPT code, you must use this setting.
|
||||
</li>
|
||||
</ul>
|
||||
@ -452,7 +456,7 @@ class CreateRemote extends React.Component<{ model: RemotesModalModel; remoteEdi
|
||||
|
||||
@mobxReact.observer
|
||||
class EditRemoteSettings extends React.Component<
|
||||
{ model: RemotesModalModel; remote: RemoteType; remoteEdit: RemoteEditType },
|
||||
{ model: RemotesModalModel; remote: T.RemoteType; remoteEdit: T.RemoteEditType },
|
||||
{}
|
||||
> {
|
||||
tempAlias: OV<string>;
|
||||
@ -703,13 +707,13 @@ class EditRemoteSettings extends React.Component<
|
||||
<InfoMessage width={350}>
|
||||
<ul>
|
||||
<li>
|
||||
<b>startup</b> - connect when [prompt] starts.
|
||||
<b>startup</b> - Connect when Wave Terminal starts.
|
||||
</li>
|
||||
<li>
|
||||
<b>auto</b> - connect when you first run a command using this connection.
|
||||
<b>auto</b> - Connect when you first run a command using this connection.
|
||||
</li>
|
||||
<li>
|
||||
<b>manual</b> - connect manually. Note, if your connection requires manual input,
|
||||
<b>manual</b> - Connect manually. Note, if your connection requires manual input,
|
||||
like an OPT code, you must use this setting.
|
||||
</li>
|
||||
</ul>
|
||||
@ -763,7 +767,7 @@ class EditRemoteSettings extends React.Component<
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class RemoteDetailView extends React.Component<{ model: RemotesModalModel; remote: RemoteType }, {}> {
|
||||
class RemoteDetailView extends React.Component<{ model: RemotesModalModel; remote: T.RemoteType }, {}> {
|
||||
termRef: React.RefObject<any> = React.createRef();
|
||||
|
||||
componentDidMount() {
|
||||
@ -793,7 +797,7 @@ class RemoteDetailView extends React.Component<{ model: RemotesModalModel; remot
|
||||
}
|
||||
}
|
||||
|
||||
getRemoteTypeStr(remote: RemoteType): string {
|
||||
getRemoteTypeStr(remote: T.RemoteType): string {
|
||||
if (!util.isBlank(remote.uname)) {
|
||||
let unameStr = remote.uname;
|
||||
unameStr = unameStr.replace("|", ", ");
|
||||
@ -827,7 +831,7 @@ class RemoteDetailView extends React.Component<{ model: RemotesModalModel; remot
|
||||
this.props.model.startEditAuth();
|
||||
}
|
||||
|
||||
renderInstallStatus(remote: RemoteType): any {
|
||||
renderInstallStatus(remote: T.RemoteType): any {
|
||||
let statusStr: string = null;
|
||||
if (remote.installstatus == "disconnected") {
|
||||
if (remote.needsmshellupgrade) {
|
||||
@ -851,7 +855,7 @@ class RemoteDetailView extends React.Component<{ model: RemotesModalModel; remot
|
||||
);
|
||||
}
|
||||
|
||||
renderRemoteMessage(remote: RemoteType): any {
|
||||
renderRemoteMessage(remote: T.RemoteType): any {
|
||||
let message: string = "";
|
||||
let buttons: any[] = [];
|
||||
// connect, disconnect, editauth, tryreconnect, install
|
||||
@ -1073,7 +1077,7 @@ class RemotesModal extends React.Component<{ model: RemotesModalModel }, {}> {
|
||||
GlobalCommandRunner.openCreateRemote();
|
||||
}
|
||||
|
||||
renderRemoteMenuItem(remote: RemoteType, selectedId: string): any {
|
||||
renderRemoteMenuItem(remote: T.RemoteType, selectedId: string): any {
|
||||
return (
|
||||
<div
|
||||
key={remote.remotecanonicalname}
|
||||
@ -1120,7 +1124,7 @@ class RemotesModal extends React.Component<{ model: RemotesModalModel }, {}> {
|
||||
let model = this.props.model;
|
||||
let selectedRemoteId = model.selectedRemoteId.get();
|
||||
let allRemotes = util.sortAndFilterRemotes(GlobalModel.remotes.slice());
|
||||
let remote: RemoteType = null;
|
||||
let remote: T.RemoteType = null;
|
||||
let isAuthEditMode = model.isAuthEditMode();
|
||||
let selectedRemote = GlobalModel.getRemote(selectedRemoteId);
|
||||
let remoteEdit = model.remoteEdit.get();
|
||||
@ -1175,89 +1179,107 @@ class RemotesModal extends React.Component<{ model: RemotesModalModel }, {}> {
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class RemotesSelector extends React.Component<{ model: RemotesModalModel; isChangeRemoteOnSelect?: boolean }, { isOpen: boolean }> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpen: false,
|
||||
};
|
||||
class ConnectionDropdown extends React.Component<{ curRemote: T.RemoteType, onSelectRemote?: (cname: string) => void, allowNewConn: boolean, onNewConn?: () => void }, {}> {
|
||||
connDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "connDropdownActive" });
|
||||
|
||||
@boundMethod
|
||||
toggleConnDropdown(): void {
|
||||
mobx.action(() => {
|
||||
this.connDropdownActive.set(!this.connDropdownActive.get());
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectRemote(remoteid: string, remotecanonicalname: string): void {
|
||||
this.props.model.selectRemote(remoteid);
|
||||
if (this.props.isChangeRemoteOnSelect) {
|
||||
let prtn = GlobalCommandRunner.screenSetRemote(remotecanonicalname, true, false);
|
||||
// TODO: see settings.tsx. use prtn to set error message
|
||||
selectRemote(cname: string): void {
|
||||
mobx.action(() => {
|
||||
this.connDropdownActive.set(false);
|
||||
})();
|
||||
if (this.props.onSelectRemote) {
|
||||
this.props.onSelectRemote(cname);
|
||||
}
|
||||
this.setState({ isOpen: false });
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
clickAddRemote(): void {
|
||||
GlobalModel.remotesModalModel.openModalForEdit({remoteedit: true}, true);
|
||||
this.setState({ isOpen: false });
|
||||
}
|
||||
|
||||
renderRemoteMenuItem(remote: RemoteType, selectedId: string): any {
|
||||
return (
|
||||
<div
|
||||
key={remote.remoteid}
|
||||
onClick={() => this.selectRemote(remote.remoteid, remote.remotecanonicalname)}
|
||||
className={cn("dropdown-item remote-menu-item hoverEffect", {
|
||||
"is-selected": remote.remoteid == selectedId,
|
||||
})}
|
||||
>
|
||||
<div className="remote-status-light">
|
||||
<RemoteStatusLight remote={remote} />
|
||||
</div>
|
||||
<If condition={util.isBlank(remote.remotealias)}>
|
||||
<div className="remote-name">
|
||||
<div className="remote-name-primary">{remote.remotecanonicalname}</div>
|
||||
</div>
|
||||
</If>
|
||||
<If condition={!util.isBlank(remote.remotealias)}>
|
||||
<div className="remote-name">
|
||||
<div className="remote-name-primary">{remote.remotealias}</div>
|
||||
<div className="remote-name-secondary">{remote.remotecanonicalname}</div>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
clickNewConnection(): void {
|
||||
mobx.action(() => {
|
||||
this.connDropdownActive.set(false);
|
||||
})();
|
||||
if (this.props.onNewConn) {
|
||||
this.props.onNewConn();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const allRemotes = util.sortAndFilterRemotes(GlobalModel.remotes.slice());
|
||||
const remote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
const selectedRemoteDiv = (
|
||||
<div className="remote-name">
|
||||
<div className="remote-status-light">
|
||||
<RemoteStatusLight remote={remote} />
|
||||
</div>
|
||||
<div className="remote-name-primary">{remote.remotealias}</div>
|
||||
<div className="remote-name-secondary">{remote.remotecanonicalname}</div>
|
||||
</div>
|
||||
);
|
||||
let { curRemote } = this.props;
|
||||
let remote: T.RemoteType = null;
|
||||
let allRemotes = util.sortAndFilterRemotes(GlobalModel.remotes.slice());
|
||||
return (
|
||||
<div className={"remotes-inline"}>
|
||||
<div className="remotes-menu">
|
||||
<div className={`dropdown ${this.state.isOpen ? "is-active" : ""}`}>
|
||||
<div className="dropdown-trigger">
|
||||
<button className="button" onClick={() => this.setState({ isOpen: !this.state.isOpen })}>
|
||||
{selectedRemoteDiv}
|
||||
<AngleDownIcon className="icon" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="dropdown-menu" id="dropdown-menu3" role="menu">
|
||||
<div className="dropdown-content">
|
||||
{allRemotes
|
||||
.filter(({ remoteid }) => remoteid !== remote.remoteid)
|
||||
.map((remote) => this.renderRemoteMenuItem(remote, remote.remoteid))}
|
||||
<div onClick={this.clickAddRemote} className=".dropdown-item hoverEffect">
|
||||
<AddIcon className="icon" /> Add SSH Connection
|
||||
<div className={cn("dropdown", "conn-dropdown", { "is-active": this.connDropdownActive.get() })}>
|
||||
<div className="dropdown-trigger" onClick={this.toggleConnDropdown}>
|
||||
<div className="conn-dd-trigger">
|
||||
<If condition={curRemote != null}>
|
||||
<div className="lefticon">
|
||||
<GlobeIcon className="globe-icon"/>
|
||||
<StatusCircleIcon className={cn("status-icon", "status-" + curRemote.status)}/>
|
||||
</div>
|
||||
<div className="conntext">
|
||||
<If condition={util.isBlank(curRemote.remotealias)}>
|
||||
<div className="text-standard conntext-solo">
|
||||
{curRemote.remotecanonicalname}
|
||||
</div>
|
||||
</If>
|
||||
<If condition={!util.isBlank(curRemote.remotealias)}>
|
||||
<div className="text-secondary conntext-1">
|
||||
{curRemote.remotealias}
|
||||
</div>
|
||||
<div className="text-caption conntext-2">
|
||||
{curRemote.remotecanonicalname}
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
<div className="dd-control">
|
||||
<ArrowsUpDownIcon className="icon"/>
|
||||
</div>
|
||||
</If>
|
||||
<If condition={curRemote == null}>
|
||||
<div className="lefticon">
|
||||
<GlobeIcon className="globe-icon"/>
|
||||
</div>
|
||||
<div className="conntext">
|
||||
<div className="text-standard conntext-solo">
|
||||
(no connection)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dd-control">
|
||||
<ArrowsUpDownIcon className="icon"/>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dropdown-menu" role="menu">
|
||||
<div className="dropdown-content conn-dd-menu">
|
||||
<For each="remote" of={allRemotes}>
|
||||
<div className="dropdown-item" key={remote.remoteid} onClick={() => this.selectRemote(remote.remotecanonicalname)}>
|
||||
<div className="status-div">
|
||||
<CircleIcon className={cn("status-icon", "status-" + remote.status)}/>
|
||||
</div>
|
||||
<If condition={util.isBlank(remote.remotealias)}>
|
||||
<div className="text-standard">{remote.remotecanonicalname}</div>
|
||||
</If>
|
||||
<If condition={!util.isBlank(remote.remotealias)}>
|
||||
<div className="text-standard">{remote.remotealias}</div>
|
||||
<div className="text-caption">{remote.remotecanonicalname}</div>
|
||||
</If>
|
||||
</div>
|
||||
</For>
|
||||
<If condition={this.props.allowNewConn}>
|
||||
<div className="dropdown-item" onClick={this.clickNewConnection}>
|
||||
<div className="add-div">
|
||||
<AddIcon className="add-icon"/>
|
||||
</div>
|
||||
<div className="text-standard">New Connection</div>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1265,4 +1287,4 @@ class RemotesSelector extends React.Component<{ model: RemotesModalModel; isChan
|
||||
}
|
||||
}
|
||||
|
||||
export { RemotesModal, RemotesSelector };
|
||||
export { RemotesModal, ConnectionDropdown };
|
||||
|
@ -9,6 +9,31 @@
|
||||
fill: @base-color;
|
||||
}
|
||||
|
||||
.history-checkbox {
|
||||
&.state-unchecked {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #3B3F3A;
|
||||
background: rgba(213, 254, 175, 0.03);
|
||||
}
|
||||
|
||||
&.checkbox-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
&.state-partial {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
fill: rgba(213, 254, 175, 0.03);
|
||||
stroke-width: 1px;
|
||||
stroke: #3B3F3A;
|
||||
}
|
||||
}
|
||||
|
||||
.is-left {
|
||||
position: absolute;
|
||||
left: 0.5em;
|
||||
@ -152,6 +177,7 @@
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
|
||||
.is-hidden {
|
||||
display: none;
|
||||
@ -162,14 +188,20 @@
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.trash-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
fill: @text-secondary;
|
||||
}
|
||||
|
||||
.control-checkbox {
|
||||
margin: 0.3em 1.2em;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.control-button {
|
||||
cursor: pointer;
|
||||
color: #aaa;
|
||||
margin-left: 10px;
|
||||
margin-left: 12px;
|
||||
|
||||
.icon {
|
||||
vertical-align: text-bottom;
|
||||
@ -277,19 +309,21 @@
|
||||
tr.history-item {
|
||||
padding: 0 10px 0 10px;
|
||||
display: flex;
|
||||
border-top: 1px solid #333;
|
||||
border-bottom: 1px solid rgba(250, 250, 250, 0.10);
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
color: @text-secondary;
|
||||
|
||||
&.is-selected {
|
||||
background-color: #003;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
&.is-selected:hover {
|
||||
background-color: #336;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #333;
|
||||
background-color: #222;
|
||||
|
||||
td.bookmark i {
|
||||
display: block;
|
||||
@ -304,8 +338,7 @@
|
||||
|
||||
td.selectbox {
|
||||
flex: 0 0 auto;
|
||||
flex-basis: 24px;
|
||||
margin-right: 1em;
|
||||
flex-basis: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -318,16 +351,15 @@
|
||||
|
||||
td.ts {
|
||||
flex: 0 0 auto;
|
||||
flex-basis: 74px;
|
||||
font-weight: bold;
|
||||
margin-right: 1em;
|
||||
flex-basis: 86px;
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
td.workspace {
|
||||
flex: 0 0 auto;
|
||||
flex-basis: 120px;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: 1em;
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
td.remote {
|
||||
@ -337,17 +369,15 @@
|
||||
padding-right: 5px;
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
margin-right: 2em;
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
td.cmdstr {
|
||||
color: @term-white;
|
||||
flex: 1 0 0;
|
||||
padding-left: 20px;
|
||||
border-radius: 3px;
|
||||
white-space: pre;
|
||||
max-height: 70px;
|
||||
cursor: pointer;
|
||||
min-width: 300px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
@ -361,6 +391,21 @@
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
td.downarrow {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
cursor: pointer;
|
||||
|
||||
.down-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,9 @@ import { ReactComponent as SquareCheckIcon } from "../assets/icons/history/squar
|
||||
import { ReactComponent as SquareMinusIcon } from "../assets/icons/history/square-minus.svg";
|
||||
import { ReactComponent as SquareIcon } from "../assets/icons/history/square.svg";
|
||||
import { ReactComponent as TrashIcon } from "../assets/icons/trash.svg";
|
||||
import { ReactComponent as CheckedCheckbox } from "../assets/icons/checked-checkbox.svg";
|
||||
import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
|
||||
import { ReactComponent as CopyIcon } from "../assets/icons/history/copy.svg";
|
||||
|
||||
import "./history.less";
|
||||
|
||||
@ -91,6 +94,86 @@ function formatSessionName(snames: Record<string, string>, sessionId: string): s
|
||||
return "#" + sname;
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class HistoryCheckbox extends React.Component<{ checked: boolean, partialCheck?: boolean, onClick?: () => void }, {}> {
|
||||
@boundMethod
|
||||
clickHandler(): void {
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.checked) {
|
||||
return <CheckedCheckbox onClick={this.clickHandler} className="history-checkbox checkbox-icon" />;
|
||||
}
|
||||
if (this.props.partialCheck) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<rect x="0.5" y="0.5" width="15" height="15" rx="3.5" fill="#D5FEAF" fill-opacity="0.026"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 8C4 6.89543 4.89543 6 6 6H10C11.1046 6 12 6.89543 12 8C12 9.10457 11.1046 10 10 10H6C4.89543 10 4 9.10457 4 8Z" fill="#58C142"/>
|
||||
<rect x="0.5" y="0.5" width="15" height="15" rx="3.5" stroke="#3B3F3A"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
else {
|
||||
return <div onClick={this.clickHandler} className="history-checkbox state-unchecked"/>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HistoryCmdStr extends React.Component<
|
||||
{
|
||||
cmdstr: string;
|
||||
onUse: () => void;
|
||||
onCopy: () => void;
|
||||
isCopied: boolean;
|
||||
fontSize: "normal" | "large";
|
||||
limitHeight: boolean;
|
||||
},
|
||||
{}
|
||||
> {
|
||||
@boundMethod
|
||||
handleUse(e: any) {
|
||||
e.stopPropagation();
|
||||
if (this.props.onUse != null) {
|
||||
this.props.onUse();
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleCopy(e: any) {
|
||||
e.stopPropagation();
|
||||
if (this.props.onCopy != null) {
|
||||
this.props.onCopy();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let { isCopied, cmdstr, fontSize, limitHeight } = this.props;
|
||||
return (
|
||||
<div className={cn("cmdstr-code", { "is-large": fontSize == "large" }, { "limit-height": limitHeight })}>
|
||||
<If condition={isCopied}>
|
||||
<div key="copied" className="copied-indicator">
|
||||
<div>copied</div>
|
||||
</div>
|
||||
</If>
|
||||
<div key="use" className="use-button hoverEffect" title="Use Command" onClick={this.handleUse}>
|
||||
<CheckIcon className="icon" />
|
||||
</div>
|
||||
<div key="code" className="code-div">
|
||||
<code>{cmdstr}</code>
|
||||
</div>
|
||||
<div key="copy" className="copy-control hoverEffect">
|
||||
<div className="inner-copy" onClick={this.handleCopy} title="copy">
|
||||
<CopyIcon className="icon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class HistoryView extends React.Component<{}, {}> {
|
||||
tableRef: React.RefObject<any> = React.createRef();
|
||||
@ -330,13 +413,6 @@ class HistoryView extends React.Component<{}, {}> {
|
||||
let hasMore = hvm.hasMore.get();
|
||||
let offset = hvm.offset.get();
|
||||
let numSelected = hvm.selectedItems.size;
|
||||
let controlCheckboxIcon = <SquareIcon className="icon" />;
|
||||
if (numSelected > 0) {
|
||||
controlCheckboxIcon = <SquareMinusIcon className="icon" />;
|
||||
}
|
||||
if (numSelected > 0 && numSelected == items.length) {
|
||||
controlCheckboxIcon = <SquareCheckIcon className="icon" />;
|
||||
}
|
||||
let activeItemId = hvm.activeItem.get();
|
||||
let activeItem = hvm.getHistoryItemById(activeItemId);
|
||||
let activeLine: LineType = null;
|
||||
@ -376,8 +452,8 @@ class HistoryView extends React.Component<{}, {}> {
|
||||
<div onClick={this.toggleSessionDropdown}>
|
||||
<span className="label">
|
||||
{hvm.searchSessionId.get() == null
|
||||
? "Limit Workspace"
|
||||
: formatSessionName(snames, hvm.searchSessionId.get())}
|
||||
? "Limit Workspace"
|
||||
: formatSessionName(snames, hvm.searchSessionId.get())}
|
||||
</span>
|
||||
<AngleDownIcon className="icon" />
|
||||
</div>
|
||||
@ -410,8 +486,8 @@ class HistoryView extends React.Component<{}, {}> {
|
||||
<div onClick={this.toggleRemoteDropdown}>
|
||||
<span className="label">
|
||||
{hvm.searchRemoteId.get() == null
|
||||
? "Limit Remote"
|
||||
: formatRemoteName(rnames, { remoteid: hvm.searchRemoteId.get() })}
|
||||
? "Limit Remote"
|
||||
: formatRemoteName(rnames, { remoteid: hvm.searchRemoteId.get() })}
|
||||
</span>
|
||||
<AngleDownIcon className="icon" />
|
||||
</div>
|
||||
@ -486,7 +562,7 @@ class HistoryView extends React.Component<{}, {}> {
|
||||
</div>
|
||||
<div className={cn("control-bar", "is-top", { "is-hidden": items.length == 0 })}>
|
||||
<div className="control-checkbox" onClick={this.handleControlCheckbox} title="Toggle Selection">
|
||||
{controlCheckboxIcon}
|
||||
<HistoryCheckbox checked={numSelected > 0 && numSelected == items.length} partialCheck={numSelected > 0}/>
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
@ -497,8 +573,8 @@ class HistoryView extends React.Component<{}, {}> {
|
||||
onClick={this.handleClickDelete}
|
||||
>
|
||||
<span>
|
||||
<TrashIcon className="icon" title="Purge Selected Items" />
|
||||
Delete Items
|
||||
<TrashIcon className="trash-icon" title="Purge Selected Items" />
|
||||
Delete Items
|
||||
</span>
|
||||
</div>
|
||||
<div className="spacer" />
|
||||
@ -527,21 +603,10 @@ class HistoryView extends React.Component<{}, {}> {
|
||||
className={cn("history-item", { "is-selected": hvm.selectedItems.get(item.historyid) })}
|
||||
>
|
||||
<td className="selectbox" onClick={() => this.handleSelect(item.historyid)}>
|
||||
<If condition={hvm.selectedItems.get(item.historyid)}>
|
||||
<SquareCheckIcon className="icon" />
|
||||
</If>
|
||||
<If condition={!hvm.selectedItems.get(item.historyid)}>
|
||||
<SquareIcon className="icon" />
|
||||
</If>
|
||||
<HistoryCheckbox checked={hvm.selectedItems.get(item.historyid)}/>
|
||||
</td>
|
||||
<td className="bookmark" style={{ display: "none" }}>
|
||||
<FavoritesIcon className="icon" />
|
||||
</td>
|
||||
<td className="ts">{getHistoryViewTs(nowDate, item.ts)}</td>
|
||||
<td className="workspace">{formatSSName(snames, scrnames, item)}</td>
|
||||
<td className="remote">{formatRemoteName(rnames, item.remote)}</td>
|
||||
<td className="cmdstr" onClick={() => this.activateItem(item.historyid)}>
|
||||
<CmdStrCode
|
||||
<td className="cmdstr">
|
||||
<HistoryCmdStr
|
||||
cmdstr={item.cmdstr}
|
||||
onUse={() => this.handleUse(item)}
|
||||
onCopy={() => this.handleCopy(item)}
|
||||
@ -550,6 +615,21 @@ class HistoryView extends React.Component<{}, {}> {
|
||||
limitHeight={true}
|
||||
/>
|
||||
</td>
|
||||
<td className="workspace text-standard">{formatSSName(snames, scrnames, item)}</td>
|
||||
<td className="remote text-standard">{formatRemoteName(rnames, item.remote)}</td>
|
||||
<td className="ts text-standard">{getHistoryViewTs(nowDate, item.ts)}</td>
|
||||
<td className="downarrow" onClick={() => this.activateItem(item.historyid)}>
|
||||
<If condition={activeItemId != item.historyid}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M12.1297 6.62492C12.3999 6.93881 12.3645 7.41237 12.0506 7.68263L8.48447 10.7531C8.20296 10.9955 7.78645 10.9952 7.50519 10.7526L3.94636 7.68213C3.63274 7.41155 3.59785 6.93796 3.86843 6.62434C4.13901 6.31072 4.6126 6.27583 4.92622 6.54641L7.99562 9.19459L11.0719 6.54591C11.3858 6.27565 11.8594 6.31102 12.1297 6.62492Z" fill="#C3C8C2"/>
|
||||
</svg>
|
||||
</If>
|
||||
<If condition={activeItemId == item.historyid}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M3.87035 9.37508C3.60009 9.06119 3.63546 8.58763 3.94936 8.31737L7.51553 5.24692C7.79704 5.00455 8.21355 5.00476 8.49481 5.24742L12.0536 8.31787C12.3673 8.58845 12.4022 9.06204 12.1316 9.37566C11.861 9.68928 11.3874 9.72417 11.0738 9.45359L8.00438 6.80541L4.92806 9.45409C4.61416 9.72435 4.14061 9.68898 3.87035 9.37508Z" fill="#C3C8C2"/>
|
||||
</svg>
|
||||
</If>
|
||||
</td>
|
||||
</tr>
|
||||
<If condition={activeItemId == item.historyid}>
|
||||
<tr className="active-history-item">
|
||||
|
@ -422,8 +422,10 @@ class LineCmd extends React.Component<
|
||||
data-screenid={line.screenid}
|
||||
style={{ height: height }}
|
||||
>
|
||||
<SmallLineAvatar line={line} cmd={cmd} />
|
||||
<div className="ts">{formattedTime}</div>
|
||||
<div className="simple-line-header">
|
||||
<SmallLineAvatar line={line} cmd={cmd} />
|
||||
<div className="ts">{formattedTime}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
||||
.line-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-bottom: 0.7em;
|
||||
padding-bottom: 0.7rem;
|
||||
width: 100%;
|
||||
|
||||
&.is-expanded {
|
||||
@ -111,7 +111,7 @@
|
||||
|
||||
.terminal {
|
||||
margin-right: 8px;
|
||||
padding: 0.25em;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
&.cmd-done .terminal .xterm-cursor {
|
||||
@ -137,7 +137,7 @@
|
||||
margin: 6px 0 2px 10px;
|
||||
padding: 2px 5px 0px 5px;
|
||||
display: inline-block;
|
||||
font-size: 0.8em;
|
||||
font-size: 10px;
|
||||
color: @term-white;
|
||||
background-color: #151715;
|
||||
}
|
||||
@ -163,8 +163,8 @@
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 1em 1em 0 1em;
|
||||
padding: 1em;
|
||||
margin: 1rem 1rem 0 1rem;
|
||||
padding: 1rem;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
@ -189,11 +189,16 @@
|
||||
}
|
||||
|
||||
&.line-simple {
|
||||
flex-direction: row;
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
color: rgba(@base-color, 0.6);
|
||||
|
||||
.simple-line-header {
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.ts {
|
||||
display: flex;
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ let MagicLayout = {
|
||||
// the 3 is for descenders, which get cut off in the terminal without this
|
||||
TermDescendersHeight: 3,
|
||||
TermWidthBuffer: 15,
|
||||
|
||||
};
|
||||
|
||||
let m = MagicLayout;
|
||||
|
@ -30,7 +30,6 @@ class PluginsView extends React.Component<{}, {}> {
|
||||
return <></>;
|
||||
}
|
||||
const { pluginsModel } = GlobalModel;
|
||||
console.log(`rendering details for ${pluginsModel.selectedPlugin.get().name}`);
|
||||
const PluginList = () => (
|
||||
<div className="plugins-list">
|
||||
{PluginModel.allPlugins().map((plugin, i) => (
|
||||
|
@ -118,13 +118,6 @@ class MainSideBar extends React.Component<{}, {}> {
|
||||
GlobalModel.showWebShareView();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleWelcomeClick(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.welcomeModalOpen.set(true);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleSettingsClick(): void {
|
||||
mobx.action(() => {
|
||||
@ -206,28 +199,23 @@ class MainSideBar extends React.Component<{}, {}> {
|
||||
</div>
|
||||
<div className="contents">
|
||||
<div className="top">
|
||||
<div className="item hoverEffect" onClick={this.handlePluginsClick}>
|
||||
<AppsIcon className="icon" />
|
||||
Apps
|
||||
<span className="hotkey">⌘A</span>
|
||||
</div>
|
||||
<div className="item hoverEffect" onClick={this.handleHistoryClick}>
|
||||
<div className="item hoverEffect unselectable" onClick={this.handleHistoryClick}>
|
||||
<HistoryIcon className="icon" />
|
||||
History
|
||||
<span className="hotkey">⌘H</span>
|
||||
</div>
|
||||
<div className="item hoverEffect" onClick={this.handleBookmarksClick}>
|
||||
{/* <div className="item hoverEffect unselectable" onClick={this.handleBookmarksClick}>
|
||||
<FavoritesIcon className="icon" />
|
||||
Favorites
|
||||
<span className="hotkey">⌘B</span>
|
||||
</div>
|
||||
<div className="item hoverEffect" onClick={this.handleConnectionsClick}>
|
||||
</div> */}
|
||||
<div className="item hoverEffect unselectable" onClick={this.handleConnectionsClick}>
|
||||
<ConnectionsIcon className="icon" />
|
||||
Connections
|
||||
</div>
|
||||
</div>
|
||||
<div className="separator" />
|
||||
<div className="item workspaces-item">
|
||||
<div className="item workspaces-item unselectable">
|
||||
<WorkspacesIcon className="icon" />
|
||||
Workspaces
|
||||
<div className="add_workspace hoverEffect" onClick={this.handleNewSession}>
|
||||
@ -236,15 +224,20 @@ class MainSideBar extends React.Component<{}, {}> {
|
||||
</div>
|
||||
<div className="middle hideScrollbarUntillHover">{this.getSessions()}</div>
|
||||
<div className="bottom">
|
||||
<div className="item hoverEffect" onClick={this.handleSettingsClick}>
|
||||
<div className="item hoverEffect unselectable" onClick={this.handlePluginsClick}>
|
||||
<AppsIcon className="icon" />
|
||||
Apps
|
||||
<span className="hotkey">⌘A</span>
|
||||
</div>
|
||||
<div className="item hoverEffect unselectable" onClick={this.handleSettingsClick}>
|
||||
<SettingsIcon className="icon" />
|
||||
Settings
|
||||
</div>
|
||||
<div className="item hoverEffect" onClick={() => openLink("https://docs.getprompt.dev")}>
|
||||
<div className="item hoverEffect unselectable" onClick={() => openLink("https://docs.getprompt.dev")}>
|
||||
<HelpIcon className="icon" />
|
||||
Documentation
|
||||
</div>
|
||||
<div className="item hoverEffect" onClick={() => openLink("https://discord.gg/XfvZ334gwU")}>
|
||||
<div className="item hoverEffect unselectable" onClick={() => openLink("https://discord.gg/XfvZ334gwU")}>
|
||||
<DiscordIcon className="icon discord" />
|
||||
Talk to us
|
||||
</div>
|
||||
|
@ -29,12 +29,6 @@ type OV<V> = mobx.IObservableValue<V>;
|
||||
class CmdInput extends React.Component<{}, {}> {
|
||||
cmdInputRef: React.RefObject<any> = React.createRef();
|
||||
|
||||
@boundMethod
|
||||
onInfoToggle(): void {
|
||||
GlobalModel.inputModel.toggleInfoMsg();
|
||||
return;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateCmdInputHeight();
|
||||
}
|
||||
@ -119,14 +113,6 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
className={cn("cmd-input", { "has-info": infoShow }, { active: focusVal })}
|
||||
onClick={this.cmdInputClick}
|
||||
>
|
||||
<div key="minmax" onClick={this.onInfoToggle} className="input-minmax-control">
|
||||
<If condition={infoShow || historyShow}>
|
||||
<i className="fa-sharp fa-solid fa-chevron-down" />
|
||||
</If>
|
||||
<If condition={!(infoShow || historyShow) && hasInfo}>
|
||||
<i className="fa-sharp fa-solid fa-chevron-up" />
|
||||
</If>
|
||||
</div>
|
||||
<If condition={historyShow}>
|
||||
<div className="cmd-input-grow-spacer"></div>
|
||||
<HistoryInfo />
|
||||
|
@ -41,6 +41,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
.window-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
color: #ccc;
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
color: #4e9a06;
|
||||
}
|
||||
|
||||
&.should-fade {
|
||||
opacity: 1;
|
||||
animation: fade-in 2.5s;
|
||||
}
|
||||
}
|
||||
|
||||
.share-tag {
|
||||
color: @term-white;
|
||||
position: absolute;
|
||||
@ -110,6 +130,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.cr-help-text {
|
||||
color: @text-caption;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.newtab-spacer {
|
||||
height: 1px;
|
||||
background: rgba(241, 246, 243, 0.15);
|
||||
@ -119,25 +144,29 @@
|
||||
display: flex;
|
||||
padding: 8px 0;
|
||||
align-items: flex-start;
|
||||
gap: 27px;
|
||||
gap: 12px;
|
||||
|
||||
.icondiv {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
.icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
|
||||
path {
|
||||
fill: black;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-white + .check-icon {
|
||||
@ -147,141 +176,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ import * as T from "../../../types/types";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { InlineSettingsTextEdit, RemoteStatusLight } from "../../common/common";
|
||||
import { getRemoteStr } from "../../common/prompt/prompt";
|
||||
import { GlobalModel, ScreenLines, Screen } from "../../../model/model";
|
||||
import { GlobalModel, ScreenLines, Screen, Session } from "../../../model/model";
|
||||
import { Line } from "../../line/linecomps";
|
||||
import { LinesView } from "../../line/linesview";
|
||||
import { ConnectionDropdown } from "../../connections/connections";
|
||||
import * as util from "../../../util/util";
|
||||
import { ReactComponent as EllipseIcon } from "../../assets/icons/ellipse.svg";
|
||||
import { ReactComponent as Check12Icon } from "../../assets/icons/check12.svg";
|
||||
@ -36,16 +37,16 @@ dayjs.extend(localizedFormat);
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
@mobxReact.observer
|
||||
class ScreenView extends React.Component<{ screen: Screen }, {}> {
|
||||
class ScreenView extends React.Component<{ session: Session, screen: Screen }, {}> {
|
||||
render() {
|
||||
let { screen } = this.props;
|
||||
let { session, screen } = this.props;
|
||||
if (screen == null) {
|
||||
return <div className="screen-view">(no screen found)</div>;
|
||||
}
|
||||
let fontSize = GlobalModel.termFontSize.get();
|
||||
return (
|
||||
<div className="screen-view" data-screenid={screen.screenId}>
|
||||
<ScreenWindowView key={screen.screenId + ":" + fontSize} screen={screen} />
|
||||
<ScreenWindowView key={screen.screenId + ":" + fontSize} session={session} screen={screen} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -53,7 +54,6 @@ class ScreenView extends React.Component<{ screen: Screen }, {}> {
|
||||
|
||||
@mobxReact.observer
|
||||
class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||
connDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "NewTabSettings-connDropdownActive" });
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "NewTabSettings-errorMessage" });
|
||||
|
||||
@boundMethod
|
||||
@ -76,92 +76,17 @@ class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
toggleConnDropdown(): void {
|
||||
mobx.action(() => {
|
||||
this.connDropdownActive.set(!this.connDropdownActive.get());
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectRemote(cname: string): void {
|
||||
mobx.action(() => {
|
||||
this.connDropdownActive.set(false);
|
||||
})();
|
||||
let prtn = GlobalCommandRunner.screenSetRemote(cname, true, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
clickNewConnection(): void {
|
||||
mobx.action(() => {
|
||||
this.connDropdownActive.set(false);
|
||||
})();
|
||||
GlobalModel.remotesModalModel.openModalForEdit({remoteedit: true}, true);
|
||||
}
|
||||
|
||||
renderConnDropdown(): any {
|
||||
let { screen } = this.props;
|
||||
let allRemotes = util.sortAndFilterRemotes(GlobalModel.remotes.slice());
|
||||
let remote: T.RemoteType = null;
|
||||
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
// TODO no remote?
|
||||
return (
|
||||
<div className={cn("dropdown", "conn-dropdown", { "is-active": this.connDropdownActive.get() })}>
|
||||
<div className="dropdown-trigger" onClick={this.toggleConnDropdown}>
|
||||
<div className="conn-dd-trigger">
|
||||
<div className="lefticon">
|
||||
<GlobeIcon className="globe-icon"/>
|
||||
<StatusCircleIcon className={cn("status-icon", "status-" + curRemote.status)}/>
|
||||
</div>
|
||||
<div className="conntext">
|
||||
<If condition={util.isBlank(curRemote.remotealias)}>
|
||||
<div className="text-standard conntext-solo">
|
||||
{curRemote.remotecanonicalname}
|
||||
</div>
|
||||
</If>
|
||||
<If condition={!util.isBlank(curRemote.remotealias)}>
|
||||
<div className="text-secondary conntext-1">
|
||||
{curRemote.remotealias}
|
||||
</div>
|
||||
<div className="text-caption conntext-2">
|
||||
{curRemote.remotecanonicalname}
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
<div className="dd-control">
|
||||
<ArrowsUpDownIcon className="icon"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dropdown-menu" role="menu">
|
||||
<div className="dropdown-content conn-dd-menu">
|
||||
<For each="remote" of={allRemotes}>
|
||||
<div className="dropdown-item" key={remote.remoteid} onClick={() => this.selectRemote(remote.remotecanonicalname)}>
|
||||
<div className="status-div">
|
||||
<CircleIcon className={cn("status-icon", "status-" + remote.status)}/>
|
||||
</div>
|
||||
<If condition={util.isBlank(remote.remotealias)}>
|
||||
<div className="text-standard">{remote.remotecanonicalname}</div>
|
||||
</If>
|
||||
<If condition={!util.isBlank(remote.remotealias)}>
|
||||
<div className="text-standard">{remote.remotealias}</div>
|
||||
<div className="text-caption">{remote.remotecanonicalname}</div>
|
||||
</If>
|
||||
</div>
|
||||
</For>
|
||||
<div className="dropdown-item" onClick={this.clickNewConnection}>
|
||||
<div className="add-div">
|
||||
<AddIcon className="add-icon"/>
|
||||
</div>
|
||||
<div className="text-standard">New Connection</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
let { screen } = this.props;
|
||||
let rptr = screen.curRemote.get();
|
||||
@ -170,19 +95,23 @@ class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||
curColor = "green";
|
||||
}
|
||||
let color: string = null;
|
||||
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
return (
|
||||
<div className="newtab-container">
|
||||
<div className="newtab-section conn-section">
|
||||
<div className="text-s1">
|
||||
<div className="text-s1 unselectable">
|
||||
You're connected to [{getRemoteStr(rptr)}]. Do you want to change it?
|
||||
</div>
|
||||
<div>
|
||||
{this.renderConnDropdown()}
|
||||
<ConnectionDropdown curRemote={curRemote} allowNewConn={true} onSelectRemote={this.selectRemote} onNewConn={this.clickNewConnection}/>
|
||||
</div>
|
||||
<div className="text-caption cr-help-text">
|
||||
To change connection from the command line use `cr [alias|user@host]`
|
||||
</div>
|
||||
</div>
|
||||
<div className="newtab-spacer"/>
|
||||
<div className="newtab-section settings-field">
|
||||
<div className="text-s1">
|
||||
<div className="text-s1 unselectable">
|
||||
Name
|
||||
</div>
|
||||
<div className="settings-input">
|
||||
@ -198,7 +127,7 @@ class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||
</div>
|
||||
<div className="newtab-spacer"/>
|
||||
<div className="newtab-section">
|
||||
<div className="text-s1">
|
||||
<div className="text-s1 unselectable">
|
||||
Select the color
|
||||
</div>
|
||||
<div className="control-iconlist">
|
||||
@ -219,7 +148,7 @@ class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||
|
||||
// screen is not null
|
||||
@mobxReact.observer
|
||||
class ScreenWindowView extends React.Component<{ screen: Screen }, {}> {
|
||||
class ScreenWindowView extends React.Component<{ session: Session, screen: Screen }, {}> {
|
||||
rszObs: any;
|
||||
windowViewRef: React.RefObject<any>;
|
||||
|
||||
@ -303,7 +232,7 @@ class ScreenWindowView extends React.Component<{ screen: Screen }, {}> {
|
||||
<div className="window-view" ref={this.windowViewRef} data-screenid={screen.screenId}>
|
||||
<div key="lines" className="lines"></div>
|
||||
<div key="window-empty" className={cn("window-empty", { "should-fade": fade })}>
|
||||
<div>{message}</div>
|
||||
<div className="text-standard">{message}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -344,7 +273,7 @@ class ScreenWindowView extends React.Component<{ screen: Screen }, {}> {
|
||||
}
|
||||
|
||||
render() {
|
||||
let { screen } = this.props;
|
||||
let { session, screen } = this.props;
|
||||
let win = this.getScreenLines();
|
||||
if (win == null || !win.loaded.get()) {
|
||||
return this.renderError("...", true);
|
||||
@ -379,7 +308,17 @@ class ScreenWindowView extends React.Component<{ screen: Screen }, {}> {
|
||||
</div>
|
||||
</div>
|
||||
<If condition={lines.length == 0}>
|
||||
<NewTabSettings screen={screen}/>
|
||||
<If condition={screen.nextLineNum.get() == 1}>
|
||||
<NewTabSettings screen={screen}/>
|
||||
</If>
|
||||
<If condition={screen.nextLineNum.get() != 1}>
|
||||
<div className="window-view" ref={this.windowViewRef} data-screenid={screen.screenId}>
|
||||
<div key="lines" className="lines"></div>
|
||||
<div key="window-empty" className={cn("window-empty")}>
|
||||
<div><code className="text-standard">[workspace="{session.name.get()}" screen="{screen.name.get()}"]</code></div>
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
</If>
|
||||
<If condition={screen.isWebShared()}>
|
||||
<div key="share-tag" className="share-tag">
|
||||
|
@ -8,15 +8,14 @@
|
||||
border-radius: 12px 0px 0px 0px;
|
||||
}
|
||||
|
||||
&.color-default {
|
||||
&.color-green, &.color-default {
|
||||
svg.left-icon path {
|
||||
fill: @tab-green;
|
||||
}
|
||||
}
|
||||
|
||||
&.color-green {
|
||||
svg.left-icon path {
|
||||
fill: @tab-green;
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-green;
|
||||
background: linear-gradient(180deg, rgba(88, 193, 66, 0.20) 9.34%, rgba(88, 193, 66, 0.03) 44.16%, rgba(88, 193, 66, 0.00) 86.79%);
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,29 +23,54 @@
|
||||
svg.left-icon path {
|
||||
fill: @tab-orange;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-orange;
|
||||
background: linear-gradient(180deg, rgba(239, 113, 59, 0.20) 9.34%, rgba(239, 113, 59, 0.03) 44.16%, rgba(239, 113, 59, 0.00) 86.79%);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-red {
|
||||
svg.left-icon path {
|
||||
fill: @tab-red;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-red;
|
||||
background: linear-gradient(180deg, rgba(229, 77, 46, 0.20) 9.34%, rgba(229, 77, 46, 0.03) 44.16%, rgba(229, 77, 46, 0.00) 86.79%);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-yellow {
|
||||
svg.left-icon path {
|
||||
fill: @tab-yellow;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-yellow;
|
||||
background: linear-gradient(180deg, rgba(224, 185, 86, 0.20) 9.34%, rgba(224, 185, 86, 0.03) 44.16%, rgba(224, 185, 86, 0.00) 86.79%);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-blue {
|
||||
svg.left-icon path {
|
||||
fill: @tab-blue;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-blue;
|
||||
background: linear-gradient(180deg, rgba(57, 113, 255, 0.20) 9.34%, rgba(57, 113, 255, 0.03) 44.16%, rgba(57, 113, 255, 0.00) 77.18%);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-magenta {
|
||||
&.color-mint {
|
||||
svg.left-icon path {
|
||||
fill: @tab-magenta;
|
||||
fill: @tab-mint;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-mint;
|
||||
background: linear-gradient(180deg, rgba(75, 255, 169, 0.20) 9.34%, rgba(75, 255, 169, 0.03) 44.16%, rgba(75, 255, 169, 0.00) 77.18%);
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,24 +78,44 @@
|
||||
svg.left-icon path {
|
||||
fill: @tab-cyan;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-cyan;
|
||||
background: linear-gradient(180deg, rgba(75, 223, 255, 0.20) 9.34%, rgba(75, 223, 255, 0.03) 44.16%, rgba(58, 186, 214, 0.00) 86.79%);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-white {
|
||||
svg.left-icon path {
|
||||
fill: @tab-white;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-white;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.20) 9.34%, rgba(255, 255, 255, 0.03) 44.16%, rgba(255, 255, 255, 0.00) 86.79%);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-violet {
|
||||
svg.left-icon path {
|
||||
fill: @tab-violet;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-violet;
|
||||
background: linear-gradient(180deg, rgba(186, 118, 255, 0.20) 9.34%, rgba(186, 118, 255, 0.03) 44.16%, rgba(186, 118, 255, 0.00) 86.79%);
|
||||
}
|
||||
}
|
||||
|
||||
&.color-pink {
|
||||
svg.left-icon path {
|
||||
fill: @tab-pink;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-top: 1px solid @tab-pink;
|
||||
background: linear-gradient(180deg, rgba(255, 136, 165, 0.20) 9.34%, rgba(255, 136, 165, 0.03) 44.16%, rgba(255, 136, 165, 0.00) 86.79%);
|
||||
}
|
||||
}
|
||||
|
||||
.web-share-icon {
|
||||
@ -134,9 +178,6 @@
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
&:hover:not(.is-active) {
|
||||
border-color: @disabled-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .screen-tab .tab-index {
|
||||
|
@ -11,7 +11,6 @@ import cn from "classnames";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import dayjs from "dayjs";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { generateBackgroundWithGradient } from "../../../util/util";
|
||||
import { GlobalModel, GlobalCommandRunner, Session, ScreenLines, Screen } from "../../../model/model";
|
||||
import { renderCmdText } from "../../common/common";
|
||||
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
|
||||
@ -121,14 +120,6 @@ class ScreenTabs extends React.Component<{ session: Session }, {}> {
|
||||
<i title="shared to web" className="fa-sharp fa-solid fa-share-nodes web-share-icon" />
|
||||
) : null;
|
||||
|
||||
const style = { borderColor: "transparent", background: "none" };
|
||||
if (screen.isActive()) {
|
||||
let tabColor = screen.getTabColor();
|
||||
if (tabColor === "default") tabColor = "green";
|
||||
style.borderColor = tabColor;
|
||||
style.background = generateBackgroundWithGradient(tabColor);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={screen.screenId}
|
||||
@ -138,7 +129,6 @@ class ScreenTabs extends React.Component<{ session: Session }, {}> {
|
||||
{ "is-active": activeScreenId == screen.screenId, "is-archived": screen.archived.get() },
|
||||
"color-" + screen.getTabColor()
|
||||
)}
|
||||
style={style}
|
||||
onClick={() => this.handleSwitchScreen(screen.screenId)}
|
||||
onContextMenu={(event) => this.openScreenSettings(event, screen)}
|
||||
>
|
||||
|
@ -14,6 +14,15 @@
|
||||
border-radius: 8px;
|
||||
transition: width 0.2s ease;
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
.center-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
color: @text-secondary;
|
||||
}
|
||||
}
|
||||
.collapsed + .session-view {
|
||||
max-width: calc(100% - 6.7em);
|
||||
|
@ -25,7 +25,11 @@ class WorkspaceView extends React.Component<{}, {}> {
|
||||
let model = GlobalModel;
|
||||
let session = model.getActiveSession();
|
||||
if (session == null) {
|
||||
return <div className="session-view">(no active session)</div>;
|
||||
return (
|
||||
<div className="session-view">
|
||||
<div className="center-message"><div>(no active workspace)</div></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
let activeScreen = session.getActiveScreen();
|
||||
let cmdInputHeight = model.inputModel.cmdInputHeight.get();
|
||||
@ -39,7 +43,7 @@ class WorkspaceView extends React.Component<{}, {}> {
|
||||
<div className={cn("session-view", { "is-hidden": isHidden })} data-sessionid={session.sessionId}>
|
||||
<ScreenTabs session={session} />
|
||||
<ErrorBoundary>
|
||||
<ScreenView screen={activeScreen} />
|
||||
<ScreenView session={session} screen={activeScreen} />
|
||||
<div className="cmdinput-height-placeholder" style={{ height: cmdInputHeight }}></div>
|
||||
<CmdInput />
|
||||
</ErrorBoundary>
|
||||
|
@ -13,15 +13,15 @@ import * as util from "util";
|
||||
import { sprintf } from "sprintf-js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const PromptAppPathVarName = "PROMPT_APP_PATH";
|
||||
const PromptDevVarName = "PROMPT_DEV";
|
||||
const AuthKeyFile = "prompt.authkey";
|
||||
const WaveAppPathVarName = "WAVETERM_APP_PATH";
|
||||
const WaveDevVarName = "WAVETERM_DEV";
|
||||
const AuthKeyFile = "waveterm.authkey";
|
||||
const DevServerEndpoint = "http://127.0.0.1:8090";
|
||||
const ProdServerEndpoint = "http://127.0.0.1:1619";
|
||||
|
||||
let isDev = process.env[PromptDevVarName] != null;
|
||||
let scHome = getPromptHomeDir();
|
||||
ensureDir(scHome);
|
||||
let isDev = process.env[WaveDevVarName] != null;
|
||||
let waveHome = getWaveHomeDir();
|
||||
ensureDir(waveHome);
|
||||
let DistDir = isDev ? "dist-dev" : "dist";
|
||||
let GlobalAuthKey = "";
|
||||
let instanceId = uuidv4();
|
||||
@ -43,7 +43,7 @@ let loggerConfig = {
|
||||
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
||||
winston.format.printf((info) => `${info.timestamp} ${info.message}`)
|
||||
),
|
||||
transports: [new winston.transports.File({ filename: path.join(scHome, "waveterm-app.log"), level: "info" })],
|
||||
transports: [new winston.transports.File({ filename: path.join(waveHome, "waveterm-app.log"), level: "info" })],
|
||||
};
|
||||
if (isDev) {
|
||||
loggerConfig.transports.push(new winston.transports.Console());
|
||||
@ -59,15 +59,15 @@ function log(...msg) {
|
||||
console.log = log;
|
||||
console.log(
|
||||
sprintf(
|
||||
"waveterm-app starting, PROMPT_HOME=%s, apppath=%s arch=%s/%s",
|
||||
scHome,
|
||||
"waveterm-app starting, WAVETERM_HOME=%s, apppath=%s arch=%s/%s",
|
||||
waveHome,
|
||||
getAppBasePath(),
|
||||
unamePlatform,
|
||||
unameArch
|
||||
)
|
||||
);
|
||||
if (isDev) {
|
||||
console.log("prompt-app PROMPT_DEV set");
|
||||
console.log("waveterm-app WAVETERM_DEV set");
|
||||
}
|
||||
let app = electron.app;
|
||||
app.setName(isDev ? "Wave (Dev)" : "Wave");
|
||||
@ -79,20 +79,20 @@ electron.dialog.showErrorBox = (title, content) => {
|
||||
};
|
||||
|
||||
// must match golang
|
||||
function getPromptHomeDir() {
|
||||
let scHome = process.env.PROMPT_HOME;
|
||||
if (scHome == null) {
|
||||
function getWaveHomeDir() {
|
||||
let waveHome = process.env.WAVETERM_HOME;
|
||||
if (waveHome == null) {
|
||||
let homeDir = process.env.HOME;
|
||||
if (homeDir == null) {
|
||||
homeDir = "/";
|
||||
}
|
||||
scHome = path.join(homeDir, isDev ? "prompt-dev" : "prompt");
|
||||
waveHome = path.join(homeDir, isDev ? ".waveterm-dev" : ".waveterm");
|
||||
}
|
||||
return scHome;
|
||||
return waveHome;
|
||||
}
|
||||
|
||||
// for dev, this is just the github.com/commandlinedev/prompt-client directory
|
||||
// for prod, this is .../Prompt.app/Contents/Resources/app
|
||||
// for dev, this is just the waveterm directory
|
||||
// for prod, this is .../Wave.app/Contents/Resources/app
|
||||
function getAppBasePath() {
|
||||
return path.dirname(__dirname);
|
||||
}
|
||||
@ -113,14 +113,14 @@ function getWaveSrvPath() {
|
||||
|
||||
function getWaveSrvCmd() {
|
||||
let waveSrvPath = getWaveSrvPath();
|
||||
let scHome = getPromptHomeDir();
|
||||
let logFile = path.join(scHome, "wavesrv.log");
|
||||
let waveHome = getWaveHomeDir();
|
||||
let logFile = path.join(waveHome, "wavesrv.log");
|
||||
return `${waveSrvPath} >> "${logFile}" 2>&1`;
|
||||
}
|
||||
|
||||
function getWaveSrvCwd() {
|
||||
let scHome = getPromptHomeDir();
|
||||
return scHome;
|
||||
let waveHome = getWaveHomeDir();
|
||||
return waveHome;
|
||||
}
|
||||
|
||||
function ensureDir(dir) {
|
||||
@ -128,7 +128,7 @@ function ensureDir(dir) {
|
||||
}
|
||||
|
||||
function readAuthKey() {
|
||||
let homeDir = getPromptHomeDir();
|
||||
let homeDir = getWaveHomeDir();
|
||||
let authKeyFileName = path.join(homeDir, AuthKeyFile);
|
||||
if (!fs.existsSync(authKeyFileName)) {
|
||||
let authKeyStr = String(uuidv4());
|
||||
@ -146,9 +146,24 @@ function readAuthKey() {
|
||||
let menuTemplate = [
|
||||
{
|
||||
role: "appMenu",
|
||||
submenu: [
|
||||
{
|
||||
label: 'About Wave Terminal',
|
||||
click: () => {
|
||||
MainWindow?.webContents.send('menu-item-about');
|
||||
}
|
||||
},
|
||||
{ type: "separator" },
|
||||
{ role: "services" },
|
||||
{ type: "separator" },
|
||||
{ role: "hide" },
|
||||
{ role: "hideOthers" },
|
||||
{ type: "separator" },
|
||||
{ role: "quit" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "File",
|
||||
label: "Filemenu",
|
||||
submenu: [{ role: "close" }, { role: "forceReload" }],
|
||||
},
|
||||
{
|
||||
@ -165,7 +180,7 @@ let menuTemplate = [
|
||||
let menu = electron.Menu.buildFromTemplate(menuTemplate);
|
||||
electron.Menu.setApplicationMenu(menu);
|
||||
|
||||
let MainWindow = null;
|
||||
let MainWindow: Electron.BrowserWindow | null = null;
|
||||
|
||||
function getMods(input: any) {
|
||||
return { meta: input.meta, shift: input.shift, ctrl: input.control, alt: input.alt };
|
||||
@ -501,9 +516,9 @@ function runWaveSrv() {
|
||||
pReject = argReject;
|
||||
});
|
||||
let envCopy = Object.assign({}, process.env);
|
||||
envCopy[PromptAppPathVarName] = getAppBasePath();
|
||||
envCopy[WaveAppPathVarName] = getAppBasePath();
|
||||
if (isDev) {
|
||||
envCopy[PromptDevVarName] = "1";
|
||||
envCopy[WaveDevVarName] = "1";
|
||||
}
|
||||
console.log("trying to run local server", getWaveSrvPath());
|
||||
let proc = child_process.spawn("/bin/bash", ["-c", getWaveSrvCmd()], {
|
||||
|
@ -19,6 +19,7 @@ contextBridge.exposeInMainWorld("api", {
|
||||
onMetaPageDown: (callback) => ipcRenderer.on("meta-pagedown", callback),
|
||||
onBracketCmd: (callback) => ipcRenderer.on("bracket-cmd", callback),
|
||||
onDigitCmd: (callback) => ipcRenderer.on("digit-cmd", callback),
|
||||
onMenuItemAbout: (callback) => ipcRenderer.on("menu-item-about", callback),
|
||||
contextScreen: (screenOpts, position) => ipcRenderer.send("context-screen", screenOpts, position),
|
||||
contextEditMenu: (position, opts) => ipcRenderer.send("context-editmenu", position, opts),
|
||||
onWaveSrvStatusChange: (callback) => ipcRenderer.on("wavesrv-status-change", callback),
|
||||
|
@ -10,9 +10,9 @@ import * as DOMPurify from "dompurify";
|
||||
import { loadFonts } from "./util/util";
|
||||
|
||||
// @ts-ignore
|
||||
let VERSION = __PROMPT_VERSION__;
|
||||
let VERSION = __WAVETERM_VERSION__;
|
||||
// @ts-ignore
|
||||
let BUILD = __PROMPT_BUILD__;
|
||||
let BUILD = __WAVETERM_BUILD__;
|
||||
|
||||
loadFonts();
|
||||
|
||||
@ -34,4 +34,4 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
(window as any).sprintf = sprintf;
|
||||
(window as any).DOMPurify = DOMPurify;
|
||||
|
||||
console.log("PROMPT", VERSION, BUILD);
|
||||
console.log("WaveTerm", VERSION, BUILD);
|
||||
|
@ -94,12 +94,12 @@ const MinFontSize = 8;
|
||||
const MaxFontSize = 15;
|
||||
const InputChunkSize = 500;
|
||||
const RemoteColors = ["red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"];
|
||||
const TabColors = ["green", "blue", "yellow", "pink", "magenta", "cyan", "violet", "orange", "red", "white"];
|
||||
const TabColors = ["red", "orange", "yellow", "green", "mint", "cyan", "blue", "violet", "pink", "white"];
|
||||
|
||||
// @ts-ignore
|
||||
const VERSION = __PROMPT_VERSION__;
|
||||
const VERSION = __WAVETERM_VERSION__;
|
||||
// @ts-ignore
|
||||
const BUILD = __PROMPT_BUILD__;
|
||||
const BUILD = __WAVETERM_BUILD__;
|
||||
|
||||
type LineContainerModel = {
|
||||
loadTerminalRenderer: (elem: Element, line: LineType, cmd: Cmd, width: number) => void;
|
||||
@ -180,6 +180,7 @@ type ElectronApi = {
|
||||
onICmd: (callback: (mods: KeyModsType) => void) => void;
|
||||
onLCmd: (callback: (mods: KeyModsType) => void) => void;
|
||||
onHCmd: (callback: (mods: KeyModsType) => void) => void;
|
||||
onMenuItemAbout: (callback: () => void) => void;
|
||||
onMetaArrowUp: (callback: () => void) => void;
|
||||
onMetaArrowDown: (callback: () => void) => void;
|
||||
onMetaPageUp: (callback: () => void) => void;
|
||||
@ -331,6 +332,7 @@ class Screen {
|
||||
name: OV<string>;
|
||||
archived: OV<boolean>;
|
||||
curRemote: OV<RemotePtrType>;
|
||||
nextLineNum: OV<number>;
|
||||
lastScreenSize: WindowSize;
|
||||
lastCols: number;
|
||||
lastRows: number;
|
||||
@ -348,6 +350,7 @@ class Screen {
|
||||
this.sessionId = sdata.sessionid;
|
||||
this.screenId = sdata.screenid;
|
||||
this.name = mobx.observable.box(sdata.name, { name: "screen-name" });
|
||||
this.nextLineNum = mobx.observable.box(sdata.nextlinenum, { name: "screen-nextlinenum" });
|
||||
this.screenIdx = mobx.observable.box(sdata.screenidx, {
|
||||
name: "screen-screenidx",
|
||||
});
|
||||
@ -423,6 +426,7 @@ class Screen {
|
||||
this.screenIdx.set(data.screenidx);
|
||||
this.opts.set(data.screenopts);
|
||||
this.name.set(data.name);
|
||||
this.nextLineNum.set(data.nextlinenum);
|
||||
this.archived.set(!!data.archived);
|
||||
let oldSelectedLine = this.selectedLine.get();
|
||||
let oldFocusType = this.focusType.get();
|
||||
@ -2696,8 +2700,8 @@ class Model {
|
||||
name: "alertMessage",
|
||||
});
|
||||
alertPromiseResolver: (result: boolean) => void;
|
||||
welcomeModalOpen: OV<boolean> = mobx.observable.box(false, {
|
||||
name: "welcomeModalOpen",
|
||||
aboutModalOpen: OV<boolean> = mobx.observable.box(false, {
|
||||
name: "aboutModalOpen",
|
||||
});
|
||||
screenSettingsModal: OV<{ sessionId: string; screenId: string }> = mobx.observable.box(null, {
|
||||
name: "screenSettingsModal",
|
||||
@ -2759,6 +2763,7 @@ class Model {
|
||||
getApi().onICmd(this.onICmd.bind(this));
|
||||
getApi().onLCmd(this.onLCmd.bind(this));
|
||||
getApi().onHCmd(this.onHCmd.bind(this));
|
||||
getApi().onMenuItemAbout(this.onMenuItemAbout.bind(this));
|
||||
getApi().onMetaArrowUp(this.onMetaArrowUp.bind(this));
|
||||
getApi().onMetaArrowDown(this.onMetaArrowDown.bind(this));
|
||||
getApi().onMetaPageUp(this.onMetaPageUp.bind(this));
|
||||
@ -2973,10 +2978,6 @@ class Model {
|
||||
GlobalModel.lineSettingsModal.set(null);
|
||||
didSomething = true;
|
||||
}
|
||||
if (GlobalModel.welcomeModalOpen.get()) {
|
||||
GlobalModel.welcomeModalOpen.set(false);
|
||||
didSomething = true;
|
||||
}
|
||||
})();
|
||||
return didSomething;
|
||||
}
|
||||
@ -3106,6 +3107,12 @@ class Model {
|
||||
}
|
||||
}
|
||||
|
||||
onMenuItemAbout(): void {
|
||||
mobx.action(() => {
|
||||
this.aboutModalOpen.set(true);
|
||||
})();
|
||||
}
|
||||
|
||||
onMetaPageUp(): void {
|
||||
GlobalCommandRunner.screenSelectLine("-1");
|
||||
}
|
||||
|
1
src/plugins/csv/readme.md
Normal file
1
src/plugins/csv/readme.md
Normal file
@ -0,0 +1 @@
|
||||
|
1
src/plugins/image/readme.md
Normal file
1
src/plugins/image/readme.md
Normal file
@ -0,0 +1 @@
|
||||
|
@ -13,10 +13,11 @@ import "./markdown.less";
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
const MaxMarkdownSize = 200000;
|
||||
const DefaultMaxMarkdownWidth = 1000;
|
||||
|
||||
@mobxReact.observer
|
||||
class SimpleMarkdownRenderer extends React.Component<
|
||||
{ data: T.ExtBlob; context: T.RendererContext; opts: T.RendererOpts; savedHeight: number },
|
||||
{ data: T.ExtBlob; context: T.RendererContext; opts: T.RendererOpts; savedHeight: number, lineState: T.LineStateType },
|
||||
{}
|
||||
> {
|
||||
markdownText: OV<string> = mobx.observable.box(null, { name: "markdownText" });
|
||||
@ -73,7 +74,7 @@ class SimpleMarkdownRenderer extends React.Component<
|
||||
maxHeight: opts.maxSize.height,
|
||||
}}
|
||||
>
|
||||
<Markdown text={this.markdownText.get()} style={{ maxHeight: opts.maxSize.height }} />
|
||||
<Markdown text={this.markdownText.get()} style={{ maxHeight: opts.maxSize.height, maxWidth: DefaultMaxMarkdownWidth }} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
1
src/plugins/markdown/readme.md
Normal file
1
src/plugins/markdown/readme.md
Normal file
@ -0,0 +1 @@
|
||||
|
1
src/plugins/markdown/screenshots/.gitignore
vendored
Normal file
1
src/plugins/markdown/screenshots/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
# placeholder
|
1
src/plugins/mustache/readme.md
Normal file
1
src/plugins/mustache/readme.md
Normal file
@ -0,0 +1 @@
|
||||
|
1
src/plugins/mustache/screenshots/.gitignore
vendored
Normal file
1
src/plugins/mustache/screenshots/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
# placeholder
|
1
src/plugins/openai/readme.md
Normal file
1
src/plugins/openai/readme.md
Normal file
@ -0,0 +1 @@
|
||||
|
1
src/plugins/openai/screenshots/.gitignore
vendored
Normal file
1
src/plugins/openai/screenshots/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
# placeholder
|
@ -137,10 +137,9 @@ class PluginModelClass {
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
imagePaths = screenshotsContext.keys().map(screenshotsContext);
|
||||
} catch (error) {
|
||||
console.error(`Failed to load screenshots for plugin ${plugin.name}`);
|
||||
// this is no longer an error. we don't need to require screenshots
|
||||
}
|
||||
plugin.screenshots = imagePaths.map((path) => path.default);
|
||||
}
|
||||
@ -148,7 +147,7 @@ class PluginModelClass {
|
||||
// use dynamic import to attach the icon etc. ensure that the 'name' matches the dir the plugin is in
|
||||
async loadPluginResources(plugin) {
|
||||
this.attachScreenshots(plugin);
|
||||
// attach other resources
|
||||
// attach other resources, these show an error because all plugins should have an icon, readme, and meta
|
||||
const handleImportError = (error, resourceType) =>
|
||||
console.error(`Failed to load ${resourceType} for plugin ${plugin.name}`);
|
||||
const iconPromise = import(`../plugins/${plugin.name}/icon.svg`)
|
||||
|
@ -283,19 +283,19 @@ function loadFonts() {
|
||||
style: "normal",
|
||||
weight: "700",
|
||||
});
|
||||
// let faFont = new FontFace("FontAwesome", "url(static/fonts/fontawesome-webfont-4.7.woff2)", {
|
||||
// style: "normal",
|
||||
// weight: "normal",
|
||||
// });
|
||||
let faFont = new FontFace("FontAwesome", "url(public/fonts/fontawesome-webfont-4.7.woff2)", {
|
||||
style: "normal",
|
||||
weight: "normal",
|
||||
});
|
||||
let docFonts: any = document.fonts; // work around ts typing issue
|
||||
docFonts.add(jbmFontNormal);
|
||||
docFonts.add(jbmFont200);
|
||||
docFonts.add(jbmFont700);
|
||||
// docFonts.add(faFont);
|
||||
docFonts.add(faFont);
|
||||
jbmFontNormal.load();
|
||||
jbmFont200.load();
|
||||
jbmFont700.load();
|
||||
// faFont.load();
|
||||
faFont.load();
|
||||
}
|
||||
|
||||
const DOW_STRS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
@ -390,19 +390,6 @@ function getColorRGB(colorInput) {
|
||||
return computedColorStyle;
|
||||
}
|
||||
|
||||
// usgae witing an element : style={background:generateBackgroundWithGradient('red')}
|
||||
function generateBackgroundWithGradient(colorName = "white", decay = 3) {
|
||||
const rgb = getColorRGB(colorName);
|
||||
const rgba = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/);
|
||||
const r = rgba[1];
|
||||
const g = rgba[2];
|
||||
const b = rgba[3];
|
||||
const lambda = -Math.log(0.01 * decay) / (0.1 * decay);
|
||||
const percentages = [9.34, 44.16, 86.79];
|
||||
const opacities = percentages.map((p) => Math.exp((-lambda * p) / 100));
|
||||
return `linear-gradient(180deg, rgba(${r}, ${g}, ${b}, ${opacities[0]}) ${percentages[0]}%, rgba(${r}, ${g}, ${b}, ${opacities[1]}) ${percentages[1]}%, rgba(${r}, ${g}, ${b}, 0) ${percentages[2]}%)`;
|
||||
}
|
||||
|
||||
function commandRtnHandler(prtn: Promise<CommandRtnType>, errorMessage: OV<string>) {
|
||||
prtn.then((crtn) => {
|
||||
if (crtn.success) {
|
||||
@ -435,7 +422,6 @@ export {
|
||||
isBoolEq,
|
||||
hasNoModifiers,
|
||||
openLink,
|
||||
generateBackgroundWithGradient,
|
||||
getColorRGB,
|
||||
commandRtnHandler,
|
||||
};
|
||||
|
22
versionmeta.json
Normal file
22
versionmeta.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"type": "dmg",
|
||||
"arch": "macos-arm64",
|
||||
"channel": "stable",
|
||||
"updated": "2023-10-31",
|
||||
"version": "v0.4.0",
|
||||
"sha": "016876cf3e9fb600d6798891c8566a7ac5d1446a",
|
||||
"url": "https://www.getprompt.dev/download/prompt-macos-arm64-v0.4.0.dmg"
|
||||
},
|
||||
{
|
||||
"type": "dmg",
|
||||
"arch": "macos-amd64",
|
||||
"channel": "stable",
|
||||
"updated": "2023-10-31",
|
||||
"version": "v0.4.0",
|
||||
"sha": "d0bc280e4630a716126e47e700d8d4364db966e6",
|
||||
"url": "https://www.getprompt.dev/download/prompt-macos-x86-v0.4.0.dmg"
|
||||
}
|
||||
]
|
||||
}
|
@ -48,8 +48,8 @@ const HttpWriteTimeout = 21 * time.Second
|
||||
const HttpMaxHeaderBytes = 60000
|
||||
const HttpTimeoutDuration = 21 * time.Second
|
||||
|
||||
const MainServerAddr = "127.0.0.1:1619" // PromptServer, P=16, S=19, PS=1619
|
||||
const WebSocketServerAddr = "127.0.0.1:1623" // PromptWebsock, P=16, W=23, PW=1623
|
||||
const MainServerAddr = "127.0.0.1:1619" // wavesrv, P=16, S=19, PS=1619
|
||||
const WebSocketServerAddr = "127.0.0.1:1623" // wavesrv:websocket, P=16, W=23, PW=1623
|
||||
const MainServerDevAddr = "127.0.0.1:8090"
|
||||
const WebSocketServerDevAddr = "127.0.0.1:8091"
|
||||
const WSStateReconnectTime = 30 * time.Second
|
||||
@ -763,11 +763,11 @@ func installSignalHandlers() {
|
||||
|
||||
func doShutdown(reason string) {
|
||||
shutdownOnce.Do(func() {
|
||||
log.Printf("[prompt] local server %v, start shutdown\n", reason)
|
||||
log.Printf("[wave] local server %v, start shutdown\n", reason)
|
||||
sendTelemetryWrapper()
|
||||
log.Printf("[prompt] closing db connection\n")
|
||||
log.Printf("[wave] closing db connection\n")
|
||||
sstore.CloseDB()
|
||||
log.Printf("[prompt] *** shutting down local server\n")
|
||||
log.Printf("[wave] *** shutting down local server\n")
|
||||
time.Sleep(1 * time.Second)
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGINT)
|
||||
time.Sleep(5 * time.Second)
|
||||
@ -787,14 +787,13 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
scHomeDir := scbase.GetPromptHomeDir()
|
||||
log.Printf("[prompt] *** starting local server\n")
|
||||
log.Printf("[prompt] local server version %s+%s\n", scbase.PromptVersion, scbase.BuildTime)
|
||||
log.Printf("[prompt] homedir = %q\n", scHomeDir)
|
||||
scHomeDir := scbase.GetWaveHomeDir()
|
||||
log.Printf("[wave] *** starting wavesrv version %s+%s\n", scbase.WaveVersion, scbase.BuildTime)
|
||||
log.Printf("[wave] homedir = %q\n", scHomeDir)
|
||||
|
||||
scLock, err := scbase.AcquirePromptLock()
|
||||
scLock, err := scbase.AcquireWaveLock()
|
||||
if err != nil || scLock == nil {
|
||||
log.Printf("[error] cannot acquire prompt lock: %v\n", err)
|
||||
log.Printf("[error] cannot acquire wave lock: %v\n", err)
|
||||
return
|
||||
}
|
||||
if len(os.Args) >= 2 && strings.HasPrefix(os.Args[1], "--migrate") {
|
||||
@ -804,7 +803,7 @@ func main() {
|
||||
}
|
||||
return
|
||||
}
|
||||
authKey, err := scbase.ReadPromptAuthKey()
|
||||
authKey, err := scbase.ReadWaveAuthKey()
|
||||
if err != nil {
|
||||
log.Printf("[error] %v\n", err)
|
||||
return
|
||||
@ -826,7 +825,7 @@ func main() {
|
||||
log.Printf("[error] ensuring local remote: %v\n", err)
|
||||
return
|
||||
}
|
||||
_, err = sstore.EnsureDefaultSession(context.Background())
|
||||
err = sstore.EnsureOneSession(context.Background())
|
||||
if err != nil {
|
||||
log.Printf("[error] ensuring default session: %v\n", err)
|
||||
return
|
||||
|
@ -1,3 +1,3 @@
|
||||
UPDATE screen
|
||||
SET screenopts = json_set(screenopts, '$.tabcolor', 'default')
|
||||
WHERE json_extract(screenopts, '$.tabcolor') = 'black';
|
||||
WHERE json_extract(screenopts, '$.tabcolor') = 'black' OR json_extract(screenopts, '$.tabcolor') = 'magenta';
|
||||
|
@ -71,7 +71,7 @@ const (
|
||||
KwArgLang = "lang"
|
||||
)
|
||||
|
||||
var ColorNames = []string{"yellow", "blue", "pink", "magenta", "cyan", "violet", "orange", "green", "red", "white"}
|
||||
var ColorNames = []string{"yellow", "blue", "pink", "mint", "cyan", "violet", "orange", "green", "red", "white"}
|
||||
var RemoteColorNames = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"}
|
||||
var RemoteSetArgs = []string{"alias", "connectmode", "key", "password", "autoinstall", "color"}
|
||||
|
||||
@ -379,7 +379,7 @@ func doCmdHistoryExpansion(ctx context.Context, ids resolvedIds, cmdStr string)
|
||||
return doHistoryExpansion(ctx, ids, -1)
|
||||
}
|
||||
if strings.HasPrefix(cmdStr, "!-") {
|
||||
return "", fmt.Errorf("prompt does not support negative history offsets, use a stable positive history offset instead: '![linenum]'")
|
||||
return "", fmt.Errorf("wave does not support negative history offsets, use a stable positive history offset instead: '![linenum]'")
|
||||
}
|
||||
m := histExpansionRe.FindStringSubmatch(cmdStr)
|
||||
if m == nil {
|
||||
@ -435,7 +435,7 @@ func SyncCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.
|
||||
}
|
||||
runPacket := packet.MakeRunPacket()
|
||||
runPacket.ReqId = uuid.New().String()
|
||||
runPacket.CK = base.MakeCommandKey(ids.ScreenId, scbase.GenPromptUUID())
|
||||
runPacket.CK = base.MakeCommandKey(ids.ScreenId, scbase.GenWaveUUID())
|
||||
runPacket.UsePty = true
|
||||
ptermVal := defaultStr(pk.Kwargs["pterm"], DefaultPTERM)
|
||||
runPacket.TermOpts, err = GetUITermOpts(pk.UIContext.WinSize, ptermVal)
|
||||
@ -531,7 +531,7 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U
|
||||
// runPacket.State is set in remote.RunCommand()
|
||||
runPacket := packet.MakeRunPacket()
|
||||
runPacket.ReqId = uuid.New().String()
|
||||
runPacket.CK = base.MakeCommandKey(ids.ScreenId, scbase.GenPromptUUID())
|
||||
runPacket.CK = base.MakeCommandKey(ids.ScreenId, scbase.GenWaveUUID())
|
||||
runPacket.UsePty = true
|
||||
ptermVal := defaultStr(pk.Kwargs["pterm"], DefaultPTERM)
|
||||
runPacket.TermOpts, err = GetUITermOpts(pk.UIContext.WinSize, ptermVal)
|
||||
@ -575,7 +575,7 @@ func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, history
|
||||
return fmt.Errorf("cannot add to history, error looking up incognito status of screen: %v", err)
|
||||
}
|
||||
hitem := &sstore.HistoryItemType{
|
||||
HistoryId: scbase.GenPromptUUID(),
|
||||
HistoryId: scbase.GenWaveUUID(),
|
||||
Ts: time.Now().UnixMilli(),
|
||||
UserId: DefaultUserId,
|
||||
SessionId: ids.SessionId,
|
||||
@ -1115,7 +1115,7 @@ func RemoteNewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
|
||||
return nil, fmt.Errorf("/remote:new %v", err)
|
||||
}
|
||||
r := &sstore.RemoteType{
|
||||
RemoteId: scbase.GenPromptUUID(),
|
||||
RemoteId: scbase.GenWaveUUID(),
|
||||
RemoteType: sstore.RemoteTypeSsh,
|
||||
RemoteAlias: editArgs.Alias,
|
||||
RemoteCanonicalName: editArgs.CanonicalName,
|
||||
@ -1606,7 +1606,7 @@ func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up
|
||||
func makeDynCmd(ctx context.Context, metaCmd string, ids resolvedIds, cmdStr string, termOpts sstore.TermOpts) (*sstore.CmdType, error) {
|
||||
cmd := &sstore.CmdType{
|
||||
ScreenId: ids.ScreenId,
|
||||
LineId: scbase.GenPromptUUID(),
|
||||
LineId: scbase.GenWaveUUID(),
|
||||
CmdStr: cmdStr,
|
||||
RawCmdStr: cmdStr,
|
||||
Remote: ids.Remote.RemotePtr,
|
||||
@ -1631,7 +1631,7 @@ func makeDynCmd(ctx context.Context, metaCmd string, ids resolvedIds, cmdStr str
|
||||
func makeStaticCmd(ctx context.Context, metaCmd string, ids resolvedIds, cmdStr string, cmdOutput []byte) (*sstore.CmdType, error) {
|
||||
cmd := &sstore.CmdType{
|
||||
ScreenId: ids.ScreenId,
|
||||
LineId: scbase.GenPromptUUID(),
|
||||
LineId: scbase.GenWaveUUID(),
|
||||
CmdStr: cmdStr,
|
||||
RawCmdStr: cmdStr,
|
||||
Remote: ids.Remote.RemotePtr,
|
||||
@ -3654,7 +3654,7 @@ func ClientShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (s
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "telemetry", boolToStr(clientData.ClientOpts.NoTelemetry, "off", "on")))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %d\n", "db-version", dbVersion))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "client-version", clientVersion))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s %s\n", "server-version", scbase.PromptVersion, scbase.BuildTime))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s %s\n", "server-version", scbase.WaveVersion, scbase.BuildTime))
|
||||
buf.WriteString(fmt.Sprintf(" %-15s %s (%s)\n", "arch", scbase.ClientArch(), scbase.MacOSRelease()))
|
||||
update := &sstore.ModelUpdate{
|
||||
Info: &sstore.InfoMsgType{
|
||||
|
@ -22,6 +22,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/armon/circbuf"
|
||||
"github.com/creack/pty"
|
||||
"github.com/google/uuid"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/shexec"
|
||||
@ -29,8 +31,6 @@ import (
|
||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbase"
|
||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/scpacket"
|
||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
||||
"github.com/creack/pty"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
@ -1133,7 +1133,7 @@ func addScVarsToState(state *packet.ShellState) *packet.ShellState {
|
||||
rtn := *state
|
||||
envMap := shexec.DeclMapFromState(&rtn)
|
||||
envMap["PROMPT"] = &shexec.DeclareDeclType{Name: "PROMPT", Value: "1", Args: "x"}
|
||||
envMap["PROMPT_VERSION"] = &shexec.DeclareDeclType{Name: "PROMPT_VERSION", Value: scbase.PromptVersion, Args: "x"}
|
||||
envMap["PROMPT_VERSION"] = &shexec.DeclareDeclType{Name: "PROMPT_VERSION", Value: scbase.WaveVersion, Args: "x"}
|
||||
rtn.ShellVars = shexec.SerializeDeclMap(envMap)
|
||||
return &rtn
|
||||
}
|
||||
|
@ -28,16 +28,16 @@ import (
|
||||
)
|
||||
|
||||
const HomeVarName = "HOME"
|
||||
const PromptHomeVarName = "PROMPT_HOME"
|
||||
const PromptDevVarName = "PROMPT_DEV"
|
||||
const WaveHomeVarName = "WAVETERM_HOME"
|
||||
const WaveDevVarName = "WAVETERM_DEV"
|
||||
const SessionsDirBaseName = "sessions"
|
||||
const ScreensDirBaseName = "screens"
|
||||
const PromptLockFile = "prompt.lock"
|
||||
const PromptDirName = "prompt"
|
||||
const PromptDevDirName = "prompt-dev"
|
||||
const PromptAppPathVarName = "PROMPT_APP_PATH"
|
||||
const PromptVersion = "v0.5.0"
|
||||
const PromptAuthKeyFileName = "prompt.authkey"
|
||||
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 WaveAuthKeyFileName = "waveterm.authkey"
|
||||
const MShellVersion = "v0.3.0"
|
||||
const DefaultMacOSShell = "/bin/bash"
|
||||
|
||||
@ -47,23 +47,23 @@ var BaseLock = &sync.Mutex{}
|
||||
var BuildTime = "-"
|
||||
|
||||
func IsDevMode() bool {
|
||||
pdev := os.Getenv(PromptDevVarName)
|
||||
pdev := os.Getenv(WaveDevVarName)
|
||||
return pdev != ""
|
||||
}
|
||||
|
||||
// must match js
|
||||
func GetPromptHomeDir() string {
|
||||
scHome := os.Getenv(PromptHomeVarName)
|
||||
func GetWaveHomeDir() string {
|
||||
scHome := os.Getenv(WaveHomeVarName)
|
||||
if scHome == "" {
|
||||
homeVar := os.Getenv(HomeVarName)
|
||||
if homeVar == "" {
|
||||
homeVar = "/"
|
||||
}
|
||||
pdev := os.Getenv(PromptDevVarName)
|
||||
pdev := os.Getenv(WaveDevVarName)
|
||||
if pdev != "" {
|
||||
scHome = path.Join(homeVar, PromptDevDirName)
|
||||
scHome = path.Join(homeVar, WaveDevDirName)
|
||||
} else {
|
||||
scHome = path.Join(homeVar, PromptDirName)
|
||||
scHome = path.Join(homeVar, WaveDirName)
|
||||
}
|
||||
|
||||
}
|
||||
@ -71,7 +71,7 @@ func GetPromptHomeDir() string {
|
||||
}
|
||||
|
||||
func MShellBinaryDir() string {
|
||||
appPath := os.Getenv(PromptAppPathVarName)
|
||||
appPath := os.Getenv(WaveAppPathVarName)
|
||||
if appPath == "" {
|
||||
appPath = "."
|
||||
}
|
||||
@ -111,13 +111,13 @@ func MShellBinaryReader(version string, goos string, goarch string) (io.ReadClos
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
func createPromptAuthKeyFile(fileName string) (string, error) {
|
||||
func createWaveAuthKeyFile(fileName string) (string, error) {
|
||||
fd, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer fd.Close()
|
||||
keyStr := GenPromptUUID()
|
||||
keyStr := GenWaveUUID()
|
||||
_, err = fd.Write([]byte(keyStr))
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -125,24 +125,24 @@ func createPromptAuthKeyFile(fileName string) (string, error) {
|
||||
return keyStr, nil
|
||||
}
|
||||
|
||||
func ReadPromptAuthKey() (string, error) {
|
||||
homeDir := GetPromptHomeDir()
|
||||
func ReadWaveAuthKey() (string, error) {
|
||||
homeDir := GetWaveHomeDir()
|
||||
err := ensureDir(homeDir)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot find/create PROMPT_HOME directory %q", homeDir)
|
||||
return "", fmt.Errorf("cannot find/create WAVETERM_HOME directory %q", homeDir)
|
||||
}
|
||||
fileName := path.Join(homeDir, PromptAuthKeyFileName)
|
||||
fileName := path.Join(homeDir, WaveAuthKeyFileName)
|
||||
fd, err := os.Open(fileName)
|
||||
if err != nil && errors.Is(err, fs.ErrNotExist) {
|
||||
return createPromptAuthKeyFile(fileName)
|
||||
return createWaveAuthKeyFile(fileName)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error opening prompt authkey:%s: %v", fileName, err)
|
||||
return "", fmt.Errorf("error opening wave authkey:%s: %v", fileName, err)
|
||||
}
|
||||
defer fd.Close()
|
||||
buf, err := io.ReadAll(fd)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading prompt authkey:%s: %v", fileName, err)
|
||||
return "", fmt.Errorf("error reading wave authkey:%s: %v", fileName, err)
|
||||
}
|
||||
keyStr := string(buf)
|
||||
_, err = uuid.Parse(keyStr)
|
||||
@ -152,13 +152,13 @@ func ReadPromptAuthKey() (string, error) {
|
||||
return keyStr, nil
|
||||
}
|
||||
|
||||
func AcquirePromptLock() (*os.File, error) {
|
||||
homeDir := GetPromptHomeDir()
|
||||
func AcquireWaveLock() (*os.File, error) {
|
||||
homeDir := GetWaveHomeDir()
|
||||
err := ensureDir(homeDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot find/create PROMPT_HOME directory %q", homeDir)
|
||||
return nil, fmt.Errorf("cannot find/create WAVETERM_HOME directory %q", homeDir)
|
||||
}
|
||||
lockFileName := path.Join(homeDir, PromptLockFile)
|
||||
lockFileName := path.Join(homeDir, WaveLockFile)
|
||||
fd, err := os.Create(lockFileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -182,7 +182,7 @@ func EnsureSessionDir(sessionId string) (string, error) {
|
||||
if ok {
|
||||
return sdir, nil
|
||||
}
|
||||
scHome := GetPromptHomeDir()
|
||||
scHome := GetWaveHomeDir()
|
||||
sdir = path.Join(scHome, SessionsDirBaseName, sessionId)
|
||||
err := ensureDir(sdir)
|
||||
if err != nil {
|
||||
@ -196,8 +196,8 @@ func EnsureSessionDir(sessionId string) (string, error) {
|
||||
|
||||
// deprecated (v0.1.8)
|
||||
func GetSessionsDir() string {
|
||||
promptHome := GetPromptHomeDir()
|
||||
sdir := path.Join(promptHome, SessionsDirBaseName)
|
||||
waveHome := GetWaveHomeDir()
|
||||
sdir := path.Join(waveHome, SessionsDirBaseName)
|
||||
return sdir
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ func EnsureScreenDir(screenId string) (string, error) {
|
||||
if ok {
|
||||
return sdir, nil
|
||||
}
|
||||
scHome := GetPromptHomeDir()
|
||||
scHome := GetWaveHomeDir()
|
||||
sdir = path.Join(scHome, ScreensDirBaseName, screenId)
|
||||
err := ensureDir(sdir)
|
||||
if err != nil {
|
||||
@ -224,8 +224,8 @@ func EnsureScreenDir(screenId string) (string, error) {
|
||||
}
|
||||
|
||||
func GetScreensDir() string {
|
||||
promptHome := GetPromptHomeDir()
|
||||
sdir := path.Join(promptHome, ScreensDirBaseName)
|
||||
waveHome := GetWaveHomeDir()
|
||||
sdir := path.Join(waveHome, ScreensDirBaseName)
|
||||
return sdir
|
||||
}
|
||||
|
||||
@ -236,7 +236,7 @@ func ensureDir(dirName string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("[prompt] created directory %q\n", dirName)
|
||||
log.Printf("[wave] created directory %q\n", dirName)
|
||||
info, err = os.Stat(dirName)
|
||||
}
|
||||
if err != nil {
|
||||
@ -277,7 +277,7 @@ func PtyOutFile(screenId string, lineId string) (string, error) {
|
||||
return fmt.Sprintf("%s/%s.ptyout.cf", sdir, lineId), nil
|
||||
}
|
||||
|
||||
func GenPromptUUID() string {
|
||||
func GenWaveUUID() string {
|
||||
for {
|
||||
rtn := uuid.New().String()
|
||||
_, err := strconv.Atoi(rtn[0:8])
|
||||
|
@ -507,6 +507,15 @@ func GetSessionById(ctx context.Context, id string) (*SessionType, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// counts non-archived sessions
|
||||
func GetSessionCount(ctx context.Context) (int, error) {
|
||||
return WithTxRtn(ctx, func(tx *TxWrap) (int, error) {
|
||||
query := `SELECT COALESCE(count(*), 0) FROM session WHERE NOT archived`
|
||||
numSessions := tx.GetInt(query)
|
||||
return numSessions, nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetSessionByName(ctx context.Context, name string) (*SessionType, error) {
|
||||
var session *SessionType
|
||||
txErr := WithTx(ctx, func(tx *TxWrap) error {
|
||||
@ -532,7 +541,7 @@ func GetSessionByName(ctx context.Context, name string) (*SessionType, error) {
|
||||
// if sessionName == "", it will be generated
|
||||
func InsertSessionWithName(ctx context.Context, sessionName string, activate bool) (*ModelUpdate, error) {
|
||||
var newScreen *ScreenType
|
||||
newSessionId := scbase.GenPromptUUID()
|
||||
newSessionId := scbase.GenWaveUUID()
|
||||
txErr := WithTx(ctx, func(tx *TxWrap) error {
|
||||
names := tx.SelectStrings(`SELECT name FROM session`)
|
||||
sessionName = fmtUniqueName(sessionName, "workspace-%d", len(names)+1, names)
|
||||
@ -684,7 +693,7 @@ func InsertScreen(ctx context.Context, sessionId string, origScreenName string,
|
||||
return fmt.Errorf("cannot create screen, base screen not found")
|
||||
}
|
||||
}
|
||||
newScreenId = scbase.GenPromptUUID()
|
||||
newScreenId = scbase.GenWaveUUID()
|
||||
screen := &ScreenType{
|
||||
SessionId: sessionId,
|
||||
ScreenId: newScreenId,
|
||||
@ -1258,7 +1267,7 @@ func UpdateRemoteState(ctx context.Context, sessionId string, screenId string, r
|
||||
ri = dbutil.GetMapGen[*RemoteInstance](tx, query, sessionId, screenId, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name)
|
||||
if ri == nil {
|
||||
ri = &RemoteInstance{
|
||||
RIId: scbase.GenPromptUUID(),
|
||||
RIId: scbase.GenWaveUUID(),
|
||||
Name: remotePtr.Name,
|
||||
SessionId: sessionId,
|
||||
ScreenId: screenId,
|
||||
@ -2035,7 +2044,7 @@ func UpdateCurrentActivity(ctx context.Context, update ActivityUpdate) error {
|
||||
if len(tzName) > MaxTzNameLen {
|
||||
tzName = tzName[0:MaxTzNameLen]
|
||||
}
|
||||
tx.Exec(query, dayStr, tdata, tzName, tzOffset, scbase.PromptVersion, scbase.ClientArch(), scbase.BuildTime, scbase.MacOSRelease())
|
||||
tx.Exec(query, dayStr, tdata, tzName, tzOffset, scbase.WaveVersion, scbase.ClientArch(), scbase.BuildTime, scbase.MacOSRelease())
|
||||
}
|
||||
tdata.NumCommands += update.NumCommands
|
||||
tdata.FgMinutes += update.FgMinutes
|
||||
@ -2052,7 +2061,7 @@ func UpdateCurrentActivity(ctx context.Context, update ActivityUpdate) error {
|
||||
clientversion = ?,
|
||||
buildtime = ?
|
||||
WHERE day = ?`
|
||||
tx.Exec(query, tdata, scbase.PromptVersion, scbase.BuildTime, dayStr)
|
||||
tx.Exec(query, tdata, scbase.WaveVersion, scbase.BuildTime, dayStr)
|
||||
return nil
|
||||
})
|
||||
if txErr != nil {
|
||||
|
@ -4,8 +4,10 @@
|
||||
package sstore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
@ -40,11 +42,14 @@ func MakeMigrate() (*migrate.Migrate, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func copyFile(srcFile string, dstFile string) error {
|
||||
func copyFile(srcFile string, dstFile string, notFoundOk bool) error {
|
||||
if srcFile == dstFile {
|
||||
return fmt.Errorf("cannot copy %s to itself", srcFile)
|
||||
}
|
||||
srcFd, err := os.Open(srcFile)
|
||||
if notFoundOk && err != nil && errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open %s: %v", err)
|
||||
}
|
||||
@ -100,10 +105,16 @@ func MigrateUp(targetVersion uint) error {
|
||||
}
|
||||
log.Printf("[db] migrating from %d to %d\n", curVersion, targetVersion)
|
||||
log.Printf("[db] backing up database %s to %s\n", DBFileName, DBFileNameBackup)
|
||||
err = copyFile(GetDBName(), GetDBBackupName())
|
||||
os.Remove(GetDBBackupName()) // don't report error
|
||||
os.Remove(GetDBWALBackupName()) // don't report error
|
||||
err = copyFile(GetDBName(), GetDBBackupName(), false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating database backup: %v", err)
|
||||
}
|
||||
err = copyFile(GetDBWALName(), GetDBWALBackupName(), true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating database(wal) backup: %v", err)
|
||||
}
|
||||
for newVersion := curVersion + 1; newVersion <= targetVersion; newVersion++ {
|
||||
err = MigrateUpStep(m, newVersion)
|
||||
if err != nil {
|
||||
|
@ -20,21 +20,23 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/sawka/txwrap"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
||||
"github.com/wavetermdev/waveterm/waveshell/pkg/shexec"
|
||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil"
|
||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbase"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/sawka/txwrap"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
const LineNoHeight = -1
|
||||
const DBFileName = "prompt.db"
|
||||
const DBFileNameBackup = "backup.prompt.db"
|
||||
const DBFileName = "waveterm.db"
|
||||
const DBWALFileName = "waveterm.db-wal"
|
||||
const DBFileNameBackup = "backup.waveterm.db"
|
||||
const DBWALFileNameBackup = "backup.waveterm.db-wal"
|
||||
const MaxWebShareLineCount = 50
|
||||
const MaxWebShareScreenCount = 3
|
||||
const MaxLineStateSize = 4 * 1024 // 4k for now, can raise if needed
|
||||
@ -145,15 +147,25 @@ func lineIdFromCK(ck base.CommandKey) string {
|
||||
}
|
||||
|
||||
func GetDBName() string {
|
||||
scHome := scbase.GetPromptHomeDir()
|
||||
scHome := scbase.GetWaveHomeDir()
|
||||
return path.Join(scHome, DBFileName)
|
||||
}
|
||||
|
||||
func GetDBWALName() string {
|
||||
scHome := scbase.GetWaveHomeDir()
|
||||
return path.Join(scHome, DBWALFileName)
|
||||
}
|
||||
|
||||
func GetDBBackupName() string {
|
||||
scHome := scbase.GetPromptHomeDir()
|
||||
scHome := scbase.GetWaveHomeDir()
|
||||
return path.Join(scHome, DBFileNameBackup)
|
||||
}
|
||||
|
||||
func GetDBWALBackupName() string {
|
||||
scHome := scbase.GetWaveHomeDir()
|
||||
return path.Join(scHome, DBWALFileNameBackup)
|
||||
}
|
||||
|
||||
func IsValidConnectMode(mode string) bool {
|
||||
return mode == ConnectModeStartup || mode == ConnectModeAuto || mode == ConnectModeManual
|
||||
}
|
||||
@ -1091,7 +1103,7 @@ func makeNewLineText(screenId string, userId string, text string) *LineType {
|
||||
rtn := &LineType{}
|
||||
rtn.ScreenId = screenId
|
||||
rtn.UserId = userId
|
||||
rtn.LineId = scbase.GenPromptUUID()
|
||||
rtn.LineId = scbase.GenWaveUUID()
|
||||
rtn.Ts = time.Now().UnixMilli()
|
||||
rtn.LineLocal = true
|
||||
rtn.LineType = LineTypeText
|
||||
@ -1160,7 +1172,7 @@ func EnsureLocalRemote(ctx context.Context) error {
|
||||
}
|
||||
// create the local remote
|
||||
localRemote := &RemoteType{
|
||||
RemoteId: scbase.GenPromptUUID(),
|
||||
RemoteId: scbase.GenWaveUUID(),
|
||||
RemoteType: RemoteTypeSsh,
|
||||
RemoteAlias: LocalRemoteAlias,
|
||||
RemoteCanonicalName: fmt.Sprintf("%s@%s", user.Username, hostName),
|
||||
@ -1177,7 +1189,7 @@ func EnsureLocalRemote(ctx context.Context) error {
|
||||
}
|
||||
log.Printf("[db] added local remote '%s', id=%s\n", localRemote.RemoteCanonicalName, localRemote.RemoteId)
|
||||
sudoRemote := &RemoteType{
|
||||
RemoteId: scbase.GenPromptUUID(),
|
||||
RemoteId: scbase.GenWaveUUID(),
|
||||
RemoteType: RemoteTypeSsh,
|
||||
RemoteAlias: "sudo",
|
||||
RemoteCanonicalName: fmt.Sprintf("sudo@%s@%s", user.Username, hostName),
|
||||
@ -1197,19 +1209,19 @@ func EnsureLocalRemote(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func EnsureDefaultSession(ctx context.Context) (*SessionType, error) {
|
||||
session, err := GetSessionByName(ctx, DefaultSessionName)
|
||||
func EnsureOneSession(ctx context.Context) error {
|
||||
numSessions, err := GetSessionCount(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
if session != nil {
|
||||
return session, nil
|
||||
if numSessions > 0 {
|
||||
return nil
|
||||
}
|
||||
_, err = InsertSessionWithName(ctx, DefaultSessionName, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return GetSessionByName(ctx, DefaultSessionName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func createClientData(tx *TxWrap) error {
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
```bash
|
||||
# @scripthaus command dump-schema-dev
|
||||
sqlite3 /Users/mike/prompt-dev/prompt.db .schema > db/schema.sql
|
||||
sqlite3 ~/.waveterm-dev/waveterm.db .schema > db/schema.sql
|
||||
```
|
||||
|
||||
```bash
|
||||
# @scripthaus command opendb-dev
|
||||
sqlite3 /Users/mike/prompt-dev/prompt.db
|
||||
sqlite3 ~/.waveterm-dev/waveterm.db
|
||||
```
|
||||
|
||||
```bash
|
||||
# @scripthaus command build
|
||||
go build -ldflags "-X main.BuildTime=$(date +'%Y%m%d%H%M')" -o bin/local-server ./cmd
|
||||
go build -ldflags "-X main.BuildTime=$(date +'%Y%m%d%H%M')" -o bin/wavesrv ./cmd
|
||||
```
|
||||
|
@ -72,9 +72,9 @@ var electronDev = webpackMerge.merge(electronCommon, {
|
||||
patterns: [{ from: "src/electron/preload.js", to: "preload.js" }],
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
__PROMPT_DEV__: "true",
|
||||
__PROMPT_VERSION__: JSON.stringify(VERSION),
|
||||
__PROMPT_BUILD__: JSON.stringify("devbuild"),
|
||||
__WAVETERM_DEV__: "true",
|
||||
__WAVETERM_VERSION__: JSON.stringify(VERSION),
|
||||
__WAVETERM_BUILD__: JSON.stringify("devbuild"),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@ -90,9 +90,9 @@ var electronProd = webpackMerge.merge(electronCommon, {
|
||||
patterns: [{ from: "src/electron/preload.js", to: "preload.js" }],
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
__PROMPT_DEV__: "false",
|
||||
__PROMPT_VERSION__: JSON.stringify(VERSION),
|
||||
__PROMPT_BUILD__: JSON.stringify(BUILD),
|
||||
__WAVETERM_DEV__: "false",
|
||||
__WAVETERM_VERSION__: JSON.stringify(VERSION),
|
||||
__WAVETERM_BUILD__: JSON.stringify(BUILD),
|
||||
}),
|
||||
],
|
||||
optimization: {
|
||||
|
@ -1,36 +0,0 @@
|
||||
const webpack = require("webpack");
|
||||
const merge = require("webpack-merge");
|
||||
const common = require("./webpack.electron.js");
|
||||
const moment = require("dayjs");
|
||||
const VERSION = require("../version.js");
|
||||
const path = require("path");
|
||||
|
||||
function makeBuildStr() {
|
||||
let buildStr = moment().format("YYYYMMDD-HHmmss");
|
||||
console.log("Prompt Electron " + VERSION + " build " + buildStr);
|
||||
return buildStr;
|
||||
}
|
||||
|
||||
const BUILD = makeBuildStr();
|
||||
|
||||
let merged = merge.merge(common, {
|
||||
mode: "production",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "../dist"),
|
||||
filename: "[name].js",
|
||||
},
|
||||
devtool: "source-map",
|
||||
optimization: {
|
||||
minimize: true,
|
||||
},
|
||||
});
|
||||
|
||||
merged.plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
__PROMPT_DEV__: "false",
|
||||
__PROMPT_VERSION__: JSON.stringify(VERSION),
|
||||
__PROMPT_BUILD__: JSON.stringify(BUILD),
|
||||
})
|
||||
);
|
||||
|
||||
module.exports = merged;
|
@ -69,11 +69,11 @@ var webCommon = {
|
||||
},
|
||||
{
|
||||
test: /\.md$/,
|
||||
use: "raw-loader",
|
||||
type: "asset/source",
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif)$/i,
|
||||
use: "file-loader",
|
||||
type: "asset/resource",
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -102,9 +102,9 @@ var webDev = webpackMerge.merge(webCommon, {
|
||||
new MiniCssExtractPlugin({ filename: "[name].css", ignoreOrder: true }),
|
||||
new LodashModuleReplacementPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
__PROMPT_DEV__: "true",
|
||||
__PROMPT_VERSION__: JSON.stringify(VERSION),
|
||||
__PROMPT_BUILD__: JSON.stringify("devbuild"),
|
||||
__WAVETERM_DEV__: "true",
|
||||
__WAVETERM_VERSION__: JSON.stringify(VERSION),
|
||||
__WAVETERM_BUILD__: JSON.stringify("devbuild"),
|
||||
}),
|
||||
],
|
||||
watchOptions: {
|
||||
@ -118,14 +118,14 @@ var webProd = webpackMerge.merge(webCommon, {
|
||||
path: path.resolve(__dirname, "../dist"),
|
||||
filename: "[name].js",
|
||||
},
|
||||
devtool: false,
|
||||
devtool: "source-map",
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({ filename: "[name].css", ignoreOrder: true }),
|
||||
new LodashModuleReplacementPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
__PROMPT_DEV__: "false",
|
||||
__PROMPT_VERSION__: JSON.stringify(VERSION),
|
||||
__PROMPT_BUILD__: JSON.stringify(BUILD),
|
||||
__WAVETERM_DEV__: "false",
|
||||
__WAVETERM_VERSION__: JSON.stringify(VERSION),
|
||||
__WAVETERM_BUILD__: JSON.stringify(BUILD),
|
||||
}),
|
||||
],
|
||||
optimization: {
|
||||
|
Loading…
Reference in New Issue
Block a user