mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f41ac1d5e3 | |||
| 0024f0f3e8 | |||
| 8a3d9628ba | |||
| 7c2265f3e3 | |||
| 4f6c2ee39b | |||
| a1e4e807cc | |||
| 5c85b2b786 | |||
| 4879e90e91 | |||
| a61310afc3 | |||
| 80c781bf4c | |||
| 33400e65ff | |||
| 7b58949027 | |||
| 3be96c949d |
+4
-2
@@ -49,8 +49,10 @@
|
||||
"tsx-control-statements": "^5.1.1",
|
||||
"uuid": "^9.0.0",
|
||||
"winston": "^3.8.2",
|
||||
"xterm": "^5.0.0",
|
||||
"xterm-addon-web-links": "^0.9.0"
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-web-links": "^0.9.0",
|
||||
"xterm-addon-serialize": "^0.11.0",
|
||||
"xterm-addon-webgl": "^0.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.10",
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
--form-element-border-color: rgba(241, 246, 243, 0.15);
|
||||
--form-element-bg-color: var(--app-bg-color);
|
||||
--form-element-text-color: var(--app-text-primary-color);
|
||||
--form-element-primary-text-color: var(--form-element-text-color);
|
||||
--form-element-primary-text-color: var(--app-text-primary-color);
|
||||
--form-element-label-color: var(--app-text-secondary-color);
|
||||
--form-element-primary-color: var(--app-accent-color);
|
||||
--form-element-secondary-color: rgba(255, 255, 255, 0.2);
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
/* form colors */
|
||||
--form-element-border-color: rgba(0, 0, 0, 0.3);
|
||||
--form-element-bg-color: var(--app-bg-color);
|
||||
--form-element-text-color: var(--app-text-color);
|
||||
--form-element-primary-text-color: var(--app-bg-color);
|
||||
--form-element-text-color: var(--app-text-primary-color);
|
||||
--form-element-primary-text-color: var(--app-text-primary-color);
|
||||
--form-element-label-color: rgba(0, 0, 0, 0.6);
|
||||
--form-element-secondary-color: rgba(0, 0, 0, 0.09);
|
||||
--form-element-icon-color: rgb(0, 0, 0, 0.6);
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.danger {
|
||||
background: var(--app-error-color);
|
||||
}
|
||||
|
||||
&.primary.outlined {
|
||||
background: none;
|
||||
border: 1px solid var(--form-element-primary-color);
|
||||
@@ -36,6 +40,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.greyoutlined {
|
||||
background: none;
|
||||
border: 1px solid var(--app-text-secondary-color);
|
||||
|
||||
i {
|
||||
fill: var(--app-text-secondary-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.outlined,
|
||||
&.primary.greyoutlined {
|
||||
&.hover-danger:hover {
|
||||
color: var(--form-element-primary-text-color);
|
||||
border: 1px solid var(--app-error-color);
|
||||
background: var(--app-error-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.outlined.danger {
|
||||
background: none;
|
||||
border: 1px solid var(--app-error-color);
|
||||
|
||||
i {
|
||||
fill: var(--app-error-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.greytext {
|
||||
color: var(--app-text-secondary-color);
|
||||
}
|
||||
|
||||
&.primary.ghost {
|
||||
background: none;
|
||||
i {
|
||||
@@ -43,6 +78,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.primary.ghost.danger {
|
||||
background: none;
|
||||
i {
|
||||
fill: var(--app-error-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
color: var(--form-element-text-color);
|
||||
background: var(--form-element-secondary-color);
|
||||
@@ -56,6 +98,11 @@
|
||||
border: 1px solid var(--form-element-text-color);
|
||||
}
|
||||
|
||||
&.secondary.outlined.danger {
|
||||
background: none;
|
||||
border: 1px solid var(--app-error-color);
|
||||
}
|
||||
|
||||
&.secondary.ghost {
|
||||
background: none;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
};
|
||||
this.wrapperRef = React.createRef();
|
||||
this.menuRef = React.createRef();
|
||||
this.curUuid == uuidv4();
|
||||
this.curUuid = uuidv4();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -100,6 +100,9 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
|
||||
@boundMethod
|
||||
handleClick() {
|
||||
if (!this.state.isOpen || !this.state.isTouched) {
|
||||
this.registerKeybindings();
|
||||
}
|
||||
this.toggleDropdown();
|
||||
}
|
||||
|
||||
@@ -109,6 +112,7 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
this.registerKeybindings();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
registerKeybindings() {
|
||||
let keybindManager = GlobalModel.keybindManager;
|
||||
let domain = "dropdown-" + this.curUuid;
|
||||
@@ -122,6 +126,7 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
});
|
||||
keybindManager.registerKeybinding("control", domain, "generic:cancel", (waveEvent) => {
|
||||
this.setState({ isOpen: false });
|
||||
this.unregisterKeybindings();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("control", domain, "generic:selectAbove", (waveEvent) => {
|
||||
@@ -165,10 +170,12 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleBlur() {
|
||||
this.unregisterKeybindings();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
unregisterKeybindings() {
|
||||
let domain = "dropdown-" + this.curUuid;
|
||||
GlobalModel.keybindManager.unregisterDomain(domain);
|
||||
@@ -189,6 +196,7 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
}
|
||||
onChange(value);
|
||||
this.setState({ isOpen: false, isTouched: true });
|
||||
this.unregisterKeybindings();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
|
||||
@@ -486,10 +486,10 @@ class HistoryView extends React.Component<{}, {}> {
|
||||
options={this.getRemoteItems(rnames, remoteIds)}
|
||||
onChange={this.clickLimitRemote}
|
||||
/>
|
||||
<div className="fromts">
|
||||
{/* <div className="fromts">
|
||||
<div className="fromts-text">From: </div>
|
||||
<DatePicker selectedDate={new Date()} onSelectDate={this.handleFromTsChange} />
|
||||
</div>
|
||||
</div> */}
|
||||
<div
|
||||
className="filter-cmds search-checkbox hoverEffect"
|
||||
title="Filter common commands like 'ls' and 'cd' from the results"
|
||||
|
||||
@@ -25,4 +25,29 @@
|
||||
&.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.keybind-debug-pane {
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
.keybind-pane-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.keybind-level {
|
||||
margin-top: 10px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.keybind-domain {
|
||||
font-size: 14px;
|
||||
margin-left: 20px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import dayjs from "dayjs";
|
||||
import { If, For } from "tsx-control-statements/components";
|
||||
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { GlobalModel } from "@/models";
|
||||
@@ -17,6 +18,41 @@ interface RightSideBarProps {
|
||||
parentRef: React.RefObject<HTMLElement>;
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class KeybindDevPane extends React.Component<{}, {}> {
|
||||
render() {
|
||||
let curActiveKeybinds: Array<{ name: string; domains: Array<string> }> =
|
||||
GlobalModel.keybindManager.getActiveKeybindings();
|
||||
let keybindLevel: { name: string; domains: Array<string> } = null;
|
||||
let domain: string = null;
|
||||
let curVersion = GlobalModel.keybindManager.getActiveKeybindsVersion().get();
|
||||
let levelIdx: number = 0;
|
||||
let domainIdx: number = 0;
|
||||
let lastKeyData = GlobalModel.keybindManager.getLastKeyData();
|
||||
return (
|
||||
<div className="keybind-debug-pane">
|
||||
<div className="keybind-pane-title">Keybind Manager</div>
|
||||
<For index="levelIdx" each="keybindLevel" of={curActiveKeybinds}>
|
||||
<div className="keybind-level" key={"level-" + curVersion + levelIdx}>
|
||||
{keybindLevel.name}
|
||||
</div>
|
||||
<For index="domainIdx" each="domain" of={keybindLevel.domains}>
|
||||
<div className="keybind-domain" key={"domain-" + curVersion + domainIdx}>
|
||||
{domain}
|
||||
</div>
|
||||
</For>
|
||||
</For>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<h1>Last KeyPress Domain: {lastKeyData.domain}</h1>
|
||||
<h1>Last KeyPress key: {lastKeyData.keyPress}</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class RightSideBar extends React.Component<RightSideBarProps, {}> {
|
||||
render() {
|
||||
@@ -35,6 +71,9 @@ class RightSideBar extends React.Component<RightSideBarProps, {}> {
|
||||
<i className="fa-sharp fa-regular fa-xmark"></i>
|
||||
</Button>
|
||||
</div>
|
||||
<If condition={GlobalModel.isDev}>
|
||||
<KeybindDevPane></KeybindDevPane>
|
||||
</If>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ResizableSidebar>
|
||||
|
||||
@@ -11,7 +11,7 @@ import cn from "classnames";
|
||||
import { GlobalModel, GlobalCommandRunner, Screen } from "@/models";
|
||||
import { getMonoFontSize } from "@/util/textmeasure";
|
||||
import * as appconst from "@/app/appconst";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent, WaveKeyboardEvent } from "@/util/keyutil";
|
||||
|
||||
type OV<T> = mobx.IObservableValue<T>;
|
||||
|
||||
@@ -41,9 +41,11 @@ function scrollDiv(div: any, amt: number) {
|
||||
|
||||
class HistoryKeybindings extends React.Component<{ inputObject: TextAreaInput }, {}> {
|
||||
componentDidMount(): void {
|
||||
if (GlobalModel.activeMainView != "session") {
|
||||
return;
|
||||
}
|
||||
let inputModel = GlobalModel.inputModel;
|
||||
let keybindManager = GlobalModel.keybindManager;
|
||||
|
||||
keybindManager.registerKeybinding("pane", "history", "generic:cancel", (waveEvent) => {
|
||||
inputModel.resetHistory();
|
||||
return true;
|
||||
@@ -101,8 +103,12 @@ class HistoryKeybindings extends React.Component<{ inputObject: TextAreaInput },
|
||||
|
||||
class CmdInputKeybindings extends React.Component<{ inputObject: TextAreaInput }, {}> {
|
||||
lastTab: boolean;
|
||||
curPress: string;
|
||||
|
||||
componentDidMount() {
|
||||
if (GlobalModel.activeMainView != "session") {
|
||||
return;
|
||||
}
|
||||
let inputObject = this.props.inputObject;
|
||||
this.lastTab = false;
|
||||
let keybindManager = GlobalModel.keybindManager;
|
||||
@@ -110,6 +116,7 @@ class CmdInputKeybindings extends React.Component<{ inputObject: TextAreaInput }
|
||||
keybindManager.registerKeybinding("pane", "cmdinput", "cmdinput:autocomplete", (waveEvent) => {
|
||||
let lastTab = this.lastTab;
|
||||
this.lastTab = true;
|
||||
this.curPress = "tab";
|
||||
let curLine = inputModel.getCurLine();
|
||||
if (lastTab) {
|
||||
GlobalModel.submitCommand(
|
||||
@@ -178,10 +185,12 @@ class CmdInputKeybindings extends React.Component<{ inputObject: TextAreaInput }
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "cmdinput", "cmdinput:previousHistoryItem", (waveEvent) => {
|
||||
this.curPress = "historyupdown";
|
||||
inputObject.controlP();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "cmdinput", "cmdinput:nextHistoryItem", (waveEvent) => {
|
||||
this.curPress = "historyupdown";
|
||||
inputObject.controlN();
|
||||
return true;
|
||||
});
|
||||
@@ -190,18 +199,22 @@ class CmdInputKeybindings extends React.Component<{ inputObject: TextAreaInput }
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "cmdinput", "generic:selectAbove", (waveEvent) => {
|
||||
inputObject.arrowUpPressed();
|
||||
return true;
|
||||
this.curPress = "historyupdown";
|
||||
let rtn = inputObject.arrowUpPressed();
|
||||
return rtn;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "cmdinput", "generic:selectBelow", (waveEvent) => {
|
||||
inputObject.arrowDownPressed();
|
||||
return true;
|
||||
this.curPress = "historyupdown";
|
||||
let rtn = inputObject.arrowDownPressed();
|
||||
return rtn;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "cmdinput", "generic:selectPageAbove", (waveEvent) => {
|
||||
this.curPress = "historyupdown";
|
||||
inputObject.scrollPage(true);
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "cmdinput", "generic:selectPageBelow", (waveEvent) => {
|
||||
this.curPress = "historyupdown";
|
||||
inputObject.scrollPage(false);
|
||||
return true;
|
||||
});
|
||||
@@ -209,6 +222,16 @@ class CmdInputKeybindings extends React.Component<{ inputObject: TextAreaInput }
|
||||
inputObject.modEnter();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerDomainCallback("cmdinput", (waveEvent) => {
|
||||
if (this.curPress != "tab") {
|
||||
this.lastTab = false;
|
||||
}
|
||||
if (this.curPress != "historyupdown") {
|
||||
inputObject.lastHistoryUpDown = false;
|
||||
}
|
||||
this.curPress = "";
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -161,11 +161,11 @@
|
||||
}
|
||||
|
||||
.newtab-container {
|
||||
margin: 16px;
|
||||
margin: 8px 16px 0 16px;
|
||||
|
||||
.newtab-section {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
padding: 10px 16px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
|
||||
@@ -558,9 +558,6 @@ class ScreenWindowView extends React.Component<{ session: Session; screen: Scree
|
||||
return (
|
||||
<div className="window-view" ref={this.windowViewRef} style={{ width: this.props.width }}>
|
||||
<If condition={lines.length == 0}>
|
||||
<If condition={false && screen.nextLineNum.get() == 1}>
|
||||
<NewTabSettings screen={screen} />
|
||||
</If>
|
||||
<If condition={screen.nextLineNum.get() != 1}>
|
||||
<div className="window-empty" ref={this.windowViewRef} data-screenid={screen.screenId}>
|
||||
<div key="lines" className="lines"></div>
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
position: absolute;
|
||||
top: var(--screentabs-height);
|
||||
width: 100%;
|
||||
height: 330px;
|
||||
transition: height 0.2s ease-in-out;
|
||||
overflow: hidden;
|
||||
z-index: 11;
|
||||
|
||||
@@ -8,7 +8,7 @@ import cn from "classnames";
|
||||
import dayjs from "dayjs";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { GlobalModel, GlobalCommandRunner } from "@/models";
|
||||
import { CmdInput } from "./cmdinput/cmdinput";
|
||||
import { ScreenView } from "./screen/screenview";
|
||||
import { ScreenTabs } from "./screen/tabs";
|
||||
@@ -17,11 +17,17 @@ import * as textmeasure from "@/util/textmeasure";
|
||||
import "./workspace.less";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import type { Screen } from "@/models";
|
||||
import { Button } from "@/elements";
|
||||
import { getRemoteStr, getRemoteStrWithAlias } from "@/common/prompt/prompt";
|
||||
import { TabColorSelector, TabIconSelector, TabNameTextField, TabRemoteSelector } from "./screen/newtabsettings";
|
||||
import * as util from "@/util/util";
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
|
||||
const ScreenDeleteMessage = `
|
||||
Are you sure you want to delete this tab?
|
||||
`.trim();
|
||||
|
||||
class SessionKeybindings extends React.Component<{}, {}> {
|
||||
componentDidMount() {
|
||||
let keybindManager = GlobalModel.keybindManager;
|
||||
@@ -48,33 +54,34 @@ class SessionKeybindings extends React.Component<{}, {}> {
|
||||
GlobalModel.onBracketCmd(1);
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "session", "app:selectLineAbove", (waveEvent) => {
|
||||
keybindManager.registerKeybinding("pane", "screen", "app:selectLineAbove", (waveEvent) => {
|
||||
GlobalModel.onMetaArrowUp();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "session", "app:selectLineBelow", (waveEvent) => {
|
||||
keybindManager.registerKeybinding("pane", "screen", "app:selectLineBelow", (waveEvent) => {
|
||||
GlobalModel.onMetaArrowDown();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "session", "app:restartCommand", (waveEvent) => {
|
||||
keybindManager.registerKeybinding("pane", "screen", "app:restartCommand", (waveEvent) => {
|
||||
GlobalModel.onRestartCommand();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "session", "app:restartLastCommand", (waveEvent) => {
|
||||
keybindManager.registerKeybinding("pane", "screen", "app:restartLastCommand", (waveEvent) => {
|
||||
GlobalModel.onRestartLastCommand();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "session", "app:focusSelectedLine", (waveEvent) => {
|
||||
keybindManager.registerKeybinding("pane", "screen", "app:focusSelectedLine", (waveEvent) => {
|
||||
GlobalModel.onFocusSelectedLine();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("pane", "session", "app:deleteActiveLine", (waveEvent) => {
|
||||
keybindManager.registerKeybinding("pane", "screen", "app:deleteActiveLine", (waveEvent) => {
|
||||
return GlobalModel.handleDeleteActiveLine();
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
GlobalModel.keybindManager.unregisterDomain("session");
|
||||
GlobalModel.keybindManager.unregisterDomain("screen");
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -105,6 +112,29 @@ class TabSettingsPulldownKeybindings extends React.Component<{}, {}> {
|
||||
class TabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "TabSettings-errorMessage" });
|
||||
|
||||
@boundMethod
|
||||
handleDeleteScreen(): void {
|
||||
const { screen } = this.props;
|
||||
if (screen == null) {
|
||||
return;
|
||||
}
|
||||
if (screen.getScreenLines().lines.length == 0) {
|
||||
GlobalCommandRunner.screenDelete(screen.screenId, false);
|
||||
GlobalModel.modalsModel.popModal();
|
||||
return;
|
||||
}
|
||||
let message = ScreenDeleteMessage;
|
||||
let alertRtn = GlobalModel.showAlert({ message: message, confirm: true, markdown: true });
|
||||
alertRtn.then((result) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.screenDelete(screen.screenId, false);
|
||||
util.commandRtnHandler(prtn, this.errorMessage);
|
||||
GlobalModel.modalsModel.popModal();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let { screen } = this.props;
|
||||
let rptr = screen.curRemote.get();
|
||||
@@ -133,6 +163,16 @@ class TabSettings extends React.Component<{ screen: Screen }, {}> {
|
||||
<div className="newtab-section">
|
||||
<TabColorSelector screen={screen} errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
<div className="newtab-spacer" />
|
||||
<div className="newtab-section">
|
||||
<Button
|
||||
onClick={this.handleDeleteScreen}
|
||||
style={{ paddingTop: 4, paddingBottom: 4 }}
|
||||
className="primary greyoutlined greytext hover-danger"
|
||||
>
|
||||
Delete Tab
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -181,7 +221,7 @@ class WorkspaceView extends React.Component<{}, {}> {
|
||||
<i className="fa-solid fa-sharp fa-xmark-large" />
|
||||
</div>
|
||||
<TabSettings key={activeScreen.screenId} screen={activeScreen} />
|
||||
<If condition={showTabSettings}>
|
||||
<If condition={showTabSettings && !isHidden}>
|
||||
<TabSettingsPulldownKeybindings />
|
||||
</If>
|
||||
</div>
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
import * as mobx from "mobx";
|
||||
import { Terminal } from "xterm";
|
||||
import type { ITheme } from "xterm";
|
||||
//TODO: replace with `@xterm/addon-web-links` when it's available as stable
|
||||
import { WebLinksAddon } from "xterm-addon-web-links";
|
||||
import { sprintf } from "sprintf-js";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { windowWidthToCols, windowHeightToRows } from "@/util/textmeasure";
|
||||
import { boundInt } from "@/util/util";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { WebglAddon } from "xterm-addon-webgl";
|
||||
import { WebLinksAddon } from "xterm-addon-web-links";
|
||||
import { SerializeAddon } from "xterm-addon-serialize";
|
||||
|
||||
type DataUpdate = {
|
||||
data: Uint8Array;
|
||||
@@ -20,6 +21,20 @@ type DataUpdate = {
|
||||
const MinTermCols = 10;
|
||||
const MaxTermCols = 1024;
|
||||
|
||||
// detect webgl support
|
||||
function detectWebGLSupport(): boolean {
|
||||
try {
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("webgl");
|
||||
return !!ctx;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const WebGLSupported = detectWebGLSupport();
|
||||
let loggedWebGL = false;
|
||||
|
||||
type TermWrapOpts = {
|
||||
termContext: TermContextUnion;
|
||||
usedRows?: number;
|
||||
@@ -87,6 +102,7 @@ class TermWrap {
|
||||
ptyDataSource: (termContext: TermContextUnion) => Promise<PtyDataType>;
|
||||
initializing: boolean;
|
||||
dataHandler?: (data: string, termWrap: TermWrap) => void;
|
||||
serializeAddon: SerializeAddon;
|
||||
|
||||
constructor(elem: Element, opts: TermWrapOpts) {
|
||||
opts = opts ?? ({} as any);
|
||||
@@ -142,6 +158,19 @@ class TermWrap {
|
||||
}
|
||||
})
|
||||
);
|
||||
if (WebGLSupported && GlobalModel.clientData.get().clientopts?.webgl) {
|
||||
const webglAddon = new WebglAddon();
|
||||
webglAddon.onContextLoss(() => {
|
||||
webglAddon.dispose();
|
||||
});
|
||||
this.terminal.loadAddon(webglAddon);
|
||||
if (!loggedWebGL) {
|
||||
console.log("loaded webgl!");
|
||||
loggedWebGL = true;
|
||||
}
|
||||
}
|
||||
this.serializeAddon = new SerializeAddon();
|
||||
this.terminal.loadAddon(this.serializeAddon);
|
||||
this.terminal._core._inputHandler._parser.setErrorHandler((state) => {
|
||||
this.numParseErrors++;
|
||||
return state;
|
||||
|
||||
Vendored
+1
@@ -588,6 +588,7 @@ declare global {
|
||||
};
|
||||
globalshortcut: string;
|
||||
globalshortcutenabled: boolean;
|
||||
webgl: boolean;
|
||||
};
|
||||
|
||||
type ReleaseInfoType = {
|
||||
|
||||
+213
-11
@@ -49,6 +49,8 @@ class KeybindManager {
|
||||
userKeybindings: KeybindConfigArray;
|
||||
userKeybindingError: OV<string>;
|
||||
globalModel: any;
|
||||
activeKeybindsVersion: OV<number>;
|
||||
lastKeyData: { domain: string; keyPress: string };
|
||||
|
||||
constructor(GlobalModel: any) {
|
||||
this.levelMap = new Map();
|
||||
@@ -59,10 +61,14 @@ class KeybindManager {
|
||||
this.levelMap.set(curLevel, new Array<Keybind>());
|
||||
}
|
||||
this.userKeybindingError = mobx.observable.box(null, {
|
||||
name: "keyutil-userKeybindingError",
|
||||
name: "keybindManager-userKeybindingError",
|
||||
});
|
||||
this.activeKeybindsVersion = mobx.observable.box(0, {
|
||||
name: "keybindManager-activeKeybindsVersion",
|
||||
});
|
||||
this.globalModel = GlobalModel;
|
||||
this.initKeyDescriptionsMap();
|
||||
this.lastKeyData = { domain: "none", keyPress: "none" };
|
||||
}
|
||||
|
||||
initKeyDescriptionsMap() {
|
||||
@@ -123,8 +129,7 @@ class KeybindManager {
|
||||
this.keyDescriptionsMap = newKeyDescriptions;
|
||||
}
|
||||
|
||||
prettyPrintKeybind(keyDescription: string): string {
|
||||
let keyPress = parseKeyDescription(keyDescription);
|
||||
prettyPrintKeyPress(keyPress: KeyPressDecl): string {
|
||||
let returnString = "";
|
||||
if (keyPress.mods.Cmd) {
|
||||
returnString += "⌘";
|
||||
@@ -148,6 +153,11 @@ class KeybindManager {
|
||||
return returnString;
|
||||
}
|
||||
|
||||
prettyPrintKeybind(keyDescription: string): string {
|
||||
let keyPress = parseKeyDescription(keyDescription);
|
||||
return this.prettyPrintKeyPress(keyPress);
|
||||
}
|
||||
|
||||
getUIDescription(keyDescription: string, prettyPrint: boolean = true): KeybindConfig {
|
||||
let keybinds = this.getKeybindsFromDescription(keyDescription, prettyPrint);
|
||||
if (!this.keyDescriptionsMap.has(keyDescription)) {
|
||||
@@ -228,10 +238,26 @@ class KeybindManager {
|
||||
});
|
||||
}
|
||||
|
||||
runDomainCallbacks(event: WaveKeyboardEvent, curDomainCallbacks: Map<string, KeybindCallback>) {
|
||||
for (let key of curDomainCallbacks.keys()) {
|
||||
let callback = curDomainCallbacks.get(key);
|
||||
if (callback != null) {
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processLevel(nativeEvent: any, event: WaveKeyboardEvent, keybindsArray: Array<Keybind>): boolean {
|
||||
// iterate through keybinds in backwards order
|
||||
let domainCallbacksToRun: Map<string, KeybindCallback> = new Map();
|
||||
for (let index = keybindsArray.length - 1; index >= 0; index--) {
|
||||
let curKeybind = keybindsArray[index];
|
||||
if (this.domainCallbacks.has(curKeybind.domain)) {
|
||||
let curDomainCallback = this.domainCallbacks.get(curKeybind.domain);
|
||||
if (curDomainCallback != null) {
|
||||
domainCallbacksToRun.set(curKeybind.domain, curDomainCallback);
|
||||
}
|
||||
}
|
||||
if (this.checkKeyPressed(event, curKeybind.keybinding)) {
|
||||
if (DumpLogs) {
|
||||
console.log("keybind found", curKeybind);
|
||||
@@ -242,12 +268,60 @@ class KeybindManager {
|
||||
shouldReturn = curKeybind.callback(event);
|
||||
shouldRunCommand = false;
|
||||
}
|
||||
if (!shouldReturn && this.domainCallbacks.has(curKeybind.domain)) {
|
||||
if (shouldRunCommand) {
|
||||
shouldReturn = this.runSlashCommand(curKeybind);
|
||||
}
|
||||
if (shouldReturn) {
|
||||
nativeEvent.preventDefault();
|
||||
nativeEvent.stopPropagation();
|
||||
this.lastKeyData.domain = curKeybind.domain;
|
||||
this.lastKeyData.keyPress = this.prettyPrintKeyPress(
|
||||
getKeyPressDeclFromKeyboardEvent(event, KeyTypeKey)
|
||||
);
|
||||
mobx.action(() => {
|
||||
this.activeKeybindsVersion.set(this.activeKeybindsVersion.get() + 1);
|
||||
})();
|
||||
this.runDomainCallbacks(event, domainCallbacksToRun);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.lastKeyData.domain = "none";
|
||||
this.lastKeyData.keyPress = "none";
|
||||
mobx.action(() => {
|
||||
this.activeKeybindsVersion.set(this.activeKeybindsVersion.get() + 1);
|
||||
})();
|
||||
this.runDomainCallbacks(event, domainCallbacksToRun);
|
||||
return false;
|
||||
}
|
||||
|
||||
processModalLevel(nativeEvent: any, event: WaveKeyboardEvent, keybindsArray: Array<Keybind>): boolean {
|
||||
let curModalDomain: string = "";
|
||||
// iterate through keybinds in backwards order
|
||||
let domainCallbacksToRun: Map<string, KeybindCallback> = new Map();
|
||||
for (let index = keybindsArray.length - 1; index >= 0; index--) {
|
||||
let curKeybind = keybindsArray[index];
|
||||
if (curModalDomain == "") {
|
||||
curModalDomain = curKeybind.domain;
|
||||
}
|
||||
if (curKeybind.domain != curModalDomain) {
|
||||
continue;
|
||||
}
|
||||
if (this.domainCallbacks.has(curKeybind.domain)) {
|
||||
let curDomainCallback = this.domainCallbacks.get(curKeybind.domain);
|
||||
if (curDomainCallback != null) {
|
||||
domainCallbacksToRun.set(curKeybind.domain, curDomainCallback);
|
||||
}
|
||||
}
|
||||
if (this.checkKeyPressed(event, curKeybind.keybinding)) {
|
||||
if (DumpLogs) {
|
||||
console.log("keybind found", curKeybind);
|
||||
}
|
||||
let shouldReturn = false;
|
||||
let shouldRunCommand = true;
|
||||
if (curKeybind.callback != null) {
|
||||
shouldReturn = curKeybind.callback(event);
|
||||
shouldRunCommand = false;
|
||||
let curDomainCallback = this.domainCallbacks.get(curKeybind.domain);
|
||||
if (curDomainCallback != null) {
|
||||
shouldReturn = curDomainCallback(event);
|
||||
}
|
||||
}
|
||||
if (shouldRunCommand) {
|
||||
shouldReturn = this.runSlashCommand(curKeybind);
|
||||
@@ -255,10 +329,12 @@ class KeybindManager {
|
||||
if (shouldReturn) {
|
||||
nativeEvent.preventDefault();
|
||||
nativeEvent.stopPropagation();
|
||||
this.runDomainCallbacks(event, domainCallbacksToRun);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.runDomainCallbacks(event, domainCallbacksToRun);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -272,7 +348,7 @@ class KeybindManager {
|
||||
if (shouldReturn) {
|
||||
return true;
|
||||
}
|
||||
shouldReturn = this.processLevel(nativeEvent, event, modalLevel);
|
||||
shouldReturn = this.processModalLevel(nativeEvent, event, modalLevel);
|
||||
if (shouldReturn) {
|
||||
return true;
|
||||
}
|
||||
@@ -314,6 +390,104 @@ class KeybindManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
getActiveKeybindsVersion() {
|
||||
return this.activeKeybindsVersion;
|
||||
}
|
||||
|
||||
checkKeyInKeybinding(key: string, keyDescription: string) {
|
||||
if (keyDescription == "any") {
|
||||
return true;
|
||||
}
|
||||
if (!this.keyDescriptionsMap.has(keyDescription)) {
|
||||
return false;
|
||||
}
|
||||
let keyPressArray = this.keyDescriptionsMap.get(keyDescription).keys;
|
||||
for (let index = 0; index < keyPressArray.length; index++) {
|
||||
let curKeyPress = keyPressArray[index];
|
||||
if (keybindingIsEqual(key, curKeyPress)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
lookupKeyInLevel(key: string, level: Array<Keybind>): Array<Keybind> {
|
||||
let toReturn: Array<Keybind> = [];
|
||||
for (let index = level.length - 1; index >= 0; index--) {
|
||||
let curKeybind = level[index];
|
||||
console.log("index", index, "curKeybind: ", curKeybind);
|
||||
if (this.checkKeyInKeybinding(key, curKeybind.keybinding)) {
|
||||
toReturn.push({ ...curKeybind }); // shallow copy
|
||||
}
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
lookupKey(key: string) {
|
||||
let modalLevel = this.levelMap.get("modal");
|
||||
let toReturn: Array<Keybind> = [];
|
||||
if (modalLevel.length != 0) {
|
||||
let controlLevel = this.levelMap.get("control");
|
||||
toReturn = toReturn.concat(this.lookupKeyInLevel(key, controlLevel));
|
||||
toReturn = toReturn.concat(this.lookupKeyInLevel(key, modalLevel));
|
||||
let systemLevel = this.levelMap.get("system");
|
||||
toReturn = toReturn.concat(this.lookupKeyInLevel(key, systemLevel));
|
||||
return toReturn;
|
||||
}
|
||||
for (let index = this.levelArray.length - 1; index >= 0; index--) {
|
||||
let curLevel = this.levelArray[index];
|
||||
let curKeybindsArray;
|
||||
if (this.levelMap.has(curLevel)) {
|
||||
curKeybindsArray = this.levelMap.get(curLevel);
|
||||
} else {
|
||||
console.error("error processing key event: couldn't find level: ", curLevel);
|
||||
continue;
|
||||
}
|
||||
toReturn = toReturn.concat(this.lookupKeyInLevel(key, curKeybindsArray));
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
getDomainListForLevel(level: Array<Keybind>): Array<string> {
|
||||
let toReturn: Array<string> = [];
|
||||
for (let index = 0; index < level.length; index++) {
|
||||
let curDomain = level[index].domain;
|
||||
if (!toReturn.includes(curDomain)) {
|
||||
toReturn.push(curDomain);
|
||||
}
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
getLastKeyData() {
|
||||
return this.lastKeyData;
|
||||
}
|
||||
|
||||
getActiveKeybindings(): Array<{ name: string; domains: Array<string> }> {
|
||||
let modalLevel = this.levelMap.get("modal");
|
||||
let toReturn: Array<{ name: string; domains: Array<string> }> = [];
|
||||
if (modalLevel.length != 0) {
|
||||
let controlLevel = this.levelMap.get("control");
|
||||
toReturn.push({ name: "control", domains: this.getDomainListForLevel(controlLevel) });
|
||||
toReturn.push({ name: "modal", domains: this.getDomainListForLevel(modalLevel) });
|
||||
let systemLevel = this.levelMap.get("system");
|
||||
toReturn.push({ name: "system", domains: this.getDomainListForLevel(systemLevel) });
|
||||
return toReturn;
|
||||
}
|
||||
for (let index = this.levelArray.length - 1; index >= 0; index--) {
|
||||
let curLevel = this.levelArray[index];
|
||||
let curKeybindsArray;
|
||||
if (this.levelMap.has(curLevel)) {
|
||||
curKeybindsArray = this.levelMap.get(curLevel);
|
||||
} else {
|
||||
console.error("error processing key event: couldn't find level: ", curLevel);
|
||||
continue;
|
||||
}
|
||||
toReturn.push({ name: curLevel, domains: this.getDomainListForLevel(curKeybindsArray) });
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
registerKeybinding(level: string, domain: string, keybinding: string, callback: KeybindCallback): boolean {
|
||||
if (domain == "" || this.keybindingAlreadyAdded(level, domain, keybinding)) {
|
||||
return false;
|
||||
@@ -326,6 +500,9 @@ class KeybindManager {
|
||||
let curKeybindArray = this.levelMap.get(level);
|
||||
curKeybindArray.push(newKeybind);
|
||||
this.levelMap.set(level, curKeybindArray);
|
||||
mobx.action(() => {
|
||||
this.activeKeybindsVersion.set(this.activeKeybindsVersion.get() + 1);
|
||||
})();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -371,8 +548,11 @@ class KeybindManager {
|
||||
keybindsArray.splice(index, 1);
|
||||
index--;
|
||||
this.levelMap.set(level, keybindsArray);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
mobx.action(() => {
|
||||
this.activeKeybindsVersion.set(this.activeKeybindsVersion.get() + 1);
|
||||
})();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -392,10 +572,13 @@ class KeybindManager {
|
||||
}
|
||||
}
|
||||
this.domainCallbacks.delete(domain);
|
||||
mobx.action(() => {
|
||||
this.activeKeybindsVersion.set(this.activeKeybindsVersion.get() + 1);
|
||||
})();
|
||||
return foundKeybind;
|
||||
}
|
||||
|
||||
getKeyPressEventForDomain(domain: string, callback: KeybindCallback) {
|
||||
registerDomainCallback(domain: string, callback: KeybindCallback) {
|
||||
if (callback == null) {
|
||||
console.log("domain callback can't be null");
|
||||
}
|
||||
@@ -483,6 +666,25 @@ function parseKeyDescription(keyDescription: string): KeyPressDecl {
|
||||
return rtn;
|
||||
}
|
||||
|
||||
function getKeyPressDeclFromKeyboardEvent(waveEvent: WaveKeyboardEvent, keyType: string): KeyPressDecl {
|
||||
let rtn = { key: "", mods: {} } as KeyPressDecl;
|
||||
rtn.mods.Cmd = waveEvent.cmd;
|
||||
rtn.mods.Ctrl = waveEvent.control;
|
||||
rtn.mods.Shift = waveEvent.shift;
|
||||
rtn.mods.Option = waveEvent.option;
|
||||
rtn.mods.Alt = waveEvent.alt && !waveEvent.option;
|
||||
rtn.mods.Meta = waveEvent.meta && !waveEvent.cmd;
|
||||
if (keyType == KeyTypeKey) {
|
||||
rtn.key = waveEvent.code;
|
||||
rtn.keyType = KeyTypeKey;
|
||||
}
|
||||
if (keyType == KeyTypeCode) {
|
||||
rtn.key = waveEvent.code;
|
||||
rtn.keyType = KeyTypeCode;
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
|
||||
function parseKey(key: string): { key: string; type: string } {
|
||||
let regexMatch = key.match(KeyTypeCodeRegex);
|
||||
if (regexMatch != null && regexMatch.length > 1) {
|
||||
|
||||
@@ -743,13 +743,14 @@ func MakeCmdFinalPacket(ck base.CommandKey) *CmdFinalPacketType {
|
||||
}
|
||||
|
||||
type CmdDonePacketType struct {
|
||||
Type string `json:"type"`
|
||||
Ts int64 `json:"ts"`
|
||||
CK base.CommandKey `json:"ck"`
|
||||
ExitCode int `json:"exitcode"`
|
||||
DurationMs int64 `json:"durationms"`
|
||||
FinalState *ShellState `json:"finalstate,omitempty"`
|
||||
FinalStateDiff *ShellStateDiff `json:"finalstatediff,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Ts int64 `json:"ts"`
|
||||
CK base.CommandKey `json:"ck"`
|
||||
ExitCode int `json:"exitcode"`
|
||||
DurationMs int64 `json:"durationms"`
|
||||
FinalState *ShellState `json:"finalstate,omitempty"`
|
||||
FinalStateDiff *ShellStateDiff `json:"finalstatediff,omitempty"`
|
||||
FinalStateBasePtr *ShellStatePtr `json:"finalstatebaseptr,omitempty"`
|
||||
}
|
||||
|
||||
func (*CmdDonePacketType) GetType() string {
|
||||
@@ -814,10 +815,10 @@ type RunPacketType struct {
|
||||
Type string `json:"type"`
|
||||
ReqId string `json:"reqid"`
|
||||
CK base.CommandKey `json:"ck"`
|
||||
ShellType string `json:"shelltype"` // new in v0.6.0 (either "bash" or "zsh") (set by remote.go)
|
||||
ShellType string `json:"shelltype"` // added in Wave v0.6.0 (either "bash" or "zsh") (set by remote.go)
|
||||
Command string `json:"command"`
|
||||
State *ShellState `json:"state,omitempty"`
|
||||
StateDiff *ShellStateDiff `json:"statediff,omitempty"`
|
||||
StatePtr *ShellStatePtr `json:"stateptr,omitempty"` // added in Wave v0.7.2
|
||||
StateComplete bool `json:"statecomplete,omitempty"` // set to true if state is complete (the default env should not be set)
|
||||
UsePty bool `json:"usepty,omitempty"`
|
||||
TermOpts *TermOpts `json:"termopts,omitempty"`
|
||||
|
||||
@@ -40,6 +40,18 @@ type ShellState struct {
|
||||
HashVal string `json:"-"`
|
||||
}
|
||||
|
||||
type ShellStatePtr struct {
|
||||
BaseHash string `json:"basehash"`
|
||||
DiffHashArr []string `json:"diffhasharr,omitempty"`
|
||||
}
|
||||
|
||||
func (ssptr *ShellStatePtr) IsEmpty() bool {
|
||||
if ssptr == nil || ssptr.BaseHash == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (state ShellState) ApproximateSize() int64 {
|
||||
return int64(len(state.Version) + len(state.Cwd) + len(state.ShellVars) + len(state.Aliases) + len(state.Funcs) + len(state.Error))
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ type MServer struct {
|
||||
Sender *packet.PacketSender
|
||||
ClientMap map[base.CommandKey]*shexec.ClientProc
|
||||
Debug bool
|
||||
StateMap *ShellStateMap
|
||||
WriteErrorCh chan bool // closed if there is a I/O write error
|
||||
WriteErrorChOnce *sync.Once
|
||||
Done bool
|
||||
@@ -338,11 +337,6 @@ func (m *MServer) reinit(reqId string, shellType string) {
|
||||
m.Sender.SendErrorResponse(reqId, fmt.Errorf("error initializing shell: %w", err))
|
||||
return
|
||||
}
|
||||
err = m.StateMap.SetCurrentState(ssPk.State.GetShellType(), ssPk.State)
|
||||
if err != nil {
|
||||
m.Sender.SendErrorResponse(reqId, fmt.Errorf("error setting current state: %w", err))
|
||||
return
|
||||
}
|
||||
ssPk.RespId = reqId
|
||||
m.Sender.SendPacket(ssPk)
|
||||
}
|
||||
@@ -710,7 +704,7 @@ func (m *MServer) ProcessRpcPacket(pk packet.RpcPacketType) {
|
||||
m.Sender.SendErrorResponse(reqId, fmt.Errorf("invalid rpc type '%s'", pk.GetType()))
|
||||
}
|
||||
|
||||
func (m *MServer) clientPacketCallback(shellType string, pk packet.PacketType) {
|
||||
func (m *MServer) clientPacketCallback(shellType string, pk packet.PacketType, runPk *packet.RunPacketType) {
|
||||
if pk.GetType() != packet.CmdDonePacketStr {
|
||||
return
|
||||
}
|
||||
@@ -718,25 +712,22 @@ func (m *MServer) clientPacketCallback(shellType string, pk packet.PacketType) {
|
||||
if donePk.FinalState == nil {
|
||||
return
|
||||
}
|
||||
stateHash, curState := m.StateMap.GetCurrentState(shellType)
|
||||
if curState == nil {
|
||||
initialState := runPk.State
|
||||
if initialState == nil {
|
||||
return
|
||||
}
|
||||
sapi, err := shellapi.MakeShellApi(curState.GetShellType())
|
||||
initialStateHash := initialState.GetHashVal(false)
|
||||
sapi, err := shellapi.MakeShellApi(initialState.GetShellType())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
diff, err := sapi.MakeShellStateDiff(curState, stateHash, donePk.FinalState)
|
||||
diff, err := sapi.MakeShellStateDiff(initialState, initialStateHash, donePk.FinalState)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
donePk.FinalState = nil
|
||||
donePk.FinalStateDiff = diff
|
||||
}
|
||||
|
||||
func (m *MServer) isShellInitialized(shellType string) bool {
|
||||
_, curState := m.StateMap.GetCurrentState(shellType)
|
||||
return curState != nil
|
||||
donePk.FinalStateBasePtr = runPk.StatePtr
|
||||
}
|
||||
|
||||
func (m *MServer) runCommand(runPacket *packet.RunPacketType) {
|
||||
@@ -786,7 +777,7 @@ func (m *MServer) runCommand(runPacket *packet.RunPacketType) {
|
||||
}()
|
||||
shexec.SendRunPacketAndRunData(context.Background(), cproc.Input, runPacket)
|
||||
cproc.ProxySingleOutput(runPacket.CK, m.Sender, func(pk packet.PacketType) {
|
||||
m.clientPacketCallback(runPacket.ShellType, pk)
|
||||
m.clientPacketCallback(runPacket.ShellType, pk, runPacket)
|
||||
})
|
||||
}()
|
||||
}
|
||||
@@ -849,7 +840,6 @@ func RunServer() (int, error) {
|
||||
server := &MServer{
|
||||
Lock: &sync.Mutex{},
|
||||
ClientMap: make(map[base.CommandKey]*shexec.ClientProc),
|
||||
StateMap: MakeShellStateMap(),
|
||||
Debug: debug,
|
||||
WriteErrorCh: make(chan bool),
|
||||
WriteErrorChOnce: &sync.Once{},
|
||||
|
||||
@@ -63,6 +63,9 @@ type ShellStateOutput struct {
|
||||
Error string
|
||||
}
|
||||
|
||||
// some timing info
|
||||
// MakeShellStateDiff takes ~1ms to run (even on a large diff)
|
||||
|
||||
type ShellApi interface {
|
||||
GetShellType() string
|
||||
MakeExitTrap(fdNum int) (string, []byte)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user