mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-02-25 03:12:07 +01:00
Break out non-autocomplete changes from autocomplete PR (#618)
This improves the ephemeral command runner to allow for honoring of timeouts and proper handling of overriding the current working directory. It also fixes some partially transparent font colors in light mode, making them solid instead. It also updates the InputModel to be auto-observable and utilize some getters to ensure the cmdinput is getting updated whenever necessary state changes take place.
This commit is contained in:
parent
ac91fa8596
commit
c6a8797ddd
@ -9,8 +9,8 @@
|
|||||||
--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: rgb(0, 0, 0);
|
--app-text-color: rgb(0, 0, 0);
|
||||||
--app-text-primary-color: rgb(0, 0, 0, 0.9);
|
--app-text-primary-color: rgb(23, 23, 23);
|
||||||
--app-text-secondary-color: rgb(0, 0, 0, 0.7);
|
--app-text-secondary-color: rgb(76, 76, 76);
|
||||||
--app-border-color: rgb(139 145 138);
|
--app-border-color: rgb(139 145 138);
|
||||||
--app-panel-bg-color: rgb(224, 224, 224);
|
--app-panel-bg-color: rgb(224, 224, 224);
|
||||||
--app-panel-bg-color-dev: rgb(224, 224, 224);
|
--app-panel-bg-color-dev: rgb(224, 224, 224);
|
||||||
|
@ -88,7 +88,7 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submitChatMessage(messageStr: string) {
|
submitChatMessage(messageStr: string) {
|
||||||
const curLine = GlobalModel.inputModel.getCurLine();
|
const curLine = GlobalModel.inputModel.curLine;
|
||||||
const prtn = GlobalModel.submitChatInfoCommand(messageStr, curLine, false);
|
const prtn = GlobalModel.submitChatInfoCommand(messageStr, curLine, false);
|
||||||
prtn.then((rtn) => {
|
prtn.then((rtn) => {
|
||||||
if (!rtn.success) {
|
if (!rtn.success) {
|
||||||
@ -103,15 +103,19 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
return { numLines, linePos };
|
return { numLines, linePos };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action.bound
|
||||||
onTextAreaFocused(e: any) {
|
onTextAreaFocused(e: any) {
|
||||||
GlobalModel.inputModel.setAuxViewFocus(true);
|
GlobalModel.inputModel.setAuxViewFocus(true);
|
||||||
|
this.onTextAreaChange(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action.bound
|
||||||
onTextAreaBlur(e: any) {
|
onTextAreaBlur(e: any) {
|
||||||
GlobalModel.inputModel.setAuxViewFocus(false);
|
GlobalModel.inputModel.setAuxViewFocus(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust the height of the textarea to fit the text
|
// Adjust the height of the textarea to fit the text
|
||||||
|
@boundMethod
|
||||||
onTextAreaChange(e: any) {
|
onTextAreaChange(e: any) {
|
||||||
// Calculate the bounding height of the text area
|
// Calculate the bounding height of the text area
|
||||||
const textAreaMaxLines = 4;
|
const textAreaMaxLines = 4;
|
||||||
@ -140,8 +144,10 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
this.submitChatMessage(messageStr);
|
this.submitChatMessage(messageStr);
|
||||||
currentRef.value = "";
|
currentRef.value = "";
|
||||||
} else {
|
} else {
|
||||||
inputModel.grabCodeSelectSelection();
|
mobx.action(() => {
|
||||||
inputModel.setAuxViewFocus(false);
|
inputModel.grabCodeSelectSelection();
|
||||||
|
inputModel.setAuxViewFocus(false);
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +188,6 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mobx.action
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
onKeyDown(e: any) {}
|
onKeyDown(e: any) {}
|
||||||
|
|
||||||
@ -254,9 +259,9 @@ class AIChat extends React.Component<{}, {}> {
|
|||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
id="chat-cmd-input"
|
id="chat-cmd-input"
|
||||||
onFocus={this.onTextAreaFocused.bind(this)}
|
onFocus={this.onTextAreaFocused}
|
||||||
onBlur={this.onTextAreaBlur.bind(this)}
|
onBlur={this.onTextAreaBlur}
|
||||||
onChange={this.onTextAreaChange.bind(this)}
|
onChange={this.onTextAreaChange}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
style={{ fontSize: this.termFontSize }}
|
style={{ fontSize: this.termFontSize }}
|
||||||
className="chat-textarea"
|
className="chat-textarea"
|
||||||
|
@ -193,8 +193,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This aligns the icons with the prompt field.
|
// This aligns the icons with the prompt field.
|
||||||
// We don't need right padding because the whole input field is already padded.
|
// We don't need right margin because the whole input field is already padded.
|
||||||
padding: 2px 0 0 12px;
|
margin: 2px 0 0 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class CmdInput extends React.Component<{}, {}> {
|
|||||||
this.updateCmdInputHeight();
|
this.updateCmdInputHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
clickFocusInputHint(): void {
|
clickFocusInputHint(): void {
|
||||||
GlobalModel.inputModel.giveFocus();
|
GlobalModel.inputModel.giveFocus();
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ class CmdInput extends React.Component<{}, {}> {
|
|||||||
GlobalModel.inputModel.setAuxViewFocus(false);
|
GlobalModel.inputModel.setAuxViewFocus(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
clickAIAction(e: any): void {
|
clickAIAction(e: any): void {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -87,7 +87,7 @@ class CmdInput extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
clickHistoryAction(e: any): void {
|
clickHistoryAction(e: any): void {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -105,11 +105,9 @@ class CmdInput extends React.Component<{}, {}> {
|
|||||||
GlobalCommandRunner.connectRemote(remoteId);
|
GlobalCommandRunner.connectRemote(remoteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
toggleFilter(screen: Screen) {
|
toggleFilter(screen: Screen) {
|
||||||
mobx.action(() => {
|
screen.filterRunning.set(!screen.filterRunning.get());
|
||||||
screen.filterRunning.set(!screen.filterRunning.get());
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
color: var(--app-text-color);
|
color: var(--app-text-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
|
min-height: 100%;
|
||||||
|
|
||||||
.history-item {
|
.history-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -169,12 +169,12 @@ class HistoryInfo extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
handleClose() {
|
handleClose() {
|
||||||
GlobalModel.inputModel.closeAuxView();
|
GlobalModel.inputModel.closeAuxView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
handleItemClick(hitem: HistoryItem) {
|
handleItemClick(hitem: HistoryItem) {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
const selItem = inputModel.getHistorySelectedItem();
|
const selItem = inputModel.getHistorySelectedItem();
|
||||||
@ -195,14 +195,14 @@ class HistoryInfo extends React.Component<{}, {}> {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
handleClickType() {
|
handleClickType() {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
inputModel.setAuxViewFocus(true);
|
inputModel.setAuxViewFocus(true);
|
||||||
inputModel.toggleHistoryType();
|
inputModel.toggleHistoryType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
handleClickRemote() {
|
handleClickRemote() {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
inputModel.setAuxViewFocus(true);
|
inputModel.setAuxViewFocus(true);
|
||||||
@ -229,7 +229,7 @@ class HistoryInfo extends React.Component<{}, {}> {
|
|||||||
render() {
|
render() {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
const selItem = inputModel.getHistorySelectedItem();
|
const selItem = inputModel.getHistorySelectedItem();
|
||||||
const hitems = inputModel.getFilteredHistoryItems();
|
const hitems = inputModel.filteredHistoryItems;
|
||||||
const opts = inputModel.historyQueryOpts.get();
|
const opts = inputModel.historyQueryOpts.get();
|
||||||
let hitem: HistoryItem = null;
|
let hitem: HistoryItem = null;
|
||||||
let snames: Record<string, string> = {};
|
let snames: Record<string, string> = {};
|
||||||
|
@ -117,7 +117,7 @@ class CmdInputKeybindings extends React.Component<{ inputObject: TextAreaInput }
|
|||||||
const lastTab = this.lastTab;
|
const lastTab = this.lastTab;
|
||||||
this.lastTab = true;
|
this.lastTab = true;
|
||||||
this.curPress = "tab";
|
this.curPress = "tab";
|
||||||
const curLine = inputModel.getCurLine();
|
const curLine = inputModel.curLine;
|
||||||
if (lastTab) {
|
if (lastTab) {
|
||||||
GlobalModel.submitCommand(
|
GlobalModel.submitCommand(
|
||||||
"_compgen",
|
"_compgen",
|
||||||
@ -250,9 +250,10 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
lastSP: StrWithPos = { str: "", pos: appconst.NoStrPos };
|
lastSP: StrWithPos = { str: "", pos: appconst.NoStrPos };
|
||||||
version: OV<number> = mobx.observable.box(0, { name: "textAreaInput-version" }); // forces render updates
|
version: OV<number> = mobx.observable.box(0, { name: "textAreaInput-version" }); // forces render updates
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
incVersion(): void {
|
incVersion(): void {
|
||||||
const v = this.version.get();
|
const v = this.version.get();
|
||||||
mobx.action(() => this.version.set(v + 1))();
|
this.version.set(v + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurSP(): StrWithPos {
|
getCurSP(): StrWithPos {
|
||||||
@ -278,6 +279,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
GlobalModel.sendCmdInputText(this.props.screen.screenId, curSP);
|
GlobalModel.sendCmdInputText(this.props.screen.screenId, curSP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setFocus(): void {
|
setFocus(): void {
|
||||||
GlobalModel.inputModel.giveFocus();
|
GlobalModel.inputModel.giveFocus();
|
||||||
}
|
}
|
||||||
@ -311,6 +313,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const activeScreen = GlobalModel.getActiveScreen();
|
const activeScreen = GlobalModel.getActiveScreen();
|
||||||
if (activeScreen != null) {
|
if (activeScreen != null) {
|
||||||
@ -324,6 +327,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
this.updateSP();
|
this.updateSP();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
const activeScreen = GlobalModel.getActiveScreen();
|
const activeScreen = GlobalModel.getActiveScreen();
|
||||||
if (activeScreen != null) {
|
if (activeScreen != null) {
|
||||||
@ -340,7 +344,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
this.mainInputRef.current.selectionStart = fcpos;
|
this.mainInputRef.current.selectionStart = fcpos;
|
||||||
this.mainInputRef.current.selectionEnd = fcpos;
|
this.mainInputRef.current.selectionEnd = fcpos;
|
||||||
}
|
}
|
||||||
mobx.action(() => inputModel.forceCursorPos.set(null))();
|
inputModel.forceCursorPos.set(null);
|
||||||
}
|
}
|
||||||
if (inputModel.forceInputFocus) {
|
if (inputModel.forceInputFocus) {
|
||||||
inputModel.forceInputFocus = false;
|
inputModel.forceInputFocus = false;
|
||||||
@ -414,21 +418,18 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentRef.setRangeText("\n", currentRef.selectionStart, currentRef.selectionEnd, "end");
|
currentRef.setRangeText("\n", currentRef.selectionStart, currentRef.selectionEnd, "end");
|
||||||
GlobalModel.inputModel.setCurLine(currentRef.value);
|
GlobalModel.inputModel.curLine = currentRef.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mobx.action
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
onKeyDown(e: any) {}
|
onKeyDown(e: any) {}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
onChange(e: any) {
|
onChange(e: any) {
|
||||||
mobx.action(() => {
|
GlobalModel.inputModel.curLine = e.target.value;
|
||||||
GlobalModel.inputModel.setCurLine(e.target.value);
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
onSelect(e: any) {
|
onSelect(e: any) {
|
||||||
this.incVersion();
|
this.incVersion();
|
||||||
}
|
}
|
||||||
@ -453,7 +454,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
GlobalModel.inputModel.updateCmdLine(cmdLineUpdate);
|
GlobalModel.inputModel.updateCmdLine(cmdLineUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
controlP() {
|
controlP() {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
if (!inputModel.isHistoryLoaded()) {
|
if (!inputModel.isHistoryLoaded()) {
|
||||||
@ -465,7 +466,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
this.lastHistoryUpDown = true;
|
this.lastHistoryUpDown = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
controlN() {
|
controlN() {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
inputModel.moveHistorySelection(-1);
|
inputModel.moveHistorySelection(-1);
|
||||||
@ -526,17 +527,15 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
handleHistoryInput(e: any) {
|
handleHistoryInput(e: any) {
|
||||||
const inputModel = GlobalModel.inputModel;
|
const inputModel = GlobalModel.inputModel;
|
||||||
mobx.action(() => {
|
const opts = mobx.toJS(inputModel.historyQueryOpts.get());
|
||||||
const opts = mobx.toJS(inputModel.historyQueryOpts.get());
|
opts.queryStr = e.target.value;
|
||||||
opts.queryStr = e.target.value;
|
inputModel.setHistoryQueryOpts(opts);
|
||||||
inputModel.setHistoryQueryOpts(opts);
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@mobx.action.bound
|
||||||
handleFocus(e: any) {
|
handleFocus(e: any) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
GlobalModel.inputModel.giveFocus();
|
GlobalModel.inputModel.giveFocus();
|
||||||
@ -561,7 +560,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
|
|||||||
render() {
|
render() {
|
||||||
const model = GlobalModel;
|
const model = GlobalModel;
|
||||||
const inputModel = model.inputModel;
|
const inputModel = model.inputModel;
|
||||||
const curLine = inputModel.getCurLine();
|
const curLine = inputModel.curLine;
|
||||||
let displayLines = 1;
|
let displayLines = 1;
|
||||||
const numLines = curLine.split("\n").length;
|
const numLines = curLine.split("\n").length;
|
||||||
const maxCols = this.getTextAreaMaxCols();
|
const maxCols = this.getTextAreaMaxCols();
|
||||||
|
@ -8,7 +8,6 @@ import { isBlank } from "@/util/util";
|
|||||||
import * as appconst from "@/app/appconst";
|
import * as appconst from "@/app/appconst";
|
||||||
import type { Model } from "./model";
|
import type { Model } from "./model";
|
||||||
import { GlobalCommandRunner, GlobalModel } from "./global";
|
import { GlobalCommandRunner, GlobalModel } from "./global";
|
||||||
import { app } from "electron";
|
|
||||||
|
|
||||||
function getDefaultHistoryQueryOpts(): HistoryQueryOpts {
|
function getDefaultHistoryQueryOpts(): HistoryQueryOpts {
|
||||||
return {
|
return {
|
||||||
@ -48,7 +47,6 @@ class InputModel {
|
|||||||
name: "history-items",
|
name: "history-items",
|
||||||
deep: false,
|
deep: false,
|
||||||
}); // sorted in reverse (most recent is index 0)
|
}); // sorted in reverse (most recent is index 0)
|
||||||
filteredHistoryItems: mobx.IComputedValue<HistoryItem[]> = null;
|
|
||||||
historyIndex: mobx.IObservableValue<number> = mobx.observable.box(0, {
|
historyIndex: mobx.IObservableValue<number> = mobx.observable.box(0, {
|
||||||
name: "history-index",
|
name: "history-index",
|
||||||
}); // 1-indexed (because 0 is current)
|
}); // 1-indexed (because 0 is current)
|
||||||
@ -73,11 +71,10 @@ class InputModel {
|
|||||||
physicalInputFocused: OV<boolean> = mobx.observable.box(false);
|
physicalInputFocused: OV<boolean> = mobx.observable.box(false);
|
||||||
forceInputFocus: boolean = false;
|
forceInputFocus: boolean = false;
|
||||||
|
|
||||||
|
lastCurLine: string = "";
|
||||||
|
|
||||||
constructor(globalModel: Model) {
|
constructor(globalModel: Model) {
|
||||||
this.globalModel = globalModel;
|
this.globalModel = globalModel;
|
||||||
this.filteredHistoryItems = mobx.computed(() => {
|
|
||||||
return this._getFilteredHistoryItems();
|
|
||||||
});
|
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
this.codeSelectSelectedIndex.set(-1);
|
this.codeSelectSelectedIndex.set(-1);
|
||||||
this.codeSelectBlockRefArray = [];
|
this.codeSelectBlockRefArray = [];
|
||||||
@ -85,12 +82,12 @@ class InputModel {
|
|||||||
this.codeSelectUuid = "";
|
this.codeSelectUuid = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setInputMode(inputMode: null | "comment" | "global"): void {
|
setInputMode(inputMode: null | "comment" | "global"): void {
|
||||||
mobx.action(() => {
|
this.inputMode.set(inputMode);
|
||||||
this.inputMode.set(inputMode);
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
toggleHistoryType(): void {
|
toggleHistoryType(): void {
|
||||||
const opts = mobx.toJS(this.historyQueryOpts.get());
|
const opts = mobx.toJS(this.historyQueryOpts.get());
|
||||||
let htype = opts.queryType;
|
let htype = opts.queryType;
|
||||||
@ -104,6 +101,7 @@ class InputModel {
|
|||||||
this.setHistoryType(htype);
|
this.setHistoryType(htype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
toggleRemoteType(): void {
|
toggleRemoteType(): void {
|
||||||
const opts = mobx.toJS(this.historyQueryOpts.get());
|
const opts = mobx.toJS(this.historyQueryOpts.get());
|
||||||
if (opts.limitRemote) {
|
if (opts.limitRemote) {
|
||||||
@ -116,67 +114,63 @@ class InputModel {
|
|||||||
this.setHistoryQueryOpts(opts);
|
this.setHistoryQueryOpts(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
onInputFocus(isFocused: boolean): void {
|
onInputFocus(isFocused: boolean): void {
|
||||||
mobx.action(() => {
|
if (isFocused) {
|
||||||
if (isFocused) {
|
this.inputFocused.set(true);
|
||||||
this.inputFocused.set(true);
|
this.lineFocused.set(false);
|
||||||
this.lineFocused.set(false);
|
} else if (this.inputFocused.get()) {
|
||||||
} else if (this.inputFocused.get()) {
|
this.inputFocused.set(false);
|
||||||
this.inputFocused.set(false);
|
}
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
onLineFocus(isFocused: boolean): void {
|
onLineFocus(isFocused: boolean): void {
|
||||||
mobx.action(() => {
|
if (isFocused) {
|
||||||
if (isFocused) {
|
this.inputFocused.set(false);
|
||||||
this.inputFocused.set(false);
|
this.lineFocused.set(true);
|
||||||
this.lineFocused.set(true);
|
} else if (this.lineFocused.get()) {
|
||||||
} else if (this.lineFocused.get()) {
|
this.lineFocused.set(false);
|
||||||
this.lineFocused.set(false);
|
}
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focuses the main input or the auxiliary view, depending on the active auxiliary view
|
// Focuses the main input or the auxiliary view, depending on the active auxiliary view
|
||||||
|
@mobx.action
|
||||||
giveFocus(): void {
|
giveFocus(): void {
|
||||||
// Override active view to the main input if aux view does not have focus
|
// Override active view to the main input if aux view does not have focus
|
||||||
const activeAuxView = this.getAuxViewFocus() ? this.getActiveAuxView() : null;
|
const activeAuxView = this.getAuxViewFocus() ? this.getActiveAuxView() : null;
|
||||||
mobx.action(() => {
|
switch (activeAuxView) {
|
||||||
switch (activeAuxView) {
|
case appconst.InputAuxView_History: {
|
||||||
case appconst.InputAuxView_History: {
|
const 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();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case appconst.InputAuxView_AIChat:
|
|
||||||
this.setAIChatFocus();
|
|
||||||
break;
|
|
||||||
case null: {
|
|
||||||
const elem = document.getElementById("main-cmd-input");
|
|
||||||
if (elem != null) {
|
|
||||||
elem.focus();
|
|
||||||
}
|
|
||||||
this.setPhysicalInputFocused(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
const elem: HTMLElement = document.querySelector(".cmd-input .auxview");
|
|
||||||
if (elem != null) {
|
|
||||||
elem.focus();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
})();
|
case appconst.InputAuxView_AIChat:
|
||||||
|
this.setAIChatFocus();
|
||||||
|
break;
|
||||||
|
case null: {
|
||||||
|
const elem = document.getElementById("main-cmd-input");
|
||||||
|
if (elem != null) {
|
||||||
|
elem.focus();
|
||||||
|
}
|
||||||
|
this.setPhysicalInputFocused(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
const elem: HTMLElement = document.querySelector(".cmd-input .auxview");
|
||||||
|
if (elem != null) {
|
||||||
|
elem.focus();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setPhysicalInputFocused(isFocused: boolean): void {
|
setPhysicalInputFocused(isFocused: boolean): void {
|
||||||
mobx.action(() => {
|
this.physicalInputFocused.set(isFocused);
|
||||||
this.physicalInputFocused.set(isFocused);
|
|
||||||
})();
|
|
||||||
if (isFocused) {
|
if (isFocused) {
|
||||||
const screen = this.globalModel.getActiveScreen();
|
const screen = this.globalModel.getActiveScreen();
|
||||||
if (screen != null) {
|
if (screen != null) {
|
||||||
@ -203,6 +197,7 @@ class InputModel {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setHistoryType(htype: HistoryTypeStrs): void {
|
setHistoryType(htype: HistoryTypeStrs): void {
|
||||||
if (this.historyQueryOpts.get().queryType == htype) {
|
if (this.historyQueryOpts.get().queryType == htype) {
|
||||||
return;
|
return;
|
||||||
@ -214,7 +209,7 @@ class InputModel {
|
|||||||
if (oldItem == null) {
|
if (oldItem == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const newItems = this.getFilteredHistoryItems();
|
const newItems = this.filteredHistoryItems;
|
||||||
if (newItems.length == 0) {
|
if (newItems.length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -234,15 +229,15 @@ class InputModel {
|
|||||||
return bestIdx + 1;
|
return bestIdx + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setHistoryQueryOpts(opts: HistoryQueryOpts): void {
|
setHistoryQueryOpts(opts: HistoryQueryOpts): void {
|
||||||
mobx.action(() => {
|
const oldItem = this.getHistorySelectedItem();
|
||||||
const oldItem = this.getHistorySelectedItem();
|
this.historyQueryOpts.set(opts);
|
||||||
this.historyQueryOpts.set(opts);
|
const bestIndex = this.findBestNewIndex(oldItem);
|
||||||
const bestIndex = this.findBestNewIndex(oldItem);
|
setTimeout(() => this.setHistoryIndex(bestIndex, true), 10);
|
||||||
setTimeout(() => this.setHistoryIndex(bestIndex, true), 10);
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setOpenAICmdInfoChat(chat: OpenAICmdInfoChatMessageType[]): void {
|
setOpenAICmdInfoChat(chat: OpenAICmdInfoChatMessageType[]): void {
|
||||||
this.AICmdInfoChatItems.replace(chat);
|
this.AICmdInfoChatItems.replace(chat);
|
||||||
this.codeSelectBlockRefArray = [];
|
this.codeSelectBlockRefArray = [];
|
||||||
@ -256,6 +251,7 @@ class InputModel {
|
|||||||
return hitems != null;
|
return hitems != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
loadHistory(show: boolean, afterLoadIndex: number, htype: HistoryTypeStrs) {
|
loadHistory(show: boolean, afterLoadIndex: number, htype: HistoryTypeStrs) {
|
||||||
if (this.historyLoading.get()) {
|
if (this.historyLoading.get()) {
|
||||||
return;
|
return;
|
||||||
@ -266,12 +262,11 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.historyAfterLoadIndex = afterLoadIndex;
|
this.historyAfterLoadIndex = afterLoadIndex;
|
||||||
mobx.action(() => {
|
this.historyLoading.set(true);
|
||||||
this.historyLoading.set(true);
|
|
||||||
})();
|
|
||||||
GlobalCommandRunner.loadHistory(show, htype);
|
GlobalCommandRunner.loadHistory(show, htype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
openHistory(): void {
|
openHistory(): void {
|
||||||
if (this.historyLoading.get()) {
|
if (this.historyLoading.get()) {
|
||||||
return;
|
return;
|
||||||
@ -287,13 +282,12 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
updateCmdLine(cmdLine: StrWithPos): void {
|
updateCmdLine(cmdLine: StrWithPos): void {
|
||||||
mobx.action(() => {
|
this.curLine = cmdLine.str;
|
||||||
this.setCurLine(cmdLine.str);
|
if (cmdLine.pos != appconst.NoStrPos) {
|
||||||
if (cmdLine.pos != appconst.NoStrPos) {
|
this.forceCursorPos.set(cmdLine.pos);
|
||||||
this.forceCursorPos.set(cmdLine.pos);
|
}
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getHistorySelectedItem(): HistoryItem {
|
getHistorySelectedItem(): HistoryItem {
|
||||||
@ -301,7 +295,7 @@ class InputModel {
|
|||||||
if (hidx == 0) {
|
if (hidx == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const hitems = this.getFilteredHistoryItems();
|
const hitems = this.filteredHistoryItems;
|
||||||
if (hidx > hitems.length) {
|
if (hidx > hitems.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -309,15 +303,16 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFirstHistoryItem(): HistoryItem {
|
getFirstHistoryItem(): HistoryItem {
|
||||||
const hitems = this.getFilteredHistoryItems();
|
const hitems = this.filteredHistoryItems;
|
||||||
if (hitems.length == 0) {
|
if (hitems.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return hitems[0];
|
return hitems[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setHistorySelectionNum(hnum: string): void {
|
setHistorySelectionNum(hnum: string): void {
|
||||||
const hitems = this.getFilteredHistoryItems();
|
const hitems = this.filteredHistoryItems;
|
||||||
for (const [i, hitem] of hitems.entries()) {
|
for (const [i, hitem] of hitems.entries()) {
|
||||||
if (hitem.historynum == hnum) {
|
if (hitem.historynum == hnum) {
|
||||||
this.setHistoryIndex(i + 1);
|
this.setHistoryIndex(i + 1);
|
||||||
@ -326,37 +321,33 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setHistoryInfo(hinfo: HistoryInfoType): void {
|
setHistoryInfo(hinfo: HistoryInfoType): void {
|
||||||
mobx.action(() => {
|
const oldItem = this.getHistorySelectedItem();
|
||||||
const oldItem = this.getHistorySelectedItem();
|
const 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;
|
if (hinfo.historytype == "session" || hinfo.historytype == "global") {
|
||||||
if (hinfo.historytype == "session" || hinfo.historytype == "global") {
|
this.historyQueryOpts.get().limitRemote = false;
|
||||||
this.historyQueryOpts.get().limitRemote = false;
|
this.historyQueryOpts.get().limitRemoteInstance = false;
|
||||||
this.historyQueryOpts.get().limitRemoteInstance = false;
|
}
|
||||||
|
if (this.historyAfterLoadIndex == -1) {
|
||||||
|
const bestIndex = this.findBestNewIndex(oldItem);
|
||||||
|
setTimeout(() => this.setHistoryIndex(bestIndex, true), 100);
|
||||||
|
} else if (this.historyAfterLoadIndex) {
|
||||||
|
if (hitems.length >= this.historyAfterLoadIndex) {
|
||||||
|
this.setHistoryIndex(this.historyAfterLoadIndex);
|
||||||
}
|
}
|
||||||
if (this.historyAfterLoadIndex == -1) {
|
}
|
||||||
const bestIndex = this.findBestNewIndex(oldItem);
|
this.historyAfterLoadIndex = 0;
|
||||||
setTimeout(() => this.setHistoryIndex(bestIndex, true), 100);
|
if (hinfo.show) {
|
||||||
} else if (this.historyAfterLoadIndex) {
|
this.openHistory();
|
||||||
if (hitems.length >= this.historyAfterLoadIndex) {
|
}
|
||||||
this.setHistoryIndex(this.historyAfterLoadIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.historyAfterLoadIndex = 0;
|
|
||||||
if (hinfo.show) {
|
|
||||||
this.openHistory();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFilteredHistoryItems(): HistoryItem[] {
|
@mobx.computed
|
||||||
return this.filteredHistoryItems.get();
|
get filteredHistoryItems(): HistoryItem[] {
|
||||||
}
|
|
||||||
|
|
||||||
_getFilteredHistoryItems(): HistoryItem[] {
|
|
||||||
const hitems: HistoryItem[] = this.historyItems.get() ?? [];
|
const hitems: HistoryItem[] = this.historyItems.get() ?? [];
|
||||||
const rtn: HistoryItem[] = [];
|
const rtn: HistoryItem[] = [];
|
||||||
const opts: HistoryQueryOpts = mobx.toJS(this.historyQueryOpts.get());
|
const opts: HistoryQueryOpts = mobx.toJS(this.historyQueryOpts.get());
|
||||||
@ -416,16 +407,15 @@ class InputModel {
|
|||||||
elem.scrollIntoView({ block: "nearest" });
|
elem.scrollIntoView({ block: "nearest" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
grabSelectedHistoryItem(): void {
|
grabSelectedHistoryItem(): void {
|
||||||
const hitem = this.getHistorySelectedItem();
|
const hitem = this.getHistorySelectedItem();
|
||||||
if (hitem == null) {
|
if (hitem == null) {
|
||||||
this.resetHistory();
|
this.resetHistory();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mobx.action(() => {
|
this.resetInput();
|
||||||
this.resetInput();
|
this.curLine = hitem.cmdstr;
|
||||||
this.setCurLine(hitem.cmdstr);
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closes the auxiliary view if it is open, focuses the main input
|
// Closes the auxiliary view if it is open, focuses the main input
|
||||||
@ -449,8 +439,8 @@ class InputModel {
|
|||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
this.auxViewFocus.set(view != null);
|
this.auxViewFocus.set(view != null);
|
||||||
this.activeAuxView.set(view);
|
this.activeAuxView.set(view);
|
||||||
|
this.giveFocus();
|
||||||
})();
|
})();
|
||||||
this.giveFocus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the focus state of the auxiliary view. If true, the view will get focus. Otherwise, the main input will get focus.
|
// Gets the focus state of the auxiliary view. If true, the view will get focus. Otherwise, the main input will get focus.
|
||||||
@ -463,39 +453,33 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sets the focus state of the auxiliary view. If true, the view will get focus. Otherwise, the main input will get focus.
|
// Sets the focus state of the auxiliary view. If true, the view will get focus. Otherwise, the main input will get focus.
|
||||||
|
@mobx.action
|
||||||
setAuxViewFocus(focus: boolean): void {
|
setAuxViewFocus(focus: boolean): void {
|
||||||
mobx.action(() => {
|
this.auxViewFocus.set(focus);
|
||||||
this.auxViewFocus.set(focus);
|
|
||||||
})();
|
|
||||||
this.giveFocus();
|
this.giveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.computed
|
||||||
shouldRenderAuxViewKeybindings(view: InputAuxViewType): boolean {
|
shouldRenderAuxViewKeybindings(view: InputAuxViewType): boolean {
|
||||||
return mobx
|
if (view != null && this.getActiveAuxView() != view) {
|
||||||
.computed(() => {
|
return false;
|
||||||
if (view != null && this.getActiveAuxView() != view) {
|
}
|
||||||
return false;
|
if (view != null && !this.getAuxViewFocus()) {
|
||||||
}
|
return false;
|
||||||
if (view != null && !this.getAuxViewFocus()) {
|
}
|
||||||
return false;
|
if (view == null && this.hasFocus() && !this.getAuxViewFocus()) {
|
||||||
}
|
return true;
|
||||||
if (view == null && this.hasFocus() && !this.getAuxViewFocus()) {
|
}
|
||||||
return true;
|
if (view != null && this.getAuxViewFocus()) {
|
||||||
}
|
return true;
|
||||||
if (view != null && this.getAuxViewFocus()) {
|
}
|
||||||
return true;
|
if (GlobalModel.getActiveScreen().getFocusType() == "input" && GlobalModel.activeMainView.get() == "session") {
|
||||||
}
|
return true;
|
||||||
if (
|
}
|
||||||
GlobalModel.getActiveScreen().getFocusType() == "input" &&
|
return false;
|
||||||
GlobalModel.activeMainView.get() == "session"
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setHistoryIndex(hidx: number, force?: boolean): void {
|
setHistoryIndex(hidx: number, force?: boolean): void {
|
||||||
if (hidx < 0) {
|
if (hidx < 0) {
|
||||||
return;
|
return;
|
||||||
@ -503,18 +487,16 @@ class InputModel {
|
|||||||
if (!force && this.historyIndex.get() == hidx) {
|
if (!force && this.historyIndex.get() == hidx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mobx.action(() => {
|
this.historyIndex.set(hidx);
|
||||||
this.historyIndex.set(hidx);
|
if (this.getActiveAuxView() == appconst.InputAuxView_History) {
|
||||||
if (this.getActiveAuxView() == appconst.InputAuxView_History) {
|
let hitem = this.getHistorySelectedItem();
|
||||||
let hitem = this.getHistorySelectedItem();
|
if (hitem == null) {
|
||||||
if (hitem == null) {
|
hitem = this.getFirstHistoryItem();
|
||||||
hitem = this.getFirstHistoryItem();
|
|
||||||
}
|
|
||||||
if (hitem != null) {
|
|
||||||
this.scrollHistoryItemIntoView(hitem.historynum);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})();
|
if (hitem != null) {
|
||||||
|
this.scrollHistoryItemIntoView(hitem.historynum);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
moveHistorySelection(amt: number): void {
|
moveHistorySelection(amt: number): void {
|
||||||
@ -524,7 +506,7 @@ class InputModel {
|
|||||||
if (!this.isHistoryLoaded()) {
|
if (!this.isHistoryLoaded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const hitems = this.getFilteredHistoryItems();
|
const hitems = this.filteredHistoryItems;
|
||||||
let idx = this.historyIndex.get() + amt;
|
let idx = this.historyIndex.get() + amt;
|
||||||
if (idx < 0) {
|
if (idx < 0) {
|
||||||
idx = 0;
|
idx = 0;
|
||||||
@ -535,11 +517,10 @@ class InputModel {
|
|||||||
this.setHistoryIndex(idx);
|
this.setHistoryIndex(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
flashInfoMsg(info: InfoType, timeoutMs: number): void {
|
flashInfoMsg(info: InfoType, timeoutMs: number): void {
|
||||||
this._clearInfoTimeout();
|
this._clearInfoTimeout();
|
||||||
mobx.action(() => {
|
this.infoMsg.set(info);
|
||||||
this.infoMsg.set(info);
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (info == null && this.getActiveAuxView() == appconst.InputAuxView_Info) {
|
if (info == null && this.getActiveAuxView() == appconst.InputAuxView_Info) {
|
||||||
this.setActiveAuxView(null);
|
this.setActiveAuxView(null);
|
||||||
@ -578,7 +559,7 @@ class InputModel {
|
|||||||
) {
|
) {
|
||||||
const curBlockRef = this.codeSelectBlockRefArray[this.codeSelectSelectedIndex.get()];
|
const curBlockRef = this.codeSelectBlockRefArray[this.codeSelectSelectedIndex.get()];
|
||||||
const codeText = curBlockRef.current.innerText.replace(/\n$/, ""); // remove trailing newline
|
const codeText = curBlockRef.current.innerText.replace(/\n$/, ""); // remove trailing newline
|
||||||
this.setCurLine(codeText);
|
this.curLine = codeText;
|
||||||
this.giveFocus();
|
this.giveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -594,72 +575,68 @@ class InputModel {
|
|||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
setCodeSelectSelectedCodeBlock(blockIndex: number) {
|
setCodeSelectSelectedCodeBlock(blockIndex: number) {
|
||||||
mobx.action(() => {
|
if (blockIndex >= 0 && blockIndex < this.codeSelectBlockRefArray.length) {
|
||||||
if (blockIndex >= 0 && blockIndex < this.codeSelectBlockRefArray.length) {
|
this.codeSelectSelectedIndex.set(blockIndex);
|
||||||
this.codeSelectSelectedIndex.set(blockIndex);
|
const currentRef = this.codeSelectBlockRefArray[blockIndex].current;
|
||||||
const currentRef = this.codeSelectBlockRefArray[blockIndex].current;
|
if (currentRef != null && this.aiChatWindowRef?.current != null) {
|
||||||
if (currentRef != null && this.aiChatWindowRef?.current != null) {
|
const chatWindowTop = this.aiChatWindowRef.current.scrollTop;
|
||||||
const chatWindowTop = this.aiChatWindowRef.current.scrollTop;
|
const chatWindowBottom = chatWindowTop + this.aiChatWindowRef.current.clientHeight - 100;
|
||||||
const chatWindowBottom = chatWindowTop + this.aiChatWindowRef.current.clientHeight - 100;
|
const elemTop = currentRef.offsetTop;
|
||||||
const elemTop = currentRef.offsetTop;
|
let elemBottom = elemTop - currentRef.offsetHeight;
|
||||||
let elemBottom = elemTop - currentRef.offsetHeight;
|
const elementIsInView = elemBottom < chatWindowBottom && elemTop > chatWindowTop;
|
||||||
const elementIsInView = elemBottom < chatWindowBottom && elemTop > chatWindowTop;
|
if (!elementIsInView) {
|
||||||
if (!elementIsInView) {
|
this.aiChatWindowRef.current.scrollTop = elemBottom - this.aiChatWindowRef.current.clientHeight / 3;
|
||||||
this.aiChatWindowRef.current.scrollTop =
|
|
||||||
elemBottom - this.aiChatWindowRef.current.clientHeight / 3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.codeSelectBlockRefArray = [];
|
}
|
||||||
this.setAIChatFocus();
|
this.codeSelectBlockRefArray = [];
|
||||||
})();
|
this.setAIChatFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
codeSelectSelectNextNewestCodeBlock() {
|
codeSelectSelectNextNewestCodeBlock() {
|
||||||
// oldest code block = index 0 in array
|
// oldest code block = index 0 in array
|
||||||
// this decrements codeSelectSelected index
|
// this decrements codeSelectSelected index
|
||||||
mobx.action(() => {
|
if (this.codeSelectSelectedIndex.get() == this.codeSelectTop) {
|
||||||
if (this.codeSelectSelectedIndex.get() == this.codeSelectTop) {
|
this.codeSelectSelectedIndex.set(this.codeSelectBottom);
|
||||||
this.codeSelectSelectedIndex.set(this.codeSelectBottom);
|
} else if (this.codeSelectSelectedIndex.get() == this.codeSelectBottom) {
|
||||||
} else if (this.codeSelectSelectedIndex.get() == this.codeSelectBottom) {
|
return;
|
||||||
return;
|
}
|
||||||
|
const incBlockIndex = this.codeSelectSelectedIndex.get() + 1;
|
||||||
|
if (this.codeSelectSelectedIndex.get() == this.codeSelectBlockRefArray.length - 1) {
|
||||||
|
this.codeSelectDeselectAll();
|
||||||
|
if (this.aiChatWindowRef?.current != null) {
|
||||||
|
this.aiChatWindowRef.current.scrollTop = this.aiChatWindowRef.current.scrollHeight;
|
||||||
}
|
}
|
||||||
const incBlockIndex = this.codeSelectSelectedIndex.get() + 1;
|
}
|
||||||
if (this.codeSelectSelectedIndex.get() == this.codeSelectBlockRefArray.length - 1) {
|
if (incBlockIndex >= 0 && incBlockIndex < this.codeSelectBlockRefArray.length) {
|
||||||
this.codeSelectDeselectAll();
|
this.setCodeSelectSelectedCodeBlock(incBlockIndex);
|
||||||
if (this.aiChatWindowRef?.current != null) {
|
}
|
||||||
this.aiChatWindowRef.current.scrollTop = this.aiChatWindowRef.current.scrollHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (incBlockIndex >= 0 && incBlockIndex < this.codeSelectBlockRefArray.length) {
|
|
||||||
this.setCodeSelectSelectedCodeBlock(incBlockIndex);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
codeSelectSelectNextOldestCodeBlock() {
|
codeSelectSelectNextOldestCodeBlock() {
|
||||||
mobx.action(() => {
|
if (this.codeSelectSelectedIndex.get() == this.codeSelectBottom) {
|
||||||
if (this.codeSelectSelectedIndex.get() == this.codeSelectBottom) {
|
if (this.codeSelectBlockRefArray.length > 0) {
|
||||||
if (this.codeSelectBlockRefArray.length > 0) {
|
this.codeSelectSelectedIndex.set(this.codeSelectBlockRefArray.length);
|
||||||
this.codeSelectSelectedIndex.set(this.codeSelectBlockRefArray.length);
|
} else {
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (this.codeSelectSelectedIndex.get() == this.codeSelectTop) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const decBlockIndex = this.codeSelectSelectedIndex.get() - 1;
|
} else if (this.codeSelectSelectedIndex.get() == this.codeSelectTop) {
|
||||||
if (decBlockIndex < 0) {
|
return;
|
||||||
this.codeSelectDeselectAll(this.codeSelectTop);
|
}
|
||||||
if (this.aiChatWindowRef?.current != null) {
|
const decBlockIndex = this.codeSelectSelectedIndex.get() - 1;
|
||||||
this.aiChatWindowRef.current.scrollTop = 0;
|
if (decBlockIndex < 0) {
|
||||||
}
|
this.codeSelectDeselectAll(this.codeSelectTop);
|
||||||
|
if (this.aiChatWindowRef?.current != null) {
|
||||||
|
this.aiChatWindowRef.current.scrollTop = 0;
|
||||||
}
|
}
|
||||||
if (decBlockIndex >= 0 && decBlockIndex < this.codeSelectBlockRefArray.length) {
|
}
|
||||||
this.setCodeSelectSelectedCodeBlock(decBlockIndex);
|
if (decBlockIndex >= 0 && decBlockIndex < this.codeSelectBlockRefArray.length) {
|
||||||
}
|
this.setCodeSelectSelectedCodeBlock(decBlockIndex);
|
||||||
})();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCodeSelectSelectedIndex() {
|
getCodeSelectSelectedIndex() {
|
||||||
@ -684,6 +661,7 @@ class InputModel {
|
|||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
openAIAssistantChat(): void {
|
openAIAssistantChat(): void {
|
||||||
this.setActiveAuxView(appconst.InputAuxView_AIChat);
|
this.setActiveAuxView(appconst.InputAuxView_AIChat);
|
||||||
this.setAuxViewFocus(true);
|
this.setAuxViewFocus(true);
|
||||||
@ -723,19 +701,19 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
clearInfoMsg(setNull: boolean): void {
|
clearInfoMsg(setNull: boolean): void {
|
||||||
this._clearInfoTimeout();
|
this._clearInfoTimeout();
|
||||||
|
|
||||||
if (this.getActiveAuxView() == appconst.InputAuxView_Info) {
|
if (this.getActiveAuxView() == appconst.InputAuxView_Info) {
|
||||||
this.setActiveAuxView(null);
|
this.setActiveAuxView(null);
|
||||||
}
|
}
|
||||||
mobx.action(() => {
|
if (setNull) {
|
||||||
if (setNull) {
|
this.infoMsg.set(null);
|
||||||
this.infoMsg.set(null);
|
}
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
toggleInfoMsg(): void {
|
toggleInfoMsg(): void {
|
||||||
this._clearInfoTimeout();
|
this._clearInfoTimeout();
|
||||||
if (this.activeAuxView.get() == appconst.InputAuxView_Info) {
|
if (this.activeAuxView.get() == appconst.InputAuxView_Info) {
|
||||||
@ -747,63 +725,51 @@ class InputModel {
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
uiSubmitCommand(): void {
|
uiSubmitCommand(): void {
|
||||||
|
const commandStr = this.curLine;
|
||||||
|
if (commandStr.trim() == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
const commandStr = this.getCurLine();
|
|
||||||
if (commandStr.trim() == "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.resetInput();
|
this.resetInput();
|
||||||
this.globalModel.submitRawCommand(commandStr, true, true);
|
|
||||||
})();
|
})();
|
||||||
|
this.globalModel.submitRawCommand(commandStr, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmpty(): boolean {
|
isEmpty(): boolean {
|
||||||
return this.getCurLine().trim() == "";
|
return this.curLine.trim() == "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
resetInputMode(): void {
|
resetInputMode(): void {
|
||||||
mobx.action(() => {
|
this.setInputMode(null);
|
||||||
this.setInputMode(null);
|
this.curLine = "";
|
||||||
this.setCurLine("");
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurLine(val: string): void {
|
|
||||||
const hidx = this.historyIndex.get();
|
|
||||||
mobx.action(() => {
|
|
||||||
if (this.modHistory.length <= hidx) {
|
|
||||||
this.modHistory.length = hidx + 1;
|
|
||||||
}
|
|
||||||
this.modHistory[hidx] = val;
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
resetInput(): void {
|
resetInput(): void {
|
||||||
mobx.action(() => {
|
this.setActiveAuxView(null);
|
||||||
this.setActiveAuxView(null);
|
this.inputMode.set(null);
|
||||||
this.inputMode.set(null);
|
this.resetHistory();
|
||||||
this.resetHistory();
|
this.dropModHistory(false);
|
||||||
this.dropModHistory(false);
|
this.infoMsg.set(null);
|
||||||
this.infoMsg.set(null);
|
this.inputExpanded.set(false);
|
||||||
this.inputExpanded.set(false);
|
this._clearInfoTimeout();
|
||||||
this._clearInfoTimeout();
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mobx.action
|
||||||
@boundMethod
|
@boundMethod
|
||||||
toggleExpandInput(): void {
|
toggleExpandInput(): void {
|
||||||
mobx.action(() => {
|
this.inputExpanded.set(!this.inputExpanded.get());
|
||||||
this.inputExpanded.set(!this.inputExpanded.get());
|
this.forceInputFocus = true;
|
||||||
this.forceInputFocus = true;
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurLine(): string {
|
@mobx.computed
|
||||||
|
get curLine(): string {
|
||||||
const 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];
|
||||||
}
|
}
|
||||||
const hitems = this.getFilteredHistoryItems();
|
const hitems = this.filteredHistoryItems;
|
||||||
if (hidx == 0 || hitems == null || hidx > hitems.length) {
|
if (hidx == 0 || hitems == null || hidx > hitems.length) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -814,31 +780,40 @@ class InputModel {
|
|||||||
return hitem.cmdstr;
|
return hitem.cmdstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
dropModHistory(keepLine0: boolean): void {
|
set curLine(val: string) {
|
||||||
|
this.lastCurLine = this.curLine;
|
||||||
|
const hidx = this.historyIndex.get();
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
if (keepLine0) {
|
if (this.modHistory.length <= hidx) {
|
||||||
if (this.modHistory.length > 1) {
|
this.modHistory.length = hidx + 1;
|
||||||
this.modHistory.splice(1, this.modHistory.length - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.modHistory.replace([""]);
|
|
||||||
}
|
}
|
||||||
|
this.modHistory[hidx] = val;
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
resetHistory(): void {
|
@mobx.action
|
||||||
mobx.action(() => {
|
dropModHistory(keepLine0: boolean): void {
|
||||||
if (this.getActiveAuxView() == appconst.InputAuxView_History) {
|
if (keepLine0) {
|
||||||
this.setActiveAuxView(null);
|
if (this.modHistory.length > 1) {
|
||||||
|
this.modHistory.splice(1, this.modHistory.length - 1);
|
||||||
}
|
}
|
||||||
this.historyLoading.set(false);
|
} else {
|
||||||
this.historyType.set("screen");
|
this.modHistory.replace([""]);
|
||||||
this.historyItems.set(null);
|
}
|
||||||
this.historyIndex.set(0);
|
}
|
||||||
this.historyQueryOpts.set(getDefaultHistoryQueryOpts());
|
|
||||||
this.historyAfterLoadIndex = 0;
|
@mobx.action
|
||||||
this.dropModHistory(true);
|
resetHistory(): void {
|
||||||
})();
|
if (this.getActiveAuxView() == appconst.InputAuxView_History) {
|
||||||
|
this.setActiveAuxView(null);
|
||||||
|
}
|
||||||
|
this.historyLoading.set(false);
|
||||||
|
this.historyType.set("screen");
|
||||||
|
this.historyItems.set(null);
|
||||||
|
this.historyIndex.set(0);
|
||||||
|
this.historyQueryOpts.set(getDefaultHistoryQueryOpts());
|
||||||
|
this.historyAfterLoadIndex = 0;
|
||||||
|
this.dropModHistory(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,6 @@ import { GlobalCommandRunner } from "./global";
|
|||||||
import { clearMonoFontCache, getMonoFontSize } from "@/util/textmeasure";
|
import { clearMonoFontCache, getMonoFontSize } from "@/util/textmeasure";
|
||||||
import type { TermWrap } from "@/plugins/terminal/term";
|
import type { TermWrap } from "@/plugins/terminal/term";
|
||||||
import * as util from "@/util/util";
|
import * as util from "@/util/util";
|
||||||
import { url } from "node:inspector";
|
|
||||||
|
|
||||||
type SWLinePtr = {
|
type SWLinePtr = {
|
||||||
line: LineType;
|
line: LineType;
|
||||||
@ -1327,7 +1326,21 @@ class Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
submitCommandPacket(cmdPk: FeCmdPacketType, interactive: boolean): Promise<CommandRtnType> {
|
/**
|
||||||
|
* Submits a command packet to the server and processes the response.
|
||||||
|
* @param cmdPk The command packet to submit.
|
||||||
|
* @param interactive Whether the command is interactive.
|
||||||
|
* @param runUpdate Whether to run the update after the command is submitted. If true, the update will be processed and the frontend will be updated. If false, the update will be returned in the promise.
|
||||||
|
* @returns A promise that resolves to a CommandRtnType.
|
||||||
|
* @throws An error if the command fails.
|
||||||
|
* @see CommandRtnType
|
||||||
|
* @see FeCmdPacketType
|
||||||
|
**/
|
||||||
|
submitCommandPacket(
|
||||||
|
cmdPk: FeCmdPacketType,
|
||||||
|
interactive: boolean,
|
||||||
|
runUpdate: boolean = true
|
||||||
|
): Promise<CommandRtnType> {
|
||||||
if (this.debugCmds > 0) {
|
if (this.debugCmds > 0) {
|
||||||
console.log("[cmd]", cmdPacketString(cmdPk));
|
console.log("[cmd]", cmdPacketString(cmdPk));
|
||||||
if (this.debugCmds > 1) {
|
if (this.debugCmds > 1) {
|
||||||
@ -1345,16 +1358,20 @@ class Model {
|
|||||||
})
|
})
|
||||||
.then((resp) => handleJsonFetchResponse(url, resp))
|
.then((resp) => handleJsonFetchResponse(url, resp))
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
mobx.action(() => {
|
return mobx.action(() => {
|
||||||
const update = data.data;
|
const update = data.data;
|
||||||
if (update != null) {
|
if (update != null) {
|
||||||
this.runUpdate(update, interactive);
|
if (runUpdate) {
|
||||||
|
this.runUpdate(update, interactive);
|
||||||
|
} else {
|
||||||
|
return { success: true, update: update };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (interactive && !this.isInfoUpdate(update)) {
|
if (interactive && !this.isInfoUpdate(update)) {
|
||||||
this.inputModel.clearInfoMsg(true);
|
this.inputModel.clearInfoMsg(true);
|
||||||
}
|
}
|
||||||
|
return { success: true };
|
||||||
})();
|
})();
|
||||||
return { success: true };
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.errorHandler("calling run-command", err, interactive);
|
this.errorHandler("calling run-command", err, interactive);
|
||||||
@ -1367,12 +1384,23 @@ class Model {
|
|||||||
return prtn;
|
return prtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits a command to the server and processes the response.
|
||||||
|
* @param metaCmd The meta command to run.
|
||||||
|
* @param metaSubCmd The meta subcommand to run.
|
||||||
|
* @param args The arguments to pass to the command.
|
||||||
|
* @param kwargs The keyword arguments to pass to the command.
|
||||||
|
* @param interactive Whether the command is interactive.
|
||||||
|
* @param runUpdate Whether to run the update after the command is submitted. If true, the update will be processed and the frontend will be updated. If false, the update will be returned in the promise.
|
||||||
|
* @returns A promise that resolves to a CommandRtnType.
|
||||||
|
*/
|
||||||
submitCommand(
|
submitCommand(
|
||||||
metaCmd: string,
|
metaCmd: string,
|
||||||
metaSubCmd: string,
|
metaSubCmd: string,
|
||||||
args: string[],
|
args: string[],
|
||||||
kwargs: Record<string, string>,
|
kwargs: Record<string, string>,
|
||||||
interactive: boolean
|
interactive: boolean,
|
||||||
|
runUpdate: boolean = true
|
||||||
): Promise<CommandRtnType> {
|
): Promise<CommandRtnType> {
|
||||||
const pk: FeCmdPacketType = {
|
const pk: FeCmdPacketType = {
|
||||||
type: "fecmd",
|
type: "fecmd",
|
||||||
@ -1393,7 +1421,7 @@ class Model {
|
|||||||
pk.interactive
|
pk.interactive
|
||||||
);
|
);
|
||||||
*/
|
*/
|
||||||
return this.submitCommandPacket(pk, interactive);
|
return this.submitCommandPacket(pk, interactive, runUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSingleEphemeralCommandOutput(url: URL): Promise<string> {
|
getSingleEphemeralCommandOutput(url: URL): Promise<string> {
|
||||||
@ -1412,12 +1440,10 @@ class Model {
|
|||||||
let stderr = "";
|
let stderr = "";
|
||||||
if (ephemeralCommandResponse.stdouturl) {
|
if (ephemeralCommandResponse.stdouturl) {
|
||||||
const url = new URL(this.getBaseHostPort() + ephemeralCommandResponse.stdouturl);
|
const url = new URL(this.getBaseHostPort() + ephemeralCommandResponse.stdouturl);
|
||||||
console.log("stdouturl", url);
|
|
||||||
stdout = await this.getSingleEphemeralCommandOutput(url);
|
stdout = await this.getSingleEphemeralCommandOutput(url);
|
||||||
}
|
}
|
||||||
if (ephemeralCommandResponse.stderrurl) {
|
if (ephemeralCommandResponse.stderrurl) {
|
||||||
const url = new URL(this.getBaseHostPort() + ephemeralCommandResponse.stderrurl);
|
const url = new URL(this.getBaseHostPort() + ephemeralCommandResponse.stderrurl);
|
||||||
console.log("stderrurl", url);
|
|
||||||
stderr = await this.getSingleEphemeralCommandOutput(url);
|
stderr = await this.getSingleEphemeralCommandOutput(url);
|
||||||
}
|
}
|
||||||
return { stdout: stdout, stderr: stderr };
|
return { stdout: stdout, stderr: stderr };
|
||||||
@ -1476,14 +1502,14 @@ class Model {
|
|||||||
interactive: interactive,
|
interactive: interactive,
|
||||||
ephemeralopts: ephemeralopts,
|
ephemeralopts: ephemeralopts,
|
||||||
};
|
};
|
||||||
console.log(
|
// console.log(
|
||||||
"CMD",
|
// "CMD",
|
||||||
pk.metacmd + (pk.metasubcmd != null ? ":" + pk.metasubcmd : ""),
|
// pk.metacmd + (pk.metasubcmd != null ? ":" + pk.metasubcmd : ""),
|
||||||
pk.args,
|
// pk.args,
|
||||||
pk.kwargs,
|
// pk.kwargs,
|
||||||
pk.interactive,
|
// pk.interactive,
|
||||||
pk.ephemeralopts
|
// pk.ephemeralopts
|
||||||
);
|
// );
|
||||||
return this.submitEphemeralCommandPacket(pk, interactive);
|
return this.submitEphemeralCommandPacket(pk, interactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
src/types/custom.d.ts
vendored
1
src/types/custom.d.ts
vendored
@ -821,6 +821,7 @@ declare global {
|
|||||||
type CommandRtnType = {
|
type CommandRtnType = {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
update?: UpdatePacket;
|
||||||
};
|
};
|
||||||
|
|
||||||
type EphemeralCommandOutputType = {
|
type EphemeralCommandOutputType = {
|
||||||
|
@ -833,6 +833,7 @@ type RunPacketType struct {
|
|||||||
Detached bool `json:"detached,omitempty"`
|
Detached bool `json:"detached,omitempty"`
|
||||||
ReturnState bool `json:"returnstate,omitempty"`
|
ReturnState bool `json:"returnstate,omitempty"`
|
||||||
IsSudo bool `json:"issudo,omitempty"`
|
IsSudo bool `json:"issudo,omitempty"`
|
||||||
|
Timeout time.Duration `json:"timeout"` // TODO: added vnext. This is the timeout for the command to run. If the command does not complete in this time, it will be killed. The default zero value will not impose a timeout.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*RunPacketType) GetType() string {
|
func (*RunPacketType) GetType() string {
|
||||||
|
@ -926,12 +926,11 @@ func RunCommandSimple(pk *packet.RunPacketType, sender *packet.PacketSender, fro
|
|||||||
rcFileName = fmt.Sprintf("/dev/fd/%d", rcFileFdNum)
|
rcFileName = fmt.Sprintf("/dev/fd/%d", rcFileFdNum)
|
||||||
}
|
}
|
||||||
if cmd.TmpRcFileName != "" {
|
if cmd.TmpRcFileName != "" {
|
||||||
go func() {
|
time.AfterFunc(2*time.Second, func() {
|
||||||
// cmd.Close() will also remove rcFileName
|
// cmd.Close() will also remove rcFileName
|
||||||
// adding this to also try to proactively clean up after 2-seconds.
|
// adding this to also try to proactively clean up after 2-seconds.
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
os.Remove(cmd.TmpRcFileName)
|
os.Remove(cmd.TmpRcFileName)
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
fullCmdStr := pk.Command
|
fullCmdStr := pk.Command
|
||||||
if pk.ReturnState {
|
if pk.ReturnState {
|
||||||
@ -1109,6 +1108,14 @@ func RunCommandSimple(pk *packet.RunPacketType, sender *packet.PacketSender, fro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pk.Timeout > 0 {
|
||||||
|
// Cancel the command if it takes too long
|
||||||
|
time.AfterFunc(pk.Timeout, func() {
|
||||||
|
cmd.Cmd.Cancel()
|
||||||
|
cmd.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
return cmd, nil
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
|
||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbase"
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbase"
|
||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/waveenc"
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/waveenc"
|
||||||
)
|
)
|
||||||
@ -110,6 +111,7 @@ func (pipe *BufferedPipe) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
|
|
||||||
// Close the pipe. This will cause any blocking WriteTo calls to return.
|
// Close the pipe. This will cause any blocking WriteTo calls to return.
|
||||||
func (pipe *BufferedPipe) Close() error {
|
func (pipe *BufferedPipe) Close() error {
|
||||||
|
wlog.Logf("closing buffered pipe %s", pipe.Key)
|
||||||
defer pipe.bufferDataCond.Broadcast()
|
defer pipe.bufferDataCond.Broadcast()
|
||||||
pipe.closed.Store(true)
|
pipe.closed.Store(true)
|
||||||
return nil
|
return nil
|
||||||
|
@ -1987,9 +1987,22 @@ func RunCommand(ctx context.Context, rcOpts RunCommandOpts, runPacket *packet.Ru
|
|||||||
// Setting UsePty to false will ensure that the outputs get written to the correct file descriptors to extract stdout and stderr
|
// Setting UsePty to false will ensure that the outputs get written to the correct file descriptors to extract stdout and stderr
|
||||||
runPacket.UsePty = rcOpts.EphemeralOpts.UsePty
|
runPacket.UsePty = rcOpts.EphemeralOpts.UsePty
|
||||||
|
|
||||||
// Ephemeral commands can override the cwd without persisting it to the DB
|
// Ephemeral commands can override the current working directory. We need to expand the home dir if it's relative.
|
||||||
if rcOpts.EphemeralOpts.OverrideCwd != "" {
|
if rcOpts.EphemeralOpts.OverrideCwd != "" {
|
||||||
currentState.Cwd = rcOpts.EphemeralOpts.OverrideCwd
|
overrideCwd := rcOpts.EphemeralOpts.OverrideCwd
|
||||||
|
if !strings.HasPrefix(overrideCwd, "/") {
|
||||||
|
expandedCwd, err := msh.GetRemoteRuntimeState().ExpandHomeDir(overrideCwd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("cannot expand home dir for cwd: %w", err)
|
||||||
|
}
|
||||||
|
overrideCwd = expandedCwd
|
||||||
|
}
|
||||||
|
currentState.Cwd = overrideCwd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ephemeral commands can override the timeout
|
||||||
|
if rcOpts.EphemeralOpts.TimeoutMs > 0 {
|
||||||
|
runPacket.Timeout = time.Duration(rcOpts.EphemeralOpts.TimeoutMs) * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ephemeral commands can override the env without persisting it to the DB
|
// Ephemeral commands can override the env without persisting it to the DB
|
||||||
@ -2405,6 +2418,7 @@ func (msh *MShellProc) handleCmdStartError(rct *RunCmdType, startErr error) {
|
|||||||
defer msh.RemoveRunningCmd(rct.CK)
|
defer msh.RemoveRunningCmd(rct.CK)
|
||||||
if rct.EphemeralOpts != nil {
|
if rct.EphemeralOpts != nil {
|
||||||
// nothing to do for ephemeral commands besides remove the running command
|
// nothing to do for ephemeral commands besides remove the running command
|
||||||
|
log.Printf("ephemeral command start error: %v\n", startErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
@ -2472,6 +2486,11 @@ func (msh *MShellProc) handleCmdDonePacket(rct *RunCmdType, donePk *packet.CmdDo
|
|||||||
|
|
||||||
// Close the ephemeral response writer if it exists
|
// Close the ephemeral response writer if it exists
|
||||||
if rct.EphemeralOpts != nil && rct.EphemeralOpts.ExpectsResponse {
|
if rct.EphemeralOpts != nil && rct.EphemeralOpts.ExpectsResponse {
|
||||||
|
if donePk.ExitCode != 0 {
|
||||||
|
// if the command failed, we need to write the error to the response writer
|
||||||
|
log.Printf("writing error to ephemeral response writer\n")
|
||||||
|
rct.EphemeralOpts.StderrWriter.Write([]byte(fmt.Sprintf("error: %d\n", donePk.ExitCode)))
|
||||||
|
}
|
||||||
log.Printf("closing ephemeral response writers\n")
|
log.Printf("closing ephemeral response writers\n")
|
||||||
defer rct.EphemeralOpts.StdoutWriter.Close()
|
defer rct.EphemeralOpts.StdoutWriter.Close()
|
||||||
defer rct.EphemeralOpts.StderrWriter.Close()
|
defer rct.EphemeralOpts.StderrWriter.Close()
|
||||||
@ -2577,6 +2596,7 @@ func (msh *MShellProc) handleDataPacket(rct *RunCmdType, dataPk *packet.DataPack
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if rct.EphemeralOpts != nil {
|
if rct.EphemeralOpts != nil {
|
||||||
|
log.Printf("ephemeral data packet: %s\n", dataPk.CK)
|
||||||
// Write to the response writer if it's set
|
// Write to the response writer if it's set
|
||||||
if len(realData) > 0 && rct.EphemeralOpts.ExpectsResponse {
|
if len(realData) > 0 && rct.EphemeralOpts.ExpectsResponse {
|
||||||
switch dataPk.FdNum {
|
switch dataPk.FdNum {
|
||||||
@ -2594,6 +2614,9 @@ func (msh *MShellProc) handleDataPacket(rct *RunCmdType, dataPk *packet.DataPack
|
|||||||
log.Printf("error handling data packet: invalid fdnum %d\n", dataPk.FdNum)
|
log.Printf("error handling data packet: invalid fdnum %d\n", dataPk.FdNum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if dataPk.Error != "" {
|
||||||
|
log.Printf("ephemeral data packet error: %s\n", dataPk.Error)
|
||||||
|
}
|
||||||
ack := makeDataAckPacket(dataPk.CK, dataPk.FdNum, len(realData), nil)
|
ack := makeDataAckPacket(dataPk.CK, dataPk.FdNum, len(realData), nil)
|
||||||
msh.ServerProc.Input.SendPacket(ack)
|
msh.ServerProc.Input.SendPacket(ack)
|
||||||
return
|
return
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const {webDev, webProd} = require("./webpack/webpack.web.js");
|
const { webDev, webProd } = require("./webpack/webpack.web.js");
|
||||||
const {electronDev, electronProd} = require("./webpack/webpack.electron.js");
|
const { electronDev, electronProd } = require("./webpack/webpack.electron.js");
|
||||||
|
|
||||||
module.exports = (env) => {
|
module.exports = (env) => {
|
||||||
if (env.prod) {
|
if (env.prod) {
|
||||||
|
Loading…
Reference in New Issue
Block a user