Compare commits

..

2 Commits

Author SHA1 Message Date
Evan Simkowitz e5a168f34c making textareainput functional component 2024-04-08 15:48:05 -07:00
Evan Simkowitz cc547e526b save 2024-04-08 14:20:10 -07:00
89 changed files with 1747 additions and 5258 deletions
+40 -22
View File
@@ -107,12 +107,12 @@
{
"command": "app:openConnectionsView",
"keys": [],
"commandStr": ["/mainview connections"]
"commandStr": "/mainview connections"
},
{
"command": "app:openSettingsView",
"keys": [],
"commandStr": ["/mainview clientsettings"]
"commandStr": "/mainview clientsettings"
},
{
"command": "app:newTab",
@@ -149,39 +149,48 @@
},
{
"command": "app:selectTab-1",
"keys": ["Cmd:1"]
"keys": ["Cmd:1"],
"commandStr":"/screen 1"
},
{
"command": "app:selectTab-2",
"keys": ["Cmd:2"]
"keys": ["Cmd:2"],
"commandStr":"/screen 2"
},
{
"command": "app:selectTab-3",
"keys": ["Cmd:3"]
"keys": ["Cmd:3"],
"commandStr":"/screen 3"
},
{
"command": "app:selectTab-4",
"keys": ["Cmd:4"]
"keys": ["Cmd:4"],
"commandStr":"/screen 4"
},
{
"command": "app:selectTab-5",
"keys": ["Cmd:5"]
"keys": ["Cmd:5"],
"commandStr":"/screen 5"
},
{
"command": "app:selectTab-6",
"keys": ["Cmd:6"]
"keys": ["Cmd:6"],
"commandStr":"/screen 6"
},
{
"command": "app:selectTab-7",
"keys": ["Cmd:7"]
"keys": ["Cmd:7"],
"commandStr":"/screen 7"
},
{
"command": "app:selectTab-8",
"keys": ["Cmd:8"]
"keys": ["Cmd:8"],
"commandStr":"/screen 8"
},
{
"command": "app:selectTab-9",
"keys": ["Cmd:9"]
"keys": ["Cmd:9"],
"commandStr":"/screen 9"
},
{
"command": "app:selectTabLeft",
@@ -193,39 +202,48 @@
},
{
"command": "app:selectWorkspace-1",
"keys": ["Cmd:Ctrl:1"]
"keys": ["Cmd:Ctrl:1"],
"commandStr": "/session 1"
},
{
"command": "app:selectWorkspace-2",
"keys": ["Cmd:Ctrl:2"]
"keys": ["Cmd:Ctrl:2"],
"commandStr": "/session 2"
},
{
"command": "app:selectWorkspace-3",
"keys": ["Cmd:Ctrl:3"]
"keys": ["Cmd:Ctrl:3"],
"commandStr": "/session 3"
},
{
"command": "app:selectWorkspace-4",
"keys": ["Cmd:Ctrl:4"]
"keys": ["Cmd:Ctrl:4"],
"commandStr": "/session 4"
},
{
"command": "app:selectWorkspace-5",
"keys": ["Cmd:Ctrl:5"]
"keys": ["Cmd:Ctrl:5"],
"commandStr": "/session 5"
},
{
"command": "app:selectWorkspace-6",
"keys": ["Cmd:Ctrl:6"]
"keys": ["Cmd:Ctrl:6"],
"commandStr": "/session 6"
},
{
"command": "app:selectWorkspace-7",
"keys": ["Cmd:Ctrl:7"]
"keys": ["Cmd:Ctrl:7"],
"commandStr": "/session 7"
},
{
"command": "app:selectWorkspace-8",
"keys": ["Cmd:Ctrl:8"]
"keys": ["Cmd:Ctrl:8"],
"commandStr": "/session 8"
},
{
"command": "app:selectWorkspace-9",
"keys": ["Cmd:Ctrl:9"]
"keys": ["Cmd:Ctrl:9"],
"commandStr": "/session 9"
},
{
"command": "app:toggleSidebar",
@@ -238,7 +256,7 @@
{
"command": "app:openBookmarksView",
"keys": ["Cmd:b"],
"commandStr": ["/bookmarks:show"]
"commandStr": "/bookmarks:show"
},
{
"command": "bookmarks:edit",
@@ -283,7 +301,7 @@
{
"command": "cmdinput:openHistory",
"keys": ["Ctrl:r"],
"commandStr": ["/history"]
"commandStr": "/history"
},
{
"command": "cmdinput:openAIChat",
+1 -6
View File
@@ -18,7 +18,6 @@
"appId": "dev.commandline.waveterm"
},
"dependencies": {
"@lexical/react": "^0.14.3",
"@monaco-editor/react": "^4.5.1",
"@table-nav/core": "^0.0.7",
"@table-nav/react": "^0.0.7",
@@ -33,7 +32,6 @@
"electron-squirrel-startup": "^1.0.0",
"electron-updater": "^6.1.8",
"framer-motion": "^10.16.16",
"lexical": "^0.14.3",
"mobx": "6.12",
"mobx-react": "^7.5.0",
"monaco-editor": "^0.44.0",
@@ -47,15 +45,14 @@
"react-markdown": "^9.0.0",
"remark": "^15.0.1",
"remark-gfm": "^4.0.0",
"shell-quote": "^1.8.1",
"sprintf-js": "^1.1.2",
"throttle-debounce": "^5.0.0",
"tsx-control-statements": "^5.1.1",
"uuid": "^9.0.0",
"winston": "^3.8.2",
"xterm": "^5.3.0",
"xterm-addon-serialize": "^0.11.0",
"xterm-addon-web-links": "^0.9.0",
"xterm-addon-serialize": "^0.11.0",
"xterm-addon-webgl": "^0.16.0"
},
"devDependencies": {
@@ -70,7 +67,6 @@
"@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.17.12",
"@electron/rebuild": "^3.6.0",
"@svgr/webpack": "^8.1.0",
"@types/classnames": "^2.3.1",
"@types/electron": "^1.6.10",
@@ -78,7 +74,6 @@
"@types/papaparse": "^5.3.10",
"@types/react": "^18.0.12",
"@types/semver": "^7.5.6",
"@types/shell-quote": "^1.7.5",
"@types/sprintf-js": "^1.1.3",
"@types/throttle-debounce": "^5.0.1",
"@types/uuid": "^9.0.7",
+2 -2
View File
@@ -9,8 +9,8 @@
--app-accent-color: rgb(75, 166, 57);
--app-accent-bg-color: rgba(75, 166, 57, 0.2);
--app-text-color: rgb(0, 0, 0);
--app-text-primary-color: rgb(23, 23, 23);
--app-text-secondary-color: rgb(76, 76, 76);
--app-text-primary-color: rgb(0, 0, 0, 0.9);
--app-text-secondary-color: rgb(0, 0, 0, 0.7);
--app-border-color: rgb(139 145 138);
--app-panel-bg-color: rgb(224, 224, 224);
--app-panel-bg-color-dev: rgb(224, 224, 224);
+1 -1
View File
@@ -146,7 +146,7 @@ class App extends React.Component<{}, {}> {
<If condition={GlobalModel.isDev && rightSidebarCollapsed && activeMainView == "session"}>
<div className="right-sidebar-triggers">
<Button className="secondary ghost right-sidebar-trigger" onClick={this.openRightSidebar}>
<i className="fa-sharp fa-solid fa-sidebar-flip"></i>
<i className="fa-sharp fa-regular fa-lightbulb"></i>
</Button>
</div>
</If>
-1
View File
@@ -68,4 +68,3 @@ export const ErrorCode_InvalidCwd = "ERRCWD";
export const InputAuxView_History = "history";
export const InputAuxView_Info = "info";
export const InputAuxView_AIChat = "aichat";
export const InputAuxView_Suggestions = "suggestions";
+5 -36
View File
@@ -6,14 +6,14 @@ import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator";
import { If } from "tsx-control-statements/components";
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "@/common/elements";
import { GlobalModel, GlobalCommandRunner, RemotesModel, getApi } from "@/models";
import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown, Button } from "@/common/elements";
import { commandRtnHandler, isBlank } from "@/util/util";
import { getTermThemes } from "@/util/themeutil";
import * as appconst from "@/app/appconst";
import { MainView } from "@/common/elements/mainview";
import "./clientsettings.less";
import { MainView } from "@/common/elements/mainview";
class ClientSettingsKeybindings extends React.Component<{}, {}> {
componentDidMount() {
@@ -70,7 +70,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
return;
}
const prtn = GlobalCommandRunner.setTheme(themeSource, false);
GlobalModel.getElectronApi().setNativeThemeSource(themeSource);
getApi().setNativeThemeSource(themeSource);
commandRtnHandler(prtn, this.errorMessage);
}
@@ -107,20 +107,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
prtn = GlobalCommandRunner.releaseCheckAutoOff(false);
}
commandRtnHandler(prtn, this.errorMessage);
GlobalModel.getElectronApi().changeAutoUpdate(val);
}
@boundMethod
handleChangeAutocompleteEnabled(val: boolean): void {
const prtn: Promise<CommandRtnType> = GlobalCommandRunner.setAutocompleteEnabled(val);
commandRtnHandler(prtn, this.errorMessage);
}
@boundMethod
handleChangeAutocompleteDebuggingEnabled(val: boolean): void {
mobx.action(() => {
GlobalModel.autocompleteModel.loggingEnabled = val;
})();
getApi().changeAutoUpdate(val);
}
getFontSizes(): DropdownItem[] {
@@ -367,24 +354,6 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
/>
</div>
</div>
<div className="settings-field">
<div className="settings-label">Command Autocomplete</div>
<div className="settings-input">
<Toggle
checked={cdata.clientopts.autocompleteenabled ?? false}
onChange={this.handleChangeAutocompleteEnabled}
/>
</div>
</div>
<div className="settings-field">
<div className="settings-label">Command Autocomplete Debugging</div>
<div className="settings-input">
<Toggle
checked={GlobalModel.autocompleteModel.loggingEnabled}
onChange={this.handleChangeAutocompleteDebuggingEnabled}
/>
</div>
</div>
<SettingsError errorMessage={this.errorMessage} />
</div>
</MainView>
+2 -5
View File
@@ -5,7 +5,7 @@
width: 100%;
border: 2px solid var(--form-element-border-color);
border-radius: 6px;
line-height: 20px;
line-height: 22px;
background: var(--form-element-bg-color);
&.no-label {
@@ -63,14 +63,11 @@
display: flex;
min-width: 120px;
padding: 5px 8px;
justify-content: space-between;
align-items: center;
align-self: stretch;
border-radius: 6px;
i {
margin-right: 8px;
}
&-highlighted,
&:hover {
background: rgba(241, 246, 243, 0.08);
+6 -8
View File
@@ -163,7 +163,7 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
if (isOpen) {
const option = options[highlightedIndex];
if (option) {
this.handleSelect(option, undefined);
this.handleSelect(option.value, undefined);
}
} else {
this.toggleDropdown();
@@ -185,19 +185,18 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
handleKeyDown(event: React.KeyboardEvent) {}
@boundMethod
handleSelect({ value, noop }: DropdownItem, event?: React.MouseEvent | React.KeyboardEvent) {
handleSelect(value: string, event?: React.MouseEvent | React.KeyboardEvent) {
const { onChange } = this.props;
if (event) {
event.stopPropagation(); // This stops the event from bubbling up to the wrapper
}
if (!("value" in this.props)) {
this.setState({ internalValue: value });
}
onChange(value);
this.setState({ isOpen: false, isTouched: true });
this.unregisterKeybindings();
if (!("value" in this.props) && !noop) {
this.setState({ internalValue: value });
}
}
@boundMethod
@@ -246,11 +245,10 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
className={cn("wave-dropdown-item unselectable", {
"wave-dropdown-item-highlighted": index === highlightedIndex,
})}
onClick={(e) => this.handleSelect(option, e)}
onClick={(e) => this.handleSelect(option.value, e)}
onMouseEnter={() => this.setState({ highlightedIndex: index })}
onMouseLeave={() => this.setState({ highlightedIndex: -1 })}
>
{option.icon && <span className="wave-dropdown-item-icon">{option.icon}</span>}
{option.label}
</div>
))}
@@ -131,7 +131,7 @@ class InlineSettingsTextEdit extends React.Component<
</div>
<div className="control">
<div
onMouseDown={this.cancelChange}
onClick={this.cancelChange}
title="Cancel (Esc)"
className="button is-prompt-danger is-outlined is-small"
>
@@ -142,7 +142,7 @@ class InlineSettingsTextEdit extends React.Component<
</div>
<div className="control">
<div
onMouseDown={this.confirmChange}
onClick={this.confirmChange}
title="Confirm (Enter)"
className="button is-wave-green is-outlined is-small"
>
+2 -2
View File
@@ -5,7 +5,7 @@ import * as React from "react";
import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator";
import { GlobalModel } from "@/models";
import { GlobalModel, getApi } from "@/models";
import { Modal, LinkButton } from "@/elements";
import * as util from "@/util/util";
import * as appconst from "@/app/appconst";
@@ -26,7 +26,7 @@ class AboutModal extends React.Component<{}, {}> {
@boundMethod
updateApp(): void {
GlobalModel.getElectronApi().installAppUpdate();
getApi().installAppUpdate();
}
@boundMethod
-4
View File
@@ -58,8 +58,4 @@
.term-prompt-python {
color: var(--term-magenta);
}
.term-prompt-k8s {
color: var(--term-magenta);
}
}
+1 -12
View File
@@ -146,7 +146,6 @@ class Prompt extends React.Component<
let branchElem = null;
let pythonElem = null;
let condaElem = null;
let k8sElem = null;
if (!isBlank(festate["PROMPTVAR_GITBRANCH"])) {
const branchName = festate["PROMPTVAR_GITBRANCH"];
branchElem = (
@@ -172,19 +171,9 @@ class Prompt extends React.Component<
</span>
);
}
if (!isBlank(festate["K8SCONTEXT"])) {
const k8sContext = festate["K8SCONTEXT"];
const k8sNs = festate["K8SNAMESPACE"];
k8sElem = (
<span title="k8s context:namespace" className="term-prompt-k8s">
k8s:({k8sContext}
{isBlank(k8sNs) ? "" : ":" + k8sNs}){" "}
</span>
);
}
return (
<span className={termClassNames}>
{remoteElem} {cwdElem} {branchElem} {condaElem} {pythonElem} {k8sElem}
{remoteElem} {cwdElem} {branchElem} {condaElem} {pythonElem}
</span>
);
}
-78
View File
@@ -665,83 +665,6 @@ class LineCmd extends React.Component<
};
};
@boundMethod
handleContextMenu(e: React.MouseEvent) {
e.preventDefault();
e.stopPropagation();
let { line, screen } = this.props;
const containerType = screen.getContainerType();
const isMainContainer = containerType == appconst.LineContainer_Main;
const cmd = screen.getCmd(line);
let menu: ContextMenuItem[] = [
{ role: "copy", label: "Copy", type: "normal" },
{ role: "paste", label: "Paste", type: "normal" },
{ type: "separator" },
{ label: "Copy Command", click: () => this.copyCommandStr() },
];
const isTerminal = isBlank(line.renderer) || line.renderer == "terminal";
if (isTerminal) {
menu.push({ label: "Copy Visible Output", click: () => this.copyOutput(false) });
menu.push({ label: "Copy Full Output", click: () => this.copyOutput(true) });
}
if (isMainContainer) {
menu.push({ type: "separator" });
const isMinimized = line.linestate["wave:min"];
if (isMinimized) {
menu.push({
label: "Show Block Output",
click: () => GlobalCommandRunner.lineMinimize(line.lineid, false, true),
});
} else {
menu.push({
label: "Hide Block Output",
click: () => GlobalCommandRunner.lineMinimize(line.lineid, true, true),
});
}
if (cmd?.isRunning()) {
menu.push({ type: "separator" });
menu.push({
label: "Send Signal",
type: "submenu",
submenu: [
{ label: "SIGINT", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGINT", true) },
{ label: "SIGTERM", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGTERM", true) },
{ label: "SIGKILL", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGKILL", true) },
{ type: "separator" },
{ label: "SIGUSR1", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGUSR1", true) },
{ label: "SIGUSR2", click: () => GlobalCommandRunner.lineSignal(line.lineid, "SIGUSR2", true) },
],
});
}
menu.push({ type: "separator" });
menu.push({ label: "Restart Line", click: () => GlobalCommandRunner.lineRestart(line.lineid, true) });
menu.push({ type: "separator" });
menu.push({ label: "Delete Block", click: () => GlobalCommandRunner.lineDelete(line.lineid, true) });
}
GlobalModel.contextMenuModel.showContextMenu(menu, { x: e.clientX, y: e.clientY });
}
copyCommandStr() {
const { line, screen } = this.props;
const cmd: Cmd = screen.getCmd(line);
if (cmd != null) {
navigator.clipboard.writeText(cmd.getCmdStr());
}
}
copyOutput(fullOutput: boolean) {
const { line, screen } = this.props;
let termWrap = screen.getTermWrap(line.lineid);
if (termWrap == null) {
return;
}
let outputStr = termWrap.getOutput(fullOutput);
if (fullOutput != null) {
navigator.clipboard.writeText(outputStr);
}
}
render() {
const { screen, line, width, staticRender, visible } = this.props;
const isVisible = visible.get();
@@ -827,7 +750,6 @@ class LineCmd extends React.Component<
data-lineid={line.lineid}
data-linenum={line.linenum}
data-screenid={line.screenid}
onContextMenu={this.handleContextMenu}
>
<If condition={isSelected || cmdError}>
<div key="mask" className={cn("line-mask", { "error-mask": cmdError })}></div>
-17
View File
@@ -22,23 +22,6 @@
border-bottom: 1px solid var(--app-border-color);
}
.rsb-modes {
display: flex;
flex-direction: row;
padding: 5px 10px;
gap: 5px;
border-bottom: 1px solid var(--app-border-color);
.icon-container {
padding: 2px;
cursor: pointer;
}
.icon-container:hover {
background-color: var(--app-selected-mask-color);
}
}
&.collapsed {
display: none;
}
+2 -34
View File
@@ -1,16 +1,14 @@
// Copyright 2023-2024, Command Line Inc.
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import dayjs from "dayjs";
import { If, For } from "tsx-control-statements/components";
import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel } from "@/models";
import { ResizableSidebar, Button } from "@/elements";
import { WaveBookDisplay } from "./wavebook";
import "./right.less";
@@ -57,14 +55,6 @@ class KeybindDevPane extends React.Component<{}, {}> {
@mobxReact.observer
class RightSideBar extends React.Component<RightSideBarProps, {}> {
mode: OV<string> = mobx.observable.box(null, { name: "RightSideBar-mode" });
setMode(mode: string) {
mobx.action(() => {
this.mode.set(mode);
})();
}
render() {
return (
<ResizableSidebar
@@ -81,31 +71,9 @@ class RightSideBar extends React.Component<RightSideBarProps, {}> {
<i className="fa-sharp fa-regular fa-xmark"></i>
</Button>
</div>
<div className="rsb-modes">
<div className="flex-spacer" />
<If condition={GlobalModel.isDev}>
<div
className="icon-container"
title="Show Keybinding Debugger"
onClick={() => this.setMode("keybind")}
>
<i className="fa-fw fa-sharp fa-keyboard fa-solid" />
</div>
</If>
<div
className="icon-container"
title="Show Keybinding Debugger"
onClick={() => this.setMode("wavebook")}
>
<i className="fa-sharp fa-solid fa-book-sparkles"></i>
</div>
</div>
<If condition={this.mode.get() == "keybind"}>
<If condition={GlobalModel.isDev}>
<KeybindDevPane></KeybindDevPane>
</If>
<If condition={this.mode.get() == "wavebook"}>
<WaveBookDisplay></WaveBookDisplay>
</If>
</React.Fragment>
)}
</ResizableSidebar>
-53
View File
@@ -1,53 +0,0 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import { If, For } from "tsx-control-statements/components";
import { GlobalModel } from "@/models";
import * as lexical from "lexical";
import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
import { LexicalComposer } from "@lexical/react/LexicalComposer";
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
import { $convertFromMarkdownString, $convertToMarkdownString, TRANSFORMERS } from "@lexical/markdown";
import { CodeNode, CodeHighlightNode } from "@lexical/code";
import { LinkNode } from "@lexical/link";
import { ListNode, ListItemNode } from "@lexical/list";
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
import { HorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode";
import { KeybindManager } from "@/util/keyutil";
const theme = {
// Theme styling goes here
};
class WaveBookKeybindings extends React.Component<{}, {}> {
componentDidMount(): void {
const keybindManager = GlobalModel.keybindManager;
keybindManager.registerKeybinding("pane", "wavebook", "generic:confirm", (waveEvent) => {
return true;
});
}
componentWillUnmount(): void {
const keybindManager = GlobalModel.keybindManager;
keybindManager.unregisterDomain("wavebook");
}
render() {
return null;
}
}
@mobxReact.observer
class WaveBookDisplay extends React.Component<{}, {}> {
render() {
return "playbooks";
}
}
export { WaveBookDisplay };
+53 -55
View File
@@ -1,72 +1,70 @@
.cmd-aichat {
padding-bottom: 0 !important;
.chat-window {
display: flex;
overflow-y: auto;
margin-bottom: 5px;
flex-direction: column;
.auxview-content {
flex-flow: column nowrap;
height: 100%;
// This is the filler that will push the chat messages to the bottom until the chat window is full
.filler {
.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: 0 0 auto;
.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-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-assistant {
color: var(--app-text-color);
}
.chat-msg {
margin-top: calc(var(--termpad) * 2);
margin-bottom: calc(var(--termpad) * 2);
.chat-msg-user {
.msg-text {
.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;
white-space: pre-wrap;
}
}
.chat-msg-error {
color: var(--cmdinput-text-error);
font-family: var(--markdown-font);
font-size: 14px;
}
}
+19 -7
View File
@@ -9,7 +9,6 @@ import { boundMethod } from "autobind-decorator";
import { If, For } from "tsx-control-statements/components";
import { Markdown } from "@/elements";
import { AuxiliaryCmdView } from "./auxview";
import * as appconst from "@/app/appconst";
import "./aichat.less";
@@ -57,13 +56,18 @@ class AIChat extends React.Component<{}, {}> {
chatListKeyCount: number = 0;
chatWindowScrollRef: React.RefObject<HTMLDivElement>;
textAreaRef: React.RefObject<HTMLTextAreaElement>;
isFocused: OV<boolean>;
termFontSize: number = 14;
constructor(props: any) {
super(props);
this.chatWindowScrollRef = React.createRef();
this.textAreaRef = React.createRef();
this.isFocused = mobx.observable.box(false, {
name: "aichat-isfocused",
});
}
componentDidMount() {
const inputModel = GlobalModel.inputModel;
if (this.chatWindowScrollRef?.current != null) {
@@ -88,7 +92,7 @@ class AIChat extends React.Component<{}, {}> {
}
submitChatMessage(messageStr: string) {
const curLine = GlobalModel.inputModel.curLine;
const curLine = GlobalModel.inputModel.getCurLine();
const prtn = GlobalModel.submitChatInfoCommand(messageStr, curLine, false);
prtn.then((rtn) => {
if (!rtn.success) {
@@ -105,14 +109,13 @@ class AIChat extends React.Component<{}, {}> {
onTextAreaFocused(e: any) {
mobx.action(() => {
GlobalModel.inputModel.setAuxViewFocus(true);
this.isFocused.set(true);
})();
this.onTextAreaChange(e);
}
onTextAreaBlur(e: any) {
mobx.action(() => {
GlobalModel.inputModel.setAuxViewFocus(false);
this.isFocused.set(false);
})();
}
@@ -234,7 +237,17 @@ class AIChat extends React.Component<{}, {}> {
render() {
const chatMessageItems = GlobalModel.inputModel.AICmdInfoChatItems.slice();
const chitem: OpenAICmdInfoChatMessageType = null;
const renderKeybindings = GlobalModel.inputModel.shouldRenderAuxViewKeybindings(appconst.InputAuxView_AIChat);
const renderKeybindings = mobx
.computed(() => {
return (
this.isFocused.get() ||
GlobalModel.inputModel.hasFocus() ||
(GlobalModel.getActiveScreen().getFocusType() == "input" &&
GlobalModel.activeMainView.get() == "session")
);
})
.get();
return (
<AuxiliaryCmdView
title="Wave AI"
@@ -246,7 +259,6 @@ class AIChat extends React.Component<{}, {}> {
<AIChatKeybindings AIChatObject={this}></AIChatKeybindings>
</If>
<div className="chat-window" ref={this.chatWindowScrollRef}>
<div className="filler"></div>
<For each="chitem" index="idx" of={chatMessageItems}>
{this.renderChatMessage(chitem)}
</For>
@@ -0,0 +1,28 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { SuggestionBlob } from "@/autocomplete/runtime/model";
import { GlobalModel } from "@/models";
import cn from "classnames";
import { useMemo } from "react";
export const AutocompleteSuggestions = async (props: { curLine: string }) => {
const inputModel = GlobalModel.inputModel;
const suggestions = await useMemo(async () => await inputModel.getSuggestions(), [props.curLine]);
if (!suggestions) {
return null;
}
const items = suggestions.suggestions.map((s) => `${s.icon} ${s.name}`);
return (
<div className="autocomplete-suggestions">
{items.map((item, idx) => (
<div key={idx} className={cn("autocomplete-item")}>
{item}
</div>
))}
</div>
);
};

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