mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
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
This commit is contained in:
parent
0fe767cdf3
commit
1c23701181
@ -42,16 +42,19 @@
|
|||||||
--app-bg-color: black;
|
--app-bg-color: black;
|
||||||
--app-accent-color: rgb(88, 193, 66);
|
--app-accent-color: rgb(88, 193, 66);
|
||||||
--app-accent-bg-color: rgba(88, 193, 66, 0.25);
|
--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-color: rgb(211, 215, 207);
|
||||||
--app-text-primary-color: rgb(255, 255, 255);
|
--app-text-primary-color: rgb(255, 255, 255);
|
||||||
--app-text-secondary-color: rgb(195, 200, 194);
|
--app-text-secondary-color: rgb(195, 200, 194);
|
||||||
--app-text-disabled-color: rgb(173, 173, 173);
|
--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-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: rgba(21, 23, 21, 1);
|
||||||
--app-panel-bg-color-dev: rgb(21, 23, 48);
|
--app-panel-bg-color-dev: rgb(21, 23, 48);
|
||||||
--app-icon-color: rgb(139, 145, 138);
|
--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);
|
--app-selected-mask-color: rgba(255, 255, 255, 0.06);
|
||||||
|
|
||||||
/* global status colors */
|
/* global status colors */
|
||||||
@ -122,18 +125,18 @@
|
|||||||
|
|
||||||
/* line colors */
|
/* line colors */
|
||||||
--line-sidebar-message-color: rgb(196, 160, 0);
|
--line-sidebar-message-color: rgb(196, 160, 0);
|
||||||
--line-background: rgba(21, 23, 21, 1);
|
--line-background: var(--app-panel-bg-color);
|
||||||
--line-avatar-color: #eceeec;
|
--line-avatar-color: rgb(236, 238, 236);
|
||||||
--line-text-color: rgb(211, 215, 207);
|
--line-text-color: rgb(211, 215, 207);
|
||||||
--line-svg-fill-color: rgb(150, 152, 150);
|
--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-separator-color: rgb(126, 126, 126);
|
||||||
--line-error-color: var(--app-error-color);
|
--line-error-color: var(--app-error-color);
|
||||||
--line-warning-color: var(--app-warning-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-active-border-color: var(--app-accent-color);
|
||||||
--line-selected-bg-color: rgba(255, 255, 255, 0.05);
|
--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-border-color: rgba(204, 0, 0, 0.8);
|
||||||
--line-selected-error-bg-color: rgb(19, 4, 3);
|
--line-selected-error-bg-color: rgb(19, 4, 3);
|
||||||
--line-error-bg-color: rgba(200, 0, 0, 0.1);
|
--line-error-bg-color: rgba(200, 0, 0, 0.1);
|
||||||
@ -152,22 +155,21 @@
|
|||||||
/* table colors */
|
/* table colors */
|
||||||
--table-border-color: rgba(241, 246, 243, 0.15);
|
--table-border-color: rgba(241, 246, 243, 0.15);
|
||||||
--table-thead-border-top-color: rgba(250, 250, 250, 0.1);
|
--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-thead-bg-color: rgba(250, 250, 250, 0.02);
|
||||||
--table-tr-border-bottom-color: rgba(241, 246, 243, 0.15);
|
--table-tr-border-bottom-color: rgba(241, 246, 243, 0.15);
|
||||||
--table-tr-hover-bg-color: rgba(255, 255, 255, 0.06);
|
--table-tr-hover-bg-color: rgba(255, 255, 255, 0.06);
|
||||||
--table-tr-selected-bg-color: #222;
|
--table-tr-selected-bg-color: rgb(34, 34, 34);
|
||||||
--table-tr-selected-hover-bg-color: #333;
|
--table-tr-selected-hover-bg-color: var(--app-maincontent-bg-color);
|
||||||
|
|
||||||
/* cmdinput colors */
|
/* cmdinput colors */
|
||||||
--cmdinput-textarea-bg-color: #171717;
|
--cmdinput-bg-color: var(--app-text-bg-color);
|
||||||
--cmdinput-text-error-color: var(--term-red);
|
--cmdinput-text-error-color: var(--term-red);
|
||||||
--cmdinput-history-item-error-color: var(--term-bright-red);
|
--cmdinput-history-item-error-color: var(--term-bright-red);
|
||||||
--cmdinput-history-item-selected-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-button-bg-color: var(--tab-green);
|
||||||
--cmdinput-comment-button-bg-color: rgb(57, 113, 255);
|
--cmdinput-disabled-bg-color: var(--app-text-bg-disabled-color);
|
||||||
--cmdinput-disabled-icon-color: rgb(76, 81, 75, 1);
|
--cmdinput-history-bg-color: var(--app-bg-color);
|
||||||
--cmdinput-history-bg-color: rgb(21, 23, 21, 1);
|
|
||||||
|
|
||||||
/* screen view color */
|
/* screen view color */
|
||||||
--screen-view-text-caption-color: rgb(139, 145, 138);
|
--screen-view-text-caption-color: rgb(139, 145, 138);
|
||||||
|
@ -5,20 +5,20 @@
|
|||||||
@import url("./term-light.css");
|
@import url("./term-light.css");
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--app-bg-color: #fefefe;
|
--app-bg-color: rgb(254, 254, 254);
|
||||||
--app-accent-color: rgb(75, 166, 57);
|
--app-accent-color: rgb(75, 166, 57);
|
||||||
--app-accent-bg-color: rgba(75, 166, 57, 0.2);
|
--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-primary-color: rgb(0, 0, 0, 0.9);
|
||||||
--app-text-secondary-color: rgb(0, 0, 0, 0.7);
|
--app-text-secondary-color: rgb(0, 0, 0, 0.7);
|
||||||
--app-border-color: rgb(139 145 138);
|
--app-border-color: rgb(139 145 138);
|
||||||
--app-panel-bg-color: #e0e0e0;
|
--app-panel-bg-color: rgb(224, 224, 224);
|
||||||
--app-panel-bg-color-dev: #e0e0e0;
|
--app-panel-bg-color-dev: rgb(224, 224, 224);
|
||||||
--app-icon-color: rgb(80, 80, 80);
|
--app-icon-color: rgb(110, 110, 110);
|
||||||
--app-icon-hover-color: rgb(100, 100, 100);
|
--app-icon-hover-color: rgb(80, 80, 80);
|
||||||
--app-selected-mask-color: rgba(0, 0, 0, 0.06);
|
--app-selected-mask-color: rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
--input-bg-color: #eeeeee;
|
--input-bg-color: rgb(238, 238, 238);
|
||||||
|
|
||||||
/* tab color */
|
/* tab color */
|
||||||
--tab-white: rgb(0, 0, 0, 0.6);
|
--tab-white: rgb(0, 0, 0, 0.6);
|
||||||
@ -30,8 +30,8 @@
|
|||||||
--table-thead-bg-color: rgba(250, 250, 250, 0.15);
|
--table-thead-bg-color: rgba(250, 250, 250, 0.15);
|
||||||
--table-tr-border-bottom-color: rgba(0, 0, 0, 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-hover-bg-color: rgba(0, 0, 0, 0.15);
|
||||||
--table-tr-selected-bg-color: #dddddd;
|
--table-tr-selected-bg-color: rgb(221, 221, 221);
|
||||||
--table-tr-selected-hover-bg-color: #cccccc;
|
--table-tr-selected-hover-bg-color: rgb(204, 204, 204);
|
||||||
|
|
||||||
/* form colors */
|
/* form colors */
|
||||||
--form-element-border-color: rgba(0, 0, 0, 0.3);
|
--form-element-border-color: rgba(0, 0, 0, 0.3);
|
||||||
@ -41,8 +41,8 @@
|
|||||||
--form-element-label-color: rgba(0, 0, 0, 0.6);
|
--form-element-label-color: rgba(0, 0, 0, 0.6);
|
||||||
--form-element-secondary-color: rgba(0, 0, 0, 0.09);
|
--form-element-secondary-color: rgba(0, 0, 0, 0.09);
|
||||||
--form-element-icon-color: rgb(0, 0, 0, 0.6);
|
--form-element-icon-color: rgb(0, 0, 0, 0.6);
|
||||||
--form-element-disabled-text-color: #b7b7b7;
|
--form-element-disabled-text-color: rgb(183, 183, 183);
|
||||||
--form-element-placeholder-color: #b7b7b7;
|
--form-element-placeholder-color: rgb(183, 183, 183);
|
||||||
|
|
||||||
--markdown-bg-color: rgb(0, 0, 0, 0.1);
|
--markdown-bg-color: rgb(0, 0, 0, 0.1);
|
||||||
|
|
||||||
@ -50,8 +50,6 @@
|
|||||||
--modal-header-bottom-border-color: rgba(0, 0, 0, 0.3);
|
--modal-header-bottom-border-color: rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
/* cmd input */
|
/* cmd input */
|
||||||
--cmdinput-textarea-bg-color: rgba(0, 0, 0, 0.1);
|
|
||||||
--cmdinput-textarea-border-color: var(--form-element-border-color);
|
|
||||||
|
|
||||||
/* scroll colors */
|
/* scroll colors */
|
||||||
--scrollbar-background-color: var(--app-bg-color);
|
--scrollbar-background-color: var(--app-bg-color);
|
||||||
@ -64,15 +62,15 @@
|
|||||||
--line-actions-inactive-color: rgba(0, 0, 0, 0.3);
|
--line-actions-inactive-color: rgba(0, 0, 0, 0.3);
|
||||||
--line-actions-active-color: rgba(0, 0, 0, 1);
|
--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);
|
--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-cell-other-text-color: rgba(0, 0, 0, 0.3);
|
||||||
--datepicker-header-fade-color: rgba(0, 0, 0, 0.4);
|
--datepicker-header-fade-color: rgba(0, 0, 0, 0.4);
|
||||||
--datepicker-year-header-bg-color: #f5f5f5;
|
--datepicker-year-header-bg-color: rgb(245, 245, 245);
|
||||||
--datepicker-year-header-border-color: #dcdcdc;
|
--datepicker-year-header-border-color: rgb(220, 220, 220);
|
||||||
|
|
||||||
/* toggle colors */
|
/* toggle colors */
|
||||||
--toggle-thumb-color: var(--app-bg-color);
|
--toggle-thumb-color: var(--app-bg-color);
|
||||||
|
@ -106,11 +106,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.top-border {
|
|
||||||
border-top: 1px solid #777;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .meta .termopts {
|
&:hover .meta .termopts {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ class MainSideBar extends React.Component<MainSideBarProps, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let mainView = GlobalModel.activeMainView.get();
|
const mainView = GlobalModel.activeMainView.get();
|
||||||
const historyActive = mainView == "history";
|
const historyActive = mainView == "history";
|
||||||
const connectionsActive = mainView == "connections";
|
const connectionsActive = mainView == "connections";
|
||||||
const settingsActive = mainView == "clientsettings";
|
const settingsActive = mainView == "clientsettings";
|
||||||
|
70
src/app/workspace/cmdinput/aichat.less
Normal file
70
src/app/workspace/cmdinput/aichat.less
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
.cmd-aichat {
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
.auxview-content {
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.chat-window {
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input {
|
||||||
|
padding: 0.5em 0.5em 0.5em 0.5em;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
|
||||||
|
.chat-textarea {
|
||||||
|
color: var(--app-text-primary-color);
|
||||||
|
background-color: var(--cmdinput-textarea-bg);
|
||||||
|
resize: none;
|
||||||
|
width: 100%;
|
||||||
|
border: transparent;
|
||||||
|
outline: none;
|
||||||
|
overflow: auto;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
font-family: var(--termfontfamily);
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: var(--termlineheight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg {
|
||||||
|
margin-top: calc(var(--termpad) * 2);
|
||||||
|
margin-bottom: calc(var(--termpad) * 2);
|
||||||
|
|
||||||
|
.chat-msg-header {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-username {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg-assistant {
|
||||||
|
color: var(--app-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg-user {
|
||||||
|
.msg-text {
|
||||||
|
font-family: var(--markdown-font);
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg-error {
|
||||||
|
color: var(--cmdinput-text-error);
|
||||||
|
font-family: var(--markdown-font);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,18 +5,18 @@ import * as React from "react";
|
|||||||
import * as mobxReact from "mobx-react";
|
import * as mobxReact from "mobx-react";
|
||||||
import * as mobx from "mobx";
|
import * as mobx from "mobx";
|
||||||
import { GlobalModel } from "@/models";
|
import { GlobalModel } from "@/models";
|
||||||
import { isBlank } from "@/util/util";
|
|
||||||
import { boundMethod } from "autobind-decorator";
|
import { boundMethod } from "autobind-decorator";
|
||||||
import cn from "classnames";
|
|
||||||
import { If, For } from "tsx-control-statements/components";
|
import { If, For } from "tsx-control-statements/components";
|
||||||
import { Markdown } from "@/elements";
|
import { Markdown } from "@/elements";
|
||||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
|
import { AuxiliaryCmdView } from "./auxview";
|
||||||
|
|
||||||
|
import "./aichat.less";
|
||||||
|
|
||||||
class AIChatKeybindings extends React.Component<{ AIChatObject: AIChat }, {}> {
|
class AIChatKeybindings extends React.Component<{ AIChatObject: AIChat }, {}> {
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
let AIChatObject = this.props.AIChatObject;
|
const AIChatObject = this.props.AIChatObject;
|
||||||
let keybindManager = GlobalModel.keybindManager;
|
const keybindManager = GlobalModel.keybindManager;
|
||||||
let inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
|
|
||||||
keybindManager.registerKeybinding("pane", "aichat", "generic:confirm", (waveEvent) => {
|
keybindManager.registerKeybinding("pane", "aichat", "generic:confirm", (waveEvent) => {
|
||||||
AIChatObject.onEnterKeyPressed();
|
AIChatObject.onEnterKeyPressed();
|
||||||
@ -54,10 +54,10 @@ class AIChatKeybindings extends React.Component<{ AIChatObject: AIChat }, {}> {
|
|||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class AIChat extends React.Component<{}, {}> {
|
class AIChat extends React.Component<{}, {}> {
|
||||||
chatListKeyCount: number = 0;
|
chatListKeyCount: number = 0;
|
||||||
textAreaNumLines: mobx.IObservableValue<number> = mobx.observable.box(1, { name: "textAreaNumLines" });
|
|
||||||
chatWindowScrollRef: React.RefObject<HTMLDivElement>;
|
chatWindowScrollRef: React.RefObject<HTMLDivElement>;
|
||||||
textAreaRef: React.RefObject<HTMLTextAreaElement>;
|
textAreaRef: React.RefObject<HTMLTextAreaElement>;
|
||||||
isFocused: OV<boolean>;
|
isFocused: OV<boolean>;
|
||||||
|
termFontSize: number = 14;
|
||||||
|
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -69,8 +69,7 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let model = GlobalModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
let inputModel = model.inputModel;
|
|
||||||
if (this.chatWindowScrollRef != null && this.chatWindowScrollRef.current != null) {
|
if (this.chatWindowScrollRef != null && this.chatWindowScrollRef.current != null) {
|
||||||
this.chatWindowScrollRef.current.scrollTop = this.chatWindowScrollRef.current.scrollHeight;
|
this.chatWindowScrollRef.current.scrollTop = this.chatWindowScrollRef.current.scrollHeight;
|
||||||
}
|
}
|
||||||
@ -79,6 +78,7 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
inputModel.setCmdInfoChatRefs(this.textAreaRef, this.chatWindowScrollRef);
|
inputModel.setCmdInfoChatRefs(this.textAreaRef, this.chatWindowScrollRef);
|
||||||
}
|
}
|
||||||
this.requestChatUpdate();
|
this.requestChatUpdate();
|
||||||
|
this.onTextAreaChange(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
@ -92,20 +92,18 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submitChatMessage(messageStr: string) {
|
submitChatMessage(messageStr: string) {
|
||||||
let model = GlobalModel;
|
const curLine = GlobalModel.inputModel.getCurLine();
|
||||||
let inputModel = model.inputModel;
|
const prtn = GlobalModel.submitChatInfoCommand(messageStr, curLine, false);
|
||||||
let curLine = inputModel.getCurLine();
|
|
||||||
let prtn = GlobalModel.submitChatInfoCommand(messageStr, curLine, false);
|
|
||||||
prtn.then((rtn) => {
|
prtn.then((rtn) => {
|
||||||
if (!rtn.success) {
|
if (!rtn.success) {
|
||||||
console.log("submit chat command error: " + rtn.error);
|
console.log("submit chat command error: " + rtn.error);
|
||||||
}
|
}
|
||||||
}).catch((error) => {});
|
}).catch((_) => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
getLinePos(elem: any): { numLines: number; linePos: number } {
|
getLinePos(elem: any): { numLines: number; linePos: number } {
|
||||||
let numLines = elem.value.split("\n").length;
|
const numLines = elem.value.split("\n").length;
|
||||||
let linePos = elem.value.substr(0, elem.selectionStart).split("\n").length;
|
const linePos = elem.value.substr(0, elem.selectionStart).split("\n").length;
|
||||||
return { numLines, linePos };
|
return { numLines, linePos };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,22 +119,32 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust the height of the textarea to fit the text
|
||||||
onTextAreaChange(e: any) {
|
onTextAreaChange(e: any) {
|
||||||
// set height of textarea based on number of newlines
|
// Calculate the bounding height of the text area
|
||||||
mobx.action(() => {
|
const textAreaMaxLines = 4;
|
||||||
this.textAreaNumLines.set(e.target.value.split(/\n/).length);
|
const textAreaLineHeight = this.termFontSize * 1.5;
|
||||||
|
const textAreaMinHeight = textAreaLineHeight;
|
||||||
|
const textAreaMaxHeight = textAreaLineHeight * textAreaMaxLines;
|
||||||
|
|
||||||
|
// Get the height of the wrapped text area content. Courtesy of https://stackoverflow.com/questions/995168/textarea-to-resize-based-on-content-length
|
||||||
|
this.textAreaRef.current.style.height = "1px";
|
||||||
|
const scrollHeight: number = this.textAreaRef.current.scrollHeight;
|
||||||
|
|
||||||
|
// Set the new height of the text area, bounded by the min and max height.
|
||||||
|
const newHeight = Math.min(Math.max(scrollHeight, textAreaMinHeight), textAreaMaxHeight);
|
||||||
|
this.textAreaRef.current.style.height = newHeight + "px";
|
||||||
GlobalModel.inputModel.codeSelectDeselectAll();
|
GlobalModel.inputModel.codeSelectDeselectAll();
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnterKeyPressed() {
|
onEnterKeyPressed() {
|
||||||
let inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
let currentRef = this.textAreaRef.current;
|
const currentRef = this.textAreaRef.current;
|
||||||
if (currentRef == null) {
|
if (currentRef == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (inputModel.getCodeSelectSelectedIndex() == -1) {
|
if (inputModel.getCodeSelectSelectedIndex() == -1) {
|
||||||
let messageStr = currentRef.value;
|
const messageStr = currentRef.value;
|
||||||
this.submitChatMessage(messageStr);
|
this.submitChatMessage(messageStr);
|
||||||
currentRef.value = "";
|
currentRef.value = "";
|
||||||
} else {
|
} else {
|
||||||
@ -145,7 +153,7 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onExpandInputPressed() {
|
onExpandInputPressed() {
|
||||||
let currentRef = this.textAreaRef.current;
|
const currentRef = this.textAreaRef.current;
|
||||||
if (currentRef == null) {
|
if (currentRef == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -154,7 +162,7 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onArrowUpPressed(): boolean {
|
onArrowUpPressed(): boolean {
|
||||||
let currentRef = this.textAreaRef.current;
|
const currentRef = this.textAreaRef.current;
|
||||||
if (currentRef == null) {
|
if (currentRef == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -168,8 +176,8 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onArrowDownPressed(): boolean {
|
onArrowDownPressed(): boolean {
|
||||||
let currentRef = this.textAreaRef.current;
|
const currentRef = this.textAreaRef.current;
|
||||||
let inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
if (currentRef == null) {
|
if (currentRef == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -190,10 +198,10 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderChatMessage(chatItem: OpenAICmdInfoChatMessageType): any {
|
renderChatMessage(chatItem: OpenAICmdInfoChatMessageType): any {
|
||||||
let curKey = "chatmsg-" + this.chatListKeyCount;
|
const curKey = "chatmsg-" + this.chatListKeyCount;
|
||||||
this.chatListKeyCount++;
|
this.chatListKeyCount++;
|
||||||
let senderClassName = chatItem.isassistantresponse ? "chat-msg-assistant" : "chat-msg-user";
|
const senderClassName = chatItem.isassistantresponse ? "chat-msg-assistant" : "chat-msg-user";
|
||||||
let msgClassName = "chat-msg " + senderClassName;
|
const msgClassName = "chat-msg " + senderClassName;
|
||||||
let innerHTML: React.JSX.Element = (
|
let innerHTML: React.JSX.Element = (
|
||||||
<span>
|
<span>
|
||||||
<div className="chat-msg-header">
|
<div className="chat-msg-header">
|
||||||
@ -226,31 +234,10 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChatWindow(): any {
|
|
||||||
let model = GlobalModel;
|
|
||||||
let inputModel = model.inputModel;
|
|
||||||
let chatMessageItems = inputModel.AICmdInfoChatItems.slice();
|
|
||||||
let chitem: OpenAICmdInfoChatMessageType = null;
|
|
||||||
return (
|
|
||||||
<div className="chat-window" ref={this.chatWindowScrollRef}>
|
|
||||||
<For each="chitem" index="idx" of={chatMessageItems}>
|
|
||||||
{this.renderChatMessage(chitem)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let model = GlobalModel;
|
const chatMessageItems = GlobalModel.inputModel.AICmdInfoChatItems.slice();
|
||||||
let inputModel = model.inputModel;
|
const chitem: OpenAICmdInfoChatMessageType = null;
|
||||||
|
const renderKeybindings = mobx
|
||||||
const termFontSize = 14;
|
|
||||||
const textAreaMaxLines = 4;
|
|
||||||
const textAreaLineHeight = termFontSize * 1.5;
|
|
||||||
const textAreaPadding = 2 * 0.5 * termFontSize;
|
|
||||||
let textAreaMaxHeight = textAreaLineHeight * textAreaMaxLines + textAreaPadding;
|
|
||||||
let textAreaInnerHeight = this.textAreaNumLines.get() * textAreaLineHeight + textAreaPadding;
|
|
||||||
let renderKeybindings = mobx
|
|
||||||
.computed(() => {
|
.computed(() => {
|
||||||
return (
|
return (
|
||||||
this.isFocused.get() ||
|
this.isFocused.get() ||
|
||||||
@ -260,27 +247,23 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="cmd-aichat">
|
<AuxiliaryCmdView
|
||||||
|
title="Wave AI"
|
||||||
|
className="cmd-aichat"
|
||||||
|
onClose={() => GlobalModel.inputModel.closeAIAssistantChat(true)}
|
||||||
|
iconClass="fa-sharp fa-solid fa-sparkles"
|
||||||
|
>
|
||||||
<If condition={renderKeybindings}>
|
<If condition={renderKeybindings}>
|
||||||
<AIChatKeybindings AIChatObject={this}></AIChatKeybindings>
|
<AIChatKeybindings AIChatObject={this}></AIChatKeybindings>
|
||||||
</If>
|
</If>
|
||||||
<div className="cmdinput-titlebar">
|
<div className="chat-window" ref={this.chatWindowScrollRef}>
|
||||||
<div className="title-icon">
|
<For each="chitem" index="idx" of={chatMessageItems}>
|
||||||
<i className="fa-sharp fa-solid fa-sparkles" />
|
{this.renderChatMessage(chitem)}
|
||||||
|
</For>
|
||||||
</div>
|
</div>
|
||||||
<div className="title-string">Wave AI</div>
|
<div className="chat-input">
|
||||||
<div className="flex-spacer"></div>
|
|
||||||
<div
|
|
||||||
className="close-button"
|
|
||||||
title="Close (ESC)"
|
|
||||||
onClick={() => inputModel.closeAIAssistantChat(true)}
|
|
||||||
>
|
|
||||||
<i className="fa-sharp fa-solid fa-xmark-large" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="titlebar-spacer" />
|
|
||||||
{this.renderChatWindow()}
|
|
||||||
<textarea
|
<textarea
|
||||||
key="main"
|
key="main"
|
||||||
ref={this.textAreaRef}
|
ref={this.textAreaRef}
|
||||||
@ -291,11 +274,12 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
onBlur={this.onTextAreaBlur.bind(this)}
|
onBlur={this.onTextAreaBlur.bind(this)}
|
||||||
onChange={this.onTextAreaChange.bind(this)}
|
onChange={this.onTextAreaChange.bind(this)}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
style={{ height: textAreaInnerHeight, maxHeight: textAreaMaxHeight, fontSize: termFontSize }}
|
style={{ fontSize: this.termFontSize }}
|
||||||
className={cn("chat-textarea")}
|
className="chat-textarea"
|
||||||
placeholder="Send a Message..."
|
placeholder="Send a Message..."
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
</AuxiliaryCmdView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
51
src/app/workspace/cmdinput/auxview.less
Normal file
51
src/app/workspace/cmdinput/auxview.less
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// For the additonal views, we want less padding on the top and bottom than we want for the base-cmdinput div
|
||||||
|
.auxview {
|
||||||
|
padding: var(--termpad) calc(var(--termpad) * 2) var(--termpad) calc(var(--termpad) * 3 - 1px);
|
||||||
|
overflow: auto;
|
||||||
|
flex-shrink: 1;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
--auxview-titlebar-height: 18px;
|
||||||
|
|
||||||
|
.auxview-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%;
|
||||||
|
border-bottom: 1px solid var(--app-border-color);
|
||||||
|
font: var(--base-font);
|
||||||
|
user-select: none;
|
||||||
|
cursor: default;
|
||||||
|
line-height: var(--auxview-titlebar-height);
|
||||||
|
|
||||||
|
.title-string {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
cursor: pointer;
|
||||||
|
i {
|
||||||
|
color: var(--app-icon-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--app-icon-hover-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div:not(.close-button, .flex-spacer) {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auxview-content {
|
||||||
|
display: flex;
|
||||||
|
padding-top: calc(var(--auxview-titlebar-height) + 6px);
|
||||||
|
}
|
||||||
|
}
|
50
src/app/workspace/cmdinput/auxview.tsx
Normal file
50
src/app/workspace/cmdinput/auxview.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import cn from "classnames";
|
||||||
|
import { If } from "tsx-control-statements/components";
|
||||||
|
|
||||||
|
import "./auxview.less";
|
||||||
|
|
||||||
|
export class AuxiliaryCmdView extends React.Component<
|
||||||
|
{
|
||||||
|
title: string;
|
||||||
|
className?: string;
|
||||||
|
iconClass?: string;
|
||||||
|
titleBarContents?: React.ReactElement[];
|
||||||
|
children?: React.ReactNode;
|
||||||
|
onClose?: React.MouseEventHandler<HTMLDivElement>;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
> {
|
||||||
|
render() {
|
||||||
|
const { title, className, iconClass, titleBarContents, children, onClose } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn("auxview", className)}>
|
||||||
|
<div className="auxview-titlebar">
|
||||||
|
<If condition={iconClass != null}>
|
||||||
|
<div className="title-icon">
|
||||||
|
<i className={iconClass} />
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
<div className="title-string">{title}</div>
|
||||||
|
|
||||||
|
<If condition={titleBarContents != null}>{titleBarContents}</If>
|
||||||
|
|
||||||
|
<div className="flex-spacer"></div>
|
||||||
|
|
||||||
|
<If condition={onClose != null}>
|
||||||
|
<div className="close-button" title="Close (ESC)" onClick={onClose}>
|
||||||
|
<i className="fa-sharp fa-solid fa-xmark-large" />
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
<If condition={children != null}>
|
||||||
|
<div className="auxview-content">{children}</div>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -7,54 +7,153 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: calc(var(--termpad) * 2) calc(var(--termpad) * 2) calc(var(--termpad) * 2) calc(var(--termpad) * 3 - 1px);
|
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
border-top: 2px solid var(--app-border-color);
|
border-top: 2px solid var(--app-border-color);
|
||||||
background-color: var(--app-bg-color);
|
background-color: var(--app-bg-color);
|
||||||
|
|
||||||
&.has-info,
|
// Apply a border between the base cmdinput and any views shown above it
|
||||||
|
// TODO: use a generic selector for this
|
||||||
&.has-aichat,
|
&.has-aichat,
|
||||||
&.has-history {
|
&.has-history,
|
||||||
.cmdinput-actions {
|
&.has-info {
|
||||||
display: none;
|
.base-cmdinput {
|
||||||
|
border-top: 1px solid var(--app-border-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar-spacer {
|
&.has-info {
|
||||||
height: 31px;
|
padding-top: var(--termpad);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cmdinput-conn {
|
&.has-history,
|
||||||
|
&.has-aichat {
|
||||||
|
padding-top: var(--termpad);
|
||||||
|
height: max(300px, 70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.remote-status-warning {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
color: var(--app-warning-color);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.wave-button,
|
||||||
|
.button {
|
||||||
|
margin-left: 10px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmd-input-grow-spacer {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-cmdinput {
|
||||||
|
position: relative;
|
||||||
|
// Rather than apply the padding to the whole container, we will apply it to the inner contents directly.
|
||||||
|
// This is more fragile, but allows us to capture a larger target area for the individual components.
|
||||||
|
--padding-top: var(--termpad);
|
||||||
|
--padding-sides: calc(var(--termpad) * 2);
|
||||||
|
|
||||||
|
.cmd-input-context {
|
||||||
|
color: var(--term-bright-white);
|
||||||
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-family: var(--termfontfamily);
|
||||||
|
font-size: var(--termfontsize);
|
||||||
|
line-height: var(--termlineheight);
|
||||||
|
|
||||||
|
// We don't want to pad the bottom or it will push the input field down.
|
||||||
|
padding: var(--padding-top) var(--padding-sides) 0 var(--padding-sides);
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmd-input-field {
|
||||||
|
position: relative;
|
||||||
|
font-family: var(--termfontfamily);
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: var(--termlineheight);
|
||||||
|
font-size: var(--termfontsize);
|
||||||
|
border: none;
|
||||||
|
cursor: text;
|
||||||
|
|
||||||
|
// We don't want to pad the top or it will push the input field down.
|
||||||
|
padding: 0 var(--padding-sides) var(--padding-top) var(--padding-sides);
|
||||||
|
|
||||||
|
.cmd-hints {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
bottom: -14px;
|
||||||
left: 0;
|
right: 0px;
|
||||||
border-radius: 0 0 4px 0;
|
}
|
||||||
background-color: rgba(88, 193, 66, 0.3);
|
.control {
|
||||||
padding: 2px 10px 4px 10px;
|
padding: 1em 2px;
|
||||||
font-size: calc(var(--termfontsize));
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
.textareainput-div {
|
||||||
background-color: rgba(88, 193, 66, 0.5);
|
position: relative;
|
||||||
|
|
||||||
|
&.control {
|
||||||
|
padding: var(--termpad) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shelltag {
|
||||||
|
position: absolute;
|
||||||
|
// 13px = 10px height + 3px padding. subtract termpad to account for textareainput-div padding (2px not sure?)
|
||||||
|
bottom: calc(-13px + var(--termpad));
|
||||||
|
right: 0;
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--app-text-secondary-color);
|
||||||
|
line-height: 1;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
color: var(--app-text-primary-color);
|
||||||
|
background-color: var(--app-bg-color);
|
||||||
|
padding: var(--termpad) 0;
|
||||||
|
resize: none;
|
||||||
|
overflow: auto;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
font-family: var(--termfontfamily);
|
||||||
|
line-height: var(--termlineheight);
|
||||||
|
font-size: var(--termfontsize);
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.history-input {
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmd-quick-context .button {
|
||||||
|
background-color: var(--app-bg-color) !important;
|
||||||
|
color: var(--app-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.inputmode-global .cmd-quick-context .button {
|
||||||
|
color: var(--app-bg-color);
|
||||||
|
background-color: var(--cmdinput-button-bg-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.inputmode-comment .cmd-quick-context .button {
|
||||||
|
color: var(--app-bg-color);
|
||||||
|
background-color: var(--cmdinput-comment-button-bg-color) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cmdinput-actions {
|
.cmdinput-actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 4px;
|
|
||||||
padding-left: 4px;
|
|
||||||
padding-right: 4px;
|
|
||||||
font-size: calc(var(--termfontsize) + 2px);
|
font-size: calc(var(--termfontsize) + 2px);
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
|
|
||||||
// we want to align to 2nd line of meta. that's 2xPad + 1xLineHightSm
|
// Align to the same bounds as the input field
|
||||||
// height of actions is 1xLineHeight + 8px (2x2px padding on icons)
|
top: var(--padding-top);
|
||||||
top: calc(var(--termpad));
|
right: var(--padding-sides);
|
||||||
right: calc(var(--termpad) * 2);
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -83,7 +182,9 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
padding: 4px 6px;
|
// This aligns the icons with the prompt field.
|
||||||
|
// We don't need right padding because the whole input field is already padded.
|
||||||
|
padding: 2px 0 0 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,467 +192,5 @@
|
|||||||
margin-left: 3px;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
.focus-indicator {
|
|
||||||
height: 90%;
|
|
||||||
top: 5%;
|
|
||||||
left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.has-history,
|
|
||||||
&.has-aichat {
|
|
||||||
padding-top: var(--termpad);
|
|
||||||
height: max(300px, 70%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.has-remote {
|
|
||||||
max-height: max(300px, 70%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.remote-status-warning {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
color: var(--app-warning-color);
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.wave-button,
|
|
||||||
.button {
|
|
||||||
margin-left: 10px;
|
|
||||||
padding: 4px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-minmax-control {
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
right: 5px;
|
|
||||||
color: var(--term-foreground);
|
|
||||||
|
|
||||||
padding: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-input-grow-spacer {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.base-cmdinput:not(:first-child) {
|
|
||||||
margin-top: var(--termpad);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-input-context {
|
|
||||||
color: var(--term-bright-white);
|
|
||||||
white-space: nowrap;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
font-family: var(--termfontfamily);
|
|
||||||
font-size: var(--termfontsize);
|
|
||||||
line-height: var(--termlineheight);
|
|
||||||
margin-left: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-input-filter {
|
|
||||||
opacity: 0.5;
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
display: inline-block;
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
margin: 0 0.5em;
|
|
||||||
vertical-align: text-top;
|
|
||||||
fill: var(--term-foreground);
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
|
||||||
fill: var(--term-yellow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-input-field {
|
|
||||||
position: relative;
|
|
||||||
padding-right: var(--termpad);
|
|
||||||
font-family: var(--termfontfamily);
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: var(--termlineheight);
|
|
||||||
font-size: var(--termfontsize);
|
|
||||||
|
|
||||||
.cmd-hints {
|
|
||||||
position: absolute;
|
|
||||||
bottom: -14px;
|
|
||||||
right: 0px;
|
|
||||||
}
|
|
||||||
.control {
|
|
||||||
padding: 1em 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textareainput-div {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&.control {
|
|
||||||
padding: var(--termpad) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shelltag {
|
|
||||||
position: absolute;
|
|
||||||
// 13px = 10px height + 3px padding. subtract termpad to account for textareainput-div padding (2px not sure?)
|
|
||||||
bottom: calc(-13px + var(--termpad));
|
|
||||||
right: 0px;
|
|
||||||
font-size: 10px;
|
|
||||||
color: var(--app-text-secondary-color);
|
|
||||||
line-height: 1;
|
|
||||||
padding: 1px 8px 3px 8px;
|
|
||||||
background-color: var(--cmdinput-textarea-bg-color);
|
|
||||||
border-radius: 0 0 5px 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
color: var(--app-text-primary-color);
|
|
||||||
background-color: var(--cmdinput-textarea-bg-color);
|
|
||||||
padding: var(--termpad);
|
|
||||||
resize: none;
|
|
||||||
overflow: auto;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
border-color: transparent;
|
|
||||||
border: none;
|
|
||||||
font-family: var(--termfontfamily);
|
|
||||||
line-height: var(--termlineheight);
|
|
||||||
font-size: var(--termfontsize);
|
|
||||||
border-radius: 4px 4px 0 4px; // 0 is for shelltag
|
|
||||||
|
|
||||||
&.display-disabled {
|
|
||||||
background-color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input.history-input {
|
|
||||||
border: 0;
|
|
||||||
padding: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-quick-context .button {
|
|
||||||
background-color: #000 !important;
|
|
||||||
color: var(--app-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.inputmode-global .cmd-quick-context .button {
|
|
||||||
color: var(--app-bg-color);
|
|
||||||
background-color: var(--cmdinput-button-bg-color) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.inputmode-comment .cmd-quick-context .button {
|
|
||||||
color: var(--app-bg-color);
|
|
||||||
background-color: var(--cmdinput-comment-button-bg-color) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-exec {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
.icon {
|
|
||||||
vertical-align: bottom;
|
|
||||||
margin-right: 1em;
|
|
||||||
width: 2.5em;
|
|
||||||
height: 2.5em;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 50%;
|
|
||||||
fill: var(--app-accent-color);
|
|
||||||
padding: 0.25em;
|
|
||||||
}
|
|
||||||
.icon.disabled {
|
|
||||||
fill: var(--cmdinput-disabled-icon-color);
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
.cmd-btn {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 0;
|
|
||||||
padding: 0.2em 0.7rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
vertical-align: super;
|
|
||||||
|
|
||||||
.hint-elem {
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.5;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-aichat {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
flex-shrink: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.chat-window {
|
|
||||||
overflow-y: auto;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
flex-shrink: 1;
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-textarea {
|
|
||||||
color: var(--app-text-primary-color);
|
|
||||||
background-color: var(--cmdinput-textarea-bg);
|
|
||||||
padding: 0.5em;
|
|
||||||
resize: none;
|
|
||||||
overflow: auto;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
border-color: transparent;
|
|
||||||
border: none;
|
|
||||||
font-family: var(--termfontfamily);
|
|
||||||
font-weight: normal;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-grow: 1;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-msg {
|
|
||||||
margin-top: calc(var(--termpad) * 2);
|
|
||||||
margin-bottom: calc(var(--termpad) * 2);
|
|
||||||
|
|
||||||
.chat-msg-header {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-username {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-msg-assistant {
|
|
||||||
color: var(--app-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-msg-user {
|
|
||||||
.msg-text {
|
|
||||||
font-family: var(--markdown-font);
|
|
||||||
font-size: 14px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-msg-error {
|
|
||||||
color: var(--cmdinput-text-error);
|
|
||||||
font-family: var(--markdown-font);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grow-spacer {
|
|
||||||
flex: 1 0 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-history {
|
|
||||||
color: var(--app-text-color);
|
|
||||||
font-family: var(--termfontfamily);
|
|
||||||
font-size: var(--termfontsize);
|
|
||||||
overflow: auto;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
.history-title {
|
|
||||||
div:first-child {
|
|
||||||
margin-left: var(--termpad);
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-opt {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-clickable-opt {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--app-text-primary-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-clickable-opt {
|
|
||||||
white-space: nowrap;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-items {
|
|
||||||
color: var(--app-text-color);
|
|
||||||
|
|
||||||
padding-bottom: 6px;
|
|
||||||
|
|
||||||
.history-line {
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-item.history-haderror {
|
|
||||||
color: var(--cmdinput-history-item-error-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-line:first-child {
|
|
||||||
margin-left: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-item {
|
|
||||||
padding-left: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-item.is-selected {
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--app-text-primary-color);
|
|
||||||
background-color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-item.is-selected.history-haderror {
|
|
||||||
color: var(--cmdinput-history-item-selected-error-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmd-input-info {
|
|
||||||
flex-shrink: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
margin-bottom: var(--termpad);
|
|
||||||
font-family: var(--termfontfamily);
|
|
||||||
font-size: var(--termfontsize);
|
|
||||||
line-height: var(--termlineheight);
|
|
||||||
|
|
||||||
.info-title {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 102;
|
|
||||||
top: 5px;
|
|
||||||
left: 0;
|
|
||||||
font-size: calc(var(--termfontsize) + 2px);
|
|
||||||
background-color: var(--app-bg-color);
|
|
||||||
color: var(--term-blue);
|
|
||||||
padding-bottom: 4px;
|
|
||||||
padding-left: calc(var(--termpad) * 2);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: 100%;
|
|
||||||
overflow-x: auto;
|
|
||||||
border-bottom: 1px solid var(--app-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-title + .info-msg,
|
|
||||||
.info-title + .info-lines,
|
|
||||||
.info-title + .info-comps,
|
|
||||||
.info-title + .info-error {
|
|
||||||
margin-top: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-msg {
|
|
||||||
color: var(--term-blue);
|
|
||||||
padding-bottom: 2px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--term-blue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-lines {
|
|
||||||
color: var(--app-text-color);
|
|
||||||
white-space: pre;
|
|
||||||
padding-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-comps {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
font-weight: normal;
|
|
||||||
font-family: var(--termfontfamily);
|
|
||||||
font-size: var(--termfontsize);
|
|
||||||
|
|
||||||
.info-comp {
|
|
||||||
min-width: 200px;
|
|
||||||
color: var(--term-foreground);
|
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
&.has-space {
|
|
||||||
text-decoration: underline dotted #777;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.metacmd-comp {
|
|
||||||
color: var(--term-bright-green);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-error {
|
|
||||||
color: var(--cmdinput-text-error-color);
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,12 +61,17 @@ class CmdInput extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
cmdInputClick(e: any): void {
|
baseCmdInputClick(e: React.MouseEvent): void {
|
||||||
if (this.promptRef.current != null) {
|
if (this.promptRef.current != null) {
|
||||||
if (this.promptRef.current.contains(e.target)) {
|
if (this.promptRef.current.contains(e.target)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((e.target as HTMLDivElement).classList.contains("cmd-input-context")) {
|
||||||
|
e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GlobalModel.inputModel.setHistoryFocus(false);
|
||||||
GlobalModel.inputModel.giveFocus();
|
GlobalModel.inputModel.giveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,9 +79,13 @@ class CmdInput extends React.Component<{}, {}> {
|
|||||||
clickAIAction(e: any): void {
|
clickAIAction(e: any): void {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
let inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
|
if (inputModel.aIChatShow.get()) {
|
||||||
|
inputModel.closeAIAssistantChat(true);
|
||||||
|
} else {
|
||||||
inputModel.openAIAssistantChat();
|
inputModel.openAIAssistantChat();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
clickHistoryAction(e: any): void {
|
clickHistoryAction(e: any): void {
|
||||||
@ -160,6 +169,9 @@ class CmdInput extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
let shellInitMsg: string = null;
|
let shellInitMsg: string = null;
|
||||||
let hidePrompt = false;
|
let hidePrompt = false;
|
||||||
|
|
||||||
|
const openView = inputModel.getOpenView();
|
||||||
|
const hasOpenView = openView ? `has-${openView}` : null;
|
||||||
if (ri == null) {
|
if (ri == null) {
|
||||||
let shellStr = "shell";
|
let shellStr = "shell";
|
||||||
if (!util.isBlank(remote?.defaultshelltype)) {
|
if (!util.isBlank(remote?.defaultshelltype)) {
|
||||||
@ -172,42 +184,7 @@ class CmdInput extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div ref={this.cmdInputRef} className={cn("cmd-input", hasOpenView, { active: focusVal })}>
|
||||||
ref={this.cmdInputRef}
|
|
||||||
className={cn(
|
|
||||||
"cmd-input",
|
|
||||||
{ "has-info": infoShow },
|
|
||||||
{ "has-aichat": aiChatShow },
|
|
||||||
{ "has-history": historyShow },
|
|
||||||
{ active: focusVal }
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="cmdinput-actions">
|
|
||||||
<If condition={numRunningLines > 0}>
|
|
||||||
<div
|
|
||||||
key="running"
|
|
||||||
className={cn("cmdinput-icon", "running-cmds", { active: filterRunning })}
|
|
||||||
title="Filter for Running Commands"
|
|
||||||
onClick={() => this.toggleFilter(screen)}
|
|
||||||
>
|
|
||||||
<CenteredIcon>{numRunningLines}</CenteredIcon>{" "}
|
|
||||||
<CenteredIcon>
|
|
||||||
<RotateIcon className="rotate warning spin" />
|
|
||||||
</CenteredIcon>
|
|
||||||
</div>
|
|
||||||
</If>
|
|
||||||
<div key="ai" title="Wave AI (Ctrl-Space)" className="cmdinput-icon" onClick={this.clickAIAction}>
|
|
||||||
<i className="fa-sharp fa-regular fa-sparkles fa-fw" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
key="history"
|
|
||||||
title="Tab History (Ctrl-R)"
|
|
||||||
className="cmdinput-icon"
|
|
||||||
onClick={this.clickHistoryAction}
|
|
||||||
>
|
|
||||||
<i className="fa-sharp fa-regular fa-clock-rotate-left fa-fw" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<If condition={historyShow}>
|
<If condition={historyShow}>
|
||||||
<div className="cmd-input-grow-spacer"></div>
|
<div className="cmd-input-grow-spacer"></div>
|
||||||
<HistoryInfo />
|
<HistoryInfo />
|
||||||
@ -252,7 +229,38 @@ class CmdInput extends React.Component<{}, {}> {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</If>
|
</If>
|
||||||
<div key="base-cmdinput" className="base-cmdinput">
|
<div key="base-cmdinput" className="base-cmdinput" onClick={this.baseCmdInputClick}>
|
||||||
|
<div className="cmdinput-actions">
|
||||||
|
<If condition={numRunningLines > 0}>
|
||||||
|
<div
|
||||||
|
key="running"
|
||||||
|
className={cn("cmdinput-icon", "running-cmds", { active: filterRunning })}
|
||||||
|
title="Filter for Running Commands"
|
||||||
|
onClick={() => this.toggleFilter(screen)}
|
||||||
|
>
|
||||||
|
<CenteredIcon>{numRunningLines}</CenteredIcon>{" "}
|
||||||
|
<CenteredIcon>
|
||||||
|
<RotateIcon className="rotate warning spin" />
|
||||||
|
</CenteredIcon>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
<div
|
||||||
|
key="ai"
|
||||||
|
title="Wave AI (Ctrl-Space)"
|
||||||
|
className="cmdinput-icon"
|
||||||
|
onClick={this.clickAIAction}
|
||||||
|
>
|
||||||
|
<i className="fa-sharp fa-regular fa-sparkles fa-fw" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
key="history"
|
||||||
|
title="Tab History (Ctrl-R)"
|
||||||
|
className="cmdinput-icon"
|
||||||
|
onClick={this.clickHistoryAction}
|
||||||
|
>
|
||||||
|
<i className="fa-sharp fa-regular fa-clock-rotate-left fa-fw" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<If condition={!hidePrompt}>
|
<If condition={!hidePrompt}>
|
||||||
<div key="prompt" className="cmd-input-context">
|
<div key="prompt" className="cmd-input-context">
|
||||||
<div className="has-text-white">
|
<div className="has-text-white">
|
||||||
|
60
src/app/workspace/cmdinput/historyinfo.less
Normal file
60
src/app/workspace/cmdinput/historyinfo.less
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
.cmd-history {
|
||||||
|
color: var(--app-text-color);
|
||||||
|
font-family: var(--termfontfamily);
|
||||||
|
font-size: var(--termfontsize);
|
||||||
|
|
||||||
|
.auxview-titlebar {
|
||||||
|
.history-opt {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-clickable-opt {
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--app-text-primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-items {
|
||||||
|
color: var(--app-text-color);
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.history-item {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
.history-line {
|
||||||
|
white-space: pre;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--table-tr-hover-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.history-haderror {
|
||||||
|
color: var(--cmdinput-history-item-error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-selected {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--app-text-primary-color);
|
||||||
|
background-color: var(--table-tr-selected-bg-color);
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--table-tr-selected-hover-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-selected.history-haderror {
|
||||||
|
color: var(--cmdinput-history-item-selected-error-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,9 @@ import localizedFormat from "dayjs/plugin/localizedFormat";
|
|||||||
import { GlobalModel } from "@/models";
|
import { GlobalModel } from "@/models";
|
||||||
import { isBlank } from "@/util/util";
|
import { isBlank } from "@/util/util";
|
||||||
|
|
||||||
|
import "./historyinfo.less";
|
||||||
|
import { AuxiliaryCmdView } from "./auxview";
|
||||||
|
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
|
|
||||||
const TDots = "⋮";
|
const TDots = "⋮";
|
||||||
@ -43,7 +46,7 @@ class HItem extends React.Component<
|
|||||||
if (hitem.remote == null || isBlank(hitem.remote.remoteid)) {
|
if (hitem.remote == null || isBlank(hitem.remote.remoteid)) {
|
||||||
return sprintf("%-15s ", "");
|
return sprintf("%-15s ", "");
|
||||||
}
|
}
|
||||||
let r = GlobalModel.getRemote(hitem.remote.remoteid);
|
const r = GlobalModel.getRemote(hitem.remote.remoteid);
|
||||||
if (r == null) {
|
if (r == null) {
|
||||||
return sprintf("%-15s ", "???");
|
return sprintf("%-15s ", "???");
|
||||||
}
|
}
|
||||||
@ -71,15 +74,15 @@ class HItem extends React.Component<
|
|||||||
if (!opts.limitRemote) {
|
if (!opts.limitRemote) {
|
||||||
remoteStr = this.renderRemote(hitem);
|
remoteStr = this.renderRemote(hitem);
|
||||||
}
|
}
|
||||||
let selectedStr = isSelected ? "*" : " ";
|
const selectedStr = isSelected ? "*" : " ";
|
||||||
let lineNumStr = hitem.linenum > 0 ? "(" + hitem.linenum + ")" : "";
|
const lineNumStr = hitem.linenum > 0 ? "(" + hitem.linenum + ")" : "";
|
||||||
if (isBlank(opts.queryType) || opts.queryType == "screen") {
|
if (isBlank(opts.queryType) || opts.queryType == "screen") {
|
||||||
return selectedStr + sprintf("%7s", lineNumStr) + " " + remoteStr;
|
return selectedStr + sprintf("%7s", lineNumStr) + " " + remoteStr;
|
||||||
}
|
}
|
||||||
if (opts.queryType == "session") {
|
if (opts.queryType == "session") {
|
||||||
let screenStr = "";
|
let screenStr = "";
|
||||||
if (!isBlank(hitem.screenid)) {
|
if (!isBlank(hitem.screenid)) {
|
||||||
let scrName = scrNames[hitem.screenid];
|
const scrName = scrNames[hitem.screenid];
|
||||||
if (scrName != null) {
|
if (scrName != null) {
|
||||||
screenStr = "[" + truncateWithTDots(scrName, 15) + "]";
|
screenStr = "[" + truncateWithTDots(scrName, 15) + "]";
|
||||||
}
|
}
|
||||||
@ -89,19 +92,18 @@ class HItem extends React.Component<
|
|||||||
if (opts.queryType == "global") {
|
if (opts.queryType == "global") {
|
||||||
let sessionStr = "";
|
let sessionStr = "";
|
||||||
if (!isBlank(hitem.sessionid)) {
|
if (!isBlank(hitem.sessionid)) {
|
||||||
let sessionName = snames[hitem.sessionid];
|
const sessionName = snames[hitem.sessionid];
|
||||||
if (sessionName != null) {
|
if (sessionName != null) {
|
||||||
sessionStr = "#" + truncateWithTDots(sessionName, 15);
|
sessionStr = "#" + truncateWithTDots(sessionName, 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let screenStr = "";
|
let screenStr = "";
|
||||||
if (!isBlank(hitem.screenid)) {
|
if (!isBlank(hitem.screenid)) {
|
||||||
let scrName = scrNames[hitem.screenid];
|
const scrName = scrNames[hitem.screenid];
|
||||||
if (scrName != null) {
|
if (scrName != null) {
|
||||||
screenStr = "[" + truncateWithTDots(scrName, 13) + "]";
|
screenStr = "[" + truncateWithTDots(scrName, 13) + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let ssStr = sessionStr + screenStr;
|
|
||||||
return (
|
return (
|
||||||
selectedStr +
|
selectedStr +
|
||||||
sprintf("%15s ", sessionStr) +
|
sprintf("%15s ", sessionStr) +
|
||||||
@ -116,12 +118,12 @@ class HItem extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { hitem, isSelected, opts, snames, scrNames } = this.props;
|
const { hitem, isSelected, opts, snames, scrNames } = this.props;
|
||||||
let lines = hitem.cmdstr.split("\n");
|
const lines = hitem.cmdstr.split("\n");
|
||||||
let line: string = "";
|
let line: string = "";
|
||||||
let idx = 0;
|
let idx = 0;
|
||||||
let infoText = this.renderHInfoText(hitem, opts, isSelected, snames, scrNames);
|
const infoText = this.renderHInfoText(hitem, opts, isSelected, snames, scrNames);
|
||||||
let infoTextSpacer = sprintf("%" + infoText.length + "s", "");
|
const infoTextSpacer = sprintf("%" + infoText.length + "s", "");
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={hitem.historynum}
|
key={hitem.historynum}
|
||||||
@ -153,7 +155,7 @@ class HistoryInfo extends React.Component<{}, {}> {
|
|||||||
containingText: mobx.IObservableValue<string> = mobx.observable.box("");
|
containingText: mobx.IObservableValue<string> = mobx.observable.box("");
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
let hitem = inputModel.getHistorySelectedItem();
|
let hitem = inputModel.getHistorySelectedItem();
|
||||||
if (hitem == null) {
|
if (hitem == null) {
|
||||||
hitem = inputModel.getFirstHistoryItem();
|
hitem = inputModel.getFirstHistoryItem();
|
||||||
@ -170,15 +172,15 @@ class HistoryInfo extends React.Component<{}, {}> {
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
handleItemClick(hitem: HistoryItem) {
|
handleItemClick(hitem: HistoryItem) {
|
||||||
let inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
let selItem = inputModel.getHistorySelectedItem();
|
const selItem = inputModel.getHistorySelectedItem();
|
||||||
|
inputModel.setHistoryFocus(true);
|
||||||
if (this.lastClickHNum == hitem.historynum && selItem != null && selItem.historynum == hitem.historynum) {
|
if (this.lastClickHNum == hitem.historynum && selItem != null && selItem.historynum == hitem.historynum) {
|
||||||
inputModel.grabSelectedHistoryItem();
|
inputModel.grabSelectedHistoryItem();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
inputModel.giveFocus();
|
|
||||||
inputModel.setHistorySelectionNum(hitem.historynum);
|
inputModel.setHistorySelectionNum(hitem.historynum);
|
||||||
let now = Date.now();
|
const now = Date.now();
|
||||||
this.lastClickHNum = hitem.historynum;
|
this.lastClickHNum = hitem.historynum;
|
||||||
this.lastClickTs = now;
|
this.lastClickTs = now;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -191,24 +193,41 @@ class HistoryInfo extends React.Component<{}, {}> {
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
handleClickType() {
|
handleClickType() {
|
||||||
let inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
|
inputModel.setHistoryFocus(true);
|
||||||
inputModel.toggleHistoryType();
|
inputModel.toggleHistoryType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
handleClickRemote() {
|
handleClickRemote() {
|
||||||
let inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
|
inputModel.setHistoryFocus(true);
|
||||||
inputModel.toggleRemoteType();
|
inputModel.toggleRemoteType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
getTitleBarContents(): React.ReactElement[] {
|
||||||
|
const opts = GlobalModel.inputModel.historyQueryOpts.get();
|
||||||
|
|
||||||
|
return [
|
||||||
|
<div className="history-opt history-clickable-opt" key="screen" onClick={this.handleClickType}>
|
||||||
|
[for {opts.queryType} ⌘S]
|
||||||
|
</div>,
|
||||||
|
<div className="history-opt" key="query-str" title="type to search">
|
||||||
|
[containing '{opts.queryStr}']
|
||||||
|
</div>,
|
||||||
|
<div className="history-opt history-clickable-opt" key="remote" onClick={this.handleClickRemote}>
|
||||||
|
[{opts.limitRemote ? "this" : "any"} remote ⌘R]
|
||||||
|
</div>,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
let idx: number = 0;
|
const selItem = inputModel.getHistorySelectedItem();
|
||||||
let selItem = inputModel.getHistorySelectedItem();
|
const hitems = inputModel.getFilteredHistoryItems().slice().reverse();
|
||||||
let hitems = inputModel.getFilteredHistoryItems();
|
const opts = inputModel.historyQueryOpts.get();
|
||||||
hitems = hitems.slice().reverse();
|
|
||||||
let hitem: HistoryItem = null;
|
let hitem: HistoryItem = null;
|
||||||
let opts = inputModel.historyQueryOpts.get();
|
|
||||||
let snames: Record<string, string> = {};
|
let snames: Record<string, string> = {};
|
||||||
let scrNames: Record<string, string> = {};
|
let scrNames: Record<string, string> = {};
|
||||||
if (opts.queryType == "global") {
|
if (opts.queryType == "global") {
|
||||||
@ -218,29 +237,13 @@ class HistoryInfo extends React.Component<{}, {}> {
|
|||||||
scrNames = GlobalModel.getScreenNames();
|
scrNames = GlobalModel.getScreenNames();
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="cmd-history hide-scrollbar">
|
<AuxiliaryCmdView
|
||||||
<div className="cmdinput-titlebar history-title">
|
title="History"
|
||||||
<div className="title-icon">
|
className="cmd-history hide-scrollbar"
|
||||||
<i className="fa-sharp fa-solid fa-clock-rotate-left" />
|
onClose={this.handleClose}
|
||||||
</div>
|
titleBarContents={this.getTitleBarContents()}
|
||||||
<div className="title-string">History</div>
|
iconClass="fa-sharp fa-solid fa-clock-rotate-left"
|
||||||
<div className="spacer"></div>
|
>
|
||||||
<div className="history-opt history-clickable-opt" onClick={this.handleClickType}>
|
|
||||||
[for {opts.queryType} ⌘S]
|
|
||||||
</div>
|
|
||||||
<div className="spacer"></div>
|
|
||||||
<div className="history-opt" title="type to search">
|
|
||||||
[containing '{opts.queryStr}']
|
|
||||||
</div>
|
|
||||||
<div className="spacer"></div>
|
|
||||||
<div className="history-opt history-clickable-opt" onClick={this.handleClickRemote}>
|
|
||||||
[{opts.limitRemote ? "this" : "any"} remote ⌘R]
|
|
||||||
</div>
|
|
||||||
<div className="flex-spacer"></div>
|
|
||||||
<div className="close-button" title="Close (ESC)">
|
|
||||||
<i className="fa-sharp fa-solid fa-xmark-large" onClick={this.handleClose}></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"history-items",
|
"history-items",
|
||||||
@ -248,7 +251,6 @@ class HistoryInfo extends React.Component<{}, {}> {
|
|||||||
{ "show-sessions": opts.queryType == "global" }
|
{ "show-sessions": opts.queryType == "global" }
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="titlebar-spacer" />
|
|
||||||
<If condition={hitems.length == 0}>[no history]</If>
|
<If condition={hitems.length == 0}>[no history]</If>
|
||||||
<If condition={hitems.length > 0}>
|
<If condition={hitems.length > 0}>
|
||||||
<For each="hitem" index="idx" of={hitems}>
|
<For each="hitem" index="idx" of={hitems}>
|
||||||
@ -264,7 +266,7 @@ class HistoryInfo extends React.Component<{}, {}> {
|
|||||||
</For>
|
</For>
|
||||||
</If>
|
</If>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AuxiliaryCmdView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
49
src/app/workspace/cmdinput/infomsg.less
Normal file
49
src/app/workspace/cmdinput/infomsg.less
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
.cmd-input-info {
|
||||||
|
font-family: var(--termfontfamily);
|
||||||
|
font-size: var(--termfontsize);
|
||||||
|
line-height: var(--termlineheight);
|
||||||
|
|
||||||
|
.info-msg {
|
||||||
|
color: var(--term-blue);
|
||||||
|
padding-bottom: 2px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--term-blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-lines {
|
||||||
|
color: var(--app-text-color);
|
||||||
|
white-space: pre;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-comps {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: var(--termfontfamily);
|
||||||
|
font-size: var(--termfontsize);
|
||||||
|
|
||||||
|
.info-comp {
|
||||||
|
min-width: 200px;
|
||||||
|
color: var(--term-foreground);
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
&.has-space {
|
||||||
|
text-decoration: underline dotted #777;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.metacmd-comp {
|
||||||
|
color: var(--term-bright-green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-error {
|
||||||
|
color: var(--cmdinput-text-error-color);
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,9 @@ import dayjs from "dayjs";
|
|||||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||||
import { GlobalModel } from "@/models";
|
import { GlobalModel } from "@/models";
|
||||||
import * as appconst from "@/app/appconst";
|
import * as appconst from "@/app/appconst";
|
||||||
|
import { AuxiliaryCmdView } from "./auxview";
|
||||||
|
|
||||||
|
import "./infomsg.less";
|
||||||
|
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
|
|
||||||
@ -40,29 +43,22 @@ class InfoMsg extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let model = GlobalModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
let inputModel = model.inputModel;
|
const infoMsg: InfoType = inputModel.infoMsg.get();
|
||||||
let infoMsg = inputModel.infoMsg.get();
|
const infoShow: boolean = inputModel.infoShow.get();
|
||||||
let infoShow = inputModel.infoShow.get();
|
|
||||||
let line: string = null;
|
let line: string = null;
|
||||||
let istr: string = null;
|
let istr: string = null;
|
||||||
let idx: number = 0;
|
let idx: number = 0;
|
||||||
let titleStr = null;
|
let titleStr = null;
|
||||||
let remoteEditKey = "inforemoteedit";
|
|
||||||
if (infoMsg != null) {
|
if (infoMsg != null) {
|
||||||
titleStr = infoMsg.infotitle;
|
titleStr = infoMsg.infotitle;
|
||||||
}
|
}
|
||||||
let activeScreen = model.getActiveScreen();
|
|
||||||
if (!infoShow) {
|
if (!infoShow) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="cmd-input-info" style={{ display: infoShow ? "block" : "none" }}>
|
<AuxiliaryCmdView title={titleStr} className="cmd-input-info">
|
||||||
<If condition={infoMsg?.infotitle}>
|
|
||||||
<div key="infotitle" className="info-title">
|
|
||||||
{titleStr}
|
|
||||||
</div>
|
|
||||||
</If>
|
|
||||||
<If condition={infoMsg?.infomsg}>
|
<If condition={infoMsg?.infomsg}>
|
||||||
<div key="infomsg" className="info-msg">
|
<div key="infomsg" className="info-msg">
|
||||||
<If condition={infoMsg.infomsghtml}>
|
<If condition={infoMsg.infomsghtml}>
|
||||||
@ -108,7 +104,7 @@ class InfoMsg extends React.Component<{}, {}> {
|
|||||||
<div className="info-error">to reset, run: /reset:cwd</div>
|
<div className="info-error">to reset, run: /reset:cwd</div>
|
||||||
</If>
|
</If>
|
||||||
</If>
|
</If>
|
||||||
</div>
|
</AuxiliaryCmdView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,7 +287,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
|
|
||||||
setFocus(): void {
|
setFocus(): void {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
if (inputModel.historyShow.get()) {
|
if (inputModel.historyFocus.get()) {
|
||||||
this.historyInputRef.current.focus();
|
this.historyInputRef.current.focus();
|
||||||
} else {
|
} else {
|
||||||
this.mainInputRef.current.focus();
|
this.mainInputRef.current.focus();
|
||||||
@ -551,7 +551,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
@boundMethod
|
@boundMethod
|
||||||
handleMainFocus(e: any) {
|
handleMainFocus(e: any) {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
if (inputModel.historyShow.get()) {
|
if (inputModel.historyFocus.get()) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.historyInputRef.current != null) {
|
if (this.historyInputRef.current != null) {
|
||||||
this.historyInputRef.current.focus();
|
this.historyInputRef.current.focus();
|
||||||
@ -578,7 +578,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
@boundMethod
|
@boundMethod
|
||||||
handleHistoryFocus(e: any) {
|
handleHistoryFocus(e: any) {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
if (!inputModel.historyShow.get()) {
|
if (!inputModel.historyFocus.get()) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.mainInputRef.current != null) {
|
if (this.mainInputRef.current != null) {
|
||||||
this.mainInputRef.current.focus();
|
this.mainInputRef.current.focus();
|
||||||
@ -616,7 +616,9 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
if (numLines > 1 || longLine || inputModel.inputExpanded.get()) {
|
if (numLines > 1 || longLine || inputModel.inputExpanded.get()) {
|
||||||
displayLines = 5;
|
displayLines = 5;
|
||||||
}
|
}
|
||||||
const disabled = inputModel.historyShow.get();
|
|
||||||
|
// TODO: invert logic here. We should track focus on the main textarea and assume aux view is focused if not.
|
||||||
|
const disabled = inputModel.historyFocus.get();
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
displayLines = 1;
|
displayLines = 1;
|
||||||
}
|
}
|
||||||
@ -661,7 +663,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
<HistoryKeybindings inputObject={this}></HistoryKeybindings>
|
<HistoryKeybindings inputObject={this}></HistoryKeybindings>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
<If condition={!disabled && !util.isBlank(shellType)}>
|
<If condition={!util.isBlank(shellType)}>
|
||||||
<div className="shelltag">{shellType}</div>
|
<div className="shelltag">{shellType}</div>
|
||||||
</If>
|
</If>
|
||||||
<textarea
|
<textarea
|
||||||
@ -678,6 +680,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
onSelect={this.onSelect}
|
onSelect={this.onSelect}
|
||||||
|
placeholder="Type here..."
|
||||||
className={cn("textarea", { "display-disabled": disabled })}
|
className={cn("textarea", { "display-disabled": disabled })}
|
||||||
></textarea>
|
></textarea>
|
||||||
<input
|
<input
|
||||||
|
@ -25,6 +25,7 @@ function getDefaultHistoryQueryOpts(): HistoryQueryOpts {
|
|||||||
class InputModel {
|
class InputModel {
|
||||||
globalModel: Model;
|
globalModel: Model;
|
||||||
historyShow: OV<boolean> = mobx.observable.box(false);
|
historyShow: OV<boolean> = mobx.observable.box(false);
|
||||||
|
historyFocus: OV<boolean> = mobx.observable.box(false);
|
||||||
infoShow: OV<boolean> = mobx.observable.box(false);
|
infoShow: OV<boolean> = mobx.observable.box(false);
|
||||||
aIChatShow: OV<boolean> = mobx.observable.box(false);
|
aIChatShow: OV<boolean> = mobx.observable.box(false);
|
||||||
cmdInputHeight: OV<number> = mobx.observable.box(0);
|
cmdInputHeight: OV<number> = mobx.observable.box(0);
|
||||||
@ -92,7 +93,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleHistoryType(): void {
|
toggleHistoryType(): void {
|
||||||
let opts = mobx.toJS(this.historyQueryOpts.get());
|
const opts = mobx.toJS(this.historyQueryOpts.get());
|
||||||
let htype = opts.queryType;
|
let htype = opts.queryType;
|
||||||
if (htype == "screen") {
|
if (htype == "screen") {
|
||||||
htype = "session";
|
htype = "session";
|
||||||
@ -105,7 +106,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleRemoteType(): void {
|
toggleRemoteType(): void {
|
||||||
let opts = mobx.toJS(this.historyQueryOpts.get());
|
const opts = mobx.toJS(this.historyQueryOpts.get());
|
||||||
if (opts.limitRemote) {
|
if (opts.limitRemote) {
|
||||||
opts.limitRemote = false;
|
opts.limitRemote = false;
|
||||||
opts.limitRemoteInstance = false;
|
opts.limitRemoteInstance = false;
|
||||||
@ -139,21 +140,21 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_focusCmdInput(): void {
|
_focusCmdInput(): void {
|
||||||
let elem = document.getElementById("main-cmd-input");
|
const elem = document.getElementById("main-cmd-input");
|
||||||
if (elem != null) {
|
if (elem != null) {
|
||||||
elem.focus();
|
elem.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_focusHistoryInput(): void {
|
_focusHistoryInput(): void {
|
||||||
let elem: HTMLElement = document.querySelector(".cmd-input input.history-input");
|
const elem: HTMLElement = document.querySelector(".cmd-input input.history-input");
|
||||||
if (elem != null) {
|
if (elem != null) {
|
||||||
elem.focus();
|
elem.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
giveFocus(): void {
|
giveFocus(): void {
|
||||||
if (this.historyShow.get()) {
|
if (this.historyFocus.get()) {
|
||||||
this._focusHistoryInput();
|
this._focusHistoryInput();
|
||||||
} else {
|
} else {
|
||||||
this._focusCmdInput();
|
this._focusCmdInput();
|
||||||
@ -165,7 +166,7 @@ class InputModel {
|
|||||||
this.physicalInputFocused.set(isFocused);
|
this.physicalInputFocused.set(isFocused);
|
||||||
})();
|
})();
|
||||||
if (isFocused) {
|
if (isFocused) {
|
||||||
let screen = this.globalModel.getActiveScreen();
|
const screen = this.globalModel.getActiveScreen();
|
||||||
if (screen != null) {
|
if (screen != null) {
|
||||||
if (screen.focusType.get() != "input") {
|
if (screen.focusType.get() != "input") {
|
||||||
GlobalCommandRunner.screenSetFocus("input");
|
GlobalCommandRunner.screenSetFocus("input");
|
||||||
@ -175,11 +176,11 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasFocus(): boolean {
|
hasFocus(): boolean {
|
||||||
let mainInputElem = document.getElementById("main-cmd-input");
|
const mainInputElem = document.getElementById("main-cmd-input");
|
||||||
if (document.activeElement == mainInputElem) {
|
if (document.activeElement == mainInputElem) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let historyInputElem = document.querySelector(".cmd-input input.history-input");
|
const historyInputElem = document.querySelector(".cmd-input input.history-input");
|
||||||
if (document.activeElement == historyInputElem) {
|
if (document.activeElement == historyInputElem) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -190,6 +191,19 @@ class InputModel {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOpenView(): string {
|
||||||
|
if (this.historyShow.get()) {
|
||||||
|
return "history";
|
||||||
|
}
|
||||||
|
if (this.aIChatShow.get()) {
|
||||||
|
return "aichat";
|
||||||
|
}
|
||||||
|
if (this.infoShow.get()) {
|
||||||
|
return "info";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
setHistoryType(htype: HistoryTypeStrs): void {
|
setHistoryType(htype: HistoryTypeStrs): void {
|
||||||
if (this.historyQueryOpts.get().queryType == htype) {
|
if (this.historyQueryOpts.get().queryType == htype) {
|
||||||
return;
|
return;
|
||||||
@ -201,20 +215,19 @@ class InputModel {
|
|||||||
if (oldItem == null) {
|
if (oldItem == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
let newItems = this.getFilteredHistoryItems();
|
const newItems = this.getFilteredHistoryItems();
|
||||||
if (newItems.length == 0) {
|
if (newItems.length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
let bestIdx = 0;
|
let bestIdx = 0;
|
||||||
for (let i = 0; i < newItems.length; i++) {
|
for (const [i, item] of newItems.entries()) {
|
||||||
// still start at i=0 to catch the historynum equality case
|
// still start at i=0 to catch the historynum equality case
|
||||||
let item = newItems[i];
|
|
||||||
if (item.historynum == oldItem.historynum) {
|
if (item.historynum == oldItem.historynum) {
|
||||||
bestIdx = i;
|
bestIdx = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let bestTsDiff = Math.abs(item.ts - newItems[bestIdx].ts);
|
const bestTsDiff = Math.abs(item.ts - newItems[bestIdx].ts);
|
||||||
let curTsDiff = Math.abs(item.ts - oldItem.ts);
|
const curTsDiff = Math.abs(item.ts - oldItem.ts);
|
||||||
if (curTsDiff < bestTsDiff) {
|
if (curTsDiff < bestTsDiff) {
|
||||||
bestIdx = i;
|
bestIdx = i;
|
||||||
}
|
}
|
||||||
@ -224,9 +237,9 @@ class InputModel {
|
|||||||
|
|
||||||
setHistoryQueryOpts(opts: HistoryQueryOpts): void {
|
setHistoryQueryOpts(opts: HistoryQueryOpts): void {
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
let oldItem = this.getHistorySelectedItem();
|
const oldItem = this.getHistorySelectedItem();
|
||||||
this.historyQueryOpts.set(opts);
|
this.historyQueryOpts.set(opts);
|
||||||
let bestIndex = this.findBestNewIndex(oldItem);
|
const bestIndex = this.findBestNewIndex(oldItem);
|
||||||
setTimeout(() => this.setHistoryIndex(bestIndex, true), 10);
|
setTimeout(() => this.setHistoryIndex(bestIndex, true), 10);
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
@ -253,17 +266,28 @@ class InputModel {
|
|||||||
this.setInputPopUpType("none");
|
this.setInputPopUpType("none");
|
||||||
}
|
}
|
||||||
this.historyShow.set(show);
|
this.historyShow.set(show);
|
||||||
|
this.historyFocus.set(show);
|
||||||
if (this.hasFocus()) {
|
if (this.hasFocus()) {
|
||||||
this.giveFocus();
|
this.giveFocus();
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setHistoryFocus(focus: boolean): void {
|
||||||
|
if (this.historyFocus.get() == focus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mobx.action(() => {
|
||||||
|
this.historyFocus.set(focus);
|
||||||
|
this.giveFocus();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
isHistoryLoaded(): boolean {
|
isHistoryLoaded(): boolean {
|
||||||
if (this.historyLoading.get()) {
|
if (this.historyLoading.get()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let hitems = this.historyItems.get();
|
const hitems = this.historyItems.get();
|
||||||
return hitems != null;
|
return hitems != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,6 +318,7 @@ class InputModel {
|
|||||||
if (!this.historyShow.get()) {
|
if (!this.historyShow.get()) {
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
this.setHistoryShow(true);
|
this.setHistoryShow(true);
|
||||||
|
this.aIChatShow.set(false);
|
||||||
this.infoShow.set(false);
|
this.infoShow.set(false);
|
||||||
this.dropModHistory(true);
|
this.dropModHistory(true);
|
||||||
this.giveFocus();
|
this.giveFocus();
|
||||||
@ -311,11 +336,11 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getHistorySelectedItem(): HistoryItem {
|
getHistorySelectedItem(): HistoryItem {
|
||||||
let hidx = this.historyIndex.get();
|
const hidx = this.historyIndex.get();
|
||||||
if (hidx == 0) {
|
if (hidx == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let hitems = this.getFilteredHistoryItems();
|
const hitems = this.getFilteredHistoryItems();
|
||||||
if (hidx > hitems.length) {
|
if (hidx > hitems.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -323,7 +348,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFirstHistoryItem(): HistoryItem {
|
getFirstHistoryItem(): HistoryItem {
|
||||||
let hitems = this.getFilteredHistoryItems();
|
const hitems = this.getFilteredHistoryItems();
|
||||||
if (hitems.length == 0) {
|
if (hitems.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -331,9 +356,9 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setHistorySelectionNum(hnum: string): void {
|
setHistorySelectionNum(hnum: string): void {
|
||||||
let hitems = this.getFilteredHistoryItems();
|
const hitems = this.getFilteredHistoryItems();
|
||||||
for (let i = 0; i < hitems.length; i++) {
|
for (const [i, hitem] of hitems.entries()) {
|
||||||
if (hitems[i].historynum == hnum) {
|
if (hitem.historynum == hnum) {
|
||||||
this.setHistoryIndex(i + 1);
|
this.setHistoryIndex(i + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -342,8 +367,8 @@ class InputModel {
|
|||||||
|
|
||||||
setHistoryInfo(hinfo: HistoryInfoType): void {
|
setHistoryInfo(hinfo: HistoryInfoType): void {
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
let oldItem = this.getHistorySelectedItem();
|
const oldItem = this.getHistorySelectedItem();
|
||||||
let hitems: HistoryItem[] = hinfo.items ?? [];
|
const hitems: HistoryItem[] = hinfo.items ?? [];
|
||||||
this.historyItems.set(hitems);
|
this.historyItems.set(hitems);
|
||||||
this.historyLoading.set(false);
|
this.historyLoading.set(false);
|
||||||
this.historyQueryOpts.get().queryType = hinfo.historytype;
|
this.historyQueryOpts.get().queryType = hinfo.historytype;
|
||||||
@ -352,7 +377,7 @@ class InputModel {
|
|||||||
this.historyQueryOpts.get().limitRemoteInstance = false;
|
this.historyQueryOpts.get().limitRemoteInstance = false;
|
||||||
}
|
}
|
||||||
if (this.historyAfterLoadIndex == -1) {
|
if (this.historyAfterLoadIndex == -1) {
|
||||||
let bestIndex = this.findBestNewIndex(oldItem);
|
const bestIndex = this.findBestNewIndex(oldItem);
|
||||||
setTimeout(() => this.setHistoryIndex(bestIndex, true), 100);
|
setTimeout(() => this.setHistoryIndex(bestIndex, true), 100);
|
||||||
} else if (this.historyAfterLoadIndex) {
|
} else if (this.historyAfterLoadIndex) {
|
||||||
if (hitems.length >= this.historyAfterLoadIndex) {
|
if (hitems.length >= this.historyAfterLoadIndex) {
|
||||||
@ -371,10 +396,10 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_getFilteredHistoryItems(): HistoryItem[] {
|
_getFilteredHistoryItems(): HistoryItem[] {
|
||||||
let hitems: HistoryItem[] = this.historyItems.get() ?? [];
|
const hitems: HistoryItem[] = this.historyItems.get() ?? [];
|
||||||
let rtn: HistoryItem[] = [];
|
const rtn: HistoryItem[] = [];
|
||||||
let opts = mobx.toJS(this.historyQueryOpts.get());
|
const opts: HistoryQueryOpts = mobx.toJS(this.historyQueryOpts.get());
|
||||||
let ctx = this.globalModel.getUIContext();
|
const ctx = this.globalModel.getUIContext();
|
||||||
let curRemote: RemotePtrType = ctx.remote;
|
let curRemote: RemotePtrType = ctx.remote;
|
||||||
if (curRemote == null) {
|
if (curRemote == null) {
|
||||||
curRemote = { ownerid: "", name: "", remoteid: "" };
|
curRemote = { ownerid: "", name: "", remoteid: "" };
|
||||||
@ -411,7 +436,7 @@ class InputModel {
|
|||||||
if (isBlank(hitem.cmdstr)) {
|
if (isBlank(hitem.cmdstr)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let idx = hitem.cmdstr.indexOf(opts.queryStr);
|
const idx = hitem.cmdstr.indexOf(opts.queryStr);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -423,24 +448,24 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scrollHistoryItemIntoView(hnum: string): void {
|
scrollHistoryItemIntoView(hnum: string): void {
|
||||||
let elem: HTMLElement = document.querySelector(".cmd-history .hnum-" + hnum);
|
const elem: HTMLElement = document.querySelector(".cmd-history .hnum-" + hnum);
|
||||||
if (elem == null) {
|
if (elem == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let historyDiv = elem.closest(".cmd-history");
|
const historyDiv = elem.closest(".cmd-history");
|
||||||
if (historyDiv == null) {
|
if (historyDiv == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let buffer = 15;
|
const buffer = 15;
|
||||||
let titleHeight = 24;
|
let titleHeight = 24;
|
||||||
let titleDiv: HTMLElement = document.querySelector(".cmd-history .history-title");
|
const titleDiv: HTMLElement = document.querySelector(".cmd-history .history-title");
|
||||||
if (titleDiv != null) {
|
if (titleDiv != null) {
|
||||||
titleHeight = titleDiv.offsetHeight + 2;
|
titleHeight = titleDiv.offsetHeight + 2;
|
||||||
}
|
}
|
||||||
let elemOffset = elem.offsetTop;
|
const elemOffset = elem.offsetTop;
|
||||||
let elemHeight = elem.clientHeight;
|
const elemHeight = elem.clientHeight;
|
||||||
let topPos = historyDiv.scrollTop;
|
const topPos = historyDiv.scrollTop;
|
||||||
let endPos = topPos + historyDiv.clientHeight;
|
const endPos = topPos + historyDiv.clientHeight;
|
||||||
if (elemOffset + elemHeight + buffer > endPos) {
|
if (elemOffset + elemHeight + buffer > endPos) {
|
||||||
if (elemHeight + buffer > historyDiv.clientHeight - titleHeight) {
|
if (elemHeight + buffer > historyDiv.clientHeight - titleHeight) {
|
||||||
historyDiv.scrollTop = elemOffset - titleHeight;
|
historyDiv.scrollTop = elemOffset - titleHeight;
|
||||||
@ -459,7 +484,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
grabSelectedHistoryItem(): void {
|
grabSelectedHistoryItem(): void {
|
||||||
let hitem = this.getHistorySelectedItem();
|
const hitem = this.getHistorySelectedItem();
|
||||||
if (hitem == null) {
|
if (hitem == null) {
|
||||||
this.resetHistory();
|
this.resetHistory();
|
||||||
return;
|
return;
|
||||||
@ -498,9 +523,8 @@ class InputModel {
|
|||||||
if (!this.isHistoryLoaded()) {
|
if (!this.isHistoryLoaded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let hitems = this.getFilteredHistoryItems();
|
const hitems = this.getFilteredHistoryItems();
|
||||||
let idx = this.historyIndex.get();
|
let idx = this.historyIndex.get() + amt;
|
||||||
idx += amt;
|
|
||||||
if (idx < 0) {
|
if (idx < 0) {
|
||||||
idx = 0;
|
idx = 0;
|
||||||
}
|
}
|
||||||
@ -540,7 +564,6 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setAIChatFocus() {
|
setAIChatFocus() {
|
||||||
console.log("setting ai chat focus");
|
|
||||||
if (this.aiChatTextAreaRef?.current != null) {
|
if (this.aiChatTextAreaRef?.current != null) {
|
||||||
this.aiChatTextAreaRef.current.focus();
|
this.aiChatTextAreaRef.current.focus();
|
||||||
}
|
}
|
||||||
@ -551,9 +574,8 @@ class InputModel {
|
|||||||
this.codeSelectSelectedIndex.get() >= 0 &&
|
this.codeSelectSelectedIndex.get() >= 0 &&
|
||||||
this.codeSelectSelectedIndex.get() < this.codeSelectBlockRefArray.length
|
this.codeSelectSelectedIndex.get() < this.codeSelectBlockRefArray.length
|
||||||
) {
|
) {
|
||||||
let curBlockRef = this.codeSelectBlockRefArray[this.codeSelectSelectedIndex.get()];
|
const curBlockRef = this.codeSelectBlockRefArray[this.codeSelectSelectedIndex.get()];
|
||||||
let codeText = curBlockRef.current.innerText;
|
const codeText = curBlockRef.current.innerText.replace(/\n$/, ""); // remove trailing newline
|
||||||
codeText = codeText.replace(/\n$/, ""); // remove trailing newline
|
|
||||||
this.setCurLine(codeText);
|
this.setCurLine(codeText);
|
||||||
this.giveFocus();
|
this.giveFocus();
|
||||||
}
|
}
|
||||||
@ -574,14 +596,13 @@ class InputModel {
|
|||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
if (blockIndex >= 0 && blockIndex < this.codeSelectBlockRefArray.length) {
|
if (blockIndex >= 0 && blockIndex < this.codeSelectBlockRefArray.length) {
|
||||||
this.codeSelectSelectedIndex.set(blockIndex);
|
this.codeSelectSelectedIndex.set(blockIndex);
|
||||||
let currentRef = this.codeSelectBlockRefArray[blockIndex].current;
|
const currentRef = this.codeSelectBlockRefArray[blockIndex].current;
|
||||||
if (currentRef != null) {
|
if (currentRef != null && this.aiChatWindowRef?.current != null) {
|
||||||
if (this.aiChatWindowRef?.current != null) {
|
const chatWindowTop = this.aiChatWindowRef.current.scrollTop;
|
||||||
let chatWindowTop = this.aiChatWindowRef.current.scrollTop;
|
const chatWindowBottom = chatWindowTop + this.aiChatWindowRef.current.clientHeight - 100;
|
||||||
let chatWindowBottom = chatWindowTop + this.aiChatWindowRef.current.clientHeight - 100;
|
const elemTop = currentRef.offsetTop;
|
||||||
let elemTop = currentRef.offsetTop;
|
|
||||||
let elemBottom = elemTop - currentRef.offsetHeight;
|
let elemBottom = elemTop - currentRef.offsetHeight;
|
||||||
let elementIsInView = elemBottom < chatWindowBottom && elemTop > chatWindowTop;
|
const elementIsInView = elemBottom < chatWindowBottom && elemTop > chatWindowTop;
|
||||||
if (!elementIsInView) {
|
if (!elementIsInView) {
|
||||||
this.aiChatWindowRef.current.scrollTop =
|
this.aiChatWindowRef.current.scrollTop =
|
||||||
elemBottom - this.aiChatWindowRef.current.clientHeight / 3;
|
elemBottom - this.aiChatWindowRef.current.clientHeight / 3;
|
||||||
@ -590,7 +611,6 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
this.codeSelectBlockRefArray = [];
|
this.codeSelectBlockRefArray = [];
|
||||||
this.setAIChatFocus();
|
this.setAIChatFocus();
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,7 +623,7 @@ class InputModel {
|
|||||||
} else if (this.codeSelectSelectedIndex.get() == this.codeSelectBottom) {
|
} else if (this.codeSelectSelectedIndex.get() == this.codeSelectBottom) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let incBlockIndex = this.codeSelectSelectedIndex.get() + 1;
|
const incBlockIndex = this.codeSelectSelectedIndex.get() + 1;
|
||||||
if (this.codeSelectSelectedIndex.get() == this.codeSelectBlockRefArray.length - 1) {
|
if (this.codeSelectSelectedIndex.get() == this.codeSelectBlockRefArray.length - 1) {
|
||||||
this.codeSelectDeselectAll();
|
this.codeSelectDeselectAll();
|
||||||
if (this.aiChatWindowRef?.current != null) {
|
if (this.aiChatWindowRef?.current != null) {
|
||||||
@ -627,7 +647,7 @@ class InputModel {
|
|||||||
} else if (this.codeSelectSelectedIndex.get() == this.codeSelectTop) {
|
} else if (this.codeSelectSelectedIndex.get() == this.codeSelectTop) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let decBlockIndex = this.codeSelectSelectedIndex.get() - 1;
|
const decBlockIndex = this.codeSelectSelectedIndex.get() - 1;
|
||||||
if (decBlockIndex < 0) {
|
if (decBlockIndex < 0) {
|
||||||
this.codeSelectDeselectAll(this.codeSelectTop);
|
this.codeSelectDeselectAll(this.codeSelectTop);
|
||||||
if (this.aiChatWindowRef?.current != null) {
|
if (this.aiChatWindowRef?.current != null) {
|
||||||
@ -666,6 +686,8 @@ class InputModel {
|
|||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
this.setInputPopUpType("aichat");
|
this.setInputPopUpType("aichat");
|
||||||
this.aIChatShow.set(true);
|
this.aIChatShow.set(true);
|
||||||
|
this.historyShow.set(false);
|
||||||
|
this.infoShow.set(false);
|
||||||
this.setAIChatFocus();
|
this.setAIChatFocus();
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
@ -686,7 +708,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearAIAssistantChat(): void {
|
clearAIAssistantChat(): void {
|
||||||
let prtn = this.globalModel.submitChatInfoCommand("", "", true);
|
const prtn = this.globalModel.submitChatInfoCommand("", "", true);
|
||||||
prtn.then((rtn) => {
|
prtn.then((rtn) => {
|
||||||
if (!rtn.success) {
|
if (!rtn.success) {
|
||||||
console.log("submit chat command error: " + rtn.error);
|
console.log("submit chat command error: " + rtn.error);
|
||||||
@ -700,11 +722,11 @@ class InputModel {
|
|||||||
if (!this.infoShow.get()) {
|
if (!this.infoShow.get()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let info = this.infoMsg.get();
|
const info = this.infoMsg.get();
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let div = document.querySelector(".cmd-input-info");
|
const div = document.querySelector(".cmd-input-info");
|
||||||
if (div == null) {
|
if (div == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -736,7 +758,7 @@ class InputModel {
|
|||||||
this.setHistoryShow(false);
|
this.setHistoryShow(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let isShowing = this.infoShow.get();
|
const isShowing = this.infoShow.get();
|
||||||
if (isShowing) {
|
if (isShowing) {
|
||||||
this.infoShow.set(false);
|
this.infoShow.set(false);
|
||||||
} else {
|
} else {
|
||||||
@ -750,7 +772,7 @@ class InputModel {
|
|||||||
@boundMethod
|
@boundMethod
|
||||||
uiSubmitCommand(): void {
|
uiSubmitCommand(): void {
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
let commandStr = this.getCurLine();
|
const commandStr = this.getCurLine();
|
||||||
if (commandStr.trim() == "") {
|
if (commandStr.trim() == "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -771,7 +793,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCurLine(val: string): void {
|
setCurLine(val: string): void {
|
||||||
let hidx = this.historyIndex.get();
|
const hidx = this.historyIndex.get();
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
if (this.modHistory.length <= hidx) {
|
if (this.modHistory.length <= hidx) {
|
||||||
this.modHistory.length = hidx + 1;
|
this.modHistory.length = hidx + 1;
|
||||||
@ -803,15 +825,15 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getCurLine(): string {
|
getCurLine(): string {
|
||||||
let hidx = this.historyIndex.get();
|
const hidx = this.historyIndex.get();
|
||||||
if (hidx < this.modHistory.length && this.modHistory[hidx] != null) {
|
if (hidx < this.modHistory.length && this.modHistory[hidx] != null) {
|
||||||
return this.modHistory[hidx];
|
return this.modHistory[hidx];
|
||||||
}
|
}
|
||||||
let hitems = this.getFilteredHistoryItems();
|
const hitems = this.getFilteredHistoryItems();
|
||||||
if (hidx == 0 || hitems == null || hidx > hitems.length) {
|
if (hidx == 0 || hitems == null || hidx > hitems.length) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
let hitem = hitems[hidx - 1];
|
const hitem = hitems[hidx - 1];
|
||||||
if (hitem == null) {
|
if (hitem == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ function getMonoFontSize(fontSize: number): MonoFontSize {
|
|||||||
if (MonoFontSizes[fontSize] != null) {
|
if (MonoFontSizes[fontSize] != null) {
|
||||||
return MonoFontSizes[fontSize];
|
return MonoFontSizes[fontSize];
|
||||||
}
|
}
|
||||||
let size = measureText("W", { pre: true, mono: true, fontSize: fontSize });
|
const size = measureText("W", { pre: true, mono: true, fontSize: fontSize });
|
||||||
if (size.height != 0 && size.width != 0) {
|
if (size.height != 0 && size.width != 0) {
|
||||||
MonoFontSizes[fontSize] = size;
|
MonoFontSizes[fontSize] = size;
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ function measureText(text: string, textOpts: { pre?: boolean; mono?: boolean; fo
|
|||||||
if (textOpts == null) {
|
if (textOpts == null) {
|
||||||
throw new Error("invalid textOpts passed to measureText (null)");
|
throw new Error("invalid textOpts passed to measureText (null)");
|
||||||
}
|
}
|
||||||
let textElem = document.createElement("span");
|
const textElem = document.createElement("span");
|
||||||
if (textOpts.pre) {
|
if (textOpts.pre) {
|
||||||
textElem.classList.add("pre");
|
textElem.classList.add("pre");
|
||||||
}
|
}
|
||||||
@ -53,19 +53,19 @@ function measureText(text: string, textOpts: { pre?: boolean; mono?: boolean; fo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
textElem.innerText = text;
|
textElem.innerText = text;
|
||||||
let measureDiv = document.getElementById("measure");
|
const measureDiv = document.getElementById("measure");
|
||||||
if (measureDiv == null) {
|
if (measureDiv == null) {
|
||||||
throw new Error("cannot measure text, no #measure div");
|
throw new Error("cannot measure text, no #measure div");
|
||||||
}
|
}
|
||||||
measureDiv.replaceChildren(textElem);
|
measureDiv.replaceChildren(textElem);
|
||||||
let height = Math.ceil(textElem.offsetHeight);
|
const height = Math.ceil(textElem.offsetHeight);
|
||||||
let width = textElem.offsetWidth;
|
const width = textElem.offsetWidth;
|
||||||
let pad = Math.floor(height / 2);
|
const pad = Math.floor(height / 2);
|
||||||
return { width, height, pad, fontSize: textOpts.fontSize };
|
return { width, height, pad, fontSize: textOpts.fontSize };
|
||||||
}
|
}
|
||||||
|
|
||||||
function windowWidthToCols(width: number, fontSize: number): number {
|
function windowWidthToCols(width: number, fontSize: number): number {
|
||||||
let dr = getMonoFontSize(fontSize);
|
const dr = getMonoFontSize(fontSize);
|
||||||
let cols = Math.trunc((width - MagicLayout.ScreenMaxContentWidthBuffer) / dr.width) - 1;
|
let cols = Math.trunc((width - MagicLayout.ScreenMaxContentWidthBuffer) / dr.width) - 1;
|
||||||
cols = boundInt(cols, MinTermCols, MaxTermCols);
|
cols = boundInt(cols, MinTermCols, MaxTermCols);
|
||||||
return cols;
|
return cols;
|
||||||
@ -80,7 +80,7 @@ function windowHeightToRows(lhe: LineHeightEnv, height: number): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function termWidthFromCols(cols: number, fontSize: number): number {
|
function termWidthFromCols(cols: number, fontSize: number): number {
|
||||||
let dr = getMonoFontSize(fontSize);
|
const dr = getMonoFontSize(fontSize);
|
||||||
return Math.ceil(dr.width * cols) + MagicLayout.TermWidthBuffer;
|
return Math.ceil(dr.width * cols) + MagicLayout.TermWidthBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,12 +89,12 @@ function termWidthFromCols(cols: number, fontSize: number): number {
|
|||||||
// works out to `realHeight = round(ceil(height * dpr) * rows / dpr) / rows`
|
// works out to `realHeight = round(ceil(height * dpr) * rows / dpr) / rows`
|
||||||
// their calculation is based off the "totalRows" (so that argument has been added)
|
// their calculation is based off the "totalRows" (so that argument has been added)
|
||||||
function termHeightFromRows(rows: number, fontSize: number, totalRows: number): number {
|
function termHeightFromRows(rows: number, fontSize: number, totalRows: number): number {
|
||||||
let dr = getMonoFontSize(fontSize);
|
const dr = getMonoFontSize(fontSize);
|
||||||
const dpr = window.devicePixelRatio;
|
const dpr = window.devicePixelRatio;
|
||||||
if (totalRows == null || totalRows == 0) {
|
if (totalRows == null || totalRows == 0) {
|
||||||
totalRows = rows > 25 ? rows : 25;
|
totalRows = rows > 25 ? rows : 25;
|
||||||
}
|
}
|
||||||
let realHeight = Math.round((Math.ceil(dr.height * dpr) * totalRows) / dpr) / totalRows;
|
const realHeight = Math.round((Math.ceil(dr.height * dpr) * totalRows) / dpr) / totalRows;
|
||||||
return Math.ceil(realHeight * rows);
|
return Math.ceil(realHeight * rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user