From 29acdc6eff2e28fa4a43d961b36268b58ac75aaa Mon Sep 17 00:00:00 2001 From: Mike Sawka Date: Wed, 6 Mar 2024 19:15:51 -0800 Subject: [PATCH] cmdinput actions (filter, ai, and history) (#395) * clientsettings fixups -- border top, padding, and remove theme dropdown for prod * new cmdinput actions, get the filter commands showing, titlebars for info history and info aichat, toggles for history --- public/themes/default.css | 1 + src/app/clientsettings/clientsettings.less | 3 +- src/app/clientsettings/clientsettings.tsx | 23 ++-- src/app/line/line.less | 2 +- src/app/workspace/cmdinput/aichat.tsx | 21 ++- src/app/workspace/cmdinput/cmdinput.less | 134 +++++++++++++++---- src/app/workspace/cmdinput/cmdinput.tsx | 37 +++-- src/app/workspace/cmdinput/historyinfo.tsx | 41 ++++-- src/app/workspace/cmdinput/textareainput.tsx | 23 +--- src/app/workspace/screen/screenview.less | 28 +++- src/app/workspace/screen/screenview.tsx | 12 +- src/models/input.ts | 25 ++++ 12 files changed, 257 insertions(+), 93 deletions(-) diff --git a/public/themes/default.css b/public/themes/default.css index 00321f1d..f76be6c3 100644 --- a/public/themes/default.css +++ b/public/themes/default.css @@ -46,6 +46,7 @@ /* global colors */ --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); diff --git a/src/app/clientsettings/clientsettings.less b/src/app/clientsettings/clientsettings.less index 44465e2d..d0dc658b 100644 --- a/src/app/clientsettings/clientsettings.less +++ b/src/app/clientsettings/clientsettings.less @@ -1,6 +1,7 @@ .clientsettings-view { .content { - padding: 0 18px 0 30px; + padding: 14px 18px 0 30px; + border-top: 1px solid var(--app-border-color); } .wave-dropdown { diff --git a/src/app/clientsettings/clientsettings.tsx b/src/app/clientsettings/clientsettings.tsx index 6e841946..495b56be 100644 --- a/src/app/clientsettings/clientsettings.tsx +++ b/src/app/clientsettings/clientsettings.tsx @@ -5,6 +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 } from "tsx-control-statements/components"; import cn from "classnames"; import { GlobalModel, GlobalCommandRunner, RemotesModel, getApi } from "@/models"; import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "@/common/elements"; @@ -191,17 +192,19 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove /> -
-
Theme
-
- + +
+
Theme
+
+ +
-
+
Client ID
{cdata.clientid}
diff --git a/src/app/line/line.less b/src/app/line/line.less index 3598d5a0..d5e24691 100644 --- a/src/app/line/line.less +++ b/src/app/line/line.less @@ -171,7 +171,7 @@ } &:hover { - color: var(--line-actions-active-color); + color: var(--app-icon-hover-color); } padding: 4px 4px; diff --git a/src/app/workspace/cmdinput/aichat.tsx b/src/app/workspace/cmdinput/aichat.tsx index dd2144b6..b3d69b43 100644 --- a/src/app/workspace/cmdinput/aichat.tsx +++ b/src/app/workspace/cmdinput/aichat.tsx @@ -152,8 +152,8 @@ class AIChat extends React.Component<{}, {}> { innerHTML = (
- -
ChatGPT
+ +
AI Assistant
@@ -195,6 +195,21 @@ class AIChat extends React.Component<{}, {}> { return (
+
+
+ +
+
Wave AI
+
+
inputModel.closeAIAssistantChat(true)} + > + +
+
+
{this.renderChatWindow()}
); diff --git a/src/app/workspace/cmdinput/cmdinput.less b/src/app/workspace/cmdinput/cmdinput.less index 0f0778c7..e99f3ad5 100644 --- a/src/app/workspace/cmdinput/cmdinput.less +++ b/src/app/workspace/cmdinput/cmdinput.less @@ -13,6 +13,99 @@ &.active { } + &.has-info, + &.has-aichat, + &.has-history { + .cmdinput-actions { + display: none; + } + } + + .titlebar-spacer { + height: 31px; + } + + .cmdinput-actions { + position: absolute; + border-radius: 4px; + padding-left: 4px; + padding-right: 4px; + font-size: calc(var(--termfontsize) + 2px); + line-height: 1.2; + + // we want to align to 2nd line of meta. that's 2xPad + 1xLineHightSm + // height of actions is 1xLineHeight + 8px (2x2px padding on icons) + top: calc(var(--termpad)); + right: calc(var(--termpad) * 2); + + display: flex; + flex-direction: row; + align-items: center; + + .cmdinput-icon { + color: var(--app-icon-hover-color); + opacity: 0.5; + + &.running-cmds i { + color: var(--app-warning-color); + } + + &.active { + opacity: 1; + } + + &:hover { + opacity: 1; + } + + padding: 4px 6px; + cursor: pointer; + } + + .line-icon + .line-icon:not(.line-icon-shrink-left) { + margin-left: 3px; + } + } + + .cmdinput-titlebar { + position: absolute; + z-index: 22; + top: 0; + left: 0; + background-color: var(--app-panel-bg-color); + color: var(--term-blue); + padding: 6px 10px 6px 10px; + display: flex; + flex-direction: row; + width: 100%; + overflow-x: auto; + border-bottom: 1px solid var(--app-border-color); + font: var(--base-font); + + .title-icon { + margin-right: 10px; + } + + .title-string { + font-weight: bold; + } + + .close-button { + cursor: pointer; + i { + color: var(--app-icon-color); + + &:hover { + color: var(--app-icon-hover-color); + } + } + } + + .spacer { + flex: 0 0 10px; + } + } + &.has-info { padding-top: var(--termpad); } @@ -23,23 +116,20 @@ left: 4px; } - &.has-history { + &.has-history, + &.has-aichat { padding-top: var(--termpad); - height: max(300px, 40%); + height: max(300px, 70%); } &.has-remote { max-height: max(300px, 70%); } - &.has-aichat { - max-height: max(300px, 70%); - } - .remote-status-warning { display: flex; flex-direction: row; - color: var(--term-yellow); + color: var(--app-warning-color); align-items: center; .button { @@ -300,19 +390,6 @@ flex-shrink: 1; .history-title { - position: absolute; - z-index: 102; - top: 5px; - left: 0; - background-color: var(--app-bg-color); - color: var(--term-blue); - padding-bottom: 4px; - display: flex; - flex-direction: row; - width: 100%; - overflow-x: auto; - border-bottom: 1px solid var(--app-border-color); - div:first-child { margin-left: var(--termpad); } @@ -321,22 +398,21 @@ white-space: nowrap; } + .history-clickable-opt { + cursor: pointer; + + &:hover { + color: var(--app-text-primary-color); + } + } + .history-clickable-opt { white-space: nowrap; cursor: pointer; } - - .grow-spacer { - flex: 1 0 10px; - } - - .spacer { - flex: 0 0 10px; - } } .history-items { - margin-top: 24px; color: var(--app-text-color); padding-bottom: 6px; diff --git a/src/app/workspace/cmdinput/cmdinput.tsx b/src/app/workspace/cmdinput/cmdinput.tsx index 71fff6ce..29c81950 100644 --- a/src/app/workspace/cmdinput/cmdinput.tsx +++ b/src/app/workspace/cmdinput/cmdinput.tsx @@ -70,7 +70,7 @@ class CmdInput extends React.Component<{}, {}> { } @boundMethod - clickAIHint(e: any): void { + clickAIAction(e: any): void { e.preventDefault(); e.stopPropagation(); let inputModel = GlobalModel.inputModel; @@ -78,7 +78,7 @@ class CmdInput extends React.Component<{}, {}> { } @boundMethod - clickHistoryHint(e: any): void { + clickHistoryAction(e: any): void { e.preventDefault(); e.stopPropagation(); @@ -131,6 +131,7 @@ class CmdInput extends React.Component<{}, {}> { const inputMode: string = inputModel.inputMode.get(); const textAreaInputKey = screen == null ? "null" : screen.screenId; const win = GlobalModel.getScreenLinesById(screen.screenId); + const filterRunning = screen.filterRunning.get(); let numRunningLines = 0; if (win != null) { numRunningLines = mobx.computed(() => win.getRunningCmdLines().length).get(); @@ -142,9 +143,33 @@ class CmdInput extends React.Component<{}, {}> { "cmd-input", { "has-info": infoShow }, { "has-aichat": aiChatShow }, + { "has-history": historyShow }, { active: focusVal } )} > +
+ 0}> +
this.toggleFilter(screen)} + > + {numRunningLines} +
+
+
+ +
+
+ +
+
@@ -188,14 +213,6 @@ class CmdInput extends React.Component<{}, {}> {
- 0}> -
this.toggleFilter(screen)} className="cmd-input-filter"> - {numRunningLines} -
- -
-
-
{ }, 3000); } + @boundMethod + handleClickType() { + let inputModel = GlobalModel.inputModel; + inputModel.toggleHistoryType(); + } + + @boundMethod + handleClickRemote() { + let inputModel = GlobalModel.inputModel; + inputModel.toggleRemoteType(); + } + render() { let inputModel = GlobalModel.inputModel; let idx: number = 0; @@ -207,19 +219,27 @@ class HistoryInfo extends React.Component<{}, {}> { } return (
-
-
history
+
+
+ +
+
History
-
[for {opts.queryType} ⌘S]
-
-
[containing '{opts.queryStr}']
-
-
[{opts.limitRemote ? "this" : "any"} remote ⌘R]
-
-
- (ESC) +
+ [for {opts.queryType} ⌘S]
+
+ [containing '{opts.queryStr}'] +
+
+
+ [{opts.limitRemote ? "this" : "any"} remote ⌘R] +
+
+
+ +
{ { "show-sessions": opts.queryType == "global" } )} > +
[no history] 0}> diff --git a/src/app/workspace/cmdinput/textareainput.tsx b/src/app/workspace/cmdinput/textareainput.tsx index c99dbc89..dcaa30c4 100644 --- a/src/app/workspace/cmdinput/textareainput.tsx +++ b/src/app/workspace/cmdinput/textareainput.tsx @@ -362,29 +362,14 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: () } if (checkKeyPressed(waveEvent, "Cmd:r") || checkKeyPressed(waveEvent, "Ctrl:r")) { e.preventDefault(); - let opts = mobx.toJS(inputModel.historyQueryOpts.get()); - if (opts.limitRemote) { - opts.limitRemote = false; - opts.limitRemoteInstance = false; - } else { - opts.limitRemote = true; - opts.limitRemoteInstance = true; - } - inputModel.setHistoryQueryOpts(opts); + e.stopPropagation(); + inputModel.toggleRemoteType(); return; } if (checkKeyPressed(waveEvent, "Cmd:s") || checkKeyPressed(waveEvent, "Ctrl:s")) { e.preventDefault(); - let opts = mobx.toJS(inputModel.historyQueryOpts.get()); - let htype = opts.queryType; - if (htype == "screen") { - htype = "session"; - } else if (htype == "session") { - htype = "global"; - } else { - htype = "screen"; - } - inputModel.setHistoryType(htype); + e.stopPropagation(); + inputModel.toggleHistoryType(); return; } if (checkKeyPressed(waveEvent, "Tab")) { diff --git a/src/app/workspace/screen/screenview.less b/src/app/workspace/screen/screenview.less index ddeeb02d..c3c1e399 100644 --- a/src/app/workspace/screen/screenview.less +++ b/src/app/workspace/screen/screenview.less @@ -181,8 +181,32 @@ } .filter-running { - margin: auto 1rem 0 1rem; - align-self: center; + position: relative; + display: flex; + flex-direction: row; + width: 100%; + border-top: 1px solid var(--app-border-color); + padding: calc(var(--termpad) + 2px) var(--termpad) calc(var(--termpad) + 2px) var(--termpad); + align-items: center; + justify-content: center; + + .filter-content { + cursor: pointer; + padding: 2px; + color: var(--app-text-primary-color); + z-index: 2; + } + + .filter-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: var(--app-accent-bg-color); + z-index: 1; + pointer-events: none; + } } } diff --git a/src/app/workspace/screen/screenview.tsx b/src/app/workspace/screen/screenview.tsx index 812bd88e..3d1c4924 100644 --- a/src/app/workspace/screen/screenview.tsx +++ b/src/app/workspace/screen/screenview.tsx @@ -763,15 +763,11 @@ class ScreenWindowView extends React.Component<{ session: Session; screen: Scree
- + +
diff --git a/src/models/input.ts b/src/models/input.ts index 96ea01c6..ef3e4896 100644 --- a/src/models/input.ts +++ b/src/models/input.ts @@ -88,6 +88,31 @@ class InputModel { })(); } + toggleHistoryType(): void { + let opts = mobx.toJS(this.historyQueryOpts.get()); + let htype = opts.queryType; + if (htype == "screen") { + htype = "session"; + } else if (htype == "session") { + htype = "global"; + } else { + htype = "screen"; + } + this.setHistoryType(htype); + } + + toggleRemoteType(): void { + let opts = mobx.toJS(this.historyQueryOpts.get()); + if (opts.limitRemote) { + opts.limitRemote = false; + opts.limitRemoteInstance = false; + } else { + opts.limitRemote = true; + opts.limitRemoteInstance = true; + } + this.setHistoryQueryOpts(opts); + } + onInputFocus(isFocused: boolean): void { mobx.action(() => { if (isFocused) {