Compare commits

..

1 Commits

Author SHA1 Message Date
Evan Simkowitz ac52ba33c3 save 2024-03-28 12:14:02 -07:00
32 changed files with 205 additions and 758 deletions
+2 -4
View File
@@ -49,10 +49,8 @@
"tsx-control-statements": "^5.1.1",
"uuid": "^9.0.0",
"winston": "^3.8.2",
"xterm": "^5.3.0",
"xterm-addon-web-links": "^0.9.0",
"xterm-addon-serialize": "^0.11.0",
"xterm-addon-webgl": "^0.16.0"
"xterm": "^5.0.0",
"xterm-addon-web-links": "^0.9.0"
},
"devDependencies": {
"@babel/cli": "^7.17.10",
+1 -1
View File
@@ -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(--app-text-primary-color);
--form-element-primary-text-color: var(--form-element-text-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);
+2 -2
View File
@@ -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-primary-color);
--form-element-primary-text-color: var(--app-text-primary-color);
--form-element-text-color: var(--app-text-color);
--form-element-primary-text-color: var(--app-bg-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);
-47
View File
@@ -27,10 +27,6 @@
}
}
&.primary.danger {
background: var(--app-error-color);
}
&.primary.outlined {
background: none;
border: 1px solid var(--form-element-primary-color);
@@ -40,37 +36,6 @@
}
}
&.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 {
@@ -78,13 +43,6 @@
}
}
&.primary.ghost.danger {
background: none;
i {
fill: var(--app-error-color);
}
}
&.secondary {
color: var(--form-element-text-color);
background: var(--form-element-secondary-color);
@@ -98,11 +56,6 @@
border: 1px solid var(--form-element-text-color);
}
&.secondary.outlined.danger {
background: none;
border: 1px solid var(--app-error-color);
}
&.secondary.ghost {
background: none;
}
+46 -8
View File
@@ -25,6 +25,7 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
const [selDate, setSelDate] = useState(dayjs(selectedDate)); // Initialize with dayjs object
const [showYearAccordion, setShowYearAccordion] = useState(false);
const [expandedYear, setExpandedYear] = useState<number | null>(selDate.year());
const [selectionTimeoutId, setSelectionTimeoutId] = useState<string | number | NodeJS.Timeout | null>(null);
const yearRefs = useRef<YearRefs>({});
const wrapperRef = useRef<HTMLDivElement>(null);
const modalRef = useRef<HTMLDivElement>(null);
@@ -301,24 +302,51 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
: null;
const handleDatePartChange = (part: string, value: string) => {
console.log("Date part changed: ", part, value);
const newDateParts = { ...dateParts, [part]: value };
setDateParts(newDateParts);
console.log("New date parts: ", newDateParts);
// Construct a new date from the updated parts
const newDate = dayjs(`${newDateParts.YYYY}-${newDateParts.MM}-${newDateParts.DD}`);
if (newDate.isValid()) {
onSelectDate(newDate.toDate()); // Call onSelectDate with the new date
setSelDate(newDate); // Update selDate with the new dayjs object
} else {
console.log("Invalid date");
setSelDate(dayjs(new Date()));
}
onSelectDate(selDate.toDate()); // Call onSelectDate with the new date
};
const handleArrowNavigation = (key: string, currentPart: string) => {
console.log("Arrow key pressed: ", key);
const currentIndex = formatParts.indexOf(currentPart);
let targetInput;
const incrementInput = (part: string, increment: number) => {
let newValue = parseInt(dateParts[part]) + increment;
switch (part) {
case "MM":
newValue = newValue > 12 ? 1 : newValue < 1 ? 12 : newValue;
break;
case "DD":
const daysInMonth = dayjs(selDate).daysInMonth();
console.log("Days in month: ", daysInMonth);
newValue = newValue > daysInMonth ? 1 : newValue < 1 ? daysInMonth : newValue;
break;
case "YYYY":
// No bounds for year
break;
}
handleDatePartChange(part, newValue.toString());
};
if (key == "ArrowLeft" && currentIndex > 0) {
targetInput = inputRefs.current[formatParts[currentIndex - 1]].current;
} else if (key == "ArrowRight" && currentIndex < formatParts.length - 1) {
targetInput = inputRefs.current[formatParts[currentIndex + 1]].current;
} else if (key == "ArrowUp") {
incrementInput(currentPart, 1);
} else if (key == "ArrowDown") {
incrementInput(currentPart, -1);
}
if (targetInput) {
@@ -350,6 +378,14 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
handleArrowNavigation("ArrowRight", part);
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:selectAbove", (_) => {
handleArrowNavigation("ArrowUp", part);
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:selectBelow", (_) => {
handleArrowNavigation("ArrowDown", part);
return true;
});
keybindManager.registerKeybinding("control", domain, "generic:space", (_) => {
toggleModal();
return true;
@@ -368,10 +404,10 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
});
for (let numpadKey = 0; numpadKey <= 9; numpadKey++) {
keybindManager.registerKeybinding("control", domain, "generic:numpad-" + numpadKey.toString(), (_) => {
console.log("Numpad key pressed: ", numpadKey);
const currentPart = part;
const maxLength = currentPart === "YYYY" ? 4 : 2;
const newValue = event.target.value.length < maxLength ? event.target.value + numpadKey : numpadKey;
let selectionTimeoutId = null;
handleDatePartChange(currentPart, newValue);
// Clear any existing timeout
@@ -380,10 +416,12 @@ const DatePicker: React.FC<DatePickerProps> = ({ selectedDate, format = "MM/DD/Y
}
// Re-focus and select the input after state update
selectionTimeoutId = setTimeout(() => {
event.target.focus();
event.target.select();
}, 0);
setSelectionTimeoutId(
setTimeout(() => {
event.target.focus();
event.target.select();
}, 0)
);
return true;
});
}
+1 -9
View File
@@ -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,9 +100,6 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
@boundMethod
handleClick() {
if (!this.state.isOpen || !this.state.isTouched) {
this.registerKeybindings();
}
this.toggleDropdown();
}
@@ -112,7 +109,6 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
this.registerKeybindings();
}
@boundMethod
registerKeybindings() {
let keybindManager = GlobalModel.keybindManager;
let domain = "dropdown-" + this.curUuid;
@@ -126,7 +122,6 @@ 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) => {
@@ -170,12 +165,10 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
}
}
@boundMethod
handleBlur() {
this.unregisterKeybindings();
}
@boundMethod
unregisterKeybindings() {
let domain = "dropdown-" + this.curUuid;
GlobalModel.keybindManager.unregisterDomain(domain);
@@ -196,7 +189,6 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
}
onChange(value);
this.setState({ isOpen: false, isTouched: true });
this.unregisterKeybindings();
}
@boundMethod
+2 -2
View File
@@ -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:&nbsp;</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
View File
@@ -25,29 +25,4 @@
&.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;
}
}
}
-39
View File
@@ -4,7 +4,6 @@
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";
@@ -18,41 +17,6 @@ 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() {
@@ -71,9 +35,6 @@ 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>
+6 -29
View File
@@ -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, WaveKeyboardEvent } from "@/util/keyutil";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
type OV<T> = mobx.IObservableValue<T>;
@@ -41,11 +41,9 @@ 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;
@@ -103,12 +101,8 @@ 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;
@@ -116,7 +110,6 @@ 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(
@@ -185,12 +178,10 @@ 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;
});
@@ -199,22 +190,18 @@ class CmdInputKeybindings extends React.Component<{ inputObject: TextAreaInput }
return true;
});
keybindManager.registerKeybinding("pane", "cmdinput", "generic:selectAbove", (waveEvent) => {
this.curPress = "historyupdown";
let rtn = inputObject.arrowUpPressed();
return rtn;
inputObject.arrowUpPressed();
return true;
});
keybindManager.registerKeybinding("pane", "cmdinput", "generic:selectBelow", (waveEvent) => {
this.curPress = "historyupdown";
let rtn = inputObject.arrowDownPressed();
return rtn;
inputObject.arrowDownPressed();
return true;
});
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;
});
@@ -222,16 +209,6 @@ 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() {
+2 -2
View File
@@ -161,11 +161,11 @@
}
.newtab-container {
margin: 8px 16px 0 16px;
margin: 16px;
.newtab-section {
display: flex;
padding: 10px 16px;
padding: 16px;
flex-direction: column;
align-items: flex-start;
gap: 8px;
+3
View File
@@ -558,6 +558,9 @@ 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>
+1
View File
@@ -28,6 +28,7 @@
position: absolute;
top: var(--screentabs-height);
width: 100%;
height: 330px;
transition: height 0.2s ease-in-out;
overflow: hidden;
z-index: 11;
+8 -48
View File
@@ -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, GlobalCommandRunner } from "@/models";
import { GlobalModel } from "@/models";
import { CmdInput } from "./cmdinput/cmdinput";
import { ScreenView } from "./screen/screenview";
import { ScreenTabs } from "./screen/tabs";
@@ -17,17 +17,11 @@ 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;
@@ -54,34 +48,33 @@ class SessionKeybindings extends React.Component<{}, {}> {
GlobalModel.onBracketCmd(1);
return true;
});
keybindManager.registerKeybinding("pane", "screen", "app:selectLineAbove", (waveEvent) => {
keybindManager.registerKeybinding("pane", "session", "app:selectLineAbove", (waveEvent) => {
GlobalModel.onMetaArrowUp();
return true;
});
keybindManager.registerKeybinding("pane", "screen", "app:selectLineBelow", (waveEvent) => {
keybindManager.registerKeybinding("pane", "session", "app:selectLineBelow", (waveEvent) => {
GlobalModel.onMetaArrowDown();
return true;
});
keybindManager.registerKeybinding("pane", "screen", "app:restartCommand", (waveEvent) => {
keybindManager.registerKeybinding("pane", "session", "app:restartCommand", (waveEvent) => {
GlobalModel.onRestartCommand();
return true;
});
keybindManager.registerKeybinding("pane", "screen", "app:restartLastCommand", (waveEvent) => {
keybindManager.registerKeybinding("pane", "session", "app:restartLastCommand", (waveEvent) => {
GlobalModel.onRestartLastCommand();
return true;
});
keybindManager.registerKeybinding("pane", "screen", "app:focusSelectedLine", (waveEvent) => {
keybindManager.registerKeybinding("pane", "session", "app:focusSelectedLine", (waveEvent) => {
GlobalModel.onFocusSelectedLine();
return true;
});
keybindManager.registerKeybinding("pane", "screen", "app:deleteActiveLine", (waveEvent) => {
keybindManager.registerKeybinding("pane", "session", "app:deleteActiveLine", (waveEvent) => {
return GlobalModel.handleDeleteActiveLine();
});
}
componentWillUnmount() {
GlobalModel.keybindManager.unregisterDomain("session");
GlobalModel.keybindManager.unregisterDomain("screen");
}
render() {
@@ -112,29 +105,6 @@ 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();
@@ -163,16 +133,6 @@ 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>
);
}
@@ -221,7 +181,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 && !isHidden}>
<If condition={showTabSettings}>
<TabSettingsPulldownKeybindings />
</If>
</div>
+2 -31
View File
@@ -4,14 +4,13 @@
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;
@@ -21,20 +20,6 @@ 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;
@@ -102,7 +87,6 @@ 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);
@@ -158,19 +142,6 @@ 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;
-1
View File
@@ -588,7 +588,6 @@ declare global {
};
globalshortcut: string;
globalshortcutenabled: boolean;
webgl: boolean;
};
type ReleaseInfoType = {
+11 -213
View File
@@ -49,8 +49,6 @@ class KeybindManager {
userKeybindings: KeybindConfigArray;
userKeybindingError: OV<string>;
globalModel: any;
activeKeybindsVersion: OV<number>;
lastKeyData: { domain: string; keyPress: string };
constructor(GlobalModel: any) {
this.levelMap = new Map();
@@ -61,14 +59,10 @@ class KeybindManager {
this.levelMap.set(curLevel, new Array<Keybind>());
}
this.userKeybindingError = mobx.observable.box(null, {
name: "keybindManager-userKeybindingError",
});
this.activeKeybindsVersion = mobx.observable.box(0, {
name: "keybindManager-activeKeybindsVersion",
name: "keyutil-userKeybindingError",
});
this.globalModel = GlobalModel;
this.initKeyDescriptionsMap();
this.lastKeyData = { domain: "none", keyPress: "none" };
}
initKeyDescriptionsMap() {
@@ -129,7 +123,8 @@ class KeybindManager {
this.keyDescriptionsMap = newKeyDescriptions;
}
prettyPrintKeyPress(keyPress: KeyPressDecl): string {
prettyPrintKeybind(keyDescription: string): string {
let keyPress = parseKeyDescription(keyDescription);
let returnString = "";
if (keyPress.mods.Cmd) {
returnString += "⌘";
@@ -153,11 +148,6 @@ 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)) {
@@ -238,26 +228,10 @@ 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);
@@ -268,60 +242,12 @@ class KeybindManager {
shouldReturn = curKeybind.callback(event);
shouldRunCommand = false;
}
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);
if (!shouldReturn && this.domainCallbacks.has(curKeybind.domain)) {
shouldRunCommand = false;
let curDomainCallback = this.domainCallbacks.get(curKeybind.domain);
if (curDomainCallback != null) {
shouldReturn = curDomainCallback(event);
}
}
if (shouldRunCommand) {
shouldReturn = this.runSlashCommand(curKeybind);
@@ -329,12 +255,10 @@ class KeybindManager {
if (shouldReturn) {
nativeEvent.preventDefault();
nativeEvent.stopPropagation();
this.runDomainCallbacks(event, domainCallbacksToRun);
return true;
}
}
}
this.runDomainCallbacks(event, domainCallbacksToRun);
return false;
}
@@ -348,7 +272,7 @@ class KeybindManager {
if (shouldReturn) {
return true;
}
shouldReturn = this.processModalLevel(nativeEvent, event, modalLevel);
shouldReturn = this.processLevel(nativeEvent, event, modalLevel);
if (shouldReturn) {
return true;
}
@@ -390,104 +314,6 @@ 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;
@@ -500,9 +326,6 @@ 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;
}
@@ -548,11 +371,8 @@ class KeybindManager {
keybindsArray.splice(index, 1);
index--;
this.levelMap.set(level, keybindsArray);
return true;
}
mobx.action(() => {
this.activeKeybindsVersion.set(this.activeKeybindsVersion.get() + 1);
})();
return true;
}
return false;
}
@@ -572,13 +392,10 @@ class KeybindManager {
}
}
this.domainCallbacks.delete(domain);
mobx.action(() => {
this.activeKeybindsVersion.set(this.activeKeybindsVersion.get() + 1);
})();
return foundKeybind;
}
registerDomainCallback(domain: string, callback: KeybindCallback) {
getKeyPressEventForDomain(domain: string, callback: KeybindCallback) {
if (callback == null) {
console.log("domain callback can't be null");
}
@@ -666,25 +483,6 @@ 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) {
+9 -10
View File
@@ -743,14 +743,13 @@ 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"`
FinalStateBasePtr *ShellStatePtr `json:"finalstatebaseptr,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"`
}
func (*CmdDonePacketType) GetType() string {
@@ -815,10 +814,10 @@ type RunPacketType struct {
Type string `json:"type"`
ReqId string `json:"reqid"`
CK base.CommandKey `json:"ck"`
ShellType string `json:"shelltype"` // added in Wave v0.6.0 (either "bash" or "zsh") (set by remote.go)
ShellType string `json:"shelltype"` // new in v0.6.0 (either "bash" or "zsh") (set by remote.go)
Command string `json:"command"`
State *ShellState `json:"state,omitempty"`
StatePtr *ShellStatePtr `json:"stateptr,omitempty"` // added in Wave v0.7.2
StateDiff *ShellStateDiff `json:"statediff,omitempty"`
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"`
-12
View File
@@ -40,18 +40,6 @@ 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))
}
+18 -8
View File
@@ -50,6 +50,7 @@ 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
@@ -337,6 +338,11 @@ 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)
}
@@ -704,7 +710,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, runPk *packet.RunPacketType) {
func (m *MServer) clientPacketCallback(shellType string, pk packet.PacketType) {
if pk.GetType() != packet.CmdDonePacketStr {
return
}
@@ -712,22 +718,25 @@ func (m *MServer) clientPacketCallback(shellType string, pk packet.PacketType, r
if donePk.FinalState == nil {
return
}
initialState := runPk.State
if initialState == nil {
stateHash, curState := m.StateMap.GetCurrentState(shellType)
if curState == nil {
return
}
initialStateHash := initialState.GetHashVal(false)
sapi, err := shellapi.MakeShellApi(initialState.GetShellType())
sapi, err := shellapi.MakeShellApi(curState.GetShellType())
if err != nil {
return
}
diff, err := sapi.MakeShellStateDiff(initialState, initialStateHash, donePk.FinalState)
diff, err := sapi.MakeShellStateDiff(curState, stateHash, donePk.FinalState)
if err != nil {
return
}
donePk.FinalState = nil
donePk.FinalStateDiff = diff
donePk.FinalStateBasePtr = runPk.StatePtr
}
func (m *MServer) isShellInitialized(shellType string) bool {
_, curState := m.StateMap.GetCurrentState(shellType)
return curState != nil
}
func (m *MServer) runCommand(runPacket *packet.RunPacketType) {
@@ -777,7 +786,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, runPacket)
m.clientPacketCallback(runPacket.ShellType, pk)
})
}()
}
@@ -840,6 +849,7 @@ 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{},

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