mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
merge main
This commit is contained in:
commit
371f3d87c1
@ -6,7 +6,7 @@
|
||||
},
|
||||
"productName": "Wave",
|
||||
"description": "An open-source, cross-platform, AI-integrated, modern terminal for seamless workflows.",
|
||||
"version": "0.6.1",
|
||||
"version": "0.7.0",
|
||||
"main": "dist/emain.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@ -101,4 +101,4 @@
|
||||
"scripts": {
|
||||
"postinstall": "electron-builder install-app-deps"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
/* Copyright 2024, Command Line Inc.
|
||||
SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
@import url("./term-default.css");
|
||||
@import url("./term-dark.css");
|
||||
|
||||
:root {
|
||||
@ -47,9 +48,9 @@
|
||||
--app-bg-color: black;
|
||||
--app-accent-color: rgb(88, 193, 66);
|
||||
--app-accent-bg-color: rgba(88, 193, 66, 0.2);
|
||||
--app-error-color: rgb(204, 0, 0);
|
||||
--app-warning-color: rgb(255, 165, 0);
|
||||
--app-success-color: rgb(38, 97, 26);
|
||||
--app-error-color: rgb(229, 77, 46);
|
||||
--app-warning-color: rgb(224, 185, 86);
|
||||
--app-success-color: rgb(78, 154, 6);
|
||||
--app-text-color: rgb(211, 215, 207);
|
||||
--app-text-primary-color: rgb(255, 255, 255);
|
||||
--app-text-secondary-color: rgb(195, 200, 194);
|
||||
@ -70,9 +71,9 @@
|
||||
|
||||
/* scrollbar colors */
|
||||
/* --scrollbar-background-color: rgba(21, 23, 21, 1); */
|
||||
--scrollbar-background-color: #030303;
|
||||
--scrollbar-thumb-color: #333;
|
||||
--scrollbar-thumb-hover-color: rgb(211, 215, 207);
|
||||
--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);
|
||||
|
||||
/* code color */
|
||||
--pre-bg-color: rgb(0, 0, 0);
|
||||
@ -91,46 +92,20 @@
|
||||
|
||||
--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-color);
|
||||
--form-element-label-color: rgb(195, 200, 194);
|
||||
--form-element-primary-color: rgb(78, 154, 6);
|
||||
--form-element-secondary-color: rgba(255, 255, 255, 0.09);
|
||||
--form-element-error-color: rgb(204, 0, 0);
|
||||
--form-element-icon-color: #fff;
|
||||
--form-element-disabled-text-color: #777;
|
||||
--form-element-placeholder-color: #777;
|
||||
|
||||
/* button colors */
|
||||
/* todo: use --form-element-* directly in elements */
|
||||
--button-text-color: var(--form-element-text-color);
|
||||
--button-primary-bg-color: var(--form-element-primary-color);
|
||||
--button-primary-color: var(--form-element-text-color);
|
||||
--button-secondary-bg-color: var(--form-element-secondary-color);
|
||||
--button-warning-bg-color: var(--form-element-error-color);
|
||||
|
||||
/* input colors */
|
||||
/* todo: use --form-element-* directly in elements */
|
||||
--checkbox-text-color: var(--form-element-text-color);
|
||||
--checkbox-bg-color: var(--form-element-primary-color);
|
||||
--checkbox-check-color: var(--form-element-text-color);
|
||||
|
||||
/* dropdown colors */
|
||||
/* todo: use --form-element-* directly in elements */
|
||||
--dropdown-text-color: var(--form-element-text-color);
|
||||
--dropdown-label-color: var(--form-element-label-color);
|
||||
--dropdown-error-color: var(--form-element-error-color);
|
||||
--dropdown-focus-color: var(--form-element-primary-color);
|
||||
--dropdown-bg-color: var(--form-element-bg-color);
|
||||
--dropdown-border-color: var(--form-element-border-color);
|
||||
--form-element-text-color: var(--app-text-primary-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);
|
||||
--form-element-error-color: var(--app-error-color);
|
||||
--form-element-icon-color: var(--app-icon-hover-color);
|
||||
|
||||
/* toggle colors */
|
||||
--toggle-bg-color: rgb(51, 51, 51);
|
||||
--toggle-thumb-color: rgb(211, 215, 207);
|
||||
--toggle-checked-bg-color: var(--form-element-primary-color);
|
||||
--toggle-bg-color: var(--app-border-color);
|
||||
--toggle-thumb-color: var(--app-text-color);
|
||||
--toggle-checked-bg-color: var(--app-accent-color);
|
||||
|
||||
/* cmdstrcode colors */
|
||||
--cmdstrcode-bg-color: rgb(0, 0, 0);
|
||||
--cmdstrcode-text-color: rgb(211, 215, 207);
|
||||
--cmdstrcode-text-color: var(--app-text-color);
|
||||
|
||||
/* markdown colors */
|
||||
--markdown-bg-color: rgb(35, 35, 35);
|
||||
@ -138,29 +113,29 @@
|
||||
|
||||
/* status(remote) colors */
|
||||
/* todo: all status colors must be unified */
|
||||
--status-connected-color: rgb(70, 167, 88);
|
||||
--status-connecting-color: rgb(245, 217, 10);
|
||||
--status-error-color: rgb(229, 77, 46);
|
||||
--status-disconnected-color: rgb(195, 200, 194);
|
||||
--status-connected-color: var(--app-success-color);
|
||||
--status-connecting-color: var(--app-warning-color);
|
||||
--status-error-color: var(--app-error-color);
|
||||
--status-disconnected-color: var(--app-text-secondary-color);
|
||||
|
||||
/* status indicator colors */
|
||||
/* todo: all status colors must be unified */
|
||||
--status-indicator-color: rgb(211, 215, 207);
|
||||
--status-indicator-error: rgb(204, 0, 0);
|
||||
--status-indicator-success: rgb(78, 154, 6);
|
||||
--status-indicator-color: var(--app-text-color);
|
||||
--status-indicator-error: var(--status-error-color);
|
||||
--status-indicator-success: var(--status-connected-color);
|
||||
|
||||
/* status(version) colors */
|
||||
/* todo: all status colors must be unified */
|
||||
--status-outdated-color: rgb(196, 160, 0);
|
||||
--status-updated-color: rgb(78, 154, 6);
|
||||
--status-outdated-color: var(--status-connecting-color);
|
||||
--status-updated-color: var(--status-connected-color);
|
||||
|
||||
/* term status colors */
|
||||
/* todo: all status colors must be unified */
|
||||
--term-error-color: rgb(204, 0, 0);
|
||||
--term-warning-color: rgb(196, 160, 0);
|
||||
--term-error-color: var(--status-error-color);
|
||||
--term-warning-color: var(--status-connecting-color);
|
||||
|
||||
/* hotkey colors */
|
||||
--hotkey-text-color: rgb(195, 200, 194);
|
||||
--hotkey-text-color: var(--app-text-secondary-color);
|
||||
|
||||
/* sidebar colors */
|
||||
--sidebar-highlight-color: rgba(241, 246, 243, 0.08);
|
||||
@ -178,10 +153,10 @@
|
||||
--line-svg-hover-fill-color: #eceeec;
|
||||
--line-selected-border-color: rgb(193, 195, 193);
|
||||
--line-separator-color: rgb(126, 126, 126);
|
||||
--line-error-color: #cc0000;
|
||||
--line-warning-color: #ffa500;
|
||||
--line-error-color: var(--app-error-color);
|
||||
--line-warning-color: var(--app-warning-color);
|
||||
--line-base-soft-blue-color: #729fcf;
|
||||
--line-active-border-color: rgb(97, 158, 72);
|
||||
--line-active-border-color: var(--app-accent-color);
|
||||
--line-selected-bg-color: rgba(255, 255, 255, 0.05);
|
||||
--line-selected-border-left-color: #777777;
|
||||
--line-selected-error-border-color: rgba(204, 0, 0, 0.8);
|
||||
@ -192,12 +167,12 @@
|
||||
--line-meta-user-color: rgba(140, 184, 232);
|
||||
--line-svg-color: rgba(236, 238, 236, 0.6);
|
||||
--line-svg-hover-color: rgba(236, 238, 236, 1);
|
||||
--line-status-success-fill: rgb(88, 193, 66);
|
||||
--line-status-error-fill: #cc0000;
|
||||
--line-status-warning-fill: #ffa500;
|
||||
--line-status-success-fill: var(--app-success-color);
|
||||
--line-status-error-fill: var(--app-error-color);
|
||||
--line-status-warning-fill: var(--app-warning-color);
|
||||
--line-actions-inactive-color: rgba(255, 255, 255, 0.5);
|
||||
--line-actions-active-color: rgba(255, 255, 255, 1);
|
||||
--line-actions-bg-color: rgba(21, 23, 21, 1);
|
||||
--line-actions-bg-color: rgba(255, 255, 255, 0.15);
|
||||
|
||||
/* view colors */
|
||||
/* todo: bookmarks is a view, colors must be unified with --view* colors */
|
||||
@ -207,8 +182,8 @@
|
||||
--bookmarks-control-hover-color: rgb(255, 255, 255);
|
||||
|
||||
/* view colors */
|
||||
--view-error-color: rgb(204, 0, 0);
|
||||
--view-text-color: rgb(195, 200, 194);
|
||||
--view-error-color: var(--app-error-color);
|
||||
--view-text-color: var(--app-text-color);
|
||||
|
||||
/* table colors */
|
||||
--table-border-color: rgba(241, 246, 243, 0.15);
|
||||
|
@ -2,10 +2,13 @@
|
||||
SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
@import "./default.css";
|
||||
@import "./term-default.css";
|
||||
@import "./term-light.css";
|
||||
|
||||
:root {
|
||||
--app-bg-color: #fefefe;
|
||||
--app-accent-color: rgb(75, 166, 57);
|
||||
--app-accent-bg-color: rgba(75, 166, 57, 0.2);
|
||||
--app-text-color: #000;
|
||||
--app-text-primary-color: rgb(0, 0, 0, 0.9);
|
||||
--app-text-secondary-color: rgb(0, 0, 0, 0.7);
|
||||
@ -42,7 +45,7 @@
|
||||
--form-element-bg-color: var(--app-bg-color);
|
||||
--form-element-text-color: var(--app-text-color);
|
||||
--form-element-label-color: rgba(0, 0, 0, 0.6);
|
||||
--form-element-secondary-color: rgba(255, 255, 255, 0.09);
|
||||
--form-element-secondary-color: rgba(0, 0, 0, 0.09);
|
||||
--form-element-icon-color: rgb(0, 0, 0, 0.6);
|
||||
--form-element-disabled-text-color: #b7b7b7;
|
||||
--form-element-placeholder-color: #b7b7b7;
|
||||
@ -55,16 +58,17 @@
|
||||
--cmdinput-textarea-border-color: var(--form-element-border-color);
|
||||
|
||||
/* scroll colors */
|
||||
--scrollbar-background-color: rgba(0, 0, 0, 0.3);
|
||||
--scrollbar-thumb-color: rgba(0, 0, 0, 0.4);
|
||||
--scrollbar-thumb-hover-color: rgba(0, 0, 0, 0.5);
|
||||
|
||||
/* cmdstrcode colors */
|
||||
--cmdstrcode-bg-color: var(--term-black);
|
||||
--cmdstrcode-text-color: var(--term-white);
|
||||
--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);
|
||||
|
||||
/* line color */
|
||||
--line-actions-bg-color: rgba(0, 0, 0, 0.4);
|
||||
--line-actions-bg-color: rgba(0, 0, 0, 0.1);
|
||||
--line-actions-inactive-color: rgba(0, 0, 0, 0.3);
|
||||
--line-actions-active-color: rgba(0, 0, 0, 1);
|
||||
|
||||
/* toggle colors */
|
||||
--toggle-thumb-color: var(--app-bg-color);
|
||||
|
||||
--logo-button-hover-bg-color: #f0f0f0;
|
||||
|
||||
|
@ -11,9 +11,9 @@
|
||||
--term-green: #4e9a06;
|
||||
--term-yellow: #c4a000;
|
||||
--term-blue: #3465a4;
|
||||
--term-magenta: #75507b;
|
||||
--term-magenta: #bc3fbc;
|
||||
--term-cyan: #06989a;
|
||||
--term-white: #d3d7cf;
|
||||
--term-white: #d0d0d0;
|
||||
--term-bright-black: #555753;
|
||||
--term-bright-red: #ef2929;
|
||||
--term-bright-green: #58c142;
|
||||
@ -21,7 +21,7 @@
|
||||
--term-bright-blue: #32afff;
|
||||
--term-bright-magenta: #ad7fa8;
|
||||
--term-bright-cyan: #34e2e2;
|
||||
--term-bright-white: #ffffff;
|
||||
--term-bright-white: #e7e7e7;
|
||||
|
||||
--term-gray: #8b918a; /* not an official terminal color */
|
||||
--term-cmdtext: #ffffff;
|
||||
|
@ -5,7 +5,5 @@
|
||||
/* term colors */
|
||||
--term-foreground: #000000;
|
||||
--term-background: #fefefe;
|
||||
--term-black: #fefefe;
|
||||
--term-white: #000000;
|
||||
--term-cmdtext: #000000;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ body {
|
||||
|
||||
body {
|
||||
&.is-dev .sidebar {
|
||||
background-color: var(--app-panel-bg-color-dev);
|
||||
--app-panel-bg-color: var(--app-panel-bg-color-dev);
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,10 +210,10 @@ svg.icon {
|
||||
.dropdown,
|
||||
.button {
|
||||
display: inline-block;
|
||||
border: 1px solid var(--button-secondary-bg-color) !important;
|
||||
border: 1px solid var(--form-element-secondary-color) !important;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
background-color: var(--button-secondary-bg-color) !important;
|
||||
background-color: var(--form-element-secondary-color) !important;
|
||||
color: var(--app-accent-color);
|
||||
.hoverEffect;
|
||||
&:hover {
|
||||
@ -221,7 +221,7 @@ svg.icon {
|
||||
}
|
||||
&.disabled {
|
||||
color: var(--app-text-color);
|
||||
background: var(--button-secondary-bg-color);
|
||||
background: var(--form-element-secondary-color);
|
||||
opacity: 0.5;
|
||||
cursor: initial;
|
||||
}
|
||||
@ -382,9 +382,9 @@ a.a-block {
|
||||
}
|
||||
|
||||
&.connect-button {
|
||||
color: var(--button-primary-bg-color);
|
||||
color: var(--form-element-primary-color);
|
||||
&:hover {
|
||||
color: var(--button-primary-bg-color);
|
||||
color: var(--form-element-primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
@ -396,9 +396,9 @@ a.a-block {
|
||||
}
|
||||
|
||||
&.success-button {
|
||||
color: var(--button-primary-bg-color);
|
||||
color: var(--form-element-primary-color);
|
||||
&:hover {
|
||||
color: var(--button-primary-bg-color);
|
||||
color: var(--form-element-primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,11 +111,12 @@ class App extends React.Component<{}, {}> {
|
||||
// used to force a full reload of the application
|
||||
const renderVersion = GlobalModel.renderVersion.get();
|
||||
const sidebarCollapsed = GlobalModel.mainSidebarModel.getCollapsed();
|
||||
const lightDarkClass = GlobalModel.isThemeDark() ? "is-dark" : "is-light";
|
||||
return (
|
||||
<div
|
||||
key={"version-" + renderVersion}
|
||||
id="main"
|
||||
className={cn("platform-" + platform, { "sidebar-collapsed": sidebarCollapsed })}
|
||||
className={cn("platform-" + platform, { "sidebar-collapsed": sidebarCollapsed }, lightDarkClass)}
|
||||
onContextMenu={this.handleContextMenu}
|
||||
>
|
||||
<If condition={sidebarCollapsed}>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="globe">
|
||||
<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M5.51561 7.24988C5.58926 5.49464 5.91878 3.94637 6.3934 2.81642C6.66453 2.17096 6.96822 1.69729 7.26714 1.39629C7.56327 1.0981 7.81035 1.00977 8.00003 1.00977C8.1897 1.00977 8.43678 1.0981 8.73291 1.39629C9.03183 1.69729 9.33553 2.17096 9.60665 2.81642C10.0813 3.94637 10.4108 5.49464 10.4844 7.24988H5.51561ZM4.01441 7.24988H1.03973C1.31693 4.64752 3.02019 2.47128 5.35297 1.51768C5.22994 1.74443 5.1158 1.98471 5.01045 2.23552C4.44737 3.57606 4.0887 5.32893 4.01441 7.24988ZM10.6471 1.51768C10.7701 1.74443 10.8842 1.98471 10.9896 2.23552C11.5527 3.57606 11.9114 5.32893 11.9856 7.24988H14.9603C14.6831 4.64752 12.9799 2.47128 10.6471 1.51768ZM10.4842 8.74988C10.4101 10.5028 10.0808 12.049 9.60665 13.1777C9.33553 13.8232 9.03183 14.2968 8.73291 14.5978C8.43678 14.896 8.1897 14.9844 8.00003 14.9844C7.81035 14.9844 7.56327 14.896 7.26714 14.5978C6.96822 14.2968 6.66453 13.8232 6.3934 13.1777C5.91928 12.049 5.58997 10.5028 5.51585 8.74988H10.4842ZM11.9854 8.74988H14.9603C14.683 11.3537 12.978 13.5309 10.6432 14.4837C10.7677 14.2548 10.8831 14.0121 10.9896 13.7586C11.5521 12.4194 11.9107 10.6686 11.9854 8.74988ZM5.35689 14.4837C3.0221 13.5309 1.31708 11.3537 1.03973 8.74988H4.01463C4.0894 10.6686 4.44792 12.4194 5.01045 13.7586C5.11692 14.0121 5.23237 14.2548 5.35689 14.4837Z" fill="#C3C8C2"/>
|
||||
</g>
|
||||
</svg>
|
||||
<g id="globe">
|
||||
<path id="Vector" fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M5.51561 7.24988C5.58926 5.49464 5.91878 3.94637 6.3934 2.81642C6.66453 2.17096 6.96822 1.69729 7.26714 1.39629C7.56327 1.0981 7.81035 1.00977 8.00003 1.00977C8.1897 1.00977 8.43678 1.0981 8.73291 1.39629C9.03183 1.69729 9.33553 2.17096 9.60665 2.81642C10.0813 3.94637 10.4108 5.49464 10.4844 7.24988H5.51561ZM4.01441 7.24988H1.03973C1.31693 4.64752 3.02019 2.47128 5.35297 1.51768C5.22994 1.74443 5.1158 1.98471 5.01045 2.23552C4.44737 3.57606 4.0887 5.32893 4.01441 7.24988ZM10.6471 1.51768C10.7701 1.74443 10.8842 1.98471 10.9896 2.23552C11.5527 3.57606 11.9114 5.32893 11.9856 7.24988H14.9603C14.6831 4.64752 12.9799 2.47128 10.6471 1.51768ZM10.4842 8.74988C10.4101 10.5028 10.0808 12.049 9.60665 13.1777C9.33553 13.8232 9.03183 14.2968 8.73291 14.5978C8.43678 14.896 8.1897 14.9844 8.00003 14.9844C7.81035 14.9844 7.56327 14.896 7.26714 14.5978C6.96822 14.2968 6.66453 13.8232 6.3934 13.1777C5.91928 12.049 5.58997 10.5028 5.51585 8.74988H10.4842ZM11.9854 8.74988H14.9603C14.683 11.3537 12.978 13.5309 10.6432 14.4837C10.7677 14.2548 10.8831 14.0121 10.9896 13.7586C11.5521 12.4194 11.9107 10.6686 11.9854 8.74988ZM5.35689 14.4837C3.0221 13.5309 1.31708 11.3537 1.03973 8.74988H4.01463C4.0894 10.6686 4.44792 12.4194 5.01045 13.7586C5.11692 14.0121 5.23237 14.2548 5.35689 14.4837Z" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
1
src/app/assets/icons/rotate-sharp-solid.svg
Normal file
1
src/app/assets/icons/rotate-sharp-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Pro 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2024 Fonticons, Inc.--><path d="M142.9 142.9c-17.5 17.5-30.1 38-37.8 59.8L44.8 181.4C55.6 150.7 73.2 122 97.6 97.6c87.5-87.5 229.3-87.5 316.8 0l0 0L472 40l24 24V224H336l-24-24 57.1-57.1 0 0c-62.5-62.5-163.8-62.5-226.3 0zm0 226.3c62.5 62.5 163.8 62.5 226.3 0c17.5-17.5 30.1-38 37.8-59.8l60.4 21.3c-10.8 30.6-28.4 59.3-52.9 83.7c-87.5 87.5-229.3 87.5-316.7 0l0 0L40 472 16 448V288H176l24 24-57.1 57.1z"/></svg>
|
After Width: | Height: | Size: 613 B |
@ -5,8 +5,6 @@ import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import cn from "classnames";
|
||||
import { GlobalModel, GlobalCommandRunner, RemotesModel, getApi } from "@/models";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "@/common/elements";
|
||||
import { commandRtnHandler, isBlank } from "@/util/util";
|
||||
@ -192,19 +190,17 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<If condition={GlobalModel.isDev}>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Theme</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="theme-dropdown"
|
||||
options={this.getThemes()}
|
||||
defaultValue={curTheme}
|
||||
onChange={this.handleChangeTheme}
|
||||
/>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Theme</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="theme-dropdown"
|
||||
options={this.getThemes()}
|
||||
defaultValue={curTheme}
|
||||
onChange={this.handleChangeTheme}
|
||||
/>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Client ID</div>
|
||||
<div className="settings-input">{cdata.clientid}</div>
|
||||
|
@ -18,20 +18,20 @@
|
||||
background: none;
|
||||
|
||||
i {
|
||||
fill: var(--button-primary-bg-color);
|
||||
fill: var(--form-element-primary-color);
|
||||
}
|
||||
|
||||
&.solid {
|
||||
color: var(--button-primary-color);
|
||||
background: var(--button-primary-bg-color);
|
||||
color: var(--form-element-text-color);
|
||||
background: var(--form-element-primary-color);
|
||||
|
||||
i {
|
||||
fill: var(--button-primary-color);
|
||||
fill: var(--form-element-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.outlined {
|
||||
border: 1px solid var(--button-primary-bg-color);
|
||||
border: 1px solid var(--form-element-primary-color);
|
||||
}
|
||||
|
||||
&.ghost {
|
||||
@ -39,29 +39,29 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--button-primary-color);
|
||||
color: var(--form-element-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
color: var(--button-text-color);
|
||||
color: var(--form-element-text-color);
|
||||
background: none;
|
||||
|
||||
&.solid {
|
||||
color: var(--button-text-color);
|
||||
background: var(--button-secondary-bg-color);
|
||||
color: var(--form-element-text-color);
|
||||
background: var(--form-element-secondary-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.outlined {
|
||||
border: 1px solid var(--button-secondary-bg-color);
|
||||
border: 1px solid var(--form-element-secondary-color);
|
||||
}
|
||||
|
||||
&.ghost {
|
||||
padding: 6px 10px;
|
||||
|
||||
i {
|
||||
fill: var(--button-primary-bg-color);
|
||||
fill: var(--form-element-primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--checkbox-bg-color);
|
||||
color: var(--form-element-primary-color);
|
||||
transition: color 250ms cubic-bezier(0.4, 0, 0.23, 1);
|
||||
}
|
||||
input[type="checkbox"] + label > span {
|
||||
@ -36,7 +36,7 @@
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked + label > span {
|
||||
border: 10px solid var(--button-primary-bg-color);
|
||||
border: 10px solid var(--form-element-primary-color);
|
||||
}
|
||||
input[type="checkbox"]:checked + label > span:before {
|
||||
content: "";
|
||||
@ -45,8 +45,8 @@
|
||||
left: 3px;
|
||||
width: 7px;
|
||||
height: 12px;
|
||||
border-right: 2px solid var(--checkbox-check-color);
|
||||
border-bottom: 2px solid var(--checkbox-check-color);
|
||||
border-right: 2px solid var(--form-element-text-color);
|
||||
border-bottom: 2px solid var(--form-element-text-color);
|
||||
transform: rotate(45deg);
|
||||
transform-origin: 0% 100%;
|
||||
animation: checkbox-check 500ms cubic-bezier(0.4, 0, 0.23, 1);
|
||||
|
@ -36,7 +36,6 @@
|
||||
}
|
||||
|
||||
.code-div {
|
||||
background-color: var(--cmdstrcode-bg-color);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-width: 100px;
|
||||
@ -49,7 +48,6 @@
|
||||
color: var(--cmdstrcode-text-color);
|
||||
white-space: pre;
|
||||
padding: 2px 8px 2px 8px;
|
||||
background-color: var(--cmdstrcode-bg-color);
|
||||
font-size: var(--termfontsize);
|
||||
font-family: var(--termfontfamily);
|
||||
line-height: var(--termlineheight);
|
||||
|
@ -3,10 +3,10 @@
|
||||
height: 44px;
|
||||
min-width: 150px;
|
||||
width: 100%;
|
||||
border: 1px solid var(--dropdown-border-color);
|
||||
border: 2px solid var(--form-element-border-color);
|
||||
border-radius: 6px;
|
||||
background: var(--dropdown-bg-color);
|
||||
line-height: 22px;
|
||||
background: var(--form-element-bg-color);
|
||||
|
||||
&.no-label {
|
||||
height: 34px;
|
||||
@ -18,7 +18,7 @@
|
||||
top: 16px;
|
||||
font-size: 12.5px;
|
||||
transition: all 0.3s;
|
||||
color: var(--dropdown-label-color);
|
||||
color: var(--form-element-label-color);
|
||||
line-height: 10px;
|
||||
|
||||
&.float {
|
||||
@ -89,11 +89,15 @@
|
||||
}
|
||||
|
||||
&-error {
|
||||
border-color: var(--dropdown-error-color);
|
||||
border-color: var(--form-element-error-color);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--dropdown-focus-color);
|
||||
border-color: var(--form-element-primary-color);
|
||||
}
|
||||
|
||||
.lefticon {
|
||||
color: var(--app-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,13 +114,13 @@
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--dropdown-border-color);
|
||||
background: var(--dropdown-bg-color);
|
||||
border: 2px solid var(--form-element-border-color);
|
||||
background: var(--form-element-bg-color);
|
||||
// box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.5), 0px 3px 8px 0px rgba(0, 0, 0, 0.35), 0px 0px 0.5px 0px #fff inset,
|
||||
// 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
animation-fill-mode: forwards;
|
||||
z-index: 1000;
|
||||
color: var(--dropdown-text-color);
|
||||
color: var(--form-element-text-color);
|
||||
}
|
||||
|
||||
.wave-dropdown-menu-close {
|
||||
|
@ -13,7 +13,6 @@ export { NumberField } from "./numberfield";
|
||||
export { PasswordField } from "./passwordfield";
|
||||
export { ResizableSidebar } from "./resizablesidebar";
|
||||
export { SettingsError } from "./settingserror";
|
||||
export { ShowWaveShellInstallPrompt } from "./showwaveshellinstallprompt";
|
||||
export { Status } from "./status";
|
||||
export { TextField } from "./textfield";
|
||||
export { Toggle } from "./toggle";
|
||||
|
@ -25,7 +25,7 @@
|
||||
padding: 0 10px;
|
||||
vertical-align: middle;
|
||||
line-height: var(--screentabs-height);
|
||||
color: var(--term-white);
|
||||
color: var(--app-text-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { GlobalModel } from "@/models";
|
||||
import * as appconst from "@/app/appconst";
|
||||
|
||||
function ShowWaveShellInstallPrompt(callbackFn: () => void) {
|
||||
let message: string = `
|
||||
In order to use Wave's advanced features like unified history and persistent sessions, Wave installs a small, open-source helper program called WaveShell on your remote machine. WaveShell does not open any external ports and only communicates with your *local* Wave terminal instance over ssh. For more information please see [the docs](https://docs.waveterm.dev/reference/waveshell).
|
||||
`;
|
||||
message = message.trim();
|
||||
let prtn = GlobalModel.showAlert({
|
||||
message: message,
|
||||
confirm: true,
|
||||
markdown: true,
|
||||
confirmflag: appconst.ConfirmKey_HideShellPrompt,
|
||||
});
|
||||
prtn.then((confirm) => {
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
if (callbackFn) {
|
||||
callbackFn();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export { ShowWaveShellInstallPrompt };
|
@ -3,10 +3,10 @@
|
||||
align-items: center;
|
||||
border-radius: 6px;
|
||||
position: relative;
|
||||
height: 44px;
|
||||
min-height: 44px;
|
||||
min-width: 412px;
|
||||
gap: 6px;
|
||||
border: 1px solid var(--form-element-border-color);
|
||||
border: 2px solid var(--form-element-border-color);
|
||||
background: var(--form-element-bg-color);
|
||||
|
||||
&:hover {
|
||||
@ -27,15 +27,14 @@
|
||||
|
||||
&-inner {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
--inner-padding: 5px 0 5px 16px;
|
||||
|
||||
&-label {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 16px;
|
||||
padding: var(--inner-padding);
|
||||
margin-bottom: -10px;
|
||||
font-size: 12.5px;
|
||||
transition: all 0.3s;
|
||||
color: var(--form-element-label-color);
|
||||
@ -55,7 +54,7 @@
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
border: none;
|
||||
padding: 5px 0 5px 16px;
|
||||
padding: var(--inner-padding);
|
||||
font-size: 16px;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
|
@ -15,7 +15,7 @@
|
||||
i {
|
||||
display: inline;
|
||||
font-size: 13px;
|
||||
fill: var(--checkbox-text-color);
|
||||
fill: var(--form-element-text-color);
|
||||
padding-top: 0.2em;
|
||||
}
|
||||
}
|
||||
|
@ -7,16 +7,7 @@ import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
|
||||
import {
|
||||
Modal,
|
||||
TextField,
|
||||
NumberField,
|
||||
InputDecoration,
|
||||
Dropdown,
|
||||
PasswordField,
|
||||
Tooltip,
|
||||
ShowWaveShellInstallPrompt,
|
||||
} from "@/elements";
|
||||
import { Modal, TextField, NumberField, InputDecoration, Dropdown, PasswordField, Tooltip } from "@/elements";
|
||||
import * as util from "@/util/util";
|
||||
|
||||
import "./createremoteconn.less";
|
||||
@ -73,12 +64,7 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleOk(): void {
|
||||
ShowWaveShellInstallPrompt(this.submitRemote);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
submitRemote(): void {
|
||||
handleSubmitRemote(): void {
|
||||
mobx.action(() => {
|
||||
this.errorStr.set(null);
|
||||
})();
|
||||
@ -275,7 +261,7 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
|
||||
{ value: "none", label: "none" },
|
||||
{ value: "key", label: "key" },
|
||||
{ value: "password", label: "password" },
|
||||
{ value: "key+password", label: "key+password" },
|
||||
{ value: "key+password", label: "key+passphrase" },
|
||||
]}
|
||||
value={this.tempAuthMode.get()}
|
||||
onChange={(val: string) => {
|
||||
@ -288,17 +274,18 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
|
||||
message={
|
||||
<ul>
|
||||
<li>
|
||||
<b>none</b> - no authentication, or authentication is already
|
||||
configured in your ssh config.
|
||||
<b>none</b> - no authentication details are stored.
|
||||
</li>
|
||||
<li>
|
||||
<b>key</b> - use a private key.
|
||||
<b>key</b> - provide a custom private key for authentication.
|
||||
</li>
|
||||
<li>
|
||||
<b>password</b> - use a password.
|
||||
<b>password</b> - provide a password (to save) for
|
||||
authentication.
|
||||
</li>
|
||||
<li>
|
||||
<b>key+password</b> - use a key with a passphrase.
|
||||
<b>key+passphrase</b> - provide a custom private key with a
|
||||
passphrase (to save) for authentication.
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
@ -374,7 +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.handleOk} okLabel="Connect" />
|
||||
<Modal.Footer onCancel={this.model.closeModal} onOk={this.handleSubmitRemote} okLabel="Connect" />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
@ -26,12 +26,16 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ 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 { For } from "tsx-control-statements/components";
|
||||
import cn from "classnames";
|
||||
import { GlobalModel, GlobalCommandRunner, Screen } from "@/models";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, Modal, Dropdown, Tooltip } from "@/elements";
|
||||
@ -38,15 +38,13 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
shareCopied: OV<boolean> = mobx.observable.box(false, { name: "ScreenSettings-shareCopied" });
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
|
||||
screen: Screen;
|
||||
sessionId: string;
|
||||
screenId: string;
|
||||
remotes: RemoteType[];
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
let screenSettingsModal = GlobalModel.screenSettingsModal.get();
|
||||
let { sessionId, screenId } = screenSettingsModal;
|
||||
this.sessionId = sessionId;
|
||||
const screenSettingsModal = GlobalModel.screenSettingsModal.get();
|
||||
const { sessionId, screenId } = screenSettingsModal;
|
||||
this.screenId = screenId;
|
||||
this.screen = GlobalModel.getScreenById(sessionId, screenId);
|
||||
if (this.screen == null || sessionId == null || screenId == null) {
|
||||
@ -67,8 +65,8 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
value: remote.remotecanonicalname,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
let connValA = util.getRemoteConnVal(a);
|
||||
let connValB = util.getRemoteConnVal(b);
|
||||
const connValA = util.getRemoteConnVal(a);
|
||||
const connValB = util.getRemoteConnVal(b);
|
||||
if (connValA !== connValB) {
|
||||
return connValA - connValB;
|
||||
}
|
||||
@ -92,7 +90,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
if (this.screen.getTabColor() == color) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { tabcolor: color }, false);
|
||||
const prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { tabcolor: color }, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@ -101,7 +99,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
if (this.screen.getTabIcon() == icon) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screen.screenId, { tabicon: icon }, false);
|
||||
const prtn = GlobalCommandRunner.screenSetSettings(this.screen.screenId, { tabicon: icon }, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@ -113,7 +111,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
if (this.screen.archived.get() == val) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenArchive(this.screenId, val);
|
||||
const prtn = GlobalCommandRunner.screenArchive(this.screenId, val);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@ -125,13 +123,13 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
if (this.screen.isWebShared() == val) {
|
||||
return;
|
||||
}
|
||||
let message = val ? WebShareConfirmMarkdown : WebStopShareConfirmMarkdown;
|
||||
let alertRtn = GlobalModel.showAlert({ message: message, confirm: true, markdown: true });
|
||||
const message = val ? WebShareConfirmMarkdown : WebStopShareConfirmMarkdown;
|
||||
const alertRtn = GlobalModel.showAlert({ message: message, confirm: true, markdown: true });
|
||||
alertRtn.then((result) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenWebShare(this.screen.screenId, val);
|
||||
const prtn = GlobalCommandRunner.screenWebShare(this.screen.screenId, val);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
});
|
||||
}
|
||||
@ -141,7 +139,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
if (this.screen == null) {
|
||||
return null;
|
||||
}
|
||||
let shareLink = this.screen.getWebShareUrl();
|
||||
const shareLink = this.screen.getWebShareUrl();
|
||||
if (shareLink == null) {
|
||||
return;
|
||||
}
|
||||
@ -164,7 +162,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
if (util.isStrEq(val, this.screen.name.get())) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { name: val }, false);
|
||||
const prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { name: val }, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@ -176,7 +174,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
if (util.isStrEq(val, this.screen.getShareName())) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { sharename: val }, false);
|
||||
const prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { sharename: val }, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@ -216,14 +214,14 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
}
|
||||
|
||||
render() {
|
||||
let screen = this.screen;
|
||||
const screen = this.screen;
|
||||
if (screen == null) {
|
||||
return null;
|
||||
}
|
||||
let color: string = null;
|
||||
let icon: string = null;
|
||||
let index: number = 0;
|
||||
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
const curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
|
||||
return (
|
||||
<Modal className="screen-settings-modal">
|
||||
|
@ -3,10 +3,14 @@
|
||||
|
||||
.wave-modal-content {
|
||||
.wave-modal-body {
|
||||
padding: 20px 20px;
|
||||
padding: 0px 20px 0px 20px;
|
||||
|
||||
.userinput-query {
|
||||
margin-bottom: 10px;
|
||||
.wave-modal-dialog {
|
||||
padding: 20px 0px 20px 0px;
|
||||
|
||||
.userinput-query {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { Choose, When, If } from "tsx-control-statements/components";
|
||||
import { Modal, PasswordField, Markdown } from "@/elements";
|
||||
import { Modal, PasswordField, Markdown, Checkbox } from "@/elements";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
|
||||
|
||||
import "./userinput.less";
|
||||
@ -9,6 +9,7 @@ import "./userinput.less";
|
||||
export const UserInputModal = (userInputRequest: UserInputRequest) => {
|
||||
const [responseText, setResponseText] = React.useState("");
|
||||
const [countdown, setCountdown] = React.useState(Math.floor(userInputRequest.timeoutms / 1000));
|
||||
const checkboxStatus = React.useRef(false);
|
||||
|
||||
const handleSendCancel = React.useCallback(() => {
|
||||
GlobalModel.sendUserInput({
|
||||
@ -24,16 +25,19 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
|
||||
type: "userinputresp",
|
||||
requestid: userInputRequest.requestid,
|
||||
text: responseText,
|
||||
checkboxstat: checkboxStatus.current,
|
||||
});
|
||||
GlobalModel.remotesModel.closeModal();
|
||||
}, [responseText, userInputRequest]);
|
||||
|
||||
const handleSendConfirm = React.useCallback(
|
||||
(response: boolean) => {
|
||||
console.log(`checkbox ${checkboxStatus}\n\n`);
|
||||
GlobalModel.sendUserInput({
|
||||
type: "userinputresp",
|
||||
requestid: userInputRequest.requestid,
|
||||
confirm: response,
|
||||
checkboxstat: checkboxStatus.current,
|
||||
});
|
||||
GlobalModel.remotesModel.closeModal();
|
||||
},
|
||||
@ -71,23 +75,32 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
|
||||
<Modal className="userinput-modal">
|
||||
<Modal.Header onClose={handleSendCancel} title={userInputRequest.title + ` (${countdown}s)`} />
|
||||
<div className="wave-modal-body">
|
||||
<div className="userinput-query">
|
||||
<If condition={userInputRequest.markdown}>
|
||||
<Markdown text={userInputRequest.querytext} extraClassName="bottom-margin" />
|
||||
</If>
|
||||
<If condition={!userInputRequest.markdown}>{userInputRequest.querytext}</If>
|
||||
<div className="wave-modal-dialog">
|
||||
<div className="userinput-query">
|
||||
<If condition={userInputRequest.markdown}>
|
||||
<Markdown text={userInputRequest.querytext} extraClassName="bottom-margin" />
|
||||
</If>
|
||||
<If condition={!userInputRequest.markdown}>{userInputRequest.querytext}</If>
|
||||
</div>
|
||||
<Choose>
|
||||
<When condition={userInputRequest.responsetype == "text"}>
|
||||
<PasswordField
|
||||
onChange={setResponseText}
|
||||
value={responseText}
|
||||
maxLength={400}
|
||||
autoFocus={true}
|
||||
onKeyDown={(e) => handleTextKeyDown(e)}
|
||||
/>
|
||||
</When>
|
||||
</Choose>
|
||||
</div>
|
||||
<Choose>
|
||||
<When condition={userInputRequest.responsetype == "text"}>
|
||||
<PasswordField
|
||||
onChange={setResponseText}
|
||||
value={responseText}
|
||||
maxLength={400}
|
||||
autoFocus={true}
|
||||
onKeyDown={(e) => handleTextKeyDown(e)}
|
||||
/>
|
||||
</When>
|
||||
</Choose>
|
||||
<If condition={userInputRequest.checkboxmsg != ""}>
|
||||
<Checkbox
|
||||
onChange={() => (checkboxStatus.current = !checkboxStatus.current)}
|
||||
label={userInputRequest.checkboxmsg}
|
||||
className="checkbox-text"
|
||||
/>
|
||||
</If>
|
||||
</div>
|
||||
<Choose>
|
||||
<When condition={userInputRequest.responsetype == "text"}>
|
||||
|
@ -1,5 +1,6 @@
|
||||
.rconndetail-modal {
|
||||
width: 631px;
|
||||
width: auto;
|
||||
max-width: 80vw;
|
||||
min-height: 565px;
|
||||
|
||||
.wave-modal-content {
|
||||
|
@ -12,56 +12,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
.term-prompt.term-prompt-color {
|
||||
.is-dark .term-prompt.term-prompt-color {
|
||||
.term-prompt-branch {
|
||||
color: var(--term-white);
|
||||
}
|
||||
|
||||
.term-prompt-python {
|
||||
color: var(--term-magenta);
|
||||
}
|
||||
|
||||
.term-prompt-remote {
|
||||
color: var(--term-bright-green);
|
||||
|
||||
&.color-green {
|
||||
color: var(--term-green);
|
||||
}
|
||||
|
||||
&.color-red {
|
||||
color: var(--term-red);
|
||||
}
|
||||
|
||||
&.color-blue {
|
||||
color: var(--term-bright-blue);
|
||||
}
|
||||
|
||||
&.color-yellow {
|
||||
color: var(--term-bright-yellow);
|
||||
}
|
||||
|
||||
&.color-magenta {
|
||||
color: var(--term-bright-magenta);
|
||||
}
|
||||
|
||||
&.color-cyan {
|
||||
color: var(--termt-bright-cyan);
|
||||
}
|
||||
|
||||
&.color-white {
|
||||
color: var(--term-bright-white);
|
||||
}
|
||||
|
||||
&.color-orange {
|
||||
color: var(--term-orange);
|
||||
}
|
||||
}
|
||||
|
||||
.term-prompt-cwd {
|
||||
color: var(--term-bright-green);
|
||||
}
|
||||
|
||||
&.term-prompt-isroot .term-prompt-cwd {
|
||||
color: var(--term-red);
|
||||
}
|
||||
}
|
||||
|
||||
.is-light .term-prompt.term-prompt-color {
|
||||
.term-prompt-branch {
|
||||
color: var(--term-black);
|
||||
}
|
||||
|
||||
.term-prompt-remote {
|
||||
color: var(--term-green);
|
||||
}
|
||||
|
||||
.term-prompt-cwd {
|
||||
color: var(--term-green);
|
||||
}
|
||||
&.term-prompt-isroot .term-prompt-cwd {
|
||||
color: var(--term-red);
|
||||
}
|
||||
}
|
||||
|
||||
.term-prompt.term-prompt-color {
|
||||
.term-prompt-python {
|
||||
color: var(--term-magenta);
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ function getRemoteStr(rptr: RemotePtrType): string {
|
||||
if (rptr == null || isBlank(rptr.remoteid)) {
|
||||
return "(invalid remote)";
|
||||
}
|
||||
let username = isBlank(rptr.ownerid) ? null : GlobalModel.resolveUserIdToName(rptr.ownerid);
|
||||
let remoteRef = GlobalModel.resolveRemoteIdToRef(rptr.remoteid);
|
||||
let fullRef = makeFullRemoteRef(username, remoteRef, rptr.name);
|
||||
const username = isBlank(rptr.ownerid) ? null : GlobalModel.resolveUserIdToName(rptr.ownerid);
|
||||
const remoteRef = GlobalModel.resolveRemoteIdToRef(rptr.remoteid);
|
||||
const fullRef = makeFullRemoteRef(username, remoteRef, rptr.name);
|
||||
return fullRef;
|
||||
}
|
||||
|
||||
@ -40,11 +40,11 @@ function getShortVEnv(venvDir: string): string {
|
||||
if (isBlank(venvDir)) {
|
||||
return "";
|
||||
}
|
||||
let lastSlash = venvDir.lastIndexOf("/");
|
||||
const lastSlash = venvDir.lastIndexOf("/");
|
||||
if (lastSlash == -1) {
|
||||
return venvDir;
|
||||
}
|
||||
return venvDir.substr(lastSlash + 1);
|
||||
return venvDir.substring(lastSlash + 1);
|
||||
}
|
||||
|
||||
function replaceHomePath(path: string, homeDir: string): string {
|
||||
@ -52,7 +52,7 @@ function replaceHomePath(path: string, homeDir: string): string {
|
||||
return "~";
|
||||
}
|
||||
if (path.startsWith(homeDir + "/")) {
|
||||
return "~" + path.substr(homeDir.length);
|
||||
return "~" + path.substring(homeDir.length);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
@ -62,7 +62,7 @@ function getCwdStr(remote: RemoteType, state: Record<string, string>): string {
|
||||
return "~";
|
||||
}
|
||||
let cwd = state.cwd;
|
||||
if (remote && remote.remotevars.home) {
|
||||
if (remote?.remotevars.home) {
|
||||
cwd = replaceHomePath(cwd, remote.remotevars.home);
|
||||
}
|
||||
return cwd;
|
||||
@ -71,30 +71,29 @@ function getCwdStr(remote: RemoteType, state: Record<string, string>): string {
|
||||
@mobxReact.observer
|
||||
class Prompt extends React.Component<{ rptr: RemotePtrType; festate: Record<string, string>; color: boolean }, {}> {
|
||||
render() {
|
||||
let rptr = this.props.rptr;
|
||||
const rptr = this.props.rptr;
|
||||
if (rptr == null || isBlank(rptr.remoteid)) {
|
||||
return <span className={cn("term-prompt", "color-green")}> </span>;
|
||||
}
|
||||
let termFontSize = GlobalModel.getTermFontSize();
|
||||
let remote = GlobalModel.getRemote(this.props.rptr.remoteid);
|
||||
let remoteStr = getRemoteStr(rptr);
|
||||
let festate = this.props.festate ?? {};
|
||||
let cwd = getCwdStr(remote, festate);
|
||||
const remote = GlobalModel.getRemote(this.props.rptr.remoteid);
|
||||
const remoteStr = getRemoteStr(rptr);
|
||||
const festate = this.props.festate ?? {};
|
||||
const cwd = getCwdStr(remote, festate);
|
||||
let isRoot = false;
|
||||
if (remote && remote.remotevars) {
|
||||
if (remote?.remotevars) {
|
||||
if (remote.remotevars["sudo"] || remote.remotevars["bestuser"] == "root") {
|
||||
isRoot = true;
|
||||
}
|
||||
}
|
||||
let remoteColorClass = isRoot ? "color-red" : "color-green";
|
||||
if (remote && remote.remoteopts && remote.remoteopts.color) {
|
||||
if (remote?.remoteopts?.color) {
|
||||
remoteColorClass = "color-" + remote.remoteopts.color;
|
||||
}
|
||||
let remoteTitle: string = null;
|
||||
if (remote && remote.remotecanonicalname) {
|
||||
if (remote?.remotecanonicalname) {
|
||||
remoteTitle = "connected to " + remote.remotecanonicalname;
|
||||
}
|
||||
let cwdElem = <span className="term-prompt-cwd">{cwd}</span>;
|
||||
const cwdElem = <span className="term-prompt-cwd">{cwd}</span>;
|
||||
let remoteElem = null;
|
||||
if (remoteStr != "local") {
|
||||
remoteElem = (
|
||||
@ -107,7 +106,7 @@ class Prompt extends React.Component<{ rptr: RemotePtrType; festate: Record<stri
|
||||
let pythonElem = null;
|
||||
let condaElem = null;
|
||||
if (!isBlank(festate["PROMPTVAR_GITBRANCH"])) {
|
||||
let branchName = festate["PROMPTVAR_GITBRANCH"];
|
||||
const branchName = festate["PROMPTVAR_GITBRANCH"];
|
||||
branchElem = (
|
||||
<span title="current git branch" className="term-prompt-branch">
|
||||
git:({branchName}){" "}
|
||||
@ -115,8 +114,8 @@ class Prompt extends React.Component<{ rptr: RemotePtrType; festate: Record<stri
|
||||
);
|
||||
}
|
||||
if (!isBlank(festate["VIRTUAL_ENV"])) {
|
||||
let venvDir = festate["VIRTUAL_ENV"];
|
||||
let venv = getShortVEnv(venvDir);
|
||||
const venvDir = festate["VIRTUAL_ENV"];
|
||||
const venv = getShortVEnv(venvDir);
|
||||
pythonElem = (
|
||||
<span title="python venv" className="term-prompt-python">
|
||||
venv:({venv}){" "}
|
||||
@ -124,7 +123,7 @@ class Prompt extends React.Component<{ rptr: RemotePtrType; festate: Record<stri
|
||||
);
|
||||
}
|
||||
if (!isBlank(festate["CONDA_DEFAULT_ENV"])) {
|
||||
let condaEnv = festate["CONDA_DEFAULT_ENV"];
|
||||
const condaEnv = festate["CONDA_DEFAULT_ENV"];
|
||||
condaElem = (
|
||||
<span title="conda env" className="term-prompt-python">
|
||||
conda:({condaEnv}){" "}
|
||||
|
@ -8,7 +8,7 @@ import { boundMethod } from "autobind-decorator";
|
||||
import { If, For } from "tsx-control-statements/components";
|
||||
import cn from "classnames";
|
||||
import { GlobalModel, RemotesModel, GlobalCommandRunner } from "@/models";
|
||||
import { Button, Status, ShowWaveShellInstallPrompt } from "@/common/elements";
|
||||
import { Button, Status } from "@/common/elements";
|
||||
import * as util from "@/util/util";
|
||||
|
||||
import "./connections.less";
|
||||
@ -71,14 +71,9 @@ class ConnectionsView extends React.Component<{ model: RemotesModel }, { hovered
|
||||
GlobalModel.remotesModel.openAddModal({ remoteedit: true });
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
importSshConfig(): void {
|
||||
GlobalCommandRunner.importSshConfig();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleImportSshConfig(): void {
|
||||
ShowWaveShellInstallPrompt(this.importSshConfig);
|
||||
GlobalCommandRunner.importSshConfig();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
|
@ -77,7 +77,7 @@
|
||||
.search-checkbox {
|
||||
margin: 0 15px;
|
||||
padding: 5px 10px 5px 10px;
|
||||
background: var(--button-secondary-bg-color);
|
||||
background: var(--form-element-secondary-color);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -100,9 +100,6 @@
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-left: 15px;
|
||||
|
||||
.fromts-text {
|
||||
}
|
||||
}
|
||||
|
||||
.reset-button {
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import "@/common/icons/icons.less";
|
||||
|
||||
.line {
|
||||
margin: 0;
|
||||
padding: calc(var(--termpad) * 2) var(--termpad) calc(var(--termpad) * 2 + 1px) calc(var(--termpad) * 3);
|
||||
@ -6,6 +8,7 @@
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
line-height: 11px;
|
||||
font-weight: normal;
|
||||
font-family: var(--termfontfamily);
|
||||
scroll-margin-bottom: 20px;
|
||||
@ -27,9 +30,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
.centered-icon {
|
||||
.positional-icon-visible;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
.positional-icon-inner {
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
&:first-child {
|
||||
font-size: var(--termfontsize-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active.selected {
|
||||
.line-mask {
|
||||
background-color: var(--line-selected-bg-color);
|
||||
border: 2px solid var(--line-active-border-color);
|
||||
border-left: 4px solid var(--line-active-border-color);
|
||||
}
|
||||
@ -92,35 +107,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.simple-line-status {
|
||||
.linenum {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
margin-left: 0.5em;
|
||||
vertical-align: text-top;
|
||||
fill: var(--primary-background-color);
|
||||
}
|
||||
|
||||
.success {
|
||||
color: var(--primary-accent-color);
|
||||
fill: var(--primary-accent-color);
|
||||
}
|
||||
|
||||
.fail {
|
||||
color: var(--line-error-color);
|
||||
fill: var(--line-error-color);
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--line-warning-color);
|
||||
fill: var(--line-warning-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.top-border {
|
||||
border-top: 1px solid #777;
|
||||
padding: 10px;
|
||||
@ -171,7 +157,7 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--app-icon-hover-color);
|
||||
color: var(--line-actions-active-color);
|
||||
}
|
||||
|
||||
padding: 4px 4px;
|
||||
@ -203,7 +189,6 @@
|
||||
.user {
|
||||
color: var(--line-meta-user-color);
|
||||
font-weight: bold;
|
||||
margin-top: 1px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
@ -213,6 +198,31 @@
|
||||
font-size: var(--termfontsize-sm);
|
||||
line-height: var(--termlineheight-sm);
|
||||
color: var(--term-gray);
|
||||
align-items: center;
|
||||
|
||||
.status-icon {
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
height: var(--termfontsize-sm);
|
||||
line-height: 1;
|
||||
|
||||
svg {
|
||||
height: var(--termfontsize-sm);
|
||||
width: var(--termfontsize-sm);
|
||||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
i.fail {
|
||||
color: var(--line-error-color);
|
||||
fill: var(--line-error-color);
|
||||
}
|
||||
|
||||
i.warning,
|
||||
svg.warning {
|
||||
color: var(--line-warning-color);
|
||||
fill: var(--line-warning-color);
|
||||
}
|
||||
}
|
||||
|
||||
.meta-divider {
|
||||
@ -310,7 +320,6 @@
|
||||
margin: 6px 0 2px 10px;
|
||||
padding: 2px 5px 0px 5px;
|
||||
display: inline-block;
|
||||
font-size: 10px;
|
||||
color: var(--term-text-white);
|
||||
background-color: #151715;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import * as util from "@/util/util";
|
||||
import * as textmeasure from "@/util/textmeasure";
|
||||
|
||||
import "./line.less";
|
||||
import { CenteredIcon, RotateIcon } from "../common/icons/icons";
|
||||
|
||||
const DebugHeightProblems = false;
|
||||
const MinLine = 0;
|
||||
@ -317,21 +318,19 @@ class SmallLineAvatar extends React.Component<{ line: LineType; cmd: Cmd; onRigh
|
||||
icon = <i className="fail fa-sharp fa-solid fa-xmark" />;
|
||||
iconTitle = "error";
|
||||
} else if (status == "running" || status == "detached") {
|
||||
icon = <i className="warning fa-sharp fa-solid fa-rotate fa-spin" />;
|
||||
icon = <RotateIcon className="warning spin rotate" />;
|
||||
iconTitle = "running";
|
||||
} else {
|
||||
icon = <i className="fail fa-sharp fa-solid fa-question" />;
|
||||
iconTitle = "unknown";
|
||||
}
|
||||
return (
|
||||
<div
|
||||
onContextMenu={this.props.onRightClick}
|
||||
title={iconTitle}
|
||||
className={cn("simple-line-status", "status-" + status)}
|
||||
>
|
||||
<span className="linenum">{lineNumStr}</span>
|
||||
<div className="avatar">{icon}</div>
|
||||
</div>
|
||||
<>
|
||||
<div className="linenum">{lineNumStr}</div>
|
||||
<div title={iconTitle} className={cn("status-icon", "status-" + status)}>
|
||||
{icon}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import "@/common/icons/icons.less";
|
||||
|
||||
.cmd-input {
|
||||
max-height: max(300px, 40%);
|
||||
display: flex;
|
||||
@ -10,9 +12,6 @@
|
||||
border-top: 2px solid var(--app-border-color);
|
||||
background-color: var(--app-bg-color);
|
||||
|
||||
&.active {
|
||||
}
|
||||
|
||||
&.has-info,
|
||||
&.has-aichat,
|
||||
&.has-history {
|
||||
@ -43,11 +42,18 @@
|
||||
align-items: center;
|
||||
|
||||
.cmdinput-icon {
|
||||
display: inline-flex;
|
||||
color: var(--app-icon-hover-color);
|
||||
opacity: 0.5;
|
||||
|
||||
&.running-cmds i {
|
||||
color: var(--app-warning-color);
|
||||
.centered-icon {
|
||||
.positional-icon-visible;
|
||||
}
|
||||
|
||||
&.running-cmds {
|
||||
.rotate {
|
||||
fill: var(--app-warning-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
|
@ -15,7 +15,7 @@ import { TextAreaInput } from "./textareainput";
|
||||
import { InfoMsg } from "./infomsg";
|
||||
import { HistoryInfo } from "./historyinfo";
|
||||
import { Prompt } from "@/common/prompt/prompt";
|
||||
import { RotateIcon } from "@/common/icons/icons";
|
||||
import { CenteredIcon, RotateIcon } from "@/common/icons/icons";
|
||||
import { AIChat } from "./aichat";
|
||||
|
||||
import "./cmdinput.less";
|
||||
@ -155,7 +155,10 @@ class CmdInput extends React.Component<{}, {}> {
|
||||
title="Filter for Running Commands"
|
||||
onClick={() => this.toggleFilter(screen)}
|
||||
>
|
||||
{numRunningLines} <i className="fa-sharp fa-regular fa-rotate fa-spin fa-fw" />
|
||||
<CenteredIcon>{numRunningLines}</CenteredIcon>{" "}
|
||||
<CenteredIcon>
|
||||
<RotateIcon className="rotate warning spin" />
|
||||
</CenteredIcon>
|
||||
</div>
|
||||
</If>
|
||||
<div key="ai" title="Wave AI (Cmd-Space)" className="cmdinput-icon" onClick={this.clickAIAction}>
|
||||
|
@ -249,12 +249,16 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ const WaveDevVarName = "WAVETERM_DEV";
|
||||
const AuthKeyFile = "waveterm.authkey";
|
||||
const DevServerEndpoint = "http://127.0.0.1:8090";
|
||||
const ProdServerEndpoint = "http://127.0.0.1:1619";
|
||||
const startTs = Date.now();
|
||||
|
||||
const isDev = process.env[WaveDevVarName] != null;
|
||||
const waveHome = getWaveHomeDir();
|
||||
@ -333,6 +334,10 @@ function createMainWindow(clientData: ClientDataType | null): Electron.BrowserWi
|
||||
webPreferences: {
|
||||
preload: path.join(getElectronAppBasePath(), DistDir, "preload.js"),
|
||||
},
|
||||
show: false,
|
||||
});
|
||||
win.once("ready-to-show", () => {
|
||||
win.show();
|
||||
});
|
||||
const indexHtml = isDev ? "index-dev.html" : "index.html";
|
||||
win.loadFile(path.join(getElectronAppBasePath(), "public", indexHtml));
|
||||
@ -617,12 +622,12 @@ function getFetchHeaders() {
|
||||
}
|
||||
|
||||
async function getClientDataPoll(loopNum: number): Promise<ClientDataType | null> {
|
||||
const lastTime = loopNum >= 6;
|
||||
const lastTime = loopNum >= 30;
|
||||
const cdata = await getClientData(!lastTime, loopNum);
|
||||
if (lastTime || cdata != null) {
|
||||
return cdata;
|
||||
}
|
||||
await sleep(1000);
|
||||
await sleep(200);
|
||||
return getClientDataPoll(loopNum + 1);
|
||||
}
|
||||
|
||||
@ -897,6 +902,10 @@ electron.ipcMain.on("change-auto-update", (_, enable: boolean) => {
|
||||
* @param clientData The client data to use to configure the auto-updater. If the clientData has noreleasecheck set to true, the auto-updater will be disabled.
|
||||
*/
|
||||
function configureAutoUpdaterStartup(clientData: ClientDataType) {
|
||||
if (clientData == null) {
|
||||
configureAutoUpdater(false);
|
||||
return;
|
||||
}
|
||||
configureAutoUpdater(!clientData.clientopts.noreleasecheck);
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@
|
||||
top: 0.2em;
|
||||
right: 12rem;
|
||||
border-radius: 5px;
|
||||
background-color: var(--button-secondary-bg-color);
|
||||
background-color: var(--form-element-secondary-color);
|
||||
color: var(--app-error-color);
|
||||
z-index: 1;
|
||||
padding: 0 0.8em;
|
||||
|
2
src/types/custom.d.ts
vendored
2
src/types/custom.d.ts
vendored
@ -666,6 +666,7 @@ declare global {
|
||||
title: string;
|
||||
markdown: boolean;
|
||||
timeoutms: number;
|
||||
checkboxmsg: string;
|
||||
};
|
||||
|
||||
type UserInputResponsePacket = {
|
||||
@ -674,6 +675,7 @@ declare global {
|
||||
text?: string;
|
||||
confirm?: boolean;
|
||||
errormsg?: string;
|
||||
checkboxstat?: boolean;
|
||||
};
|
||||
|
||||
type RenderModeType = "normal" | "collapsed" | "expanded";
|
||||
|
@ -2202,7 +2202,7 @@ func NewHostInfo(hostName string) (*HostInfoType, error) {
|
||||
func RemoteConfigParseCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) {
|
||||
home := base.GetHomeDir()
|
||||
localConfig := filepath.Join(home, ".ssh", "config")
|
||||
systemConfig := filepath.Join("/", "ssh", "config")
|
||||
systemConfig := filepath.Join("/etc", "ssh", "config")
|
||||
sshConfigFiles := []string{localConfig, systemConfig}
|
||||
ssh_config.ReloadConfigs()
|
||||
hostPatterns, hostPatternsErr := resolveSshConfigPatterns(sshConfigFiles)
|
||||
|
@ -258,7 +258,7 @@ func isRtnStateCmd(cmd syntax.Command) bool {
|
||||
}
|
||||
arg1 := getCallExprLitArg(callExpr, 1)
|
||||
if arg0 == "git" {
|
||||
if arg1 == "checkout" || arg1 == "switch" {
|
||||
if arg1 == "checkout" || arg1 == "co" || arg1 == "switch" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -1202,14 +1202,54 @@ func (msh *MShellProc) RunInstall(autoInstall bool) {
|
||||
msh.WriteToPtyBuffer("*error: cannot install on archived remote\n")
|
||||
return
|
||||
}
|
||||
if autoInstall {
|
||||
|
||||
var makeClientCtx context.Context
|
||||
var makeClientCancelFn context.CancelFunc
|
||||
msh.WithLock(func() {
|
||||
makeClientCtx, makeClientCancelFn = context.WithCancel(context.Background())
|
||||
msh.MakeClientCancelFn = makeClientCancelFn
|
||||
msh.MakeClientDeadline = nil
|
||||
go msh.NotifyRemoteUpdate()
|
||||
})
|
||||
defer makeClientCancelFn()
|
||||
clientData, err := sstore.EnsureClientData(makeClientCtx)
|
||||
if err != nil {
|
||||
msh.WriteToPtyBuffer("*error: cannot obtain client data: %v", err)
|
||||
return
|
||||
}
|
||||
hideShellPrompt := clientData.ClientOpts.ConfirmFlags["hideshellprompt"]
|
||||
baseStatus := msh.GetStatus()
|
||||
|
||||
if baseStatus == StatusConnected {
|
||||
ctx, cancelFn := context.WithTimeout(makeClientCtx, 60*time.Second)
|
||||
defer cancelFn()
|
||||
request := &userinput.UserInputRequestType{
|
||||
ResponseType: "confirm",
|
||||
QueryText: "Waveshell is running on your connection and must be restarted to re-install. Would you like to continue?",
|
||||
Title: "Restart Waveshell",
|
||||
}
|
||||
response, err := userinput.GetUserInput(ctx, scbus.MainRpcBus, request)
|
||||
if err != nil {
|
||||
if err == context.Canceled {
|
||||
msh.WriteToPtyBuffer("installation canceled by user\n")
|
||||
} else {
|
||||
msh.WriteToPtyBuffer("timed out waiting for user input\n")
|
||||
}
|
||||
return
|
||||
}
|
||||
if !response.Confirm {
|
||||
msh.WriteToPtyBuffer("installation canceled by user\n")
|
||||
return
|
||||
}
|
||||
} else if !hideShellPrompt {
|
||||
ctx, cancelFn := context.WithTimeout(makeClientCtx, 60*time.Second)
|
||||
defer cancelFn()
|
||||
request := &userinput.UserInputRequestType{
|
||||
ResponseType: "confirm",
|
||||
QueryText: "Waveshell must be reinstalled on the connection to continue. Would you like to install it?",
|
||||
Title: "Install Waveshell",
|
||||
CheckBoxMsg: "Don't show me this again",
|
||||
}
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancelFn()
|
||||
response, err := userinput.GetUserInput(ctx, scbus.MainRpcBus, request)
|
||||
if err != nil {
|
||||
var errMsg error
|
||||
@ -1234,28 +1274,24 @@ func (msh *MShellProc) RunInstall(autoInstall bool) {
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
baseStatus := msh.GetStatus()
|
||||
if baseStatus == StatusConnected {
|
||||
request := &userinput.UserInputRequestType{
|
||||
ResponseType: "confirm",
|
||||
QueryText: "Waveshell is running on your connection and must be restarted to re-install. Would you like to continue?",
|
||||
Title: "Restart Waveshell",
|
||||
}
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancelFn()
|
||||
response, err := userinput.GetUserInput(ctx, scbus.MainRpcBus, request)
|
||||
if err != nil {
|
||||
if err == context.Canceled {
|
||||
msh.WriteToPtyBuffer("installation canceled by user\n")
|
||||
} else {
|
||||
msh.WriteToPtyBuffer("timed out waiting for user input\n")
|
||||
if response.CheckboxStat {
|
||||
clientData.ClientOpts.ConfirmFlags["hideshellprompt"] = true
|
||||
err = sstore.SetClientOpts(makeClientCtx, clientData.ClientOpts)
|
||||
if err != nil {
|
||||
msh.WriteToPtyBuffer("*error, %s\n", err)
|
||||
msh.setErrorStatus(err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
if !response.Confirm {
|
||||
msh.WriteToPtyBuffer("installation canceled by user\n")
|
||||
return
|
||||
|
||||
//reload updated clientdata before sending
|
||||
clientData, err = sstore.EnsureClientData(makeClientCtx)
|
||||
if err != nil {
|
||||
msh.WriteToPtyBuffer("*error, %s\n", err)
|
||||
msh.setErrorStatus(err)
|
||||
return
|
||||
}
|
||||
update := scbus.MakeUpdatePacket()
|
||||
update.AddUpdate(*clientData)
|
||||
}
|
||||
}
|
||||
curStatus := msh.GetInstallStatus()
|
||||
@ -1267,14 +1303,14 @@ func (msh *MShellProc) RunInstall(autoInstall bool) {
|
||||
msh.WriteToPtyBuffer("*error: cannot install on a local remote\n")
|
||||
return
|
||||
}
|
||||
_, err := shellapi.MakeShellApi(packet.ShellType_bash)
|
||||
_, err = shellapi.MakeShellApi(packet.ShellType_bash)
|
||||
if err != nil {
|
||||
msh.WriteToPtyBuffer("*error: %v\n", err)
|
||||
return
|
||||
}
|
||||
if msh.Client == nil {
|
||||
remoteDisplayName := fmt.Sprintf("%s [%s]", remoteCopy.RemoteAlias, remoteCopy.RemoteCanonicalName)
|
||||
client, err := ConnectToClient(remoteCopy.SSHOpts, remoteDisplayName)
|
||||
client, err := ConnectToClient(makeClientCtx, remoteCopy.SSHOpts, remoteDisplayName)
|
||||
if err != nil {
|
||||
statusErr := fmt.Errorf("ssh cannot connect to client: %w", err)
|
||||
msh.setInstallErrorStatus(statusErr)
|
||||
@ -1481,7 +1517,7 @@ func (msh *MShellProc) getActiveShellTypes(ctx context.Context) ([]string, error
|
||||
return utilfn.CombineStrArrays(rtn, activeShells), nil
|
||||
}
|
||||
|
||||
func (msh *MShellProc) createWaveshellSession(remoteCopy sstore.RemoteType) (shexec.ConnInterface, error) {
|
||||
func (msh *MShellProc) createWaveshellSession(clientCtx context.Context, remoteCopy sstore.RemoteType) (shexec.ConnInterface, error) {
|
||||
msh.WithLock(func() {
|
||||
msh.Err = nil
|
||||
msh.ErrNoInitPk = false
|
||||
@ -1510,7 +1546,7 @@ func (msh *MShellProc) createWaveshellSession(remoteCopy sstore.RemoteType) (she
|
||||
wsSession = shexec.CmdWrap{Cmd: ecmd}
|
||||
} else if msh.Client == nil {
|
||||
remoteDisplayName := fmt.Sprintf("%s [%s]", remoteCopy.RemoteAlias, remoteCopy.RemoteCanonicalName)
|
||||
client, err := ConnectToClient(remoteCopy.SSHOpts, remoteDisplayName)
|
||||
client, err := ConnectToClient(clientCtx, remoteCopy.SSHOpts, remoteDisplayName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh cannot connect to client: %w", err)
|
||||
}
|
||||
@ -1559,16 +1595,6 @@ func (NewLauncher) Launch(msh *MShellProc, interactive bool) {
|
||||
msh.WriteToPtyBuffer("remote is trying to install, cancel install before trying to connect again\n")
|
||||
return
|
||||
}
|
||||
msh.WriteToPtyBuffer("connecting to %s...\n", remoteCopy.RemoteCanonicalName)
|
||||
wsSession, err := msh.createWaveshellSession(remoteCopy)
|
||||
if err != nil {
|
||||
msh.WriteToPtyBuffer("*error, %s\n", err.Error())
|
||||
msh.setErrorStatus(err)
|
||||
msh.WithLock(func() {
|
||||
msh.Client = nil
|
||||
})
|
||||
return
|
||||
}
|
||||
var makeClientCtx context.Context
|
||||
var makeClientCancelFn context.CancelFunc
|
||||
msh.WithLock(func() {
|
||||
@ -1578,6 +1604,16 @@ func (NewLauncher) Launch(msh *MShellProc, interactive bool) {
|
||||
go msh.NotifyRemoteUpdate()
|
||||
})
|
||||
defer makeClientCancelFn()
|
||||
msh.WriteToPtyBuffer("connecting to %s...\n", remoteCopy.RemoteCanonicalName)
|
||||
wsSession, err := msh.createWaveshellSession(makeClientCtx, remoteCopy)
|
||||
if err != nil {
|
||||
msh.WriteToPtyBuffer("*error, %s\n", err.Error())
|
||||
msh.setErrorStatus(err)
|
||||
msh.WithLock(func() {
|
||||
msh.Client = nil
|
||||
})
|
||||
return
|
||||
}
|
||||
cproc, err := shexec.MakeClientProc(makeClientCtx, wsSession)
|
||||
msh.WithLock(func() {
|
||||
msh.MakeClientCancelFn = nil
|
||||
|
@ -65,7 +65,7 @@ func createDummySigner() ([]ssh.Signer, error) {
|
||||
// they were successes. An error in this function prevents any other
|
||||
// keys from being attempted. But if there's an error because of a dummy
|
||||
// file, the library can still try again with a new key.
|
||||
func createPublicKeyCallback(sshKeywords *SshKeywords, passphrase string) func() ([]ssh.Signer, error) {
|
||||
func createPublicKeyCallback(connCtx context.Context, sshKeywords *SshKeywords, passphrase string) func() ([]ssh.Signer, error) {
|
||||
var identityFiles []string
|
||||
existingKeys := make(map[string][]byte)
|
||||
|
||||
@ -124,7 +124,7 @@ func createPublicKeyCallback(sshKeywords *SshKeywords, passphrase string) func()
|
||||
QueryText: fmt.Sprintf("Enter passphrase for the SSH key: %s", identityFile),
|
||||
Title: "Publickey Auth + Passphrase",
|
||||
}
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
ctx, cancelFn := context.WithTimeout(connCtx, 60*time.Second)
|
||||
defer cancelFn()
|
||||
response, err := userinput.GetUserInput(ctx, scbus.MainRpcBus, request)
|
||||
if err != nil {
|
||||
@ -150,11 +150,11 @@ func createDefaultPasswordCallbackPrompt(password string) func() (secret string,
|
||||
}
|
||||
}
|
||||
|
||||
func createInteractivePasswordCallbackPrompt(remoteDisplayName string) func() (secret string, err error) {
|
||||
func createInteractivePasswordCallbackPrompt(connCtx context.Context, remoteDisplayName string) func() (secret string, err error) {
|
||||
return func() (secret string, err error) {
|
||||
// limited to 15 seconds for some reason. this should be investigated more
|
||||
// in the future
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
ctx, cancelFn := context.WithTimeout(connCtx, 60*time.Second)
|
||||
defer cancelFn()
|
||||
queryText := fmt.Sprintf(
|
||||
"Password Authentication requested from connection \n"+
|
||||
@ -174,13 +174,13 @@ func createInteractivePasswordCallbackPrompt(remoteDisplayName string) func() (s
|
||||
}
|
||||
}
|
||||
|
||||
func createCombinedPasswordCallbackPrompt(password string, remoteDisplayName string) func() (secret string, err error) {
|
||||
func createCombinedPasswordCallbackPrompt(connCtx context.Context, password string, remoteDisplayName string) func() (secret string, err error) {
|
||||
var once sync.Once
|
||||
return func() (secret string, err error) {
|
||||
var prompt func() (secret string, err error)
|
||||
once.Do(func() { prompt = createDefaultPasswordCallbackPrompt(password) })
|
||||
if prompt == nil {
|
||||
prompt = createInteractivePasswordCallbackPrompt(remoteDisplayName)
|
||||
prompt = createInteractivePasswordCallbackPrompt(connCtx, remoteDisplayName)
|
||||
}
|
||||
return prompt()
|
||||
}
|
||||
@ -199,14 +199,14 @@ func createNaiveKbdInteractiveChallenge(password string) func(name, instruction
|
||||
}
|
||||
}
|
||||
|
||||
func createInteractiveKbdInteractiveChallenge(remoteName string) func(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||
func createInteractiveKbdInteractiveChallenge(connCtx context.Context, remoteName string) func(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||
return func(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||
if len(questions) != len(echos) {
|
||||
return nil, fmt.Errorf("bad response from server: questions has len %d, echos has len %d", len(questions), len(echos))
|
||||
}
|
||||
for i, question := range questions {
|
||||
echo := echos[i]
|
||||
answer, err := promptChallengeQuestion(question, echo, remoteName)
|
||||
answer, err := promptChallengeQuestion(connCtx, question, echo, remoteName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -216,10 +216,10 @@ func createInteractiveKbdInteractiveChallenge(remoteName string) func(name, inst
|
||||
}
|
||||
}
|
||||
|
||||
func promptChallengeQuestion(question string, echo bool, remoteName string) (answer string, err error) {
|
||||
func promptChallengeQuestion(connCtx context.Context, question string, echo bool, remoteName string) (answer string, err error) {
|
||||
// limited to 15 seconds for some reason. this should be investigated more
|
||||
// in the future
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
ctx, cancelFn := context.WithTimeout(connCtx, 60*time.Second)
|
||||
defer cancelFn()
|
||||
queryText := fmt.Sprintf(
|
||||
"Keyboard Interactive Authentication requested from connection \n"+
|
||||
@ -238,13 +238,13 @@ func promptChallengeQuestion(question string, echo bool, remoteName string) (ans
|
||||
return response.Text, nil
|
||||
}
|
||||
|
||||
func createCombinedKbdInteractiveChallenge(password string, remoteName string) ssh.KeyboardInteractiveChallenge {
|
||||
func createCombinedKbdInteractiveChallenge(connCtx context.Context, password string, remoteName string) ssh.KeyboardInteractiveChallenge {
|
||||
var once sync.Once
|
||||
return func(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||
var challenge ssh.KeyboardInteractiveChallenge
|
||||
once.Do(func() { challenge = createNaiveKbdInteractiveChallenge(password) })
|
||||
if challenge == nil {
|
||||
challenge = createInteractiveKbdInteractiveChallenge(remoteName)
|
||||
challenge = createInteractiveKbdInteractiveChallenge(connCtx, remoteName)
|
||||
}
|
||||
return challenge(name, instruction, questions, echos)
|
||||
}
|
||||
@ -505,7 +505,20 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
|
||||
return waveHostKeyCallback, nil
|
||||
}
|
||||
|
||||
func ConnectToClient(opts *sstore.SSHOpts, remoteDisplayName string) (*ssh.Client, error) {
|
||||
func DialContext(ctx context.Context, network string, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
|
||||
d := net.Dialer{Timeout: config.Timeout}
|
||||
conn, err := d.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, chans, reqs, err := ssh.NewClientConn(conn, addr, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ssh.NewClient(c, chans, reqs), nil
|
||||
}
|
||||
|
||||
func ConnectToClient(connCtx context.Context, opts *sstore.SSHOpts, remoteDisplayName string) (*ssh.Client, error) {
|
||||
sshConfigKeywords, err := findSshConfigKeywords(opts.SSHHost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -516,9 +529,9 @@ func ConnectToClient(opts *sstore.SSHOpts, remoteDisplayName string) (*ssh.Clien
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKeyCallback := ssh.PublicKeysCallback(createPublicKeyCallback(sshKeywords, opts.SSHPassword))
|
||||
keyboardInteractive := ssh.KeyboardInteractive(createCombinedKbdInteractiveChallenge(opts.SSHPassword, remoteDisplayName))
|
||||
passwordCallback := ssh.PasswordCallback(createCombinedPasswordCallbackPrompt(opts.SSHPassword, remoteDisplayName))
|
||||
publicKeyCallback := ssh.PublicKeysCallback(createPublicKeyCallback(connCtx, sshKeywords, opts.SSHPassword))
|
||||
keyboardInteractive := ssh.KeyboardInteractive(createCombinedKbdInteractiveChallenge(connCtx, opts.SSHPassword, remoteDisplayName))
|
||||
passwordCallback := ssh.PasswordCallback(createCombinedPasswordCallbackPrompt(connCtx, opts.SSHPassword, remoteDisplayName))
|
||||
|
||||
// batch mode turns off interactive input. this means the number of
|
||||
// attemtps must drop to 1 with this setup
|
||||
@ -566,7 +579,7 @@ func ConnectToClient(opts *sstore.SSHOpts, remoteDisplayName string) (*ssh.Clien
|
||||
HostKeyCallback: hostKeyCallback,
|
||||
}
|
||||
networkAddr := sshKeywords.HostName + ":" + sshKeywords.Port
|
||||
return ssh.Dial("tcp", networkAddr, clientConfig)
|
||||
return DialContext(connCtx, "tcp", networkAddr, clientConfig)
|
||||
}
|
||||
|
||||
type SshKeywords struct {
|
||||
|
@ -21,6 +21,7 @@ type UserInputRequestType struct {
|
||||
Title string `json:"title"`
|
||||
Markdown bool `json:"markdown"`
|
||||
TimeoutMs int `json:"timeoutms"`
|
||||
CheckBoxMsg string `json:"checkboxmsg"`
|
||||
}
|
||||
|
||||
func (*UserInputRequestType) GetType() string {
|
||||
@ -39,11 +40,12 @@ const UserInputResponsePacketStr = "userinputresp"
|
||||
|
||||
// An RpcResponse for user input requests
|
||||
type UserInputResponsePacketType struct {
|
||||
Type string `json:"type"`
|
||||
RequestId string `json:"requestid"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Confirm bool `json:"confirm,omitempty"`
|
||||
ErrorMsg string `json:"errormsg,omitempty"`
|
||||
Type string `json:"type"`
|
||||
RequestId string `json:"requestid"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Confirm bool `json:"confirm,omitempty"`
|
||||
ErrorMsg string `json:"errormsg,omitempty"`
|
||||
CheckboxStat bool `json:"checkboxstat,omitempty"`
|
||||
}
|
||||
|
||||
func (*UserInputResponsePacketType) GetType() string {
|
||||
|
Loading…
Reference in New Issue
Block a user