Compare commits

..

1 Commits

Author SHA1 Message Date
Sylvia Crowe 01c9a3444a fix: integrate skeema knownhosts fix
This fix makes it possible to differentiate between keys when multiple
are provided by the remote server. It does not solve the case of
multiple keys of the same type being shared, but it handles multiple
keys of different types being shared, which is much more common.
2024-08-28 07:14:35 -07:00
78 changed files with 1883 additions and 2790 deletions
+14
View File
@@ -134,14 +134,28 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+10 -12
View File
@@ -6,7 +6,7 @@
},
"productName": "Wave",
"description": "An Open-Source, AI-Native, Terminal Built for Seamless Workflows",
"version": "0.7.2",
"version": "0.7.1",
"main": "dist/emain.js",
"license": "Apache-2.0",
"repository": {
@@ -23,6 +23,7 @@
"@table-nav/react": "^0.0.7",
"@tanstack/match-sorter-utils": "^8.8.4",
"@tanstack/react-table": "^8.10.3",
"@types/semver": "^7.5.6",
"autobind-decorator": "^2.4.0",
"base64-js": "^1.5.1",
"classnames": "^2.3.1",
@@ -36,8 +37,6 @@
"monaco-editor": "^0.44.0",
"mustache": "^4.2.0",
"node-fetch": "^3.2.10",
"overlayscrollbars": "^2.6.1",
"overlayscrollbars-react": "^0.5.5",
"papaparse": "^5.4.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
@@ -46,7 +45,7 @@
"remark-gfm": "^4.0.0",
"sprintf-js": "^1.1.2",
"throttle-debounce": "^5.0.0",
"tsx-control-statements": "^5.1.1",
"tsx-control-statements": "^4.1.1",
"uuid": "^9.0.0",
"winston": "^3.8.2",
"xterm": "^5.0.0",
@@ -67,25 +66,24 @@
"@svgr/webpack": "^8.1.0",
"@types/classnames": "^2.3.1",
"@types/electron": "^1.6.10",
"@types/node": "^20.11.0",
"@types/node": "20.11.0",
"@types/papaparse": "^5.3.10",
"@types/react": "^18.0.12",
"@types/semver": "^7.5.6",
"@types/sprintf-js": "^1.1.3",
"@types/throttle-debounce": "^5.0.1",
"@types/uuid": "^9.0.7",
"@types/uuid": "9.0.7",
"@types/webpack-env": "^1.18.3",
"babel-loader": "^9.1.3",
"babel-plugin-jsx-control-statements": "^4.1.2",
"copy-webpack-plugin": "^12.0.0",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.1",
"electron": "^29.0.1",
"electron": "29.0.1",
"electron-builder": "^24.13.3",
"electron-builder-squirrel-windows": "^24.13.3",
"file-loader": "^6.2.0",
"http-server": "^14.1.1",
"less": "^4.1.2",
"less-loader": "^12.0.0",
"less-loader": "^11.0.0",
"lodash-webpack-plugin": "^0.11.6",
"mini-css-extract-plugin": "^2.6.0",
"prettier": "^2.8.8",
@@ -96,11 +94,11 @@
"webpack": "^5.73.0",
"webpack-bundle-analyzer": "^4.10.1",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4",
"webpack-dev-server": "^4.9.1",
"webpack-merge": "^5.8.0",
"yaml": "^2.4.0"
},
"scripts": {
"postinstall": "electron-builder install-app-deps"
}
}
}
+6 -6
View File
@@ -5,14 +5,14 @@
<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/solid.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="public/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="public/fontawesome/css/brands.min.css">
<link rel="stylesheet" href="public/fontawesome/css/solid.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" />
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/themequery.css" />
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/default.css">
</head>
<body>
<div id="measure"></div>
+6 -6
View File
@@ -5,14 +5,14 @@
<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/solid.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="public/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="public/fontawesome/css/brands.min.css">
<link rel="stylesheet" href="public/fontawesome/css/solid.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" />
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/themequery.css" />
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/default.css" />
</head>
<body>
<div id="measure"></div>
+1 -10
View File
@@ -67,7 +67,6 @@
--scrollbar-background-color: var(--app-bg-color);
--scrollbar-thumb-color: rgba(255, 255, 255, 0.3);
--scrollbar-thumb-hover-color: rgba(255, 255, 255, 0.5);
--scrollbar-thumb-active-color: rgba(255, 255, 255, 0.6);
/* code color */
--pre-bg-color: rgb(0, 0, 0);
@@ -87,7 +86,6 @@
--form-element-border-color: rgba(241, 246, 243, 0.15);
--form-element-bg-color: var(--app-bg-color);
--form-element-text-color: var(--app-text-primary-color);
--form-element-primary-text-color: var(--form-element-text-color);
--form-element-label-color: var(--app-text-secondary-color);
--form-element-primary-color: var(--app-accent-color);
--form-element-secondary-color: rgba(255, 255, 255, 0.2);
@@ -114,7 +112,7 @@
--hotkey-text-color: var(--app-text-secondary-color);
/* sidebar colors */
--sidebar-highlight-color: var(--app-accent-bg-color);
--sidebar-highlight-color: rgba(241, 246, 243, 0.08);
--sidebar-font-size: 15px;
--sidebar-line-height: 1.5;
--sidebar-font-weight: normal;
@@ -188,11 +186,4 @@
--datepicker-header-fade-color: rgba(255, 255, 255, 0.4);
--datepicker-year-header-bg-color: rgba(255, 255, 255, 0.2); /* Light grey background */
--datepicker-year-header-border-color: rgba(241, 246, 243, 0.15); /* Light grey border */
/* OverlayScrollbars styling */
.os-scrollbar {
--os-handle-bg: var(--scrollbar-thumb-color);
--os-handle-bg-hover: var(--scrollbar-thumb-hover-color);
--os-handle-bg-active: var(--scrollbar-thumb-active-color);
}
}
+8 -4
View File
@@ -1,8 +1,9 @@
/* Copyright 2024, Command Line Inc.
SPDX-License-Identifier: Apache-2.0 */
@import url("./term-default.css");
@import url("./term-light.css");
@import "./default.css";
@import "./term-default.css";
@import "./term-light.css";
:root {
--app-bg-color: #fefefe;
@@ -24,6 +25,11 @@
--tab-white: rgb(0, 0, 0, 0.6);
--tab-mint: rgb(70, 235, 156);
/* button colors */
--button-text-color: rgb(50, 50, 50); /* Dark gray for light theme */
--button-primary-color: #fefefe;
--button-secondary-bg-color: rgba(200, 200, 200, 0.3); /* Semi-transparent light gray */
/* table colors */
--table-thead-border-top-color: rgba(0, 0, 0, 0.2);
--table-thead-border-bottom-color: rgba(0, 0, 0, 0.3);
@@ -37,7 +43,6 @@
--form-element-border-color: rgba(0, 0, 0, 0.3);
--form-element-bg-color: var(--app-bg-color);
--form-element-text-color: var(--app-text-color);
--form-element-primary-text-color: var(--app-bg-color);
--form-element-label-color: rgba(0, 0, 0, 0.6);
--form-element-secondary-color: rgba(0, 0, 0, 0.09);
--form-element-icon-color: rgb(0, 0, 0, 0.6);
@@ -57,7 +62,6 @@
--scrollbar-background-color: var(--app-bg-color);
--scrollbar-thumb-color: rgba(0, 0, 0, 0.2);
--scrollbar-thumb-hover-color: rgba(0, 0, 0, 0.4);
--scrollbar-thumb-active-color: rgba(0, 0, 0, 0.5);
/* line color */
--line-actions-bg-color: rgba(0, 0, 0, 0.1);
-3
View File
@@ -1,3 +0,0 @@
@import url("./default.css") screen;
@import url("./light.css") screen and (prefers-color-scheme: light);
+3 -3
View File
@@ -44,7 +44,7 @@ rm -rf bin/
rm -rf build/
node_modules/.bin/webpack --env prod
WAVESRV_VERSION=$(node -e 'console.log(require("./version.js"))')
WAVESHELL_VERSION=v0.6
WAVESHELL_VERSION=v0.5
GO_LDFLAGS="-s -w -X main.BuildTime=$(date +'%Y%m%d%H%M')"
function buildWaveShell {
(cd waveshell; CGO_ENABLED=0 GOOS=$1 GOARCH=$2 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-$WAVESHELL_VERSION-$1.$2 main-waveshell.go)
@@ -69,7 +69,7 @@ rm -rf bin/
rm -rf build/
node_modules/.bin/webpack --env prod
WAVESRV_VERSION=$(node -e 'console.log(require("./version.js"))')
WAVESHELL_VERSION=v0.6
WAVESHELL_VERSION=v0.5
GO_LDFLAGS="-s -w -X main.BuildTime=$(date +'%Y%m%d%H%M')"
function buildWaveShell {
(cd waveshell; CGO_ENABLED=0 GOOS=$1 GOARCH=$2 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-$WAVESHELL_VERSION-$1.$2 main-waveshell.go)
@@ -96,7 +96,7 @@ CGO_ENABLED=1 go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflag
```bash
# @scripthaus command fullbuild-waveshell
set -e
WAVESHELL_VERSION=v0.6
WAVESHELL_VERSION=v0.5
GO_LDFLAGS="-s -w -X main.BuildTime=$(date +'%Y%m%d%H%M')"
function buildWaveShell {
(cd waveshell; CGO_ENABLED=0 GOOS=$1 GOARCH=$2 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-$WAVESHELL_VERSION-$1.$2 main-waveshell.go)
+39 -77
View File
@@ -251,13 +251,6 @@ input[type="checkbox"] {
justify-content: center;
}
.flex-centered-column {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
a.a-block {
display: block;
}
@@ -793,75 +786,6 @@ a.a-block {
margin-top: 6px;
}
.conn-dropdown {
width: 412px;
.lefticon {
position: absolute;
top: 50%;
left: 16px;
transform: translateY(-50%);
.globe-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
fill: var(--app-text-secondary-color);
}
.status-icon {
position: absolute;
left: 7px;
top: 8px;
circle {
stroke: var(--app-bg-color);
}
}
}
.wave-dropdown-display {
bottom: 7px;
}
}
.tab-colors,
.tab-icons {
display: flex;
flex-direction: row;
align-items: center;
.tab-color-sep,
.tab-icon-sep {
padding-left: 10px;
padding-right: 10px;
font-weight: bold;
user-select: none;
}
.tab-color-icon,
.tab-icon-icon {
width: 1.1em;
vertical-align: middle;
}
.tab-color-name,
.tab-icon-name {
display: inline-block;
margin-left: 1em;
min-width: 70px;
user-select: none;
}
.tab-color-select,
.tab-icon-select {
cursor: pointer;
margin: 3px;
&:hover {
outline: 2px solid white;
}
}
}
.settings-field {
display: flex;
flex-direction: row;
@@ -923,7 +847,7 @@ a.a-block {
}
}
input:not(.wave-input) {
input {
padding: 4px;
border-radius: 3px;
}
@@ -934,6 +858,44 @@ a.a-block {
}
}
.tab-colors,
.tab-icons {
display: flex;
flex-direction: row;
align-items: center;
.tab-color-sep,
.tab-icon-sep {
padding-left: 10px;
padding-right: 10px;
font-weight: bold;
user-select: none;
}
.tab-color-icon,
.tab-icon-icon {
width: 1.1em;
vertical-align: middle;
}
.tab-color-name,
.tab-icon-name {
display: inline-block;
margin-left: 1em;
min-width: 70px;
user-select: none;
}
.tab-color-select,
.tab-icon-select {
cursor: pointer;
margin: 3px;
&:hover {
outline: 2px solid white;
}
}
}
.action-text {
margin-left: 20px;
+5 -5
View File
@@ -8,7 +8,7 @@ import { boundMethod } from "autobind-decorator";
import { If } from "tsx-control-statements/components";
import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel } from "@/models";
import { GlobalCommandRunner, GlobalModel } from "@/models";
import { isBlank } from "@/util/util";
import { WorkspaceView } from "./workspace/workspaceview";
import { PluginsView } from "./pluginsview/pluginsview";
@@ -101,7 +101,7 @@ class App extends React.Component<{}, {}> {
return (
<div id="main" className={"platform-" + platform} onContextMenu={this.handleContextMenu}>
<div ref={this.mainContentRef} className="main-content">
<MainSideBar parentRef={this.mainContentRef} />
<MainSideBar parentRef={this.mainContentRef} clientData={clientData} />
<div className="session-view" />
</div>
<If condition={dcWait}>
@@ -123,7 +123,7 @@ class App extends React.Component<{}, {}> {
const mainSidebarCollapsed = GlobalModel.mainSidebarModel.getCollapsed();
const rightSidebarCollapsed = GlobalModel.rightSidebarModel.getCollapsed();
const activeMainView = GlobalModel.activeMainView.get();
const lightDarkClass = GlobalModel.isDarkTheme.get() ? "is-dark" : "is-light";
const lightDarkClass = GlobalModel.isThemeDark() ? "is-dark" : "is-light";
return (
<div
key={"version-" + renderVersion}
@@ -151,7 +151,7 @@ class App extends React.Component<{}, {}> {
</div>
</If>
<div ref={this.mainContentRef} className="main-content">
<MainSideBar parentRef={this.mainContentRef} />
<MainSideBar parentRef={this.mainContentRef} clientData={clientData} />
<ErrorBoundary>
<PluginsView />
<WorkspaceView />
@@ -160,7 +160,7 @@ class App extends React.Component<{}, {}> {
<ConnectionsView model={remotesModel} />
<ClientSettingsView model={remotesModel} />
</ErrorBoundary>
<RightSideBar parentRef={this.mainContentRef} />
<RightSideBar parentRef={this.mainContentRef} clientData={clientData} />
</div>
<ModalsProvider />
</div>
+12 -14
View File
@@ -64,12 +64,11 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
}
@boundMethod
handleChangeThemeSource(themeSource: NativeThemeSource): void {
if (GlobalModel.getThemeSource() == themeSource) {
handleChangeTheme(theme: string): void {
if (GlobalModel.getTheme() == theme) {
return;
}
const prtn = GlobalCommandRunner.setTheme(themeSource, false);
getApi().setNativeThemeSource(themeSource);
const prtn = GlobalCommandRunner.setTheme(theme, false);
commandRtnHandler(prtn, this.errorMessage);
}
@@ -112,12 +111,11 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
return availableFontFamilies;
}
getThemeSources(): DropdownItem[] {
const themeSources: DropdownItem[] = [];
themeSources.push({ label: "Dark", value: "dark" });
themeSources.push({ label: "Light", value: "light" });
themeSources.push({ label: "System", value: "system" });
return themeSources;
getThemes(): DropdownItem[] {
const themes: DropdownItem[] = [];
themes.push({ label: "Dark", value: "dark" });
themes.push({ label: "Light", value: "light" });
return themes;
}
@boundMethod
@@ -192,7 +190,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
);
const curFontSize = GlobalModel.getTermFontSize();
const curFontFamily = GlobalModel.getTermFontFamily();
const curTheme = GlobalModel.getThemeSource();
const curTheme = GlobalModel.getTheme();
return (
<MainView className="clientsettings-view" title="Client Settings" onClose={this.handleClose}>
@@ -227,9 +225,9 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
<div className="settings-input">
<Dropdown
className="theme-dropdown"
options={this.getThemeSources()}
options={this.getThemes()}
defaultValue={curTheme}
onChange={this.handleChangeThemeSource}
onChange={this.handleChangeTheme}
/>
</div>
</div>
@@ -283,7 +281,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
text={isBlank(openAIOpts.baseurl) ? "openai default" : openAIOpts.baseurl}
value={openAIOpts.baseurl ?? ""}
onChange={this.inlineUpdateOpenAIBaseURL}
maxLength={200}
maxLength={10}
showIcon={true}
/>
</div>
+2 -1
View File
@@ -10,6 +10,7 @@
border-radius: 6px;
height: auto;
line-height: 1.5;
display: block;
white-space: nowrap;
user-select: none;
@@ -20,7 +21,7 @@
}
&.primary {
color: var(--form-element-primary-text-color);
color: var(--form-element-text-color);
background: var(--form-element-primary-color);
i {
fill: var(--form-element-text-color);
-4
View File
@@ -22,7 +22,6 @@
display: flex;
align-items: center;
justify-content: center;
background-color: var(--form-element-bg-color);
input {
outline: none !important;
@@ -30,7 +29,6 @@
height: 100%;
padding: 0 !important;
cursor: default !important;
background-color: inherit !important;
}
.fa-calendar {
@@ -101,7 +99,6 @@
background-color: var(--datepicker-cell-hover-bg-color);
}
&.selected {
color: var(--form-element-primary-text-color);
background-color: var(--form-element-primary-color);
font-weight: bold;
}
@@ -160,7 +157,6 @@
}
&.selected {
color: var(--form-element-primary-text-color);
background-color: var(--form-element-primary-color);
}
}
+49 -81
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef, createRef } from "react";
import * as mobx from "mobx";
import ReactDOM from "react-dom";
import dayjs from "dayjs";
import cn from "classnames";
import cs from "classnames";
import { Button } from "@/elements";
import { If } from "tsx-control-statements/components";
import { GlobalModel } from "@/models";
@@ -25,7 +25,6 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
const [selDate, setSelDate] = useState(dayjs(selectedDate)); // Initialize with dayjs object
const [showYearAccordion, setShowYearAccordion] = useState(false);
const [expandedYear, setExpandedYear] = useState<number | null>(selDate.year());
const [selectionTimeoutId, setSelectionTimeoutId] = useState<string | number | NodeJS.Timeout | null>(null);
const yearRefs = useRef<YearRefs>({});
const wrapperRef = useRef<HTMLDivElement>(null);
const modalRef = useRef<HTMLDivElement>(null);
@@ -40,8 +39,8 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
MM: selDate.format("MM"),
DD: selDate.format("DD"),
});
const curUuid = uuidv4();
const keybindsRegistered = mobx.observable.box(false, {
let curUuid = uuidv4();
let keybindsRegistered = mobx.observable.box(false, {
name: "datepicker-keybinds-registered",
});
@@ -91,6 +90,7 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
const newDate = dayjs(date);
setSelDate(newDate); // Update selDate with the new dayjs object
onSelectDate && onSelectDate(date); // Call parent's onSelectDate
setIsOpen(false); // Close the picker
};
const changeMonth = (delta: number) => {
@@ -103,7 +103,7 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
return (
<div className="day-picker-header">
<div
className={cn({ fade: showYearAccordion })}
className={cs({ fade: showYearAccordion })}
onClick={() => {
if (!showYearAccordion) {
setExpandedYear(selDate.year()); // Set expandedYear when opening accordion
@@ -112,7 +112,7 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
}}
>
{selDate.format("MMMM YYYY")}
<span className={cn("dropdown-arrow", { fade: showYearAccordion })}></span>
<span className={cs("dropdown-arrow", { fade: showYearAccordion })}></span>
</div>
<If condition={!showYearAccordion}>
<div className="arrows">
@@ -183,9 +183,9 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
}
// Next month's filler days
let overFlowIndex = 1;
while (days.length < 42) {
const dayDate = endDay.add(overFlowIndex++, "day");
const fillerDayCount = days.length - daysInPreviousMonth - endDay.date();
const dayDate = endDay.add(fillerDayCount + 1, "day");
days.push(
<div
key={`next-month-day-${dayDate.format("YYYY-MM-DD")}`}
@@ -251,14 +251,14 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
</div>
<If condition={expandedYear === year}>
<div
className={cn("month-container", {
className={cs("month-container", {
expanded: expandedYear === year,
})}
>
{Array.from({ length: 12 }, (_, i) => i + 1).map((month) => (
<div
key={month}
className={cn("month", {
className={cs("month", {
selected: year === currentYear && month === selDate.month() + 1,
})}
onClick={() => handleMonthYearSelect(month, year)}
@@ -301,52 +301,25 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
)
: null;
const handleDatePartChange = (part: string, value: string) => {
console.log("Date part changed: ", part, value);
const handleDatePartChange = (part, value) => {
const newDateParts = { ...dateParts, [part]: value };
console.log("New date parts: ", newDateParts);
setDateParts(newDateParts);
// Construct a new date from the updated parts
const newDate = dayjs(`${newDateParts.YYYY}-${newDateParts.MM}-${newDateParts.DD}`);
if (newDate.isValid()) {
setSelDate(newDate); // Update selDate with the new dayjs object
} else {
console.log("Invalid date");
setSelDate(dayjs(new Date()));
onSelectDate(newDate.toDate()); // Call onSelectDate with the new date
}
onSelectDate(selDate.toDate()); // Call onSelectDate with the new date
};
const handleArrowNavigation = (key: string, currentPart: string) => {
console.log("Arrow key pressed: ", key);
const handleArrowNavigation = (key, currentPart) => {
const currentIndex = formatParts.indexOf(currentPart);
let targetInput;
const incrementInput = (part: string, increment: number) => {
let newValue = parseInt(dateParts[part]) + increment;
switch (part) {
case "MM":
newValue = newValue > 12 ? 1 : newValue < 1 ? 12 : newValue;
break;
case "DD":
const daysInMonth = dayjs(selDate).daysInMonth();
console.log("Days in month: ", daysInMonth);
newValue = newValue > daysInMonth ? 1 : newValue < 1 ? daysInMonth : newValue;
break;
case "YYYY":
// No bounds for year
break;
}
handleDatePartChange(part, newValue.toString());
};
if (key == "ArrowLeft" && currentIndex > 0) {
targetInput = inputRefs.current[formatParts[currentIndex - 1]].current;
} else if (key == "ArrowRight" && currentIndex < formatParts.length - 1) {
targetInput = inputRefs.current[formatParts[currentIndex + 1]].current;
} else if (key == "ArrowUp") {
incrementInput(currentPart, 1);
} else if (key == "ArrowDown") {
incrementInput(currentPart, -1);
}
if (targetInput) {
@@ -354,9 +327,9 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
}
};
const handleKeyDown = (event, currentPart: string) => {};
const handleKeyDown = (event, currentPart) => {};
const handleFocus = (event, part: string) => {
const handleFocus = (event, part) => {
event.target.select();
registerKeybindings(event, part);
};
@@ -368,79 +341,74 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
mobx.action(() => {
keybindsRegistered.set(true);
})();
const keybindManager = GlobalModel.keybindManager;
const domain = "datepicker-" + curUuid + "-" + part;
keybindManager.registerKeybinding("control", domain, "generic:selectLeft", (_) => {
let keybindManager = GlobalModel.keybindManager;
let domain = "datepicker-" + curUuid + "-" + part;
keybindManager.registerKeybinding("control", domain, "generic:selectLeft", (waveEvent) => {
handleArrowNavigation("ArrowLeft", part);
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:selectRight", (_) => {
keybindManager.registerKeybinding("control", domain, "generic:selectRight", (waveEvent) => {
handleArrowNavigation("ArrowRight", part);
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:selectAbove", (_) => {
handleArrowNavigation("ArrowUp", part);
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:selectBelow", (_) => {
handleArrowNavigation("ArrowDown", part);
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:space", (_) => {
keybindManager.registerKeybinding("control", domain, "generic:space", (waveEvent) => {
toggleModal();
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:confirm", (_) => {
keybindManager.registerKeybinding("control", domain, "generic:confirm", (waveEvent) => {
toggleModal();
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:cancel", (_) => {
keybindManager.registerKeybinding("control", domain, "generic:cancel", (waveEvent) => {
closeModal();
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:tab", (_) => {
keybindManager.registerKeybinding("control", domain, "generic:tab", (waveEvent) => {
handleArrowNavigation("ArrowRight", part);
return true;
});
for (let numpadKey = 0; numpadKey <= 9; numpadKey++) {
keybindManager.registerKeybinding("control", domain, "generic:numpad-" + numpadKey.toString(), (_) => {
console.log("Numpad key pressed: ", numpadKey);
const currentPart = part;
const maxLength = currentPart === "YYYY" ? 4 : 2;
const newValue = event.target.value.length < maxLength ? event.target.value + numpadKey : numpadKey;
handleDatePartChange(currentPart, newValue);
keybindManager.registerKeybinding(
"control",
domain,
"generic:numpad-" + numpadKey.toString(),
(waveEvent) => {
let currentPart = part;
const maxLength = currentPart === "YYYY" ? 4 : 2;
const newValue = event.target.value.length < maxLength ? event.target.value + numpadKey : numpadKey;
let selectionTimeoutId = null;
handleDatePartChange(currentPart, newValue);
// Clear any existing timeout
if (selectionTimeoutId !== null) {
clearTimeout(selectionTimeoutId);
}
// Clear any existing timeout
if (selectionTimeoutId !== null) {
clearTimeout(selectionTimeoutId);
}
// Re-focus and select the input after state update
setSelectionTimeoutId(
setTimeout(() => {
// Re-focus and select the input after state update
selectionTimeoutId = setTimeout(() => {
event.target.focus();
event.target.select();
}, 0)
);
return true;
});
}, 0);
return true;
}
);
}
};
const handleBlur = (event, part: string) => {
const handleBlur = (event, part) => {
unregisterKeybindings(part);
};
const unregisterKeybindings = (part: string) => {
const unregisterKeybindings = (part) => {
mobx.action(() => {
keybindsRegistered.set(false);
})();
const domain = "datepicker-" + curUuid + "-" + part;
let domain = "datepicker-" + curUuid + "-" + part;
GlobalModel.keybindManager.unregisterDomain(domain);
};
// Prevent use from selecting text in the input
const handleMouseDown = (event, part: string) => {
const handleMouseDown = (event, part) => {
event.preventDefault();
handleFocus(event, part);
+8 -15
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import * as mobx from "mobx";
import { If } from "tsx-control-statements/components";
import ReactDOM from "react-dom";
import { Button } from "./button";
@@ -13,15 +14,11 @@ import { boundMethod } from "autobind-decorator";
interface ModalHeaderProps {
onClose?: () => void;
keybindings?: boolean;
title: string;
}
const ModalHeader: React.FC<ModalHeaderProps> = ({ onClose, keybindings, title }) => (
const ModalHeader: React.FC<ModalHeaderProps> = ({ onClose, title }) => (
<div className="wave-modal-header">
<If condition={keybindings && onClose}>
<ModalKeybindings onCancel={onClose}></ModalKeybindings>
</If>
{<div className="wave-modal-title">{title}</div>}
<If condition={onClose}>
<Button className="secondary ghost" onClick={onClose}>
@@ -39,27 +36,23 @@ interface ModalFooterProps {
keybindings?: boolean;
}
class ModalKeybindings extends React.Component<{ onOk?: () => void; onCancel?: () => void }, {}> {
class ModalKeybindings extends React.Component<{ onOk; onCancel }, {}> {
curId: string;
@boundMethod
componentDidMount(): void {
this.curId = uuidv4();
const domain = "modal-" + this.curId;
const keybindManager = GlobalModel.keybindManager;
let domain = "modal-" + this.curId;
let keybindManager = GlobalModel.keybindManager;
if (this.props.onOk) {
keybindManager.registerKeybinding("modal", domain, "generic:confirm", (waveEvent) => {
if (this.props.onOk) {
this.props.onOk();
}
this.props.onOk();
return true;
});
}
if (this.props.onCancel) {
keybindManager.registerKeybinding("modal", domain, "generic:cancel", (waveEvent) => {
if (this.props.onCancel) {
this.props.onCancel();
}
this.props.onCancel();
return true;
});
}
@@ -80,7 +73,7 @@ const ModalFooter: React.FC<ModalFooterProps> = ({
onOk,
cancelLabel = "Cancel",
okLabel = "Ok",
keybindings,
keybindings = true,
}) => (
<div className="wave-modal-footer">
<If condition={keybindings}>
+1 -3
View File
@@ -159,9 +159,7 @@ class TextField extends React.Component<TextFieldProps, TextFieldState> {
</label>
</If>
<input
className={cn("wave-textfield-inner-input", "wave-input", {
"offset-left": decoration?.startDecoration,
})}
className={cn("wave-textfield-inner-input", { "offset-left": decoration?.startDecoration })}
ref={this.inputRef}
id={label}
value={inputValue}
+12
View File
@@ -63,6 +63,18 @@
}
}
.status.updated {
div {
display: flex;
align-items: center;
margin-bottom: 5px;
i {
color: var(--app-success-color);
}
}
}
.status.outdated {
div {
i {
+40 -23
View File
@@ -5,12 +5,10 @@ import * as React from "react";
import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator";
import { GlobalModel, getApi } from "@/models";
import { GlobalModel } from "@/models";
import { Modal, LinkButton } from "@/elements";
import * as util from "@/util/util";
import * as appconst from "@/app/appconst";
import cn from "classnames";
import { If } from "tsx-control-statements/components";
import logo from "@/assets/waveterm-logo-with-bg.svg";
import "./about.less";
@@ -25,34 +23,53 @@ class AboutModal extends React.Component<{}, {}> {
}
@boundMethod
updateApp(): void {
getApi().installAppUpdate();
isUpToDate(): boolean {
return true;
}
@boundMethod
getClientVersion(): JSX.Element {
const clientData: ClientDataType = GlobalModel.clientData.get();
const showUpdateStatus = clientData.clientopts.noreleasecheck !== true;
const isUpToDate = !showUpdateStatus || GlobalModel.appUpdateStatus.get() !== "ready";
updateApp(): void {
// GlobalCommandRunner.updateApp();
}
@boundMethod
getStatus(isUpToDate: boolean): JSX.Element {
// TODO no up-to-date status reporting
return (
<div className={cn("status", { outdated: !isUpToDate })}>
<If condition={!isUpToDate}>
<div className="status updated">
<div className="text-selectable">
Client Version {appconst.VERSION} ({appconst.BUILD})
</div>
</div>
);
if (isUpToDate) {
return (
<div className="status updated">
<div>
<i className="fa-sharp fa-solid fa-triangle-exclamation" />
<span>Outdated Version</span>
<i className="fa-sharp fa-solid fa-circle-check" />
<span>Up to Date</span>
</div>
</If>
<div className="selectable">
Client Version {appconst.VERSION} ({appconst.BUILD})
</div>
</div>
);
}
return (
<div className="status outdated">
<div>
<i className="fa-sharp fa-solid fa-triangle-exclamation" />
<span>Outdated Version</span>
</div>
<div className="selectable">
Client Version {appconst.VERSION} ({appconst.BUILD})
</div>
<If condition={!isUpToDate}>
<div>
<button onClick={this.updateApp} className="button color-green text-secondary">
Restart to Update
</button>
</div>
</If>
<div>
<button onClick={this.updateApp} className="button color-green text-secondary">
Update
</button>
</div>
</div>
);
}
@@ -61,7 +78,7 @@ class AboutModal extends React.Component<{}, {}> {
const currentDate = new Date();
return (
<Modal className="about-modal">
<Modal.Header onClose={this.closeModal} title="About" keybindings={true} />
<Modal.Header onClose={this.closeModal} title="About" />
<div className="wave-modal-body">
<div className="about-section">
<div className="logo-wrapper">
@@ -76,7 +93,7 @@ class AboutModal extends React.Component<{}, {}> {
</div>
</div>
</div>
<div className="about-section text-standard">{this.getClientVersion()}</div>
<div className="about-section text-standard">{this.getStatus(this.isUpToDate())}</div>
<div className="about-section">
<LinkButton
className="secondary solid"
+1 -1
View File
@@ -39,7 +39,7 @@ class AlertModal extends React.Component<{}, {}> {
return (
<Modal className="alert-modal">
<Modal.Header onClose={this.closeModal} title={title} keybindings={true} />
<Modal.Header onClose={this.closeModal} title={title} />
<div className="wave-modal-body">
<If condition={message?.markdown}>
<Markdown text={message?.message ?? ""} extraClassName="bottom-margin" />
+10 -17
View File
@@ -107,17 +107,15 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
this.model.setRecentConnAdded(true);
this.model.closeModal();
if (GlobalModel.activeMainView.get() == "session") {
let crRtn = GlobalCommandRunner.screenSetRemote(cname, true, true);
crRtn.then((crcrtn) => {
if (crcrtn.success) {
return;
}
mobx.action(() => {
this.errorStr.set(crcrtn.error);
})();
});
}
let crRtn = GlobalCommandRunner.screenSetRemote(cname, true, false);
crRtn.then((crcrtn) => {
if (crcrtn.success) {
return;
}
mobx.action(() => {
this.errorStr.set(crcrtn.error);
})();
});
return;
}
mobx.action(() => {
@@ -363,12 +361,7 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
<div className="settings-field settings-error">Error: {this.getErrorStr()}</div>
</If>
</div>
<Modal.Footer
onCancel={this.model.closeModal}
onOk={this.handleSubmitRemote}
okLabel="Connect"
keybindings={true}
/>
<Modal.Footer onCancel={this.model.closeModal} onOk={this.handleSubmitRemote} okLabel="Connect" />
</Modal>
);
}

Some files were not shown because too many files have changed in this diff Show More