mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1a6dfb249 | |||
| 6319b26924 | |||
| 41fe49a54c | |||
| e044487489 | |||
| 0fdcd27fee | |||
| d923de412a | |||
| 15485d7235 | |||
| 59aef86e77 | |||
| 5353f40a20 | |||
| f86f010a34 | |||
| 73e5515e17 | |||
| 6919dbfb5f | |||
| 1f5309e097 | |||
| af7cc866d3 | |||
| 37e56acf63 | |||
| 70088afdb5 | |||
| 455790416d | |||
| eed234a131 | |||
| 5a6575a393 | |||
| 84cea373a8 | |||
| 181e14f55c | |||
| 1c23701181 | |||
| 0fe767cdf3 | |||
| 097623ab51 | |||
| 9de25e4869 | |||
| a139633375 | |||
| ccc63937b6 | |||
| dc7fc2c823 | |||
| ca5117cda0 | |||
| f41ac1d5e3 | |||
| 0024f0f3e8 | |||
| 8a3d9628ba | |||
| 7c2265f3e3 | |||
| 4f6c2ee39b | |||
| a1e4e807cc | |||
| 5c85b2b786 | |||
| 4879e90e91 | |||
| a61310afc3 | |||
| 80c781bf4c | |||
| 33400e65ff | |||
| 7b58949027 | |||
| 3be96c949d | |||
| 052645e0df |
+7
-2
@@ -18,6 +18,7 @@
|
||||
"appId": "dev.commandline.waveterm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lexical/react": "^0.14.3",
|
||||
"@monaco-editor/react": "^4.5.1",
|
||||
"@table-nav/core": "^0.0.7",
|
||||
"@table-nav/react": "^0.0.7",
|
||||
@@ -31,6 +32,7 @@
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"electron-updater": "^6.1.8",
|
||||
"framer-motion": "^10.16.16",
|
||||
"lexical": "^0.14.3",
|
||||
"mobx": "6.12",
|
||||
"mobx-react": "^7.5.0",
|
||||
"monaco-editor": "^0.44.0",
|
||||
@@ -41,6 +43,7 @@
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-frame-component": "^5.2.6",
|
||||
"react-markdown": "^9.0.0",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
@@ -49,8 +52,10 @@
|
||||
"tsx-control-statements": "^5.1.1",
|
||||
"uuid": "^9.0.0",
|
||||
"winston": "^3.8.2",
|
||||
"xterm": "^5.0.0",
|
||||
"xterm-addon-web-links": "^0.9.0"
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-serialize": "^0.11.0",
|
||||
"xterm-addon-web-links": "^0.9.0",
|
||||
"xterm-addon-webgl": "^0.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.10",
|
||||
|
||||
+18
-16
@@ -42,16 +42,19 @@
|
||||
--app-bg-color: black;
|
||||
--app-accent-color: rgb(88, 193, 66);
|
||||
--app-accent-bg-color: rgba(88, 193, 66, 0.25);
|
||||
--app-accent-bg-darker-color: rgba(88, 193, 66, 0.5);
|
||||
--app-text-color: rgb(211, 215, 207);
|
||||
--app-text-primary-color: rgb(255, 255, 255);
|
||||
--app-text-secondary-color: rgb(195, 200, 194);
|
||||
--app-text-disabled-color: rgb(173, 173, 173);
|
||||
--app-text-bg-color: rgb(23, 23, 23);
|
||||
--app-text-bg-disabled-color: rgb(51, 51, 51);
|
||||
--app-border-color: rgb(51, 51, 51);
|
||||
--app-maincontent-bg-color: #333;
|
||||
--app-maincontent-bg-color: rgb(51, 51, 51);
|
||||
--app-panel-bg-color: rgba(21, 23, 21, 1);
|
||||
--app-panel-bg-color-dev: rgb(21, 23, 48);
|
||||
--app-icon-color: rgb(139, 145, 138);
|
||||
--app-icon-hover-color: #fff;
|
||||
--app-icon-hover-color: rgb(255, 255, 255);
|
||||
--app-selected-mask-color: rgba(255, 255, 255, 0.06);
|
||||
|
||||
/* global status colors */
|
||||
@@ -87,7 +90,7 @@
|
||||
--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-primary-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);
|
||||
@@ -122,18 +125,18 @@
|
||||
|
||||
/* line colors */
|
||||
--line-sidebar-message-color: rgb(196, 160, 0);
|
||||
--line-background: rgba(21, 23, 21, 1);
|
||||
--line-avatar-color: #eceeec;
|
||||
--line-background: var(--app-panel-bg-color);
|
||||
--line-avatar-color: rgb(236, 238, 236);
|
||||
--line-text-color: rgb(211, 215, 207);
|
||||
--line-svg-fill-color: rgb(150, 152, 150);
|
||||
--line-svg-hover-fill-color: #eceeec;
|
||||
--line-svg-hover-fill-color: rgb(236, 238, 236);
|
||||
--line-separator-color: rgb(126, 126, 126);
|
||||
--line-error-color: var(--app-error-color);
|
||||
--line-warning-color: var(--app-warning-color);
|
||||
--line-base-soft-blue-color: #729fcf;
|
||||
--line-base-soft-blue-color: rgb(114, 159, 207);
|
||||
--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-border-left-color: rgb(119, 119, 119);
|
||||
--line-selected-error-border-color: rgba(204, 0, 0, 0.8);
|
||||
--line-selected-error-bg-color: rgb(19, 4, 3);
|
||||
--line-error-bg-color: rgba(200, 0, 0, 0.1);
|
||||
@@ -152,22 +155,21 @@
|
||||
/* table colors */
|
||||
--table-border-color: rgba(241, 246, 243, 0.15);
|
||||
--table-thead-border-top-color: rgba(250, 250, 250, 0.1);
|
||||
--table-thead-bright-border-color: #ccc;
|
||||
--table-thead-bright-border-color: rgb(204, 204, 204);
|
||||
--table-thead-bg-color: rgba(250, 250, 250, 0.02);
|
||||
--table-tr-border-bottom-color: rgba(241, 246, 243, 0.15);
|
||||
--table-tr-hover-bg-color: rgba(255, 255, 255, 0.06);
|
||||
--table-tr-selected-bg-color: #222;
|
||||
--table-tr-selected-hover-bg-color: #333;
|
||||
--table-tr-selected-bg-color: rgb(34, 34, 34);
|
||||
--table-tr-selected-hover-bg-color: var(--app-maincontent-bg-color);
|
||||
|
||||
/* cmdinput colors */
|
||||
--cmdinput-textarea-bg-color: #171717;
|
||||
--cmdinput-bg-color: var(--app-text-bg-color);
|
||||
--cmdinput-text-error-color: var(--term-red);
|
||||
--cmdinput-history-item-error-color: var(--term-bright-red);
|
||||
--cmdinput-history-item-selected-error-color: var(--term-bright-red);
|
||||
--cmdinput-button-bg-color: rgb(88, 193, 66);
|
||||
--cmdinput-comment-button-bg-color: rgb(57, 113, 255);
|
||||
--cmdinput-disabled-icon-color: rgb(76, 81, 75, 1);
|
||||
--cmdinput-history-bg-color: rgb(21, 23, 21, 1);
|
||||
--cmdinput-button-bg-color: var(--tab-green);
|
||||
--cmdinput-disabled-bg-color: var(--app-text-bg-disabled-color);
|
||||
--cmdinput-history-bg-color: var(--app-bg-color);
|
||||
|
||||
/* screen view color */
|
||||
--screen-view-text-caption-color: rgb(139, 145, 138);
|
||||
|
||||
+17
-19
@@ -5,20 +5,20 @@
|
||||
@import url("./term-light.css");
|
||||
|
||||
:root {
|
||||
--app-bg-color: #fefefe;
|
||||
--app-bg-color: rgb(254, 254, 254);
|
||||
--app-accent-color: rgb(75, 166, 57);
|
||||
--app-accent-bg-color: rgba(75, 166, 57, 0.2);
|
||||
--app-text-color: #000;
|
||||
--app-text-color: rgb(0, 0, 0);
|
||||
--app-text-primary-color: rgb(0, 0, 0, 0.9);
|
||||
--app-text-secondary-color: rgb(0, 0, 0, 0.7);
|
||||
--app-border-color: rgb(139 145 138);
|
||||
--app-panel-bg-color: #e0e0e0;
|
||||
--app-panel-bg-color-dev: #e0e0e0;
|
||||
--app-icon-color: rgb(80, 80, 80);
|
||||
--app-icon-hover-color: rgb(100, 100, 100);
|
||||
--app-panel-bg-color: rgb(224, 224, 224);
|
||||
--app-panel-bg-color-dev: rgb(224, 224, 224);
|
||||
--app-icon-color: rgb(110, 110, 110);
|
||||
--app-icon-hover-color: rgb(80, 80, 80);
|
||||
--app-selected-mask-color: rgba(0, 0, 0, 0.06);
|
||||
|
||||
--input-bg-color: #eeeeee;
|
||||
--input-bg-color: rgb(238, 238, 238);
|
||||
|
||||
/* tab color */
|
||||
--tab-white: rgb(0, 0, 0, 0.6);
|
||||
@@ -30,19 +30,19 @@
|
||||
--table-thead-bg-color: rgba(250, 250, 250, 0.15);
|
||||
--table-tr-border-bottom-color: rgba(0, 0, 0, 0.15);
|
||||
--table-tr-hover-bg-color: rgba(0, 0, 0, 0.15);
|
||||
--table-tr-selected-bg-color: #dddddd;
|
||||
--table-tr-selected-hover-bg-color: #cccccc;
|
||||
--table-tr-selected-bg-color: rgb(221, 221, 221);
|
||||
--table-tr-selected-hover-bg-color: rgb(204, 204, 204);
|
||||
|
||||
/* form colors */
|
||||
--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-text-color: var(--app-text-primary-color);
|
||||
--form-element-primary-text-color: var(--app-text-primary-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);
|
||||
--form-element-disabled-text-color: #b7b7b7;
|
||||
--form-element-placeholder-color: #b7b7b7;
|
||||
--form-element-disabled-text-color: rgb(183, 183, 183);
|
||||
--form-element-placeholder-color: rgb(183, 183, 183);
|
||||
|
||||
--markdown-bg-color: rgb(0, 0, 0, 0.1);
|
||||
|
||||
@@ -50,8 +50,6 @@
|
||||
--modal-header-bottom-border-color: rgba(0, 0, 0, 0.3);
|
||||
|
||||
/* cmd input */
|
||||
--cmdinput-textarea-bg-color: rgba(0, 0, 0, 0.1);
|
||||
--cmdinput-textarea-border-color: var(--form-element-border-color);
|
||||
|
||||
/* scroll colors */
|
||||
--scrollbar-background-color: var(--app-bg-color);
|
||||
@@ -64,15 +62,15 @@
|
||||
--line-actions-inactive-color: rgba(0, 0, 0, 0.3);
|
||||
--line-actions-active-color: rgba(0, 0, 0, 1);
|
||||
|
||||
--logo-button-hover-bg-color: #f0f0f0;
|
||||
--logo-button-hover-bg-color: rgb(240, 240, 240);
|
||||
|
||||
--xterm-viewport-border-color: rgba(0, 0, 0, 0.3);
|
||||
|
||||
--datepicker-cell-hover-bg-color: #f0f0f0;
|
||||
--datepicker-cell-hover-bg-color: rgb(240, 240, 240);
|
||||
--datepicker-cell-other-text-color: rgba(0, 0, 0, 0.3);
|
||||
--datepicker-header-fade-color: rgba(0, 0, 0, 0.4);
|
||||
--datepicker-year-header-bg-color: #f5f5f5;
|
||||
--datepicker-year-header-border-color: #dcdcdc;
|
||||
--datepicker-year-header-bg-color: rgb(245, 245, 245);
|
||||
--datepicker-year-header-border-color: rgb(220, 220, 220);
|
||||
|
||||
/* toggle colors */
|
||||
--toggle-thumb-color: var(--app-bg-color);
|
||||
|
||||
+1
-1
@@ -146,7 +146,7 @@ class App extends React.Component<{}, {}> {
|
||||
<If condition={GlobalModel.isDev && rightSidebarCollapsed && activeMainView == "session"}>
|
||||
<div className="right-sidebar-triggers">
|
||||
<Button className="secondary ghost right-sidebar-trigger" onClick={this.openRightSidebar}>
|
||||
<i className="fa-sharp fa-regular fa-lightbulb"></i>
|
||||
<i className="fa-sharp fa-solid fa-sidebar-flip"></i>
|
||||
</Button>
|
||||
</div>
|
||||
</If>
|
||||
|
||||
@@ -64,3 +64,7 @@ export enum StatusIndicatorLevel {
|
||||
|
||||
// matches packet.go
|
||||
export const ErrorCode_InvalidCwd = "ERRCWD";
|
||||
|
||||
export const InputAuxView_History = "history";
|
||||
export const InputAuxView_Info = "info";
|
||||
export const InputAuxView_AIChat = "aichat";
|
||||
|
||||
@@ -6,9 +6,10 @@ import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { GlobalModel, GlobalCommandRunner, RemotesModel, getApi } from "@/models";
|
||||
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "@/common/elements";
|
||||
import { commandRtnHandler, isBlank } from "@/util/util";
|
||||
import { getTermThemes } from "@/util/themeutil";
|
||||
import * as appconst from "@/app/appconst";
|
||||
|
||||
import "./clientsettings.less";
|
||||
@@ -69,7 +70,20 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
return;
|
||||
}
|
||||
const prtn = GlobalCommandRunner.setTheme(themeSource, false);
|
||||
getApi().setNativeThemeSource(themeSource);
|
||||
GlobalModel.getElectronApi().setNativeThemeSource(themeSource);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeTermTheme(theme: string): void {
|
||||
// For global terminal theme, the key is global, otherwise it's either
|
||||
// sessionId or screenId.
|
||||
const currTheme = GlobalModel.getTermTheme()["global"];
|
||||
if (currTheme == theme) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prtn = GlobalCommandRunner.setGlobalTermTheme(theme, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@@ -93,7 +107,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
prtn = GlobalCommandRunner.releaseCheckAutoOff(false);
|
||||
}
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
getApi().changeAutoUpdate(val);
|
||||
GlobalModel.getElectronApi().changeAutoUpdate(val);
|
||||
}
|
||||
|
||||
getFontSizes(): DropdownItem[] {
|
||||
@@ -193,6 +207,8 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
const curFontSize = GlobalModel.getTermFontSize();
|
||||
const curFontFamily = GlobalModel.getTermFontFamily();
|
||||
const curTheme = GlobalModel.getThemeSource();
|
||||
const termThemes = getTermThemes(GlobalModel.termThemes, "Wave Default");
|
||||
const currTermTheme = GlobalModel.getTermTheme()["global"] ?? termThemes[0].label;
|
||||
|
||||
return (
|
||||
<MainView className="clientsettings-view" title="Client Settings" onClose={this.handleClose}>
|
||||
@@ -233,6 +249,19 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<If condition={termThemes.length > 0}>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Terminal Theme</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="terminal-theme-dropdown"
|
||||
options={termThemes}
|
||||
defaultValue={currTermTheme}
|
||||
onChange={this.handleChangeTermTheme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Client ID</div>
|
||||
<div className="settings-input">{cdata.clientid}</div>
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.danger {
|
||||
background: var(--app-error-color);
|
||||
}
|
||||
|
||||
&.primary.outlined {
|
||||
background: none;
|
||||
border: 1px solid var(--form-element-primary-color);
|
||||
@@ -36,6 +40,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.greyoutlined {
|
||||
background: none;
|
||||
border: 1px solid var(--app-text-secondary-color);
|
||||
|
||||
i {
|
||||
fill: var(--app-text-secondary-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.outlined,
|
||||
&.primary.greyoutlined {
|
||||
&.hover-danger:hover {
|
||||
color: var(--form-element-primary-text-color);
|
||||
border: 1px solid var(--app-error-color);
|
||||
background: var(--app-error-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.outlined.danger {
|
||||
background: none;
|
||||
border: 1px solid var(--app-error-color);
|
||||
|
||||
i {
|
||||
fill: var(--app-error-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.greytext {
|
||||
color: var(--app-text-secondary-color);
|
||||
}
|
||||
|
||||
&.primary.ghost {
|
||||
background: none;
|
||||
i {
|
||||
@@ -43,6 +78,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.ghost.danger {
|
||||
background: none;
|
||||
i {
|
||||
fill: var(--app-error-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
color: var(--form-element-text-color);
|
||||
background: var(--form-element-secondary-color);
|
||||
@@ -56,6 +98,11 @@
|
||||
border: 1px solid var(--form-element-text-color);
|
||||
}
|
||||
|
||||
&.secondary.outlined.danger {
|
||||
background: none;
|
||||
border: 1px solid var(--app-error-color);
|
||||
}
|
||||
|
||||
&.secondary.ghost {
|
||||
background: none;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import "./button.less";
|
||||
|
||||
interface ButtonProps {
|
||||
children: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
disabled?: boolean;
|
||||
leftIcon?: React.ReactNode;
|
||||
rightIcon?: React.ReactNode;
|
||||
@@ -14,6 +14,7 @@ interface ButtonProps {
|
||||
autoFocus?: boolean;
|
||||
className?: string;
|
||||
termInline?: boolean;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
class Button extends React.Component<ButtonProps> {
|
||||
@@ -23,14 +24,14 @@ class Button extends React.Component<ButtonProps> {
|
||||
};
|
||||
|
||||
@boundMethod
|
||||
handleClick() {
|
||||
handleClick(e) {
|
||||
if (this.props.onClick && !this.props.disabled) {
|
||||
this.props.onClick();
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { leftIcon, rightIcon, children, disabled, style, autoFocus, termInline, className } = this.props;
|
||||
const { leftIcon, rightIcon, children, disabled, style, autoFocus, termInline, className, title } = this.props;
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -39,6 +40,7 @@ class Button extends React.Component<ButtonProps> {
|
||||
disabled={disabled}
|
||||
style={style}
|
||||
autoFocus={autoFocus}
|
||||
title={title}
|
||||
>
|
||||
{leftIcon && <span className="icon-left">{leftIcon}</span>}
|
||||
{children}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.copy-button {
|
||||
padding: 5px 5px;
|
||||
|
||||
.fa-check {
|
||||
color: var(--app-success-color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as React from "react";
|
||||
import { Button } from "./button";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
|
||||
import "./copybutton.less";
|
||||
|
||||
type CopyButtonProps = {
|
||||
title: string;
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
};
|
||||
|
||||
@mobxReact.observer
|
||||
class CopyButton extends React.Component<CopyButtonProps, {}> {
|
||||
isCopied: OV<boolean> = mobx.observable.box(false, { name: "isCopied" });
|
||||
|
||||
@boundMethod
|
||||
handleOnClick(e: React.MouseEvent<HTMLButtonElement>) {
|
||||
if (this.isCopied.get()) {
|
||||
return;
|
||||
}
|
||||
mobx.action(() => {
|
||||
this.isCopied.set(true);
|
||||
})();
|
||||
setTimeout(() => {
|
||||
mobx.action(() => {
|
||||
this.isCopied.set(false);
|
||||
})();
|
||||
}, 2000);
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { title, onClick } = this.props;
|
||||
const isCopied = this.isCopied.get();
|
||||
return (
|
||||
<Button onClick={this.handleOnClick} className="copy-button secondary ghost" title={title}>
|
||||
{isCopied ? (
|
||||
<i className="fa-sharp fa-solid fa-check"></i>
|
||||
) : (
|
||||
<i className="fa-sharp fa-solid fa-copy"></i>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { CopyButton };
|
||||
@@ -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);
|
||||
@@ -302,51 +301,24 @@ 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 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 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) {
|
||||
@@ -378,14 +350,6 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
|
||||
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", (_) => {
|
||||
toggleModal();
|
||||
return true;
|
||||
@@ -404,10 +368,10 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
|
||||
});
|
||||
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;
|
||||
let selectionTimeoutId = null;
|
||||
handleDatePartChange(currentPart, newValue);
|
||||
|
||||
// Clear any existing timeout
|
||||
@@ -416,12 +380,10 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
|
||||
}
|
||||
|
||||
// Re-focus and select the input after state update
|
||||
setSelectionTimeoutId(
|
||||
setTimeout(() => {
|
||||
event.target.focus();
|
||||
event.target.select();
|
||||
}, 0)
|
||||
);
|
||||
selectionTimeoutId = setTimeout(() => {
|
||||
event.target.focus();
|
||||
event.target.select();
|
||||
}, 0);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
};
|
||||
this.wrapperRef = React.createRef();
|
||||
this.menuRef = React.createRef();
|
||||
this.curUuid == uuidv4();
|
||||
this.curUuid = uuidv4();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -100,6 +100,9 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
|
||||
@boundMethod
|
||||
handleClick() {
|
||||
if (!this.state.isOpen || !this.state.isTouched) {
|
||||
this.registerKeybindings();
|
||||
}
|
||||
this.toggleDropdown();
|
||||
}
|
||||
|
||||
@@ -109,6 +112,7 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
this.registerKeybindings();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
registerKeybindings() {
|
||||
let keybindManager = GlobalModel.keybindManager;
|
||||
let domain = "dropdown-" + this.curUuid;
|
||||
@@ -122,6 +126,7 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
});
|
||||
keybindManager.registerKeybinding("control", domain, "generic:cancel", (waveEvent) => {
|
||||
this.setState({ isOpen: false });
|
||||
this.unregisterKeybindings();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("control", domain, "generic:selectAbove", (waveEvent) => {
|
||||
@@ -165,10 +170,12 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleBlur() {
|
||||
this.unregisterKeybindings();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
unregisterKeybindings() {
|
||||
let domain = "dropdown-" + this.curUuid;
|
||||
GlobalModel.keybindManager.unregisterDomain(domain);
|
||||
@@ -189,6 +196,7 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
}
|
||||
onChange(value);
|
||||
this.setState({ isOpen: false, isTouched: true });
|
||||
this.unregisterKeybindings();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
|
||||
@@ -18,3 +18,4 @@ export { Toggle } from "./toggle";
|
||||
export { Tooltip } from "./tooltip";
|
||||
export { TabIcon } from "./tabicon";
|
||||
export { DatePicker } from "./datepicker";
|
||||
export { CopyButton } from "./copybutton";
|
||||
|
||||
@@ -7,8 +7,10 @@ import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import cn from "classnames";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import "./markdown.less";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
|
||||
function LinkRenderer(props: any): any {
|
||||
let newUrl = "https://extern?" + encodeURIComponent(props.href);
|
||||
@@ -28,14 +30,17 @@ function CodeRenderer(props: any): any {
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class CodeBlockMarkdown extends React.Component<{ children: React.ReactNode; codeSelectSelectedIndex?: number }, {}> {
|
||||
class CodeBlockMarkdown extends React.Component<
|
||||
{ children: React.ReactNode; codeSelectSelectedIndex?: number; uuid: string },
|
||||
{}
|
||||
> {
|
||||
blockIndex: number;
|
||||
blockRef: React.RefObject<HTMLPreElement>;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.blockRef = React.createRef();
|
||||
this.blockIndex = GlobalModel.inputModel.addCodeBlockToCodeSelect(this.blockRef);
|
||||
this.blockIndex = GlobalModel.inputModel.addCodeBlockToCodeSelect(this.blockRef, this.props.uuid);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -62,9 +67,21 @@ class Markdown extends React.Component<
|
||||
{ text: string; style?: any; extraClassName?: string; codeSelect?: boolean },
|
||||
{}
|
||||
> {
|
||||
CodeBlockRenderer(props: any, codeSelect: boolean, codeSelectIndex: number): any {
|
||||
curUuid: string;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.curUuid = uuidv4();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
CodeBlockRenderer(props: any, codeSelect: boolean, codeSelectIndex: number, curUuid: string): any {
|
||||
if (codeSelect) {
|
||||
return <CodeBlockMarkdown codeSelectSelectedIndex={codeSelectIndex}>{props.children}</CodeBlockMarkdown>;
|
||||
return (
|
||||
<CodeBlockMarkdown codeSelectSelectedIndex={codeSelectIndex} uuid={curUuid}>
|
||||
{props.children}
|
||||
</CodeBlockMarkdown>
|
||||
);
|
||||
} else {
|
||||
const clickHandler = (e: React.MouseEvent<HTMLElement>) => {
|
||||
let blockText = (e.target as HTMLElement).innerText;
|
||||
@@ -90,7 +107,7 @@ class Markdown extends React.Component<
|
||||
h5: (props) => HeaderRenderer(props, 5),
|
||||
h6: (props) => HeaderRenderer(props, 6),
|
||||
code: (props) => CodeRenderer(props),
|
||||
pre: (props) => this.CodeBlockRenderer(props, codeSelect, curCodeSelectIndex),
|
||||
pre: (props) => this.CodeBlockRenderer(props, codeSelect, curCodeSelectIndex, this.curUuid),
|
||||
};
|
||||
return (
|
||||
<div className={cn("markdown content", this.props.extraClassName)} style={this.props.style}>
|
||||
|
||||
@@ -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 { 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";
|
||||
@@ -26,7 +26,7 @@ class AboutModal extends React.Component<{}, {}> {
|
||||
|
||||
@boundMethod
|
||||
updateApp(): void {
|
||||
getApi().installAppUpdate();
|
||||
GlobalModel.getElectronApi().installAppUpdate();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
|
||||
@@ -312,7 +312,7 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
|
||||
endDecoration: (
|
||||
<InputDecoration>
|
||||
<Tooltip
|
||||
message={`(Required) The path to your ssh key file.`}
|
||||
message={`(Required) The path to your ssh private key file.`}
|
||||
icon={<i className="fa-sharp fa-regular fa-circle-question" />}
|
||||
>
|
||||
<i className="fa-sharp fa-regular fa-circle-question" />
|
||||
|
||||
@@ -338,7 +338,7 @@ class EditRemoteConnModal extends React.Component<{}, {}> {
|
||||
endDecoration: (
|
||||
<InputDecoration>
|
||||
<Tooltip
|
||||
message={`(Required) The path to your ssh key file.`}
|
||||
message={`(Required) The path to your ssh private key file.`}
|
||||
icon={<i className="fa-sharp fa-regular fa-circle-question" />}
|
||||
>
|
||||
<i className="fa-sharp fa-regular fa-circle-question" />
|
||||
|
||||
@@ -11,8 +11,9 @@ import { GlobalModel, GlobalCommandRunner, Screen } from "@/models";
|
||||
import { SettingsError, Modal, Dropdown, Tooltip } from "@/elements";
|
||||
import * as util from "@/util/util";
|
||||
import { Button } from "@/elements";
|
||||
import { ReactComponent as GlobeIcon } from "@/assets/icons/globe.svg";
|
||||
import { ReactComponent as StatusCircleIcon } from "@/assets/icons/statuscircle.svg";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { commandRtnHandler } from "@/util/util";
|
||||
import { getTermThemes } from "@/util/themeutil";
|
||||
import {
|
||||
TabColorSelector,
|
||||
TabIconSelector,
|
||||
@@ -153,11 +154,34 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeTermTheme(theme: string): void {
|
||||
const currTheme = GlobalModel.getTermTheme()[this.screenId];
|
||||
if (currTheme == theme) {
|
||||
return;
|
||||
}
|
||||
const prtn = GlobalCommandRunner.setScreenTermTheme(this.screenId, theme, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
selectRemote(cname: string): void {
|
||||
let prtn = GlobalCommandRunner.screenSetRemote(cname, true, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
render() {
|
||||
const screen = this.screen;
|
||||
if (screen == null) {
|
||||
return null;
|
||||
}
|
||||
let color: string = null;
|
||||
let icon: string = null;
|
||||
let index: number = 0;
|
||||
const curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
|
||||
const termThemes = getTermThemes(GlobalModel.termThemes);
|
||||
const currTermTheme = GlobalModel.getTermTheme()[this.screenId] ?? termThemes[0].label;
|
||||
|
||||
return (
|
||||
<Modal className="screen-settings-modal">
|
||||
<Modal.Header onClose={this.closeModal} title={`Tab Settings (${screen.name.get()})`} />
|
||||
@@ -186,6 +210,19 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
|
||||
<TabIconSelector screen={screen} errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
</div>
|
||||
<If condition={termThemes.length > 0}>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Terminal Theme</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="terminal-theme-dropdown"
|
||||
options={termThemes}
|
||||
defaultValue={currTermTheme}
|
||||
onChange={this.handleChangeTermTheme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label actions-label">
|
||||
<div>Actions</div>
|
||||
|
||||
@@ -6,8 +6,10 @@ import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { GlobalModel, GlobalCommandRunner, Session } from "@/models";
|
||||
import { Button } from "@/elements";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, Modal, Tooltip } from "@/elements";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, Modal, Tooltip, Button, Dropdown } from "@/elements";
|
||||
import { commandRtnHandler } from "@/util/util";
|
||||
import { getTermThemes } from "@/util/themeutil";
|
||||
import * as util from "@/util/util";
|
||||
|
||||
import "./sessionsettings.less";
|
||||
@@ -76,6 +78,16 @@ class SessionSettingsModal extends React.Component<{}, {}> {
|
||||
});
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeTermTheme(theme: string): void {
|
||||
const currTheme = GlobalModel.getTermTheme()[this.sessionId];
|
||||
if (currTheme == theme) {
|
||||
return;
|
||||
}
|
||||
const prtn = GlobalCommandRunner.setSessionTermTheme(this.sessionId, theme, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
dismissError(): void {
|
||||
mobx.action(() => {
|
||||
@@ -87,6 +99,9 @@ class SessionSettingsModal extends React.Component<{}, {}> {
|
||||
if (this.session == null) {
|
||||
return null;
|
||||
}
|
||||
const termThemes = getTermThemes(GlobalModel.termThemes);
|
||||
const currTermTheme = GlobalModel.getTermTheme()[this.sessionId] ?? termThemes[0].label;
|
||||
|
||||
return (
|
||||
<Modal className="session-settings-modal">
|
||||
<Modal.Header onClose={this.closeModal} title={`Workspace Settings (${this.session.name.get()})`} />
|
||||
@@ -104,6 +119,19 @@ class SessionSettingsModal extends React.Component<{}, {}> {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<If condition={termThemes.length > 0}>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Terminal Theme</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="terminal-theme-dropdown"
|
||||
options={termThemes}
|
||||
defaultValue={currTermTheme}
|
||||
onChange={this.handleChangeTermTheme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">
|
||||
<div>Archived</div>
|
||||
|
||||
@@ -357,6 +357,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wave-button {
|
||||
padding: 5px 5px;
|
||||
}
|
||||
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user