Compare commits

...

66 Commits

Author SHA1 Message Date
Red Adaya 80e1e3ce55 re-render terminal when theme is updated 2024-04-15 20:06:34 +08:00
Red Adaya 85886afeb4 fix validation 2024-04-15 20:03:07 +08:00
Red Adaya 567e5b662c reload terminal when styel block is unmounted and mounted 2024-04-15 19:52:09 +08:00
Red Adaya 6b5d2833eb use themes coming from backend 2024-04-15 13:30:12 +08:00
Red Adaya b10910d229 make methods cohesive 2024-04-15 10:02:51 +08:00
Red Adaya a7e6224862 use UpdatePacket for theme changes as well 2024-04-14 23:07:51 +08:00
Red Adaya 5476b45f22 save work 2024-04-14 22:42:00 +08:00
Red Adaya efcd8ad03a save work 2024-04-12 14:57:42 +08:00
Red Adaya 03acda07b5 cleanup 2024-04-11 22:34:26 +08:00
Red Adaya 32342629c8 trigger componentDidUpdate when switching tabs and sessions 2024-04-11 22:27:46 +08:00
Red Adaya d2ddf7bc58 working 2024-04-11 22:02:53 +08:00
Red Adaya 01fb217785 save work 2024-04-11 21:55:55 +08:00
Red Adaya e5d3db7631 save work 2024-04-11 17:35:32 +08:00
Red Adaya c24e99e9ff save work 2024-04-11 14:31:34 +08:00
Red Adaya 65cab8a59a add label to tab settings terminal theme dropdown 2024-04-10 14:58:53 +08:00
Red Adaya c737c9df57 add key and value validation 2024-04-10 10:14:33 +08:00
Red Adaya 17cc4769af fix issue where line in history view doesn't reflect the terminal theme 2024-04-10 09:21:13 +08:00
Red Adaya b4d04c3a57 merge main 2024-04-10 09:09:34 +08:00
Mike Sawka 73e5515e17 when the window gets focus, if our mainview is session (and no modals are open), refocus either the cmdinput or the cmd (#562) 2024-04-09 11:48:34 -07:00
Mike Sawka 6919dbfb5f force our exit trap to always run (for rtnstate commands) (#556)
* add command validation to shellapi.  mock out bash/zsh versions

* implement validate command fn bash and zsh

* test validate command

* change rtnstate commands to always end with a builtin, so we always get our exit trap to run

* simplify the rtnstate modification, don't add the 'wait' (as this is a different problem/feature)

* update schema
2024-04-09 11:33:23 -07:00
Evan Simkowitz 1f5309e097 undo text cursor on prompt area (#559) 2024-04-08 13:47:03 -07:00
Evan Simkowitz af7cc866d3 Make cmdinput prompt smaller, properly handle select events to take priority over onclick (#558) 2024-04-08 13:15:33 -07:00
Evan Simkowitz 37e56acf63 Cleanup unused variables in workspaceview (#557) 2024-04-08 10:17:26 -07:00
Mike Sawka 70088afdb5 daystr custom func (#555)
* working on daystr funcs

* daystr custom function
2024-04-05 22:42:22 -07:00
Evan Simkowitz 455790416d Clean up the input model's auxiliary view logic (#553)
* Clean up the input model's auxiliary view logic

* fix

* save work

* rename appconst

* fix keybindings

* remove debugs

* Add comments

* fix focus order

* givefocus whenever focus var is updated, don't update if nothign changes

* remove debug statements

* one more debug

* revert unnecessary newline

* remove cmdinput placeholder to allow for better window resizing
2024-04-05 17:39:27 -07:00
Cole Lashley eed234a131 added codeedit keybinding fix (#554) 2024-04-05 13:01:51 -07:00
Red J Adaya 5a6575a393 Copy button (#550)
* cop button

* cleanup

* fix wrong type

* updates to try to set the cmdinput position (as well as text).  fix button alignment, change checkmark to green (and extend), and remove the transition from parent component and move to copy (sawka)
2024-04-05 12:06:04 -07:00
Sylvie Crowe 84cea373a8 SSH Bugfixes Early April 2024 (#551)
* fix: add vix for missing known_hosts file

In a recent cleanup, I accidentally deleted this fix from before. This
adds it back.

* chore: clarify that the ssh should use private key
2024-04-05 10:54:12 -07:00
Mike Sawka 181e14f55c try to detect and return mimetype with stream file info (#552) 2024-04-05 10:52:04 -07:00
Evan Simkowitz 1c23701181 Clean up styling and focus behavior for cmdinput (#546)
* Clean up cmdinput

* Remove unused css styles, clicking on textarea will focus back to textarea without closing history

* cleanup logic for activating textarea

* actions buttons should always show, should properly disable inactive views

* clicking actions toggles the view

* remove titlebar spacer, clean up padding

* Make AIChat and HistoryInfo share a common layout

* fix ai chat scroll

* clean up formatting

* fix chat textarea resizing

* align prompt and input

* update infomsg to use auxview

* update comments

* fix widths and key error

* add todo

* adjust padding for input, remove debug

* Don't capture clicks on the prompt area
2024-04-04 19:29:43 -07:00
Cole Lashley 0fe767cdf3 Bugfixes for ai chat code select (#537)
* added uuid to code select to fix some render related bugs

* added input popup type, and fixed aichat computed condition

* fixed stash artifacts
2024-04-04 16:58:26 -07:00
Mike Sawka 097623ab51 have initial run-command return faster to the frontend for quicker updating (#549)
* have initial run-command return faster to the frontend for quicker updating.  cuts time from 70-80ms down to 20ms for an average command

* remove wlogs

* more logging cleanup

* fix focus for when start cmd returns an error
2024-04-04 15:08:45 -07:00
Red Adaya 5e4eb738b3 remove file from another branch 2024-04-04 22:02:07 +08:00
Red Adaya 936e9fa7b4 cleanup 2024-04-04 11:15:38 +08:00
Red Adaya af6486e8bb remove old implementation 2024-04-04 11:11:08 +08:00
Red Adaya 1038a08789 revert changes in tabs2.less 2024-04-04 11:09:32 +08:00
Red Adaya 7a377c4a2a loadThemeStyles doesn't have to be async 2024-04-04 11:08:04 +08:00
Red Adaya 8afa62fff2 cleanup 2024-04-04 11:04:34 +08:00
Red Adaya 97595a7718 working 2024-04-04 11:02:27 +08:00
Red Adaya e4d353b5c1 remove selector prop from StyleBlock 2024-04-04 07:34:38 +08:00
Red Adaya 2462c64793 selector should always be :root 2024-04-04 07:33:10 +08:00
Red Adaya b451ee1ef9 change prop from classname to selector 2024-04-04 07:21:26 +08:00
Red J Adaya 9de25e4869 truncate labels (#544) 2024-04-03 09:55:36 -07:00
Red Adaya 2c64668382 ability to inherit root styles 2024-04-03 22:22:05 +08:00
Red Adaya de21303fb1 root level 2024-04-03 22:12:48 +08:00
Red Adaya f6cdd53fd4 StyleBlock in elements dir 2024-04-03 20:50:47 +08:00
Red Adaya 840b69d100 reusable StyleBlock component 2024-04-03 20:48:13 +08:00
Red Adaya 683c66f386 save work 2024-04-03 16:37:15 +08:00
Evan Simkowitz a139633375 Send NumRunningCommands updates via the main bus, regardless of where other updates are being sent (#545) 2024-04-02 18:47:54 -07:00
Evan Simkowitz ccc63937b6 Support running ephemeral commands (#543)
* initial

* save work, starting to add backend types

* save work

* save work

* Add EphemeralWriteCloser

* Command pipes thru, triggers infinite loop

* save debugging

* fix bad merge

* save debug statements

* fixing spaghetti

* clean up code

* got cwd override working

* Add separate paths for stdout and stderr writers

* fix stdout/stderr

* env vars are now working

* revert waveshell changes

* Making EphemeralWriteCloser into a more generic BufferedPipe

* formatting

* comment

* delete unused package

* more naming changes

* add package comment

* add UsePty to EphemeralRunOpts

* document UsePty

* ensure only one downstream writer can read from the buffer

* store pointer to syncs

* remove inshellisense stuff for now

* remove debugs

* revert yarn

* remove unnecessary debugs in main-server

* more debugging removed

* revert tsconfig
2024-04-02 15:46:32 -07:00
Evan Simkowitz dc7fc2c823 Pull in linting fixes from preact branch (#538)
* Pull in linting fixes from preact branch

* more consts

* one more const

* Revert PureComponent

* revert preact artifavt

* revert another change
2024-04-02 12:30:17 -07:00
Red J Adaya ca5117cda0 Terminal theming (#485)
* init

* use setStyleVar

* backend implementation. scrope level terminal theming.

* only invoke this.applyTermTheme for keys that are updated. command runner for global term theme

* invoke applyTermTheme for global terminal themes as well

* fix nil error

* fix issue were theme can't be found

* fix issue where selected termtheme is not set as default value

* term theme switcher for session

* do not force reload after setting css vars

* fix issues. screenview terminal theme switcher

* remove debugging code

* move getTermThemes to util

* fix global theme reset

* fix workspace theme reset

* fix screenview terminal theme reset issue

* cleanup

* do not apply theme if theme hasn't changed

* do not apply theme if theme hasn't changed in workspace view

* cleanup

* cleanup

* force reload terminal

* fix inconsistency

* fix reset issue

* add a mobx reaction so that theming working when switching sessions

* workig reset

* simplify and cleanup

* refactor

* working global and session terminal theming

* add check

* perf improvement

* more perf improvements

* put reaction componentDidUpdate to make sure ref is already associated to the element

* cleanup

* fix issue where session theme is overriden by global theme on reload

* reduce flickering on reload

* more on reducing flickering on reload

* cleanup

* more cleanup

* fix file not found when no global theme is set

* screen level terminal theming

* update comment

* re-render terminal in history view. cleanup.

* cleanup

* merge main
2024-04-01 23:41:24 -07:00
Cole Lashley f41ac1d5e3 Added this.lastHistoryUpDown = false to cmdinput domain callback (#533)
* added more cmdinput-anykey behavior

* fixed lasthistoryupdown

* more bug fixes

* fixed history bugs hopefully fully fixed
2024-03-29 13:28:49 -07:00
Mike Sawka 0024f0f3e8 fix xterm.js versions and addon versions (#534)
* make xterm packages consistent

* add serializeAddon, fix varsUpdated
2024-03-29 10:52:18 -07:00
Mike Sawka 8a3d9628ba add addons-webgl, protected with clientsettings flag (#532) 2024-03-28 23:19:39 -07:00
Mike Sawka 7c2265f3e3 add debug command to see remote-instance state (#531) 2024-03-28 18:53:16 -07:00
Cole Lashley 4f6c2ee39b Other key bind bug fixes (#529) 2024-03-28 17:53:03 -07:00
Mike Sawka a1e4e807cc update statediff algorithm for wavesrv / remote instances (#530)
* remote statemap from waveshell server (diff against initial state)

* move ShellStatePtr from sstore to packet so it can be passed over the wire

* add finalstatebaseptr to cmddone

* much improved diff computation code on wavesrv side

* fix displayname -- now using hash

* add comments, change a couple msh.WriteToPtyBuffer calls to log.Printfs
2024-03-28 16:56:39 -07:00
Cole Lashley 5c85b2b786 Cmdinput keybinding bug fixes (#524)
* added domain callback for every layer

* added domain callback for every layer

* switched dump logs to false

* dropdown fixes

* moved uuid back to constructor
2024-03-28 13:19:20 -07:00
Cole Lashley 4879e90e91 fixed 2 more switches for baseurl (#527) 2024-03-28 13:14:05 -07:00
Evan Simkowitz a61310afc3 Remove datepicker from history until we have a proper date range selection (#526) 2024-03-28 12:21:58 -07:00
Cole Lashley 80c781bf4c added check for no baseurl (#525) 2024-03-28 12:20:58 -07:00
Mike Sawka 33400e65ff restore delete screen button (#522) 2024-03-27 22:42:37 -07:00
Sylvie Crowe 7b58949027 fix: this prevents possible loops while connecting (#521)
A recent change made it possible to get stuck in a loop when connecting
to a remote. This reverts the part of it that caused this while
retaining the other behavior. This makes it possible to add to blank
known_host files again. It also adds a printout to display when a
connection is complete.
2024-03-27 21:19:35 -07:00
Cole Lashley 3be96c949d Debug pane and lookup key (#518)
* added new util functions for logging keyutils and added debug pane

* clean up the formatting
2024-03-27 18:33:29 -07:00
Evan Simkowitz 052645e0df Remove debug statement (#520) 2024-03-27 17:03:01 -07:00
90 changed files with 3875 additions and 1862 deletions
+4 -2
View File
@@ -49,8 +49,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-web-links": "^0.9.0",
"xterm-addon-serialize": "^0.11.0",
"xterm-addon-webgl": "^0.16.0"
},
"devDependencies": {
"@babel/cli": "^7.17.10",
+1
View File
@@ -13,6 +13,7 @@
<link rel="stylesheet" href="dist-dev/waveterm.css" />
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/themequery.css" />
<link rel="stylesheet" href="public/themes/test.css" />
</head>
<body>
<div id="measure"></div>
+18 -16
View File
@@ -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
View File
@@ -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);
+28 -2
View File
@@ -20,8 +20,9 @@ import { MainSideBar } from "./sidebar/main";
import { RightSideBar } from "./sidebar/right";
import { DisconnectedModal, ClientStopModal } from "@/modals";
import { ModalsProvider } from "@/modals/provider";
import { Button } from "@/elements";
import { Button, TermStyleBlock } from "@/elements";
import { ErrorBoundary } from "@/common/error/errorboundary";
import { For } from "tsx-control-statements/components";
import cn from "classnames";
import "./app.less";
@@ -80,6 +81,21 @@ class App extends React.Component<{}, {}> {
rightSidebarModel.saveState(width, false);
}
getSelector(themeKey: string) {
const sessions = GlobalModel.getSessionNames();
const screens = GlobalModel.getScreenNames();
if (themeKey === "main") {
return ":root";
} else if (themeKey in screens) {
return `.main-content [data-screenid="${themeKey}"]`;
} else if (themeKey in sessions) {
return `.main-content [data-sessionid="${themeKey}"]`;
}
return null;
}
render() {
const remotesModel = GlobalModel.remotesModel;
const disconnected = !GlobalModel.ws.open.get() || !GlobalModel.waveSrvRunning.get();
@@ -124,9 +140,12 @@ class App extends React.Component<{}, {}> {
const rightSidebarCollapsed = GlobalModel.rightSidebarModel.getCollapsed();
const activeMainView = GlobalModel.activeMainView.get();
const lightDarkClass = GlobalModel.isDarkTheme.get() ? "is-dark" : "is-light";
const termTheme = GlobalModel.getTermTheme();
const themeKey = null;
return (
<div
key={"version-" + renderVersion}
key={`version- + ${renderVersion}`}
id="main"
className={cn(
"platform-" + platform,
@@ -151,6 +170,13 @@ class App extends React.Component<{}, {}> {
</div>
</If>
<div ref={this.mainContentRef} className="main-content">
<For index="idx" each="themeKey" of={Object.keys(termTheme)}>
<TermStyleBlock
key={themeKey}
themeName={termTheme[themeKey]}
selector={this.getSelector(themeKey)}
/>
</For>
<MainSideBar parentRef={this.mainContentRef} />
<ErrorBoundary>
<PluginsView />
+4
View File
@@ -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";
+28
View File
@@ -9,6 +9,7 @@ import { If } from "tsx-control-statements/components";
import { GlobalModel, GlobalCommandRunner, RemotesModel, getApi } 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";
@@ -73,6 +74,18 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
commandRtnHandler(prtn, this.errorMessage);
}
@boundMethod
handleChangeTermTheme(theme: string): void {
// For main terminal theme, the key is main, otherwise it's either
// sessionId or screenId.
const currTheme = GlobalModel.getTermTheme()["main"];
if (currTheme == theme) {
return;
}
const prtn = GlobalCommandRunner.setMainTermTheme(theme, false);
commandRtnHandler(prtn, this.errorMessage);
}
@boundMethod
handleChangeTelemetry(val: boolean): void {
let prtn: Promise<CommandRtnType> = null;
@@ -193,6 +206,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.termThemeOptions.get(), "Wave Default");
const currTermTheme = GlobalModel.getTermTheme()["main"] ?? termThemes[0].label;
return (
<MainView className="clientsettings-view" title="Client Settings" onClose={this.handleClose}>
@@ -233,6 +248,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>
+47
View File
@@ -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 -4
View File
@@ -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}
+7
View File
@@ -0,0 +1,7 @@
.copy-button {
padding: 5px 5px;
.fa-check {
color: var(--app-success-color);
}
}
+51
View File
@@ -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 };
-1
View File
@@ -312,7 +312,6 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
};
const handleArrowNavigation = (key: string, currentPart: string) => {
console.log("Arrow key pressed: ", key);
const currentIndex = formatParts.indexOf(currentPart);
let targetInput;
+1 -1
View File
@@ -5,7 +5,7 @@
width: 100%;
border: 2px solid var(--form-element-border-color);
border-radius: 6px;
line-height: 22px;
line-height: 20px;
background: var(--form-element-bg-color);
&.no-label {
+9 -1
View File
@@ -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
+2
View File
@@ -18,3 +18,5 @@ export { Toggle } from "./toggle";
export { Tooltip } from "./tooltip";
export { TabIcon } from "./tabicon";
export { DatePicker } from "./datepicker";
export { TermStyleBlock } from "./termstyleblock";
export { CopyButton } from "./copybutton";
+22 -5
View File
@@ -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}>
@@ -0,0 +1,95 @@
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import * as mobxReact from "mobx-react";
import { GlobalModel } from "@/models";
const VALID_CSS_VARIABLES = [
"--term-black",
"--term-red",
"--term-green",
"--term-yellow",
"--term-blue",
"--term-magenta",
"--term-cyan",
"--term-white",
"--term-bright-black",
"--term-bright-red",
"--term-bright-green",
"--term-bright-yellow",
"--term-bright-blue",
"--term-bright-magenta",
"--term-bright-cyan",
"--term-bright-white",
"--term-gray",
"--term-cmdtext",
"--term-foreground",
"--term-background",
"--term-selection-background",
"--term-cursor-accent",
];
@mobxReact.observer
class TermStyleBlock extends React.Component<{
themeName: string;
selector: string;
}> {
componentDidMount() {
GlobalModel.bumpTermRenderVersion();
}
componentWillUnmount() {
GlobalModel.bumpTermRenderVersion();
}
componentDidUpdate(prevProps) {
if (prevProps.themeName !== this.props.themeName || prevProps.selector !== this.props.selector) {
GlobalModel.bumpTermRenderVersion();
}
}
isValidCSSColor(color) {
const element = document.createElement("div");
element.style.color = color;
return element.style.color !== "";
}
camelCaseToKebabCase(str) {
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
}
getStyleRules() {
const { selector, themeName } = this.props;
const termThemeOptions = GlobalModel.getTermThemeOptions();
if (!termThemeOptions) {
return null;
}
const theme = termThemeOptions[themeName];
if (!theme) {
return null;
}
const styleProperties = Object.entries(theme)
.filter(([key, value]) => {
const cssVarName = `--term-${this.camelCaseToKebabCase(key)}`;
return VALID_CSS_VARIABLES.includes(cssVarName) && this.isValidCSSColor(value);
})
.map(([key, value]) => `--term-${key}: ${value};`)
.join(" ");
if (!styleProperties) {
return null;
}
return `${selector} { ${styleProperties} }`;
}
render() {
const styleRules = this.getStyleRules();
if (!styleRules) {
return null;
}
return <style>{styleRules}</style>;
}
}
export { TermStyleBlock };
+1 -1
View File
@@ -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" />
+1 -1
View File
@@ -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" />
+35 -2
View File
@@ -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,30 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
});
}
@boundMethod
handleChangeTermTheme(theme: string): void {
const currTheme = GlobalModel.getTermTheme()[this.screenId];
if (currTheme == theme) {
return;
}
GlobalModel.setTermThemeScope(this.screenId, `.main-content [data-screenid="${this.screenId}"]`);
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;
}
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 +206,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>

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