aichat improvements (#667)

This commit is contained in:
Red J Adaya 2024-06-07 08:31:52 +08:00 committed by GitHub
parent 1234919229
commit d7173c970c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 315 additions and 234 deletions

View File

@ -313,6 +313,10 @@
"command": "aichat:clearHistory",
"keys": ["Ctrl:l"]
},
{
"command": "aichat:setCmdInputValue",
"keys": ["Ctrl:Shift:e"]
},
{
"command": "terminal:copy",
"keys": ["Ctrl:Shift:c"]

View File

@ -31,12 +31,20 @@ class App extends React.Component<{}, {}> {
dcWait: OV<boolean> = mobx.observable.box(false, { name: "dcWait" });
mainContentRef: React.RefObject<HTMLDivElement> = React.createRef();
termThemesLoaded: OV<boolean> = mobx.observable.box(false, { name: "termThemesLoaded" });
chatFocusTimeoutId: NodeJS.Timeout = null;
constructor(props: {}) {
super(props);
if (GlobalModel.isDev) document.body.classList.add("is-dev");
}
componentWillUnmount() {
if (this.chatFocusTimeoutId) {
clearTimeout(this.chatFocusTimeoutId);
this.chatFocusTimeoutId = null;
}
}
@boundMethod
handleContextMenu(e: any) {
let isInNonTermInput = false;
@ -68,16 +76,15 @@ class App extends React.Component<{}, {}> {
@boundMethod
openMainSidebar() {
const mainSidebarModel = GlobalModel.mainSidebarModel;
const width = mainSidebarModel.getWidth(true);
mainSidebarModel.saveState(width, false);
GlobalModel.mainSidebarModel.setCollapsed(false);
}
@boundMethod
openRightSidebar() {
const rightSidebarModel = GlobalModel.rightSidebarModel;
const width = rightSidebarModel.getWidth(true);
rightSidebarModel.saveState(width, false);
GlobalModel.rightSidebarModel.setCollapsed(false);
this.chatFocusTimeoutId = setTimeout(() => {
GlobalModel.inputModel.setChatSidebarFocus();
}, 100);
}
@boundMethod

View File

@ -128,7 +128,8 @@ class LineActions extends React.Component<{ screen: LineContainerType; line: Lin
screen.getUsedRows(lineutil.getRendererContext(line), line, cmd, 300) * 2,
cmdShouldMarkError(cmd)
);
GlobalModel.sidebarchatModel.setFocus("input", true);
GlobalModel.inputModel.setChatSidebarFocus();
GlobalModel.sidebarchatModel.resetSelectedCodeBlockIndex();
}
}

View File

@ -20,6 +20,11 @@
.filler {
flex: 1 1 auto;
}
> * {
cursor: default;
user-select: none;
}
}
.chat-msg {
@ -78,6 +83,7 @@
font-family: var(--termfontfamily);
font-weight: normal;
line-height: var(--termlineheight);
height: 21px;
}
}
}

View File

@ -16,7 +16,7 @@ import "./aichat.less";
const outline = "2px solid var(--markdown-outline-color)";
class ChatKeyBindings extends React.Component<{ component: ChatSidebar; bindArrowUpDownKeys: boolean }, {}> {
class ChatKeyBindings extends React.Component<{ component: ChatSidebar }, {}> {
componentDidMount(): void {
const { component } = this.props;
const keybindManager = GlobalModel.keybindManager;
@ -34,26 +34,19 @@ class ChatKeyBindings extends React.Component<{ component: ChatSidebar; bindArro
inputModel.clearAIAssistantChat();
return true;
});
}
componentDidUpdate(): void {
const { component, bindArrowUpDownKeys } = this.props;
const keybindManager = GlobalModel.keybindManager;
if (bindArrowUpDownKeys) {
keybindManager.registerKeybinding("pane", "aichat:arrowupdown", "generic:selectAbove", (waveEvent) => {
keybindManager.registerKeybinding("pane", "aichat", "generic:selectAbove", (waveEvent) => {
return component.onArrowUpPressed();
});
keybindManager.registerKeybinding("pane", "aichat:arrowupdown", "generic:selectBelow", (waveEvent) => {
keybindManager.registerKeybinding("pane", "aichat", "generic:selectBelow", (waveEvent) => {
return component.onArrowDownPressed();
});
} else {
GlobalModel.keybindManager.unregisterDomain("aichat:arrowupdown");
}
keybindManager.registerKeybinding("pane", "aichat", "aichat:setCmdInputValue", (waveEvent) => {
return component.onSetCmdInputValue();
});
}
componentWillUnmount(): void {
GlobalModel.keybindManager.unregisterDomain("aichat");
GlobalModel.keybindManager.unregisterDomain("aichat:arrowupdown");
}
render() {
@ -62,20 +55,16 @@ class ChatKeyBindings extends React.Component<{ component: ChatSidebar; bindArro
}
@mobxReact.observer
class ChatItem extends React.Component<{ chatItem: OpenAICmdInfoChatMessageType; itemCount: number }, {}> {
handleExecuteCommand(cmd: string) {
GlobalModel.sidebarchatModel.setCmdToExec(cmd);
GlobalModel.sidebarchatModel.resetFocus();
GlobalModel.inputModel.curLine = cmd;
GlobalModel.inputModel.giveFocus();
}
class ChatItem extends React.Component<
{ chatItem: OpenAICmdInfoChatMessageType; itemCount: number; onSetCmdInputValue: (cmd: string) => void },
{}
> {
renderError(err: string): any {
return <div className="chat-msg-error">{err}</div>;
}
render() {
const { chatItem, itemCount } = this.props;
const { chatItem, itemCount, onSetCmdInputValue } = this.props;
const { isassistantresponse, assistantresponse } = chatItem;
const curKey = "chatmsg-" + itemCount;
const senderClassName = isassistantresponse ? "chat-msg-assistant" : "chat-msg-user";
@ -108,7 +97,7 @@ class ChatItem extends React.Component<{ chatItem: OpenAICmdInfoChatMessageType;
<div className="chat-msg-header">
<i className="fa-sharp fa-solid fa-sparkles"></i>
</div>
<Markdown2 text={assistantresponse.message} onClickExecute={this.handleExecuteCommand} />
<Markdown2 text={assistantresponse.message} onClickExecute={onSetCmdInputValue} />
</>
);
}
@ -130,7 +119,14 @@ class ChatItem extends React.Component<{ chatItem: OpenAICmdInfoChatMessageType;
}
@mobxReact.observer
class ChatWindow extends React.Component<{ chatWindowRef; onRendered }, {}> {
class ChatWindow extends React.Component<
{
chatWindowRef: React.RefObject<HTMLDivElement>;
onRendered: (osInstance: OverlayScrollbars) => void;
onSetCmdInputValue: (cmd: string) => void;
},
{}
> {
itemCount: number = 0;
containerRef: React.RefObject<OverlayScrollbarsComponentRef> = React.createRef();
osInstance: OverlayScrollbars = null;
@ -164,6 +160,7 @@ class ChatWindow extends React.Component<{ chatWindowRef; onRendered }, {}> {
}
render() {
const { onSetCmdInputValue } = this.props;
const chatMessageItems = GlobalModel.inputModel.AICmdInfoChatItems.slice();
const chitem: OpenAICmdInfoChatMessageType = null;
let idx;
@ -177,7 +174,12 @@ class ChatWindow extends React.Component<{ chatWindowRef; onRendered }, {}> {
<div ref={this.props.chatWindowRef} className="chat-window">
<div className="filler"></div>
<For each="chitem" index="idx" of={chatMessageItems}>
<ChatItem key={idx} chatItem={chitem} itemCount={idx + 1} />
<ChatItem
key={idx}
chatItem={chitem}
itemCount={idx + 1}
onSetCmdInputValue={onSetCmdInputValue}
/>
</For>
</div>
</OverlayScrollbarsComponent>
@ -190,11 +192,11 @@ class ChatSidebar extends React.Component<{}, {}> {
sidebarRef: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>();
textAreaRef: React.RefObject<HTMLTextAreaElement> = React.createRef<HTMLTextAreaElement>();
chatWindowRef: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>();
bindArrowUpDownKeys: OV<boolean> = mobx.observable.box(false, { name: "bindArrowUpDownKeys" });
value: OV<string> = mobx.observable.box("", { deep: false, name: "value" });
osInstance: OverlayScrollbars;
termFontSize: number = 14;
blockIndex: number;
disposeReaction: () => void;
constructor(props) {
super(props);
@ -202,27 +204,32 @@ class ChatSidebar extends React.Component<{}, {}> {
}
componentDidUpdate() {
if (GlobalModel.sidebarchatModel.focused == "input") {
this.textAreaRef.current.focus();
}
if (GlobalModel.sidebarchatModel.hasCmdAndOutput()) {
const newCmdAndOutput = GlobalModel.sidebarchatModel.getCmdAndOutput();
const newValue = this.formChatMessage(newCmdAndOutput);
if (newValue !== this.value.get()) {
this.value.set(newValue);
GlobalModel.sidebarchatModel.resetCmdAndOutput();
}
}
this.adjustTextAreaHeight();
}
componentDidMount() {
GlobalModel.sidebarchatModel.setFocus("input", true);
this.textAreaRef.current.focus();
this.disposeReaction = mobx.reaction(
() => [
GlobalModel.sidebarchatModel.hasCmdAndOutput(),
GlobalModel.sidebarchatModel.getSelectedCodeBlockIndex(),
],
([hasCmdAndOutput, selectedCodeBlockIndex]) => {
if (hasCmdAndOutput) {
const newCmdAndOutput = GlobalModel.sidebarchatModel.getCmdAndOutput();
const newValue = this.formChatMessage(newCmdAndOutput);
this.value.set(newValue);
GlobalModel.sidebarchatModel.resetCmdAndOutput();
}
if (selectedCodeBlockIndex == null) {
this.updatePreTagOutline();
}
}
);
if (this.sidebarRef.current) {
this.sidebarRef.current.addEventListener("click", this.handleSidebarClick);
}
document.addEventListener("mousedown", this.handleClickOutside);
document.addEventListener("click", this.handleClickOutside);
this.requestChatUpdate();
}
@ -230,13 +237,19 @@ class ChatSidebar extends React.Component<{}, {}> {
if (this.sidebarRef.current) {
this.sidebarRef.current.removeEventListener("click", this.handleSidebarClick);
}
document.removeEventListener("mousedown", this.handleClickOutside);
document.removeEventListener("click", this.handleClickOutside);
GlobalModel.sidebarchatModel.resetFocus();
if (this.disposeReaction) {
this.disposeReaction();
}
}
@mobx.action.bound
handleClickOutside(event) {
if (this.sidebarRef.current && !this.sidebarRef.current.contains(event.target)) {
this.onClickOutsideSidebar();
handleClickOutside(e: MouseEvent) {
const sidebar = this.sidebarRef.current;
if (sidebar && !sidebar.contains(e.target as Node)) {
GlobalModel.sidebarchatModel.resetFocus();
GlobalModel.inputModel.giveFocus();
}
}
@ -281,20 +294,20 @@ class ChatSidebar extends React.Component<{}, {}> {
}).catch((_) => {});
}
onClickOutsideSidebar() {
GlobalModel.sidebarchatModel.resetFocus();
@mobx.action.bound
onTextAreaFocus() {
GlobalModel.inputModel.setChatSidebarFocus();
return true;
}
@mobx.action.bound
onTextAreaFocused(e) {
GlobalModel.sidebarchatModel.setFocus("input", true);
this.bindArrowUpDownKeys.set(false);
const pres = this.chatWindowRef.current?.querySelectorAll("pre");
this.blockIndex = pres.length - 1;
this.onTextAreaChange(e);
onTextAreaMouseDown(e) {
this.updatePreTagOutline();
// Reset blockIndex to null
GlobalModel.sidebarchatModel.resetSelectedCodeBlockIndex();
}
@mobx.action.bound
onEnterKeyPressed() {
const messageStr = this.value.get();
this.submitChatMessage(messageStr);
@ -302,6 +315,7 @@ class ChatSidebar extends React.Component<{}, {}> {
GlobalModel.sidebarchatModel.resetCmdAndOutput();
}
@mobx.action.bound
onExpandInputPressed() {
const currentRef = this.textAreaRef.current;
if (currentRef == null) {
@ -317,7 +331,7 @@ class ChatSidebar extends React.Component<{}, {}> {
}
pres.forEach((preElement, idx) => {
if (preElement === clickedPre) {
this.blockIndex = idx;
GlobalModel.sidebarchatModel.setSelectedCodeBlockIndex(idx);
preElement.style.outline = outline;
} else {
preElement.style.outline = "none";
@ -327,36 +341,36 @@ class ChatSidebar extends React.Component<{}, {}> {
@mobx.action.bound
handleSidebarClick(event) {
let detection = 0;
const target = event.target as HTMLElement;
if (target.closest(".copy-button") || target.closest(".fa-square-terminal")) {
if (
target.closest(".copy-button") ||
target.closest(".fa-square-terminal") ||
target.closest(".chat-textarea")
) {
return;
}
const chatWindow = target.closest(".chat-window");
if (chatWindow) {
detection++;
}
const pre = target.closest("pre");
if (pre) {
detection++;
const pres = this.chatWindowRef.current?.querySelectorAll("pre");
if (pres) {
pres.forEach((preElement, idx) => {
if (preElement === pre) {
GlobalModel.sidebarchatModel.setSelectedCodeBlockIndex(idx);
this.updatePreTagOutline(pre);
}
if (detection > 0) {
this.bindArrowUpDownKeys.set(true);
GlobalModel.sidebarchatModel.setFocus("block", true);
});
}
}
GlobalModel.inputModel.setChatSidebarFocus();
}
updateScrollTop() {
const pres = this.chatWindowRef.current?.querySelectorAll("pre");
if (pres == null) {
return;
}
const block = pres[this.blockIndex];
const block = pres[GlobalModel.sidebarchatModel.getSelectedCodeBlockIndex()];
if (block == null) {
return;
}
@ -387,41 +401,99 @@ class ChatSidebar extends React.Component<{}, {}> {
this.osInstance = osInstance;
}
@mobx.action.bound
onArrowUpPressed() {
if (this.onTextAreaKeyDown("ArrowUp")) {
const pres = this.chatWindowRef.current?.querySelectorAll("pre");
let blockIndex = GlobalModel.sidebarchatModel.getSelectedCodeBlockIndex();
if (pres == null) {
return;
return false;
}
if (this.blockIndex == null) {
this.blockIndex = pres.length - 1;
} else if (this.blockIndex > 0) {
this.blockIndex--;
if (blockIndex == null) {
GlobalModel.sidebarchatModel.setSelectedCodeBlockIndex(pres.length - 1);
} else if (blockIndex > 0) {
blockIndex--;
GlobalModel.sidebarchatModel.setSelectedCodeBlockIndex(blockIndex);
}
this.updatePreTagOutline(pres[this.blockIndex]);
blockIndex = GlobalModel.sidebarchatModel.getSelectedCodeBlockIndex();
this.updatePreTagOutline(pres[blockIndex]);
this.updateScrollTop();
return true;
}
return false;
}
@mobx.action.bound
onArrowDownPressed() {
if (this.onTextAreaKeyDown("ArrowDown")) {
const pres = this.chatWindowRef.current?.querySelectorAll("pre");
let blockIndex = GlobalModel.sidebarchatModel.getSelectedCodeBlockIndex();
if (pres == null) {
return;
return false;
}
if (this.blockIndex == null) {
return;
if (blockIndex == null) {
return false;
}
if (this.blockIndex < pres.length && this.blockIndex >= 0) {
this.blockIndex++;
this.updatePreTagOutline(pres[this.blockIndex]);
if (blockIndex < pres.length - 1 && blockIndex >= 0) {
GlobalModel.sidebarchatModel.setSelectedCodeBlockIndex(blockIndex++);
this.updatePreTagOutline(pres[blockIndex]);
} else {
this.bindArrowUpDownKeys.set(false);
GlobalModel.sidebarchatModel.setFocus(true);
this.textAreaRef.current.focus();
this.updatePreTagOutline();
GlobalModel.sidebarchatModel.setSelectedCodeBlockIndex(null);
}
this.updateScrollTop();
return true;
}
return false;
}
@mobx.action.bound
onTextAreaKeyDown(key: "ArrowUp" | "ArrowDown") {
const textarea = this.textAreaRef.current;
const cursorPosition = textarea.selectionStart;
const textBeforeCursor = textarea.value.slice(0, cursorPosition);
const blockIndex = GlobalModel.sidebarchatModel.getSelectedCodeBlockIndex();
// Check if the cursor is at the first line for ArrowUp
if ((textBeforeCursor.indexOf("\n") == -1 && cursorPosition == 0 && key == "ArrowUp") || blockIndex != null) {
return true;
}
GlobalModel.sidebarchatModel.setFocus(true);
return false;
}
@mobx.action.bound
onSetCmdInputValue(cmd?: string) {
console.log("got here");
if (cmd) {
this.setCmdInputValue(cmd);
} else {
const pres = this.chatWindowRef.current?.querySelectorAll("pre");
if (pres) {
const selectedIdx = GlobalModel.sidebarchatModel.getSelectedCodeBlockIndex();
pres.forEach((preElement, idx) => {
if (selectedIdx === idx) {
const codeElement = preElement.querySelector("code");
if (codeElement) {
const command = codeElement.textContent.replace(/\n$/, "");
this.setCmdInputValue(command);
}
}
});
}
}
return true;
}
@mobx.action.bound
setCmdInputValue(cmd: string) {
GlobalModel.sidebarchatModel.setCmdToExec(cmd);
GlobalModel.sidebarchatModel.resetFocus();
GlobalModel.inputModel.curLine = cmd;
GlobalModel.inputModel.giveFocus();
}
@mobx.action.bound
formChatMessage(cmdAndOutput) {
@ -430,7 +502,7 @@ class ChatSidebar extends React.Component<{}, {}> {
return "";
}
// Escape backticks in the output
let escapedOutput = output ? output.replace(/```/g, "\\`\\`\\`") : "";
let escapedOutput = output ? output.replace(/`/g, "\\`") : "";
// Truncate the output if usedRows is over 100
if (usedRows > 100) {
const outputLines = escapedOutput.split("\n");
@ -450,38 +522,22 @@ class ChatSidebar extends React.Component<{}, {}> {
return chatMessage;
}
@mobx.action.bound
handleKeyDown(e) {
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
if (this.bindArrowUpDownKeys.get()) {
GlobalModel.sidebarchatModel.setFocus("block", true);
this.textAreaRef.current.blur();
}
const textarea = this.textAreaRef.current;
const cursorPosition = textarea.selectionStart;
const textBeforeCursor = textarea.value.slice(0, cursorPosition);
// Check if the cursor is at the first line
if (textBeforeCursor.indexOf("\n") === -1 && cursorPosition === 0 && e.key === "ArrowUp") {
this.bindArrowUpDownKeys.set(true);
}
}
}
render() {
const chatMessageItems = GlobalModel.inputModel.AICmdInfoChatItems.slice();
const renderAIChatKeybindings = GlobalModel.sidebarchatModel.hasFocus;
const renderAIChatKeybindings = GlobalModel.sidebarchatModel.hasFocus();
const textAreaValue = this.value.get();
const bindArrowUpDownKeys = this.bindArrowUpDownKeys.get();
return (
<div ref={this.sidebarRef} className="sidebarchat">
<If condition={renderAIChatKeybindings}>
<ChatKeyBindings component={this} bindArrowUpDownKeys={bindArrowUpDownKeys} />
<ChatKeyBindings component={this} />
</If>
{chatMessageItems.length > 0 && (
<ChatWindow chatWindowRef={this.chatWindowRef} onRendered={this.onChatWindowRendered} />
<ChatWindow
chatWindowRef={this.chatWindowRef}
onRendered={this.onChatWindowRendered}
onSetCmdInputValue={this.onSetCmdInputValue}
/>
)}
<div className="sidebarchat-input-wrapper">
<textarea
@ -490,8 +546,8 @@ class ChatSidebar extends React.Component<{}, {}> {
autoComplete="off"
autoCorrect="off"
className="sidebarchat-input chat-textarea"
onKeyDown={this.handleKeyDown}
onMouseDown={this.onTextAreaFocused}
onFocus={this.onTextAreaFocus}
onMouseDown={this.onTextAreaMouseDown} // When the user clicks on the textarea
onChange={this.onTextAreaChange}
style={{ fontSize: this.termFontSize }}
placeholder="Send a Message..."

View File

@ -53,16 +53,12 @@ class KeybindDevPane extends React.Component<{}, {}> {
}
}
class SidebarKeyBindings extends React.Component<{ component: RightSideBar; isOpen: boolean }, {}> {
class SidebarKeyBindings extends React.Component<{ component: RightSideBar }, {}> {
componentDidMount(): void {
const { component } = this.props;
const keybindManager = GlobalModel.keybindManager;
keybindManager.registerKeybinding("pane", "rightsidebar", "rightsidebar:toggle", (waveEvent) => {
if (this.props.isOpen) {
return component.onClose();
} else {
return component.onOpen();
}
return component.toggleCollapse();
});
}
@ -87,13 +83,21 @@ class RightSideBar extends React.Component<
{}
> {
mode: OV<string> = mobx.observable.box("aichat", { name: "RightSideBar-mode" });
timeoutId: NodeJS.Timeout = null;
constructor(props) {
super(props);
mobx.makeObservable(this);
}
@mobx.action
componentWillUnmount() {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
}
@mobx.action.bound
setMode(mode: string) {
if (mode == this.mode.get()) {
return;
@ -101,15 +105,19 @@ class RightSideBar extends React.Component<
this.mode.set(mode);
}
@boundMethod
onOpen() {
GlobalModel.rightSidebarModel.setCollapsed(false);
return true;
@mobx.action.bound
toggleCollapse() {
const isCollapsed = GlobalModel.rightSidebarModel.getCollapsed();
GlobalModel.rightSidebarModel.setCollapsed(!isCollapsed);
if (this.mode.get() == "aichat") {
if (isCollapsed) {
this.timeoutId = setTimeout(() => {
GlobalModel.inputModel.setChatSidebarFocus();
}, 100);
} else {
GlobalModel.inputModel.setChatSidebarFocus(false);
}
}
@boundMethod
onClose() {
GlobalModel.rightSidebarModel.setCollapsed(true);
return true;
}
@ -124,9 +132,9 @@ class RightSideBar extends React.Component<
enableSnap={true}
parentRef={this.props.parentRef}
>
{(toggleCollapse) => (
{() => (
<React.Fragment>
<SidebarKeyBindings component={this} isOpen={!isCollapsed} />
<SidebarKeyBindings component={this} />
<div className="header">
<div className="rsb-modes">
<div
@ -148,7 +156,7 @@ class RightSideBar extends React.Component<
</div>
</If>
</div>
<Button className="secondary ghost close" onClick={toggleCollapse}>
<Button className="secondary ghost close" onClick={this.toggleCollapse}>
<i className="fa-sharp fa-solid fa-xmark-large" />
</Button>
</div>

View File

@ -139,6 +139,7 @@ class InputModel {
const activeAuxView = this.getAuxViewFocus() ? this.getActiveAuxView() : null;
switch (activeAuxView) {
case appconst.InputAuxView_History:
console.log("focus history");
const elem: HTMLElement = document.querySelector(".cmd-input input.history-input");
if (elem) {
elem.focus();
@ -148,8 +149,12 @@ class InputModel {
this.setAIChatFocus();
break;
case null:
if (GlobalModel.sidebarchatModel.hasFocus) {
if (GlobalModel.sidebarchatModel.hasFocus()) {
this.auxViewFocus.set(false);
const elem: HTMLElement = document.querySelector(".sidebarchat-input");
if (elem != null) {
elem.focus();
}
} else {
const elem = document.getElementById("main-cmd-input");
if (elem) {
@ -159,6 +164,7 @@ class InputModel {
}
break;
default: {
console.log("focus auxview");
const elem: HTMLElement = document.querySelector(".cmd-input .auxview");
if (elem != null) {
elem.focus();
@ -282,6 +288,12 @@ class InputModel {
}
}
@mobx.action
setChatSidebarFocus(focus = true): void {
GlobalModel.sidebarchatModel.setFocus(focus);
this.giveFocus();
}
@mobx.action
updateCmdLine(cmdLine: StrWithPos): void {
this.curLine = cmdLine.str;
@ -468,7 +480,7 @@ class InputModel {
}
// (view == null) means standard cmdinput keybindings
if (view == null) {
return !this.getAuxViewFocus() && !GlobalModel.sidebarchatModel.hasFocus;
return !this.getAuxViewFocus() && !GlobalModel.sidebarchatModel.hasFocus();
} else {
return this.getAuxViewFocus() && view == this.getActiveAuxView();
}

View File

@ -82,6 +82,11 @@ class MainSidebarModel {
return collapsed;
}
setCollapsed(collapsed: boolean): void {
const width = this.getWidth(true);
this.saveState(width, collapsed);
}
saveState(width: number, collapsed: boolean): void {
GlobalCommandRunner.clientSetMainSidebar(width, collapsed).finally(() => {
mobx.action(() => {

View File

@ -738,10 +738,12 @@ class Model {
setTimeout(() => {
// allows for the session view to load
this.inputModel.setAuxViewFocus(false);
this.inputModel.setChatSidebarFocus(false);
}, 100);
})();
} else {
this.inputModel.setAuxViewFocus(false);
this.inputModel.setChatSidebarFocus(false);
}
}

View File

@ -1,104 +1,91 @@
import * as mobx from "mobx";
import { Model } from "./model";
type SidebarChatFocus = {
input: boolean;
block: boolean;
};
class SidebarChatModel {
globalModel: Model;
sidebarChatFocus: SidebarChatFocus;
cmdAndOutput: CmdAndOutput;
cmdFromChat: string;
sidebarChatFocused: OV<boolean> = mobx.observable.box(false, { name: "SidebarChatModel-sidebarChatFocused" });
cmdAndOutput: OV<{ cmd: string; output: string; usedRows: number; isError: boolean }> = mobx.observable.box(
{ cmd: "", output: "", usedRows: 0, isError: false },
{ name: "SidebarChatModel-cmdAndOutput" }
);
cmdFromChat: OV<string> = mobx.observable.box("", { name: "SidebarChatModel-cmdFromChat" });
selectedCodeBlockIndex: OV<number> = mobx.observable.box(null, { name: "SidebarChatModel-codeBlockIndex" });
constructor(globalModel: Model) {
this.globalModel = globalModel;
mobx.makeObservable(this, {
sidebarChatFocus: mobx.observable,
cmdAndOutput: mobx.observable,
setFocus: mobx.action,
resetFocus: mobx.action,
setCmdAndOutput: mobx.action,
resetCmdAndOutput: mobx.action,
setCmdToExec: mobx.action,
resetCmdToExec: mobx.action,
hasFocus: mobx.computed,
focused: mobx.computed,
cmdToExec: mobx.computed,
mobx.makeObservable(this);
}
// block can be the chat-window in terms of focus
@mobx.action
setFocus(focus: boolean): void {
this.resetFocus();
this.sidebarChatFocused.set(focus);
}
hasFocus(): boolean {
return this.sidebarChatFocused.get();
}
@mobx.action
resetFocus(): void {
this.sidebarChatFocused.set(false);
}
@mobx.action
setCmdAndOutput(cmd: string, output: string, usedRows: number, isError: boolean): void {
console.log("cmd", cmd);
this.cmdAndOutput.set({
cmd: cmd,
output: output,
usedRows: usedRows,
isError: isError,
});
this.sidebarChatFocus = {
input: false,
block: false,
};
this.cmdAndOutput = {
}
getCmdAndOutput(): { cmd: string; output: string; usedRows: number; isError: boolean } {
return this.cmdAndOutput.get();
}
@mobx.action
resetCmdAndOutput(): void {
this.cmdAndOutput.set({
cmd: "",
output: "",
usedRows: 0,
isError: false,
};
this.cmdFromChat = "";
}
// block can be the chat-window in terms of focus
setFocus(section: "input" | "block", focus: boolean): void {
document.querySelector(".sidebarchat .sidebarchat-input");
this.resetFocus();
this.sidebarChatFocus[section] = focus;
}
get hasFocus(): boolean {
return this.sidebarChatFocus.input || this.sidebarChatFocus.block;
}
get focused(): "input" | "block" | null {
if (this.sidebarChatFocus.input) return "input";
if (this.sidebarChatFocus.block) return "block";
return null;
}
resetFocus(): void {
this.sidebarChatFocus.input = false;
this.sidebarChatFocus.block = false;
}
setCmdAndOutput(cmd: string, output: string, usedRows: number, isError: boolean): void {
this.cmdAndOutput.cmd = cmd;
this.cmdAndOutput.output = output;
this.cmdAndOutput.usedRows = usedRows;
this.cmdAndOutput.isError = isError;
}
getCmdAndOutput(): CmdAndOutput {
return {
cmd: this.cmdAndOutput.cmd,
output: this.cmdAndOutput.output,
usedRows: this.cmdAndOutput.usedRows,
isError: this.cmdAndOutput.isError,
};
}
resetCmdAndOutput(): void {
this.cmdAndOutput.cmd = "";
this.cmdAndOutput.output = "";
this.cmdAndOutput.usedRows = 0;
this.cmdAndOutput.isError = false;
});
}
hasCmdAndOutput(): boolean {
return this.cmdAndOutput.cmd.length > 0 || this.cmdAndOutput.output.length > 0;
const { cmd, output } = this.cmdAndOutput.get();
return cmd.length > 0 || output.length > 0;
}
@mobx.action
setCmdToExec(cmd: string): void {
this.cmdFromChat = cmd;
this.cmdFromChat.set(cmd);
}
@mobx.action
resetCmdToExec(): void {
this.cmdFromChat = "";
this.cmdFromChat.set("");
}
get cmdToExec(): string {
return this.cmdFromChat;
getCmdToExec(): string {
return this.cmdFromChat.get();
}
getSelectedCodeBlockIndex(): number {
return this.selectedCodeBlockIndex.get();
}
setSelectedCodeBlockIndex(index: number): void {
this.selectedCodeBlockIndex.set(index);
}
resetSelectedCodeBlockIndex(): void {
this.selectedCodeBlockIndex.set(null);
}
}

View File

@ -990,13 +990,6 @@ declare global {
click?: () => void; // not required if role is set
submenu?: ContextMenuItem[];
};
type CmdAndOutput = {
cmd: string;
output: string;
usedRows: number;
isError: boolean;
};
}
export {};